diff --git a/.changeset/add_better_misc_data_styling.md b/.changeset/add_better_misc_data_styling.md deleted file mode 100644 index 70e10a2bb9..0000000000 --- a/.changeset/add_better_misc_data_styling.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -default: fix ---- - -Fix spam-clicking abbreviations crashing sable diff --git a/.changeset/add_multiple_previews.md b/.changeset/add_multiple_previews.md deleted file mode 100644 index 7e1d707823..0000000000 --- a/.changeset/add_multiple_previews.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -default: minor ---- - -Reorganize Embed settings and reintroduce multiple embeds diff --git a/.changeset/allow_swiping_away_in_app_notifications_on_mobile.md b/.changeset/allow_swiping_away_in_app_notifications_on_mobile.md new file mode 100644 index 0000000000..2d248a8ac8 --- /dev/null +++ b/.changeset/allow_swiping_away_in_app_notifications_on_mobile.md @@ -0,0 +1,5 @@ +--- +default: minor +--- + +Allow swiping away in-app notifications on mobile diff --git a/.changeset/better_misc_data_styling.md b/.changeset/better_misc_data_styling.md deleted file mode 100644 index b71cd29492..0000000000 --- a/.changeset/better_misc_data_styling.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -default: minor ---- - -Change Misc. data styling in users profile pages diff --git a/.changeset/caddy-cache-headers.md b/.changeset/caddy-cache-headers.md deleted file mode 100644 index 498db7463a..0000000000 --- a/.changeset/caddy-cache-headers.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -default: patch ---- - -Add cache-control headers in Caddyfile for assets, service worker, and index.html diff --git a/.changeset/change-settings-link-markers.md b/.changeset/change-settings-link-markers.md deleted file mode 100644 index fba906673a..0000000000 --- a/.changeset/change-settings-link-markers.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -default: patch ---- - -# Change how settings links are shared - -Settings links copied from Sable now stay on the current client URL and include a small Sable marker in the link. That lets Sable recognize settings links copied from other Sable instances without treating unrelated third-party `/settings/...` links as Sable settings links. - -When you send a bare settings link in the composer, Sable now rewrites it into a labeled link so it looks better on non-Sable clients too. For example: `[Settings > Account > Display Name](https://client.example/settings/account?focus=display-name&moe.sable.client.action=settings)`. - -Invalid or malformed settings-looking links now stay normal links instead of being shown as settings chips. - -If you previously set `settingsLinkBaseUrl` in `config.json`, remove it. Sable now derives settings links from the runtime app URL, and the old config key is no longer used. diff --git a/.changeset/enable-gif-sticker-triggers.md b/.changeset/enable-gif-sticker-triggers.md new file mode 100644 index 0000000000..d82e1e4d13 --- /dev/null +++ b/.changeset/enable-gif-sticker-triggers.md @@ -0,0 +1,5 @@ +--- +default: minor +--- + +Show the gif and sticker composer buttons by default, keeping only emoji while typing on mobile. diff --git a/.changeset/fix-android-emote-backspace-keyboard.md b/.changeset/fix-android-emote-backspace-keyboard.md new file mode 100644 index 0000000000..95af104ff2 --- /dev/null +++ b/.changeset/fix-android-emote-backspace-keyboard.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +Fix the Android keyboard flickering when backspacing a native emote. diff --git a/.changeset/fix-crypto-mismatch-reload.md b/.changeset/fix-crypto-mismatch-reload.md new file mode 100644 index 0000000000..13db8be7ed --- /dev/null +++ b/.changeset/fix-crypto-mismatch-reload.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +Fix devices randomly becoming unverified: clear the account's crypto stores on forced logout, and reload instead of retrying in place after a store mismatch. diff --git a/.changeset/fix-emoji-autocomplete-focus.md b/.changeset/fix-emoji-autocomplete-focus.md new file mode 100644 index 0000000000..5b90218b85 --- /dev/null +++ b/.changeset/fix-emoji-autocomplete-focus.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +Fix a crash when emoji autocomplete results reappear while typing. diff --git a/.changeset/fix-forum-event-timeline.md b/.changeset/fix-forum-event-timeline.md new file mode 100644 index 0000000000..454f953969 --- /dev/null +++ b/.changeset/fix-forum-event-timeline.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +Fix the forum room "Event Timeline" developer entry bouncing straight back to the forum view. diff --git a/.changeset/fix-forum-routing.md b/.changeset/fix-forum-routing.md new file mode 100644 index 0000000000..6584e60e09 --- /dev/null +++ b/.changeset/fix-forum-routing.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +Fix forum rooms opening in the normal chat view and improve forum post and thread navigation. diff --git a/.changeset/fix-initial-pins.md b/.changeset/fix-initial-pins.md deleted file mode 100644 index 424daeb280..0000000000 --- a/.changeset/fix-initial-pins.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -default: patch ---- - -Fix the first pin event in a room looking empty diff --git a/.changeset/fix-mobile-composer-keyboard.md b/.changeset/fix-mobile-composer-keyboard.md new file mode 100644 index 0000000000..6b78641746 --- /dev/null +++ b/.changeset/fix-mobile-composer-keyboard.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +Fix the mobile keyboard staying open over the room list after swiping out of a room. diff --git a/.changeset/fix-mobile-image-tap-keyboard.md b/.changeset/fix-mobile-image-tap-keyboard.md new file mode 100644 index 0000000000..68bbc51fe0 --- /dev/null +++ b/.changeset/fix-mobile-image-tap-keyboard.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +Fix the mobile keyboard reopening when tapping a timeline image. diff --git a/.changeset/fix-mobile-sheet-keyboard.md b/.changeset/fix-mobile-sheet-keyboard.md new file mode 100644 index 0000000000..8de0a4a224 --- /dev/null +++ b/.changeset/fix-mobile-sheet-keyboard.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +Dismiss the mobile keyboard when opening composer sheets (emoji, attachments, personas) and keep it dismissed after they close. diff --git a/.changeset/fix-sent-message-draft.md b/.changeset/fix-sent-message-draft.md new file mode 100644 index 0000000000..8e800e720d --- /dev/null +++ b/.changeset/fix-sent-message-draft.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +Fix a sent message coming back in the composer, and stop undo restoring cleared content. diff --git a/.changeset/fix-space-preview-back-navigation.md b/.changeset/fix-space-preview-back-navigation.md new file mode 100644 index 0000000000..2b915bf3df --- /dev/null +++ b/.changeset/fix-space-preview-back-navigation.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +Fix being unable to back out of a space preview opened by address, and give Tauri webview back/swipe somewhere to land on deep-linked launches. diff --git a/.changeset/fix-status-sticking-issue.md b/.changeset/fix-status-sticking-issue.md deleted file mode 100644 index c954c09caa..0000000000 --- a/.changeset/fix-status-sticking-issue.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -default: patch ---- - -Fix status sometimes sticking in member tile diff --git a/.changeset/fix-tauri-avatar-token-rotation.md b/.changeset/fix-tauri-avatar-token-rotation.md new file mode 100644 index 0000000000..736513782f --- /dev/null +++ b/.changeset/fix-tauri-avatar-token-rotation.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +Fix room and user avatars disappearing on desktop after an access-token rotation. diff --git a/.changeset/fix-tauri-media-loading.md b/.changeset/fix-tauri-media-loading.md new file mode 100644 index 0000000000..e13bfaa058 --- /dev/null +++ b/.changeset/fix-tauri-media-loading.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +Fix avatars, images and videos disappearing or reloading on desktop and mobile. diff --git a/.changeset/msc4454.md b/.changeset/msc4454.md deleted file mode 100644 index af548e99ee..0000000000 --- a/.changeset/msc4454.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -default: patch ---- - -spoilered text now gets replaced with `[Spoiler]` in the plain text fallback, as per MSC4454 diff --git a/.changeset/reimplement_responsive_composer_layout.md b/.changeset/reimplement_responsive_composer_layout.md new file mode 100644 index 0000000000..096a84ec81 --- /dev/null +++ b/.changeset/reimplement_responsive_composer_layout.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +Reimplement responsive composer layout diff --git a/.changeset/remove-dynamic-settings-links.md b/.changeset/remove-dynamic-settings-links.md deleted file mode 100644 index 93a2d76e59..0000000000 --- a/.changeset/remove-dynamic-settings-links.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -default: patch ---- - -# Hide copied settings links on dynamic rows diff --git a/.changeset/vlist-hover-transform.md b/.changeset/vlist-hover-transform.md deleted file mode 100644 index bb580175d8..0000000000 --- a/.changeset/vlist-hover-transform.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -default: patch ---- - -Fix button hover background smearing in virtual list rows by suppressing transform on hover diff --git a/.changeset/workbox-precaching.md b/.changeset/workbox-precaching.md deleted file mode 100644 index 2537469a58..0000000000 --- a/.changeset/workbox-precaching.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -default: patch ---- - -Fix Workbox precaching by removing injectionPoint override that was silently disabling all precache entries diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index 4a7250577a..0000000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1 +0,0 @@ -* @7w1 @hazre diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 8d22671f06..a866e80580 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -25,4 +25,5 @@ Fixes # - [ ] Partially AI assisted (clarify which code was AI assisted and briefly explain what it does). - [ ] Fully AI generated (explain what all the generated code does in moderate detail). + diff --git a/.github/actions/prepare-tofu/action.yml b/.github/actions/prepare-tofu/action.yml index c64332e3cd..0cb06882de 100644 --- a/.github/actions/prepare-tofu/action.yml +++ b/.github/actions/prepare-tofu/action.yml @@ -2,24 +2,43 @@ name: Prepare OpenTofu Deployment description: Prepares OpenTofu in infra/web. inputs: - opentofu-version: - description: The OpenTofu version to install. + build-flavor: + description: Build flavor (stable or dev). Controls app build behavior. required: false - default: '1.11.5' - is_release_tag: - description: Whether the build is for a release tag. Passed through to VITE_IS_RELEASE_TAG. - required: false - default: 'false' + default: stable runs: using: composite steps: + - name: Validate deployment configuration + shell: bash + run: | + missing=() + for name in \ + TF_HTTP_ADDRESS \ + TF_HTTP_LOCK_ADDRESS \ + TF_HTTP_UNLOCK_ADDRESS \ + TF_VAR_custom_domain \ + TF_VAR_worker_name \ + SENTRY_ORG \ + SENTRY_PROJECT + do + if [[ -z "${!name:-}" ]]; then + missing+=("$name") + fi + done + + if (( ${#missing[@]} > 0 )); then + printf 'Missing required GitHub Environment variables: %s\n' "${missing[*]}" >&2 + exit 1 + fi + - name: Setup app and build uses: ./.github/actions/setup with: build: 'true' env: - VITE_IS_RELEASE_TAG: ${{ inputs.is_release_tag }} + SABLE_BUILD_FLAVOR: ${{ inputs.build-flavor }} VITE_SENTRY_DSN: ${{ env.VITE_SENTRY_DSN }} VITE_SENTRY_ENVIRONMENT: ${{ env.VITE_SENTRY_ENVIRONMENT }} VITE_APP_VERSION: ${{ env.VITE_APP_VERSION }} @@ -27,22 +46,17 @@ runs: SENTRY_ORG: ${{ env.SENTRY_ORG }} SENTRY_PROJECT: ${{ env.SENTRY_PROJECT }} - - name: Setup OpenTofu - uses: opentofu/setup-opentofu@9d84900f3238fab8cd84ce47d658d25dd008be2f # v1.0.8 - with: - tofu_version: ${{ inputs.opentofu-version }} - - name: Check formatting shell: bash working-directory: ${{ github.workspace }}/infra/web - run: tofu fmt -check + run: mise run opentofu -- fmt -check - name: Initialize OpenTofu shell: bash working-directory: ${{ github.workspace }}/infra/web - run: tofu init -input=false -reconfigure + run: mise run opentofu -- init -input=false -reconfigure - name: Validate configuration shell: bash working-directory: ${{ github.workspace }}/infra/web - run: tofu validate -no-color + run: mise run opentofu -- validate -no-color diff --git a/.github/actions/resolve-release-meta/action.yml b/.github/actions/resolve-release-meta/action.yml new file mode 100644 index 0000000000..59b0fdd5c7 --- /dev/null +++ b/.github/actions/resolve-release-meta/action.yml @@ -0,0 +1,41 @@ +name: Resolve release meta +description: Detect whether the current build is a nightly/dev or stable release and output tag, version, ref, and build flavor. + +inputs: + input-tag: + description: Explicit tag for workflow_dispatch triggers + required: false + default: '' + +outputs: + tag: + description: Release tag name (e.g. "nightly" or "v1.2.3") + value: ${{ steps.meta.outputs.tag }} + version: + description: SemVer version string + value: ${{ steps.meta.outputs.version }} + ref: + description: Git ref (sha for nightly, tag ref for stable) + value: ${{ steps.meta.outputs.ref }} + nightly: + description: '"true" if this is a nightly/dev build' + value: ${{ steps.meta.outputs.nightly }} + started_at: + description: ISO 8601 timestamp of when the build started + value: ${{ steps.meta.outputs.started_at }} + build_flavor: + description: '"dev" or "stable"' + value: ${{ steps.meta.outputs.build_flavor }} + +runs: + using: composite + steps: + - shell: bash + id: meta + env: + EVENT_NAME: ${{ github.event_name }} + INPUT_TAG: ${{ inputs.input-tag }} + GIT_REF: ${{ github.ref }} + GIT_REF_NAME: ${{ github.ref_name }} + GIT_SHA: ${{ github.sha }} + run: node ${{ github.action_path }}/../../scripts/release-meta.mjs "$GITHUB_OUTPUT" diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 9b4c9acbb3..9c5c5c1b0e 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -6,6 +6,10 @@ inputs: description: Command used to install dependencies. required: false default: pnpm install --frozen-lockfile + tauri: + description: Whether to install Tauri build dependencies via mise. + required: false + default: 'false' build: description: Whether to build the app after installing dependencies. required: false @@ -18,14 +22,30 @@ inputs: runs: using: composite steps: - - name: Setup pnpm - uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0 + - name: Setup tools (mise) + id: setup-mise + uses: jdx/mise-action@9e7f7633ff6f6d6048a9418a68d48f288f50eb14 # v4.2.3 + with: + cache: true - - name: Setup node - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + - name: Cache Rust toolchain (workaround jdx/mise-action#215) + if: ${{ inputs.tauri == 'true' }} + id: cache-rust + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: - node-version-file: '.node-version' - cache: 'pnpm' + path: ~/.rustup/toolchains/ + key: rust-${{ runner.os }}-${{ hashFiles('rust-toolchain.toml') }} + restore-keys: rust-${{ runner.os }}- + + - name: Ensure task scripts are executable + shell: bash + working-directory: ${{ github.workspace }} + run: chmod -R +x scripts/ + + - name: Trust project mise configuration + shell: bash + working-directory: ${{ github.workspace }} + run: mise settings set trusted_config_paths "$GITHUB_WORKSPACE" - name: Install dependencies shell: bash @@ -34,6 +54,18 @@ runs: env: INPUTS_INSTALL_COMMAND: ${{ inputs.install-command }} + - name: Install Tauri build dependencies + if: ${{ inputs.tauri == 'true' }} + shell: bash + working-directory: ${{ github.workspace }} + run: mise run tauri:setup + + - name: Force reinstall Rust on cache mismatch + if: ${{ inputs.tauri == 'true' && steps.setup-mise.outputs.cache-hit == 'true' && steps.cache-rust.outputs.cache-hit != 'true' }} + shell: bash + working-directory: ${{ github.workspace }} + run: mise install rust --force + - name: Build app if: ${{ inputs.build == 'true' }} shell: bash diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 3c4f0f7ef7..0d203af98c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -46,10 +46,22 @@ updates: commit-message: prefix: chore groups: - npm-major: - update-types: - - major - npm-non-major: - update-types: - - minor - - patch + npm: + patterns: + - '*' + + - package-ecosystem: cargo + cooldown: + default-days: 7 + directory: /src-tauri + schedule: + interval: daily + open-pull-requests-limit: 5 + labels: + - internal + commit-message: + prefix: chore + groups: + cargo: + patterns: + - '*' diff --git a/.github/scripts/build-updater-manifest.mjs b/.github/scripts/build-updater-manifest.mjs new file mode 100644 index 0000000000..eecf2f1b2b --- /dev/null +++ b/.github/scripts/build-updater-manifest.mjs @@ -0,0 +1,89 @@ +// Composes the Tauri updater manifest (latest.json) from the per-platform .sig +// assets already attached to the release. +// Required env: TAG, VERSION, REPO, GH_TOKEN. + +import { execSync } from 'node:child_process'; +import { readFileSync, readdirSync, writeFileSync, mkdtempSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; + +const TAG = process.env.TAG; +const VERSION = process.env.VERSION; +const REPO = process.env.REPO; +if (!TAG || !VERSION || !REPO) { + console.error('TAG, VERSION, and REPO env vars are required'); + process.exit(1); +} +if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(VERSION)) { + console.error(`VERSION must be valid SemVer (got: ${VERSION})`); + process.exit(1); +} +const version = VERSION; + +const isNightly = process.env.IS_NIGHTLY === 'true'; + +const dir = mkdtempSync(join(tmpdir(), 'sable-sigs-')); +const signaturePattern = `Sable-${VERSION}-*.sig`; +const signatures = execSync( + `gh release view "${TAG}" --repo "${REPO}" --json assets --jq '.assets[].name'`, + { encoding: 'utf8' } +) + .split('\n') + .filter((name) => name.startsWith(`Sable-${VERSION}-`) && name.endsWith('.sig')); + +if (signatures.length === 0) { + console.warn('No signed artifacts found for this version; skipping updater manifest.'); + process.exit(0); +} + +execSync( + `gh release download "${TAG}" --repo "${REPO}" --pattern "${signaturePattern}" --dir "${dir}"`, + { + stdio: 'inherit', + } +); + +const urlFor = (name) => + `https://github.com/${REPO}/releases/download/${TAG}/${encodeURIComponent(name)}`; + +const platforms = {}; +let windowsIsNsis = false; + +for (const sig of readdirSync(dir).filter((f) => f.endsWith('.sig'))) { + const artifact = sig.replace(/\.sig$/, ''); + const entry = { signature: readFileSync(join(dir, sig), 'utf8').trim(), url: urlFor(artifact) }; + const name = artifact.toLowerCase(); + + if (name.endsWith('.app.tar.gz')) { + platforms['darwin-aarch64'] = entry; + platforms['darwin-x86_64'] = entry; + } else if (name.endsWith('.appimage')) { + platforms[name.includes('-linux-aarch64') ? 'linux-aarch64' : 'linux-x86_64'] = entry; + } else if (name.endsWith('-setup.exe') || name.endsWith('.nsis.zip')) { + platforms['windows-x86_64'] = entry; + windowsIsNsis = true; + } else if (name.endsWith('.msi') && !windowsIsNsis) { + platforms['windows-x86_64'] = entry; + } +} + +if (Object.keys(platforms).length === 0) { + console.warn('No .sig assets found on the release; skipping updater manifest.'); + process.exit(0); +} + +let notes = `Sable ${version}`; +if (!isNightly) { + try { + notes = + execSync(`gh release view "${TAG}" --repo "${REPO}" --json body -q .body`, { + encoding: 'utf8', + }).trim() || notes; + } catch { + // keep the default note + } +} + +const manifest = { version, notes, pub_date: new Date().toISOString(), platforms }; +writeFileSync('latest.json', JSON.stringify(manifest, null, 2)); +console.warn(JSON.stringify(manifest, null, 2)); diff --git a/.github/scripts/release-meta.mjs b/.github/scripts/release-meta.mjs new file mode 100644 index 0000000000..b835d9af16 --- /dev/null +++ b/.github/scripts/release-meta.mjs @@ -0,0 +1,129 @@ +#!/usr/bin/env node +/* oxlint-disable no-console */ + +import { execSync } from 'node:child_process'; +import { readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; +import { pathToFileURL } from 'node:url'; + +export function computeNightlyVersion(baseVersion, timestamp = new Date()) { + const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(baseVersion); + if (!match) { + throw new Error(`Version must be stable SemVer (got: ${baseVersion})`); + } + + const y = String(timestamp.getUTCFullYear()).slice(-2); + const m = String(timestamp.getUTCMonth() + 1).padStart(2, '0'); + const d = String(timestamp.getUTCDate()).padStart(2, '0'); + const h = String(timestamp.getUTCHours()).padStart(2, '0'); + const min = String(timestamp.getUTCMinutes()).padStart(2, '0'); + const sec = String(timestamp.getUTCSeconds()).padStart(2, '0'); + + const [, major, minor, patch] = match; + // Second resolution so same-minute commits get distinct build versions. + return `${major}.${minor}.${Number(patch) + 1}-nightly.${y}${m}${d}${h}${min}${sec}`; +} + +export function nightlyVersion() { + const { version } = JSON.parse(readFileSync('package.json', 'utf8')); + + let timestamp; + try { + const commitTimestamp = execSync('git log -1 --format=%ct', { + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'ignore'], + }).trim(); + if (/^\d+$/.test(commitTimestamp)) { + timestamp = new Date(Number(commitTimestamp) * 1000); + } + } catch {} + + let sha = 'local'; + try { + sha = execSync('git rev-parse --short=12 HEAD', { + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'ignore'], + }).trim(); + } catch {} + + return `${computeNightlyVersion(version, timestamp)}.${sha}`; +} + +export async function writeOutputs(outputPath, entries) { + const { appendFileSync } = await import('node:fs'); + appendFileSync(outputPath, entries.map((e) => `${e.key}=${e.value}`).join('\n') + '\n'); +} + +export function resolveReleaseMeta({ + eventName = '', + inputTag = '', + gitRef = '', + gitRefName = '', + gitSha = '', +} = {}) { + const startedAt = new Date().toISOString(); + let tag, ref, isNightly, version; + + if (eventName === 'workflow_dispatch' && inputTag) { + tag = inputTag; + ref = inputTag; + isNightly = false; + version = tag.replace(/^v/, ''); + } else if (gitRef === 'refs/heads/dev') { + tag = 'nightly'; + ref = gitSha; + isNightly = true; + version = nightlyVersion(); + } else if (/^v\d+\./.test(gitRefName)) { + tag = gitRefName; + ref = gitRef; + isNightly = false; + version = gitRefName.replace(/^v/, ''); + } else { + tag = ''; + ref = gitRef; + isNightly = false; + version = ''; + } + + return { + tag, + version, + ref, + isNightly, + startedAt, + buildFlavor: isNightly ? 'dev' : 'stable', + }; +} + +const isMain = process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href; +if (isMain) { + try { + const meta = resolveReleaseMeta({ + eventName: process.env.EVENT_NAME ?? '', + inputTag: process.env.INPUT_TAG ?? '', + gitRef: process.env.GIT_REF ?? '', + gitRefName: process.env.GIT_REF_NAME ?? '', + gitSha: process.env.GIT_SHA ?? '', + }); + + const outputPath = process.argv[2]; + const entries = [ + { key: 'tag', value: meta.tag }, + { key: 'version', value: meta.version }, + { key: 'ref', value: meta.ref }, + { key: 'nightly', value: String(meta.isNightly) }, + { key: 'started_at', value: meta.startedAt }, + { key: 'build_flavor', value: meta.buildFlavor }, + ]; + + if (outputPath) { + await writeOutputs(outputPath, entries); + } else { + entries.forEach(({ key, value }) => console.log(`${key}=${value}`)); + } + } catch (err) { + console.error(err.message); + process.exit(1); + } +} diff --git a/.github/scripts/set-tauri-version.mjs b/.github/scripts/set-tauri-version.mjs new file mode 100644 index 0000000000..3fb5a0ba39 --- /dev/null +++ b/.github/scripts/set-tauri-version.mjs @@ -0,0 +1,64 @@ +#!/usr/bin/env node +/* oxlint-disable no-console */ + +// Stamps release-specific settings into src-tauri/tauri.conf.json, which can +// drift from the package.json version Knope tags on. Used by tauri-build.yml. + +import { readFileSync, writeFileSync } from 'node:fs'; +import process from 'node:process'; + +const args = process.argv.slice(2); +const foldNightlyIntoPatch = args.includes('--apple-short-version'); +const [version, updaterEndpoint] = args.filter((arg) => !arg.startsWith('--')); +if (!version || !/^\d+\.\d+\.\d+/.test(version)) { + console.error( + `Usage: set-tauri-version.mjs [updater-endpoint] [--apple-short-version] (got: ${version ?? ''})` + ); + process.exit(1); +} + +if (updaterEndpoint && !updaterEndpoint.startsWith('https://')) { + console.error(`Updater endpoint must use HTTPS (got: ${updaterEndpoint})`); + process.exit(1); +} + +const file = 'src-tauri/tauri.conf.json'; +const config = JSON.parse(readFileSync(file, 'utf8')); + +// Sanitize hyphens and leading zeroes in prerelease portion for SemVer 2.0.0 and iOS compatibility +const sanitizedVersion = version.replace(/^(\d+\.\d+\.\d+-nightly)\.(.+)$/, (_, prefix, build) => { + const sanitizedBuild = build + .split(/[-.]/) + .map((part) => (/^\d+$/.test(part) ? String(Number(part)) : part)) + .join('.'); + return `${prefix}.${sanitizedBuild}`; +}); + +// The nightly stamp goes into patch as a Unix timestamp, since the YYMMDDHHMMSS form +// overflows the u32 Tauri parses each version part into. +let stampedVersion = sanitizedVersion; +if (foldNightlyIntoPatch) { + const nightly = + /^(\d+\.\d+)\.\d+-nightly\.(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\.[0-9A-Za-z-]+)?$/.exec( + sanitizedVersion + ); + if (!nightly) { + console.error(`--apple-short-version needs a nightly version (got: ${sanitizedVersion})`); + process.exit(1); + } + const [, base, yy, mm, dd, hh, min, sec] = nightly; + const patch = Date.UTC(2000 + +yy, mm - 1, +dd, +hh, +min, +sec) / 1000; + if (!Number.isInteger(patch) || patch <= 0 || patch > 0xffffffff) { + console.error(`Nightly stamp does not map to a u32 patch version: ${sanitizedVersion}`); + process.exit(1); + } + stampedVersion = `${base}.${patch}`; +} + +config.version = stampedVersion; +if (updaterEndpoint) { + config.plugins.updater.endpoints = [updaterEndpoint]; +} +writeFileSync(file, `${JSON.stringify(config, null, 2)}\n`); +console.log(`Set ${file} version to ${stampedVersion}`); +if (updaterEndpoint) console.log(`Set ${file} updater endpoint to ${updaterEndpoint}`); diff --git a/.github/scripts/update-altstore-source.mjs b/.github/scripts/update-altstore-source.mjs new file mode 100644 index 0000000000..01ac0ac693 --- /dev/null +++ b/.github/scripts/update-altstore-source.mjs @@ -0,0 +1,76 @@ +#!/usr/bin/env node +/* oxlint-disable no-console */ + +// Updates altstore-source.json with a new version entry pointing at a release +// IPA asset, then prints the updated file. Used by .github/workflows/tauri-build.yml. +// +// Usage: update-altstore-source.mjs [description] +// Env: ALTSTORE_MAX_VERSIONS - how many version entries to keep (default 20) + +import { readFileSync, writeFileSync } from 'node:fs'; +import process from 'node:process'; + +const [sourcePath, version, build, sizeStr, downloadURL, date, description] = process.argv.slice(2); + +if (!sourcePath || !version || !build || !sizeStr || !downloadURL || !date) { + console.error( + 'Usage: update-altstore-source.mjs [description]' + ); + process.exit(1); +} + +const size = Number(sizeStr); +if (!Number.isInteger(size) || size <= 0) { + console.error(`ipa-size must be a positive integer (got: ${sizeStr})`); + process.exit(1); +} + +const source = JSON.parse(readFileSync(sourcePath, 'utf8')); +const app = source.apps?.[0]; +if (!app) { + console.error('Source JSON has no apps[0] to update.'); + process.exit(1); +} + +// Clean version string for AltStore/SideStore compatibility: +// Apple Info.plist CFBundleShortVersionString / CFBundleVersion replaces hyphens/prereleases (e.g. -nightly.) with numeric/dot delimiters. +const normalizedVersion = version + .replace(/-nightly\./g, '.') + .replace(/[^0-9.]/g, '.') + .replace(/\.+/g, '.') + .replace(/^\.|\.$/g, ''); +const normalizedBuild = build + .replace(/-nightly\./g, '.') + .replace(/[^0-9.]/g, '.') + .replace(/\.+/g, '.') + .replace(/^\.|\.$/g, ''); + +const entry = { + version: normalizedVersion, + buildVersion: normalizedBuild, + date, + size, + downloadURL, + localizedDescription: description ?? `v${version}`, +}; + +// Replace the existing entry for this version, otherwise prepend as latest. +const versions = Array.isArray(app.versions) ? app.versions : []; +const idx = versions.findIndex((v) => v.version === normalizedVersion); +if (idx >= 0) versions[idx] = entry; +else versions.unshift(entry); + +// Keep only the most recent versions in the manifest. +const maxVersions = Number(process.env.ALTSTORE_MAX_VERSIONS ?? 20); +if (!Number.isInteger(maxVersions) || maxVersions <= 0) { + console.error( + `ALTSTORE_MAX_VERSIONS must be a positive integer (got: ${process.env.ALTSTORE_MAX_VERSIONS})` + ); + process.exit(1); +} +app.versions = versions.slice(0, maxVersions); + +writeFileSync(sourcePath, `${JSON.stringify(source, null, 2)}\n`); +console.log( + `Updated ${sourcePath}: ${app.bundleIdentifier} v${version} (${build}) -> ${downloadURL}` +); diff --git a/.github/templates/altstore-source-nightly.json b/.github/templates/altstore-source-nightly.json new file mode 100644 index 0000000000..2eca6db7a9 --- /dev/null +++ b/.github/templates/altstore-source-nightly.json @@ -0,0 +1,32 @@ +{ + "name": "Sable (nightly)", + "identifier": "moe.sable.client.source.nightly", + "subtitle": "Sable for iOS (nightly)", + "description": "Sable iOS nightly builds for AltStore and SideStore, rebuilt on every push to dev.", + "iconURL": "https://raw.githubusercontent.com/SableClient/Sable/dev/src-tauri/icons/icon.png", + "website": "https://github.com/SableClient/Sable", + "tintColor": "#5CA399", + "featuredApps": ["moe.sable.client.nightly"], + "apps": [ + { + "name": "Sable Nightly", + "bundleIdentifier": "moe.sable.client.nightly", + "developerName": "SableClient", + "subtitle": "Sable iOS (nightly)", + "localizedDescription": "Sable for iOS. Nightly builds from dev; unsigned, resigned at install by AltStore/SideStore.", + "iconURL": "https://raw.githubusercontent.com/SableClient/Sable/dev/src-tauri/icons/icon.png", + "tintColor": "#5CA399", + "category": "social", + "screenshots": [], + "versions": [], + "appPermissions": { + "entitlements": ["aps-environment"], + "privacy": { + "NSCameraUsageDescription": "Sable needs camera access for video calls.", + "NSMicrophoneUsageDescription": "Sable needs microphone access for voice and video calls." + } + } + } + ], + "news": [] +} diff --git a/.github/templates/altstore-source.json b/.github/templates/altstore-source.json new file mode 100644 index 0000000000..e15f73a6ac --- /dev/null +++ b/.github/templates/altstore-source.json @@ -0,0 +1,32 @@ +{ + "name": "Sable", + "identifier": "moe.sable.client.source", + "subtitle": "Sable for iOS", + "description": "Sable iOS builds for AltStore and SideStore.", + "iconURL": "https://raw.githubusercontent.com/SableClient/Sable/dev/src-tauri/icons/icon.png", + "website": "https://github.com/SableClient/Sable", + "tintColor": "#5CA399", + "featuredApps": ["moe.sable.client"], + "apps": [ + { + "name": "Sable", + "bundleIdentifier": "moe.sable.client", + "developerName": "SableClient", + "subtitle": "Sable iOS", + "localizedDescription": "Sable for iOS. Unsigned builds, resigned at install by AltStore/SideStore.", + "iconURL": "https://raw.githubusercontent.com/SableClient/Sable/dev/src-tauri/icons/icon.png", + "tintColor": "#5CA399", + "category": "social", + "screenshots": [], + "versions": [], + "appPermissions": { + "entitlements": ["aps-environment"], + "privacy": { + "NSCameraUsageDescription": "Sable needs camera access for video calls.", + "NSMicrophoneUsageDescription": "Sable needs microphone access for voice and video calls." + } + } + } + ], + "news": [] +} diff --git a/.github/workflows/cloudflare-web-deploy.yml b/.github/workflows/cloudflare-web-deploy.yml deleted file mode 100644 index d3d2c44614..0000000000 --- a/.github/workflows/cloudflare-web-deploy.yml +++ /dev/null @@ -1,104 +0,0 @@ -name: Cloudflare Infra - -on: - pull_request: - paths: - - 'infra/web/**' - - '.github/workflows/cloudflare-web-deploy.yml' - - '.github/actions/prepare-tofu/**' - - '.github/actions/setup/**' - push: - tags: - - 'v*' - workflow_dispatch: - inputs: - git_tag: - description: 'Git tag to deploy (e.g. v1.2.3). Leave empty to deploy current HEAD.' - required: false - type: string - -env: - CLOUDFLARE_API_TOKEN: ${{ secrets.TF_CLOUDFLARE_API_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - TF_VAR_workers_message: ${{ github.event_name == 'pull_request' && github.event.pull_request.title || github.ref_name }} - TF_VAR_account_id: ${{ secrets.TF_VAR_ACCOUNT_ID }} - TF_VAR_zone_id: ${{ secrets.TF_VAR_ZONE_ID }} - TF_HTTP_ADDRESS: ${{ secrets.TF_HTTP_ADDRESS }} - TF_HTTP_LOCK_ADDRESS: ${{ secrets.TF_HTTP_LOCK_ADDRESS }} - TF_HTTP_UNLOCK_ADDRESS: ${{ secrets.TF_HTTP_UNLOCK_ADDRESS }} - TF_HTTP_USERNAME: ${{ secrets.TF_HTTP_USERNAME }} - TF_HTTP_PASSWORD: ${{ secrets.TF_HTTP_PASSWORD }} - TF_HTTP_LOCK_METHOD: 'POST' - TF_HTTP_UNLOCK_METHOD: 'DELETE' - TF_HTTP_RETRY_WAIT_MIN: '5' - -concurrency: - group: cloudflare-infra - cancel-in-progress: false - -jobs: - plan: - if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: write - defaults: - run: - working-directory: infra/web - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - - name: Prepare OpenTofu deployment - uses: ./.github/actions/prepare-tofu - with: - is_release_tag: ${{ startsWith(github.ref, 'refs/tags/v') || (github.event_name == 'workflow_dispatch' && inputs.git_tag != '') }} - env: - VITE_SENTRY_DSN: ${{ secrets.VITE_SENTRY_DSN }} - VITE_SENTRY_ENVIRONMENT: production - VITE_APP_VERSION: ${{ github.ref_name }} - SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - SENTRY_ORG: ${{ secrets.SENTRY_ORG }} - SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} - - - name: Comment PR plan - uses: dflook/tofu-plan@3f5dc358343fb58cd60f83b019e810315aa8258f # v2.2.3 - with: - path: infra/web - label: production - - apply: - if: (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')) || github.event_name == 'workflow_dispatch' - runs-on: ubuntu-latest - permissions: - contents: read - defaults: - run: - working-directory: infra/web - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - ref: ${{ github.event_name == 'workflow_dispatch' && inputs.git_tag || '' }} - - - name: Prepare OpenTofu deployment - uses: ./.github/actions/prepare-tofu - with: - is_release_tag: ${{ startsWith(github.ref, 'refs/tags/v') || (github.event_name == 'workflow_dispatch' && inputs.git_tag != '') }} - env: - VITE_SENTRY_DSN: ${{ secrets.VITE_SENTRY_DSN }} - VITE_SENTRY_ENVIRONMENT: production - VITE_APP_VERSION: ${{ github.ref_name }} - SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - SENTRY_ORG: ${{ secrets.SENTRY_ORG }} - SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} - - - name: Plan infrastructure - run: tofu plan -input=false -no-color - - - name: Apply infrastructure - run: tofu apply -input=false -auto-approve diff --git a/.github/workflows/cloudflare-web-preview.yml b/.github/workflows/cloudflare-web-preview.yml index 8b93a4bb9b..c394d90648 100644 --- a/.github/workflows/cloudflare-web-preview.yml +++ b/.github/workflows/cloudflare-web-preview.yml @@ -2,6 +2,7 @@ name: Cloudflare Worker Preview Deploy on: pull_request: + types: [labeled] paths: - 'src/**' - 'index.html' @@ -23,21 +24,20 @@ on: - 'tsconfig.json' - '.github/workflows/cloudflare-web-preview.yml' - '.github/actions/setup/**' - concurrency: group: cloudflare-worker-preview-${{ github.event.pull_request.number || github.ref_name }} cancel-in-progress: true jobs: deploy: - if: github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'push' + if: (github.event_name == 'pull_request' && github.event.label.name == 'deploy-preview' && github.event.pull_request.head.repo.full_name == github.repository && github.head_ref != 'release') || github.event_name == 'push' runs-on: ubuntu-latest permissions: contents: read pull-requests: write steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -89,7 +89,7 @@ jobs: - name: Upload Worker preview id: deploy - uses: cloudflare/wrangler-action@da0e0dfe58b7a431659754fdf3f186c529afbe65 # v3.14.1 + uses: cloudflare/wrangler-action@ebbaa1584979971c8614a24965b4405ff95890e0 # v4.0.0 env: PREVIEW_MESSAGE: ${{ steps.metadata.outputs.preview_message }} with: @@ -126,7 +126,7 @@ jobs: echo "preview_url=${preview_url}" >> "$GITHUB_OUTPUT" - name: Publish summary and PR comment - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: MARKER: '' PREVIEW_URL: ${{ steps.preview.outputs.preview_url }} diff --git a/.github/workflows/cloudflare-web.yml b/.github/workflows/cloudflare-web.yml new file mode 100644 index 0000000000..64add08c05 --- /dev/null +++ b/.github/workflows/cloudflare-web.yml @@ -0,0 +1,147 @@ +name: Cloudflare Infra + +on: + push: + branches: [dev] + tags: ['v*'] + pull_request: + paths: + - 'infra/web/**' + - '.github/workflows/cloudflare-web.yml' + - '.github/actions/prepare-tofu/**' + - '.github/actions/setup/**' + workflow_dispatch: + inputs: + git_tag: + description: 'Git tag to deploy (e.g. v1.2.3). Empty = deploy current HEAD as dev build.' + required: false + type: string + +env: + CLOUDFLARE_API_TOKEN: ${{ secrets.TF_CLOUDFLARE_API_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TF_VAR_account_id: ${{ secrets.TF_VAR_ACCOUNT_ID }} + TF_VAR_zone_id: ${{ secrets.TF_VAR_ZONE_ID }} + TF_HTTP_USERNAME: ${{ secrets.TF_HTTP_USERNAME }} + TF_HTTP_PASSWORD: ${{ secrets.TF_HTTP_PASSWORD }} + TF_VAR_workers_message: ${{ github.event_name == 'pull_request' && github.event.pull_request.title || github.ref_name }} + TF_HTTP_LOCK_METHOD: 'POST' + TF_HTTP_UNLOCK_METHOD: 'DELETE' + TF_HTTP_RETRY_WAIT_MIN: '5' + +jobs: + detect: + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + environment: ${{ steps.env.outputs.environment }} + sentry_environment: ${{ steps.env.outputs.sentry_environment }} + build_flavor: ${{ steps.version.outputs.build_flavor }} + version: ${{ steps.version.outputs.version }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - uses: ./.github/actions/resolve-release-meta + id: version + with: + input-tag: ${{ inputs.git_tag }} + + - name: Detect environment + id: env + env: + REF_NAME: ${{ github.ref_name }} + EVENT_NAME: ${{ github.event_name }} + INPUT_GIT_TAG: ${{ inputs.git_tag }} + run: | + if [[ "${REF_NAME}" == "dev" && "${EVENT_NAME}" != "workflow_dispatch" ]] || [[ "${EVENT_NAME}" == "workflow_dispatch" && "${INPUT_GIT_TAG}" == "" ]]; then + echo "environment=development" >> "$GITHUB_OUTPUT" + echo "sentry_environment=development" >> "$GITHUB_OUTPUT" + else + echo "environment=production" >> "$GITHUB_OUTPUT" + echo "sentry_environment=production" >> "$GITHUB_OUTPUT" + fi + + plan: + needs: detect + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + environment: + name: ${{ needs.detect.outputs.environment }} + env: + TF_HTTP_ADDRESS: ${{ vars.TF_HTTP_ADDRESS }} + TF_HTTP_LOCK_ADDRESS: ${{ vars.TF_HTTP_LOCK_ADDRESS }} + TF_HTTP_UNLOCK_ADDRESS: ${{ vars.TF_HTTP_UNLOCK_ADDRESS }} + TF_VAR_custom_domain: ${{ vars.TF_VAR_CUSTOM_DOMAIN }} + TF_VAR_worker_name: ${{ vars.TF_VAR_WORKER_NAME }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + ref: ${{ github.event_name == 'workflow_dispatch' && inputs.git_tag || '' }} + + - uses: ./.github/actions/prepare-tofu + with: + build-flavor: ${{ needs.detect.outputs.build_flavor }} + env: + VITE_SENTRY_DSN: ${{ secrets.VITE_SENTRY_DSN }} + VITE_SENTRY_ENVIRONMENT: ${{ needs.detect.outputs.sentry_environment }} + VITE_APP_VERSION: ${{ needs.detect.outputs.version }} + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: ${{ vars.SENTRY_ORG }} + SENTRY_PROJECT: ${{ vars.SENTRY_PROJECT }} + + - name: Plan infrastructure + working-directory: infra/web + run: mise run opentofu -- plan -input=false -no-color + + - name: Comment PR plan + if: github.event_name == 'pull_request' + uses: dflook/tofu-plan@cdb4f4d604c9fa695e457744dd1f802853397ce7 # v3.0.0 + with: + path: infra/web + label: ${{ needs.detect.outputs.environment }} + + apply: + needs: [detect, plan] + if: github.event_name != 'pull_request' + runs-on: ubuntu-latest + permissions: + contents: read + environment: + name: ${{ needs.detect.outputs.environment }} + url: https://${{ vars.TF_VAR_CUSTOM_DOMAIN }} + env: + TF_HTTP_ADDRESS: ${{ vars.TF_HTTP_ADDRESS }} + TF_HTTP_LOCK_ADDRESS: ${{ vars.TF_HTTP_LOCK_ADDRESS }} + TF_HTTP_UNLOCK_ADDRESS: ${{ vars.TF_HTTP_UNLOCK_ADDRESS }} + TF_VAR_custom_domain: ${{ vars.TF_VAR_CUSTOM_DOMAIN }} + TF_VAR_worker_name: ${{ vars.TF_VAR_WORKER_NAME }} + concurrency: + group: cloudflare-infra-${{ needs.detect.outputs.environment }} + cancel-in-progress: false + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + ref: ${{ github.event_name == 'workflow_dispatch' && inputs.git_tag || '' }} + + - uses: ./.github/actions/prepare-tofu + with: + build-flavor: ${{ needs.detect.outputs.build_flavor }} + env: + VITE_SENTRY_DSN: ${{ secrets.VITE_SENTRY_DSN }} + VITE_SENTRY_ENVIRONMENT: ${{ needs.detect.outputs.sentry_environment }} + VITE_APP_VERSION: ${{ needs.detect.outputs.version }} + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: ${{ vars.SENTRY_ORG }} + SENTRY_PROJECT: ${{ vars.SENTRY_PROJECT }} + + - name: Apply infrastructure + working-directory: infra/web + run: mise run opentofu -- apply -input=false -auto-approve diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index f1df6a2dbe..9019e9916b 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -57,7 +57,9 @@ jobs: # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false # Add any setup steps before running the `github/codeql-action/init` action. # This includes steps like installing compilers or runtimes (`actions/setup-node` @@ -67,7 +69,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 + uses: github/codeql-action/init@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} @@ -96,6 +98,6 @@ jobs: exit 1 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 + uses: github/codeql-action/analyze@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7 with: category: '/language:${{matrix.language}}' diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 03a63ef99f..69c3b21435 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -32,52 +32,46 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - ref: ${{ github.event_name == 'workflow_dispatch' && inputs.git_tag || '' }} - name: Log in to GitHub Container Registry if: github.event_name != 'pull_request' - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Resolve release tag value - id: release_tag - shell: bash - run: | - if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then - TAG="${{ inputs.git_tag }}" - else - TAG="${GITHUB_REF#refs/tags/}" - fi - if [[ "${TAG}" == v* ]]; then - echo "value=${TAG}" >> "$GITHUB_OUTPUT" - echo "is_release=true" >> "$GITHUB_OUTPUT" - else - echo "value=" >> "$GITHUB_OUTPUT" - echo "is_release=false" >> "$GITHUB_OUTPUT" - fi + - uses: ./.github/actions/resolve-release-meta + id: release_meta + with: + input-tag: ${{ inputs.git_tag }} + + - name: Checkout release target + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + ref: ${{ steps.release_meta.outputs.ref }} - name: Extract metadata id: meta - uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0 + uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} flavor: | latest=false tags: | - # dev branch or manual dispatch without a tag: short commit SHA + latest - type=sha,prefix=,format=short,enable=${{ github.ref == 'refs/heads/dev' || (github.event_name == 'workflow_dispatch' && inputs.git_tag == '') }} - type=raw,value=latest,enable=${{ github.ref == 'refs/heads/dev' || (github.event_name == 'workflow_dispatch' && inputs.git_tag == '') }} + # dev branch or manual dispatch without a tag: short commit SHA + dev + type=sha,prefix=,format=short,enable=${{ steps.release_meta.outputs.version != '' && steps.release_meta.outputs.nightly == 'true' }} + type=raw,value=dev,enable=${{ steps.release_meta.outputs.version != '' && steps.release_meta.outputs.nightly == 'true' }} - # git tags (push or manual dispatch with a tag): semver breakdown - type=semver,pattern={{version}},value=${{ steps.release_tag.outputs.value }},enable=${{ steps.release_tag.outputs.is_release == 'true' }} - type=semver,pattern={{major}}.{{minor}},value=${{ steps.release_tag.outputs.value }},enable=${{ steps.release_tag.outputs.is_release == 'true' }} - type=semver,pattern={{major}},value=${{ steps.release_tag.outputs.value }},enable=${{ steps.release_tag.outputs.is_release == 'true' && !startsWith(steps.release_tag.outputs.value, 'v0.') }} + # git tags (push or manual dispatch with a tag): semver breakdown + latest + type=raw,value=latest,enable=${{ steps.release_meta.outputs.version != '' && steps.release_meta.outputs.nightly != 'true' }} + type=semver,pattern={{version}},value=${{ steps.release_meta.outputs.version }},enable=${{ steps.release_meta.outputs.version != '' && steps.release_meta.outputs.nightly != 'true' }} + type=semver,pattern={{major}}.{{minor}},value=${{ steps.release_meta.outputs.version }},enable=${{ steps.release_meta.outputs.version != '' && steps.release_meta.outputs.nightly != 'true' }} + type=semver,pattern={{major}},value=${{ steps.release_meta.outputs.version }},enable=${{ steps.release_meta.outputs.version != '' && steps.release_meta.outputs.nightly != 'true' && !startsWith(steps.release_meta.outputs.version, 'v0.') }} - name: Compute short SHA id: vars @@ -89,19 +83,20 @@ jobs: - name: Build site env: VITE_BUILD_HASH: ${{ steps.vars.outputs.short_sha }} - VITE_IS_RELEASE_TAG: ${{ steps.release_tag.outputs.is_release }} + VITE_APP_VERSION: ${{ steps.release_meta.outputs.version }} + SABLE_BUILD_FLAVOR: ${{ steps.release_meta.outputs.build_flavor }} run: | NODE_OPTIONS=--max_old_space_size=4096 pnpm run build - name: Set up QEMU - uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0 + uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 + uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 - name: Build and push Docker image id: push - uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2 + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 with: context: . push: ${{ github.event_name != 'pull_request' }} @@ -115,8 +110,8 @@ jobs: - name: Generate artifact attestation if: github.event_name != 'pull_request' - uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 + uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 with: subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} subject-digest: ${{ steps.push.outputs.digest }} - push-to-registry: false + push-to-registry: true diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml new file mode 100644 index 0000000000..8f095852a3 --- /dev/null +++ b/.github/workflows/e2e.yml @@ -0,0 +1,38 @@ +name: End-to-end tests + +on: + pull_request: + push: + branches: [dev] + merge_group: + +jobs: + e2e: + name: Playwright + runs-on: ubuntu-latest + if: github.head_ref != 'release' + permissions: + contents: read + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Setup app + uses: ./.github/actions/setup + + - name: Run Playwright tests + run: pnpm run test:e2e:docker + env: + NODE_OPTIONS: --max-old-space-size=8192 + + - name: Upload report + if: ${{ !cancelled() }} + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: playwright-report + path: | + playwright-report + test-results + retention-days: 7 diff --git a/.github/workflows/flake-lock-fix.yml b/.github/workflows/flake-lock-fix.yml new file mode 100644 index 0000000000..3285901262 --- /dev/null +++ b/.github/workflows/flake-lock-fix.yml @@ -0,0 +1,71 @@ +name: Fix flake hashes + +on: + workflow_dispatch: + push: + branches: [dev] + paths: ['pnpm-lock.yaml'] + +jobs: + fix: + name: Fix flake hashes + runs-on: ubuntu-latest + permissions: + id-token: write + contents: write + steps: + - name: Generate bot token + id: app-token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + permission-contents: write + + - name: Checkout dev + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: dev + persist-credentials: true + token: ${{ steps.app-token.outputs.token }} + + - name: Install Determinate Nix + uses: DeterminateSystems/determinate-nix-action@61cbfe2efc2d4e7a8a6d56967c3c1058e846c858 # v3.21.9 + + - id: check + run: nix flake check -L + continue-on-error: true + + - name: Fix hash mismatches + if: steps.check.outcome == 'failure' + run: determinate-nixd fix hashes --auto-apply + + - name: Get GitHub App User ID + id: get-user-id + run: echo "user-id=$(gh api "/users/${STEPS_APP_TOKEN_OUTPUTS_APP_SLUG}[bot]" --jq .id)" >> "$GITHUB_OUTPUT" + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + STEPS_APP_TOKEN_OUTPUTS_APP_SLUG: ${{ steps.app-token.outputs.app-slug }} + + - name: Configure bot git identity + run: | + git config --global user.name "${STEPS_APP_TOKEN_OUTPUTS_APP_SLUG}[bot]" + git config --global user.email "${STEPS_GET_USER_ID_OUTPUTS_USER_ID}+${STEPS_APP_TOKEN_OUTPUTS_APP_SLUG}[bot]@users.noreply.github.com" + gh auth setup-git + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + STEPS_APP_TOKEN_OUTPUTS_APP_SLUG: ${{ steps.app-token.outputs.app-slug }} + STEPS_GET_USER_ID_OUTPUTS_USER_ID: ${{ steps.get-user-id.outputs.user-id }} + + - name: Commit and push if changed + if: steps.check.outcome == 'failure' + shell: bash + run: | + set -euo pipefail + if git diff --quiet; then + echo "No changes after fix hashes; nothing to commit." + exit 0 + fi + git add --update --ignore-removal . + git commit -m "chore(nix): auto-fix nix hashes" + git push origin HEAD:dev --force-with-lease diff --git a/.github/workflows/pr-build-dispatch.yml b/.github/workflows/pr-build-dispatch.yml new file mode 100644 index 0000000000..7a9454fede --- /dev/null +++ b/.github/workflows/pr-build-dispatch.yml @@ -0,0 +1,67 @@ +name: PR build dispatch + +# Member-only /build gate: dispatches pr-build.yml. +# /build -> android + ios +# /build android -> android only +# /build ios -> ios only +# Anyone without repo write access is ignored; the gate fails closed. +on: + issue_comment: + types: [created] + +permissions: + actions: write + +jobs: + dispatch: + if: github.event.issue.pull_request + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Parse /build command + id: cmd + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const body = (context.payload.comment.body || '').trim() + const match = /^\/build(?:\s+(android|ios|both))?\s*$/i.exec(body) + if (!match) { + core.setOutput('command', 'false') + return + } + core.setOutput('command', 'true') + core.setOutput('platform', (match[1] || 'both').toLowerCase()) + + - name: Check commenter is a member + id: perm + if: steps.cmd.outputs.command == 'true' + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { owner, repo } = context.repo + const login = context.payload.comment.user.login + try { + const { data } = await github.rest.repos.getCollaboratorPermissionLevel({ + owner, + repo, + username: login, + }) + const allowed = ['admin', 'maintain', 'write'].includes(data.permission) + core.info(`${login} has ${data.permission} -> ${allowed ? 'authorized' : 'not authorized'}`) + core.setOutput('allowed', String(allowed)) + } catch { + core.info(`${login} is not a collaborator -> denied`) + core.setOutput('allowed', 'false') + } + + - name: Dispatch PR build + if: ${{ steps.cmd.outputs.command == 'true' && steps.perm.outputs.allowed == 'true' }} + env: + GH_TOKEN: ${{ github.token }} + PR_NUMBER: ${{ github.event.issue.number }} + PLATFORM: ${{ steps.cmd.outputs.platform }} + run: | + gh workflow run pr-build.yml --ref dev \ + --field pr_number="$PR_NUMBER" \ + --field platform="$PLATFORM" + echo "Dispatched pr-build.yml for PR #$PR_NUMBER ($PLATFORM)." diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml new file mode 100644 index 0000000000..aff52eb7b6 --- /dev/null +++ b/.github/workflows/pr-build.yml @@ -0,0 +1,243 @@ +name: PR build + +# On-demand PR build (APK / unsigned IPA); dispatched by /build or run manually. +on: + workflow_dispatch: + inputs: + pr_number: + description: Pull request number to build + required: true + type: number + platform: + description: Platform to build + required: true + default: both + type: choice + options: + - android + - ios + - both + +permissions: + contents: read + issues: write + pull-requests: write + +concurrency: + group: pr-build-${{ inputs.pr_number }} + cancel-in-progress: false + +env: + CARGO_INCREMENTAL: 0 + CARGO_TERM_COLOR: always + SABLE_BUILD_FLAVOR: dev + VITE_APP_VERSION: 0.0.0-pr.${{ inputs.pr_number }} + +jobs: + resolve: + name: Resolve PR #${{ inputs.pr_number }} + runs-on: ubuntu-latest + timeout-minutes: 5 + outputs: + head_sha: ${{ steps.pr.outputs.head_sha }} + title: ${{ steps.pr.outputs.title }} + steps: + - name: Fetch pull request + id: pr + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { owner, repo } = context.repo + const { data: pull } = await github.rest.pulls.get({ + owner, + repo, + pull_number: ${{ inputs.pr_number }}, + }) + core.setOutput('head_sha', pull.head.sha) + core.setOutput('title', pull.title) + + build-android: + name: Build Android (PR #${{ inputs.pr_number }}) + needs: resolve + if: ${{ inputs.platform != 'ios' }} + runs-on: ubuntu-latest + timeout-minutes: 45 + env: + MISE_ENV: tauri + RUST_TARGETS: aarch64-linux-android armv7-linux-androideabi + ANDROID_KEY_BASE64: ${{ secrets.ANDROID_KEY_BASE64 }} + ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} + ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: refs/pull/${{ inputs.pr_number }}/merge + persist-credentials: false + + - name: Setup app + uses: ./.github/actions/setup + with: + tauri: 'true' + + - name: Setup Android SDK + NDK + shell: bash + run: mise run tauri:setup:android + + - name: Add Rust targets + shell: bash + run: rustup target add $RUST_TARGETS + + - name: Cache Rust build + uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 + with: + workspaces: src-tauri + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 + + - name: Resolve NDK + shell: bash + run: echo "NDK_HOME=$ANDROID_HOME/ndk/$ANDROID_NDK_VERSION" >> "$GITHUB_ENV" + + - name: Setup Android signing + if: ${{ env.ANDROID_KEY_BASE64 != '' }} + shell: bash + run: | + cd src-tauri/gen/android + echo "keyAlias=$ANDROID_KEY_ALIAS" > keystore.properties + echo "password=$ANDROID_KEY_PASSWORD" >> keystore.properties + base64 -d <<< "$ANDROID_KEY_BASE64" > "$RUNNER_TEMP/keystore.jks" + echo "storeFile=$RUNNER_TEMP/keystore.jks" >> keystore.properties + + - name: Build Android APK + shell: bash + run: | + if [ -n "${ANDROID_KEY_BASE64:-}" ]; then + pnpm tauri android build --config src-tauri/tauri.pr.conf.json --apk --target aarch64 armv7 + else + # No keystore secret available: build a debug APK (signed with the + # debug keystore) so members can still sideload the build. + pnpm tauri android build --config src-tauri/tauri.pr.conf.json --debug --apk --target aarch64 armv7 + fi + OUT='src-tauri/gen/android/app/build/outputs' + APK=$(find "$OUT/apk" -name '*.apk' -type f | head -1) + [ -n "$APK" ] || { echo "APK not found"; exit 1; } + mv "$APK" "$OUT/Sable-pr${{ inputs.pr_number }}-android.apk" + + - name: Upload APK + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: sable-pr${{ inputs.pr_number }}-android + path: src-tauri/gen/android/app/build/outputs/Sable-pr${{ inputs.pr_number }}-android.apk + if-no-files-found: error + retention-days: 7 + + build-ios: + name: Build iOS (PR #${{ inputs.pr_number }}) + needs: resolve + if: ${{ inputs.platform != 'android' }} + runs-on: macos-latest + timeout-minutes: 60 + env: + RUST_TARGETS: aarch64-apple-ios + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: refs/pull/${{ inputs.pr_number }}/merge + persist-credentials: false + + - name: Setup app + uses: ./.github/actions/setup + with: + tauri: 'true' + + - name: Add Rust targets + shell: bash + run: rustup target add $RUST_TARGETS + + - name: Cache Rust build + uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 + with: + workspaces: src-tauri + + - name: Set up iOS + shell: bash + run: mise -y run tauri:setup:ios + + - name: Download iOS frameworks + shell: bash + run: pnpm ios:frameworks + + - name: Symlink icons to the ios folder + shell: bash + run: mise run icons:symlink --write --force + + - name: Build unsigned IPA + shell: bash + run: | + pnpm tauri ios build --config src-tauri/tauri.pr.conf.json --no-sign --ci + IPA=$(find src-tauri/gen/apple/build -name '*.ipa' -type f | head -1) + [ -n "$IPA" ] || { echo "IPA not found"; exit 1; } + mv "$IPA" "$(dirname "$IPA")/Sable-pr${{ inputs.pr_number }}-ios.ipa" + + - name: Upload IPA + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: sable-pr${{ inputs.pr_number }}-ios + path: src-tauri/gen/apple/build/**/Sable-pr${{ inputs.pr_number }}-ios.ipa + if-no-files-found: error + retention-days: 7 + + comment: + name: Post build result comment + needs: [resolve, build-android, build-ios] + if: ${{ always() && !cancelled() && needs.resolve.result == 'success' }} + runs-on: ubuntu-latest + timeout-minutes: 5 + env: + HEAD_SHA: ${{ needs.resolve.outputs.head_sha }} + PR_TITLE: ${{ needs.resolve.outputs.title }} + steps: + - name: Update PR comment + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { owner, repo } = context.repo + const issue_number = ${{ inputs.pr_number }} + const marker = '' + const shortSha = process.env.HEAD_SHA.slice(0, 7) + const runUrl = `${context.serverUrl}/${owner}/${repo}/actions/runs/${context.runId}` + const status = (result) => + result === 'success' ? ':white_check_mark: success' + : result === 'failure' ? ':x: failure' + : result === 'cancelled' ? ':stop_sign: cancelled' + : ':fast_forward: skipped' + + const body = [ + marker, + '## PR build', + '', + '| Platform | Status |', + '| --- | --- |', + `| Android | ${status('${{ needs.build-android.result }}')} |`, + `| iOS | ${status('${{ needs.build-ios.result }}')} |`, + '', + `- [Download artifacts](${runUrl}#artifacts) — expire after 7 days.`, + `- Commit \`${shortSha}\`: ${process.env.PR_TITLE}`, + ].join('\n') + + const { data: comments } = await github.rest.issues.listComments({ owner, repo, issue_number }) + const previous = comments.find((comment) => comment.body?.includes(marker)) + + if (previous) { + await github.rest.issues.updateComment({ + owner, + repo, + comment_id: previous.id, + body, + }) + } else { + await github.rest.issues.createComment({ owner, repo, issue_number, body }) + } diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index e88da3a79b..29430ba8b6 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -16,118 +16,34 @@ jobs: steps: - name: Generate bot token id: generate-token - uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 with: app-id: ${{ secrets.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} + permission-contents: write + permission-pull-requests: write - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 persist-credentials: true token: ${{ steps.generate-token.outputs.token }} - - uses: fregante/setup-git-user@024bc0b8e177d7e77203b48dab6fb45666854b35 # v2.0.2 - - - uses: knope-dev/action@19617851f9f13ab2f27a05989c55efb18aca3675 # v2.1.2 + - name: Setup app + uses: ./.github/actions/setup with: - version: 0.22.3 + install-command: '' + + - uses: fregante/setup-git-user@024bc0b8e177d7e77203b48dab6fb45666854b35 # v2.0.2 - name: Prepare Release - run: knope prepare-release --verbose + run: mise run knope -- prepare-release --verbose env: GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }} - - name: Enrich changelog and update release PR - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - github-token: ${{ steps.generate-token.outputs.token }} - script: | - const fs = require("fs"); - const { owner, repo } = context.repo; - - async function resolveHash(hash) { - try { - const prs = await github.rest.repos.listPullRequestsAssociatedWithCommit({ - owner, repo, commit_sha: hash, - }); - const pr = prs.data[0]; - return pr - ? `([#${pr.number}](${pr.html_url}) by @${pr.user.login})` - : `(\`${hash}\`)`; - } catch { - return `(\`${hash}\`)`; - } - } - - async function enrichText(text) { - const marker = //g; - const hashes = [...new Set([...text.matchAll(marker)].map(m => m[1]))]; - for (const hash of hashes) { - text = text.replaceAll(``, await resolveHash(hash)); - } - return text; - } - - const changelog = "CHANGELOG.md"; - if (fs.existsSync(changelog)) { - const enriched = await enrichText(fs.readFileSync(changelog, "utf8")); - fs.writeFileSync(changelog, enriched); - } - - const prs = await github.rest.pulls.list({ - owner, repo, - head: `${owner}:release`, - state: "open", - }); - - if (prs.data.length === 0) { - core.warning("No open PR found for the release branch."); - return; - } - - const pr = prs.data[0]; - - const label = "internal"; - try { - await github.rest.issues.getLabel({ owner, repo, name: label }); - } catch (e) { - if (e.status === 404) { - await github.rest.issues.createLabel({ owner, repo, name: label, color: "e4e669" }); - } else { - throw e; - } - } - - await Promise.all([ - github.rest.pulls.update({ - owner, repo, - pull_number: pr.number, - body: await enrichText(pr.body ?? ""), - }), - github.rest.issues.addLabels({ - owner, repo, - issue_number: pr.number, - labels: [label], - }), - ]); - - - name: Amend release commit with enriched changelog - shell: bash + - name: Add internal label run: | - if [[ "$(git rev-parse --abbrev-ref HEAD)" != "release" ]]; then - echo "Not on release branch — skipping amend." - exit 1 - fi - - if git diff --cached --name-only | grep -q "CHANGELOG.md"; then - git add CHANGELOG.md - git commit --amend --no-edit - elif git diff --name-only HEAD | grep -q "CHANGELOG.md"; then - git add CHANGELOG.md - git commit --amend --no-edit - fi - - - name: Push release branch - shell: bash - run: git push --force --set-upstream origin release + PR_NUMBER=$(gh pr list --head release --json number --jq '.[0].number') + gh pr edit "$PR_NUMBER" --add-label internal + env: + GH_TOKEN: ${{ steps.generate-token.outputs.token }} diff --git a/.github/workflows/quality-checks.yml b/.github/workflows/quality-checks.yml index a9ac437082..b192f2a173 100644 --- a/.github/workflows/quality-checks.yml +++ b/.github/workflows/quality-checks.yml @@ -15,7 +15,7 @@ jobs: contents: read steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -33,14 +33,14 @@ jobs: contents: read steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Setup app uses: ./.github/actions/setup - - name: Run ESLint + - name: Run Lint run: pnpm run lint typecheck: @@ -51,7 +51,7 @@ jobs: contents: read steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -69,7 +69,7 @@ jobs: contents: read steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -87,7 +87,7 @@ jobs: contents: read steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -105,7 +105,7 @@ jobs: contents: read steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d3314e27ba..f0b4cab6e5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,22 +16,24 @@ jobs: steps: - name: Generate bot token id: generate-token - uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 with: app-id: ${{ secrets.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} + permission-contents: write - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 persist-credentials: true token: ${{ steps.generate-token.outputs.token }} - - uses: knope-dev/action@19617851f9f13ab2f27a05989c55efb18aca3675 # v2.1.2 + - name: Setup app + uses: ./.github/actions/setup with: - version: 0.22.3 + install-command: '' - name: Create Release - run: knope release --verbose + run: mise run knope -- release --verbose env: GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }} diff --git a/.github/workflows/require-changeset.yml b/.github/workflows/require-changeset.yml index 17dfda277c..06ef3b17be 100644 --- a/.github/workflows/require-changeset.yml +++ b/.github/workflows/require-changeset.yml @@ -28,7 +28,7 @@ jobs: echo "skip=false" >> "$GITHUB_OUTPUT" fi - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 if: steps.labels.outputs.skip == 'false' with: fetch-depth: 0 @@ -51,7 +51,7 @@ jobs: fi - name: Manage changeset comment - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: SKIP: ${{ steps.labels.outputs.skip }} CHANGESET_FOUND: ${{ steps.check.outputs.found }} diff --git a/.github/workflows/rust-quality.yml b/.github/workflows/rust-quality.yml new file mode 100644 index 0000000000..338c0d43d6 --- /dev/null +++ b/.github/workflows/rust-quality.yml @@ -0,0 +1,87 @@ +name: Rust quality checks + +on: + pull_request: + paths: + - 'src-tauri/**' + - '.github/workflows/rust-quality.yml' + push: + branches: [dev] + paths: + - 'src-tauri/**' + - '.github/workflows/rust-quality.yml' + merge_group: + +permissions: {} + +jobs: + format: + name: Rust format + runs-on: ubuntu-latest + permissions: + contents: read + env: + CARGO_INCREMENTAL: 0 + CARGO_TERM_COLOR: always + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Setup mise + uses: jdx/mise-action@3c2e0cf82a5b2e5249f0d3635a4d83d0ae861518 # v4.2.5 + with: + cache: true + + - name: Check Rust formatting + run: cargo fmt --manifest-path src-tauri/Cargo.toml -- --check + + checks: + name: Rust check, test, and Clippy + runs-on: ubuntu-22.04 + permissions: + contents: read + env: + CARGO_INCREMENTAL: 0 + CARGO_TERM_COLOR: always + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Install Tauri build dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + build-essential \ + libayatana-appindicator3-dev \ + libgtk-3-dev \ + librsvg2-dev \ + libsoup-3.0-dev \ + libssl-dev \ + libwebkit2gtk-4.1-dev \ + libxdo-dev \ + pkg-config + + - name: Setup mise + uses: jdx/mise-action@3c2e0cf82a5b2e5249f0d3635a4d83d0ae861518 # v4.2.5 + with: + cache: true + + - name: Cache Rust build + uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 + with: + workspaces: src-tauri + # Restore only on PRs; the repo sits at the 10 GiB cache cap. + save-if: ${{ github.ref == 'refs/heads/dev' }} + + - name: Check native app + run: cargo check --locked --manifest-path src-tauri/Cargo.toml + + - name: Run Rust tests + run: cargo test --locked --manifest-path src-tauri/Cargo.toml + + - name: Run Clippy + run: cargo clippy --locked --manifest-path src-tauri/Cargo.toml --all-targets -- -D warnings diff --git a/.github/workflows/sentry-preview-issues.yml b/.github/workflows/sentry-preview-issues.yml index c81787e743..8020ee7ffd 100644 --- a/.github/workflows/sentry-preview-issues.yml +++ b/.github/workflows/sentry-preview-issues.yml @@ -29,7 +29,7 @@ jobs: pull-requests: write steps: - name: Triage Sentry preview errors - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_ORG: ${{ secrets.SENTRY_ORG }} diff --git a/.github/workflows/tauri-build.yml b/.github/workflows/tauri-build.yml new file mode 100644 index 0000000000..88d56fcefb --- /dev/null +++ b/.github/workflows/tauri-build.yml @@ -0,0 +1,900 @@ +name: Build desktop apps + +on: + push: + branches: [dev] + tags: ['v*'] + workflow_dispatch: + inputs: + tag: + description: 'Release tag to build and attach desktop bundles to (e.g. v1.20.0)' + required: true + type: string + +permissions: {} + +concurrency: + group: tauri-build-${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }} + cancel-in-progress: ${{ github.ref == 'refs/heads/dev' }} + +env: + CARGO_INCREMENTAL: 0 + CARGO_TERM_COLOR: always + NODE_OPTIONS: --max-old-space-size=8192 + +jobs: + setup-release: + name: Resolve release target + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: write + if: "${{ !(github.event_name == 'push' && github.ref == 'refs/heads/dev' && contains(github.event.head_commit.message, 'chore: prepare release')) }}" + outputs: + tag: ${{ steps.meta.outputs.tag }} + version: ${{ steps.meta.outputs.version }} + ref: ${{ steps.meta.outputs.ref }} + nightly: ${{ steps.meta.outputs.nightly }} + started_at: ${{ steps.meta.outputs.started_at }} + env: + GH_TOKEN: ${{ github.token }} + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - uses: ./.github/actions/resolve-release-meta + id: meta + with: + input-tag: ${{ inputs.tag }} + + - name: Wait for release to exist + if: ${{ steps.meta.outputs.nightly != 'true' }} + shell: bash + env: + TAG: ${{ steps.meta.outputs.tag }} + run: | + for i in $(seq 1 12); do + if gh release view "$TAG" >/dev/null 2>&1; then + echo "Release $TAG found." + exit 0 + fi + echo "Waiting for release $TAG to be published ($i/12)…" + sleep 15 + done + echo "Release $TAG was not found." >&2 + exit 1 + + - name: Ensure nightly prerelease exists + if: ${{ steps.meta.outputs.nightly == 'true' }} + shell: bash + env: + GIT_SHA: ${{ github.sha }} + run: | + if gh release view nightly >/dev/null 2>&1; then + echo "Reusing the existing nightly release while the new build is prepared." + else + gh release create nightly \ + --prerelease \ + --target "$GIT_SHA" \ + --title "Nightly (dev)" \ + --notes "Rolling nightly build from dev — $GIT_SHA" + fi + + build: + name: Build ${{ matrix.name }} + needs: setup-release + runs-on: ${{ matrix.platform }} + timeout-minutes: 60 + permissions: + contents: write + id-token: write + attestations: write + artifact-metadata: write + outputs: + dmg_sha256: ${{ steps.dmg_sha.outputs.sha }} + strategy: + fail-fast: false + matrix: + include: + - name: macOS + platform: macos-latest + asset-platform: macos-universal + rust-targets: aarch64-apple-darwin x86_64-apple-darwin + args: --target universal-apple-darwin + bundle_path: src-tauri/target/universal-apple-darwin/release/bundle + - name: Windows + platform: windows-latest + asset-platform: windows-x86_64 + rust-targets: '' + args: '' + bundle_path: src-tauri/target/release/bundle + env: + TAG: ${{ needs.setup-release.outputs.tag }} + VERSION: ${{ needs.setup-release.outputs.version }} + VITE_APP_VERSION: ${{ needs.setup-release.outputs.version }} + SABLE_BUILD_FLAVOR: ${{ needs.setup-release.outputs.nightly == 'true' && 'dev' || 'stable' }} + VITE_SENTRY_DSN: ${{ secrets.VITE_SENTRY_DSN }} + VITE_SENTRY_ENVIRONMENT: ${{ needs.setup-release.outputs.nightly == 'true' && 'preview' || 'production' }} + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ needs.setup-release.outputs.ref }} + persist-credentials: false + + - name: Setup app + uses: ./.github/actions/setup + with: + tauri: 'true' + + - name: Add Rust targets + if: ${{ matrix.rust-targets != '' }} + shell: bash + run: rustup target add ${{ matrix.rust-targets }} + + - name: Cache Rust build + uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 + with: + workspaces: src-tauri + + - name: Stamp release version into tauri.conf.json + shell: bash + env: + UPDATER_ENDPOINT: ${{ needs.setup-release.outputs.nightly == 'true' && format('https://github.com/{0}/releases/download/nightly/latest.json', github.repository) || '' }} + run: node .github/scripts/set-tauri-version.mjs "$VERSION" "$UPDATER_ENDPOINT" + + - name: Configure build args + shell: bash + env: + IS_NIGHTLY: ${{ needs.setup-release.outputs.nightly }} + MATRIX_NAME: ${{ matrix.name }} + run: | + ARGS="" + if [ -z "${TAURI_SIGNING_PRIVATE_KEY:-}" ]; then + ARGS='--config {"bundle":{"createUpdaterArtifacts":false}}' + fi + if [ "$IS_NIGHTLY" = "true" ] && [ "$MATRIX_NAME" = "Windows" ]; then + ARGS="$ARGS --bundles nsis" + fi + if [ "$IS_NIGHTLY" = "true" ]; then + echo "SABLE_BUILD_FLAVOR=dev" >> "$GITHUB_ENV" + fi + echo "TAURI_BUILD_ARGS=${ARGS# }" >> "$GITHUB_ENV" + + - name: Build desktop bundles + shell: bash + run: pnpm tauri build ${{ matrix.args }} $TAURI_BUILD_ARGS + + - name: Normalize desktop artifact names + shell: bash + env: + ASSET_PLATFORM: ${{ matrix.asset-platform }} + BUNDLE_PATH: ${{ matrix.bundle_path }} + run: | + test -d "$BUNDLE_PATH" || { echo "Bundle directory not found: $BUNDLE_PATH" >&2; exit 1; } + find "$BUNDLE_PATH" -type f -print0 \ + | while IFS= read -r -d '' path; do + case "$path" in + *.app.tar.gz.sig) suffix='.app.tar.gz.sig' ;; + *.app.tar.gz) suffix='.app.tar.gz' ;; + *.nsis.zip.sig) suffix='.nsis.zip.sig' ;; + *.nsis.zip) suffix='.nsis.zip' ;; + *-setup.exe.sig) suffix='-setup.exe.sig' ;; + *-setup.exe) suffix='-setup.exe' ;; + *.msi.sig) suffix='.msi.sig' ;; + *.msi) suffix='.msi' ;; + *.dmg) suffix='.dmg' ;; + *) continue ;; + esac + target="$(dirname "$path")/Sable-${VERSION}-${ASSET_PLATFORM}${suffix}" + [ "$path" = "$target" ] || mv "$path" "$target" + done + + - name: Attest Windows bundles + if: ${{ matrix.name == 'Windows' }} + uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 + with: + subject-path: | + src-tauri/target/release/bundle/msi/*.msi + src-tauri/target/release/bundle/msi/*.sig + src-tauri/target/release/bundle/nsis/*.exe + src-tauri/target/release/bundle/nsis/*.sig + + - name: Attach bundles to release + shell: bash + env: + GH_TOKEN: ${{ github.token }} + BUNDLE_PATH: ${{ matrix.bundle_path }} + run: | + test -d "$BUNDLE_PATH" || { echo "Bundle directory not found: $BUNDLE_PATH" >&2; exit 1; } + find "$BUNDLE_PATH" -type f -name 'Sable-*' -print0 \ + | while IFS= read -r -d '' path; do + echo "Uploading $path" + gh release upload "$TAG" "$path" --clobber + done + + - name: Compute DMG checksum + id: dmg_sha + if: ${{ matrix.name == 'macOS' }} + shell: bash + env: + BUNDLE_PATH: ${{ matrix.bundle_path }} + run: | + DMG="$(find "$BUNDLE_PATH" -name "Sable-${VERSION}-macos-universal.dmg" -type f | head -1)" + [ -n "$DMG" ] || { echo "DMG not found in $BUNDLE_PATH" >&2; exit 1; } + SHA="$(sha256sum "$DMG" | awk '{print $1}')" + echo "sha=$SHA" >> "$GITHUB_OUTPUT" + + android: + name: Build Android + needs: setup-release + runs-on: ubuntu-latest + timeout-minutes: 45 + permissions: + contents: write + id-token: write + attestations: write + artifact-metadata: write + env: + TAG: ${{ needs.setup-release.outputs.tag }} + VERSION: ${{ needs.setup-release.outputs.version }} + VITE_APP_VERSION: ${{ needs.setup-release.outputs.version }} + SABLE_BUILD_FLAVOR: ${{ needs.setup-release.outputs.nightly == 'true' && 'dev' || 'stable' }} + VITE_SENTRY_DSN: ${{ secrets.VITE_SENTRY_DSN }} + VITE_SENTRY_ENVIRONMENT: ${{ needs.setup-release.outputs.nightly == 'true' && 'preview' || 'production' }} + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} + MISE_ENV: tauri + # Matches the ABIs passed to `tauri android build` below. + RUST_TARGETS: aarch64-linux-android armv7-linux-androideabi + ANDROID_KEY_BASE64: ${{ secrets.ANDROID_KEY_BASE64 }} + ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} + ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ needs.setup-release.outputs.ref }} + persist-credentials: false + + - name: Setup app + uses: ./.github/actions/setup + with: + tauri: 'true' + + - name: Setup Android SDK + NDK + shell: bash + run: mise run tauri:setup:android + + - name: Add Rust targets + shell: bash + run: rustup target add $RUST_TARGETS + + - name: Cache Rust build + uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 + with: + workspaces: src-tauri + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 + + - name: Resolve NDK + shell: bash + run: | + echo "NDK_HOME=$ANDROID_HOME/ndk/$ANDROID_NDK_VERSION" >> "$GITHUB_ENV" + + - name: Stamp release version into tauri.conf.json + shell: bash + run: node .github/scripts/set-tauri-version.mjs "$VERSION" + + - name: Generate nightly Android project + if: ${{ needs.setup-release.outputs.nightly == 'true' }} + shell: bash + run: | + # Tauri requires source files at the configured package path. Overlay the + # nightly package rather than regenerating and losing tracked customizations. + APP_DIR=src-tauri/gen/android/app + NIGHTLY_PACKAGE_DIR="$APP_DIR/src/main/java/moe/sable/client/nightly" + NIGHTLY_SOURCE_DIR="$RUNNER_TEMP/android-nightly-package" + cp -a "$APP_DIR/src/main/java/moe/sable/client" "$NIGHTLY_SOURCE_DIR" + find "$NIGHTLY_SOURCE_DIR" -type f -exec sed -i \ + 's/moe\.sable\.client/moe.sable.client.nightly/g' {} + + find "$APP_DIR/src/main/java/moe/sable/client" -mindepth 1 -maxdepth 1 \ + ! -name nightly -exec rm -rf {} + + mv "$NIGHTLY_SOURCE_DIR" "$NIGHTLY_PACKAGE_DIR" + sed -i 's/moe\.sable\.client/moe.sable.client.nightly/g' \ + "$APP_DIR/build.gradle.kts" "$APP_DIR/src/main/AndroidManifest.xml" + + - name: Setup Android signing + if: ${{ env.ANDROID_KEY_BASE64 != '' }} + shell: bash + run: | + cd src-tauri/gen/android + echo "keyAlias=$ANDROID_KEY_ALIAS" > keystore.properties + echo "password=$ANDROID_KEY_PASSWORD" >> keystore.properties + base64 -d <<< "$ANDROID_KEY_BASE64" > "$RUNNER_TEMP/keystore.jks" + echo "storeFile=$RUNNER_TEMP/keystore.jks" >> keystore.properties + + - name: Build Android bundles + shell: bash + env: + IS_NIGHTLY: ${{ needs.setup-release.outputs.nightly }} + run: | + if [ "$IS_NIGHTLY" = "true" ]; then + export SABLE_BUILD_FLAVOR=dev + fi + CONFIG_ARGS="" + [ "$IS_NIGHTLY" = "true" ] && CONFIG_ARGS="--config src-tauri/tauri.nightly.conf.json" + pnpm tauri android build $CONFIG_ARGS --apk --aab --target aarch64 armv7 + OUT='src-tauri/gen/android/app/build/outputs' + APK=$(find "$OUT/apk/universal/release" -name '*.apk' -type f | head -1) + AAB=$(find "$OUT/bundle/universalRelease" -name '*.aab' -type f | head -1) + [ -n "$APK" ] || { echo 'APK not found' >&2; exit 1; } + [ -n "$AAB" ] || { echo 'AAB not found' >&2; exit 1; } + mv "$APK" "$OUT/apk/universal/release/Sable-${VERSION}-android-universal.apk" + mv "$AAB" "$OUT/bundle/universalRelease/Sable-${VERSION}-android-universal.aab" + + - name: Attest Android bundles + uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 + with: + subject-path: | + src-tauri/gen/android/app/build/outputs/apk/universal/release/*.apk + src-tauri/gen/android/app/build/outputs/bundle/universalRelease/*.aab + + - name: Attach Android bundles to release + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: | + OUT="src-tauri/gen/android/app/build/outputs" + for f in "$OUT"/apk/universal/release/*.apk \ + "$OUT"/bundle/universalRelease/*.aab; do + [ -e "$f" ] || continue + echo "Uploading $f" + gh release upload "$TAG" "$f" --clobber + done + + obtainium: + name: Publish Obtainium config + needs: [setup-release, android] + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + contents: write + env: + TAG: ${{ needs.setup-release.outputs.tag }} + VERSION: ${{ needs.setup-release.outputs.version }} + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ needs.setup-release.outputs.ref }} + persist-credentials: false + + - name: Generate obtainium.json + shell: bash + run: node scripts/obtainium-generate.js "$VERSION" "$TAG" obtainium.json + + - name: Attach Obtainium config to release + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: gh release upload "$TAG" obtainium.json --clobber + + ios: + name: Build iOS (AltStore/SideStore) + needs: setup-release + runs-on: macos-latest + timeout-minutes: 60 + permissions: + contents: write + id-token: write + attestations: write + artifact-metadata: write + env: + TAG: ${{ needs.setup-release.outputs.tag }} + VERSION: ${{ needs.setup-release.outputs.version }} + VITE_APP_VERSION: ${{ needs.setup-release.outputs.version }} + SABLE_BUILD_FLAVOR: ${{ needs.setup-release.outputs.nightly == 'true' && 'dev' || 'stable' }} + VITE_SENTRY_DSN: ${{ secrets.VITE_SENTRY_DSN }} + VITE_SENTRY_ENVIRONMENT: ${{ needs.setup-release.outputs.nightly == 'true' && 'preview' || 'production' }} + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} + RUST_TARGETS: aarch64-apple-ios + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ needs.setup-release.outputs.ref }} + persist-credentials: false + + - name: Setup app + uses: ./.github/actions/setup + with: + tauri: 'true' + + - name: Add Rust targets + shell: bash + run: rustup target add $RUST_TARGETS + + - name: Cache Rust build + uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 + with: + workspaces: src-tauri + + - name: Stamp release version into tauri.conf.json + shell: bash + env: + IS_NIGHTLY: ${{ needs.setup-release.outputs.nightly }} + run: | + if [ "$IS_NIGHTLY" = "true" ]; then + node .github/scripts/set-tauri-version.mjs "$VERSION" --apple-short-version + else + node .github/scripts/set-tauri-version.mjs "$VERSION" + fi + + - name: Set up iOS + shell: bash + env: + IS_NIGHTLY: ${{ needs.setup-release.outputs.nightly }} + run: | + if [ "$IS_NIGHTLY" = "true" ]; then + rm -rf src-tauri/gen/apple + command -v pod >/dev/null 2>&1 || brew install cocoapods + pnpm tauri ios init --ci --config src-tauri/tauri.nightly.conf.json + else + mise -y run tauri:setup:ios + fi + + - name: Download iOS frameworks + shell: bash + run: pnpm ios:frameworks + + - name: Symlink icons to the ios folder + shell: bash + run: mise run icons:symlink --write --force + + - name: Build unsigned IPA + id: ipa + shell: bash + env: + IS_NIGHTLY: ${{ needs.setup-release.outputs.nightly }} + run: | + if [ "$IS_NIGHTLY" = "true" ]; then + export SABLE_BUILD_FLAVOR=dev + fi + CONFIG_ARGS="" + [ "$IS_NIGHTLY" = "true" ] && CONFIG_ARGS="--config src-tauri/tauri.nightly.conf.json" + pnpm tauri ios build $CONFIG_ARGS --no-sign --ci + IPA=$(find src-tauri/gen/apple/build -name '*.ipa' -type f | head -1) + [ -n "$IPA" ] || { echo "IPA not found"; ls -R src-tauri/gen/apple/build 2>/dev/null || true; exit 1; } + NORMALIZED_IPA="$(dirname "$IPA")/Sable-${VERSION}-ios-arm64.ipa" + [ "$IPA" = "$NORMALIZED_IPA" ] || mv "$IPA" "$NORMALIZED_IPA" + IPA="$NORMALIZED_IPA" + unzip -p "$IPA" 'Payload/*.app/Info.plist' >Info.plist + # version - CFBundleShortVersionString, buildVersion - CFBundleVersion : https://faq.altstore.io/developers/make-a-source#app-versions + IPA_VERSION="$(plutil -extract CFBundleShortVersionString raw -o - Info.plist 2>/dev/null || true)" + IPA_BUILD="$(plutil -extract CFBundleVersion raw -o - Info.plist 2>/dev/null || true)" + echo "size=$(stat -f%z "$IPA")" >> "$GITHUB_OUTPUT" + echo "path=$IPA" >> "$GITHUB_OUTPUT" + echo "name=$(basename "$IPA")" >> "$GITHUB_OUTPUT" + echo "ipa_version=${IPA_VERSION:-$VERSION}" >> "$GITHUB_OUTPUT" + echo "ipa_build=${IPA_BUILD:-$VERSION}" >> "$GITHUB_OUTPUT" + + - name: Attest iOS bundle + uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 + with: + subject-path: ${{ steps.ipa.outputs.path }} + + - name: Attach IPA to release + shell: bash + env: + GH_TOKEN: ${{ github.token }} + STEPS_IPA_OUTPUTS_PATH: ${{ steps.ipa.outputs.path }} + run: gh release upload "$TAG" "${STEPS_IPA_OUTPUTS_PATH}" --clobber + + - name: Update AltStore/SideStore source + shell: bash + env: + GH_TOKEN: ${{ github.token }} + REPO: ${{ github.repository }} + NIGHTLY: ${{ needs.setup-release.outputs.nightly }} + ALTSTORE_VERSION: ${{ steps.ipa.outputs.ipa_version }} + ALTSTORE_VERSION_BUILD: ${{ steps.ipa.outputs.ipa_build }} + STEPS_IPA_OUTPUTS_NAME: ${{ steps.ipa.outputs.name }} + STEPS_IPA_OUTPUTS_SIZE: ${{ steps.ipa.outputs.size }} + run: | + set -euo pipefail + if [ "$NIGHTLY" = "true" ]; then + SOURCE_FILE="altstore-source-nightly.json" + # Superseded nightly IPAs get deleted, so older entries would be dead links. + export ALTSTORE_MAX_VERSIONS=1 + else + SOURCE_FILE="altstore-source.json" + fi + DOWNLOAD_URL="https://github.com/$REPO/releases/download/$TAG/${STEPS_IPA_OUTPUTS_NAME}" + DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" + MANIFEST="$RUNNER_TEMP/$SOURCE_FILE" + + if [ "$NIGHTLY" = "true" ]; then + cp ".github/templates/$SOURCE_FILE" "$MANIFEST" + else + # Preserve the stable rollback history from the latest prior release. + PREVIOUS_TAG="$( + gh release list --limit 100 --exclude-pre-releases --json tagName --jq \ + '.[] | select(.tagName != env.TAG) | .tagName' \ + | while read -r candidate; do + if gh release view "$candidate" --json assets --jq '.assets[].name' | grep -qx "$SOURCE_FILE"; then + echo "$candidate" + break + fi + done + )" + if [ -n "$PREVIOUS_TAG" ]; then + gh release download "$PREVIOUS_TAG" --pattern "$SOURCE_FILE" --output "$MANIFEST" + else + cp ".github/templates/$SOURCE_FILE" "$MANIFEST" + fi + fi + + node .github/scripts/update-altstore-source.mjs \ + "$MANIFEST" "${ALTSTORE_VERSION:-$VERSION}" "${ALTSTORE_VERSION_BUILD:-$VERSION}" "${STEPS_IPA_OUTPUTS_SIZE}" "$DOWNLOAD_URL" "$DATE" "Sable $VERSION" + pnpm oxfmt "$MANIFEST" + gh release upload "$TAG" "$MANIFEST#$SOURCE_FILE" --clobber + if [ "$NIGHTLY" = "true" ]; then + # Existing nightly sources point at releases/download/nightly/altstore-source.json. + cp "$MANIFEST" "$RUNNER_TEMP/altstore-source.json" + gh release upload "$TAG" "$RUNNER_TEMP/altstore-source.json" --clobber + fi + + updater-manifest: + name: Publish updater manifest + needs: [setup-release, build, linux, android] + if: ${{ always() && needs.setup-release.result == 'success' && needs.build.result == 'success' && needs.linux.result == 'success' }} + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: write + id-token: write + attestations: write + artifact-metadata: write + env: + TAG: ${{ needs.setup-release.outputs.tag }} + VERSION: ${{ needs.setup-release.outputs.version }} + VITE_APP_VERSION: ${{ needs.setup-release.outputs.version }} + SABLE_BUILD_FLAVOR: ${{ needs.setup-release.outputs.nightly == 'true' && 'dev' || 'stable' }} + REPO: ${{ github.repository }} + STARTED_AT: ${{ needs.setup-release.outputs.started_at }} + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ needs.setup-release.outputs.ref }} + persist-credentials: false + + - name: Build updater manifest + id: manifest + shell: bash + env: + GH_TOKEN: ${{ github.token }} + IS_NIGHTLY: ${{ needs.setup-release.outputs.nightly }} + run: | + node .github/scripts/build-updater-manifest.mjs + if [ -f latest.json ]; then + echo "produced=true" >> "$GITHUB_OUTPUT" + else + echo "produced=false" >> "$GITHUB_OUTPUT" + echo "No updater manifest produced (no signed artifacts); skipping attestation and upload." + fi + + - name: Attest updater manifest + if: steps.manifest.outputs.produced == 'true' + uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 + with: + subject-path: latest.json + + - name: Finalize nightly release + if: ${{ needs.setup-release.outputs.nightly == 'true' && steps.manifest.outputs.produced == 'true' }} + shell: bash + env: + GH_TOKEN: ${{ github.token }} + GIT_SHA: ${{ needs.setup-release.outputs.ref }} + run: | + gh api --method PATCH "repos/$REPO/git/refs/tags/nightly" \ + -f sha="$GIT_SHA" -F force=true + gh release edit nightly \ + --prerelease \ + --target "$GIT_SHA" \ + --title "Nightly (dev)" \ + --notes "Rolling nightly build $VERSION from dev — $GIT_SHA" + + - name: Upload updater manifest + if: steps.manifest.outputs.produced == 'true' + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: gh release upload "$TAG" latest.json --clobber + + - name: Remove superseded nightly assets + if: ${{ needs.setup-release.outputs.nightly == 'true' && needs.android.result == 'success' && steps.manifest.outputs.produced == 'true' }} + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: | + # .json manifests are overwritten in place, not accumulated. + gh release view nightly --json assets \ + | jq -r --arg cutoff "$STARTED_AT" \ + '.assets[] | select(.updatedAt <= $cutoff and (.name | endswith(".json") | not)) | .name' \ + | while read -r name; do + [ -n "$name" ] || continue + echo "Removing superseded nightly asset: $name" + gh release delete-asset nightly "$name" --yes + done + + # Nightlies use `--snapshot`, which packages without publishing. + linux: + name: Build Linux ${{ matrix.arch }} (CEF) + needs: setup-release + runs-on: ${{ matrix.runner }} + timeout-minutes: 90 + strategy: + fail-fast: false + matrix: + include: + - arch: x86_64 + runner: ubuntu-22.04 + - arch: aarch64 + runner: ubuntu-22.04-arm + permissions: + contents: write + id-token: write + attestations: write + artifact-metadata: write + env: + ARCH: ${{ matrix.arch }} + TAG: ${{ needs.setup-release.outputs.tag }} + VERSION: ${{ needs.setup-release.outputs.version }} + VITE_APP_VERSION: ${{ needs.setup-release.outputs.version }} + SABLE_BUILD_FLAVOR: ${{ needs.setup-release.outputs.nightly == 'true' && 'dev' || 'stable' }} + VITE_SENTRY_DSN: ${{ secrets.VITE_SENTRY_DSN }} + VITE_SENTRY_ENVIRONMENT: ${{ needs.setup-release.outputs.nightly == 'true' && 'preview' || 'production' }} + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} + IS_NIGHTLY: ${{ needs.setup-release.outputs.nightly }} + MISE_ENV: tauri + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ needs.setup-release.outputs.ref }} + persist-credentials: false + + - name: Setup app + uses: ./.github/actions/setup + with: + tauri: 'true' + + - name: Cache Rust build + uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 + with: + workspaces: src-tauri + key: ${{ matrix.arch }} + + - name: Stamp release version into tauri.conf.json + shell: bash + env: + UPDATER_ENDPOINT: ${{ needs.setup-release.outputs.nightly == 'true' && format('https://github.com/{0}/releases/download/nightly/latest.json', github.repository) || '' }} + run: node .github/scripts/set-tauri-version.mjs "$VERSION" "$UPDATER_ENDPOINT" + + - name: Build and package Linux bundles + shell: bash + run: | + pnpm tauri:cef build + ! strings src-tauri/target/release/sable | grep -F 'http://localhost:8080' + cp src-tauri/target/release/sable src-tauri/target/release/sable-updater + pnpm tauri:cef build --no-updater + + DISPLAY_NAME=Sable + [ "$IS_NIGHTLY" = "true" ] && DISPLAY_NAME="Sable Nightly" + mise run cef:package "$VERSION" \ + src-tauri/target/release/sable "$DISPLAY_NAME" \ + src-tauri/target/release/sable-updater + + DEB="src-tauri/target/release/bundle/deb/Sable-${VERSION}-linux-${ARCH}.deb" + dpkg-deb --contents "$DEB" | grep 'etc/apparmor.d/sable$' >/dev/null + dpkg-deb --ctrl-tarfile "$DEB" | tar -t | grep -x './postinst' >/dev/null + dpkg-deb --ctrl-tarfile "$DEB" | tar -t | grep -x './prerm' >/dev/null + + RELEASE_DIR="src-tauri/target/release/cef-pkg/release" + mkdir -p "$RELEASE_DIR" + cp src-tauri/target/release/sable "$RELEASE_DIR/sable" + cp -a src-tauri/target/release/cef-pkg/stage/runtime \ + src-tauri/target/release/cef-pkg/stage/share "$RELEASE_DIR/" + test -f "$RELEASE_DIR/runtime/CEF-LICENSE.txt" + tar -C "$RELEASE_DIR" -czf \ + "src-tauri/target/release/bundle/Sable-${VERSION}-linux-${ARCH}.tar.gz" \ + sable runtime share + + - name: Sign the AppImage for the updater + if: ${{ env.TAURI_SIGNING_PRIVATE_KEY != '' }} + shell: bash + run: | + for f in src-tauri/target/release/bundle/appimage/Sable-*-linux-${ARCH}.AppImage; do + [ -e "$f" ] || continue + pnpm tauri signer sign "$f" + done + + - name: Attest Linux bundles + uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 + with: + subject-path: | + src-tauri/target/release/bundle/deb/*.deb + src-tauri/target/release/bundle/rpm/*.rpm + src-tauri/target/release/bundle/*.tar.gz + src-tauri/target/release/bundle/appimage/*.AppImage + src-tauri/target/release/bundle/appimage/*.sig + + - name: Attach Linux packages to release + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: | + for f in src-tauri/target/release/bundle/deb/Sable-*.deb \ + src-tauri/target/release/bundle/rpm/Sable-*.rpm \ + src-tauri/target/release/bundle/Sable-*-linux-${ARCH}.tar.gz \ + src-tauri/target/release/bundle/appimage/Sable-*-linux-${ARCH}.AppImage \ + src-tauri/target/release/bundle/appimage/Sable-*-linux-${ARCH}.AppImage.sig; do + [ -e "$f" ] || continue + echo "Uploading $f" + gh release upload "$TAG" "$f" --clobber + done + + - name: Upload .deb as workflow artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: sable-linux-deb-${{ matrix.arch }} + path: src-tauri/target/release/bundle/deb/Sable-*.deb + if-no-files-found: error + retention-days: 1 + include-hidden-files: true + + distribute-nightly-aur: + name: Publish nightly to AUR + needs: [setup-release, linux] + runs-on: ubuntu-latest + timeout-minutes: 10 + if: ${{ needs.setup-release.outputs.nightly == 'true' }} + permissions: + contents: read + env: + TAG: ${{ needs.setup-release.outputs.tag }} + VERSION: ${{ needs.setup-release.outputs.version }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Download .deb artifact and compute checksum + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: sable-linux-deb-x86_64 + path: deb-artifact + + - name: Compute checksum + shell: bash + run: | + set -- deb-artifact/Sable-*-linux-x86_64.deb + [ "$#" -eq 1 ] && [ -e "$1" ] || { echo "Expected exactly one .deb, got $*" >&2; exit 1; } + echo "SHA256=$(sha256sum "$1" | awk '{print $1}')" >> "$GITHUB_ENV" + + - name: Push to AUR sable-nightly-bin + shell: bash + env: + AUR_KEY: ${{ secrets.AUR_KEY }} + run: | + if [ -z "${AUR_KEY:-}" ]; then + echo "::notice::AUR_KEY is not set; skipping the AUR publish." + exit 0 + fi + mkdir -p ~/.ssh + printf '%s\n' "$AUR_KEY" > ~/.ssh/aur_key + chmod 600 ~/.ssh/aur_key + ssh-keyscan -t ed25519 aur.archlinux.org >> ~/.ssh/known_hosts + export GIT_SSH_COMMAND="ssh -i $HOME/.ssh/aur_key -o IdentitiesOnly=yes" + + git clone "ssh://aur@aur.archlinux.org/sable-nightly-bin.git" aur-repo + cp packaging/aur/sable-nightly-bin.PKGBUILD aur-repo/PKGBUILD + cp packaging/aur/sable-nightly-bin.install aur-repo/sable-nightly-bin.install + + cd aur-repo + # pkgver forbids '-'; _relver keeps the asset URL's form. + sed -i \ + -e "s/^pkgver=.*/pkgver=${VERSION//-/.}/" \ + -e "s/^pkgrel=.*/pkgrel=1/" \ + -e "s/^_relver=.*/_relver=${VERSION}/" \ + -e "s/^sha256sums_x86_64=.*/sha256sums_x86_64=('${SHA256}')/" \ + PKGBUILD + + # makepkg refuses to run as root. + docker run --rm -v "$PWD:/src:ro" archlinux:base-devel bash -c ' + useradd -m build + cp /src/PKGBUILD /home/build/ + cp /src/sable-nightly-bin.install /home/build/ + su build -c "cd /home/build && makepkg --printsrcinfo" + ' > .SRCINFO + + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add PKGBUILD sable-nightly-bin.install .SRCINFO + if git diff --cached --quiet; then + echo "Nothing changed; skipping the push." + exit 0 + fi + git commit -m "nightly ${VERSION}" + git push + + distribute-homebrew: + name: Publish Homebrew cask + needs: [setup-release, build] + runs-on: ubuntu-latest + timeout-minutes: 10 + # Casks track stable releases. + if: ${{ needs.setup-release.outputs.nightly != 'true' }} + permissions: + contents: read + env: + TAG: ${{ needs.setup-release.outputs.tag }} + VERSION: ${{ needs.setup-release.outputs.version }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Resolve DMG checksum + shell: bash + env: + DMG_SHA256: ${{ needs.build.outputs.dmg_sha256 }} + run: | + [ -n "${DMG_SHA256:-}" ] || { echo "DMG sha256 is empty" >&2; exit 1; } + echo "SHA256=$DMG_SHA256" >> "$GITHUB_ENV" + + - name: Push the cask to the tap + shell: bash + env: + HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} + run: | + if [ -z "${HOMEBREW_TAP_TOKEN:-}" ]; then + echo "::notice::HOMEBREW_TAP_TOKEN is not set; skipping the Homebrew publish." + exit 0 + fi + git clone --depth 1 \ + "https://x-access-token:${HOMEBREW_TAP_TOKEN}@github.com/SableClient/homebrew-sable.git" tap + + mkdir -p tap/Casks + sed \ + -e "s/^ version .*/ version \"${VERSION}\"/" \ + -e "s/^ sha256 .*/ sha256 \"${SHA256}\"/" \ + packaging/homebrew/sable.rb > tap/Casks/sable.rb + + cd tap + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add Casks/sable.rb + if git diff --cached --quiet; then + echo "Nothing changed; skipping the push." + exit 0 + fi + git commit -m "sable ${VERSION}" + # The tap may still be an empty repository on the first run. + git push origin HEAD:refs/heads/main diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 0000000000..191a75c29d --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,4 @@ +rules: + cache-poisoning: + remap: + severity: informational diff --git a/.gitignore b/.gitignore index 7ec7197097..97eb818375 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,13 @@ experiment dist +.goreleaser-dist coverage node_modules devAssets .DS_Store .idea - +*.code-workspace .env .wrangler @@ -24,5 +25,47 @@ build.sh # the following line was added with the "git ignore" tool by itsrye.dev, version 0.1.0 .lh +# Tauri +/build +/target/ +/gen/schemas +dist-js + +# Gradle / Android build artifacts +**/android/build/ +**/android/.tauri/ +**/android/.gradle/ +**/.gradle/ + +# Rust build artifacts +src-tauri/target/ +# Tauri Android build outputs +src-tauri/gen/android/app/build/ +# Tauri Apple outputs +src-tauri/gen/apple/* +# Tauri Android release keystore +*.jks + +# Tauri-typegen cache +.typecache + +# Worktrees +.worktrees + # the following line was added with nvim by Shea because its annoying to clear every so often .vscode/bookmarks.json + +# Playwright visual regression +/test-results +/playwright-report +/blob-report +/tests/e2e/.auth + +# Nix things +## nix build output +result +## direnv config + local state +.envrc +.direnv +## auto-generated pre-commit config +.pre-commit-config.yaml diff --git a/.node-version b/.node-version deleted file mode 100644 index 32f8c50de0..0000000000 --- a/.node-version +++ /dev/null @@ -1 +0,0 @@ -24.13.1 diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/.nvmrc b/.nvmrc deleted file mode 100644 index a4a7a41bca..0000000000 --- a/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -v24.14.0 diff --git a/.vscode/settings.json b/.vscode/settings.json index f2ceb43b58..413bba80de 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -12,5 +12,20 @@ }, "[json]": { "editor.defaultFormatter": "oxc.oxc-vscode" - } + }, + "[rust]": { + "editor.defaultFormatter": "rust-lang.rust-analyzer" + }, + "nixEnvSelector.nixFile": "${workspaceFolder}/flake.nix", + "nixEnvSelector.useFlakes": true, + "explorer.fileNesting.enabled": true, + "explorer.fileNesting.patterns": { + "package.json": "pnpm-*.yaml, yarn.lock, package-lock.json, .npmrc, .nvmrc, .node-version", + "tsconfig.json": "tsconfig.*.json", + "Dockerfile": ".dockerignore", + ".env": ".env.*", + "flake.nix": "flake.lock", + ".gitattributes": ".gitignore" + }, + "i18n-ally.localesPaths": ["public/locales"] } diff --git a/CHANGELOG.md b/CHANGELOG.md index 5695f023ce..32effa06e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,450 @@ # Sable Client Changelog +## 1.21.0 (2026-08-17) + +### Features + +* Add the `forum` room type with a dedicated forum view that presents threads as topics. by @smokku in #980 +* New composer commands: `/gif` opens the GIF picker and `/rainbowme` sends rainbow-formatted action messages (#1109). by @eleboucher in #1573 +* Add selectable app icons on Android and iOS. by @eleboucher in #1594 +* Add MSC4466 profile sync controls for supported homeservers. by @7w1 in #1117 +* Add experimental new calls using LiveKit on web, desktop, and supported mobile devices, enabled by a single setting with Element Call as the fallback. by @eleboucher in #1575 +* Add toggleable, reorderable composer trigger buttons (emoji, gif, sticker) with a drag-and-drop settings list. by @eleboucher in #1261 +* Settings improvements: search the settings sidebar and customize keyboard shortcuts (#1121). by @eleboucher in #1660 +* Add an accessibility settings tab with color correction filters. by @j0lol in #1664 +* Persona improvements: pick a persona directly in the editor, an account-wide persona toggle, latching shorthands, persona name colors, and a redesigned persona settings menu (#1223, #1264, #1368, #1369, #1435, #1462). by @j0lol in #1171 +* Style tables in formatted messages. by @Steffo99 in #1580 +* New bottom sheets across the app: attach files and pick emoji from sheets in the composer, plus a room details sheet on small screens (#1173, #1433). by @eleboucher in #1234 +* Update the device display name after login to reflect the current client and build flavor. by @hazre in #1458 +* Replaced the connection status top bar with a small styled popup and added a variant that shows sliding sync initial progress. by @7w1 in #1200 +* Add per-message profiles (MSC4461): a different name and avatar in each room, with PluralKit import, shorthands, backup and restore, editing sent messages, and profile info on user profiles (#1352, #1479, #1586, #1611, #1651, #1701). by @j0lol in #1589 +* The notifications inbox is now built from push rules evaluated on this device instead of the server's `/notifications` endpoint, so mentions in encrypted rooms are detected correctly. by @eleboucher in #1372 +* Add beta desktop and mobile builds with a greatly improved mobile experience, UnifiedPush support and more reliable notifications, better desktop shortcuts, updates and window controls, and improved native media handling and sharing. by @eleboucher in #1103 + +### Fixes + +* Publish an `obtainium.json` app config with every release so Android builds can be installed and auto-updated through Obtainium. by @eleboucher in #1616 +* Performance: faster startup and warm syncs by caching room metadata, profile data, and device verification, plus lower memory use by pruning inactive room history (#1127, #1184, #1305, #1466). by @7w1 in #1118 +* Login and account fixes: full-URL homeservers no longer land on an empty room, a retry button when discovery fails, OAuth device verification no longer hangs, identity reset completes, key backup restores on verified devices, and account switch or logout from the offline screen (#1115, #1270, #1348, #1379, #1380). by @eleboucher in #1582 +* Media fixes: encrypted images and GIFs render again, playback works on Firefox, better loading on slow connections, decrypted media is freed from memory, gzipped Lottie support, stickers send reliably, and notification sounds no longer play as media (#1104, #1296, #1331, #1332, #1334, #1353, #1381). by @eleboucher in #1271 +* Fix link previews leaving empty space in a message until it is reloaded by @eleboucher in #1827 +* Message formatting fixes: checklists no longer render as raw HTML, spoilers work with proxy tags, maps with malformed coordinates no longer crash the app, and membership reasons are wrapped in parentheses (#1248, #1307, #1470). by @eleboucher in #1354 +* Fix context menus closing instead of opening, keep the room options sheet open, and let sidebar, space, and image viewer menus open on long-press as well as right-click (#1336, #1358). by @eleboucher in #1431 +* Assorted fixes: popups on start appear one at a time, room leave updates immediately, accepted invites stop reappearing, synced CSS tweaks are restored, the scheduled-messages chip shows again, faster recovery from network switches, plus small visual and formatting fixes (#1114, #1120, #1146, #1200, #1220, #1284, #1514, #1656). by @eleboucher in #1399 +* Profile fixes: hero colours and full scrolling restored on profile sheets, local time respects the 24-hour setting, devices stay sorted when last-active times are missing, and no crash in room cosmetics before members load (#1298, #1579, #1793). by @eleboucher in #1503 +* Fix sliding sync issues: missing or stale display names, bridged avatars, blank space icons, sticker and emote packs, subspaces, call updates, and incomplete member lists (#1111, #1112, #1119, #1173, #1530, #1554). by @7w1 in #1170 +* Notification and unread fixes: rooms no longer stay unread after a thread reply or restart, the inbox shows real message previews, the page title shows the mention count, and tapping a notification navigates correctly (#1245, #1390). by @eleboucher in #1816 +* Timeline fixes: reliably follow new messages, stop loading a room's whole history on open, unstuck pagination, restored polls, and fixed thread rendering, reactions, and replies (#1113, #1358, #1363, #1367, #1373, #1425). by @eleboucher in #1105 +* Fix voice recordings continuing after cancel or rapid taps, and stop the microphone when a recording is interrupted before the permission prompt resolves (#1428). by @eleboucher in #1252 +* Add option to always keep the last line of the text editor "inline" with the UI buttons. by @lunar-seal in #1218 +* Mobile polish: smoother swipe navigation and drawer, steady keyboard while editing, better touch feedback, chat input and account panel, swipe-to-edit on messages, and fixed swiping in attachments (#1103, #1130, #1131, #1144, #1173, #1267, #1303, #1382, #1754). by @eleboucher in #1166 + +## 1.20.0 (2026-07-17) + +### Security + +* This release includes an important security fix. Please update as soon as possible. Sensitive Matrix account data is not exposed by this issue. ([#1107]() by @7w1) + +### Features + +* Add Native OIDC (OAuth 2.0) login so you can sign in to and register on homeservers that use delegated authentication, such as continuwuity and Synapse with the Matrix Authentication Service. Sessions stay signed in through automatic token refresh, and signing out revokes the tokens on the server. ([#1071]() by @eleboucher) +* Show the unread mention count in the page title. ([#1093]() by @eleboucher) + +#### Improve sliding sync. ([#1069]() by @7w1, @eleboucher) + +- Make sliding sync easier to enable during login or loading and from settings. +- Move the sliding sync toggle from Experimental to the bottom of General settings. +- Start faster and restore rooms and spaces sooner. +- Reduce unnecessary background room data. +- Improve room details, memberships, presence, and unread indicators. +- Make startup loading smoother for both sliding and classic sync. +- It's fast now :3 + +### Fixes + +* Use attachment filenames instead of captions when downloading files and harden unsafe download names. ([#1086]() by @7w1) +* Allow users to send messages in encrypted rooms when they have permission to send `m.room.encrypted` events. ([#1098]() by @7w1) +* Allow users without a locally configured LiveKit focus to join calls started by others. ([#1099]() by @7w1) +* Fix the new messages divider not appearing when the read receipt points at an event hidden from the timeline (reaction, edit, thread reply). ([#1101]() by @eleboucher) +* Fix the new messages divider staying at the room-open position instead of re-anchoring at the last read when the window loses focus. ([#1095]() by @eleboucher) +* Fix redacted messages rendering as "Unsupported message (no body)" instead of "Message deleted" with sliding sync. ([#1089]() by @eleboucher) +* Display redacted state events (membership, room name/topic/avatar, pins and other state events) as a "deleted" tombstone in the timeline instead of rendering them with empty/redacted content or hiding them, so the reply-to of a redaction highlights the redacted event. ([#1090]() by @eleboucher) +* Fix joining spaces with sliding sync enabled. ([#1085]() by @eleboucher) +* Display the explicit room name for non-direct channels with two members. ([#1098]() by @7w1) +* Fix resetting animal identity ([#1070]() by @nushea) +* Fix the sliding-sync presence fallback consuming one-time-key updates and to-device messages without processing them. ([#1078]() by @7w1) +* Fix clickable handles ([#1068]() by @nushea) +* Hide the "Register" link on the login page when the homeserver has registration disabled and offers no SSO to register through, so you are no longer sent to a dead-end registration page. ([#1073]() by @eleboucher) +* Add uploaded Sable CSS theme loading, automatic catalog updates, and a cleaner theme browser with full CSS viewing. ([#1087]() by @7w1) +* Show room members in developer tools. ([#1088]() by @7w1) + +## 1.19.4 (2026-07-12) + +### Fixes + +* Fix some image embeds not creating embeds. ([#1062]() by @7w1) +* Allow rooms with empty local parts to be viewed. ([#1062]() by @7w1) +* Fix the account button showing you 0 sessions active and the verify this device showing when you have zero devices to verify ([#1064]() by @nushea) +* Various performance optimizations ([#1062]() by @7w1) +* Fixed the entire timeline being rerendered every time you scroll :P ([#1055]() by @7w1) + +### Notes + +* Added a new workflow that automatically previews dev on [dev.sable.moe](https://dev.sable.moe) ([#1059]() by @7w1) + +## 1.19.3 (2026-07-09) + +### Fixes + +* Fix crash with some embed links. ([#1047]() by @7w1) +* Fix even more visual stuffs. ([#1049]() by @nushea) + +## 1.19.2 (2026-07-09) + +### Fixes + +* Fix right click menu not closing when viewing reactions. ([#1045]() by @7w1) +* Send gifs with proper file extensions in body. ([#1045]() by @7w1) +* Fix crash in autocomplete menu when editing messages. ([#1045]() by @7w1) +* Ensure call buttons appear/disappear when relevant state events occur. ([#1045]() by @7w1) +* Fix format for recent emojis and related crash. ([#1045]() by @7w1) +* Fix sliding sync debug saying sliding when it is in fact not sliding. ([#1045]() by @7w1) +* Fix very small little visual issues you probably didnt even notice 🙏 ([#1038]() by @nushea) + +## 1.19.1 (2026-07-09) + +### Fixes + +* Fix sliding sync being enabled when setting is disabled. ([#1036]() by @7w1) + +## 1.19.0 (2026-07-09) + +### Features + +* Added a GIF search functionality ([#970]() by @7w1, @nikiwastaken) +* Change unverified device flow. ([#1017]() by @nikiwastaken, @nushea) +* Change animal identity to allow arbitrary animals ([#984]() by @nushea) +* Add a new Mobile Context ([#946]() by @7w1, @nushea) +* Add bookmark functionality using account data ([#1000]() by @nikiwastaken, @nushea) +* Add Copy Message button to copy unformatted version. ([#1001]() by @nushea) +* Change profile handle part to a button that copies it ([#981]() by @nushea) +* Add reactions to arbitrary events ([#946]() by @7w1, @nushea) +* Added a gallery support as per msc4274 ([#1019]() by @nikiwastaken, @nushea) +* Add a context menu for copying and downloading images. ([#994]() by @nikiwastaken, @nushea) +* Add search message keybind and combine sidebar items. ([#943]() by @nushea) +* Add option for showing icons only when available and changed default to it ([#978]() by @nushea) +* Add CTRL+E shortcut to editor to bring sticker menu up ([#982]() by @nushea) +* Redesign the user menu tab ([#990]() by @nikiwastaken, @nushea) +* Modify large parts of the mobile view of Sable! ([#1017]() by @nikiwastaken, @nushea) +* Improved sliding sync (only requests specific room state data), cleaned up and fixed most flickering issues, and added buttons in developer tools to request full state. ([#1020]() by @7w1) + +#### Revamped the calling experience in Sable! ([#841]() by @7w1) + +- Pulled the latest Element Call updates. +- Style improvements to the call widget so it uses the Sable theme and fonts. +- Added support for custom ringtones and ringbacks. +- Added call permissions. +- Fixed various bugs around call ending, declining, and joining. + +### Fixes + +* Allow favoriting all animated attachment types ([#1029]() by @nikiwastaken) +* Updated emojis to use stable 1.19 identifiers ([#1034]() by @7w1) +* Swap to using the stable identifier `m.recent_emoji` for recent emojis and add migration logic. ([#1009]() by @7w1) +* Add a direct link to room/space settings when the current sticker list is empty ([#1016]() by @7w1, @ellieplayswow) +* Fix leaving modal looking outsized ([#991]() by @nushea) +* Fix cat status not being present on its own. ([#971]() by @nikiwastaken) +* Fix encrypted thread messages not appearing until reopened. ([#975]() by @7w1) +* Fix codebase issues with gadient themes ([#1007]() by @nushea) +* Fix account switcher's add account button redirecting to the wrong path when hashRouter is enabled ([#993]() by @EphemeralFog) +* Fix the image viewer header not being fully visible on smaller phone screens. ([#1035]() by @7w1) +* Fix mentions not sending in non-textual replies ([#1004]() by @nushea) +* Fixed oidc device delete key being incorrect. ([#1030]() by @7w1) +* Fix crash when opening pins with stickers. ([#972]() by @7w1) +* Fix pixelated image scaling not pixelating when zoomed in. ([#1033]() by @7w1) +* Fix registration not working when accessed from add account button ([#992]() by @EphemeralFog) +* Reverse the order of message header elements in the right aligned bubbles layout. ([#967]() by @EphemeralFog) +* Fixed outdated Firefox versions and some Safari versions not being able to send messages. ([#1010]() by @7w1) +* Fix timeline sorting order regression when "show hidden events" is enabled. ([#974]() by @7w1) +* Fix ghost quickmenu ([#946]() by @7w1, @nushea) +* Fix Collapsed mode showing the wrong icon ([#978]() by @nushea) +* Fix poll crashing sable if interacted with different clients ([#969]() by @nushea) +* Fix video player sometimes not working ([#1031]() by @nushea) +* Add support for searching users via nicknames in the autocomplete menu. ([#1028]() by @7w1) +* Update `@sableclient/twemoji-font` to fix missing Twemoji emoji coverage. ([#964]() by @7w1) + +## 1.18.3 (2026-06-17) + +### Fixes + +* Fixed broken Twemoji rendering and updated emoji picker, autocomplete, and jumbo detection for new emojis. ([#957]() by @7w1) +* Updated various dependencies. ([#957]() by @7w1) + +## 1.18.2 (2026-06-17) + +### Fixes + +* Adding servers in the explore tab now persist in account data and are actually added to the list. ([#953]() by @7w1) +* Added a button to leave all rooms/subspaces within a space in the leave space confirmation dialog. Also has text to show number of children. ([#953]() by @7w1) +* Added a setting to play notification sounds when tab is in background, enabled by default. ([#953]() by @7w1) +* Centered the notification bell icon in the room list. ([#954]() by @7w1) +* Fixed edit diffs not properly scaling with zoom. ([#951]() by @7w1) +* Fixed icons not scaling with zoom. ([#951]() by @7w1) +* Fixed invalid language tag when no locale set for some systems. ([#953]() by @7w1) +* Fixed replying to reaction/edit events not replying. ([#942]() by @7w1) +* Fix sending thread messages sometimes sending in the general timeline ([#944]() by @nushea) + +#### Updates Twemoji to from v15 to v17 🫪 ([#950]() by @7w1, @hazre) + +You can check here for emoji changes: + +- https://www.unicode.org/emoji/charts-16.0/emoji-released.html +- https://www.unicode.org/emoji/charts-17.0/emoji-released.html + +## 1.18.1 (2026-06-14) + +### Fixes + +* Added some icon size settings. ([#941]() by @7w1) +* Add 'Show Results' to show their poll's result before casting an answer ([#939]() by @nushea) +* Fix edit events collapsing with normal messages. ([#941]() by @7w1) +* Make more Icon changes ([#940]() by @nushea) +* Fix missing the mark icon on Map, and the mark moving around when clicked ([#937]() by @nushea) +* Hide membership events in read only rooms now also hides reactions and reaction redactions. ([#941]() by @7w1) + +## 1.18.0 (2026-06-14) + +### Features + +* Added configurable max pronoun pill count and max pill length rendering settings. ([#904]() by @7w1) +* Adds the ability to hover/tap over `...` to see the rest of someone's pronouns. ([#904]() by @7w1) +* Support rendering `matrix:` URIs in incoming messages. ([#935]() by @7w1) +* Added an inline add-reaction button at the end of message reaction lists. ([#934]() by @7w1) +* Add button to silently dismiss Invite ([#936]() by @nushea) +* Highlight message that you are about to reply to! ([#897]() by @nushea) +* Add Location styling with a modal! ([#927]() by @nushea) + +#### Added more hidden timeline events with settings and rendering. ([#934]() by @7w1) + +##### Settings + +- Added a master Show Hidden Events toggle with per-type sub-toggles for message edits, redactions, reactions, and other unrecognized events +- Sub-toggles stay visible beneath the master toggle and are disabled while hidden events are off + +##### Timeline rendering + +- Show message edits as timeline events with reply navigation and an inline word/line diff between versions +- Show reactions, message redactions, and reaction redactions as timeline events +- Keep redacted reactions in the timeline as tombstones with redaction events linking back to them when possible +- Improve reply-chip previews for edits, redactions, reactions, and redacted targets + +##### Safeguards + +- Hide forward, delete, and other message actions on timeline meta events that cannot be forwarded or meaningfully deleted +- Disallow forwarding deleted messages and other non-message event types + +#### Add polls! ([#916]() by @nushea) + +##### Add Polls with a new Menu for adding items + +- The polls have a simple style for showing and interfacing with poll events +- There is now a simple interface for creating polls which integrates the spec +- Now the Plus button on the bottom left can open a menu for selecting what to send, which will be useful for future options as well +- For the people that prefer to only add files with that button they can disable the menu and still create polls with the /poll command +- You can see who voted for what in a clear menu + +### Fixes + +* Change image rendering to allow enabling anti-aliasing in the image viewer ([#919]() by @nushea) +* Fix crash during scrubbing before duration duration appears. ([#902]() by @7w1) +* Fixed lists rendering `p` html tags on new lines. ([#906]() by @7w1) +* Fix math parsing inside of color blocks not being parsed properly. ([#910]() by @7w1) +* Fixed nested lists having wrong indentation levels when editing. ([#906]() by @7w1) +* Fix missing filename breaking edits, and missing a fallback, and codeblock titles overflowing ([#917]() by @nushea) +* Fix unset User Profile cards having incorectly colored text. ([#896]() by @nushea) +* Migrated all icons to Phosphor Icons and added a setting to display globe/lock icon instead of the hash tag for the room sidebar. ([#934]() by @7w1) + +## 1.17.0 (2026-05-22) + +### Features + +* Add support for misskey-flavored markdown color definitions. E.g. `$[fg.color=f00 bg.color=00ff00 red on green]`. ([#860]() by @7w1) +* Add per Space setting for when to show room icons in sidebar ([#851]() by @nushea) +* Add toggle to list all rooms inside of the home sidebar. ([#866]() by @nushea) + +#### Change Image Viewer to feel more natural to use. ([#686]() by @7w1, @nushea, @Septicity) + +- Fixed zoom gestures generally not working on mobile. +- Changed the % number in the top right to reflect the zoom of the original image as opposed to the change from it fitting the container. +- Made the zoom pill allow entering custom values. +- Added a button that zooms you to the original size of the image, and a button to return to the size that fills the container. +- Added a pixelated image scaling setting: choose Both, Chat, Image viewer (default), or Neither for crisp nearest-neighbor rendering. +- Transitions are now disabled for manual panning to improve responsiveness. + +### Fixes + +* Fixed starting lists at arbitrary numbers and list markers extending off screen with long numbers. ([#860]() by @7w1) +* Fix single new lines after block quotes being block-quoted. ([#860]() by @7w1) +* Fix emojis not rendering in reply chips. ([#860]() by @7w1) +* Hardened html parsing in standard input box, should no longer randomly delete text in arrow brackets (unless valid, properly closed, legal html). ([#860]() by @7w1) +* Fixed the message loading spinner flickering instead of continuing during large pagination chunks. ([#895]() by @7w1) +* Fix matrix.to links getting arrow brackets inserted when editing messages. ([#860]() by @7w1) +* Fix mentions breaking after editing messages with mentions. ([#860]() by @7w1) +* Fix account switching and logging in with multiple (SSO) accounts silently failing ([#882]() by @770grappenmaker) +* Removed the arbitrary bio limit from the bio editor. ([#863]() by @7w1) +* Remove target and rel attributes from outgoing html links. ([#891]() by @7w1) +* Fix tablet layouts having the sidebar fill up the screen. ([#864]() by @7w1) +* Add support for stable mutual rooms endpoint, manually ported from [this commit](https://github.com/cinnyapp/cinny/commit/bef267257a28b3be8a96852fed1718f63cf570f9) by ajbura upstream. ([#852]() by @7w1) + +### Documentation + +* Docker build tags have been updated: `latest` now tracks the latest versioned release and `dev` now tracks the dev branch. ([#892]() by @7w1) + +## 1.16.1 (2026-05-15) + +### Fixes + +* Fix issues related to editing messages with links losing previews or gaining \<> ([#847]() by @7w1) +* Made markdown headers also function properly with single new lines instead of only two new lines. ([#837]() by @7w1) +* Fix mentions not being linkfied. ([#837]() by @7w1) +* Fixed crash when rendering some `m.room.pinned_events` timeline rows (old/malformed pin state edge case). ([#848]() by @7w1) +* Fixed reply chips for deleted messages and media without captions showing `m.room.message` type instead of the event. ([#846]() by @7w1) +* Fixed room pings looking like normal message links instead of pings. ([#837]() by @7w1) +* Properly centered message loading indicators to avoid brief scrollbar shift while loading messages. ([#849]() by @7w1) + +## 1.16.0 (2026-05-14) + +### Features + +* Add Space banner support per MSC4221. You can now set it from the space settings. ([#801]() by @nushea) +* Add setting to show icons of the rooms in the Rooms sidebar ([#768]() by @nushea) +* Add Resize the sidepanels and the thread height of the original object using hoverable tools. ([#768]() by @nushea) +* Add toggle to allow one to not join a call in a room by just clicking it in the sidebar. ([#768]() by @nushea) +* Added the ability to right click on a folder to rename it. ([#814]() by @7w1) +* Upgraded the forward modal to use the same modal present for search and added the ability to forward to same room the message is from. ([#808]() by @7w1) + +#### Introduce the new Sable logo! ([#809]() by @Septicity) + +- You may need to reinstall PWAs to recieve the new app icons, if you use them. +- Our logo is [licensed under CC0](https://github.com/SableClient/Sable/blob/dev/TRADEMARKS.md), aka do whatever you want with it! +- Also, there's a script for other us or other forks to replace the icon in the future, if needed. + +### Fixes + +* Add graceful fail if MSC4140 event delay exceeded ([#276]() by @jasonlaguidice) +* Drops paragraph tags when messages are only a single paragraph, use markdown (two new lines) to define a new paragraph rather than a line break. ([#799]() by @7w1) +* Fixed the text wrapping behavior of fallback messages. ([#806]() by @7w1) +* Fixed the thin line appearing at the top of unstyled profiles. ([#803]() by @7w1) +* Matrix.to links sent without explicit markdown formatting are sent as raw links instead of html links. ([#786]() by @7w1) +* Fix `/myroomnick` and room cosmetics display name not updating name. ([#805]() by @7w1) +* Fix spoilers not hiding nested content like mentions, emoji images, and custom-colored spans. ([#807]() by @7w1) +* Added svgs to the allowed embeds for rendering/sending. ([#804]() by @7w1) +* Suppress timeline dividers when there's no rendered events between them. ([#720]() by @mvanhorn) +* Improve thread drawer separation between the pinned root message and replies with a border. ([#830]() by @7w1) + +## 1.15.3 (2026-05-09) + +### Fixes + +* Self-hosted deployments can set optional `settingsDefaults` in `config.json` to override built-in client settings. See the README for details. ([#785]() by @7w1) +* Updated the math detection to avoid accidental detection when talking about math or spamming dollar signs. ([#779]() by @7w1) +* Added a couple new settings for max incoming inline image height and default height for unspecified. http://localhost:8080/settings/appearance?focus=incoming-inline-images-default-height&moe.sable.client.action=settings ([#772]() by @7w1) +* Fixed links with suppressed previews not having the arrow brackets readded when editing a message. ([#772]() by @7w1) +* Added the ability to cap preview embed size. http://localhost:8080/settings/appearance?focus=link-preview-image-max-height&moe.sable.client.action=settings ([#783]() by @7w1) +* Fix the inconsistent sizing for the read receipt dialog boxes. ([#772]() by @7w1) +* Fixed room avatars set in the settings cosmetics menu not applying. ([#772]() by @7w1) +* Fix room names being overriden when only 1 other person is in a room. ([#784]() by @7w1) +* Fix small text being parsed in code blocks and not being escapeable. ([#782]() by @7w1) +* Fixed the hang when a message that replies to a message has a reply, and you attempt to start a thread on that message. ([#778]() by @7w1) +* Fix RTL/LTR mixed text formatting and alignment in messages ([#743]() by @sinasadeghi83) +* Readded various missing settings from the settings sharing list. ([#765]() by @7w1) +* Various small adjustments to the themed profiles for better consistency. ([#777]() by @7w1) + +## 1.15.2 (2026-05-07) + +### Fixes + +* Adds back the message editor toolbar under an optional setting. No longer uses WYSIWYG, just applies markdown. http://localhost:8080/settings/general?focus=composer-formatting-toolbar&moe.sable.client.action=settings ([#762]() by @7w1) +* Fixed blockquotes needing a double backslash to escape and require a space after the `>` in order to form a blockquote. ([#758]() by @7w1) +* Fix empty messages being displayed as broken messages. ([#754]() by @7w1) +* Reescape arrow brackets when editing a message. ([#763]() by @7w1) +* Fix extraneous markdown escape characters when editing code blocks. ([#762]() by @7w1) +* Fixed jumpting to arbitrary events (e.g. reactions, edits, pins, leaves/joins). ([#759]() by @7w1) +* Fix latex in codeblocks getting parsed. ([#758]() by @7w1) +* Fixed message links being rendered as full links. ([#751]() by @7w1) +* Fixed per-message profile proxies not unwrapping and generally just not working. (`f8a9a8f`) +* Fixed tweak automatic favoriting behavior when entering/leaving the catalog. ([#757]() by @7w1) +* Fix tweaks not applying on built-in themes. ([#756]() by @7w1) +* Added the ability to **underline** using `__underscores__`. ([#761]() by @7w1) + +## 1.15.1 (2026-05-05) + +### Fixes + +* Fix editing messages with custom emojis being converted into html tags. ([#749](https://github.com/SableClient/Sable/pull/749) by @7w1) +* Fix muted rooms appearing as standard unread rooms. ([#750](https://github.com/SableClient/Sable/pull/750) by @7w1) +* Fix the call stack size crash on load when sliding sync is enabled. ([#748](https://github.com/SableClient/Sable/pull/748) by @7w1) + +## 1.15.0 (2026-05-05) + +### Features + +* Add background styling to user profile cards ([#712](https://github.com/SableClient/Sable/pull/712) by @nushea) +* Add preventing url preview cards by surrounding a link in anglebrackets like ([#717](https://github.com/SableClient/Sable/pull/717) by @nushea) +* Reorganize Embed settings and reintroduce multiple embeds ([#667](https://github.com/SableClient/Sable/pull/667) by @nushea) +* Change Misc. data styling in users profile pages ([#663](https://github.com/SableClient/Sable/pull/663) by @nushea) +* add initial support for sending discoverable emojis and sticker ([#730](https://github.com/SableClient/Sable/pull/730) by @dozro) + +#### Themes and tweaks from the catalog ([#633](https://github.com/SableClient/Sable/pull/633) by @7w1) + +Themes are pulled from [a repo](https://github.com/SableClient/themes) now, so you get the full power of CSS instead of a palette. Tweaks are new: CSS overlays that sit on top of whatever theme you are using. + +You'll be prompted to migrate to the new system whenever you update, if you choose not to, you'll be limited to the basic dark/light themes. A few additional themes have been added (Rose Pine variantes, Catpuccin) along with some basic tweaks (circular avatars, monochrome avatars, and square stuff). + +You can share themes and tweaks. For themes uploaded online, simply hit the copy button in settings and paste the link in chat. If the setting is enabled, a preview will be generated. Third party themes (as defined by the config.json) have prominent warning banners and fetching is disabled by default. + +You can also export and share theme files directly, although no previews are generated for these. + +If you're intrested in getting a theme or tweak added to the official catalog, contribute to the themes repo linked above! We're eager to add more! + +#### Markdown parser and render updates ([#727](https://github.com/SableClient/Sable/pull/727) by @7w1) + +Migrated markdown parsing and rendering to use marked, which should fix most (all?) markdown issues involving lists/nested structures, inconsistent/inaccurate code blocks, escape sequences, and all the other bugs with literally everything. + +Added math rendering support via marked and KaTeX, uses standard `$$` and `$` delimiters. Only renders a subset of latex tags that will likely need to be expanded so feel free to make issues if needed. + +Also adds support for sending markdown tables (although they're rendered rather plainly at the moment), sending valid html directly (such as for colored text), and properly escaping anything with backslashes. + +Fixes link previews appearing in code blocks, fixes pmp new line behavior, fixes links not opening in new tabs, and fixes editing arbitrary html messages, probably. + +Finally, the old WYSIWYG editor has been completely removed. + +### Fixes + +* Fix spam-clicking abbreviations crashing sable ([#665](https://github.com/SableClient/Sable/pull/665) by @nushea) +* Add cache-control headers in Caddyfile for assets, service worker, and index.html ([#609](https://github.com/SableClient/Sable/pull/609) by @Just-Insane) +* Fix the first pin event in a room looking empty ([#685](https://github.com/SableClient/Sable/pull/685) by @nushea) +* Fix read receipt scrolling not working ([#631](https://github.com/SableClient/Sable/pull/631) by @Septicity) +* Fix status sometimes sticking in member tile ([#664](https://github.com/SableClient/Sable/pull/664) by @nushea) +* Fix apng files not animating. ([#737](https://github.com/SableClient/Sable/pull/737) by @7w1) +* Some fixes to sync requests being spammed on loading screen and for multi-account background syncing, it should also load faster now! ([#736](https://github.com/SableClient/Sable/pull/736) by @7w1) +* Fix other dmed party not being added as a founder by default when creating a dm. ([#737](https://github.com/SableClient/Sable/pull/737) by @7w1) +* Update verbiage in the credits ([#728](https://github.com/SableClient/Sable/pull/728) by @nushea) +* spoilered text now gets replaced with `[Spoiler]` in the plain text fallback, as per MSC4454 ([#715](https://github.com/SableClient/Sable/pull/715) by @dozro) +* Hide copied settings links on dynamic rows ([#695](https://github.com/SableClient/Sable/pull/695) by @hazre) +* Fix button hover background smearing in virtual list rows by suppressing transform on hover ([#614](https://github.com/SableClient/Sable/pull/614) by @Just-Insane) +* Fix Workbox precaching by removing injectionPoint override that was silently disabling all precache entries ([#611](https://github.com/SableClient/Sable/pull/611) by @Just-Insane) + +#### Change how settings links are shared ([#695](https://github.com/SableClient/Sable/pull/695) by @hazre) + +Settings links copied from Sable now stay on the current client URL and include a small Sable marker in the link. That lets Sable recognize settings links copied from other Sable instances without treating unrelated third-party `/settings/...` links as Sable settings links. + +When you send a bare settings link in the composer, Sable now rewrites it into a labeled link so it looks better on non-Sable clients too. For example: `[Settings > Account > Display Name](https://client.example/settings/account?focus=display-name&moe.sable.client.action=settings)`. + +Invalid or malformed settings-looking links now stay normal links instead of being shown as settings chips. + +If you previously set `settingsLinkBaseUrl` in `config.json`, remove it. Sable now derives settings links from the runtime app URL, and the old config key is no longer used. + ## 1.14.0 (2026-04-10) ### Features diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index adbdab8821..820903120a 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,128 +1,78 @@ -# Contributor Covenant Code of Conduct +# Sable Community Code of Conduct -## Our Pledge +## Welcome! -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, religion, or sexual identity -and orientation. +We're excited to have you here. Sable is a Matrix client meant for everyone. +Because of that we're committed to keeping this community open and welcoming to all. -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. +We ask all of our community—the people who build it, use it, break it, suggest things for it, help each other with it, and +occasionally meow—to abide by some general rules when in official Sable spaces. -## Our Standards +## The rules -Examples of behavior that contributes to a positive environment for our -community include: +Conversation here is casual. You can disagree, joke around, swear, be weird, +and criticize Sable or its decisions. Just remember that other people share the room with you. -- Demonstrating empathy and kindness toward other people -- Being respectful of differing opinions, viewpoints, and experiences -- Giving and gracefully accepting constructive feedback -- Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -- Focusing on what is best not just for us as individuals, but for the - overall community +The following are not allowed: -Examples of unacceptable behavior include: +- **Do not harass people.** This includes targeted insults, dogpiling, repeatedly + provoking someone, unwelcome sexual advances or attention, or continuing to contact someone after they ask you to + stop. +- **Do not use hate speech or attack someone based on their identity**, including but not + limited to race, ethnicity, nationality, religion, disability, age, body size or + appearance, sex, sex characteristics, sexuality, gender identity, gender + expression, and socioeconomic status. +- **Do not make threats, encourage violence or self-harm, or doxx anyone.** Doxxing includes + sharing or threatening to share someone's private information. +- **Do not post explicit sexual content.** This includes graphic NSFW media, pornography, + and explicit sexual comments or jokes. +- **Do not abuse the shared spaces.** This includes impersonation, scams, malicious links or + files, spam, flooding, ban evasion, or retaliation against someone for making + a report or setting a boundary. -- The use of sexualized language or imagery, and sexual attention or - advances of any kind -- Trolling, insulting or derogatory comments, and personal or political attacks -- Public or private harassment -- Publishing others' private information, such as a physical or email - address, without their explicit permission -- Other conduct which could reasonably be considered inappropriate in a - professional setting +Context and patterns matter. Being blunt, disagreeing about politics or project +direction, or criticizing a maintainer is not automatically a violation. These +rules cannot cover every situation, so moderators have final say on how they +apply. -## Enforcement Responsibilities +## Where this applies -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. +These rules apply to Sable's official GitHub repositories and Matrix space and +rooms. -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. +We do not police people's unrelated private lives. We may act on credible +threats, doxxing, or targeted harassment outside these spaces when it is +connected to Sable. -## Scope +## Moderation -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. +If a moderator asks you to stop, stop. -## Enforcement +When this code of conduct is not followed, +moderators may remove content, issue a warning, kick, or ban someone from Sable's Matrix +rooms or block them from its GitHub repositories. -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at -executioner@sable.moe. -All complaints will be reviewed and investigated promptly and fairly. +When practical, moderators will explain the problem and what needs to happen next. +Moderators will step back from handling reports that involve them directly, provided another moderator is available. You may request a review by emailing the address listed in the Reporting section below if you believe a decision was mistaken. -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. +## Reporting -## Enforcement Guidelines +Message a moderator (anyone with an Admin or Moderator power level) in the relevant Matrix room or email +[conduct@sable.moe](mailto:conduct@sable.moe). Reports +will be handled thoughtfully and as privately as we reasonably can. -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: +If your report is about a moderator, email +[conduct@sable.moe](mailto:conduct@sable.moe) or contact 7w1 directly at +[`@seven:sable.moe`](https://matrix.to/#/@seven:sable.moe). -### 1. Correction +Send whatever context you have. You do not need to build a case first. +We will limit access to people who need it to handle the report. We will not +identify you to the reported person without your permission, though details of +the report may need to be discussed to investigate or act on it. +Knowingly false reports or using reports to harass someone are not allowed. -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. +## Other stuff -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series -of actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or -permanent ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within -the community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.0, available at -https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. - -Community Impact Guidelines were inspired by [Mozilla's code of conduct -enforcement ladder](https://github.com/mozilla/diversity). - -[homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct, see the FAQ at -https://www.contributor-covenant.org/faq. Translations are available at -https://www.contributor-covenant.org/translations. +These rules may change as Sable and its community change. +By continuing to participate in official Sable spaces, you agree to continue abiding by these rules. +If something is unclear, missing, or badly written, please tell us and we will update this document. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c037e31df0..21f3620094 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -48,6 +48,21 @@ Also, we use [ESLint](https://eslint.org/) for clean and stylistically consisten If your change touches logic with testable behaviour, please include tests. See [docs/TESTING.md](./docs/TESTING.md) for a guide on how to write them. +## Architecture changes + +Sable separates runtime-specific code from Matrix and product-domain code. + +- `src/app/platform/`: browser and Tauri behavior. +- `src/app/session/`: Matrix account and sync lifecycle. +- `src/app//`: shared policy and data, with no React or platform imports. +- `src/app/features/`: one screen or surface. Shared feature code moves down into a domain or platform module. + +Dependencies flow from UI to services, then to platform ports or pure domain code. React subscribes to long-lived resources but does not own clients, listeners, retries, or timers. Those need explicit cleanup and must ignore stale async completions. + +Parse untrusted input at ingress, give each persistence store one owner, and use a durable inbox for events that can arrive while the app is closed. Do not loosen native capabilities or attach Matrix tokens without validating the homeserver origin and media path. + +For changes across runtime, persistence, or lifecycle boundaries, explain the boundary and its tests in the PR. + ## Restrictions on Generative AI Usage We expect and appreciate authentic engagement in our community. diff --git a/Dockerfile b/Dockerfile index 7376067c8e..7c6abd406f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,9 +11,9 @@ FROM base AS builder WORKDIR /src ARG VITE_BUILD_HASH -ARG VITE_IS_RELEASE_TAG=false +ARG SABLE_BUILD_FLAVOR=stable ENV VITE_BUILD_HASH=$VITE_BUILD_HASH -ENV VITE_IS_RELEASE_TAG=$VITE_IS_RELEASE_TAG +ENV SABLE_BUILD_FLAVOR=$SABLE_BUILD_FLAVOR COPY pnpm-lock.yaml pnpm-workspace.yaml /src/ RUN pnpm fetch COPY . /src/ diff --git a/README.md b/README.md index 2a1f7d853f..4d3c32dd4b 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,91 @@ Join our matrix space [here](https://matrix.to/#/#sable:sable.moe) to discuss fe Forked from [Cinny](https://github.com/cinnyapp/cinny/). ## Getting started -The web app is available at [app.sable.moe](https://app.sable.moe/) and gets updated on frequently, as soon as a feature is deemed stable. +The stable web app is available at [app.sable.moe](https://app.sable.moe/) and tracks the [latest GitHub release](https://github.com/SableClient/Sable/releases/latest). Nightly builds are available at [dev.sable.moe](https://dev.sable.moe/) and from the [`nightly` GitHub release](https://github.com/SableClient/Sable/releases/tag/nightly). -You can also download our desktop app for windows and linux from [releases](https://github.com/SableClient/Sable/releases/latest). +You can also download our desktop app for Windows and Linux from [releases](https://github.com/SableClient/Sable/releases/latest). Release artifacts include build attestations. AppImage and Windows installations update automatically; `.deb`, `.rpm`, and AUR installations update through their package manager. + +### Desktop (Linux / macOS / Windows) + +Download +  +Flathub + +On macOS, via our [Homebrew tap](https://github.com/SableClient/homebrew-sable): + +```sh +brew install --cask SableClient/sable/sable +``` + +The fully qualified name matters: since [Homebrew 6.0.0](https://brew.sh/2026/06/11/homebrew-6.0.0/) non-official taps need explicit trust, and installing this way trusts just this one cask. `brew tap` followed by a short-name install now fails unless you also run `brew trust`. + +## Android (Obtainium) + +Android APKs are published to every release, and [Obtainium](https://obtainium.imranr.dev) keeps them updated straight from GitHub. Each release also ships an `obtainium.json` app config. Use it for the nightly channel, where prereleases and date-based version tracking have to be enabled to follow the rolling `nightly` tag. + +### Stable + +Add to Obtainium +  +App config +  +Download APK + +### Nightly + +Add to Obtainium +  +App config +  +Download APK + +### Setup & install + +1. Install [Obtainium](https://github.com/ImranR98/Obtainium/releases/latest). +2. Tap **Add to Obtainium** above. Stable opens the **Add App** page prefilled with `https://github.com/SableClient/Sable`; nightly opens an import prompt, since it carries the prerelease and version-tracking settings the rolling `nightly` tag needs. +3. Or download the `obtainium.json` for either channel and import it with **Import/Export → Import from file**. + +Android builds are produced by the `android` job in [`tauri-build.yml`](.github/workflows/tauri-build.yml), and the config by the `obtainium` job in the same workflow. + +### Relevant variables and switching between Nightly and Stable + +Relevant variable differences between Nightly and Stable: +- *Include prereleases* - `true` for Nightly, `false` for Stable +- *Fallback to older releases* - `false` for Nightly, `true` for Stable +- *Use latest asset upload as release date* - `true` for Nightly, `false` for Stable (this is due to Nightly builds being uploaded to one release instead of creating new ones) +- *Use release date as version string (pseudo-version)* - `true` for Nightly, `false` for Stable (this is due to Nightly builds being uploaded to one release instead of creating new ones) + +## iOS (AltStore / SideStore) + +Sable iOS builds are distributed as unsigned IPAs through [AltStore](https://altstore.io) and [SideStore](https://sidestore.io). Each release publishes both the IPA and an `altstore-source.json` manifest — stable builds to the [latest GitHub release](https://github.com/SableClient/Sable/releases/latest), nightly builds to the [`nightly` GitHub release](https://github.com/SableClient/Sable/releases/tag/nightly). + +### Stable Source + +Add to AltStore +  +Add to SideStore +  +Direct URL + +### Nightly Source + +Add to AltStore +  +Add to SideStore +  +Direct URL + +### Setup & install + +1. Set up [AltStore Classic](https://faq.altstore.io/altstore-classic/altserver) or [SideStore](https://docs.sidestore.io) on your device. +2. Add the Sable source (tap a button above), or add it manually: + - AltStore (stable): `altstore://source?url=https://github.com/SableClient/Sable/releases/latest/download/altstore-source.json` + - SideStore (stable): `sidestore://source?url=https://github.com/SableClient/Sable/releases/latest/download/altstore-source.json` + - AltStore (nightly): `altstore://source?url=https://github.com/SableClient/Sable/releases/download/nightly/altstore-source.json` + - SideStore (nightly): `sidestore://source?url=https://github.com/SableClient/Sable/releases/download/nightly/altstore-source.json` +3. Install Sable from the source. The IPA is unsigned; AltStore/SideStore re-sign it with your personal development certificate at install time, so apps refresh every 7 days (the standard free-account limitation). + +iOS builds are produced by the `ios` job in [`tauri-build.yml`](.github/workflows/tauri-build.yml) and track the same `dev`/`v*` triggers as desktop builds. ## Self-hosting You have a few options for self hosting, you can: @@ -23,7 +105,8 @@ You have a few options for self hosting, you can: Prebuilt images are published to `ghcr.io/sableclient/sable`. -- `latest` tracks the current `dev` branch image. +- `latest` tracks the current latest version release. +- `dev` tracks the current `dev` branch image. - `X.Y.Z` tags are versioned releases. - `X.Y` tags float within a release line. - Pushes to `dev` also publish a short commit SHA tag. @@ -62,27 +145,64 @@ After that, you can copy the dist/ directory to your server and serve it. * In the [`config.json`](config.json), you can modify the default homeservers, feature rooms/spaces, toggle the account switcher, and toggle experimental simplified slilding sync support. +#### Optional default client settings + +While the default settings are recommended for most users, you can optionally add a top-level `"settingsDefaults"` object whose keys match [client settings](src/app/state/settings.ts) (only fields you want to override) to override them. The default settings for any new logins will match these. Existing keys in local storage or users who chose to sync settings with their account data will still have their settings set. + +For example: + +```json +{ + "settingsDefaults": { + "hour24Clock": true, + "pageZoom": 110, + "messageLayout": 2, + "rightSwipeAction": "members", + "captionPosition": "below", + "renderUserCards": "both", + "jumboEmojiSize": "large" + } +} +``` + +Invalid or unknown keys are ignored. + * To deploy on subdirectory, you need to rebuild the app youself after updating the `base` path in [`build.config.ts`](build.config.ts). * For example, if you want to deploy on `https://sable.moe/app`, then set `base: '/app'`. ## Local development + > [!TIP] -> We recommend using a version manager as versions change quickly. [fnm](https://github.com/Schniz/fnm) is a great cross-platform option (Windows, macOS, and Linux). [NVM on Windows](https://github.com/coreybutler/nvm-windows#installation--upgrades) and [nvm](https://github.com/nvm-sh/nvm) on Linux/macOS are also good choices. Use the version defined in [`.node-version`](.node-version). +> The easiest way to get started is with [mise](https://mise.jdx.dev/getting-started.html), it manages node, pnpm, rust, and other tooling. -Execute the following commands to start a development server: -```sh -fnm use --corepack-enabled # Activates the Node version and enables corepack -# If you not using fnm, install corepack manually: npm install --global corepack@latest -corepack install # Installs the pnpm version specified in package.json -pnpm i # Installs all dependencies -pnpm run dev # Serve a development version +```bash +mise install # Install all required tools +mise run setup # Install dependencies (pnpm install) +mise run dev # Start the Vite dev server ``` +Run `mise tasks` to list all available tasks (build, test, lint, etc.). + To build the app: ```sh -pnpm run build # Compiles the app into the dist/ directory +mise run build ``` +### Desktop & Mobile (Tauri) + +Sable uses [Tauri](https://v2.tauri.app) for native desktop and mobile builds. + +```bash +mise run tauri:setup # Install Rust toolchain + system packages +mise run tauri:setup:macos # Install Xcode (macOS only) +mise run tauri:setup:windows # Install VS Build Tools + WebView2 (Windows only) +mise run tauri wry dev # Dev server with system webview (WebKit/WebView2) +mise run tauri cef build # Production build with Chromium Embedded Framework +mise run tauri --help # Any other args pass through to the Tauri CLI +``` + +When the first argument is `wry` or `cef` and the second is `dev` or `build`, the wrapper injects `--features ,updater --no-default-features`. Everything else is forwarded to `tauri` as-is. + ## Deployment and infrastructure Deployment workflows and infrastructure details live in [`infra/README.md`](infra/README.md). diff --git a/TRADEMARKS.md b/TRADEMARKS.md new file mode 100644 index 0000000000..b5ddb4aba3 --- /dev/null +++ b/TRADEMARKS.md @@ -0,0 +1,63 @@ +# Trademarks + +The Sable name and the Sable logo belong to the FOSS community. + +While our source code is governed by the GNU Affero General Public License (AGPLv3), we believe the Free and Open Source ethos shouldn't stop there. We actually like it when people use, share, and build upon our work. + +## Logo + +The Sable logo is dedicated to the public domain under the CC0 1.0 Universal (Public Domain Dedication). All rights waived. + +The logo may absolutely be: + +- Modified, distorted, or recolored +- Used as part of another logo or mark +- Given a tiny top hat, sunglasses, or a cool jacket +- Used more prominently than your own branding, if you really want to for some reason + +### Permitted Uses + +You are highly encouraged to: + +- Use the name “Sable” in text anywhere you like +- Link to the official repository or documentation +- State in plain text that your project is “based on Sable”, “a fork of Sable”, or “better than Sable” +- Use the name and logo when self-hosting, whether you modified the software or not +- Strip out our branding entirely and replace it with your own. If you’re hosting, you can do whatever you want! + +### Restricted Uses + +We only have two rules to protect the community. You may not: + +- Impersonate the core Sable maintainers or use the Sable name/logo to distribute malware or malicious software. +- Use the Sable name or logo to falsely claim that the core maintainers officially legally endorse your specific corporate product. + +That's literally it. + +## Requirements for Forks and Modifications + +If you modify or build upon the Sable source code: + +You must: + +- Comply with the AGPLv3 license terms (keep open source open!). + +You may: + +- Use the Sable name, logo, or branding in your project’s name or identity (e.g., "Sable-Enterprise", "Sable for Android", "Sable-Lite"). +- Create derivative, similar, or completely wild new logos based on ours. +- Distribute or package the software for platforms not officially supported by us using the Sable name. +- Take this trademark document and derive your own version of it, provided it's for your branding, rather than Sable + +You must not: + +- Feel bad about forking our project. Forks are healthy for FOSS! + +## Community and Social Use + +Community pages, groups, or accounts: + +- Are awesome. +- Can absolutely use the name Sable. +- Can freely use the Sable logo. +- Please just add a tiny disclaimer somewhere that it's a community-run space so people don't yell at us when your server goes down for maintenance. diff --git a/config.json b/config.json index 1bdffb675d..1260284a02 100644 --- a/config.json +++ b/config.json @@ -1,4 +1,5 @@ { + "productName": "Sable", "defaultHomeserver": 0, "homeserverList": ["matrix.org", "mozilla.org", "unredacted.org", "sable.moe", "kendama.moe"], "allowCustomHomeservers": true, @@ -10,12 +11,13 @@ "pushNotificationDetails": { "pushNotifyUrl": "https://sygnal.sable.moe/_matrix/push/v1/notify", "vapidPublicKey": "BCnS4SbHjeOaqVFW4wjt5xDt_pYIL62qMzKePfYF9fl9PQU14RieIaObh7nLR_9dQf4sykZa-CTrcjkgMIE1mcg", - "webPushAppID": "moe.sable.app.sygnal" + "webPushAppID": "moe.sable.app.sygnal", + "nativePushAppID": "moe.sable.client.android", + "unifiedPushAppID": "moe.sable.up" }, - "slidingSync": { - "enabled": true - }, + "themeCatalogBaseUrl": "https://raw.githubusercontent.com/SableClient/themes/main/", + "themeCatalogApprovedHostPrefixes": ["https://raw.githubusercontent.com/SableClient/themes/"], "featuredCommunities": { "openAsDefault": false, @@ -41,5 +43,9 @@ "hashRouter": { "enabled": false, "basename": "/" + }, + + "gifs": { + "klipyApiKey": "IfeIBlDMvq0av2BcKPDuxwRqbnYRbS90yNqFHEkK2Ja207tkR5nssh3NIlJRCr76" } } diff --git a/docs/SENTRY_INTEGRATION.md b/docs/SENTRY_INTEGRATION.md index df5511e574..1d2c0315a5 100644 --- a/docs/SENTRY_INTEGRATION.md +++ b/docs/SENTRY_INTEGRATION.md @@ -241,7 +241,7 @@ docker build \ - Set `VITE_SENTRY_ENVIRONMENT=production` - Gets 10% sampling for traces and session replay - Cost-effective for production usage -- Configured in `.github/workflows/cloudflare-web-deploy.yml` +- Configured in `.github/workflows/cloudflare-web.yml` **Preview deployments (PR previews, Cloudflare Pages):** @@ -250,6 +250,15 @@ docker build \ - Full debugging capabilities for testing - Configured in `.github/workflows/cloudflare-web-preview.yml` +**Desktop & mobile (Tauri) builds (`.github/workflows/tauri-build.yml`):** + +- Tagged releases (`v*`) → `VITE_SENTRY_ENVIRONMENT=production` (10% sampling) +- Nightly builds (from `dev`) → `VITE_SENTRY_ENVIRONMENT=preview` (100% sampling) +- `VITE_SENTRY_DSN`, `SENTRY_AUTH_TOKEN`, `SENTRY_ORG`, and `SENTRY_PROJECT` are injected from repository secrets into the `build`, `android`, and `ios` jobs +- Source maps are generated for release builds **only** when the Sentry upload credentials are present, then uploaded and deleted from the bundle via `filesToDeleteAfterUpload` — so no `.map` files ship inside the app bundle. Without credentials, no source maps are emitted +- The `release` is `VITE_APP_VERSION` (the tag version, or the nightly version), matching the release the source maps are uploaded against so Sentry can un-minify stack traces +- Native (Rust) panics are captured via the `sentry` crate, consent-gated to match the JS opt-in: the SDK initialises at startup but a `before_send` hook drops all events until the frontend calls `set_native_sentry_enabled(true)` after its `sable_sentry_enabled` check + **Local development:** - `VITE_SENTRY_ENVIRONMENT` not set (defaults to `development` via Vite MODE) diff --git a/docs/SENTRY_PRIVACY.md b/docs/SENTRY_PRIVACY.md index 39be916ce7..de4321e795 100644 --- a/docs/SENTRY_PRIVACY.md +++ b/docs/SENTRY_PRIVACY.md @@ -297,6 +297,24 @@ Matrix IDs are replaced with placeholder tokens before sending: **Code:** `src/instrument.ts` — `beforeSend` callback (applied to `event.message` and all `event.exception.values`) +### Homeserver Host + +Any external http(s) origin (the Matrix homeserver, OIDC issuers, media +redirectors) is replaced with `[HOMESERVER]` before sending. Only the scheme +is kept (`https://[HOMESERVER]`). The path, query, and hash are preserved so +routing and grouping still work. + +This applies to HTTP span descriptions and `span.data` URL fields, breadcrumb +`data.url` / `data.from` / `data.to`, structured log attributes and message +strings, error messages, exception values, and request URLs. + +The app's own origin and local dev hosts (`localhost`, `127.0.0.1`, +`tauri.localhost`) are preserved. They identify the app shell, not a +homeserver. + +**Code:** `src/app/utils/sentryScrubbers.ts` — `scrubExternalHosts`, applied +inside `scrubMatrixIds` and `scrubMatrixUrl` + --- ## User Controls diff --git a/docs/screenshots/timeline.png b/docs/screenshots/timeline.png new file mode 100644 index 0000000000..8a4d0e3307 Binary files /dev/null and b/docs/screenshots/timeline.png differ diff --git a/docs/shallow-routes.md b/docs/shallow-routes.md new file mode 100644 index 0000000000..58880bae84 --- /dev/null +++ b/docs/shallow-routes.md @@ -0,0 +1,35 @@ +# Shallow routes + +Some surfaces should be an overlay on desktop and a full page on mobile: create +room, create space, bug report, navigate, and settings. They are all plain +routes — the URL is the only source of truth — and the presentation is chosen at +render time. + +## How it works + +`src/app/pages/client/shallowRoute.ts` lists the paths that behave this way. A +route is _shallow_ when it matches one of them, the viewport is not mobile, and +`location.state.backgroundLocation` is set. + +- `useOpenShallowRoute()` navigates and records the current location as the + background. Navigating within the same surface (settings section to settings + section) keeps the background it was opened over. +- `ClientRouteOutlet` renders the previous outlet while a shallow route is + active, so the page behind stays mounted. +- `ShallowRouteRenderer` (and `SettingsShallowRouteRenderer`) draw the overlay + from the persistent layout, since the route element itself is suppressed. +- `useCloseShallowRoute()` returns to the background location, or home. + +React Router's data router (`createBrowserRouter`) does not support the +`` trick — descendant `` trees cannot use +the data APIs — so the outlet is cached instead. + +## Adding a surface + +1. Add its path to `SHALLOW_ROUTE_PATHS`. +2. Render `RouteSurface` from the route element. It picks `FormModal` on desktop + and `FormPage` on mobile, and wires up close for both. +3. Add the path to `ShallowRouteRenderer`. + +`FormPage` renders its own desktop sidebar, so `RouteSurface` must only use it +when the surface is _not_ an overlay. diff --git a/docs/signing.md b/docs/signing.md new file mode 100644 index 0000000000..dff7a7aa54 --- /dev/null +++ b/docs/signing.md @@ -0,0 +1,77 @@ +## Signing secrets for releases + +Commands to generate the signing secrets used by the release workflows. Placeholders in `<...>` are chosen or provided by the maintainer. + +### Android + +```bash +# ANDROID_KEY_ALIAS — e.g. "upload" +# ANDROID_KEY_PASSWORD — a strong password (store & key must match) + +keytool -genkeypair -v \ + -keystore upload-keystore.jks \ + -alias "$ANDROID_KEY_ALIAS" \ + -keyalg RSA -keysize 2048 -validity 10000 \ + -storepass "$ANDROID_KEY_PASSWORD" \ + -keypass "$ANDROID_KEY_PASSWORD" \ + -dname "CN=, O=, C=" + +# ANDROID_KEY_BASE64 +base64 -w0 upload-keystore.jks > android-key-base64.txt +``` + +Keep `upload-keystore.jks` safe — losing it blocks future Play Store updates. + +### macOS — App Store Connect API key + +From **App Store Connect → Users and Access → Integrations → App Store Connect API**: + +- `APPLE_API_KEY` — the Key ID +- `APPLE_API_ISSUER` — the Issuer ID (UUID at top of the page) +- `APPLE_TEAM_ID` — the Team ID (Apple Developer → Membership) + +Key creation gives a one-time download of `AuthKey_.p8`. If the workflow needs it as a secret: + +```bash +base64 -w0 AuthKey_.p8 > apple-api-key-base64.txt +``` + +### Desktop auto-updater + +```bash +# TAURI_SIGNING_PRIVATE_KEY_PASSWORD — a chosen password (may be empty) +pnpm tauri signer generate -w ~/.tauri/sable.key +``` + +This writes `~/.tauri/sable.key` (private) and `~/.tauri/sable.key.pub` (public). + +- `TAURI_SIGNING_PRIVATE_KEY` — contents of `~/.tauri/sable.key` +- `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` — the chosen password +- Put the contents of `~/.tauri/sable.key.pub` in `plugins.updater.pubkey` in `tauri.conf.json` + +Never share the private key. Losing it prevents publishing updates to installed apps. + +### macOS — code signing certificate + +```bash +# APPLE_CERTIFICATE_PASSWORD — a chosen password + +openssl genrsa -out mac-signing.key 2048 +openssl req -new -key mac-signing.key -out mac-signing.csr \ + -subj "/emailAddress=/CN=/C=" + +# Upload mac-signing.csr at developer.apple.com → Certificates → +# "Developer ID Application"; download developer_id.cer + +openssl x509 -inform DER -in developer_id.cer -out developer_id.pem +openssl pkcs12 -export \ + -inkey mac-signing.key \ + -in developer_id.pem \ + -out developer_id.p12 \ + -passout pass:"$APPLE_CERTIFICATE_PASSWORD" + +# APPLE_CERTIFICATE +base64 -w0 developer_id.p12 > apple-certificate-base64.txt +``` + +On a Mac, generate the CSR via Keychain Access, export the cert+key as `.p12`, and run only the last step. diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000000..f549108fd5 --- /dev/null +++ b/flake.lock @@ -0,0 +1,159 @@ +{ + "nodes": { + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1786606848, + "narHash": "sha256-RS78j/7iFHjIMzTr9bS3PypjvPozHBfdR5xBJ+ABXXA=", + "owner": "nix-community", + "repo": "fenix", + "rev": "3df888be63045c349a6d322f0214f1346bb18be9", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1767039857, + "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=", + "owner": "NixOS", + "repo": "flake-compat", + "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1785627969, + "narHash": "sha256-4dtXQk/NMePegK/nWp5NSeuZKLATItOq61lpEvmXqGw=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "427bf4bd9435fdf21321c8cc628c24efc14c0f7a", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "git-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1784288435, + "narHash": "sha256-ReRHaLgr/uVqdD8afFSn+myXIfpHeOhP0yYe0TJqAA8=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "43b3c1ab9d40fb1dbb008f451988a91e375825e9", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1786599213, + "narHash": "sha256-yNJd40f11EzXBjSByCB7IPpeFFAdeoSKKM67dGkfFoU=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "0e251e24a4f24e036a084b6b4b2d2491af4167f4", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1785031560, + "narHash": "sha256-OmshNvn2vupOFpYinLUu+1Dnpu4n7Q5N3ggGVNHpkUI=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "0e79af5e3d4dcfcd676ab5ba3f95d2e3352e078c", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "root": { + "inputs": { + "fenix": "fenix", + "flake-parts": "flake-parts", + "git-hooks": "git-hooks", + "nixpkgs": "nixpkgs", + "treefmt-nix": "treefmt-nix" + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1786530578, + "narHash": "sha256-fCOcC64H7e0EIlIHTCsl5FQ9CW4S6VK3OLn1ugWR9zI=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "068bb508581477a5e3f753e1e206d34288dfa9c9", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1785945821, + "narHash": "sha256-NLSyTCW4K4ofhNBllt3omPasm6QpralXH1DBZOc91Dw=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "ae7910970dddc408fe6ab1c8e4b277bb21d72dc0", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000000..75daff6dc9 --- /dev/null +++ b/flake.nix @@ -0,0 +1,375 @@ +# SPDX-License-Identifier: AGPL-3.0 +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-parts.url = "github:hercules-ci/flake-parts"; + git-hooks.url = "github:cachix/git-hooks.nix"; + git-hooks.inputs.nixpkgs.follows = "nixpkgs"; + treefmt-nix.url = "github:numtide/treefmt-nix"; + treefmt-nix.inputs.nixpkgs.follows = "nixpkgs"; + fenix = { + url = "github:nix-community/fenix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + inputs@{ flake-parts, ... }: + flake-parts.lib.mkFlake { inherit inputs; } { + imports = [ + inputs.git-hooks.flakeModule + inputs.treefmt-nix.flakeModule + ]; + + systems = [ + "x86_64-linux" + "aarch64-linux" + "aarch64-darwin" + ]; + + perSystem = + { + inputs', + config, + system, + lib, + ... + }: + let + pkgs = import inputs.nixpkgs { + inherit system; + + config = { + allowUnfree = true; + android_sdk.accept_license = true; + }; + }; + + ### rust ### + + rust = ( + with inputs'.fenix.packages; + combine [ + stable.toolchain + targets.aarch64-linux-android.stable.rust-std + targets.x86_64-linux-android.stable.rust-std + targets.armv7-linux-androideabi.stable.rust-std + targets.i686-linux-android.stable.rust-std + ] + ); + + ### android ### + + platformVersion = "36"; + systemImageType = "default"; + androidEnv = pkgs.androidenv.override { licenseAccepted = true; }; + androidComp = ( + androidEnv.composeAndroidPackages { + cmdLineToolsVersion = "8.0"; + includeNDK = true; + ndkVersion = "29.0.14206865"; + buildToolsVersions = [ "35.0.0" ]; + + platformVersions = [ + "35" + platformVersion + ]; + includeEmulator = true; + includeSystemImages = true; + systemImageTypes = [ + systemImageType + ]; + abiVersions = [ + "x86" + "x86_64" + "armeabi-v7a" + "arm64-v8a" + ]; + cmakeVersions = [ "3.10.2" ]; + } + ); + android-sdk = androidComp.androidsdk; + + ### web ### + + packageJson = builtins.fromJSON (builtins.readFile ./package.json); + + nodejs = pkgs.nodejs_24; + pnpm = pkgs.pnpm_10; + # Keep in sync with package.json; currently 0.57.0. + oxfmt = + (builtins.getFlake "github:NixOS/nixpkgs/ede57125e7b774b47b6f98855a5c150a4b87b67f?narHash=sha256-wpyvzzT8H%2B8hQ3W4EyQraEbOXolQm91FqbF7OywX3fQ%3D") + .legacyPackages.${system}.oxfmt; + pnpmConfigHook = pkgs.pnpmConfigHook.override { inherit pnpm; }; + + pnpmNativeBuildInputs = [ + pkgs.pkg-config + nodejs + pnpm + pnpmConfigHook + ]; + + mkPnpmDeps = + { + src, + version, + pnpmInstallFlags, + }: + pkgs.fetchPnpmDeps { + inherit + pnpm + src + version + pnpmInstallFlags + ; + pname = "sable"; + fetcherVersion = 3; + hash = "sha256-zGUJ7Kl+nHumhrM51hS8nWzzxwUh9xn6TGPLcydakdo="; + }; + + mkPnpmCheck = + name: script: + pkgs.stdenv.mkDerivation (finalAttrs: { + pname = "sable-${name}"; + inherit (packageJson) version; + src = lib.cleanSource ./.; + + pnpmInstallFlags = [ "--ignore-scripts" ]; + + pnpmDeps = mkPnpmDeps { + inherit (finalAttrs) src version pnpmInstallFlags; + }; + + nativeBuildInputs = pnpmNativeBuildInputs; + + buildPhase = '' + runHook preBuild + pnpm run ${script} + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + touch $out + runHook postInstall + ''; + + doCheck = false; + }); + + ### CEF ### + + cef = pkgs.cef-binary.override { + version = "150.0.14"; + gitRevision = "7c1aa68"; + chromiumVersion = "150.0.7871.129"; + srcHashes = { + x86_64-linux = "sha256-QO9hPkVcrNB6p8gfQl76qLb3frg/E8wo1HDuuk5h+Y8="; + }; + }; + + # fake archive json to prevent tauri from downloading cef again https://github.com/tauri-apps/cef-rs/issues/426 + fakeArchiveJson = pkgs.writeText "archive.json" ( + builtins.toJSON { + name = cef.src.name; + sha1 = ""; + type = "minimal"; + } + ); + + cefFlat = pkgs.symlinkJoin { + name = "cef-${cef.version}-flat"; + paths = [ + "${cef}/${cef.buildType}" + "${cef}/Resources" + ]; + postBuild = '' + ln -s ${cef}/libcef_dll "$out/" + ln -s ${fakeArchiveJson} "$out/archive.json" + ''; + }; + + ### shells ### + + webPackages = [ + nodejs + pnpm + pkgs.oxlint + oxfmt + pkgs.knope + pkgs.typescript + pkgs.typescript-language-server + pkgs.nixd + pkgs.mise + ]; + + libayatana-appindicator = pkgs.libayatana-appindicator.overrideAttrs (old: { + postFixup = (old.postFixup or "") + '' + substituteInPlace "$dev/lib/pkgconfig/ayatana-appindicator3-0.1.pc" \ + --replace-fail "Requires:" "Requires.private:" + ''; + }); + + linuxDesktopBuildInputs = with pkgs; [ + at-spi2-atk + atkmm + cairo + dbus + dbus.dev + gdk-pixbuf + glib + glib-networking + gsettings-desktop-schemas + gtk3 + harfbuzz + libayatana-appindicator + librsvg + libsoup_3 + openssl + pango + webkitgtk_4_1 + ]; + + linuxRuntimeLibs = with pkgs; [ + libGL + libpulseaudio + libxkbcommon + pipewire + ]; + + gstPlugins = with pkgs.gst_all_1; [ + gstreamer + gst-plugins-base + gst-plugins-good + gst-plugins-bad + gst-plugins-ugly + gst-libav + ]; + + in + { + treefmt = { + projectRootFile = "flake.nix"; + programs = { + nixfmt.enable = true; + oxfmt = { + enable = true; + package = oxfmt; + }; + }; + settings.global.excludes = [ + "dist" + "node_modules" + "pnpm-lock.yaml" + "pnpm-workspace.yaml" + "package.json" + "LICENSE" + "CHANGELOG.md" + "./changeset" + ]; + }; + pre-commit.settings.hooks = { + treefmt = { + enable = true; + package = config.treefmt.build.wrapper; + }; + }; + + packages = { + sable = pkgs.stdenv.mkDerivation (finalAttrs: { + pname = "sable"; + inherit (packageJson) version; + src = lib.cleanSource ./.; + + pnpmInstallFlags = [ "--ignore-scripts" ]; + + pnpmDeps = mkPnpmDeps { + inherit (finalAttrs) src version pnpmInstallFlags; + }; + + nativeBuildInputs = pnpmNativeBuildInputs; + + env.VITE_BUILD_HASH = inputs.self.shortRev or inputs.self.dirtyShortRev or ""; + env.SABLE_BUILD_FLAVOR = "stable"; + + buildPhase = '' + runHook preBuild + pnpm run build + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + cp -r dist $out + runHook postInstall + ''; + }); + + default = config.packages.sable; + + android-emulator = androidEnv.emulateApp { + name = "emulate-sable"; + platformVersion = platformVersion; + abiVersion = "x86_64"; # arm64-v8a + systemImageType = systemImageType; + }; + }; + + checks = { + build = config.packages.sable; + lint = mkPnpmCheck "lint" "lint"; + fmt = mkPnpmCheck "fmt" "fmt:check"; + test = mkPnpmCheck "test" "test:run"; + typecheck = mkPnpmCheck "typecheck" "typecheck"; + knip = mkPnpmCheck "knip" "knip"; + }; + + devShells = { + default = pkgs.mkShell { + packages = webPackages; + }; + + desktop = pkgs.mkShell { + packages = webPackages ++ [ + rust + pkgs.cargo-tauri + pkgs.desktop-file-utils + pkgs.pkg-config + pkgs.xdg-utils + ]; + + buildInputs = linuxDesktopBuildInputs; + + CEF_PATH = "${cefFlat}"; + GIO_EXTRA_MODULES = "${pkgs.glib-networking}/lib/gio/modules"; + GDK_PIXBUF_MODULE_FILE = "${pkgs.librsvg}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache"; + GST_PLUGIN_SYSTEM_PATH_1_0 = lib.makeSearchPathOutput "lib" "lib/gstreamer-1.0" gstPlugins; + LD_LIBRARY_PATH = lib.concatStringsSep ":" [ + (lib.makeLibraryPath (linuxRuntimeLibs ++ linuxDesktopBuildInputs)) + "${pkgs.addDriverRunpath.driverLink}/lib" + "${cefFlat}" + ]; + RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}"; + }; + + android = pkgs.mkShell rec { + packages = webPackages ++ [ + rust + pkgs.cargo-tauri + pkgs.pkg-config + android-sdk + pkgs.jdk + ]; + + ANDROID_HOME = "${androidComp.androidsdk}/libexec/android-sdk"; + ANDROID_SDK_ROOT = "${androidComp.androidsdk}/libexec/android-sdk"; + ANDROID_NDK_ROOT = "${androidComp.androidsdk}/libexec/android-sdk/ndk-bundle"; + + GRADLE_OPTS = "-Dorg.gradle.project.android.aapt2FromMavenOverride=${ANDROID_SDK_ROOT}/build-tools/35.0.0/aapt2"; + + RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}"; + }; + }; + }; + }; +} diff --git a/index.html b/index.html index c22ea4274a..a30ee45e73 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,10 @@ - + Sable Client @@ -28,6 +31,24 @@ + + @@ -35,86 +56,86 @@ - + @@ -122,8 +143,24 @@ +
-
diff --git a/infra/README.md b/infra/README.md index 3e01697cfe..210d14358e 100644 --- a/infra/README.md +++ b/infra/README.md @@ -16,14 +16,24 @@ Required GitHub repository secrets: - `TF_CLOUDFLARE_API_TOKEN` - `TF_VAR_ACCOUNT_ID` - `TF_VAR_ZONE_ID` +- `TF_HTTP_USERNAME` +- `TF_HTTP_PASSWORD` + +Required variables in both the `development` and `production` GitHub Environments: + +- `SENTRY_ORG` +- `SENTRY_PROJECT` - `TF_HTTP_ADDRESS` - `TF_HTTP_LOCK_ADDRESS` - `TF_HTTP_UNLOCK_ADDRESS` -- `TF_HTTP_USERNAME` -- `TF_HTTP_PASSWORD` +- `TF_VAR_CUSTOM_DOMAIN` +- `TF_VAR_WORKER_NAME` + +The HTTP backend addresses must point to distinct OpenTofu states for development +and production. -The workflows map those secrets onto the actual runtime environment variable names -that Cloudflare and OpenTofu expect. +The workflows map those secrets and variables onto the runtime environment variable +names that Cloudflare and OpenTofu expect. Cloudflare API token permissions: @@ -97,8 +107,8 @@ npx wrangler versions upload Production deploys: -- `.github/workflows/cloudflare-web-deploy.yml` comments PR plans for `infra/web` changes. +- `.github/workflows/cloudflare-web.yml` comments PR plans for `infra/web` changes. - That PR plan job only runs for same-repo PRs, not fork PRs, because it needs repo secrets. -- The same workflow applies production on pushes to `dev` or manual dispatch. +- The workflow applies dev on pushes to `dev` or manual dispatch without tag; production on pushes to tags or manual dispatch with a tag. - `tofu apply` uploads `dist/` through `cloudflare_worker_version` and promotes it with `cloudflare_workers_deployment`. - Production lives on `app.sable.moe`. diff --git a/knip.json b/knip.json index 6cc8c8581c..33381b638d 100644 --- a/knip.json +++ b/knip.json @@ -1,17 +1,19 @@ { - "$schema": "https://unpkg.com/knip@5/schema.json", - "entry": ["src/sw.ts", "scripts/normalize-imports.js"], - "ignore": ["oxlint.config.ts", "oxfmt.config.ts"], + "$schema": "https://unpkg.com/knip@6/schema.json", + "entry": ["scripts/**/*.js", "tests/e2e/global.setup.ts", "tests/e2e/global.teardown.ts"], + "ignore": ["src/test/notifications-api-stub.ts"], "ignoreExportsUsedInFile": { "interface": true, "type": true }, + "ignoreFiles": ["src/app/generated/**/*"], "ignoreDependencies": [ "buffer", "@sableclient/sable-call-embedded", - "@matrix-org/matrix-sdk-crypto-wasm" + "@matrix-org/matrix-sdk-crypto-wasm", + "@sableclient/twemoji-font" ], - "ignoreBinaries": ["knope"], + "ignoreBinaries": ["knope", "mise"], "rules": { "exports": "off", "types": "off", diff --git a/knope.toml b/knope.toml index fc533824c8..a3d53ae420 100644 --- a/knope.toml +++ b/knope.toml @@ -1,7 +1,19 @@ [package] -versioned_files = ["package.json"] +versioned_files = ["package.json", "src-tauri/tauri.conf.json", "src-tauri/Cargo.toml"] changelog = "CHANGELOG.md" extra_changelog_sections = [ + { name = "Security", types = [ + "security", + ] }, + { name = "Breaking Changes", types = [ + "major", + ] }, + { name = "Features", types = [ + "minor", + ] }, + { name = "Fixes", types = [ + "patch", + ] }, { name = "Documentation", types = [ "docs", ] }, @@ -42,6 +54,8 @@ template = "chore: prepare release $version" [workflows.steps.body] template = """ +> **Preview the next release on [dev.sable.moe](https://dev.sable.moe)** + > [!IMPORTANT] > Merging this PR will create a new release. @@ -66,8 +80,8 @@ owner = "SableClient" repo = "Sable" [release_notes] -# The marker is used by prepare-release.yml change_templates = [ - "### $summary \n\n$details", - "* $summary ", + "* $summary by @$pr_author_login in #$pr_number", + "* $summary by @$pr_author_login", + "* $summary", ] diff --git a/mise.tauri.toml b/mise.tauri.toml new file mode 100644 index 0000000000..33ba8a4c45 --- /dev/null +++ b/mise.tauri.toml @@ -0,0 +1,65 @@ +# Tauri-specific dev tools and system dependencies. +# Loaded via `MISE_ENV=tauri` (set by the `tauri:setup` task's env, or CI). + +[env] +ANDROID_NDK_VERSION = "29.0.14206865" + +[settings] +idiomatic_version_file_enable_tools = ["pnpm", "node", "rust"] +experimental = true + +[tools] +# Android +java = "temurin-21" +android-sdk = "22.0" +actionlint = "1.7.10" +"aqua:goreleaser/nfpm" = "2.47.0" + +[bootstrap.packages] +# Debian/Ubuntu +"apt:build-essential" = "latest" +"apt:file" = "latest" +"apt:libayatana-appindicator3-dev" = "latest" +"apt:libgtk-3-dev" = "latest" +"apt:librsvg2-dev" = "latest" +"apt:libsoup-3.0-dev" = "latest" +"apt:libssl-dev" = "latest" +"apt:libwebkit2gtk-4.1-dev" = "latest" +"apt:libxdo-dev" = "latest" +"apt:pkg-config" = "latest" +"apt:curl" = "latest" +"apt:wget" = "latest" + +# Fedora/RHEL +"dnf:webkit2gtk4.1-devel" = "latest" +"dnf:openssl-devel" = "latest" +"dnf:libappindicator-gtk3-devel" = "latest" +"dnf:librsvg2-devel" = "latest" +"dnf:libxdo-devel" = "latest" +"dnf:curl" = "latest" +"dnf:wget" = "latest" +"dnf:file" = "latest" + +# Arch +"pacman:webkit2gtk-4.1" = "latest" +"pacman:base-devel" = "latest" +"pacman:openssl" = "latest" +"pacman:librsvg" = "latest" +"pacman:xdotool" = "latest" +"pacman:curl" = "latest" +"pacman:wget" = "latest" +"pacman:file" = "latest" + +# Alpine +"apk:webkit2gtk-4.1-dev" = "latest" +"apk:build-base" = "latest" +"apk:openssl" = "latest" +"apk:libayatana-appindicator-dev" = "latest" +"apk:librsvg" = "latest" +"apk:curl" = "latest" +"apk:wget" = "latest" +"apk:file" = "latest" + +# macOS ships curl, and the iOS setup task installs CocoaPods on demand with +# Homebrew itself. Keeping these out of mise's bootstrap avoids link conflicts +# with packages preinstalled on GitHub's macOS runners. diff --git a/mise.toml b/mise.toml new file mode 100644 index 0000000000..50646cb50c --- /dev/null +++ b/mise.toml @@ -0,0 +1,136 @@ +min_version = '2026.7.11' + +[env] +_.path = ["{{config_root}}/node_modules/.bin"] +NODE_OPTIONS = "--max-old-space-size=4192" + +[settings] +idiomatic_version_file_enable_tools = ["pnpm", "node"] +minimum_release_age = "1d" +experimental = true + +[task_config] +includes = ["scripts"] + +[tasks.setup] +description = "Install dependencies with pnpm" +run = 'pnpm install' +sources = ['package.json', 'pnpm-lock.yaml', 'pnpm-workspace.yaml', 'mise.toml'] +outputs = ['node_modules/.pnpm/lock.yaml'] + +[tasks.dev] +description = "Start Vite dev server" +alias = "d" +run = "vite dev" +raw = true + +[tasks.build] +description = "Build frontend with Vite" +alias = "b" +run = "vite build" + +[tasks.preview] +description = "Preview the production build" +alias = "p" +run = "vite preview" + +[tasks.lint] +description = "Check lint, types, formatting, and dead code" +alias = "l" +run = ["tsc", "oxlint .", "oxfmt --check .", "knip"] + +[tasks."lint:fix"] +description = "Fix lint, formatting, and dead code issues" +alias = "lf" +run = ["oxlint . --fix", "oxfmt .", "knip --fix"] + +[tasks.test] +description = "Run tests" +alias = "t" +run = "vitest" +raw = true + +[tasks.knope] +run = "knope" +raw_args = true +hide = true +tools = { "cargo:knope" = "0.23.0", cargo-binstall = "1.20.1" } + +[tasks."knope:document-change"] +description = "Document a change with knope" +alias = ["doc", "dc"] +run = { task = "knope", args = ["document-change"] } + +[tasks."tauri:setup"] +description = "Install Tauri tools and system packages" +run = ["mise install", "mise bootstrap packages apply --yes --update"] +env = { MISE_ENV = "tauri" } + +[tasks."tauri:setup:android"] +description = "Install Android SDK packages + NDK (no Android Studio needed)" +depends = ["tauri:setup"] +env = { MISE_ENV = "tauri" } +run = [ + "yes | sdkmanager --licenses", + 'sdkmanager "platform-tools" "platforms;android-36" "build-tools;35.0.0" "ndk;{{env.ANDROID_NDK_VERSION}}"', +] + +[tasks."tauri:setup:macos"] +description = "Install Xcode" +confirm = { message = "Install Xcode? (required for Tauri)", default = "no" } +run = "xcode-select --print-path >/dev/null 2>&1 || xcode-select --install" + +[tasks."tauri:setup:ios"] +description = "Set up iOS Xcode project" +depends = ["tauri:setup:macos"] +confirm = { message = "Set up iOS Xcode project? (required for Tauri)", default = "no" } +run = ["command -v pod >/dev/null 2>&1 || brew install cocoapods", "pnpm tauri ios init"] + +[tasks."tauri:setup:windows"] +description = "Install Visual Studio Build Tools + WebView2 runtime" +confirm = { message = "Install Visual Studio Build Tools and WebView2 runtime? (required for Tauri)", default = "no" } +run = [ + "winget install --id Microsoft.VisualStudio.BuildTools --source winget --force --override '--add Microsoft.VisualStudio.Workload.VCTools --includeRecommended --add Microsoft.VisualStudio.Component.Windows11SDK.26100 --addProductLang En-us'", + "winget install --id Microsoft.EdgeWebView2Runtime --source winget", +] + +[tasks."cargo:lint"] +description = "Check Rust formatting and Clippy lints" +alias = ["cl", "cargo-lint"] +dir = "src-tauri" +run = ["cargo fmt --check", "cargo clippy --locked --all-targets --all-features -- -D warnings"] + +[tasks."cargo:lint:fix"] +description = "Fix Rust formatting and Clippy issues" +alias = ["clf", "cargo-lint-fix"] +dir = "src-tauri" +run = ["cargo fmt", "cargo clippy --fix --allow-dirty --all-targets --all-features -- -D warnings"] + +[tasks."cargo:test"] +description = "Run Rust tests" +alias = ["ct", "cargo-test"] +dir = "src-tauri" +run = "cargo test --locked" +raw = true + +[tasks."actions:pin"] +description = "Pin GitHub Actions to SHA commit hashes" +run = "pinact run" +tools = { "pinact" = "latest" } + +[tasks."actions:audit"] +description = "Audit GitHub Actions for security issues" +run = "zizmor .github/workflows" +tools = { "zizmor" = "latest" } + +[tasks."actions:update"] +description = "Update pinned GitHub Actions to latest SHAs" +run = "pinact run -u" +tools = { "pinact" = "latest" } + +[tasks.opentofu] +run = "tofu" +dir = "infra/web" +raw_args = true +hide = true +tools = { "opentofu" = "1.11.5" } diff --git a/nfpm.yaml b/nfpm.yaml new file mode 100644 index 0000000000..9f71db3daf --- /dev/null +++ b/nfpm.yaml @@ -0,0 +1,58 @@ +# yaml-language-server: $schema=https://nfpm.goreleaser.com/schema.json + +name: sable +arch: ${PKG_ARCH} +platform: linux +version: ${PKG_VERSION} +release: ${PKG_RELEASE} +version_schema: semver +section: net +maintainer: SableClient +description: Sable, a Matrix client +homepage: https://sable.moe + +contents: + - src: ${PKGROOT}/opt/sable/ + dst: /opt/sable + type: tree + expand: true + - src: ${PKGROOT}/usr/ + dst: /usr + type: tree + expand: true + - src: src-tauri/packaging/deb/apparmor/sable + dst: /etc/apparmor.d/sable + type: config + packager: deb + +overrides: + deb: + scripts: + postinstall: src-tauri/packaging/deb/postinst + preremove: src-tauri/packaging/deb/prerm + depends: + - apparmor + - libgtk-3-0 + - libnss3 + - libnspr4 + - libgbm1 + - libdrm2 + - libxkbcommon0 + - libasound2 + - libcups2 + - xdg-utils + - xwayland + - libayatana-appindicator3-1 + rpm: + depends: + - gtk3 + - nss + - nspr + - mesa-libgbm + - libdrm + - libxkbcommon.so.0()(64bit) + - alsa-lib + - cups-libs + - xdg-utils + - xorg-x11-server-Xwayland + - libayatana-appindicator3.so.1()(64bit) diff --git a/oxfmt.config.ts b/oxfmt.config.ts index 593c2425ca..a2bd4e77ba 100644 --- a/oxfmt.config.ts +++ b/oxfmt.config.ts @@ -1,6 +1,6 @@ -import { defineConfig } from 'oxfmt'; +import type { OxfmtConfig } from 'oxfmt'; -export default defineConfig({ +export default { printWidth: 100, tabWidth: 2, singleQuote: true, @@ -8,6 +8,8 @@ export default defineConfig({ ignorePatterns: [ 'dist', 'node_modules', + 'src/app/generated', + 'src-tauri/ios-project.yml', 'package.json', 'pnpm-lock.yaml', 'LICENSE', @@ -15,4 +17,4 @@ export default defineConfig({ 'CHANGELOG.md', './changeset', ], -}); +} satisfies OxfmtConfig; diff --git a/oxlint.config.ts b/oxlint.config.ts index 8681734142..9d1c044644 100644 --- a/oxlint.config.ts +++ b/oxlint.config.ts @@ -1,6 +1,7 @@ import { defineConfig } from 'oxlint'; export default defineConfig({ + ignorePatterns: ['src/app/generated/**/*'], options: { typeAware: true, }, @@ -16,6 +17,20 @@ export default defineConfig({ builtin: true, }, rules: { + 'import/no-cycle': ['error', { maxDepth: 3 }], + 'no-restricted-imports': [ + 'error', + { + paths: [ + { + name: 'folds', + importNames: ['Overlay', 'PopOut', 'TooltipProvider'], + message: + 'Import Overlay/PopOut/TooltipProvider from $components/overlay-stack so they stack by open order.', + }, + ], + }, + ], 'import/no-unassigned-import': 'off', 'import/no-named-as-default': 'off', 'import/no-named-as-default-member': 'off', @@ -33,8 +48,30 @@ export default defineConfig({ 'typescript/no-unsafe-type-assertion': 'off', 'typescript/no-floating-promises': 'off', 'typescript/no-unnecessary-type-arguments': 'off', + // Maybe reconsider this in the future, but it causes a lot of cascading issues + // because of types we use that don't exist in the matrix-js-sdk + 'typescript/no-unnecessary-type-assertion': 'off', 'oxc/no-map-spread': 'off', 'promise/always-return': 'off', + 'no-underscore-dangle': [ + 'warn', + { + allow: [ + '_fetched', + '_fetchedAt', + '__dirname', + '__filename', + '__sableAndroidBack', + '__SABLE_PRELOAD', + '__WB_MANIFEST', + '_unstable_sendDelayedEvent', + '_unstable_getDelayedEvents', + '_unstable_updateDelayedEvent', + '_unstable_sendDelayedStateEvent', + '_unstable_getSharedRooms', + ], + }, + ], }, overrides: [ { @@ -58,5 +95,11 @@ export default defineConfig({ 'typescript/no-unsafe-enum-comparison': 'off', }, }, + { + files: ['tests/e2e/**'], + rules: { + 'react/rules-of-hooks': 'off', + }, + }, ], }); diff --git a/package.json b/package.json index 47bfe902fd..8f7ce0a75f 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,11 @@ { "name": "sable", - "version": "1.14.0", + "version": "1.21.0", "description": "An almost stable Matrix client", + "keywords": [], + "license": "AGPL-3.0-only", + "author": "7w1", "type": "module", - "packageManager": "pnpm@10.32.1+sha512.a706938f0e89ac1456b6563eab4edf1d1faf3368d1191fc5c59790e96dc918e4456ab2e67d613de1043d2e8c81f87303e6b40d4ffeca9df15ef1ad567348f2be", - "engines": { - "node": "24.x", - "pnpm": ">=10" - }, "scripts": { "dev": "vite dev", "build": "vite build", @@ -15,119 +13,170 @@ "lint": "oxlint .", "lint:fix": "oxlint . --fix && oxfmt .", "fmt": "oxfmt .", + "format": "oxfmt .", "fmt:check": "oxfmt --check .", "typecheck": "tsc", + "tauri": "tauri", + "tauri:cef": "node scripts/tauri.js cef", + "ios:frameworks": "node scripts/download-ios-frameworks.js", + "tauri:wry": "node scripts/tauri.js wry", "test": "vitest", "test:ui": "vitest --ui", "test:run": "vitest run", "test:coverage": "vitest run --coverage", + "test:e2e": "playwright test", + "test:e2e:update": "playwright test --update-snapshots", + "test:e2e:docker": "node scripts/e2e-docker.js", + "test:e2e:docker:update": "node scripts/e2e-docker.js --update-snapshots", + "test:e2e:ui": "playwright test --ui", + "test:e2e:report": "playwright show-report", "knip": "knip", "tunnel": "cloudflared tunnel --url http://localhost:8080", "knope": "knope", "document-change": "knope document-change", - "postinstall": "node scripts/install-knope.js" + "postinstall": "node scripts/install-knope.js", + "setup:knope": "node scripts/install-knope.js", + "setup:tauri-icons": "node scripts/icons/symlink.js", + "generate:app-icons:android": "node node_modules/tauri-plugin-app-icon-api/scripts/generate-android.mjs --config src-tauri/app-icons.json && pnpm setup:tauri-icons --write --force", + "generate:app-icons:ios": "node node_modules/tauri-plugin-app-icon-api/scripts/generate-ios.mjs --config src-tauri/app-icons.json", + "lint:normalize-imports": "node scripts/normalize-imports.js" }, - "keywords": [], - "author": "7w1", - "license": "AGPL-3.0-only", "dependencies": { - "@arborium/arborium": "^2.16.0", - "@atlaskit/pragmatic-drag-and-drop": "^1.7.7", - "@atlaskit/pragmatic-drag-and-drop-auto-scroll": "^1.4.0", - "@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.1.0", - "@fontsource-variable/nunito": "5.2.7", - "@fontsource/space-mono": "5.2.9", + "@arborium/arborium": "^2.18.1", + "@atlaskit/pragmatic-drag-and-drop": "^2.0.2", + "@atlaskit/pragmatic-drag-and-drop-auto-scroll": "^3.0.0", + "@atlaskit/pragmatic-drag-and-drop-hitbox": "^2.0.0", + "@choochmeque/tauri-plugin-sharekit-api": "0.4.0-rc.5", + "@fontsource-variable/nunito": "5.3.0", + "@fontsource/space-mono": "5.3.0", + "@livekit/components-react": "2.9.23", + "@lottiefiles/dotlottie-react": "^0.19.14", + "@lottiefiles/dotlottie-web": "0.79.1", + "@noble/hashes": "^2.3.0", "@phosphor-icons/react": "^2.1.10", - "@sentry/react": "^10.43.0", - "@tanstack/react-query": "^5.90.21", - "@tanstack/react-query-devtools": "^5.91.3", - "@tanstack/react-virtual": "^3.13.19", + "@rolldown/plugin-node-polyfills": "^1.0.3", + "@sableclient/matrixrtc": "^0.1.0", + "@sableclient/tauri-plugin-livekit-mobile": "^0.2.0", + "@sableclient/tauri-plugin-notifications-api": "^0.5.2", + "@sableclient/twemoji-font": "^1.0.4", + "@sentry/react": "^10.70.0", + "@tanstack/react-query": "^5.101.4", + "@tanstack/react-query-devtools": "^5.101.4", + "@tanstack/react-virtual": "^3.14.9", + "@tauri-apps/api": "2.11.1", + "@tauri-apps/plugin-clipboard-manager": "2.3.2", + "@tauri-apps/plugin-deep-link": "2.4.9", + "@tauri-apps/plugin-dialog": "2.7.2", + "@tauri-apps/plugin-fs": "2.5.1", + "@tauri-apps/plugin-http": "2.5.9", + "@tauri-apps/plugin-opener": "2.5.4", + "@tauri-apps/plugin-os": "2.3.2", + "@tauri-apps/plugin-process": "~2.3.1", + "@tauri-apps/plugin-store": "^2.4.4", + "@tauri-apps/plugin-updater": "~2.10.1", "@use-gesture/react": "10.3.1", - "@vanilla-extract/css": "^1.18.0", + "@vanilla-extract/css": "^1.21.2", "@vanilla-extract/recipes": "^0.5.7", - "@vanilla-extract/vite-plugin": "^5.1.4", + "@vanilla-extract/vite-plugin": "^5.2.6", "await-to-js": "^3.0.0", - "badwords-list": "^2.0.1-4", + "badwords-list": "2.0.1-4", "blurhash": "^2.0.5", "browser-encrypt-attachment": "^0.3.0", "chroma-js": "^3.2.0", "classnames": "^2.5.1", - "dayjs": "^1.11.19", - "domhandler": "^5.0.3", - "dompurify": "^3.3.3", - "emojibase": "^15.3.1", - "emojibase-data": "^15.3.2", + "dayjs": "^1.11.21", + "domhandler": "^6.0.1", + "dompurify": "^3.4.13", + "emojibase": "^17.0.0", + "emojibase-data": "^17.0.0", "eventemitter3": "^5.0.4", "file-saver": "^2.0.5", - "focus-trap-react": "^10.3.1", - "folds": "^2.6.2", - "framer-motion": "12.34.3", - "html-dom-parser": "^5.1.8", - "html-react-parser": "^4.2.10", - "i18next": "^25.8.13", + "focus-trap-react": "^12.0.3", + "folds": "^2.7.1", + "html-dom-parser": "^8.0.1", + "html-react-parser": "^6.1.5", + "i18next": "^26.3.6", "i18next-browser-languagedetector": "^8.2.1", - "i18next-http-backend": "^2.7.3", - "immer": "^9.0.21", + "i18next-http-backend": "^4.0.1", + "immer": "^11.1.16", "is-hotkey": "^0.2.0", - "jotai": "^2.18.0", - "linkify-react": "^4.3.2", - "linkifyjs": "^4.3.2", - "matrix-js-sdk": "^38.4.0", - "matrix-widget-api": "^1.16.1", - "pdfjs-dist": "^5.4.624", - "react": "^18.3.1", - "react-aria": "^3.46.0", + "jotai": "^2.20.2", + "katex": "^0.18.1", + "leaflet": "1.9.4", + "linkify-react": "^4.3.3", + "linkifyjs": "^4.3.3", + "livekit-client": "2.21.0", + "marked": "^18.0.9", + "matrix-js-sdk": "42.0.0", + "matrix-widget-api": "^1.17.0", + "nanoid": "^6.0.1", + "pdfjs-dist": "^6.2.108", + "prosemirror-commands": "^1.7.2", + "prosemirror-history": "^1.5.0", + "prosemirror-keymap": "^1.2.3", + "prosemirror-model": "^1.25.11", + "prosemirror-state": "^1.4.4", + "prosemirror-view": "^1.42.2", + "react": "^19.2.8", + "react-aria": "^3.50.0", "react-blurhash": "^0.3.0", - "react-colorful": "^5.6.1", - "react-dom": "^18.3.1", - "react-google-recaptcha": "^2.1.0", - "react-i18next": "^16.5.4", + "react-colorful": "^5.8.0", + "react-dom": "^19.2.8", + "react-google-recaptcha": "^3.1.0", + "react-i18next": "^17.0.11", + "react-leaflet": "^5.0.0", "react-range": "^1.10.0", - "react-router-dom": "^6.30.3", - "slate": "^0.123.0", - "slate-dom": "^0.123.0", - "slate-history": "^0.113.1", - "slate-react": "^0.123.0", - "ua-parser-js": "^1.0.41", - "virtua": "^0.48.8", - "workbox-precaching": "^7.4.0" + "react-router": "^8.3.0", + "tauri-plugin-android-fs-api": "28.4.0", + "ua-parser-js": "^2.0.10", + "virtua": "^0.50.1", + "workbox-precaching": "^7.4.1" }, "devDependencies": { - "@cloudflare/vite-plugin": "^1.26.0", - "@esbuild-plugins/node-globals-polyfill": "^0.2.3", + "@faker-js/faker": "10.5.0", + "@playwright/test": "^1.62.0", "@rollup/plugin-inject": "^5.0.5", "@rollup/plugin-wasm": "^6.2.2", - "@sableclient/sable-call-embedded": "v1.1.4", - "@sentry/vite-plugin": "^5.1.1", - "@testing-library/jest-dom": "^6.9.1", + "@sableclient/sable-call-embedded": "1.1.8", + "@sentry/vite-plugin": "^5.4.0", + "@tauri-apps/cli": "2.11.4", + "tauri-plugin-app-icon-api": "github:SableClient/tauri-plugin-app-icon#bab80ae17f6d018702e472c539d0f02eba480c21", + "@testing-library/jest-dom": "^7.0.0", "@testing-library/react": "^16.3.2", - "@testing-library/user-event": "^14.6.1", + "@testing-library/user-event": "^14.6.3", "@types/chroma-js": "^3.1.2", "@types/file-saver": "^2.0.7", "@types/is-hotkey": "^0.1.10", - "@types/node": "24.10.13", - "@types/react": "^18.3.28", - "@types/react-dom": "^18.3.7", + "@types/leaflet": "^1.9.22", + "@types/node": "24.13.2", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", "@types/react-google-recaptcha": "^2.1.9", - "@types/ua-parser-js": "^0.7.39", - "@vitejs/plugin-react": "^5.1.4", - "@vitest/coverage-v8": "^4.1.0", - "@vitest/ui": "^4.1.0", + "@vitejs/plugin-react": "^6.0.4", + "@vitest/coverage-v8": "^4.1.10", + "@vitest/ui": "^4.1.10", "buffer": "^6.0.3", "cloudflared": "^0.7.1", - "jsdom": "^29.0.0", - "knip": "5.85.0", - "oxfmt": "^0.45.0", - "oxlint": "^1.60.0", - "oxlint-tsgolint": "^0.21.0", - "typescript": "^5.9.3", - "vite": "^7.3.1", - "vite-plugin-compression2": "2.5.0", - "vite-plugin-pwa": "^1.2.0", - "vite-plugin-static-copy": "^3.2.0", - "vite-plugin-svgr": "4.5.0", - "vite-plugin-top-level-await": "^1.6.0", - "vitest": "^4.1.0", - "wrangler": "^4.70.0" - } -} \ No newline at end of file + "fake-indexeddb": "6.2.5", + "jsdom": "^30.0.1", + "knip": "6.32.1", + "oxfmt": "^0.61.0", + "oxlint": "^1.76.0", + "oxlint-tsgolint": "^7.0.2001", + "testcontainers": "^12.0.4", + "typescript": "^7.0.2", + "vite": "^8.2.0", + "vite-plugin-compression2": "^2.5.3", + "vite-plugin-pwa": "^1.3.0", + "vite-plugin-static-copy": "^4.1.1", + "vite-plugin-svgr": "5.2.0", + "vitest": "^4.1.10" + }, + "devEngines": { + "runtime": { + "name": "node", + "version": "^24.18.0" + } + }, + "packageManager": "pnpm@11.18.0+sha512.33d83c77da82f49fba836925c6f1b841181ec3132b670639bd012f7075f5c7cf634c5f870147c19aae7478fac01df09d8892e880454896edd23ee9b33757563c" +} diff --git a/packaging/aur/sable-nightly-bin.PKGBUILD b/packaging/aur/sable-nightly-bin.PKGBUILD new file mode 100644 index 0000000000..316bdf8655 --- /dev/null +++ b/packaging/aur/sable-nightly-bin.PKGBUILD @@ -0,0 +1,45 @@ +# Maintainer: Sable Maintainers + +pkgname=sable-nightly-bin +# pkgver mirrors the release version with '-' replaced by '.', so each nightly +# sorts above the last. _relver keeps the original form for the asset URL. +pkgver=1.20.1.nightly.2607242130 +pkgrel=1 +_relver=1.20.1-nightly.2607242130 +pkgdesc="An almost stable Matrix client (nightly builds)" +arch=('x86_64') +url="https://github.com/SableClient/Sable" +license=('AGPL-3.0-or-later') +# The bundled CEF runtime needs Chromium's system libraries, not webkit2gtk. +depends=( + 'gtk3' + 'nss' + 'alsa-lib' + 'libcups' + 'libdrm' + 'mesa' + 'libxkbcommon' + 'libxss' + 'libxcomposite' + 'libxdamage' + 'libxrandr' + 'at-spi2-core' + 'dbus' + 'libayatana-appindicator' + 'hicolor-icon-theme' + 'desktop-file-utils' + 'xdg-utils' +) +provides=('sable') +conflicts=('sable' 'sable-bin') +options=('!strip' '!debug') +install=${pkgname}.install +source_x86_64=("${pkgname}-${pkgver}.deb::${url}/releases/download/nightly/Sable-${_relver}-linux-x86_64.deb") +sha256sums_x86_64=('0000000000000000000000000000000000000000000000000000000000000000') + +package() { + # bsdtar reads whichever compression nfpm used for data.tar.* + bsdtar -O -xf "${srcdir}/${pkgname}-${pkgver}.deb" 'data.tar*' \ + | bsdtar -xp -C "${pkgdir}" + find "${pkgdir}" -type d -exec chmod 755 {} + +} diff --git a/packaging/aur/sable-nightly-bin.install b/packaging/aur/sable-nightly-bin.install new file mode 100644 index 0000000000..71627fa642 --- /dev/null +++ b/packaging/aur/sable-nightly-bin.install @@ -0,0 +1,12 @@ +post_install() { + gtk-update-icon-cache -q -t -f /usr/share/icons/hicolor + update-desktop-database -q +} + +post_upgrade() { + post_install +} + +post_remove() { + post_install +} diff --git a/packaging/flatpak/flathub.json b/packaging/flatpak/flathub.json new file mode 100644 index 0000000000..637604e82e --- /dev/null +++ b/packaging/flatpak/flathub.json @@ -0,0 +1,3 @@ +{ + "only-arches": ["x86_64"] +} diff --git a/packaging/flatpak/icons/128x128.png b/packaging/flatpak/icons/128x128.png new file mode 100644 index 0000000000..a2ea9ba0ee Binary files /dev/null and b/packaging/flatpak/icons/128x128.png differ diff --git a/packaging/flatpak/icons/256x256.png b/packaging/flatpak/icons/256x256.png new file mode 100644 index 0000000000..8e5fc5c0a7 Binary files /dev/null and b/packaging/flatpak/icons/256x256.png differ diff --git a/packaging/flatpak/icons/32x32.png b/packaging/flatpak/icons/32x32.png new file mode 100644 index 0000000000..edf0662cc2 Binary files /dev/null and b/packaging/flatpak/icons/32x32.png differ diff --git a/packaging/flatpak/icons/64x64.png b/packaging/flatpak/icons/64x64.png new file mode 100644 index 0000000000..355c0b8391 Binary files /dev/null and b/packaging/flatpak/icons/64x64.png differ diff --git a/packaging/flatpak/moe.sable.client.desktop b/packaging/flatpak/moe.sable.client.desktop new file mode 100644 index 0000000000..005db10267 --- /dev/null +++ b/packaging/flatpak/moe.sable.client.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Type=Application +Name=Sable +Comment=Chat over the Matrix network +Exec=sable %U +Icon=moe.sable.client +Terminal=false +Categories=Network;InstantMessaging;Chat; +StartupWMClass=sable +MimeType=x-scheme-handler/sable;x-scheme-handler/moe.sable.app; diff --git a/packaging/flatpak/moe.sable.client.metainfo.xml b/packaging/flatpak/moe.sable.client.metainfo.xml new file mode 100644 index 0000000000..1af1ebfa69 --- /dev/null +++ b/packaging/flatpak/moe.sable.client.metainfo.xml @@ -0,0 +1,58 @@ + + + moe.sable.client + + Sable + Chat over the Matrix network + + + Sable Maintainers + + + CC0-1.0 + AGPL-3.0-only + + https://sable.moe + https://github.com/SableClient/Sable/issues + https://github.com/SableClient/Sable + + + intense + intense + + + + pointing + keyboard + touch + + + +

+ Sable is a Matrix client for desktop, with end-to-end encrypted + messaging, spaces, threads, and voice and video calls. +

+

Features:

+
    +
  • End-to-end encryption with cross-signing and key backup
  • +
  • Spaces, threads, and replies
  • +
  • Voice and video calls
  • +
  • Custom themes
  • +
+
+ + moe.sable.client.desktop + + + + https://raw.githubusercontent.com/SableClient/Sable/dev/docs/screenshots/timeline.png + A room timeline + + + + + + https://github.com/SableClient/Sable/releases/tag/v1.21.0 + + +
diff --git a/packaging/flatpak/moe.sable.client.yml b/packaging/flatpak/moe.sable.client.yml new file mode 100644 index 0000000000..9ffff6ea9c --- /dev/null +++ b/packaging/flatpak/moe.sable.client.yml @@ -0,0 +1,103 @@ +id: moe.sable.client + +runtime: org.freedesktop.Platform +runtime-version: '25.08' +sdk: org.freedesktop.Sdk + +base: org.electronjs.Electron2.BaseApp +base-version: '25.08' + +command: sable + +finish-args: + - --socket=x11 + - --share=ipc + - --device=dri + - --share=network + - --socket=pulseaudio + - --device=all + - --talk-name=org.freedesktop.secrets + - --talk-name=org.freedesktop.Notifications + - --talk-name=org.kde.StatusNotifierWatcher + - --filesystem=xdg-download + - --env=XCURSOR_PATH=/run/host/user-share/icons:/run/host/share/icons + +modules: + - name: xdg-utils + buildsystem: simple + build-commands: + - autoreconf -fi + - ./configure --prefix=${FLATPAK_DEST} + - touch scripts/xdg-mime.txt + - make -C scripts xdg-mime + - install -Dm755 scripts/xdg-mime ${FLATPAK_DEST}/bin/xdg-mime + sources: + - type: git + url: https://gitlab.freedesktop.org/xdg/xdg-utils.git + tag: v1.2.1 + commit: 356c380ad6fecc9ce6bea1f6a77986ba67402c80 + + - name: desktop-file-utils + buildsystem: meson + sources: + - type: archive + url: https://www.freedesktop.org/software/desktop-file-utils/releases/desktop-file-utils-0.28.tar.xz + sha256: 4401d4e231d842c2de8242395a74a395ca468cd96f5f610d822df33594898a70 + + - name: sable + buildsystem: simple + build-commands: + - install -Dm755 apply_extra ${FLATPAK_DEST}/bin/apply_extra + - install -Dm755 sable.sh ${FLATPAK_DEST}/bin/sable + - install -Dm644 LICENSE ${FLATPAK_DEST}/share/licenses/${FLATPAK_ID}/LICENSE + - install -Dm644 moe.sable.client.metainfo.xml ${FLATPAK_DEST}/share/metainfo/${FLATPAK_ID}.metainfo.xml + - install -Dm644 moe.sable.client.desktop ${FLATPAK_DEST}/share/applications/${FLATPAK_ID}.desktop + - install -Dm644 32x32.png ${FLATPAK_DEST}/share/icons/hicolor/32x32/apps/${FLATPAK_ID}.png + - install -Dm644 64x64.png ${FLATPAK_DEST}/share/icons/hicolor/64x64/apps/${FLATPAK_ID}.png + - install -Dm644 128x128.png ${FLATPAK_DEST}/share/icons/hicolor/128x128/apps/${FLATPAK_ID}.png + - install -Dm644 256x256.png ${FLATPAK_DEST}/share/icons/hicolor/256x256/apps/${FLATPAK_ID}.png + sources: + - type: extra-data + filename: sable.tar.gz + url: https://github.com/SableClient/Sable/releases/download/v1.21.0/Sable-1.21.0-linux-x86_64.tar.gz + sha256: 57caeeaa7b4c4002afd1bc6bbc47ba5807a3e326d77b55545f7e6ea2d7f69df0 + size: 159125471 + only-arches: [x86_64] + x-checker-data: + type: json + url: https://api.github.com/repos/SableClient/Sable/releases/latest + version-query: .tag_name | sub("^v"; "") + url-query: .assets[] | select(.name == "Sable-" + $version + "-linux-x86_64.tar.gz") + | .browser_download_url + is-main-source: true + - type: script + dest-filename: apply_extra + commands: + - set -eu + - mkdir -p sable + - tar --no-same-owner -xzf sable.tar.gz -C sable + - mv sable/runtime/* sable/ + - rmdir sable/runtime + - rm sable.tar.gz + - type: file + path: sable.sh + - type: file + url: https://raw.githubusercontent.com/SableClient/Sable/v1.21.0/LICENSE + sha256: 4df3c306dddaaf4baffdff5ca820cc679ac8cd6dc263c6a74517783e42fa7a3b + dest-filename: LICENSE + - type: file + path: moe.sable.client.desktop + - type: file + path: moe.sable.client.metainfo.xml + - type: file + path: icons/32x32.png + dest-filename: 32x32.png + - type: file + path: icons/64x64.png + dest-filename: 64x64.png + - type: file + path: icons/128x128.png + dest-filename: 128x128.png + - type: file + path: icons/256x256.png + dest-filename: 256x256.png diff --git a/packaging/flatpak/sable.sh b/packaging/flatpak/sable.sh new file mode 100755 index 0000000000..b46b50747a --- /dev/null +++ b/packaging/flatpak/sable.sh @@ -0,0 +1,3 @@ +#!/bin/sh +export ZYPAK_CEF_LIBRARY_PATH=/app/extra/sable/libcef.so +exec zypak-wrapper /app/extra/sable/sable "$@" diff --git a/packaging/homebrew/sable.rb b/packaging/homebrew/sable.rb new file mode 100644 index 0000000000..a6786adc57 --- /dev/null +++ b/packaging/homebrew/sable.rb @@ -0,0 +1,40 @@ +# Generated from packaging/homebrew/sable.rb in SableClient/Sable. Edit it there. +cask 'sable' do + version '1.20.0' + sha256 '0000000000000000000000000000000000000000000000000000000000000000' + + url "https://github.com/SableClient/Sable/releases/download/v#{version}/Sable-#{version}-macos-universal.dmg", + verified: 'github.com/SableClient/Sable/' + name 'Sable' + desc 'Client for the Matrix chat network' + homepage 'https://sable.moe/' + + livecheck do + url :url + strategy :github_latest + end + + # The app updates itself through tauri-plugin-updater. + auto_updates true + depends_on :macos + + app 'Sable.app' + + # The DMG is neither signed nor notarized, so Gatekeeper would refuse to open + # it. Drop the quarantine flag the download picked up. + postflight do + system_command '/usr/bin/xattr', + args: ['-dr', 'com.apple.quarantine', "#{appdir}/Sable.app"] + end + + uninstall quit: 'moe.sable.client' + + zap trash: [ + '~/Library/Application Support/moe.sable.client', + '~/Library/Caches/moe.sable.client', + '~/Library/HTTPStorages/moe.sable.client', + '~/Library/Preferences/moe.sable.client.plist', + '~/Library/Saved Application State/moe.sable.client.savedState', + '~/Library/WebKit/moe.sable.client' + ] +end diff --git a/packaging/licenses/CEF-LICENSE.txt b/packaging/licenses/CEF-LICENSE.txt new file mode 100644 index 0000000000..b55d613db3 --- /dev/null +++ b/packaging/licenses/CEF-LICENSE.txt @@ -0,0 +1,29 @@ +// Copyright (c) 2008-2020 Marshall A. Greenblatt. Portions Copyright (c) +// 2006-2009 Google Inc. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the name Chromium Embedded +// Framework nor the names of its contributors may be used to endorse +// or promote products derived from this software without specific prior +// written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packaging/linux/postinstall.sh b/packaging/linux/postinstall.sh new file mode 100644 index 0000000000..44073ed5b2 --- /dev/null +++ b/packaging/linux/postinstall.sh @@ -0,0 +1,9 @@ +#!/bin/sh +set -e + +if command -v gtk-update-icon-cache >/dev/null 2>&1; then + gtk-update-icon-cache -q -t -f /usr/share/icons/hicolor || true +fi +if command -v update-desktop-database >/dev/null 2>&1; then + update-desktop-database -q || true +fi diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000000..1980928ac3 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,65 @@ +import { defineConfig, devices } from '@playwright/test'; + +export default defineConfig({ + testDir: './tests/e2e', + testMatch: '**/*.spec.ts', + snapshotPathTemplate: 'tests/e2e/__screenshots__/{projectName}/{testFileName}/{arg}{ext}', + fullyParallel: true, + workers: 4, + // Locally a failure should surface at once; CI keeps retries for flakes. + retries: process.env.CI ? 2 : 0, + reporter: [['html', { open: 'never' }], ['list']], + // Tests run in 3-6s against a built app; this is headroom, not a wait budget. + timeout: 60_000, + expect: { + toHaveScreenshot: { maxDiffPixelRatio: 0.01 }, + }, + use: { + baseURL: 'http://127.0.0.1:4175', + storageState: 'tests/e2e/.auth/state-4175.json', + trace: 'on-first-retry', + // Sheets skip their entrance animation here, so a click cannot land mid-slide. + reducedMotion: 'reduce', + }, + projects: [ + { + name: 'setup', + testMatch: /global\.setup\.ts/, + teardown: 'teardown', + retries: 0, + }, + { + name: 'teardown', + testMatch: /global\.teardown\.ts/, + }, + // touch.spec.ts drives page.touchscreen, which throws unless hasTouch is set. + { + name: 'desktop', + dependencies: ['setup'], + testIgnore: /touch\.spec\.ts/, + use: { ...devices['Desktop Chrome'], viewport: { width: 1280, height: 800 } }, + }, + { + name: 'mobile', + dependencies: ['setup'], + testIgnore: /touch\.spec\.ts/, + use: { ...devices['Desktop Chrome'], viewport: { width: 390, height: 844 } }, + }, + // hasTouch + isMobile: covers long-press, swipe-to-dismiss and tap targets, + // which the `mobile` project (a viewport-narrowed desktop) cannot reach. + { + name: 'touch', + dependencies: ['setup'], + testMatch: ['**/touch.spec.ts', '**/permalink-jump.spec.ts'], + use: { ...devices['Pixel 7'] }, + }, + ], + webServer: { + // Test the production build. + command: 'pnpm run build && pnpm exec vite preview --host 127.0.0.1 --port 4175 --strictPort', + url: 'http://127.0.0.1:4175', + reuseExistingServer: false, + timeout: 180_000, + env: { NODE_OPTIONS: '--max-old-space-size=8192' }, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 02f7aba34b..a234907034 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,69 +5,138 @@ settings: excludeLinksFromLockfile: false overrides: - brace-expansion: '>=1.1.12' - esbuild: '>=0.25.0' + brace-expansion: '>=5.0.9' + esbuild: '>=0.28.1' + fast-uri: '>=3.1.5' + flatted: '>=3.4.2' + js-yaml: '>=4.3.1' + jsdom>undici: ^7.29.0 lodash: '>=4.17.23' minimatch: '>=10.2.3' + nanoid@<6.0.1: '>=3.3.18' + postcss: '>=8.5.23' rollup: '>=4.59.0' serialize-javascript: '>=7.0.3' - flatted: '>=3.4.2' - undici: '>=7.24.0' + undici: '>=7.29.0' + uuid: '>=11.1.1' importers: .: dependencies: '@arborium/arborium': - specifier: ^2.16.0 - version: 2.16.0 + specifier: ^2.18.1 + version: 2.18.1 '@atlaskit/pragmatic-drag-and-drop': - specifier: ^1.7.7 - version: 1.7.9 + specifier: ^2.0.2 + version: 2.0.2 '@atlaskit/pragmatic-drag-and-drop-auto-scroll': - specifier: ^1.4.0 - version: 1.4.0 + specifier: ^3.0.0 + version: 3.0.0 '@atlaskit/pragmatic-drag-and-drop-hitbox': - specifier: ^1.1.0 - version: 1.1.0 + specifier: ^2.0.0 + version: 2.0.0 + '@choochmeque/tauri-plugin-sharekit-api': + specifier: 0.4.0-rc.5 + version: 0.4.0-rc.5 '@fontsource-variable/nunito': - specifier: 5.2.7 - version: 5.2.7 + specifier: 5.3.0 + version: 5.3.0 '@fontsource/space-mono': - specifier: 5.2.9 - version: 5.2.9 + specifier: 5.3.0 + version: 5.3.0 + '@livekit/components-react': + specifier: 2.9.23 + version: 2.9.23(livekit-client@2.21.0(@types/dom-mediacapture-record@1.0.22))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(tslib@2.8.1) + '@lottiefiles/dotlottie-react': + specifier: ^0.19.14 + version: 0.19.15(react@19.2.8) + '@lottiefiles/dotlottie-web': + specifier: 0.79.1 + version: 0.79.1 + '@noble/hashes': + specifier: ^2.3.0 + version: 2.3.0 '@phosphor-icons/react': specifier: ^2.1.10 - version: 2.1.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 2.1.10(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@rolldown/plugin-node-polyfills': + specifier: ^1.0.3 + version: 1.0.3 + '@sableclient/matrixrtc': + specifier: ^0.1.0 + version: 0.1.0(livekit-client@2.21.0(@types/dom-mediacapture-record@1.0.22))(matrix-js-sdk@42.0.0) + '@sableclient/tauri-plugin-livekit-mobile': + specifier: ^0.2.0 + version: 0.2.0 + '@sableclient/tauri-plugin-notifications-api': + specifier: ^0.5.2 + version: 0.5.2 + '@sableclient/twemoji-font': + specifier: ^1.0.4 + version: 1.0.4 '@sentry/react': - specifier: ^10.43.0 - version: 10.43.0(react@18.3.1) + specifier: ^10.70.0 + version: 10.70.0(react@19.2.8) '@tanstack/react-query': - specifier: ^5.90.21 - version: 5.90.21(react@18.3.1) + specifier: ^5.101.4 + version: 5.101.4(react@19.2.8) '@tanstack/react-query-devtools': - specifier: ^5.91.3 - version: 5.91.3(@tanstack/react-query@5.90.21(react@18.3.1))(react@18.3.1) + specifier: ^5.101.4 + version: 5.101.4(@tanstack/react-query@5.101.4(react@19.2.8))(react@19.2.8) '@tanstack/react-virtual': - specifier: ^3.13.19 - version: 3.13.21(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^3.14.9 + version: 3.14.9(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@tauri-apps/api': + specifier: 2.11.1 + version: 2.11.1 + '@tauri-apps/plugin-clipboard-manager': + specifier: 2.3.2 + version: 2.3.2 + '@tauri-apps/plugin-deep-link': + specifier: 2.4.9 + version: 2.4.9 + '@tauri-apps/plugin-dialog': + specifier: 2.7.2 + version: 2.7.2 + '@tauri-apps/plugin-fs': + specifier: 2.5.1 + version: 2.5.1 + '@tauri-apps/plugin-http': + specifier: 2.5.9 + version: 2.5.9 + '@tauri-apps/plugin-opener': + specifier: 2.5.4 + version: 2.5.4 + '@tauri-apps/plugin-os': + specifier: 2.3.2 + version: 2.3.2 + '@tauri-apps/plugin-process': + specifier: ~2.3.1 + version: 2.3.1 + '@tauri-apps/plugin-store': + specifier: ^2.4.4 + version: 2.4.4 + '@tauri-apps/plugin-updater': + specifier: ~2.10.1 + version: 2.10.1 '@use-gesture/react': specifier: 10.3.1 - version: 10.3.1(react@18.3.1) + version: 10.3.1(react@19.2.8) '@vanilla-extract/css': - specifier: ^1.18.0 - version: 1.18.0 + specifier: ^1.21.2 + version: 1.21.2 '@vanilla-extract/recipes': specifier: ^0.5.7 - version: 0.5.7(@vanilla-extract/css@1.18.0) + version: 0.5.7(@vanilla-extract/css@1.21.2) '@vanilla-extract/vite-plugin': - specifier: ^5.1.4 - version: 5.1.4(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2))(yaml@2.8.2) + specifier: ^5.2.6 + version: 5.2.6(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(supports-color@10.2.2)(terser@5.49.0)(vite@8.2.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0))(yaml@2.9.0) await-to-js: specifier: ^3.0.0 version: 3.0.0 badwords-list: - specifier: ^2.0.1-4 + specifier: 2.0.1-4 version: 2.0.1-4 blurhash: specifier: ^2.0.5 @@ -82,20 +151,20 @@ importers: specifier: ^2.5.1 version: 2.5.1 dayjs: - specifier: ^1.11.19 - version: 1.11.19 + specifier: ^1.11.21 + version: 1.11.21 domhandler: - specifier: ^5.0.3 - version: 5.0.3 + specifier: ^6.0.1 + version: 6.0.1 dompurify: - specifier: ^3.3.3 - version: 3.3.3 + specifier: ^3.4.13 + version: 3.4.13 emojibase: - specifier: ^15.3.1 - version: 15.3.1 + specifier: ^17.0.0 + version: 17.0.0 emojibase-data: - specifier: ^15.3.2 - version: 15.3.2(emojibase@15.3.1) + specifier: ^17.0.0 + version: 17.0.0(emojibase@17.0.0) eventemitter3: specifier: ^5.0.4 version: 5.0.4 @@ -103,129 +172,156 @@ importers: specifier: ^2.0.5 version: 2.0.5 focus-trap-react: - specifier: ^10.3.1 - version: 10.3.1(prop-types@15.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^12.0.3 + version: 12.0.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) folds: - specifier: ^2.6.2 - version: 2.6.2(@vanilla-extract/css@1.18.0)(@vanilla-extract/recipes@0.5.7(@vanilla-extract/css@1.18.0))(classnames@2.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - framer-motion: - specifier: 12.34.3 - version: 12.34.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^2.7.1 + version: 2.7.1(@vanilla-extract/css@1.21.2)(@vanilla-extract/recipes@0.5.7(@vanilla-extract/css@1.21.2))(classnames@2.5.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) html-dom-parser: - specifier: ^5.1.8 - version: 5.1.8 + specifier: ^8.0.1 + version: 8.0.2 html-react-parser: - specifier: ^4.2.10 - version: 4.2.10(react@18.3.1) + specifier: ^6.1.5 + version: 6.1.5(@types/react@19.2.17)(react@19.2.8) i18next: - specifier: ^25.8.13 - version: 25.8.17(typescript@5.9.3) + specifier: ^26.3.6 + version: 26.3.6(typescript@7.0.2) i18next-browser-languagedetector: specifier: ^8.2.1 version: 8.2.1 i18next-http-backend: - specifier: ^2.7.3 - version: 2.7.3 + specifier: ^4.0.1 + version: 4.0.1 immer: - specifier: ^9.0.21 - version: 9.0.21 + specifier: ^11.1.16 + version: 11.1.18 is-hotkey: specifier: ^0.2.0 version: 0.2.0 jotai: - specifier: ^2.18.0 - version: 2.18.1(@babel/core@7.29.0)(@babel/template@7.28.6)(@types/react@18.3.28)(react@18.3.1) + specifier: ^2.20.2 + version: 2.20.2(@babel/core@7.29.7(supports-color@10.2.2))(@babel/template@7.29.7)(@types/react@19.2.17)(react@19.2.8) + katex: + specifier: ^0.18.1 + version: 0.18.1 + leaflet: + specifier: 1.9.4 + version: 1.9.4 linkify-react: - specifier: ^4.3.2 - version: 4.3.2(linkifyjs@4.3.2)(react@18.3.1) + specifier: ^4.3.3 + version: 4.3.3(linkifyjs@4.3.3)(react@19.2.8) linkifyjs: - specifier: ^4.3.2 - version: 4.3.2 + specifier: ^4.3.3 + version: 4.3.3 + livekit-client: + specifier: 2.21.0 + version: 2.21.0(@types/dom-mediacapture-record@1.0.22) + marked: + specifier: ^18.0.9 + version: 18.0.10 matrix-js-sdk: - specifier: ^38.4.0 - version: 38.4.0 + specifier: 42.0.0 + version: 42.0.0 matrix-widget-api: - specifier: ^1.16.1 + specifier: ^1.17.0 version: 1.17.0 + nanoid: + specifier: ^6.0.1 + version: 6.0.1 pdfjs-dist: - specifier: ^5.4.624 - version: 5.5.207 + specifier: ^6.2.108 + version: 6.2.108 + prosemirror-commands: + specifier: ^1.7.2 + version: 1.7.2 + prosemirror-history: + specifier: ^1.5.0 + version: 1.5.0 + prosemirror-keymap: + specifier: ^1.2.3 + version: 1.2.3 + prosemirror-model: + specifier: ^1.25.11 + version: 1.25.11 + prosemirror-state: + specifier: ^1.4.4 + version: 1.4.4 + prosemirror-view: + specifier: ^1.42.2 + version: 1.42.2 react: - specifier: ^18.3.1 - version: 18.3.1 + specifier: ^19.2.8 + version: 19.2.8 react-aria: - specifier: ^3.46.0 - version: 3.47.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^3.50.0 + version: 3.50.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react-blurhash: specifier: ^0.3.0 - version: 0.3.0(blurhash@2.0.5)(react@18.3.1) + version: 0.3.0(blurhash@2.0.5)(react@19.2.8) react-colorful: - specifier: ^5.6.1 - version: 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^5.8.0 + version: 5.8.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react-dom: - specifier: ^18.3.1 - version: 18.3.1(react@18.3.1) + specifier: ^19.2.8 + version: 19.2.8(react@19.2.8) react-google-recaptcha: - specifier: ^2.1.0 - version: 2.1.0(react@18.3.1) + specifier: ^3.1.0 + version: 3.1.0(react@19.2.8) react-i18next: - specifier: ^16.5.4 - version: 16.5.7(i18next@25.8.17(typescript@5.9.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + specifier: ^17.0.11 + version: 17.0.11(i18next@26.3.6(typescript@7.0.2))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(typescript@7.0.2) + react-leaflet: + specifier: ^5.0.0 + version: 5.0.0(leaflet@1.9.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react-range: specifier: ^1.10.0 - version: 1.10.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react-router-dom: - specifier: ^6.30.3 - version: 6.30.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - slate: - specifier: ^0.123.0 - version: 0.123.0 - slate-dom: - specifier: ^0.123.0 - version: 0.123.0(slate@0.123.0) - slate-history: - specifier: ^0.113.1 - version: 0.113.1(slate@0.123.0) - slate-react: - specifier: ^0.123.0 - version: 0.123.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.123.0(slate@0.123.0))(slate@0.123.0) + version: 1.10.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + react-router: + specifier: ^8.3.0 + version: 8.3.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + tauri-plugin-android-fs-api: + specifier: 28.4.0 + version: 28.4.0 ua-parser-js: - specifier: ^1.0.41 - version: 1.0.41 + specifier: ^2.0.10 + version: 2.0.10 virtua: - specifier: ^0.48.8 - version: 0.48.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^0.50.1 + version: 0.50.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8) workbox-precaching: - specifier: ^7.4.0 - version: 7.4.0 + specifier: ^7.4.1 + version: 7.4.1 devDependencies: - '@cloudflare/vite-plugin': - specifier: ^1.26.0 - version: 1.27.0(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2))(workerd@1.20260310.1)(wrangler@4.72.0) - '@esbuild-plugins/node-globals-polyfill': - specifier: ^0.2.3 - version: 0.2.3(esbuild@0.27.3) + '@faker-js/faker': + specifier: 10.5.0 + version: 10.5.0 + '@playwright/test': + specifier: ^1.62.0 + version: 1.62.1 '@rollup/plugin-inject': specifier: ^5.0.5 - version: 5.0.5(rollup@4.59.0) + version: 5.0.5(rollup@4.62.3) '@rollup/plugin-wasm': specifier: ^6.2.2 - version: 6.2.2(rollup@4.59.0) + version: 6.2.2(rollup@4.62.3) '@sableclient/sable-call-embedded': - specifier: v1.1.4 - version: 1.1.4 + specifier: 1.1.8 + version: 1.1.8 '@sentry/vite-plugin': - specifier: ^5.1.1 - version: 5.1.1(rollup@4.59.0) + specifier: ^5.4.0 + version: 5.4.0(rollup@4.62.3)(supports-color@10.2.2) + '@tauri-apps/cli': + specifier: 2.11.4 + version: 2.11.4 '@testing-library/jest-dom': - specifier: ^6.9.1 - version: 6.9.1 + specifier: ^7.0.0 + version: 7.0.0(@testing-library/dom@10.4.1) '@testing-library/react': specifier: ^16.3.2 - version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) '@testing-library/user-event': - specifier: ^14.6.1 - version: 14.6.1(@testing-library/dom@10.4.1) + specifier: ^14.6.3 + version: 14.6.5(@testing-library/dom@10.4.1) '@types/chroma-js': specifier: ^3.1.2 version: 3.1.2 @@ -235,145 +331,145 @@ importers: '@types/is-hotkey': specifier: ^0.1.10 version: 0.1.10 + '@types/leaflet': + specifier: ^1.9.22 + version: 1.9.22 '@types/node': - specifier: 24.10.13 - version: 24.10.13 + specifier: 24.13.2 + version: 24.13.2 '@types/react': - specifier: ^18.3.28 - version: 18.3.28 + specifier: ^19.2.17 + version: 19.2.17 '@types/react-dom': - specifier: ^18.3.7 - version: 18.3.7(@types/react@18.3.28) + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.17) '@types/react-google-recaptcha': specifier: ^2.1.9 version: 2.1.9 - '@types/ua-parser-js': - specifier: ^0.7.39 - version: 0.7.39 '@vitejs/plugin-react': - specifier: ^5.1.4 - version: 5.1.4(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2)) + specifier: ^6.0.4 + version: 6.0.5(vite@8.2.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0)) '@vitest/coverage-v8': - specifier: ^4.1.0 - version: 4.1.0(vitest@4.1.0) + specifier: ^4.1.10 + version: 4.1.10(vitest@4.1.10) '@vitest/ui': - specifier: ^4.1.0 - version: 4.1.0(vitest@4.1.0) + specifier: ^4.1.10 + version: 4.1.10(vitest@4.1.10) buffer: specifier: ^6.0.3 version: 6.0.3 cloudflared: specifier: ^0.7.1 - version: 0.7.1 + version: 0.7.3 + fake-indexeddb: + specifier: 6.2.5 + version: 6.2.5 jsdom: - specifier: ^29.0.0 - version: 29.0.0 + specifier: ^30.0.1 + version: 30.0.1(@noble/hashes@2.3.0) knip: - specifier: 5.85.0 - version: 5.85.0(@types/node@24.10.13)(typescript@5.9.3) + specifier: 6.32.1 + version: 6.32.1 oxfmt: - specifier: ^0.45.0 - version: 0.45.0 + specifier: ^0.61.0 + version: 0.61.0 oxlint: - specifier: ^1.60.0 - version: 1.60.0(oxlint-tsgolint@0.21.0) + specifier: ^1.76.0 + version: 1.76.0(oxlint-tsgolint@7.0.2001) oxlint-tsgolint: - specifier: ^0.21.0 - version: 0.21.0 + specifier: ^7.0.2001 + version: 7.0.2001 + tauri-plugin-app-icon-api: + specifier: github:SableClient/tauri-plugin-app-icon#bab80ae17f6d018702e472c539d0f02eba480c21 + version: https://codeload.github.com/SableClient/tauri-plugin-app-icon/tar.gz/bab80ae17f6d018702e472c539d0f02eba480c21 + testcontainers: + specifier: ^12.0.4 + version: 12.0.4(supports-color@10.2.2) typescript: - specifier: ^5.9.3 - version: 5.9.3 + specifier: ^7.0.2 + version: 7.0.2 vite: - specifier: ^7.3.1 - version: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2) + specifier: ^8.2.0 + version: 8.2.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0) vite-plugin-compression2: - specifier: 2.5.0 - version: 2.5.0(rollup@4.59.0) + specifier: ^2.5.3 + version: 2.5.3(rollup@4.62.3) vite-plugin-pwa: - specifier: ^1.2.0 - version: 1.2.0(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2))(workbox-build@7.4.0(@types/babel__core@7.20.5))(workbox-window@7.4.0) + specifier: ^1.3.0 + version: 1.3.0(supports-color@10.2.2)(vite@8.2.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0))(workbox-build@7.4.1(supports-color@10.2.2))(workbox-window@7.4.1) vite-plugin-static-copy: - specifier: ^3.2.0 - version: 3.2.0(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2)) + specifier: ^4.1.1 + version: 4.1.1(vite@8.2.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0)) vite-plugin-svgr: - specifier: 4.5.0 - version: 4.5.0(rollup@4.59.0)(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2)) - vite-plugin-top-level-await: - specifier: ^1.6.0 - version: 1.6.0(@swc/helpers@0.5.19)(rollup@4.59.0)(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2)) + specifier: 5.2.0 + version: 5.2.0(rollup@4.62.3)(supports-color@10.2.2)(typescript@7.0.2)(vite@8.2.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0)) vitest: - specifier: ^4.1.0 - version: 4.1.0(@types/node@24.10.13)(@vitest/ui@4.1.0)(jsdom@29.0.0)(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2)) - wrangler: - specifier: ^4.70.0 - version: 4.72.0 + specifier: ^4.1.10 + version: 4.1.10(@types/node@24.13.2)(@vitest/coverage-v8@4.1.10)(@vitest/ui@4.1.10)(jsdom@30.0.1(@noble/hashes@2.3.0))(vite@8.2.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0)) packages: - '@adobe/css-tools@4.4.4': - resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} + '@adobe/css-tools@4.5.0': + resolution: {integrity: sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==} - '@apideck/better-ajv-errors@0.3.6': - resolution: {integrity: sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==} + '@apideck/better-ajv-errors@0.3.7': + resolution: {integrity: sha512-TajUJwGWbDwkCx/CZi7tRE8PVB7simCvKJfHUsSdvps+aTM/PDPP4gkLmKnc+x3CE//y9i/nj74GqdL/hwk7Iw==} engines: {node: '>=10'} peerDependencies: ajv: '>=8' - '@arborium/arborium@2.16.0': - resolution: {integrity: sha512-9rz2J9Hx+nMq1qon65SbmE+XZvwr/oDqYPinj+BnYOzef7lGwzn1GtYhuB1Cz8jTZ84wIaBt+B8nTQEGYniqNg==} - - '@asamuzakjp/css-color@5.0.1': - resolution: {integrity: sha512-2SZFvqMyvboVV1d15lMf7XiI3m7SDqXUuKaTymJYLN6dSGadqp+fVojqJlVoMlbZnlTmu3S0TLwLTJpvBMO1Aw==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@arborium/arborium@2.18.1': + resolution: {integrity: sha512-v6WuWy2yd/+R5OFT7g+asDXKImOrUdnTmEvGZwaBgOE2DSFKcksa2Us4QxxIr3C2ohVguRJf/rtbnZska6/UKg==} - '@asamuzakjp/dom-selector@7.0.3': - resolution: {integrity: sha512-Q6mU0Z6bfj6YvnX2k9n0JxiIwrCFN59x/nWmYQnAqP000ruX/yV+5bp/GRcF5T8ncvfwJQ7fgfP74DlpKExILA==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@asamuzakjp/css-color@6.0.5': + resolution: {integrity: sha512-mbhpPMmnw/kwW19aRNmSUl1QzLbdGo1SCuE49BT98MNwqF6zaHb3o2owssFc/PEO/4t2UjqtCNwocuDtJornzA==} + engines: {node: ^22.13.0 || >=24.0.0} - '@asamuzakjp/nwsapi@2.3.9': - resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} + '@asamuzakjp/dom-selector@8.3.0': + resolution: {integrity: sha512-UJLfKXBhrc8i1vH2eJXuYQMwlsLKWFw3O+CPqXSuVEiikeAim3UgrfWX0k4tA/X8cRFM8iZ7OaqBokFGbYusdg==} + engines: {node: ^22.13.0 || >=24.0.0} - '@atlaskit/pragmatic-drag-and-drop-auto-scroll@1.4.0': - resolution: {integrity: sha512-5GoikoTSW13UX76F9TDeWB8x3jbbGlp/Y+3aRkHe1MOBMkrWkwNpJ42MIVhhX/6NSeaZiPumP0KbGJVs2tOWSQ==} + '@atlaskit/pragmatic-drag-and-drop-auto-scroll@3.0.0': + resolution: {integrity: sha512-8TlkMi417zIPrTnCpbNV0Ce63JTKBn3dGkqiadkGQROvzDAox7Y9TYRHpvbQiEdtPoHWnmyRBi+RsGDxXsQxgQ==} - '@atlaskit/pragmatic-drag-and-drop-hitbox@1.1.0': - resolution: {integrity: sha512-JWt6eVp6Br2FPHRM8s0dUIHQk/jFInGP1f3ti5CdtM1Ji5/pt8Akm44wDC063Gv2i5RGseixtbW0z/t6RYtbdg==} + '@atlaskit/pragmatic-drag-and-drop-hitbox@2.0.0': + resolution: {integrity: sha512-vkXCB/23pT8YgYU8biGmR9uu0fr9oWoaq2GMSKyiNPk3reA12XVrCdgB2SLBoB24Ic1FOm9PdaQJ9ZSUQdCVpw==} - '@atlaskit/pragmatic-drag-and-drop@1.7.9': - resolution: {integrity: sha512-m/bcw5flyjfcF/rdX4JeomtIBrWuDNOwcQieiywHv7zkfIRmUC34Q9ZLeNGVoz73UiGsRqxysMuw4tC7lSJ89g==} + '@atlaskit/pragmatic-drag-and-drop@2.0.2': + resolution: {integrity: sha512-ou1j+lBqutK7p2S80lMresJeKu9qAL1zr1K+N0OyCTzbMpD6pXRHray8VkRoHgJICI7Wp1fk5ScJh6EctyAuPw==} - '@babel/code-frame@7.29.0': - resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.29.0': - resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} engines: {node: '>=6.9.0'} - '@babel/core@7.29.0': - resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} engines: {node: '>=6.9.0'} - '@babel/generator@7.29.1': - resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + '@babel/generator@7.29.7': + resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} engines: {node: '>=6.9.0'} - '@babel/helper-annotate-as-pure@7.27.3': - resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} + '@babel/helper-annotate-as-pure@7.29.7': + resolution: {integrity: sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.28.6': - resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} engines: {node: '>=6.9.0'} - '@babel/helper-create-class-features-plugin@7.28.6': - resolution: {integrity: sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==} + '@babel/helper-create-class-features-plugin@7.29.7': + resolution: {integrity: sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-create-regexp-features-plugin@7.28.5': - resolution: {integrity: sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==} + '@babel/helper-create-regexp-features-plugin@7.29.7': + resolution: {integrity: sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -383,99 +479,105 @@ packages: peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - '@babel/helper-globals@7.28.0': - resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} engines: {node: '>=6.9.0'} - '@babel/helper-member-expression-to-functions@7.28.5': - resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==} + '@babel/helper-member-expression-to-functions@7.29.7': + resolution: {integrity: sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.28.6': - resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.28.6': - resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-optimise-call-expression@7.27.1': - resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} + '@babel/helper-optimise-call-expression@7.29.7': + resolution: {integrity: sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==} engines: {node: '>=6.9.0'} - '@babel/helper-plugin-utils@7.28.6': - resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + '@babel/helper-plugin-utils@7.29.7': + resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} engines: {node: '>=6.9.0'} - '@babel/helper-remap-async-to-generator@7.27.1': - resolution: {integrity: sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==} + '@babel/helper-remap-async-to-generator@7.29.7': + resolution: {integrity: sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-replace-supers@7.28.6': - resolution: {integrity: sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==} + '@babel/helper-replace-supers@7.29.7': + resolution: {integrity: sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-skip-transparent-expression-wrappers@7.27.1': - resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} + '@babel/helper-skip-transparent-expression-wrappers@7.29.7': + resolution: {integrity: sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==} engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.27.1': - resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.28.5': - resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.27.1': - resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} engines: {node: '>=6.9.0'} - '@babel/helper-wrap-function@7.28.6': - resolution: {integrity: sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==} + '@babel/helper-wrap-function@7.29.7': + resolution: {integrity: sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.28.6': - resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} engines: {node: '>=6.9.0'} - '@babel/parser@7.29.0': - resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5': - resolution: {integrity: sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==} + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.29.7': + resolution: {integrity: sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.29.7': + resolution: {integrity: sha512-r8j8escF+U2FUHo0KOhPUdMzUO+jp9fInva6+ACVAF3Y97Ev+5iNZwiqTghmzNeWwDkOPlYuTcfb1vDaoZKmAQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1': - resolution: {integrity: sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==} + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.29.7': + resolution: {integrity: sha512-GE1TFSiuFeGsCxmYXZl8HwoPrVlwe4rHPFE8weieGKZqnDORK+Ar3vgWMgW+AOxQ6/2TgLSKx9p6W7O4rC6qgQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1': - resolution: {integrity: sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==} + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.7': + resolution: {integrity: sha512-oBNVCvnO5tND+xSopWvV8WNGfpTfgP4Zr/YXXSj8zfmcPktp5Ku/aZlsIowgSD4fjmgHn6sGmB9APVsU5zOdhA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1': - resolution: {integrity: sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==} + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.29.7': + resolution: {integrity: sha512-QQt9qKHZ2sg/kivaLr7lnQr8HVrQDdBNSfCsTjiDxRuX/K5ORyKq+Bu8Xr0cDE3Dfkv0cw28Ve0EKyKMvulkOw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.13.0 - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6': - resolution: {integrity: sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==} + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.29.7': + resolution: {integrity: sha512-pn6QacGLgvCcwc+syUhKE/qSjV2D1IHDB84RNxWYSt1mW3K/SCtjinZ2p0cETJxAWBjPy3K/1lHwG5BjjPxNlw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -486,20 +588,20 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-import-assertions@7.28.6': - resolution: {integrity: sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==} + '@babel/plugin-syntax-import-assertions@7.29.7': + resolution: {integrity: sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-import-attributes@7.28.6': - resolution: {integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==} + '@babel/plugin-syntax-import-attributes@7.29.7': + resolution: {integrity: sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-typescript@7.28.6': - resolution: {integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==} + '@babel/plugin-syntax-typescript@7.29.7': + resolution: {integrity: sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -510,326 +612,314 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-transform-arrow-functions@7.27.1': - resolution: {integrity: sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==} + '@babel/plugin-transform-arrow-functions@7.29.7': + resolution: {integrity: sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-async-generator-functions@7.29.0': - resolution: {integrity: sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==} + '@babel/plugin-transform-async-generator-functions@7.29.7': + resolution: {integrity: sha512-d98gXZkgswvkyohMBABkhm3GeXhYj8psWfwQ2C7gtfrKGTykQa/iOIi+JJhwMjPlZ6Vm2XN+DCf3Es1EoG4ZLA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-async-to-generator@7.28.6': - resolution: {integrity: sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==} + '@babel/plugin-transform-async-to-generator@7.29.7': + resolution: {integrity: sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-block-scoped-functions@7.27.1': - resolution: {integrity: sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==} + '@babel/plugin-transform-block-scoped-functions@7.29.7': + resolution: {integrity: sha512-cUSmjh72N+rN4PrkFlN1dJwNCwjVp5d38/CQrEsFggkD10UiFlBFgdH3tv5dNsLuHY+3S8db2xCHjhZcv5WgvA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-block-scoping@7.28.6': - resolution: {integrity: sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==} + '@babel/plugin-transform-block-scoping@7.29.7': + resolution: {integrity: sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-class-properties@7.28.6': - resolution: {integrity: sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==} + '@babel/plugin-transform-class-properties@7.29.7': + resolution: {integrity: sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-class-static-block@7.28.6': - resolution: {integrity: sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==} + '@babel/plugin-transform-class-static-block@7.29.7': + resolution: {integrity: sha512-kibJgmEdX2iMwsHY2tSZNDgj8PwIlCQz7FK9KuGKO8zsuoUwSEhoNnNVp/emKWrbY4HeO6kkXfdMqRKKKXBm2A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.12.0 - '@babel/plugin-transform-classes@7.28.6': - resolution: {integrity: sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==} + '@babel/plugin-transform-classes@7.29.7': + resolution: {integrity: sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-computed-properties@7.28.6': - resolution: {integrity: sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==} + '@babel/plugin-transform-computed-properties@7.29.7': + resolution: {integrity: sha512-RK7/IyU5phpuCdBAuig5VkzG/EnbDaui5SQGdU9BFrHdV+mV4cUjLMQ9lJDjLNtWHsqtiefpGZUXQP2BiTYMsA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-destructuring@7.28.5': - resolution: {integrity: sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==} + '@babel/plugin-transform-destructuring@7.29.7': + resolution: {integrity: sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-dotall-regex@7.28.6': - resolution: {integrity: sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==} + '@babel/plugin-transform-dotall-regex@7.29.7': + resolution: {integrity: sha512-3qc18hsD2RdZiyJNDNc7HQpv6xbncwh8FYtxNFFzclSyh/trPD9KkVR9BDECUjDLvb7yJVF15GfYUuC+LMkkiQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-duplicate-keys@7.27.1': - resolution: {integrity: sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==} + '@babel/plugin-transform-duplicate-keys@7.29.7': + resolution: {integrity: sha512-6IvRRriEMqnBwD6chtxdLpMYCHWEzN+oL5cyQtjykya19UgzbmKhxmhZgKC/LHxS2nYr9Q/qYPZ5Lr6jOL9+yQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0': - resolution: {integrity: sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==} + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.7': + resolution: {integrity: sha512-2wiIyo2BjtgU7HufSeDnL9L2O7zr8jmhFKuSr65VpRkUiRKRNpb0mdlk56+XPPKoIrfHqzbMuglDvZun0RISsA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-transform-dynamic-import@7.27.1': - resolution: {integrity: sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==} + '@babel/plugin-transform-dynamic-import@7.29.7': + resolution: {integrity: sha512-giOlEm/EFjfjr+te9NsdjkUo2v4f8rS/SXPumRVHAtbNcyNlvtREkU1dZzaIDclNpnaVhlCqRdFKhJBjBikzLg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-explicit-resource-management@7.28.6': - resolution: {integrity: sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==} + '@babel/plugin-transform-explicit-resource-management@7.29.7': + resolution: {integrity: sha512-Rstj7coNz8sE+7Ju7ihpHLI564lsK5pUpNNlvptCIC/16E/S5hbl6n3kESPKdNRmqEWlpn5xpS5Q2dvXBsySLw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-exponentiation-operator@7.28.6': - resolution: {integrity: sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==} + '@babel/plugin-transform-exponentiation-operator@7.29.7': + resolution: {integrity: sha512-zFpMOTLZBdW5LfObqcSbL6kefg4R4eLdmvS0wbN9M6D5Mym/sKm9toOoWyVOa+xDjvCnuWcHls2YonXwHvH3CQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-export-namespace-from@7.27.1': - resolution: {integrity: sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==} + '@babel/plugin-transform-export-namespace-from@7.29.7': + resolution: {integrity: sha512-24B2nOy2TeJSMheqwPD4DDQOV/elLSIlKxjZt4i05H5AgdPdWR3n18HnNrcJ+j76WJd9gbwb9jPjNYUy6RautA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-for-of@7.27.1': - resolution: {integrity: sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==} + '@babel/plugin-transform-for-of@7.29.7': + resolution: {integrity: sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-function-name@7.27.1': - resolution: {integrity: sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==} + '@babel/plugin-transform-function-name@7.29.7': + resolution: {integrity: sha512-otRWaHXE6fbAGkePvaj/kvs3HsqXfPhlnzwSOlnFgbqCPMd975dW+4wZ00WFBt+/YlBGcJwNrARQTOJOb4ZrIg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-json-strings@7.28.6': - resolution: {integrity: sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==} + '@babel/plugin-transform-json-strings@7.29.7': + resolution: {integrity: sha512-RRnE2+eon1rJAq8MnoF1b5kTpY1vU88twHcvcKMrsqP/jxIRqDVs9iJB5fqPuqyeFAW0wJo4MlUIPpQCq/aRsg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-literals@7.27.1': - resolution: {integrity: sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==} + '@babel/plugin-transform-literals@7.29.7': + resolution: {integrity: sha512-DZ/oLP21ZuWx1vKqnoNv6/tvEK48AQOBRai40CX9dTjGluvT/YZCyY3rryDtyUqCEoyNroy5KKPwX2iQCiRvyw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-logical-assignment-operators@7.28.6': - resolution: {integrity: sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==} + '@babel/plugin-transform-logical-assignment-operators@7.29.7': + resolution: {integrity: sha512-A0H91hh6W8MFRkp5TqJmMr39jzGD1A1E1Ysiv2O06Sfbhkapm+XyIzxWCEh5kqwOZ1/8QZ0dY3SeQ7XBqfJd5Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-member-expression-literals@7.27.1': - resolution: {integrity: sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==} + '@babel/plugin-transform-member-expression-literals@7.29.7': + resolution: {integrity: sha512-hl1kwFZCCiDyfH25Xmco9jTrkPgnS9pmOzSG7W5I4SaGbLeqKv417hcU2RKmaxoPEgsoJh7ZPOrnPGq99bHoUg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-amd@7.27.1': - resolution: {integrity: sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==} + '@babel/plugin-transform-modules-amd@7.29.7': + resolution: {integrity: sha512-fxtQoH3m5ywUSIfaH0FGCzWu4McsYon5bD3K4XnskC7f+OyQMj7rsOMi4NvvmJ83WwBAg4UCe+ov4VZlqEvyew==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-commonjs@7.28.6': - resolution: {integrity: sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==} + '@babel/plugin-transform-modules-commonjs@7.29.7': + resolution: {integrity: sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-systemjs@7.29.0': - resolution: {integrity: sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==} + '@babel/plugin-transform-modules-systemjs@7.29.7': + resolution: {integrity: sha512-TM2ZcQLoG2/y4HODiStCo10DibYhWhGWAwVv+EQKmG/7GFl0N+AAmUiXOMKM+aiJ9XBJ9AHVZBvTzMnJ2sM3cQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-umd@7.27.1': - resolution: {integrity: sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==} + '@babel/plugin-transform-modules-umd@7.29.7': + resolution: {integrity: sha512-B4UkaTK3QpgCwJnrxKfMPKdo92CN7OKXAlpAAnM3UPu0Q0lCCk57ylA9AJbRy2v8dDKOPAAWcoR6CMyeoHwRCA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-named-capturing-groups-regex@7.29.0': - resolution: {integrity: sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==} + '@babel/plugin-transform-named-capturing-groups-regex@7.29.7': + resolution: {integrity: sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-transform-new-target@7.27.1': - resolution: {integrity: sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==} + '@babel/plugin-transform-new-target@7.29.7': + resolution: {integrity: sha512-fEo41GmsOUhOBlw8ioo6zvjX5Xc2Lqkzlyfqbpsk3eB6TReV18uhxZ0esfEokVbY2+PVJAQHNKxER6lGrzNd3A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-nullish-coalescing-operator@7.28.6': - resolution: {integrity: sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==} + '@babel/plugin-transform-nullish-coalescing-operator@7.29.7': + resolution: {integrity: sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-numeric-separator@7.28.6': - resolution: {integrity: sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==} + '@babel/plugin-transform-numeric-separator@7.29.7': + resolution: {integrity: sha512-zR7fv/z14OjgHl4AgRtkDBvBMhIzCxqV/qN/2BCRC7LjFwvuzjYe7gDWxC4Wl/SNsLM6SE1IWvRPYMgSJaUvNw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-object-rest-spread@7.28.6': - resolution: {integrity: sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==} + '@babel/plugin-transform-object-rest-spread@7.29.7': + resolution: {integrity: sha512-Ld98jn4c0smUywL57m7SgsHq3OpThOa6LqZJif3G6jYOovPleoFhVrBJ1WegRApSFB2wu4+RelAj9AC9G08Z4A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-object-super@7.27.1': - resolution: {integrity: sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==} + '@babel/plugin-transform-object-super@7.29.7': + resolution: {integrity: sha512-Ea/diGcw0twB5IlZPO5sgET6fJsLJqPABqTuFWIR+iMPGPZJkATEIWx0wa+aEQ5UY1CBQyP/gkAiLEqn1vBiQA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-optional-catch-binding@7.28.6': - resolution: {integrity: sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==} + '@babel/plugin-transform-optional-catch-binding@7.29.7': + resolution: {integrity: sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-optional-chaining@7.28.6': - resolution: {integrity: sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==} + '@babel/plugin-transform-optional-chaining@7.29.7': + resolution: {integrity: sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-parameters@7.27.7': - resolution: {integrity: sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==} + '@babel/plugin-transform-parameters@7.29.7': + resolution: {integrity: sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-private-methods@7.28.6': - resolution: {integrity: sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==} + '@babel/plugin-transform-private-methods@7.29.7': + resolution: {integrity: sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-private-property-in-object@7.28.6': - resolution: {integrity: sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==} + '@babel/plugin-transform-private-property-in-object@7.29.7': + resolution: {integrity: sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-property-literals@7.27.1': - resolution: {integrity: sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==} + '@babel/plugin-transform-property-literals@7.29.7': + resolution: {integrity: sha512-bOMRLQuI0A5ZqHq3OWJ89/rXpJ/NJrbVhXiP4zwPGMs6kpcVsuTUNjwoE30K0Qm3mf48a/TnRYYD6vPNqcg6jA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx-self@7.27.1': - resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + '@babel/plugin-transform-regenerator@7.29.7': + resolution: {integrity: sha512-rNNFV0DBAJp988xW2DOntfDoYn1eR8GGF5AT5vYc+rjyfaQkM242c9tZUHHPe7KYaiJizXPWhQTzzdbXySyhBw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx-source@7.27.1': - resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-regenerator@7.29.0': - resolution: {integrity: sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-regexp-modifiers@7.28.6': - resolution: {integrity: sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==} + '@babel/plugin-transform-regexp-modifiers@7.29.7': + resolution: {integrity: sha512-mB5Fs0VWrJ42ZCmc8114v60qetdaUVNkj9PmSZRmanCZM3S9hm0CFRLjRmYIsuXav14l2jvZ+4T8iiCGnhj3nQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-transform-reserved-words@7.27.1': - resolution: {integrity: sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==} + '@babel/plugin-transform-reserved-words@7.29.7': + resolution: {integrity: sha512-5+YhdpVgmfSmwZyLMftfaiffLRMHjzIRHFHHLdibcSyJm2pasMrKHrO3Ptrt2DRshjvpgjEJJ1zVW14WPq/6QA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-shorthand-properties@7.27.1': - resolution: {integrity: sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==} + '@babel/plugin-transform-shorthand-properties@7.29.7': + resolution: {integrity: sha512-I+WYbGBAiCn7nA6xBrlgPH+MB7HWb4u8pv5S0Pv7OtwNvIFvCCb24YlttKEeUFVurfBCEaOTnuhlqsb7f0Z5Dg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-spread@7.28.6': - resolution: {integrity: sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==} + '@babel/plugin-transform-spread@7.29.7': + resolution: {integrity: sha512-/u5K1QWada7tbYNqTjMh96718g9NTwh9tfPJMsSmVsQwGT447FskV+KcfeXkXq2GWki4EM/MuTdmBec+hOuVTQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-sticky-regex@7.27.1': - resolution: {integrity: sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==} + '@babel/plugin-transform-sticky-regex@7.29.7': + resolution: {integrity: sha512-BCHzNYJGe9l7EpwwDBN/ztlL2NYFFq8hp9ddjtUEM9f2O7S7kKV/lL6Fwo7IF7NSkYhPK2vO+86nIGltA90MsA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-template-literals@7.27.1': - resolution: {integrity: sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==} + '@babel/plugin-transform-template-literals@7.29.7': + resolution: {integrity: sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-typeof-symbol@7.27.1': - resolution: {integrity: sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==} + '@babel/plugin-transform-typeof-symbol@7.29.7': + resolution: {integrity: sha512-223mNGoTkBiTEWFoK+Q6Go3tueMRclO8vxxxxquNCYuNI4jWOofFKJRRDu6SDrB8Sgo1UEGW9T4GAQ8ZyRso1A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-unicode-escapes@7.27.1': - resolution: {integrity: sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==} + '@babel/plugin-transform-unicode-escapes@7.29.7': + resolution: {integrity: sha512-jCfXxSjf94lf4E0hKE0AByxF6F3/pVFqRdUUNkDJhsY0m1ZKjnN6ZYyMeHNpzflxb/0q5b7t3p+BE+SLF1WOtA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-unicode-property-regex@7.28.6': - resolution: {integrity: sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==} + '@babel/plugin-transform-unicode-property-regex@7.29.7': + resolution: {integrity: sha512-OgZ+zoAJgZLUCunsTRQ5LAjOywDv5zzZ2/hQ5aMw1pGXyY2rtE8/chXYUmu3AlVHKpm10KEdG9aMwbI/K76ZGw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-unicode-regex@7.27.1': - resolution: {integrity: sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==} + '@babel/plugin-transform-unicode-regex@7.29.7': + resolution: {integrity: sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-unicode-sets-regex@7.28.6': - resolution: {integrity: sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==} + '@babel/plugin-transform-unicode-sets-regex@7.29.7': + resolution: {integrity: sha512-BLOhLht9DOJwIxlmp91wHvkXv1lguuHS3/FwUO8HL1H0u8s4hR1gASVFyilu9iGtcTRYqjTZmlsFFeQletntEg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/preset-env@7.29.2': - resolution: {integrity: sha512-DYD23veRYGvBFhcTY1iUvJnDNpuqNd/BzBwCvzOTKUnJjKg5kpUBh3/u9585Agdkgj+QuygG7jLfOPWMa2KVNw==} + '@babel/preset-env@7.29.7': + resolution: {integrity: sha512-GYzX36n1nsciIb0uyH0GHwxwtNwPQIcpxSeiVLDtG/B7jB5xXgchnmL1f/jCX5o+pwnaDBtO60ONSJhEBJfxYA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -839,26 +929,28 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 - '@babel/runtime@7.28.6': - resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} engines: {node: '>=6.9.0'} - '@babel/runtime@7.29.2': - resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} - engines: {node: '>=6.9.0'} + '@babel/runtime@8.0.0': + resolution: {integrity: sha512-sL6cvO2IfkSu/iU+zs2S/w01B7A8V7suXSIKEN4hPFFdZoiPGxrj5pAG0lCaqLWiEIrjKzdznIWuaLcxPR53qw==} - '@babel/template@7.28.6': - resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.29.0': - resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + '@babel/traverse@7.29.7': + resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} engines: {node: '>=6.9.0'} - '@babel/types@7.29.0': - resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} + '@balena/dockerignore@1.0.2': + resolution: {integrity: sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==} + '@bcoe/v8-coverage@1.0.2': resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} @@ -867,72 +959,25 @@ packages: resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} hasBin: true - '@cloudflare/kv-asset-handler@0.4.2': - resolution: {integrity: sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==} - engines: {node: '>=18.0.0'} - - '@cloudflare/unenv-preset@2.15.0': - resolution: {integrity: sha512-EGYmJaGZKWl+X8tXxcnx4v2bOZSjQeNI5dWFeXivgX9+YCT69AkzHHwlNbVpqtEUTbew8eQurpyOpeN8fg00nw==} - peerDependencies: - unenv: 2.0.0-rc.24 - workerd: 1.20260301.1 || ~1.20260302.1 || ~1.20260303.1 || ~1.20260304.1 || >1.20260305.0 <2.0.0-0 - peerDependenciesMeta: - workerd: - optional: true - - '@cloudflare/vite-plugin@1.27.0': - resolution: {integrity: sha512-+s8APrIadCf2h0zYZ3WIPVeQ2CYgbXgFiR3BIrKvbpoo5klLgTeYpTq8ggxBmtjwt88PNruI4eCGZ6XNaDHMaA==} - peerDependencies: - vite: ^6.1.0 || ^7.0.0 - wrangler: ^4.72.0 - - '@cloudflare/workerd-darwin-64@1.20260310.1': - resolution: {integrity: sha512-hF2VpoWaMb1fiGCQJqCY6M8I+2QQqjkyY4LiDYdTL5D/w6C1l5v1zhc0/jrjdD1DXfpJtpcSMSmEPjHse4p9Ig==} - engines: {node: '>=16'} - cpu: [x64] - os: [darwin] - - '@cloudflare/workerd-darwin-arm64@1.20260310.1': - resolution: {integrity: sha512-h/Vl3XrYYPI6yFDE27XO1QPq/1G1lKIM8tzZGIWYpntK3IN5XtH3Ee/sLaegpJ49aIJoqhF2mVAZ6Yw+Vk2gJw==} - engines: {node: '>=16'} - cpu: [arm64] - os: [darwin] - - '@cloudflare/workerd-linux-64@1.20260310.1': - resolution: {integrity: sha512-XzQ0GZ8G5P4d74bQYOIP2Su4CLdNPpYidrInaSOuSxMw+HamsHaFrjVsrV2mPy/yk2hi6SY2yMbgKFK9YjA7vw==} - engines: {node: '>=16'} - cpu: [x64] - os: [linux] - - '@cloudflare/workerd-linux-arm64@1.20260310.1': - resolution: {integrity: sha512-sxv4CxnN4ZR0uQGTFVGa0V4KTqwdej/czpIc5tYS86G8FQQoGIBiAIs2VvU7b8EROPcandxYHDBPTb+D9HIMPw==} - engines: {node: '>=16'} - cpu: [arm64] - os: [linux] - - '@cloudflare/workerd-windows-64@1.20260310.1': - resolution: {integrity: sha512-+1ZTViWKJypLfgH/luAHCqkent0DEBjAjvO40iAhOMHRLYP/SPphLvr4Jpi6lb+sIocS8Q1QZL4uM5Etg1Wskg==} - engines: {node: '>=16'} - cpu: [x64] - os: [win32] + '@bufbuild/protobuf@1.10.1': + resolution: {integrity: sha512-wJ8ReQbHxsAfXhrf9ixl0aYbZorRuOWpBNzm8pL8ftmSxQx/wnJD5Eg861NwJU/czy2VXFIebCeZnZrI9rktIQ==} - '@cspotcode/source-map-support@0.8.1': - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} - engines: {node: '>=12'} + '@choochmeque/tauri-plugin-sharekit-api@0.4.0-rc.5': + resolution: {integrity: sha512-VpO3Uw5ZmPa0M0ta/oS5WyiW/jdNAPFh1zXbHA5dJtLMeA4RJqqj4vAlVMYoYRqtrE/1Op/GQ3jXvxgU4WgSVA==} - '@csstools/color-helpers@6.0.2': - resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==} + '@csstools/color-helpers@6.1.0': + resolution: {integrity: sha512-064IFJdjTfUqnjpCVpMOdbr8FLQBhinbZj6yRv2An2E41O/pLEXqfFRWqGq/SxlE5PEUYTlvWsG2r8MswAVvkg==} engines: {node: '>=20.19.0'} - '@csstools/css-calc@3.1.1': - resolution: {integrity: sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==} + '@csstools/css-calc@3.3.0': + resolution: {integrity: sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-color-parser@4.0.2': - resolution: {integrity: sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==} + '@csstools/css-color-parser@4.1.10': + resolution: {integrity: sha512-UZhQLIUyJaaMepqehrCODwCg2KW25vFvLWBmqYFaPclYvvxzj/sG8LBOhBFCp11i9uE7t1EyS+RAoV9tztPFyw==} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 @@ -944,8 +989,8 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.1.1': - resolution: {integrity: sha512-BvqN0AMWNAnLk9G8jnUT77D+mUbY/H2b3uDTvg2isJkHaOufUE2R3AOwxWo7VBQKT1lOdwdvorddo2B/lk64+w==} + '@csstools/css-syntax-patches-for-csstree@1.1.7': + resolution: {integrity: sha512-fQ+05118eQS1cofO3aJpB5efgpBZMvIzwr/sbC8kDLVA5XLG8q1kJV5yzrUAI1f7lvhPnm8fgIjzFB8/O/5Dig==} peerDependencies: css-tree: ^3.2.1 peerDependenciesMeta: @@ -956,181 +1001,185 @@ packages: resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} engines: {node: '>=20.19.0'} - '@emnapi/core@1.8.1': - resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} + '@emnapi/core@1.11.2': + resolution: {integrity: sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==} + + '@emnapi/core@2.0.0-alpha.3': + resolution: {integrity: sha512-AZypUeJ/yByuxyS7BlSNRDOMLMlROYtjYdIAuBmJssVz1UJDSeYxLrdizhXCFYhedC5bqd/ASy8EuNXbVVXp9g==} + + '@emnapi/runtime@1.11.2': + resolution: {integrity: sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==} + + '@emnapi/runtime@2.0.0-alpha.3': + resolution: {integrity: sha512-hFPAhMUjJD9BSyCANEISPOogeXC9Zo9ZQl7L6vKnaVsMkCtzznaW/naYypeyl0Gv5rYfWYsZbpixTMpjDJzQeA==} - '@emnapi/runtime@1.8.1': - resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} + '@emnapi/wasi-threads@1.2.2': + resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} - '@emnapi/wasi-threads@1.1.0': - resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + '@emnapi/wasi-threads@2.0.1': + resolution: {integrity: sha512-9DsSk+o5NBX0CCJT8s0EROGSGxjR/tKu6aBTaVyq+SjAEQH4XcdcRxPBRzsBLizTTJ49MJjF+jgu3qnO9GLQcQ==} '@emotion/hash@0.9.2': resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} - '@esbuild-plugins/node-globals-polyfill@0.2.3': - resolution: {integrity: sha512-r3MIryXDeXDOZh7ih1l/yE9ZLORCd5e8vWg02azWRGj5SPTuoh69A2AIyn0Z31V/kHBfZ4HgWJ+OK3GTTwLmnw==} - peerDependencies: - esbuild: '>=0.25.0' - - '@esbuild/aix-ppc64@0.27.3': - resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} + '@esbuild/aix-ppc64@0.28.1': + resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.27.3': - resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} + '@esbuild/android-arm64@0.28.1': + resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.27.3': - resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} + '@esbuild/android-arm@0.28.1': + resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.27.3': - resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} + '@esbuild/android-x64@0.28.1': + resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.27.3': - resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} + '@esbuild/darwin-arm64@0.28.1': + resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.27.3': - resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} + '@esbuild/darwin-x64@0.28.1': + resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.27.3': - resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} + '@esbuild/freebsd-arm64@0.28.1': + resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.27.3': - resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} + '@esbuild/freebsd-x64@0.28.1': + resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.27.3': - resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} + '@esbuild/linux-arm64@0.28.1': + resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.27.3': - resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} + '@esbuild/linux-arm@0.28.1': + resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.27.3': - resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} + '@esbuild/linux-ia32@0.28.1': + resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.27.3': - resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} + '@esbuild/linux-loong64@0.28.1': + resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.27.3': - resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} + '@esbuild/linux-mips64el@0.28.1': + resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.27.3': - resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} + '@esbuild/linux-ppc64@0.28.1': + resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.27.3': - resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} + '@esbuild/linux-riscv64@0.28.1': + resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.27.3': - resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} + '@esbuild/linux-s390x@0.28.1': + resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.27.3': - resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} + '@esbuild/linux-x64@0.28.1': + resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.27.3': - resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} + '@esbuild/netbsd-arm64@0.28.1': + resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.27.3': - resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} + '@esbuild/netbsd-x64@0.28.1': + resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.27.3': - resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} + '@esbuild/openbsd-arm64@0.28.1': + resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.27.3': - resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} + '@esbuild/openbsd-x64@0.28.1': + resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.27.3': - resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} + '@esbuild/openharmony-arm64@0.28.1': + resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.27.3': - resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} + '@esbuild/sunos-x64@0.28.1': + resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.27.3': - resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} + '@esbuild/win32-arm64@0.28.1': + resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.27.3': - resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} + '@esbuild/win32-ia32@0.28.1': + resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.27.3': - resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} + '@esbuild/win32-x64@0.28.1': + resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} engines: {node: '>=18'} cpu: [x64] os: [win32] - '@exodus/bytes@1.15.0': - resolution: {integrity: sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==} + '@exodus/bytes@1.15.1': + resolution: {integrity: sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: '@noble/hashes': ^1.8.0 || ^2.0.0 @@ -1138,191 +1187,51 @@ packages: '@noble/hashes': optional: true - '@fontsource-variable/nunito@5.2.7': - resolution: {integrity: sha512-2N8QhatkyKgSUbAGZO2FYLioxA32+RyI1EplVLawbpkGjUeui9Qg9VMrpkCaik1ydjFjfLV+kzQ0cGEsMrMenQ==} - - '@fontsource/space-mono@5.2.9': - resolution: {integrity: sha512-b61faFOHEISQ/pD25G+cfGY9o/WW6lRv6hBQQfpWvEJ4y1V+S4gmth95EVyBE2VL3qDYHeVQ8nBzrplzdXTDDg==} - - '@formatjs/ecma402-abstract@2.3.6': - resolution: {integrity: sha512-HJnTFeRM2kVFVr5gr5kH1XP6K0JcJtE7Lzvtr3FS/so5f1kpsqqqxy5JF+FRaO6H2qmcMfAUIox7AJteieRtVw==} - - '@formatjs/fast-memoize@2.2.7': - resolution: {integrity: sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==} - - '@formatjs/icu-messageformat-parser@2.11.4': - resolution: {integrity: sha512-7kR78cRrPNB4fjGFZg3Rmj5aah8rQj9KPzuLsmcSn4ipLXQvC04keycTI1F7kJYDwIXtT2+7IDEto842CfZBtw==} - - '@formatjs/icu-skeleton-parser@1.8.16': - resolution: {integrity: sha512-H13E9Xl+PxBd8D5/6TVUluSpxGNvFSlN/b3coUp0e0JpuWXXnQDiavIpY3NnvSp4xhEMoXyyBvVfdFX8jglOHQ==} - - '@formatjs/intl-localematcher@0.6.2': - resolution: {integrity: sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==} + '@faker-js/faker@10.5.0': + resolution: {integrity: sha512-bsxD8WLS5lIj7aaoCx1YJkktqYj5vlBUE6HWzu2Q51ksrGJ0H737ECCKlFU7Yf8Br45z9t99frBp/J7kzbMPAg==} + engines: {node: ^20.19.0 || ^22.13.0 || ^23.5.0 || >=24.0.0, npm: '>=10'} - '@img/colour@1.1.0': - resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} - engines: {node: '>=18'} - - '@img/sharp-darwin-arm64@0.34.5': - resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [darwin] - - '@img/sharp-darwin-x64@0.34.5': - resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [darwin] - - '@img/sharp-libvips-darwin-arm64@1.2.4': - resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} - cpu: [arm64] - os: [darwin] - - '@img/sharp-libvips-darwin-x64@1.2.4': - resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} - cpu: [x64] - os: [darwin] - - '@img/sharp-libvips-linux-arm64@1.2.4': - resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-arm@1.2.4': - resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} - cpu: [arm] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-ppc64@1.2.4': - resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-riscv64@1.2.4': - resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-s390x@1.2.4': - resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-x64@1.2.4': - resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': - resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@img/sharp-libvips-linuxmusl-x64@1.2.4': - resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} - cpu: [x64] - os: [linux] - libc: [musl] - - '@img/sharp-linux-arm64@0.34.5': - resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-arm@0.34.5': - resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-ppc64@0.34.5': - resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-riscv64@0.34.5': - resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-s390x@0.34.5': - resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [s390x] - os: [linux] - libc: [glibc] + '@floating-ui/core@1.8.0': + resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==} - '@img/sharp-linux-x64@0.34.5': - resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - libc: [glibc] + '@floating-ui/dom@1.7.6': + resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==} - '@img/sharp-linuxmusl-arm64@0.34.5': - resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - libc: [musl] + '@floating-ui/utils@0.2.12': + resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} - '@img/sharp-linuxmusl-x64@0.34.5': - resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - libc: [musl] + '@fontsource-variable/nunito@5.3.0': + resolution: {integrity: sha512-tw0ch7evIDSwoeUy+0XzocYHfbRWLeCRPJy22KgbrLjDLAbNw/fErBiil10Ssrs3Sjr55zaZrr36a975j8cBkQ==} - '@img/sharp-wasm32@0.34.5': - resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [wasm32] + '@fontsource/space-mono@5.3.0': + resolution: {integrity: sha512-FrpBMOVWn3PRdRHlrZWC55X3kZG/2BTH7SM5ZyS/bky3iKya2BturI5lk+t0RoDQL2JyHzURsVq/EisCsxpI5A==} - '@img/sharp-win32-arm64@0.34.5': - resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [win32] + '@grpc/grpc-js@1.14.4': + resolution: {integrity: sha512-k9Dj3DV/itK9D06Y8f190Qgop7/Ui+D0njFV3LHMPwPT75DpXLQohE9Wmz0QElrJnzsjB7KPWiKJbOl7IPDArQ==} + engines: {node: '>=12.10.0'} - '@img/sharp-win32-ia32@0.34.5': - resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [ia32] - os: [win32] + '@grpc/proto-loader@0.7.15': + resolution: {integrity: sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==} + engines: {node: '>=6'} + hasBin: true - '@img/sharp-win32-x64@0.34.5': - resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [win32] + '@grpc/proto-loader@0.8.1': + resolution: {integrity: sha512-wtF6h+DY6M3YaDBPAmvuuA6jV8Sif9MjtOI5euKFWRgCDl5PeDpPsHR9u2l6St5ceY8AZgoNDww5+HvEsXFsGg==} + engines: {node: '>=6'} + hasBin: true - '@internationalized/date@3.12.0': - resolution: {integrity: sha512-/PyIMzK29jtXaGU23qTvNZxvBXRtKbNnGDFD+PY6CZw/Y8Ex8pFUzkuCJCG9aOqmShjqhS9mPqP6Dk5onQY8rQ==} + '@internationalized/date@3.12.2': + resolution: {integrity: sha512-FY1Y+H64NDs+HAF6omlnWxm3mEpfgaCSWtL5l551ZZfImA+kGjPFgrnJrGjH6lfmLL0g8Z/mBu1R3kufeCp6Jw==} - '@internationalized/message@3.1.8': - resolution: {integrity: sha512-Rwk3j/TlYZhn3HQ6PyXUV0XP9Uv42jqZGNegt0BXlxjE6G3+LwHjbQZAGHhCnCPdaA6Tvd3ma/7QzLlLkJxAWA==} + '@internationalized/number@3.6.7': + resolution: {integrity: sha512-3ji1fcrT+FPAK86UqEhB/psHixYo6niWPJtt7+qRaYFynt/BaJG8GhAPimtWUpEiVSTq8ZM8L5psMxGquiB/Vg==} - '@internationalized/number@3.6.5': - resolution: {integrity: sha512-6hY4Kl4HPBvtfS62asS/R22JzNNy8vi/Ssev7x6EobfCp+9QIB2hKvI2EtbdJ0VSQacxVNtqhE/NmF/NZ0gm6g==} + '@internationalized/string@3.2.9': + resolution: {integrity: sha512-kzP/M/mbQxODlmOt4bIQZ2SBVUWUSqMLXooXixnX7noche8WHaQcA+nwFN1K2KCF/cp+LDUhcJsCicwkvhD1pg==} - '@internationalized/string@3.2.7': - resolution: {integrity: sha512-D4OHBjrinH+PFZPvfCXvG28n2LSykWcJ7GIioQL+ok0LON15SdfoUssoHzzOUmVZLbRoREsQXVzA6r8JKsbP6A==} + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} '@isaacs/cliui@9.0.0': resolution: {integrity: sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==} @@ -1347,1053 +1256,810 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@jridgewell/trace-mapping@0.3.9': - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@js-sdsl/ordered-map@4.4.2': + resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==} + + '@kwsites/file-exists@1.1.1': + resolution: {integrity: sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==} + + '@livekit/components-core@0.12.14': + resolution: {integrity: sha512-6OKP/1Ok2fCZewDLKd3SzaTb7KvfZl/6hjggI+TgUdhp0Y7HBg3+tHA7YmhZRc0BO8wIyVy4VgTPn7qkLHt2nQ==} + engines: {node: '>=18'} + peerDependencies: + livekit-client: ^2.20.1 + tslib: ^2.6.2 + + '@livekit/components-react@2.9.23': + resolution: {integrity: sha512-clO+0g/u3YBpuOvnAjUSAJBH/o7w+RpUAseswjiSML9rHrXlTUhwBNm8LPk+qN5GOU3A6lzsNnFYVpUHUBVOkg==} + engines: {node: '>=18'} + peerDependencies: + '@livekit/krisp-noise-filter': ^0.2.12 || ^0.3.0 || ^0.4.0 + livekit-client: ^2.20.1 + react: '>=18' + react-dom: '>=18' + tslib: ^2.6.2 + peerDependenciesMeta: + '@livekit/krisp-noise-filter': + optional: true + + '@livekit/mutex@1.1.1': + resolution: {integrity: sha512-EsshAucklmpuUAfkABPxJNhzj9v2sG7JuzFDL4ML1oJQSV14sqrpTYnsaOudMAw9yOaW53NU3QQTlUQoRs4czw==} - '@juggle/resize-observer@3.4.0': - resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==} + '@livekit/protocol@1.50.4': + resolution: {integrity: sha512-L1uggNQAqyY21smQY8AllyOYbcv9Me9TaxwuLytL1R8ck9nbYPmQLNwEDi3pOFGAMa5F8I2nUi2Jc59W5awxlA==} - '@matrix-org/matrix-sdk-crypto-wasm@15.3.0': - resolution: {integrity: sha512-QyxHvncvkl7nf+tnn92PjQ54gMNV8hMSpiukiDgNrqF6IYwgySTlcSdkPYdw8QjZJ0NR6fnVrNzMec0OohM3wA==} + '@lottiefiles/dotlottie-react@0.19.15': + resolution: {integrity: sha512-KODY2xE+94Se9Co8Jwf7Q36DWp7uWvC84ORPx/cgyRieNwB5/QDFJItfpnuPquQQb2ddwnUskOEJLbzx39yKOQ==} + engines: {node: '>=18.17.0', npm: '>=9.5.0'} + peerDependencies: + react: ^17 || ^18 || ^19 + + '@lottiefiles/dotlottie-web@0.79.1': + resolution: {integrity: sha512-UKk+DmY2yurQs2T02AekNGVLD+fSGlygHKnf9485LCWF+CUvMSUXizAgR2qDGFUH2APj2i58nk21QhfVi9EG6g==} + engines: {node: '>=18.17.0', npm: '>=9.5.0'} + + '@lottiefiles/dotlottie-web@0.79.2': + resolution: {integrity: sha512-9/LBOmTqhrFOB6298tD9BEfCmTESHKL51u8meNQF7lgsSsdFMC2Xx7jd/jZTC5svWXzcE2qZ0XvaoGbopIUElw==} + engines: {node: '>=18.17.0', npm: '>=9.5.0'} + + '@matrix-org/matrix-sdk-crypto-wasm@18.4.0': + resolution: {integrity: sha512-osxkU1DQ+05+anGHapjWyvZqdHUb94Id37gy54mCKn1Cq/D7iGT5oEUEhjp4oTnCLo4TOtI6ULJ/LHsapaIptQ==} engines: {node: '>= 18'} - '@napi-rs/canvas-android-arm64@0.1.96': - resolution: {integrity: sha512-ew1sPrN3dGdZ3L4FoohPfnjq0f9/Jk7o+wP7HkQZokcXgIUD6FIyICEWGhMYzv53j63wUcPvZeAwgewX58/egg==} + '@napi-rs/canvas-android-arm64@1.0.3': + resolution: {integrity: sha512-7kSCdUhoXiO+AaIMXdBGdtp6EctZNkmF62Rea/BmVQlwKaM3bBhOzyGUzxyxz9dv5vdBfpyAaxhSRSJF4kqK4A==} engines: {node: '>= 10'} cpu: [arm64] os: [android] - '@napi-rs/canvas-darwin-arm64@0.1.96': - resolution: {integrity: sha512-Q/wOXZ5PzTqpdmA5eUOcegCf4Go/zz3aZ5DlzSeDpOjFmfwMKh8EzLAoweQ+mJVagcHQyzoJhaTEnrO68TNyNg==} + '@napi-rs/canvas-darwin-arm64@1.0.3': + resolution: {integrity: sha512-ds14V1BPagLszQyaDTeggny5fNeTCqsUQ5QhFj9VDxSEfzrVxXtdbR0LoFyKa0Siaaw8KvqSk4t7k/WoZJwvbg==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@napi-rs/canvas-darwin-x64@0.1.96': - resolution: {integrity: sha512-UrXiQz28tQEvGM1qvyptewOAfmUrrd5+wvi6Rzjj2VprZI8iZ2KIvBD2lTTG1bVF95AbeDeG7PJA0D9sLKaOFA==} + '@napi-rs/canvas-darwin-x64@1.0.3': + resolution: {integrity: sha512-qof3LRAAycmkV2I1izZo9RoSHF8kCQr5O05sFwv0jK8rSdYV6KHVwimo6Qb7RxZj40WHKbLHm5JDaUF0o5XUAA==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@napi-rs/canvas-linux-arm-gnueabihf@0.1.96': - resolution: {integrity: sha512-I90ODxweD8aEP6XKU/NU+biso95MwCtQ2F46dUvhec1HesFi0tq/tAJkYic/1aBSiO/1kGKmSeD1B0duOHhEHQ==} + '@napi-rs/canvas-linux-arm-gnueabihf@1.0.3': + resolution: {integrity: sha512-FU2kKZLmolHA9+KcUA+l1+xH3WTLUUTQDU/kLv9SEUr2TrRPu94aytOeizFJDHPs/QBcw4QL1mCQhetQXYBbag==} engines: {node: '>= 10'} cpu: [arm] os: [linux] - '@napi-rs/canvas-linux-arm64-gnu@0.1.96': - resolution: {integrity: sha512-Dx/0+RFV++w3PcRy+4xNXkghhXjA5d0Mw1bs95emn5Llinp1vihMaA6WJt3oYv2LAHc36+gnrhIBsPhUyI2SGw==} + '@napi-rs/canvas-linux-arm64-gnu@1.0.3': + resolution: {integrity: sha512-GVSjntxKeA+/y/ZKf1F+cmUw1WeIkE5aMRPqnZUlBTBvBcrvgWccJAWuYCKPX4QJQwZILIIwhgdAbl51yj6fpA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [glibc] - '@napi-rs/canvas-linux-arm64-musl@0.1.96': - resolution: {integrity: sha512-UvOi7fii3IE2KDfEfhh8m+LpzSRvhGK7o1eho99M2M0HTik11k3GX+2qgVx9EtujN3/bhFFS1kSO3+vPMaJ0Mg==} + '@napi-rs/canvas-linux-arm64-musl@1.0.3': + resolution: {integrity: sha512-J51oK/axyZ13kxycumSMfLiDZMdWdOVvqDFI28BpuViZHE3A0bQfr8B5vg8YnPEnqLD3BSn1hkdlh2buspEcNQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [musl] - '@napi-rs/canvas-linux-riscv64-gnu@0.1.96': - resolution: {integrity: sha512-MBSukhGCQ5nRtf9NbFYWOU080yqkZU1PbuH4o1ROvB4CbPl12fchDR35tU83Wz8gWIM9JTn99lBn9DenPIv7Ig==} + '@napi-rs/canvas-linux-riscv64-gnu@1.0.3': + resolution: {integrity: sha512-CtQgQjoVTX67jS9XuCTtJ40Sl7wRLMguoFnnGnfDmCWf7kzKFZVwj5ynqUOIGKFMSB61ZCuQlwPvVNxYTTseaw==} engines: {node: '>= 10'} cpu: [riscv64] os: [linux] libc: [glibc] - '@napi-rs/canvas-linux-x64-gnu@0.1.96': - resolution: {integrity: sha512-I/ccu2SstyKiV3HIeVzyBIWfrJo8cN7+MSQZPnabewWV6hfJ2nY7Df2WqOHmobBRUw84uGR6zfQHsUEio/m5Vg==} + '@napi-rs/canvas-linux-x64-gnu@1.0.3': + resolution: {integrity: sha512-jtfzAHFp+FRaR7zGT4jyCe6wUgAG/dVb5A4Apd8FY9jKarntDfUAlJXscugiH7ZF5kKnu7/lHFk9LaDPcrGEVQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [glibc] - '@napi-rs/canvas-linux-x64-musl@0.1.96': - resolution: {integrity: sha512-H3uov7qnTl73GDT4h52lAqpJPsl1tIUyNPWJyhQ6gHakohNqqRq3uf80+NEpzcytKGEOENP1wX3yGwZxhjiWEQ==} + '@napi-rs/canvas-linux-x64-musl@1.0.3': + resolution: {integrity: sha512-xTzaUCKUHTY4bCGadeeRZggbRVbGUT1petg7Z8r9AJR2+D9Bqu6nQAgqBGC6D47tA70LjaaaLTrJ7wNY1T74dg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [musl] - '@napi-rs/canvas-win32-arm64-msvc@0.1.96': - resolution: {integrity: sha512-ATp6Y+djOjYtkfV/VRH7CZ8I1MEtkUQBmKUbuWw5zWEHHqfL0cEcInE4Cxgx7zkNAhEdBbnH8HMVrqNp+/gwxA==} + '@napi-rs/canvas-win32-arm64-msvc@1.0.3': + resolution: {integrity: sha512-ktVLuBkI6QVOm5BwO/WbdGwxgeetAMJa7TTmR8qBarXF0OU2NKjvjUtPJAl2y8t+zBRczJl/1VOl9gua6WcK2g==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@napi-rs/canvas-win32-x64-msvc@0.1.96': - resolution: {integrity: sha512-UYGdTltVd+Z8mcIuoqGmAXXUvwH5CLf2M6mIB5B0/JmX5J041jETjqtSYl7gN+aj3k1by/SG6sS0hAwCqyK7zw==} + '@napi-rs/canvas-win32-x64-msvc@1.0.3': + resolution: {integrity: sha512-SGhlQ8bDjL1Cz2KnsKMasr/5sTcwG/SZkB6WCJxLsmSm/3aS2C+3p39bA7iZ2/94+NkVDySZfbiGoaSZSFHYxA==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@napi-rs/canvas@0.1.96': - resolution: {integrity: sha512-6NNmNxvoJKeucVjxaaRUt3La2i5jShgiAbaY3G/72s1Vp3U06XPrAIxkAjBxpDcamEn/t+WJ4OOlGmvILo4/Ew==} + '@napi-rs/canvas@1.0.3': + resolution: {integrity: sha512-OlI657a5XXvKGFX7kNeIzJ8rO7IXt87Mqu2H8rXE46viAuOfum/JA7ysX7+eBhxNKznT+RCZh418mndlcFX3+w==} engines: {node: '>= 10'} - '@napi-rs/wasm-runtime@1.1.1': - resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==} - - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} + '@napi-rs/wasm-runtime@1.2.1': + resolution: {integrity: sha512-KjZdi8Q1wh89gsVmghvbrMgWl6ZWmRmHV6wjB7/g4Zf0dyO+hH3neZUtuDNPO00qq5YE5RITVWvrIZKRaAmzGQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} + peerDependencies: + '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.3 + '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.3 - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} + '@noble/hashes@2.3.0': + resolution: {integrity: sha512-oN+QwyX7VSHotibwubG3kpzbwKrfnyR6OOO+3Nk/53ADL7FmgHHz4TgrbaYKvvOw09u6QTx0oiH1cNCIOuN0CQ==} + engines: {node: '>= 20.19.0'} - '@oxc-resolver/binding-android-arm-eabi@11.19.1': - resolution: {integrity: sha512-aUs47y+xyXHUKlbhqHUjBABjvycq6YSD7bpxSW7vplUmdzAlJ93yXY6ZR0c1o1x5A/QKbENCvs3+NlY8IpIVzg==} + '@oxc-parser/binding-android-arm-eabi@0.142.0': + resolution: {integrity: sha512-ZiRGDutGsv1G6bL/ozy/koC0Sv39T1DqyoC4KD1DOy9ZoACm1O5UWhEK2c02Qdk+4lfLVkvFa/mQ0fm/4h1BtQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxc-resolver/binding-android-arm64@11.19.1': - resolution: {integrity: sha512-oolbkRX+m7Pq2LNjr/kKgYeC7bRDMVTWPgxBGMjSpZi/+UskVo4jsMU3MLheZV55jL6c3rNelPl4oD60ggYmqA==} + '@oxc-parser/binding-android-arm64@0.142.0': + resolution: {integrity: sha512-WZkvGRLNQTz8lR9zP5nLjUdlroRCopBu3g9zF1p/laE6DzT1UbQo8Rdz5MWhaJUPYg/6gp+jo7HUgsyKaN1FtQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxc-resolver/binding-darwin-arm64@11.19.1': - resolution: {integrity: sha512-nUC6d2i3R5B12sUW4O646qD5cnMXf2oBGPLIIeaRfU9doJRORAbE2SGv4eW6rMqhD+G7nf2Y8TTJTLiiO3Q/dQ==} + '@oxc-parser/binding-darwin-arm64@0.142.0': + resolution: {integrity: sha512-l4khS8LQOOVYsGRVARo1gSaCT/aBSceUVXgtovWc2+drnxVuDr082WA3OCHVdVzIz5JIrP/y9CWsSKxBDNmYGg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxc-resolver/binding-darwin-x64@11.19.1': - resolution: {integrity: sha512-cV50vE5+uAgNcFa3QY1JOeKDSkM/9ReIcc/9wn4TavhW/itkDGrXhw9jaKnkQnGbjJ198Yh5nbX/Gr2mr4Z5jQ==} + '@oxc-parser/binding-darwin-x64@0.142.0': + resolution: {integrity: sha512-QBsNF3nqlXmcH2B1YOPqQYmCJoy4HuIjUxGbBO/k5JAJUl68ghU2psRY2zPk+RyBaWqKP/qfL4oaFgEMCdwskA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxc-resolver/binding-freebsd-x64@11.19.1': - resolution: {integrity: sha512-xZOQiYGFxtk48PBKff+Zwoym7ScPAIVp4c14lfLxizO2LTTTJe5sx9vQNGrBymrf/vatSPNMD4FgsaaRigPkqw==} + '@oxc-parser/binding-freebsd-x64@0.142.0': + resolution: {integrity: sha512-b7Q7m4Cqc6XqNhri3R+QhU+GVy646Pn+bkdhrDdWym/Fdi0ZUa+d73H9dm5H91JtbtAQ/z1d8XKMW3oOV8a4tQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxc-resolver/binding-linux-arm-gnueabihf@11.19.1': - resolution: {integrity: sha512-lXZYWAC6kaGe/ky2su94e9jN9t6M0/6c+GrSlCqL//XO1cxi5lpAhnJYdyrKfm0ZEr/c7RNyAx3P7FSBcBd5+A==} + '@oxc-parser/binding-linux-arm-gnueabihf@0.142.0': + resolution: {integrity: sha512-3riVS5IhdH3uCZj1Y9ftDQlR0dvLsIlw/edrRqk8JhgNd5K0XSs+UBtgh50N13CAlW9/TXj6sVGXaKNBocd0Yg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-resolver/binding-linux-arm-musleabihf@11.19.1': - resolution: {integrity: sha512-veG1kKsuK5+t2IsO9q0DErYVSw2azvCVvWHnfTOS73WE0STdLLB7Q1bB9WR+yHPQM76ASkFyRbogWo1GR1+WbQ==} + '@oxc-parser/binding-linux-arm-musleabihf@0.142.0': + resolution: {integrity: sha512-NmXUOpgpTSkhl795TiXmWppTwmSJ92RC1qvD6e4XOF+slgmo3e6Ah+kEu+6AN8s7NAOEwqGmir58MgSQSWmBSA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-resolver/binding-linux-arm64-gnu@11.19.1': - resolution: {integrity: sha512-heV2+jmXyYnUrpUXSPugqWDRpnsQcDm2AX4wzTuvgdlZfoNYO0O3W2AVpJYaDn9AG4JdM6Kxom8+foE7/BcSig==} + '@oxc-parser/binding-linux-arm64-gnu@0.142.0': + resolution: {integrity: sha512-gc0EXsKtXgerujmU2Bql3u1L1HsSQ2774R83idq/FoNMPVV/RY/1ErFsvnit7KoiP/sLvzQixeUo4Ut0ic0wmw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxc-resolver/binding-linux-arm64-musl@11.19.1': - resolution: {integrity: sha512-jvo2Pjs1c9KPxMuMPIeQsgu0mOJF9rEb3y3TdpsrqwxRM+AN6/nDDwv45n5ZrUnQMsdBy5gIabioMKnQfWo9ew==} + '@oxc-parser/binding-linux-arm64-musl@0.142.0': + resolution: {integrity: sha512-F2XvmWSE0uWpie+jHKKIFgdVOe9ypGhkEZxKx5DuW215K6cbAC274yYaPkcM7EqY4Df3Weyhpcz3lsURyH2LVg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxc-resolver/binding-linux-ppc64-gnu@11.19.1': - resolution: {integrity: sha512-vLmdNxWCdN7Uo5suays6A/+ywBby2PWBBPXctWPg5V0+eVuzsJxgAn6MMB4mPlshskYbppjpN2Zg83ArHze9gQ==} + '@oxc-parser/binding-linux-ppc64-gnu@0.142.0': + resolution: {integrity: sha512-wLMbT21U/QxknQsk+VvNF0b9D2/aGWhcaQQQ+VYlE8FwD5+GoWZIPPXNzyHmkYyhm0KB3itL+TBavjMatqNnYA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxc-resolver/binding-linux-riscv64-gnu@11.19.1': - resolution: {integrity: sha512-/b+WgR+VTSBxzgOhDO7TlMXC1ufPIMR6Vj1zN+/x+MnyXGW7prTLzU9eW85Aj7Th7CCEG9ArCbTeqxCzFWdg2w==} + '@oxc-parser/binding-linux-riscv64-gnu@0.142.0': + resolution: {integrity: sha512-+G8F/4ckwT7FCJV4H2bt09xEzJbjNCfuL4Sp1AYNaFtFMVtgIGMuJlteT82U+K0UIZ/DzAR/LDlMFnEuajG7Kw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxc-resolver/binding-linux-riscv64-musl@11.19.1': - resolution: {integrity: sha512-YlRdeWb9j42p29ROh+h4eg/OQ3dTJlpHSa+84pUM9+p6i3djtPz1q55yLJhgW9XfDch7FN1pQ/Vd6YP+xfRIuw==} + '@oxc-parser/binding-linux-riscv64-musl@0.142.0': + resolution: {integrity: sha512-hTsHtTLxMAfCo+rpF5K3qZJKW2NpPN/CHd4mYB3y7XlSdspHkd2gehDIofP64AacA9nWQw2tY3O7wR6UY8IVOA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxc-resolver/binding-linux-s390x-gnu@11.19.1': - resolution: {integrity: sha512-EDpafVOQWF8/MJynsjOGFThcqhRHy417sRyLfQmeiamJ8qVhSKAn2Dn2VVKUGCjVB9C46VGjhNo7nOPUi1x6uA==} + '@oxc-parser/binding-linux-s390x-gnu@0.142.0': + resolution: {integrity: sha512-6y7qYY3TCUDYjqswImdTGl92y+KA/80twALegQPN27kfY+bG7Ib1+L3jbmrCZQx6wrVnai9IPsEZp07I0hx7JQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxc-resolver/binding-linux-x64-gnu@11.19.1': - resolution: {integrity: sha512-NxjZe+rqWhr+RT8/Ik+5ptA3oz7tUw361Wa5RWQXKnfqwSSHdHyrw6IdcTfYuml9dM856AlKWZIUXDmA9kkiBQ==} + '@oxc-parser/binding-linux-x64-gnu@0.142.0': + resolution: {integrity: sha512-i69kAWU+2LgoH5bR+zWiiu+UzAw7Oxkwv7COeJTeY19pn4e70nKQcr9Pm6cL2Z0Z54d+gl9qADlK/0yyuCPiBA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxc-resolver/binding-linux-x64-musl@11.19.1': - resolution: {integrity: sha512-cM/hQwsO3ReJg5kR+SpI69DMfvNCp+A/eVR4b4YClE5bVZwz8rh2Nh05InhwI5HR/9cArbEkzMjcKgTHS6UaNw==} + '@oxc-parser/binding-linux-x64-musl@0.142.0': + resolution: {integrity: sha512-4SQs678MmjYVrmhAgCWD4o0vpaFszXw9xLX5p2Z9MMFcltxiLkA88wQjh80YHjPrXtpyZ2CWI5m+1yNKM0m2Pw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxc-resolver/binding-openharmony-arm64@11.19.1': - resolution: {integrity: sha512-QF080IowFB0+9Rh6RcD19bdgh49BpQHUW5TajG1qvWHvmrQznTZZjYlgE2ltLXyKY+qs4F/v5xuX1XS7Is+3qA==} + '@oxc-parser/binding-openharmony-arm64@0.142.0': + resolution: {integrity: sha512-YHpx9N7Ln3a++Tc8rv+H7mrK1zyJQOAwCFg8LZ3lTs1T5afGWeZrLPhPT9HLnIwSjCyJqPWVMIrMxbjcmBr2oQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxc-resolver/binding-wasm32-wasi@11.19.1': - resolution: {integrity: sha512-w8UCKhX826cP/ZLokXDS6+milN8y4X7zidsAttEdWlVoamTNf6lhBJldaWr3ukTDiye7s4HRcuPEPOXNC432Vg==} - engines: {node: '>=14.0.0'} + '@oxc-parser/binding-wasm32-wasi@0.142.0': + resolution: {integrity: sha512-3pLDyY3+oogW73RM5uehNgAiR/Xfb7fvO2Q1Z1gIqZ2+50XDVQmBVlRkHXZTU4gKnQHpwETNsYQVsJ3joVB2iA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] - '@oxc-resolver/binding-win32-arm64-msvc@11.19.1': - resolution: {integrity: sha512-nJ4AsUVZrVKwnU/QRdzPCCrO0TrabBqgJ8pJhXITdZGYOV28TIYystV1VFLbQ7DtAcaBHpocT5/ZJnF78YJPtQ==} + '@oxc-parser/binding-win32-arm64-msvc@0.142.0': + resolution: {integrity: sha512-Had/VeVY28Oyb0K+Q4FV8KCzoBycIh93oDK6pCbya9lkzdq+ikMHMgBubsdqqlybjJmQRawCQRrnBRHyQwYvcQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxc-resolver/binding-win32-ia32-msvc@11.19.1': - resolution: {integrity: sha512-EW+ND5q2Tl+a3pH81l1QbfgbF3HmqgwLfDfVithRFheac8OTcnbXt/JxqD2GbDkb7xYEqy1zNaVFRr3oeG8npA==} + '@oxc-parser/binding-win32-ia32-msvc@0.142.0': + resolution: {integrity: sha512-GGi3+YphVHavvgs6gum2UXoNCqzHAmPt/nXkn8ZQZstV2Q1qZD1Mn8fz/nWrDkefHQtrG/+1/XrbMxsBTo6Svw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxc-resolver/binding-win32-x64-msvc@11.19.1': - resolution: {integrity: sha512-6hIU3RQu45B+VNTY4Ru8ppFwjVS/S5qwYyGhBotmjxfEKk41I2DlGtRfGJndZ5+6lneE2pwloqunlOyZuX/XAw==} + '@oxc-parser/binding-win32-x64-msvc@0.142.0': + resolution: {integrity: sha512-Ny/Wv4Us1LGC/ljwNTp+Hx3r/pH15EFfeDF0p+n898gt+TtRd6C9SccHcuUhDiNTb8s5tt7jdeAMDRQZ4Vq6hg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@oxfmt/binding-android-arm-eabi@0.45.0': - resolution: {integrity: sha512-A/UMxFob1fefCuMeGxQBulGfFE38g2Gm23ynr3u6b+b7fY7/ajGbNsa3ikMIkGMLJW/TRoQaMoP1kME7S+815w==} - engines: {node: ^20.19.0 || >=22.12.0} + '@oxc-project/types@0.142.0': + resolution: {integrity: sha512-7W+2q5AKQVU36fkaryontrHn3YDt1RyUYXatw9i5H8ocYe2sPKSFB6eS8WNPeRKiN1qAWWZUPm7gwFzJGrccqQ==} + + '@oxc-resolver/binding-android-arm-eabi@11.24.2': + resolution: {integrity: sha512-y09e0L0SRI2OA2tUIrjBgoV3eH5hvUKXNkJqXmNo5V2WxIjyC7I7aJfRLMEVpA8yi95f90gFDvO0VMgrDw+vwA==} cpu: [arm] os: [android] - '@oxfmt/binding-android-arm64@0.45.0': - resolution: {integrity: sha512-L63z4uZmHjgvvqvMJD7mwff8aSBkM0+X4uFr6l6U5t6+Qc9DCLVZWIunJ7Gm4fn4zHPdSq6FFQnhu9yqqobxIg==} - engines: {node: ^20.19.0 || >=22.12.0} + '@oxc-resolver/binding-android-arm64@11.24.2': + resolution: {integrity: sha512-cl4icWaZFnLdg8m6qtnh5rBMuGbxc/ptStFHLeCNwr+2cZjkjNwQu/jYRS0CHlnPecOJMpuS5M6/BH+0J/YkEg==} cpu: [arm64] os: [android] - '@oxfmt/binding-darwin-arm64@0.45.0': - resolution: {integrity: sha512-UV34dd623FzqT+outIGndsCA/RBB+qgB3XVQhgmmJ9PJwa37NzPC9qzgKeOhPKxVk2HW+JKldQrVL54zs4Noww==} - engines: {node: ^20.19.0 || >=22.12.0} + '@oxc-resolver/binding-darwin-arm64@11.24.2': + resolution: {integrity: sha512-At29QEMF6HajbQvgY8K6OXnHD1x9rad74xBEfmCB6ZqCGsdq75aK7tOYcTbOanMy8qdIBrfL3SMr3p/lfSlb9w==} cpu: [arm64] os: [darwin] - '@oxfmt/binding-darwin-x64@0.45.0': - resolution: {integrity: sha512-pMNJv0CMa1pDefVPeNbuQxibh8ITpWDFEhMC/IBB9Zlu76EbgzYwrzI4Cb11mqX2+rIYN70UTrh3z06TM59ptQ==} - engines: {node: ^20.19.0 || >=22.12.0} + '@oxc-resolver/binding-darwin-x64@11.24.2': + resolution: {integrity: sha512-A5Kqr1EUj4oIL5CF4WRssq/o5P0Y11cwoFouMRmQ7YnC/A8V93nv1nb7aSU8HwcgmXropjLNkVTl4MN87cu28Q==} cpu: [x64] os: [darwin] - '@oxfmt/binding-freebsd-x64@0.45.0': - resolution: {integrity: sha512-xTcRoxbbo61sW2+ZRPeH+vp/o9G8gkdhiVumFU+TpneiPm14c79l6GFlxPXlCE9bNWikigbsrvJw46zCVAQFfg==} - engines: {node: ^20.19.0 || >=22.12.0} + '@oxc-resolver/binding-freebsd-x64@11.24.2': + resolution: {integrity: sha512-R5xkRBRRz7ceH/P5Jrc6G7FmdUdgpLYyESFAUDVTNQ9K0sGPxcp4ljiwEwEqsvNcQ4sYbMRrWcHHBCu7ksAJVw==} cpu: [x64] os: [freebsd] - '@oxfmt/binding-linux-arm-gnueabihf@0.45.0': - resolution: {integrity: sha512-hWL8Hdni+3U1mPFx1UtWeGp3tNb6EhBAUHRMbKUxVkOp3WwoJbpVO2bfUVbS4PfpledviXXNHSTl1veTa6FhkQ==} - engines: {node: ^20.19.0 || >=22.12.0} + '@oxc-resolver/binding-linux-arm-gnueabihf@11.24.2': + resolution: {integrity: sha512-k/RuYL4L/R58IBn3wT5ma3Wh4k62bp1eYCFRWCmMsasUOqL+H6sW0VGFadEzKWXFFlz+2uIMoeMk9ySSZJHgbg==} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm-musleabihf@0.45.0': - resolution: {integrity: sha512-6Blt/0OBT7vvfQpqYuYbpbFLPqSiaYpEJzUUWhinPEuADypDbtV1+LdjM0vYBNGPvnj85ex7lTerEX6JGcPt9w==} - engines: {node: ^20.19.0 || >=22.12.0} + '@oxc-resolver/binding-linux-arm-musleabihf@11.24.2': + resolution: {integrity: sha512-bnHAak3ujYfH5pKk4NieFNbvYvernfoQDgwLddbZ3OtMYrem87/qjlA+u+aKG0oZcqSLGCful/6/CEA+aeAgaA==} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm64-gnu@0.45.0': - resolution: {integrity: sha512-jLjoLfe+hGfjhA8hNBSdw85yCA8ePKq7ME4T+g6P9caQXvmt6IhE2X7iVjnVdkmYUWEzZrxlh4p6RkDmAMJY/A==} - engines: {node: ^20.19.0 || >=22.12.0} + '@oxc-resolver/binding-linux-arm64-gnu@11.24.2': + resolution: {integrity: sha512-vDT3KHgzYp47gmtNOqL2VNhCyl5Zv643eyxm//A68J8DeUGXrvD1pZFiaT4jSfe+RInfnn1R2yVHye4enx6RnA==} cpu: [arm64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-arm64-musl@0.45.0': - resolution: {integrity: sha512-XQKXZIKYJC3GQJ8FnD3iMntpw69Wd9kDDK/Xt79p6xnFYlGGxSNv2vIBvRTDg5CKByWFWWZLCRDOXoP/m6YN4g==} - engines: {node: ^20.19.0 || >=22.12.0} + '@oxc-resolver/binding-linux-arm64-musl@11.24.2': + resolution: {integrity: sha512-+kMlQvbzfyEYtu5FcjE4p+ttBLpKW4d/AsAsuE69BxV6V4twZJeIQZFfD8gh/wqglY0MkPSezWXQH0jBV13MUw==} cpu: [arm64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-ppc64-gnu@0.45.0': - resolution: {integrity: sha512-+g5RiG+xOkdrCWkKodv407nTvMq4vYM18Uox2MhZBm/YoqFxxJpWKsloskFFG5NU13HGPw1wzYjjOVcyd9moCA==} - engines: {node: ^20.19.0 || >=22.12.0} + '@oxc-resolver/binding-linux-ppc64-gnu@11.24.2': + resolution: {integrity: sha512-shjfMhmZ3gq9fv/w7bi3PnZlgOPG+2QAOFf0BJF0EgBSIGZ6PMLN2zbGEblTUYB/NKVDRyYhE2ff3dJ1QqNPkA==} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-gnu@0.45.0': - resolution: {integrity: sha512-V7dXKoSyEbWAkkSF4JJNtF+NJZDmJoSarSoP30WCsB3X636Rehd3CvxBj49FIJxEBFWhvcUjGSHVeU8Erck1bQ==} - engines: {node: ^20.19.0 || >=22.12.0} + '@oxc-resolver/binding-linux-riscv64-gnu@11.24.2': + resolution: {integrity: sha512-zGelwFR5oRo+b69k8Lrzun86DyUHzfKN6cnjbR9l7Z7NIRznOE/2ZvPa1IUKqAL2PzAXOdwkfVqNvO1H2RlpAw==} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-musl@0.45.0': - resolution: {integrity: sha512-Vdelft1sAEYojVGgcODEFXSWYQYlIvoyIGWebKCuUibd1tvS1TjTx413xG2ZLuHpYj45CkN/ztMLMX6jrgqpgg==} - engines: {node: ^20.19.0 || >=22.12.0} + '@oxc-resolver/binding-linux-riscv64-musl@11.24.2': + resolution: {integrity: sha512-qxZ1SWCXJY0eyhAlP6Lmo9F2Nrtx7EkYj9oCgL8apDPCwXwCEDA2U697bbT81JIc2IrVjxO4KX6WU2N+oN9Z4w==} cpu: [riscv64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-s390x-gnu@0.45.0': - resolution: {integrity: sha512-RR7xKgNpqwENnK0aYCGYg0JycY2n93J0reNjHyes+I9Gq52dH95x+CBlnlAQHCPfz6FGnKA9HirgUl14WO6o7w==} - engines: {node: ^20.19.0 || >=22.12.0} + '@oxc-resolver/binding-linux-s390x-gnu@11.24.2': + resolution: {integrity: sha512-sGCecF3cx2DFlH4t/z7ApnOnXqN48p5p5mlHDEnHTAukQa2P+qMVE4CwyWE9W+q/m3QJ7kKfGrIjax31f44oFQ==} cpu: [s390x] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-gnu@0.45.0': - resolution: {integrity: sha512-U/QQ0+BQNSHxjuXR/utvXnQ50Vu5kUuqEomZvQ1/3mhgbBiMc2WU9q5kZ5WwLp3gnFIx9ibkveoRSe2EZubkqg==} - engines: {node: ^20.19.0 || >=22.12.0} + '@oxc-resolver/binding-linux-x64-gnu@11.24.2': + resolution: {integrity: sha512-k/VlMMcSzMlahb3/fENM4rTlsJ0s3fFROA0KXPBmKggqmTSaE383sl8F3KCOXPLmVsYfW6hCitMhXCEtNeZxxg==} cpu: [x64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-musl@0.45.0': - resolution: {integrity: sha512-o5TLOUCF0RWQjsIS06yVC+kFgp092/yLe6qBGSUvtnmTVw9gxjpdQSXc3VN5Cnive4K11HNstEZF8ROKHfDFSw==} - engines: {node: ^20.19.0 || >=22.12.0} + '@oxc-resolver/binding-linux-x64-musl@11.24.2': + resolution: {integrity: sha512-8hbnZyNi97b/8wapYaIF9+t9GmZKBW2vunaOc3h9HGJptH7b7XpvZqOTBSm/MpTjr7H497BlgOaSfLUdhmy2bw==} cpu: [x64] os: [linux] libc: [musl] - '@oxfmt/binding-openharmony-arm64@0.45.0': - resolution: {integrity: sha512-RnGcV3HgPuOjsGx/k9oyRNKmOp+NBLGzZTdPDYbc19r7NGeYPplnUU/BfU35bX2Y/O4ejvHxcfkvW2WoYL/gsg==} - engines: {node: ^20.19.0 || >=22.12.0} + '@oxc-resolver/binding-openharmony-arm64@11.24.2': + resolution: {integrity: sha512-MvyGik3a6pVgZ0t/kWlbmFxFLmXQJwgLsY2eYFHLpy0wGwRbfzeIGgDwQ3kXqE30z+kSXennRkCrT7TUvkptNg==} cpu: [arm64] os: [openharmony] - '@oxfmt/binding-win32-arm64-msvc@0.45.0': - resolution: {integrity: sha512-v3Vj7iKKsUFwt9w5hsqIIoErKVoENC6LoqfDlteOQ5QMDCXihlqLoxpmviUhXnNncg4zV6U9BPwlBbwa+qm4wg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [win32] - - '@oxfmt/binding-win32-ia32-msvc@0.45.0': - resolution: {integrity: sha512-N8yotPBX6ph0H3toF4AEpdCeVPrdcSetj+8eGiZGsrLsng3bs/Q5HPu4bbSxip5GBPx5hGbGHrZwH4+rcrjhHA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ia32] - os: [win32] - - '@oxfmt/binding-win32-x64-msvc@0.45.0': - resolution: {integrity: sha512-w5MMTRCK1dpQeRA+HHqXQXyN33DlG/N2LOYxJmaT4fJjcmZrbNnqw7SmIk7I2/a2493PPLZ+2E/Ar6t2iKVMug==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [win32] - - '@oxlint-tsgolint/darwin-arm64@0.21.0': - resolution: {integrity: sha512-P20j3MLqfwIT+94qGU3htC7dWp4pXGZW1p1p7FRUzu1aopq7c9nPCgf0W/WjktqQ57+iuTq9mbSlwWinl6+H1A==} - cpu: [arm64] - os: [darwin] - - '@oxlint-tsgolint/darwin-x64@0.21.0': - resolution: {integrity: sha512-81TmmuBcPedEA0MwRmObuQuXnCprS1UiHQWGe7pseqNAJzUWXeAPrayqKTACX92VpruJI+yvY0XJrFp11PpcTA==} - cpu: [x64] - os: [darwin] - - '@oxlint-tsgolint/linux-arm64@0.21.0': - resolution: {integrity: sha512-sbjBr6zDduX8rNO0PTjhf7VYLCPWqdijWiMPp8e10qu6Tam1GdaVLaLlX8QrNupTgglO1GvqqgY/jcacWL8a6g==} - cpu: [arm64] - os: [linux] - - '@oxlint-tsgolint/linux-x64@0.21.0': - resolution: {integrity: sha512-jNrOcy53R5TJQfrK444Cm60bW9437xDoxPbm3AdvFSo/fhdFMllawc7uZC2Wzr+EAjTkW13K8R4QHzsUdBG9fQ==} - cpu: [x64] - os: [linux] + '@oxc-resolver/binding-wasm32-wasi@11.24.2': + resolution: {integrity: sha512-vHcssMPwO08RTvj/c0iOBz90attxyG3wQJ0dTcyEQK43LRpcdLWZlV5feBhv6Isn6ahbQIzHbCgfa81+RiML0Q==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] - '@oxlint-tsgolint/win32-arm64@0.21.0': - resolution: {integrity: sha512-xWeRxJJILDE4b9UqHEWGBxcBc1TUS6zWHhxcyxTZMwf4q3wdKeu0OHYAcwLGJzoSjEIf6FTjyfPiRNil2oqsdg==} + '@oxc-resolver/binding-win32-arm64-msvc@11.24.2': + resolution: {integrity: sha512-uokJqro2iBqkFvJdKQLP7d8/BUmFwESQFVmIJUQKj1Xn1a/LysJoe1vmeECLF5b3jsV8CAL5sEMJXX6SdK9Nhg==} cpu: [arm64] os: [win32] - '@oxlint-tsgolint/win32-x64@0.21.0': - resolution: {integrity: sha512-Ob9AA9teI8ckPo1whV1smLr5NrqwgBv/8boDbK0YZG+fKgNGRwr1hBj1ORgFWOQaUBv+5njp5A0RAfJJjQ95QQ==} + '@oxc-resolver/binding-win32-x64-msvc@11.24.2': + resolution: {integrity: sha512-UqGPmo56KDfLlfXFAFIrNflHT8tFxWGEivWg3Zeyp4Uy2NlKN1FGPr6/BxcLGG3+kZ6Wp14g5Uj+n71boqZfiw==} cpu: [x64] os: [win32] - '@oxlint/binding-android-arm-eabi@1.60.0': - resolution: {integrity: sha512-YdeJKaZckDQL1qa62a1aKq/goyq48aX3yOxaaWqWb4sau4Ee4IiLbamftNLU3zbePky6QsDj6thnSSzHRBjDfA==} + '@oxfmt/binding-android-arm-eabi@0.61.0': + resolution: {integrity: sha512-BaS+1OVvg9sr+Xav0+KdWedQRcAzrdoEcwMZeqoc2F6ieC1s/t5eM35YQoRPQ7vAqkZ+p3tbQb1r9I9mrV5oGA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxlint/binding-android-arm64@1.60.0': - resolution: {integrity: sha512-7ANS7PpXCfq84xZQ8E5WPs14gwcuPcl+/8TFNXfpSu0CQBXz3cUo2fDpHT8v8HJN+Ut02eacvMAzTnc9s6X4tw==} + '@oxfmt/binding-android-arm64@0.61.0': + resolution: {integrity: sha512-of8atAV0M1egGcVOMbgZCvc10sFOP3ayQBNQV5h5G3fNq8gACdEswfFk9bzGrdbM23rtg0Coxi7np7oPLcueNw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxlint/binding-darwin-arm64@1.60.0': - resolution: {integrity: sha512-pJsgd9AfplLGBm1fIr25V6V14vMrayhx4uIQvlfH7jWs2SZwSrvi3TfgfJySB8T+hvyEH8K2zXljQiUnkgUnfQ==} + '@oxfmt/binding-darwin-arm64@0.61.0': + resolution: {integrity: sha512-7l8+5ov4BGwtAcmpzvEik/TG3bciwyw/S3e6j5GKH7pcQqcgCVxD3AuJeP6upto+SOTBKQ4wrrdbMt0gq8fHSQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxlint/binding-darwin-x64@1.60.0': - resolution: {integrity: sha512-Ue1aXHX49ivwflKqGJc7zcd/LeLgbhaTcDCQStgx5x06AXgjEAZmvrlMuIkWd4AL4FHQe6QJ9f33z04Cg448VQ==} + '@oxfmt/binding-darwin-x64@0.61.0': + resolution: {integrity: sha512-Fnz4dDDXBb7udk+DmwelNjxbD6yptyxwCqwCH2ebo4RVLxVsRfFsn/AHJC49KIltPrVokamGv4SSOsiV50DTxQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxlint/binding-freebsd-x64@1.60.0': - resolution: {integrity: sha512-YCyQzsQtusQw+gNRW9rRTifSO+Dt/+dtCl2NHoDMZqJlRTEZ/Oht9YnuporI9yiTx7+cB+eqzX3MtHHVHGIWhg==} + '@oxfmt/binding-freebsd-x64@0.61.0': + resolution: {integrity: sha512-mddOebKNCP+AucmzfNsk3jgbr681qAUvgMqi865GW5gWLJ/AnzXbvjQRrny0e++NAN8aphav/aRSrfFxNsNjpA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxlint/binding-linux-arm-gnueabihf@1.60.0': - resolution: {integrity: sha512-c7dxM2Zksa45Qw16i2iGY3Fti2NirJ38FrsBsKw+qcJ0OtqTsBgKJLF0xV+yLG56UH01Z8WRPgsw31e0MoRoGQ==} + '@oxfmt/binding-linux-arm-gnueabihf@0.61.0': + resolution: {integrity: sha512-svx59iYL+DbaZGZUIoice4W0CjRXGExnbz7Re+awIb60rVxBS2KrU7Hnlx+nZYanLGLpjneUEgo/VFEKkSZAyQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm-musleabihf@1.60.0': - resolution: {integrity: sha512-ZWALoA42UYqBEP1Tbw9OWURgFGS1nWj2AAvLdY6ZcGx/Gj93qVCBKjcvwXMupZibYwFbi9s/rzqkZseb/6gVtQ==} + '@oxfmt/binding-linux-arm-musleabihf@0.61.0': + resolution: {integrity: sha512-BYK9MPJPCf6d+fLKMTruThmEyCtHzQ1zLcsrTlUVkmnoXIaHAbfpeLYQwX1tkjs7W11dyzoi6HFvKcdnvX1zNg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm64-gnu@1.60.0': - resolution: {integrity: sha512-tpy+1w4p9hN5CicMCxqNy6ymfRtV5ayE573vFNjp1k1TN/qhLFgflveZoE/0++RlkHikBz2vY545NWm/hp7big==} + '@oxfmt/binding-linux-arm64-gnu@0.61.0': + resolution: {integrity: sha512-QUaCNLq2/EC6G5ljOuFanl9Lgw6ZWp4co7rs4+KOMUzbGfA4Lq58FHRjjF9sVIG+93XSbo343MxFATrOU1qctA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-arm64-musl@1.60.0': - resolution: {integrity: sha512-eDYDXZGhQAXyn6GwtwiX/qcLS0HlOLPJ/+iiIY8RYr+3P8oKBmgKxADLlniL6FtWfE7pPk7IGN9/xvDEvDvFeg==} + '@oxfmt/binding-linux-arm64-musl@0.61.0': + resolution: {integrity: sha512-S6uvJ6MXnRXl+zTs0CARNDvkE+cymj0EVWEKKsyKnlLlqTyQJBjw5s4D2pSIOZc+S46cy4STefzcr/sm0VzVPA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxlint/binding-linux-ppc64-gnu@1.60.0': - resolution: {integrity: sha512-nxehly5XYBHUWI9VJX1bqCf9j/B43DaK/aS/T1fcxCpX3PA4Rm9BB54nPD1CKayT8xg6REN1ao+01hSRNgy8OA==} + '@oxfmt/binding-linux-ppc64-gnu@0.61.0': + resolution: {integrity: sha512-6VDlRcytvZG6UlSIdAFKDLbppo9tvPxrWzle6vHldYFMeuDPQEfMKrkwezp7FaBq1wik9ra554ZZeRPsyIkFpg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-gnu@1.60.0': - resolution: {integrity: sha512-j1qf/NaUfOWQutjeoooNG1Q0zsK0XGmSu1uDLq3cctquRF3j7t9Hxqf/76ehCc5GEUAanth2W4Fa+XT1RFg/nw==} + '@oxfmt/binding-linux-riscv64-gnu@0.61.0': + resolution: {integrity: sha512-KkBTYbzExpbmn15XjKPLu2fRV2PVlq+KWt+brad5rwIa03vdYoaDRWiS7raHII/dCTR6Ro4UpYUCH4t6lif4WQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-musl@1.60.0': - resolution: {integrity: sha512-YELKPRefQ/q/h3RUmeRfPCUhh2wBvgV1RyZ/F9M9u8cDyXsQW2ojv1DeWQTt466yczDITjZnIOg/s05pk7Ve2A==} + '@oxfmt/binding-linux-riscv64-musl@0.61.0': + resolution: {integrity: sha512-69tzIq7sJLVB9dxYYtvMzcSSsnZHSO+U2U19O2RqDqgj6+Q4O7HjSXdaszbcgqzhsUwzSH7z5kWvk8nmf6BHTg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxlint/binding-linux-s390x-gnu@1.60.0': - resolution: {integrity: sha512-JkO3C6Gki7Y6h/MiIkFKvHFOz98/YWvQ4WYbK9DLXACMP2rjULzkeGyAzorJE5S1dzLQGFgeqvN779kSFwoV1g==} + '@oxfmt/binding-linux-s390x-gnu@0.61.0': + resolution: {integrity: sha512-Oqi/N0OvtOVXsPKAOOhKgGH3msRYF8BLJaNBbWiupRiKoKVyc8JRKPCfarkQJC+RgP9U8raUKLe+bNwd0HUMiA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-gnu@1.60.0': - resolution: {integrity: sha512-XjKHdFVCpZZZSWBCKyyqCq65s2AKXykMXkjLoKYODrD+f5toLhlwsMESscu8FbgnJQ4Y/dpR/zdazsahmgBJIA==} + '@oxfmt/binding-linux-x64-gnu@0.61.0': + resolution: {integrity: sha512-3TKwv/ed4uwJSemAA8P9XcoqETpjQI4waquF9UilhA9Mn/dhr1PdUEXWlL74mtc6ZNfmKPA9+NEJm01nRF8CVA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-musl@1.60.0': - resolution: {integrity: sha512-js29ZWIuPhNWzY8NC7KoffEMEeWG105vbmm+8EOJsC+T/jHBiKIJEUF78+F/IrgEWMMP9N0kRND4Pp75+xAhKg==} + '@oxfmt/binding-linux-x64-musl@0.61.0': + resolution: {integrity: sha512-uFso4u4nLkVSlMCpgjyvWV60Gt7GvDQHnk1mmRxHIkZTMB0ljpUKwCD9FYGgN9H97x2wYl0UwEjgRZaPIuhEhw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxlint/binding-openharmony-arm64@1.60.0': - resolution: {integrity: sha512-H+PUITKHk04stFpWj3x3Kg08Afp/bcXSBi0EhasR5a0Vw7StXHTzdl655PUI0fB4qdh2Wsu6Dsi+3ACxPoyQnA==} + '@oxfmt/binding-openharmony-arm64@0.61.0': + resolution: {integrity: sha512-keGLkzeOvkMpNmPp4hffXWpfoSsY6e1K8++KXD4mSSfxdvM8q9QUDsYY689TB1k6Co832DZn1MnaaVx6cIBMWQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxlint/binding-win32-arm64-msvc@1.60.0': - resolution: {integrity: sha512-WA/yc7f7ZfCefBXVzNHn1Ztulb1EFwNBb4jMZ6pjML0zz6pHujlF3Q3jySluz3XHl/GNeMTntG1seUBWVMlMag==} + '@oxfmt/binding-win32-arm64-msvc@0.61.0': + resolution: {integrity: sha512-VzsAISkFxmNhJ5LBDEL9VuH6tJsVJMtqYit2LyIUf/HLnsCe4Pg9SMOjjVQzGWt0bnpyfJ94CrqTqcpNZzK+ug==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxlint/binding-win32-ia32-msvc@1.60.0': - resolution: {integrity: sha512-33YxL1sqwYNZXtn3MD/4dno6s0xeedXOJlT1WohkVD565WvohClZUr7vwKdAk954n4xiEWJkewiCr+zLeq7AeA==} + '@oxfmt/binding-win32-ia32-msvc@0.61.0': + resolution: {integrity: sha512-xv4t7yzwJoYaLB6Zv28B3W+j7brEjsyv50rLTAQgmxJzddce9fAMCxed8dSAkbWES0zz2J29nYK5FaTuD2YBHg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxlint/binding-win32-x64-msvc@1.60.0': - resolution: {integrity: sha512-JOro4ZcfBLamJCyfURQmOQByoorgOdx3ZjAkSqnb/CyG/i+lN3KoV5LAgk5ZAW6DPq7/Cx7n23f8DuTWXTWgyQ==} + '@oxfmt/binding-win32-x64-msvc@0.61.0': + resolution: {integrity: sha512-6EZXFkqOwxdDYjIn3TSNnPk3ST5E5GiYd4FiM6UF/mCL/LZSfr6D6UygTfW3R1PCQP2quCKpCEGRlij8E3VYbg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@phosphor-icons/react@2.1.10': - resolution: {integrity: sha512-vt8Tvq8GLjheAZZYa+YG/pW7HDbov8El/MANW8pOAz4eGxrwhnbfrQZq0Cp4q8zBEu8NIhHdnr+r8thnfRSNYA==} - engines: {node: '>=10'} - peerDependencies: - react: '>= 16.8' - react-dom: '>= 16.8' + '@oxlint-tsgolint/darwin-arm64@7.0.2001': + resolution: {integrity: sha512-CUJEdbSZ54+Xy9OXqOhWLTKZKV0BBiV7C2i/ygyVmXtkUNXx5YCzN8DpSSshTAKktoL7S+tnQ/ftFG/i7X896w==} + cpu: [arm64] + os: [darwin] - '@polka/url@1.0.0-next.29': - resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + '@oxlint-tsgolint/darwin-x64@7.0.2001': + resolution: {integrity: sha512-pXfBb5BqONCcgrXQNUZWXgiYmRSWJzd97S8i41VVOh6ut0tyo+cJ5FKFpczDHxiVNfj/3e7c9B4MtztNdpIVCw==} + cpu: [x64] + os: [darwin] - '@poppinss/colors@4.1.6': - resolution: {integrity: sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==} + '@oxlint-tsgolint/linux-arm64@7.0.2001': + resolution: {integrity: sha512-roP7zujb/QDPzDwEKsFFpzNHHy91/Y7oX9vQXk78ekyZtcQj1QXDIMH33gjDdHBfRl4K9pZ36xhRgrP4Zr+R8A==} + cpu: [arm64] + os: [linux] - '@poppinss/dumper@0.6.5': - resolution: {integrity: sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==} + '@oxlint-tsgolint/linux-x64@7.0.2001': + resolution: {integrity: sha512-UDezNqdECVmngu2TPnjaS1YoAmcTaBoI5lV9vk3VahBxoi+I5r9k3iJTT7qZoYWOXTD/7T7bNcwRgrocR6BscQ==} + cpu: [x64] + os: [linux] - '@poppinss/exception@1.2.3': - resolution: {integrity: sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==} + '@oxlint-tsgolint/win32-arm64@7.0.2001': + resolution: {integrity: sha512-uJZhqB6pdXLuN+AD1F5082byyQti/NPmJA77GtcFlmT2HzRelqbNls3SaIqxpjdFgvSBF9g0yOKGBkGFg7kX8Q==} + cpu: [arm64] + os: [win32] - '@react-aria/breadcrumbs@3.5.32': - resolution: {integrity: sha512-S61vh5DJ2PXiXUwD7gk+pvS/b4VPrc3ZJOUZ0yVRLHkVESr5LhIZH+SAVgZkm1lzKyMRG+BH+fiRH/DZRSs7SA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@oxlint-tsgolint/win32-x64@7.0.2001': + resolution: {integrity: sha512-FkDRm8hx9OwzGQqyWG1tO5QrTLRApff9DzSgpz9QZau37BR8d1VYKOxMLGf6shPZntJFoTwIIJYT68VndYDCog==} + cpu: [x64] + os: [win32] - '@react-aria/button@3.14.5': - resolution: {integrity: sha512-ZuLx+wQj9VQhH9BYe7t0JowmKnns2XrFHFNvIVBb5RwxL+CIycIOL7brhWKg2rGdxvlOom7jhVbcjSmtAaSyaQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/calendar@3.9.5': - resolution: {integrity: sha512-k0kvceYdZZu+DoeqephtlmIvh1CxqdFyoN52iqVzTz9O0pe5Xfhq7zxPGbeCp4pC61xzp8Lu/6uFA/YNfQQNag==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/checkbox@3.16.5': - resolution: {integrity: sha512-ZhUT7ELuD52hb+Zpzw0ElLQiVOd5sKYahrh+PK3vq13Wk5TedBscALpjuXetI4pwFfdmAM1Lhgcsrd8+6AmyvA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/color@3.1.5': - resolution: {integrity: sha512-eysWdBRzE8WDhBzh1nfjyUgzseMokXGHjIoJo880T7IPJ8tTavfQni49pU1B2qWrNOWPyrwx4Bd9pzHyboxJSA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/combobox@3.15.0': - resolution: {integrity: sha512-qSjQTFwKl3x1jCP2NRSJ6doZqAp6c2GTfoiFwWjaWg1IewwLsglaW6NnzqRDFiqFbDGgXPn4MqtC1VYEJ3NEjA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/datepicker@3.16.1': - resolution: {integrity: sha512-6BltCVWt09yefTkGjb2gViGCwoddx9HKJiZbY9u6Es/Q+VhwNJQRtczbnZ3K32p262hIknukNf/5nZaCOI1AKA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/dialog@3.5.34': - resolution: {integrity: sha512-/x53Q5ynpW5Kv9637WYu7SrDfj3woSp6jJRj8l6teGnWW/iNZWYJETgzHfbxx+HPKYATCZesRoIeO2LnYIXyEA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/disclosure@3.1.3': - resolution: {integrity: sha512-S3k7Wqrj+x0sWcP88Z1stSr5TIZmKEmx2rU7RB1O1/jPpbw5mgKnjtiriOlTh+kwdK11FkeqgxyHzAcBAR+FMQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/dnd@3.11.6': - resolution: {integrity: sha512-4YLHUeYJleF+moAYaYt8UZqujudPvpoaHR+QMkWIFzhfridVUhCr6ZjGWrzpSZY3r68k46TG7YCsi4IEiNnysw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/focus@3.21.5': - resolution: {integrity: sha512-V18fwCyf8zqgJdpLQeDU5ZRNd9TeOfBbhLgmX77Zr5ae9XwaoJ1R3SFJG1wCJX60t34AW+aLZSEEK+saQElf3Q==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/form@3.1.5': - resolution: {integrity: sha512-BWlONgHn8hmaMkcS6AgMSLQeNqVBwqPNLhdqjDO/PCfzvV7O8NZw/dFeIzJwfG4aBfSpbHHRdXGdfrk3d8dylQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/grid@3.14.8': - resolution: {integrity: sha512-X6rRFKDu/Kh6Sv8FBap3vjcb+z4jXkSOwkYnexIJp5kMTo5/Dqo55cCBio5B70Tanfv32Ev/6SpzYG7ryxnM9w==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/gridlist@3.14.4': - resolution: {integrity: sha512-C/SbwC0qagZatoBrCjx8iZUex9apaJ8o8iRJ9eVHz0cpj7mXg6HuuotYGmDy9q67A2hve4I693RM1Cuwqwm+PQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/i18n@3.12.16': - resolution: {integrity: sha512-Km2CAz6MFQOUEaattaW+2jBdWOHUF8WX7VQoNbjlqElCP58nSaqi9yxTWUDRhAcn8/xFUnkFh4MFweNgtrHuEA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/interactions@3.27.1': - resolution: {integrity: sha512-M3wLpTTmDflI0QGNK0PJNUaBXXfeBXue8ZxLMngfc1piHNiH4G5lUvWd9W14XVbqrSCVY8i8DfGrNYpyyZu0tw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/label@3.7.25': - resolution: {integrity: sha512-oNK3Pqj4LDPwEbQaoM/uCip4QvQmmwGOh08VeW+vzSi6TAwf+KoWTyH/tiAeB0CHWNDK0k3e1iTygTAt4wzBmg==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/landmark@3.0.10': - resolution: {integrity: sha512-GpNjJaI8/a6WxYDZgzTCLYSzPM6xp2pxCIQ4udiGbTCtxx13Trmm0cPABvPtzELidgolCf05em9Phr+3G0eE8A==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/link@3.8.9': - resolution: {integrity: sha512-UaAFBfs84/Qq6TxlMWkREqqNY6SFLukot+z2Aa1kC+VyStv1kWG6sE5QLjm4SBn1Q3CGRsefhB/5+taaIbB4Pw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/listbox@3.15.3': - resolution: {integrity: sha512-C6YgiyrHS5sbS5UBdxGMhEs+EKJYotJgGVtl9l0ySXpBUXERiHJWLOyV7a8PwkUOmepbB4FaLD7Y9EUzGkrGlw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/live-announcer@3.4.4': - resolution: {integrity: sha512-PTTBIjNRnrdJOIRTDGNifY2d//kA7GUAwRFJNOEwSNG4FW+Bq9awqLiflw0JkpyB0VNIwou6lqKPHZVLsGWOXA==} - - '@react-aria/menu@3.21.0': - resolution: {integrity: sha512-CKTVZ4izSE1eKIti6TbTtzJAUo+WT8O4JC0XZCYDBpa0f++lD19Kz9aY+iY1buv5xGI20gAfpO474E9oEd4aQA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/meter@3.4.30': - resolution: {integrity: sha512-ZmANKW7s/Z4QGylHi46nhwtQ47T1bfMsU9MysBu7ViXXNJ03F4b6JXCJlKL5o2goQ3NbfZ68GeWamIT0BWSgtw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/numberfield@3.12.5': - resolution: {integrity: sha512-Fi41IUWXEHLFIeJ/LHuZ9Azs8J/P563fZi37GSBkIq5P1pNt1rPgJJng5CNn4KsHxwqadTRUlbbZwbZraWDtRg==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/overlays@3.31.2': - resolution: {integrity: sha512-78HYI08r6LvcfD34gyv19ArRIjy1qxOKuXl/jYnjLDyQzD4pVb634IQWcm0zt10RdKgyuH6HTqvuDOgZTLet7Q==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/progress@3.4.30': - resolution: {integrity: sha512-S6OWVGgluSWYSd/A6O8CVjz83eeMUfkuWSra0ewAV9bmxZ7TP9pUmD3bGdqHZEl97nt5vHGjZ3eq/x8eCmzKhA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/radio@3.12.5': - resolution: {integrity: sha512-8CCJKJzfozEiWBPO9QAATG1rBGJEJ+xoqvHf9LKU2sPFGsA2/SRnLs6LB9fCG5R3spvaK1xz0any1fjWPl7x8A==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/searchfield@3.8.12': - resolution: {integrity: sha512-kYlUHD/+mWzNroHoR8ojUxYBoMviRZn134WaKPFjfNUGZDOEuh4XzOoj+cjdJfe6N3mwTaYu6rJQtunSHIAfhA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/select@3.17.3': - resolution: {integrity: sha512-u0UFWw0S7q9oiSbjetDpRoLLIcC+L89uYlm+YfCrdT8ntbQgABNiJRxdVvxnhR0fR6MC9ASTTvuQnNHNn52+1A==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/selection@3.27.2': - resolution: {integrity: sha512-GbUSSLX/ciXix95KW1g+SLM9np7iXpIZrFDSXkC6oNx1uhy18eAcuTkeZE25+SY5USVUmEzjI3m/3JoSUcebbg==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/separator@3.4.16': - resolution: {integrity: sha512-RCUtQhDGnPxKzyG8KM79yOB0fSiEf8r/rxShidOVnGLiBW2KFmBa22/Gfc4jnqg/keN3dxvkSGoqmeXgctyp6g==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/slider@3.8.5': - resolution: {integrity: sha512-gqkJxznk141mE0JamXF5CXml9PDbPkBz8dyKlihtWHWX4yhEbVYdC9J0otE7iCR3zx69Bm7WHoTGL0BsdpKzVA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/spinbutton@3.7.2': - resolution: {integrity: sha512-adjE1wNCWlugvAtVXlXWPtIG9JWurEgYVn1Eeyh19x038+oXGvOsOAoKCXM+SnGleTWQ9J7pEZITFoEI3cVfAw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/ssr@3.9.10': - resolution: {integrity: sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==} - engines: {node: '>= 12'} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/switch@3.7.11': - resolution: {integrity: sha512-dYVX71HiepBsKyeMaQgHbhqI+MQ3MVoTd5EnTbUjefIBnmQZavYj1/e4NUiUI4Ix+/C0HxL8ibDAv4NlSW3eLQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/table@3.17.11': - resolution: {integrity: sha512-GkYmWPiW3OM+FUZxdS33teHXHXde7TjHuYgDDaG9phvg6cQTQjGilJozrzA3OfftTOq5VB8XcKTIQW3c0tpYsQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/tabs@3.11.1': - resolution: {integrity: sha512-3Ppz7yaEDW9L7p9PE9yNOl5caLwNnnLQqI+MX/dwbWlw9HluHS7uIjb21oswNl6UbSxAWyENOka45+KN4Fkh7A==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/tag@3.8.1': - resolution: {integrity: sha512-VonpO++F8afXGDWc9VUxAc2wefyJpp1n9OGpbnB7zmqWiuPwO/RixjUdcH7iJkiC4vADwx9uLnhyD6kcwGV2ig==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/textfield@3.18.5': - resolution: {integrity: sha512-ttwVSuwoV3RPaG2k2QzEXKeQNQ3mbdl/2yy6I4Tjrn1ZNkYHfVyJJ26AjenfSmj1kkTQoSAfZ8p+7rZp4n0xoQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/toast@3.0.11': - resolution: {integrity: sha512-2DjZjBAvm8/CWbnZ6s7LjkYCkULKtjMve6GvhPTq98AthuEDLEiBvM1wa3xdecCRhZyRT1g6DXqVca0EfZ9fJA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/toggle@3.12.5': - resolution: {integrity: sha512-XXVFLzcV8fr9mz7y/wfxEAhWvaBZ9jSfhCMuxH2bsivO7nTcMJ1jb4g2xJNwZgne17bMWNc7mKvW5dbsdlI6BA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/toolbar@3.0.0-beta.24': - resolution: {integrity: sha512-B2Rmpko7Ghi2RbNfsGdbR7I+RQBDhPGVE4bU3/EwHz+P/vNe5LyGPTeSwqaOMsQTF9lKNCkY8424dVTCr6RUMg==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/tooltip@3.9.2': - resolution: {integrity: sha512-VrgkPwHiEnAnBhoQ4W7kfry/RfVuRWrUPaJSp0+wKM6u0gg2tmn7OFRDXTxBAm/omQUguIdIjRWg7sf3zHH82A==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/tree@3.1.7': - resolution: {integrity: sha512-C54yH5NmsOFa2Q+cg6B1BPr5KUlU9vLIoBnVrgrH237FRSXQPIbcM4VpmITAHq1VR7w6ayyS1hgTwFxo67ykWQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/utils@3.33.1': - resolution: {integrity: sha512-kIx1Sj6bbAT0pdqCegHuPanR9zrLn5zMRiM7LN12rgRf55S19ptd9g3ncahArifYTRkfEU9VIn+q0HjfMqS9/w==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/visually-hidden@3.8.31': - resolution: {integrity: sha512-RTOHHa4n56a9A3criThqFHBifvZoV71+MCkSuNP2cKO662SUWjqKkd0tJt/mBRMEJPkys8K7Eirp6T8Wt5FFRA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-stately/calendar@3.9.3': - resolution: {integrity: sha512-uw7fCZXoypSBBUsVkbNvJMQWTihZReRbyLIGG3o/ZM630N3OCZhb/h4Uxke4pNu7n527H0V1bAnZgAldIzOYqg==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-stately/checkbox@3.7.5': - resolution: {integrity: sha512-K5R5ted7AxLB3sDkuVAazUdyRMraFT1imVqij2GuAiOUFvsZvbuocnDuFkBVKojyV3GpqLBvViV8IaCMc4hNIw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-stately/collections@3.12.10': - resolution: {integrity: sha512-wmF9VxJDyBujBuQ76vXj2g/+bnnj8fx5DdXgRmyfkkYhPB46+g2qnjbVGEvipo7bJuGxDftCUC4SN7l7xqUWfg==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-stately/color@3.9.5': - resolution: {integrity: sha512-8pZxzXWDRuglzDwyTG7mLw2LQMCHIVNbVc9YmbsxbOjAL+lOqszo60KzyaFKVxeDQczSvrNTHcQZqlbNIC0eyQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-stately/combobox@3.13.0': - resolution: {integrity: sha512-dX9g/cK1hjLRjcbWVF6keHxTQDGhKGB2QAgPhWcBmOK3qJv+2dQqsJ6YCGWn/Y2N2acoEseLrAA7+Qe4HWV9cg==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@oxlint/binding-android-arm-eabi@1.76.0': + resolution: {integrity: sha512-ZHIE5Zt9AsPDcY4nOlofXt0YfneEeo+QrKMPcPzLf2Z6Q8VtV2W73d7SFJ920WUwyik783u/doKCs3KXdwG+7w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] - '@react-stately/datepicker@3.16.1': - resolution: {integrity: sha512-BtAMDvxd1OZxkxjqq5tN5TYmp6Hm8+o3+IDA4qmem2/pfQfVbOZeWS2WitcPBImj4n4T+W1A5+PI7mT/6DUBVg==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@oxlint/binding-android-arm64@1.76.0': + resolution: {integrity: sha512-shm/ngQilHK6bs+ElJWa4oHfNj5vL1Gl/iVEJldTQjpr0/67oSgr0KUpbmcnLig5Fo0v/l6j2567A7TOL89ONA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] - '@react-stately/disclosure@3.0.11': - resolution: {integrity: sha512-/KjB/0HkxGWbhFAPztCP411LUKZCx9k8cKukrlGqrUWyvrcXlmza90j0g/CuxACBoV+DJP9V+4q+8ide0x750A==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@oxlint/binding-darwin-arm64@1.76.0': + resolution: {integrity: sha512-rvJmrAPKSQ9aWJ6wIS6CK2tJjwzfW0ApQH9qokq6sfDvmHwoyIHxHFMq7z7i7GiV6fdE6s8qvBqWKPTu8RmT6Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] - '@react-stately/dnd@3.7.4': - resolution: {integrity: sha512-YD0TVR5JkvTqskc1ouBpVKs6t/QS4RYCIyu8Ug8RgO122iIizuf2pfKnRLjYMdu5lXzBXGaIgd49dvnLzEXHIw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@oxlint/binding-darwin-x64@1.76.0': + resolution: {integrity: sha512-U/zYdb7VYKGY6pA9Vd2rYl9O/HlCylcOlb5PGPvVLtg+oLGsk6H3XGKEMHKyqD3nmmtmlmwb/8SwU2vfSAtvMw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] - '@react-stately/flags@3.1.2': - resolution: {integrity: sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg==} + '@oxlint/binding-freebsd-x64@1.76.0': + resolution: {integrity: sha512-WvKG9CAriuo0XNiFzpXjDngUZcRGFNpaK2kLyMUsnJlShxkT96u+BpJQ3KqdQwGOrvI14L6V8bAwXwAYNNY6Jg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] - '@react-stately/form@3.2.4': - resolution: {integrity: sha512-qNBzun8SbLdgahryhKLqL1eqP+MXY6as82sVXYOOvUYLzgU5uuN8mObxYlxJgMI5akSdQJQV3RzyfVobPRE7Kw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@oxlint/binding-linux-arm-gnueabihf@1.76.0': + resolution: {integrity: sha512-qJ5+RH99TqFRq3UCDxkW0zJJu9c+OAHFY72vGlxZLEpuO+MpKo3POgqb8sYipL9KYm8XY6ofb0HsOuvY6hQNqQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] - '@react-stately/grid@3.11.9': - resolution: {integrity: sha512-qQY6F+27iZRn30dt0ZOrSetUmbmNJ0pLe9Weuqw3+XDVSuWT+2O/rO1UUYeK+mO0Acjzdv+IWiYbu9RKf2wS9w==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@oxlint/binding-linux-arm-musleabihf@1.76.0': + resolution: {integrity: sha512-PvPCVptkgVARsucgIqFQQcSmJ6xc6GtnVB5bRBekRahTc9eObMtjHfMjy5M+C2tHt5UCMttWM9RuSk/H9NqYeg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] - '@react-stately/list@3.13.4': - resolution: {integrity: sha512-HHYSjA9VG7FPSAtpXAjQyM/V7qFHWGg88WmMrDt5QDlTBexwPuH0oFLnW0qaVZpAIxuWIsutZfxRAnme/NhhAA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@oxlint/binding-linux-arm64-gnu@1.76.0': + resolution: {integrity: sha512-3KeFDx8Bu4HPAXbuHZOr/oHvN+QT+JQhMw/NYPz7Z071xLSsG27Jfh9PIQVEY7hk1I+jr43ExqRIeJ6VKk2yLw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] - '@react-stately/menu@3.9.11': - resolution: {integrity: sha512-vYkpO9uV2OUecsIkrOc+Urdl/s1xw/ibNH/UXsp4PtjMnS6mK9q2kXZTM3WvMAKoh12iveUO+YkYCZQshmFLHQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@oxlint/binding-linux-arm64-musl@1.76.0': + resolution: {integrity: sha512-oPFkkKTgl0K/EIg9fQ8oA3IGcI05/Mq1en04iFa41mmNPT+6KEiByVazTOZZJiHMBBrbsns1YJ2e1Scqwzesjw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] - '@react-stately/numberfield@3.11.0': - resolution: {integrity: sha512-rxfC047vL0LP4tanjinfjKAriAvdVL57Um5RUL5nHML8IOWCB3TBxegQkJ6to6goScC/oZhd0/Y2LSaiRuKbNw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@oxlint/binding-linux-ppc64-gnu@1.76.0': + resolution: {integrity: sha512-gN7yZ0eqflA5Fhf1wvHxGUltIV3FsvmB1zhNMDEK9vSHhc7E6qg9CuPeBgPZab66Tjzq6w6kHAtNEvnTHf4cyw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] - '@react-stately/overlays@3.6.23': - resolution: {integrity: sha512-RzWxots9A6gAzQMP4s8hOAHV7SbJRTFSlQbb6ly1nkWQXacOSZSFNGsKOaS0eIatfNPlNnW4NIkgtGws5UYzfw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@oxlint/binding-linux-riscv64-gnu@1.76.0': + resolution: {integrity: sha512-S/HqMbn22mQrjtErUxEoS/a55u8kIeXvreIxiJu5G7Le3UecEd6SQZxrDIpuhtgaFnsY/nVra3ytP+pRljDilA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] - '@react-stately/radio@3.11.5': - resolution: {integrity: sha512-QxA779S4ea5icQ0ja7CeiNzY1cj7c9G9TN0m7maAIGiTSinZl2Ia8naZJ0XcbRRp+LBll7RFEdekne15TjvS/w==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@oxlint/binding-linux-riscv64-musl@1.76.0': + resolution: {integrity: sha512-ZIga3097VJZolGZk6SrIAUokIGfRkxRlhiHDUznZptGBfwrhD7pNfD1rzEzsCwvk/1DX0A1bLz+liuNh5QKIVQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] - '@react-stately/searchfield@3.5.19': - resolution: {integrity: sha512-URllgjbtTQEaOCfddbHpJSPKOzG3pE3ajQHJ7Df8qCoHTjKfL6hnm/vp7X5sxPaZaN7VLZ5kAQxTE8hpo6s0+A==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@oxlint/binding-linux-s390x-gnu@1.76.0': + resolution: {integrity: sha512-ZGiiA7pFzMJSyMWYZTVlPgbTsx+Vl8ihLGMIujPwaslUF7kIPPWAbVmAlTc+9lWDV+DCiB8Ikixu+lSHeOIIWQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] - '@react-stately/select@3.9.2': - resolution: {integrity: sha512-oWn0bijuusp8YI7FRM/wgtPVqiIrgU/ZUfLKe/qJUmT8D+JFaMAJnyrAzKpx98TrgamgtXynF78ccpopPhgrKQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@oxlint/binding-linux-x64-gnu@1.76.0': + resolution: {integrity: sha512-JLiy5WuvEBFTT6ErIFV35SLzi0R7Iri6MKU6dZbTxfIx8pndbbPs3Mj780nMipBFcPkti+okAPOJ9POKkHFEgg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] - '@react-stately/selection@3.20.9': - resolution: {integrity: sha512-RhxRR5Wovg9EVi3pq7gBPK2BoKmP59tOXDMh2r1PbnGevg/7TNdR67DCEblcmXwHuBNS46ELfKdd0XGHqmS8nQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@oxlint/binding-linux-x64-musl@1.76.0': + resolution: {integrity: sha512-z7lgKQtbo/I1NIe8G5NHLesxJDv0tRSUWTpXKb9Pm3E9nKFKfO4IOSDtFroKgXtOYb0jQbcdH+0wzTyMXVes+A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] - '@react-stately/slider@3.7.5': - resolution: {integrity: sha512-OrQMNR5xamLYH52TXtvTgyw3EMwv+JI+1istQgEj1CHBjC9eZZqn5iNCN20tzm+uDPTH0EIGULFjjPIumqYUQg==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@oxlint/binding-openharmony-arm64@1.76.0': + resolution: {integrity: sha512-JOjKymIpb9QcYfEhZsN6h4V9Ivd474W38cNIBRv6bg2TbIvogbMTH0Mg6YWW9TiRDqfcX+/Hyfsbo5vcSE5guQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] - '@react-stately/table@3.15.4': - resolution: {integrity: sha512-fGaNyw3wv7JgRCNzgyDzpaaTFuSy5f4Qekch4UheMXDJX7dOeaMhUXeOfvnXCVg+BGM4ey/D82RvDOGvPy1Nww==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@oxlint/binding-win32-arm64-msvc@1.76.0': + resolution: {integrity: sha512-pqDWZiwcmByWUEm1NFUBNiT6aentCcaoMWJv0HbXEmuYermJ4sg8ppVrshubYP2MZ6SHccJJcpr6x469PuDFIw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] - '@react-stately/tabs@3.8.9': - resolution: {integrity: sha512-AQ4Xrn6YzIolaVShCV9cnwOjBKPAOGP/PTp7wpSEtQbQ0HZzUDG2RG/M4baMeUB2jZ33b7ifXyPcK78o0uOftg==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@oxlint/binding-win32-ia32-msvc@1.76.0': + resolution: {integrity: sha512-Ba0O659kgMv6pwO3z9PdO+K3aMxQRaw9HnG+e6AtOfgwcKFvYilciQYBoUBmxfQvOCKZe1SwjMkuB542NkuDMQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] - '@react-stately/toast@3.1.3': - resolution: {integrity: sha512-mT9QJKmD523lqFpOp0VWZ6QHZENFK7HrodnNJDVc7g616s5GNmemdlkITV43fSY3tHeThCVvPu+Uzh7RvQ9mpQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@oxlint/binding-win32-x64-msvc@1.76.0': + resolution: {integrity: sha512-5qcirPHO8nKfkoowEVWtpAoVTcYDy6g0UT0NGic450Qv8J2NrOqg4uQ8QppRP4MDTC7Xx47lbZnmadTH03CGGA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] - '@react-stately/toggle@3.9.5': - resolution: {integrity: sha512-PVzXc788q3jH98Kvw1LYDL+wpVC14dCEKjOku8cSaqhEof6AJGaLR9yq+EF1yYSL2dxI6z8ghc0OozY8WrcFcA==} + '@phosphor-icons/react@2.1.10': + resolution: {integrity: sha512-vt8Tvq8GLjheAZZYa+YG/pW7HDbov8El/MANW8pOAz4eGxrwhnbfrQZq0Cp4q8zBEu8NIhHdnr+r8thnfRSNYA==} + engines: {node: '>=10'} peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react: '>= 16.8' + react-dom: '>= 16.8' - '@react-stately/tooltip@3.5.11': - resolution: {integrity: sha512-o8PnFXbvDCuVZ4Ht9ahfS6KHwIZjXopvoQ2vUPxv920irdgWEeC+4omgDOnJ/xFvcpmmJAmSsrQsTQrTguDUQA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} - '@react-stately/tree@3.9.6': - resolution: {integrity: sha512-JCuhGyX2A+PAMsx2pRSwArfqNFZJ9JSPkDaOQJS8MFPAsBe5HemvXsdmv9aBIMzlbCYcVq6EsrFnzbVVTBt/6w==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@playwright/test@1.62.1': + resolution: {integrity: sha512-DTcUc8qii+cpHvtOwggMtBRMjKZHXYWdw8syRYu2vtzuq4Wxphqq4NfCs5Zt44L6mA8rfDfj+PHnxFc/FeK6mQ==} + engines: {node: '>=20'} + hasBin: true - '@react-stately/utils@3.11.0': - resolution: {integrity: sha512-8LZpYowJ9eZmmYLpudbo/eclIRnbhWIJZ994ncmlKlouNzKohtM8qTC6B1w1pwUbiwGdUoyzLuQbeaIor5Dvcw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} - '@react-types/breadcrumbs@3.7.19': - resolution: {integrity: sha512-AnkyYYmzaM2QFi/N0P/kQLM8tHOyFi7p397B/jEMucXDfwMw5Ny1ObCXeIEqbh8KrIa2Xp8SxmQlCV+8FPs4LA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} - '@react-types/button@3.15.1': - resolution: {integrity: sha512-M1HtsKreJkigCnqceuIT22hDJBSStbPimnpmQmsl7SNyqCFY3+DHS7y/Sl3GvqCkzxF7j9UTL0dG38lGQ3K4xQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} - '@react-types/calendar@3.8.3': - resolution: {integrity: sha512-fpH6WNXotzH0TlKHXXxtjeLZ7ko0sbyHmwDAwmDFyP7T0Iwn1YQZ+lhceLifvynlxuOgX6oBItyUKmkHQ0FouQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@protobufjs/codegen@2.0.5': + resolution: {integrity: sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==} - '@react-types/checkbox@3.10.4': - resolution: {integrity: sha512-tYCG0Pd1usEz5hjvBEYcqcA0youx930Rss1QBIse9TgMekA1c2WmPDNupYV8phpO8Zuej3DL1WfBeXcgavK8aw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@protobufjs/eventemitter@1.1.1': + resolution: {integrity: sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==} - '@react-types/color@3.1.4': - resolution: {integrity: sha512-s+Xj4pvNBlJPpQ1Gr7bO1j4/tuwMUfdS9xIVFuiW5RvDsSybKTUJ/gqPzTxms94VDCRhLFocVn2STNdD2Erf6A==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@protobufjs/fetch@1.1.1': + resolution: {integrity: sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==} - '@react-types/combobox@3.14.0': - resolution: {integrity: sha512-zmSSS7BcCOD8rGT8eGbVy7UlL5qq1vm88fFn4WgFe+lfK33ne+E7yTzTxcPY2TCGSo5fY6xMj3OG79FfVNGbSg==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} - '@react-types/datepicker@3.13.5': - resolution: {integrity: sha512-j28Vz+xvbb4bj7+9Xbpc4WTvSitlBvt7YEaEGM/8ZQ5g4Jr85H2KwkmDwjzmMN2r6VMQMMYq9JEcemq5wWpfUQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} - '@react-types/dialog@3.5.24': - resolution: {integrity: sha512-NFurEP/zV0dA/41422lV1t+0oh6f/13n+VmLHZG8R13m1J3ql/kAXZ49zBSqkqANBO1ojyugWebk99IiR4pYOw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} - '@react-types/grid@3.3.8': - resolution: {integrity: sha512-zJvXH8gc1e1VH2H3LRnHH/W2HIkLkZMH3Cu5pLcj0vDuLBSWpcr3Ikh3jZ+VUOZF0G1Jt1lO8pKIaqFzDLNmLQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@protobufjs/utf8@1.1.2': + resolution: {integrity: sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==} - '@react-types/link@3.6.7': - resolution: {integrity: sha512-1apXCFJgMC1uydc2KNENrps1qR642FqDpwlNWe254UTpRZn/hEZhA6ImVr8WhomfLJu672WyWA0rUOv4HT+/pQ==} + '@react-leaflet/core@3.0.0': + resolution: {integrity: sha512-3EWmekh4Nz+pGcr+xjf0KNyYfC3U2JjnkWsh0zcqaexYqmmB5ZhH37kz41JXGmKzpaMZCnPofBBm64i+YrEvGQ==} peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + leaflet: ^1.9.0 + react: ^19.0.0 + react-dom: ^19.0.0 - '@react-types/listbox@3.7.6': - resolution: {integrity: sha512-335NYElKEByXMalAmeRPyulKIDd2cjOCQhLwvv2BtxO5zaJfZnBbhZs+XPd9zwU6YomyOxODKSHrwbNDx+Jf3w==} + '@react-types/shared@3.36.0': + resolution: {integrity: sha512-DkP/H0C2YjjS7gZWKNqOmU8a16qHPjQNdzMwmTq9SzplM6Iw0kVMTZ0OIoe6FOgGqa+FwMsE2QbPjh/n3g/jXQ==} peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - '@react-types/menu@3.10.7': - resolution: {integrity: sha512-+p7ixZdvPDJZhisqdtWiiuJ9pteNfK5i19NB6wzAw5XkljbEzodNhwLv6rI96DY5XpbFso2kcjw7IWi+rAAGGQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@rolldown/binding-android-arm64@1.2.1': + resolution: {integrity: sha512-02hOeOSryYxVrOIphmLAsqnCJWxwlzFk+pEt/N/i6OgT3lShHO7xGCU5cpgchRDHboAEbSjzgGh+O/u1GswQmA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] - '@react-types/meter@3.4.15': - resolution: {integrity: sha512-9WjNphhLLM+TA4Ev1y2MkpugJ5JjTXseHh7ZWWx2veq5DrXMZYclkRpfUrUdLVKvaBIPQCgpQIj0TcQi+quR9A==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@rolldown/binding-darwin-arm64@1.2.1': + resolution: {integrity: sha512-fMsTOnN0OjFm3CyppWPitKnc8UlliVARUULW6cfU6AIqjdtgmSFWSk9vecHzZduv/yMWIHDlRhM1e8Iff9uAfA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] - '@react-types/numberfield@3.8.18': - resolution: {integrity: sha512-nLzk7YAG9yAUtSv+9R8LgCHsu8hJq8/A+m1KsKxvc8WmNJjIujSFgWvT21MWBiUgPBzJKGzAqpMDDa087mltJQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@rolldown/binding-darwin-x64@1.2.1': + resolution: {integrity: sha512-1wjKdz/XLGKHaTNHjQveQ/B23TKx4ItAqm1JbyVuvNPc4Ze0Fb48s49TAd/2zcplPl8okE/UbTgmlVfwT7eFeQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] - '@react-types/overlays@3.9.4': - resolution: {integrity: sha512-7Z9HaebMFyYBqtv3XVNHEmVkm7AiYviV7gv0c98elEN2Co+eQcKFGvwBM9Gy/lV57zlTqFX1EX/SAqkMEbCLOA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@rolldown/binding-freebsd-x64@1.2.1': + resolution: {integrity: sha512-Fa0jHR07E7YBN4vOEsbVf2briYNsuOowfLJaXULZM0ldMlaCaj2LJgLMbMe4iacRyZmvR8efFhgR9wKuGclQUg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] - '@react-types/progress@3.5.18': - resolution: {integrity: sha512-mKeQn+KrHr1y0/k7KtrbeDGDaERH6i4f6yBwj/ZtYDCTNKMO3tPHJY6nzF0w/KKZLplIO+BjUbHXc2RVm8ovwQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@rolldown/binding-linux-arm-gnueabihf@1.2.1': + resolution: {integrity: sha512-pzkgu1SSHGgRRyRZ4fbmSgmajbVt+epaLP99NDjFft69v/ypfTi6swBMiVdh2EkQ0OSnHE1lZDM7DRGkyAzUpA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] - '@react-types/radio@3.9.4': - resolution: {integrity: sha512-TkMRY3sA1PcFZhhclu4IUzUTIir6MzNJj8h6WT8vO6Nug2kXJ72qigugVFBWJSE472mltduOErEAo0rtAYWbQA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@rolldown/binding-linux-arm64-gnu@1.2.1': + resolution: {integrity: sha512-QI5SEDY8cbiYWHx0VO4vIc3UlS6a32vXHjU8Qy/17adEmZIPuByJg13UEvo9c/UCiUkdcVWY83C+b+JrwnNyUg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] - '@react-types/searchfield@3.6.8': - resolution: {integrity: sha512-M2p7OVdMTMDmlBcHd4N2uCBwg3uJSNM4lmEyf09YD44N5wDAI0yogk52QBwsnhpe+i2s65UwCYgunB+QltRX8A==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@rolldown/binding-linux-arm64-musl@1.2.1': + resolution: {integrity: sha512-Sm41FyCeXqmYcERoYOCbGIL5hNfd8w9LQ7Y61Bev48HkcjaJqV/iiVOaiDxjVTRMS+QKrZmD8cfPt4uMVnvM+A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] - '@react-types/select@3.12.2': - resolution: {integrity: sha512-AseOjfr3qM1W1qIWcbAe6NFpwZluVeQX/dmu9BYxjcnVvtoBLPMbE5zX/BPbv+N5eFYjoMyj7Ug9dqnI+LrlGw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@rolldown/binding-linux-ppc64-gnu@1.2.1': + resolution: {integrity: sha512-2x+WhXTGl9yJYPbltW/BSEPTVz9OIWQyER4N+gJEDWkkn904eRcBzELqh/Hf7K0w/ubGbKNMv0ZC+94QK/IFEg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] - '@react-types/shared@3.33.1': - resolution: {integrity: sha512-oJHtjvLG43VjwemQDadlR5g/8VepK56B/xKO2XORPHt9zlW6IZs3tZrYlvH29BMvoqC7RtE7E5UjgbnbFtDGag==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@rolldown/binding-linux-s390x-gnu@1.2.1': + resolution: {integrity: sha512-eEjmQpuRQayHPWWnywaWHkFT3ToPbP3RYy42VVd/B9aBGDA+Ol25EIWHxKQST3IiWJjikCWUF7KtbfqwZrzVwQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] - '@react-types/slider@3.8.4': - resolution: {integrity: sha512-C+xFVvfKREai9S/ekBDCVaGPOQYkNUAsQhjQnNsUAATaox4I6IYLmcIgLmljpMQWqAe+gZiWsIwacRYMez2Tew==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@rolldown/binding-linux-x64-gnu@1.2.1': + resolution: {integrity: sha512-/Orga1fZYkLc/56jBICcHrKchl8Z2UKdDSr3LG9ToWO1lQ6a4Livk9Xz+9WN91zsz5QR3XQz2NNoSDEvP6qadw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] - '@react-types/switch@3.5.17': - resolution: {integrity: sha512-2GTPJvBCYI8YZ3oerHtXg+qikabIXCMJ6C2wcIJ5Xn0k9XOovowghfJi10OPB2GGyOiLBU74CczP5nx8adG90Q==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@rolldown/binding-linux-x64-musl@1.2.1': + resolution: {integrity: sha512-xxBJRL+0q0Kce7orznGWLuylHDY65vuARXZRpX+hPdv+DqK2c3NlCsVA98tlWzWNEE7yPqA/1NQ5nnCrj49Y5A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] - '@react-types/table@3.13.6': - resolution: {integrity: sha512-eluL+iFfnVmFm7OSZrrFG9AUjw+tcv898zbv+NsZACa8oXG1v9AimhZfd+Mo8q/5+sX/9hguWNXFkSvmTjuVPQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@rolldown/binding-openharmony-arm64@1.2.1': + resolution: {integrity: sha512-M6AdXIXw3s+/8XpKMzdGDEXGS1S7kwUsy+rcTIUIOx5Ge4nXKCtAFHFV9YKkXvGcC5WMoTjAteLzlsQROVI0Yw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] - '@react-types/tabs@3.3.22': - resolution: {integrity: sha512-HGwLD9dA3k3AGfRKGFBhNgxU9/LyRmxN0kxVj1ghA4L9S/qTOzS6GhrGNkGzsGxyVLV4JN8MLxjWN2o9QHnLEg==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@rolldown/binding-wasm32-wasi@1.2.1': + resolution: {integrity: sha512-/TX0SoRGojHzSAHpfVBbavRVSazg5U3h3Y3VXfcc0cdugq6kxdqw8LPGFiPr+/7gE/60zRcsOY2Vi9b9eT0jww==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} - '@react-types/textfield@3.12.8': - resolution: {integrity: sha512-wt6FcuE5AyntxsnPika/h3nf/DPmeAVbI018L9o6h+B/IL4sMWWdx663wx2KOOeHH8ejKGZQNPLhUKs4s1mVQA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@rolldown/binding-win32-arm64-msvc@1.2.1': + resolution: {integrity: sha512-EvRrivJieyHG+AO9lleZWgq+g0+S7oV2C51yuqlcyU/R9net+sI4Pj0F+lUoP2bEr6TWX3SqFaaS0SzfLxSzkw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] - '@react-types/tooltip@3.5.2': - resolution: {integrity: sha512-FvSuZ2WP08NEWefrpCdBYpEEZh/5TvqvGjq0wqGzWg2OPwpc14HjD8aE7I3MOuylXkD4MSlMjl7J4DlvlcCs3Q==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@rolldown/binding-win32-x64-msvc@1.2.1': + resolution: {integrity: sha512-Z4eCmn5QJ/5+azF9knpLWKfVd9aidn0mAe9TpJgvBLId9Ax3t0+JVxBmT25Bv7NBbVW1TZyKjQjQReouMeH5UQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] - '@remix-run/router@1.23.2': - resolution: {integrity: sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==} - engines: {node: '>=14.0.0'} + '@rolldown/plugin-node-polyfills@1.0.3': + resolution: {integrity: sha512-V7iWE6UI+InppK51NfuHlaCpep7Rzv4w92pZif6BtRP3ULDHfQjU6HNyIS41Plrx1tqzcANcAsnjDS6ye+1kFQ==} - '@rolldown/pluginutils@1.0.0-rc.3': - resolution: {integrity: sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==} + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} - '@rollup/plugin-babel@5.3.1': - resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==} - engines: {node: '>= 10.0.0'} + '@rollup/plugin-babel@6.1.0': + resolution: {integrity: sha512-dFZNuFD2YRcoomP4oYf+DvQNSUA9ih+A3vUqopQx5EdtPGo3WBnQcI/S8pwpz91UsGfL0HsMSOlaMld8HrbubA==} + engines: {node: '>=14.0.0'} peerDependencies: '@babel/core': ^7.0.0 '@types/babel__core': ^7.1.9 @@ -2401,6 +2067,8 @@ packages: peerDependenciesMeta: '@types/babel__core': optional: true + rollup: + optional: true '@rollup/plugin-inject@5.0.5': resolution: {integrity: sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==} @@ -2411,8 +2079,8 @@ packages: rollup: optional: true - '@rollup/plugin-node-resolve@15.3.1': - resolution: {integrity: sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==} + '@rollup/plugin-node-resolve@16.0.3': + resolution: {integrity: sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==} engines: {node: '>=14.0.0'} peerDependencies: rollup: '>=4.59.0' @@ -2420,13 +2088,8 @@ packages: rollup: optional: true - '@rollup/plugin-replace@2.4.2': - resolution: {integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==} - peerDependencies: - rollup: '>=4.59.0' - - '@rollup/plugin-terser@0.4.4': - resolution: {integrity: sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==} + '@rollup/plugin-replace@6.0.3': + resolution: {integrity: sha512-J4RZarRvQAm5IF0/LwUUg+obsm+xZhYnbMXmXROyoSE1ATJe3oXSb9L5MMppdxP2ylNSjv6zFBwKYjcKMucVfA==} engines: {node: '>=14.0.0'} peerDependencies: rollup: '>=4.59.0' @@ -2434,9 +2097,9 @@ packages: rollup: optional: true - '@rollup/plugin-virtual@3.0.2': - resolution: {integrity: sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A==} - engines: {node: '>=14.0.0'} + '@rollup/plugin-terser@1.0.0': + resolution: {integrity: sha512-FnCxhTBx6bMOYQrar6C8h3scPt8/JwIzw3+AJ2K++6guogH5fYaIFia+zZuhqv0eo1RN7W1Pz630SyvLbDjhtQ==} + engines: {node: '>=20.0.0'} peerDependencies: rollup: '>=4.59.0' peerDependenciesMeta: @@ -2452,14 +2115,8 @@ packages: rollup: optional: true - '@rollup/pluginutils@3.1.0': - resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} - engines: {node: '>= 8.0.0'} - peerDependencies: - rollup: '>=4.59.0' - - '@rollup/pluginutils@5.3.0': - resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} + '@rollup/pluginutils@5.4.0': + resolution: {integrity: sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==} engines: {node: '>=14.0.0'} peerDependencies: rollup: '>=4.59.0' @@ -2467,260 +2124,272 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.59.0': - resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} + '@rollup/rollup-android-arm-eabi@4.62.3': + resolution: {integrity: sha512-c0wdcekXtQvvn5Tsrk/+op/gUArrbWaFduBnTLP2l1cKLSQs4diMWjJw3m6A0DdzT8dAAX95KpkJ3qynCePbmw==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.59.0': - resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} + '@rollup/rollup-android-arm64@4.62.3': + resolution: {integrity: sha512-3YjElDdWN+qXAFbJ/CzPV+0wspLqh54k/I6GfdYtEJRqg7buSgc1yPM3B+93j1M4neobtkATHZTmxK2AMVGfnA==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.59.0': - resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} + '@rollup/rollup-darwin-arm64@4.62.3': + resolution: {integrity: sha512-Pch2pFNOxxz1hTjypIdPyRTR6riiwRl84+VcN9djS680fw+Co1nAJINrdpqp7KV0NvyuU8ilZXZCjd7ykJl1GQ==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.59.0': - resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} + '@rollup/rollup-darwin-x64@4.62.3': + resolution: {integrity: sha512-LEuncFUHFiF8t4yZVZvvZA1wk0pjAscRnsrn1EfTEmN4HXotBi2YtcnLRyaK6UbuczW7xZS5ES+81Rdz8Z0T6g==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.59.0': - resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} + '@rollup/rollup-freebsd-arm64@4.62.3': + resolution: {integrity: sha512-zvBUvsQUpOWALdDsk6qbS8bXf2VxmPisuudNDrY7x0p0jBdsoZl8HsHczIOgkQiZldmcacMKtBzpoGVNeIe2bQ==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.59.0': - resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} + '@rollup/rollup-freebsd-x64@4.62.3': + resolution: {integrity: sha512-C2KmNrcSem/AMg984H/dev+si0lieQGdXdR/lYGJnuumXnFb9Y7QdiI62obFdLlxRYLBv4P0eUVIDbD4c1vVvw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': - resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} + '@rollup/rollup-linux-arm-gnueabihf@4.62.3': + resolution: {integrity: sha512-ggXnsTAEzNQx74XpunRsiZ9aBZDsI7XIa0hm2nzR9f4WzH5/f/d73ZSDaC5ejJ8YLY4NW+V3wr0tjOaeCq8hqA==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.59.0': - resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} + '@rollup/rollup-linux-arm-musleabihf@4.62.3': + resolution: {integrity: sha512-2vng+FlzNUhKZxtej3IUqJgbZoQk2M/dwQM20+ULV0R/E/8tr9/P6uEf2iiGIk4HL0zMKh5Jry7mUHdUOvyGgA==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.59.0': - resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} + '@rollup/rollup-linux-arm64-gnu@4.62.3': + resolution: {integrity: sha512-LLLFZKt4/Nraf9rxDkhiU8QVgLF4WmCkfr0L4fj0fPfIZFBib0DeiFk1hhaYKd03LFAFJcxHslhDFlNJLylf5Q==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.59.0': - resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} + '@rollup/rollup-linux-arm64-musl@4.62.3': + resolution: {integrity: sha512-WJkdQCvS9sWNOUBJZfQRKpZGFBztRzcowI+nndmflKgU4XY+3a420FgTOSKTsVqJbnzSxeT4vaJalpOaPo2YCQ==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.59.0': - resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} + '@rollup/rollup-linux-loong64-gnu@4.62.3': + resolution: {integrity: sha512-PwHXCCS2n64/1Ot6rP1YEYA02MGYBcQlr8CSZZyrUG2O7NH6NklYmvr9v3Jy+5e/eDeNchc/ukmKJi9LuflMIQ==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.59.0': - resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} + '@rollup/rollup-linux-loong64-musl@4.62.3': + resolution: {integrity: sha512-vUjxINQu3RC8NZS3ykk1gN65gIz8pAopOq2HXuZhiIxHdx7TFvDG+jgrdSgInu1Eza4/Rfi2VzZgyIgEH4WOaw==} cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.59.0': - resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} + '@rollup/rollup-linux-ppc64-gnu@4.62.3': + resolution: {integrity: sha512-wzko4aJ13+0G3kGnviCg5gnXFKd40izKsrf2uOw12US4XqprkDrmwOpeW14aSNa37V8bfPcz5Fkob6LZ3BAPmA==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.59.0': - resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} + '@rollup/rollup-linux-ppc64-musl@4.62.3': + resolution: {integrity: sha512-8120ue0JUMSwy11stlwnfdX3pPd+WZYGCDBwEHWtIHi6pOpZmsEF5QKB7a/UN+XFdqvobxz98kv8RTqikyCEBw==} cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.59.0': - resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} + '@rollup/rollup-linux-riscv64-gnu@4.62.3': + resolution: {integrity: sha512-XLFHnR3tXMjbOCh2vtVJHmxt+995uJsTERQyseFDRA0xxMxyTZPLa3OIUlyFaO4mF/Lu0FjmWHCuPXJT1n/IOg==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.59.0': - resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} + '@rollup/rollup-linux-riscv64-musl@4.62.3': + resolution: {integrity: sha512-se6yXvNGMIl0f+RQzyh7XAmia8/9kplQx424wnG2w0C1oi6XgO6Y8otKhdXFHbHs88Ihavzmvh1NWjuovE76BQ==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.59.0': - resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} + '@rollup/rollup-linux-s390x-gnu@4.62.3': + resolution: {integrity: sha512-gNoxRefktVIiGflpONuxWWXZAzIQG++z9qHO3xKwk4WdDMuQja3JHGfE1u0i3PfPDyvhypdk+WrgIJqLhGG7sg==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.59.0': - resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} + '@rollup/rollup-linux-x64-gnu@4.62.3': + resolution: {integrity: sha512-V4KtWtQfAFMU7+9/A/VDps/VI8CHd3cYz0L8sgJzz8qK7eY7wI4ruFD82UYIYvW9Z4DtlTfhQcsl4XyPHW5uSg==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.59.0': - resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} + '@rollup/rollup-linux-x64-musl@4.62.3': + resolution: {integrity: sha512-LBx9LYXvj2CBkMkjLdNAWLwH0MLMin7do2VcVo9kVPibGLkY0BQQut2fv7NVqkXqZ/CrAu9LqDHVV1xHCMpCPw==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.59.0': - resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} + '@rollup/rollup-openbsd-x64@4.62.3': + resolution: {integrity: sha512-ABVf3Q0RCu7NcyCCOZQI0pJ3GuSdfSl8EXcy88QtdceIMIoCUdfhsJChZ64L9zVM2aJHjde1Bhn5uqSRcX9ySA==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.59.0': - resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} + '@rollup/rollup-openharmony-arm64@4.62.3': + resolution: {integrity: sha512-+2Cy/ldweGBLlPIKsQLF8U5N44a0KDdbrk1rAjHOM9M2K+kGdIVjHLmmrZIcx+9Ny3ke/1JomCsDI1ocb11+sg==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.59.0': - resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} + '@rollup/rollup-win32-arm64-msvc@4.62.3': + resolution: {integrity: sha512-dtZvzc8BedpSaFNy75x6uiWwAGTH+aZHDtdrqP6qk+WcLJrfti6sGje1ZJ9UxyzDLF23d/mV+PaMwuC0hL7UVA==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.59.0': - resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} + '@rollup/rollup-win32-ia32-msvc@4.62.3': + resolution: {integrity: sha512-Rj8Ra4noo+aYy7sKBggCx0407mws34kAb1ySyWuq5DAtFBQdkSwnsjCgPrhPe9cvgBKZIukpE+CVHvORCS93kQ==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.59.0': - resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} + '@rollup/rollup-win32-x64-gnu@4.62.3': + resolution: {integrity: sha512-vp7N084ew/odXn2gi/mzm9mUkQu9l6AiN6dt4IeUM2Uvm9o+cVmP+YkqbMOteLbiGgqBBlJZjIMYVCfOOIVbVQ==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.59.0': - resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} + '@rollup/rollup-win32-x64-msvc@4.62.3': + resolution: {integrity: sha512-MOG/3gTOn4Fwf574RVOaY61I5o6P90legkFADiTyn1hyjNydT+cerU2rLUwPdZkKKyJ+iT+K9p7WXK4LM1Ka6g==} cpu: [x64] os: [win32] - '@sableclient/sable-call-embedded@1.1.4': - resolution: {integrity: sha512-XLRcbUPcn7i3QKZAPjIfUkUEXP0E4DOr0dyRoVCWMjHWj28kq+T7jeB2fRr5lB77olBwNHMjIuoTwrv02xiepQ==} + '@sableclient/matrixrtc@0.1.0': + resolution: {integrity: sha512-2odY8NRY2QYFGdqnuPg3ix4f2crSqY45zH+h4RrLxPCRmUxWERkZY419b3NOeLfC6QN9dYzX8LFUW24+CgIwJQ==} + engines: {node: '>=20'} + peerDependencies: + livekit-client: ^2.21.0 + matrix-js-sdk: ^42.0.0 - '@sentry-internal/browser-utils@10.43.0': - resolution: {integrity: sha512-8zYTnzhAPvNkVH1Irs62wl0J/c+0QcJ62TonKnzpSFUUD3V5qz8YDZbjIDGfxy+1EB9fO0sxtddKCzwTHF/MbQ==} - engines: {node: '>=18'} + '@sableclient/sable-call-embedded@1.1.8': + resolution: {integrity: sha512-+95vxaqTxh5gIrIwMX+FWH4n+K82rv/hlVIO/KIhIl2udJ2+r/GESiAElnTXa1bom1+FWrdekNvkCEEuh7m2Jw==} - '@sentry-internal/feedback@10.43.0': - resolution: {integrity: sha512-YoXuwluP6eOcQxTeTtaWb090++MrLyWOVsUTejzUQQ6LFL13Jwt+bDPF1kvBugMq4a7OHw/UNKQfd6//rZMn2g==} - engines: {node: '>=18'} + '@sableclient/tauri-plugin-livekit-mobile@0.2.0': + resolution: {integrity: sha512-xpv+9qDEIYM1lmbdLpLgD8fwctyT7QNq8R7uwhm7T1Wt95MF7TqgtGUo8kHAUZq4v4ung0jxacbvkp9POMVutw==} - '@sentry-internal/replay-canvas@10.43.0': - resolution: {integrity: sha512-ZIw1UNKOFXo1LbPCJPMAx9xv7D8TMZQusLDUgb6BsPQJj0igAuwd7KRGTkjjgnrwBp2O/sxcQFRhQhknWk7QPg==} - engines: {node: '>=18'} + '@sableclient/tauri-plugin-notifications-api@0.5.2': + resolution: {integrity: sha512-sZPdNCFqWpImICdZ6ayCxMntmV3boDKRGBQrhpHNnZwwdKfYysER7Mge0vWwAp802cbAgUpWEQwrENfkPIl0ng==} - '@sentry-internal/replay@10.43.0': - resolution: {integrity: sha512-khCXlGrlH1IU7P5zCEAJFestMeH97zDVCekj8OsNNDtN/1BmCJ46k6Xi0EqAUzdJgrOLJeLdoYdgtiIjovZ8Sg==} - engines: {node: '>=18'} + '@sableclient/twemoji-font@1.0.4': + resolution: {integrity: sha512-LzvQB/VZtv5KEq1VYq2azr7v7cYT8oklOVRGkAN2RNwa3sF0XcnqM3lBHSYRFXQleGLSUgs3gA0slbwkD2+sJw==} - '@sentry/babel-plugin-component-annotate@5.1.1': - resolution: {integrity: sha512-x2wEpBHwsTyTF2rWsLKJlzrRF1TTIGOfX+ngdE+Yd5DBkoS58HwQv824QOviPGQRla4/ypISqAXzjdDPL/zalg==} - engines: {node: '>= 18'} + '@sentry/browser-utils@10.70.0': + resolution: {integrity: sha512-IvjhafF5NFXrPCg5EHbAPGDwIhHxgUcLlHTklZ3DAdA6ky88BJYMfevbcnOEmgavf4nylhCaquRUFNB5+szj+A==} + engines: {node: '>=18'} - '@sentry/browser@10.43.0': - resolution: {integrity: sha512-2V3I3sXi3SMeiZpKixd9ztokSgK27cmvsD9J5oyOyjhGLTW/6QKCwHbKnluMgQMXq20nixQk5zN4wRjRUma3sg==} + '@sentry/browser@10.70.0': + resolution: {integrity: sha512-IK6+J+8H06tZe+A8L37TT5ZxxwNtyQatW8zl5RYYJ/e9CsjrM8fPi8I1OT7uquTw8UtjqFHt7bEef/Vy63ksPg==} engines: {node: '>=18'} - '@sentry/bundler-plugin-core@5.1.1': - resolution: {integrity: sha512-F+itpwR9DyQR7gEkrXd2tigREPTvtF5lC8qu6e4anxXYRTui1+dVR0fXNwjpyAZMhIesLfXRN7WY7ggdj7hi0Q==} + '@sentry/bundler-plugins@10.69.0': + resolution: {integrity: sha512-I1otnSJIH4IOugLp+kcBbT0Kcex+J8xnHuUyzMAwCYSpZ0FMVvA723uNmkMdW0PxRRw0oEhe0qDB+t+ZjHxUiA==} engines: {node: '>= 18'} + peerDependencies: + rollup: '>=4.59.0' + webpack: '>=5.0.0' + peerDependenciesMeta: + rollup: + optional: true + webpack: + optional: true - '@sentry/cli-darwin@2.58.5': - resolution: {integrity: sha512-lYrNzenZFJftfwSya7gwrHGxtE+Kob/e1sr9lmHMFOd4utDlmq0XFDllmdZAMf21fxcPRI1GL28ejZ3bId01fQ==} + '@sentry/cli-darwin@2.58.6': + resolution: {integrity: sha512-udAVvcyfNa0R+95GvPz/+43/N3TC0TYKdkQ7D7jhPSzbcMc7l2fxRNN5yB3UpCA5fWFnW4toeaqwDBhb/Wh3LA==} engines: {node: '>=10'} os: [darwin] - '@sentry/cli-linux-arm64@2.58.5': - resolution: {integrity: sha512-/4gywFeBqRB6tR/iGMRAJ3HRqY6Z7Yp4l8ZCbl0TDLAfHNxu7schEw4tSnm2/Hh9eNMiOVy4z58uzAWlZXAYBQ==} + '@sentry/cli-linux-arm64@2.58.6': + resolution: {integrity: sha512-q8mEcNNmeXMy5i+jWT30TVpH7LcP4HD21CD5XRSPAd/a912HF6EpK0ybf/1USO14WOhoXbAGi9txwaWabSe33g==} engines: {node: '>=10'} cpu: [arm64] os: [linux, freebsd, android] - '@sentry/cli-linux-arm@2.58.5': - resolution: {integrity: sha512-KtHweSIomYL4WVDrBrYSYJricKAAzxUgX86kc6OnlikbyOhoK6Fy8Vs6vwd52P6dvWPjgrMpUYjW2M5pYXQDUw==} + '@sentry/cli-linux-arm@2.58.6': + resolution: {integrity: sha512-pD0LAt5PcUzAinBwvDqc66x9+2CabHEv486yP0gRjWO7SakbaxmfVq/EXd8VLq/Tzi39LAu422UYK1lpW3MILw==} engines: {node: '>=10'} cpu: [arm] os: [linux, freebsd, android] - '@sentry/cli-linux-i686@2.58.5': - resolution: {integrity: sha512-G7261dkmyxqlMdyvyP06b+RTIVzp1gZNgglj5UksxSouSUqRd/46W/2pQeOMPhloDYo9yLtCN2YFb3Mw4aUsWw==} + '@sentry/cli-linux-i686@2.58.6': + resolution: {integrity: sha512-q8vNJi1eOV/4vxAFWBsEwLHoSYapaZHIf4j76KJGJXFKTkEbsjCOOsKbwUIBTQQhRgV4DFWh3ryfsPS/que4Kg==} engines: {node: '>=10'} cpu: [x86, ia32] os: [linux, freebsd, android] - '@sentry/cli-linux-x64@2.58.5': - resolution: {integrity: sha512-rP04494RSmt86xChkQ+ecBNRYSPbyXc4u0IA7R7N1pSLCyO74e5w5Al+LnAq35cMfVbZgz5Sm0iGLjyiUu4I1g==} + '@sentry/cli-linux-x64@2.58.6': + resolution: {integrity: sha512-DZu956Mhi3ZRjTBe1WdbGV46ldVbA8d2rgp/fh51GsI25zjBHah4wZnPTSzpc+YqxU6pJpg579B/r3jrIK530Q==} engines: {node: '>=10'} cpu: [x64] os: [linux, freebsd, android] - '@sentry/cli-win32-arm64@2.58.5': - resolution: {integrity: sha512-AOJ2nCXlQL1KBaCzv38m3i2VmSHNurUpm7xVKd6yAHX+ZoVBI8VT0EgvwmtJR2TY2N2hNCC7UrgRmdUsQ152bA==} + '@sentry/cli-win32-arm64@2.58.6': + resolution: {integrity: sha512-nj0Ff/kmAB73EPDhR8B4O9r+NUHK5GkPCkGWC+kXVemqAJWL5jcJ5KdxG0l/S0z6RoEoltID8/43/B+TaMlT7A==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@sentry/cli-win32-i686@2.58.5': - resolution: {integrity: sha512-EsuboLSOnlrN7MMPJ1eFvfMDm+BnzOaSWl8eYhNo8W/BIrmNgpRUdBwnWn9Q2UOjJj5ZopukmsiMYtU/D7ml9g==} + '@sentry/cli-win32-i686@2.58.6': + resolution: {integrity: sha512-WNZiDzPbgsEMQWq4avsQ391v/xWKJDIWWWo9GYl+N/w5qcYKkoDW7wQG7T9FasI6ENn68phChTOAPXXxbfAdOg==} engines: {node: '>=10'} cpu: [x86, ia32] os: [win32] - '@sentry/cli-win32-x64@2.58.5': - resolution: {integrity: sha512-IZf+XIMiQwj+5NzqbOQfywlOitmCV424Vtf9c+ep61AaVScUFD1TSrQbOcJJv5xGxhlxNOMNgMeZhdexdzrKZg==} + '@sentry/cli-win32-x64@2.58.6': + resolution: {integrity: sha512-R35WJ17oF4D2eqI1DR2sQQqr0fjRTt5xoP16WrTu91XM2lndRMFsnjh+/GttbxapLCBNlrjzia99MJ0PZHZpgA==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@sentry/cli@2.58.5': - resolution: {integrity: sha512-tavJ7yGUZV+z3Ct2/ZB6mg339i08sAk6HDkgqmSRuQEu2iLS5sl9HIvuXfM6xjv8fwlgFOSy++WNABNAcGHUbg==} + '@sentry/cli@2.58.6': + resolution: {integrity: sha512-baBcNPLLfUi9WuL+Tpri9BFaAdvugZIKelC5X0tt0Zdy+K0K+PCVSrnNmwMWU/HyaF/SEv6b6UHnXIdqanBlcg==} engines: {node: '>= 10'} hasBin: true - '@sentry/core@10.43.0': - resolution: {integrity: sha512-l0SszQAPiQGWl/ferw8GP3ALyHXiGiRKJaOvNmhGO+PrTQyZTZ6OYyPnGijAFRg58dE1V3RCH/zw5d2xSUIiNg==} + '@sentry/conventions@0.16.0': + resolution: {integrity: sha512-fO9PLmHdVURcSPUpWCItWAtgKiMwGdJHbovoSEyLplX5sxs2ugvI4CBPTrkkgqhObnZOD0CnWBKDzSVQYBKEyQ==} + engines: {node: '>=14'} + + '@sentry/core@10.69.0': + resolution: {integrity: sha512-+uuqVEeiDzYuAKjZLqsROKXvRTbl/QeH0gfGRtpYib1cud4rAFWRIkFmcR7Jb7JGFYwmReyQotiTj/hcDszTZg==} engines: {node: '>=18'} - '@sentry/react@10.43.0': - resolution: {integrity: sha512-shvErEpJ41i0Q3lIZl0CDWYQ7m8yHLi7ECG0gFvN8zf8pEdl5grQIOoe3t/GIUzcpCcor16F148ATmKJJypc/Q==} + '@sentry/core@10.70.0': + resolution: {integrity: sha512-ozhCTDqg89oB4XmWfAwuHshABpvT7AkRpaPnogopPfMAaI61G1t8EKCJ4W7aum8JSBonlfyjPCyW5oYZFm0KvA==} engines: {node: '>=18'} - peerDependencies: - react: ^16.14.0 || 17.x || 18.x || 19.x - '@sentry/rollup-plugin@5.1.1': - resolution: {integrity: sha512-1d5NkdRR6aKWBP7czkY8sFFWiKnfmfRpQOj+m9bJTsyTjbMiEQJst6315w5pCVlRItPhBqpAraqAhutZFgvyVg==} - engines: {node: '>= 18'} + '@sentry/feedback@10.70.0': + resolution: {integrity: sha512-6VQn2ETJjHkk4QQDdx/587/JDfXsk3yBTLZ3UZOMtBVrkmwgk+1FJZ8ULJ3ud1xZcuh2icunIkc7tGVv2axdnw==} + engines: {node: '>=18'} + + '@sentry/react@10.70.0': + resolution: {integrity: sha512-j1d/4hvoaUVKs5GRrfmwUCkiEmkfaeHsk+xgausZlzg5YvmwpF+gyevBLNP26GFEH7nyHXW4l8dbbo0eNieJmw==} + engines: {node: '>=18'} peerDependencies: - rollup: '>=4.59.0' + react: ^16.14.0 || 17.x || 18.x || 19.x - '@sentry/vite-plugin@5.1.1': - resolution: {integrity: sha512-i6NWUDi2SDikfSUeMJvJTRdwEKYSfTd+mvBO2Ja51S1YK+hnickBuDfD+RvPerIXLuyRu3GamgNPbNqgCGUg/Q==} - engines: {node: '>= 18'} + '@sentry/replay-canvas@10.70.0': + resolution: {integrity: sha512-irzpw22bK5CF3jbecDa0gBUcfjv7tgeUoLAvtIfeHOP5ajmf3o4Cp99a9RQqkfgvkcMeRZBUwE91SQhp6Ank+w==} + engines: {node: '>=18'} - '@sindresorhus/is@7.2.0': - resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==} + '@sentry/replay@10.70.0': + resolution: {integrity: sha512-xMnSGzJn9Xd29rYd32lkx/gFW+5mtgqADJ2FiZvis0MBGZuDlNRwPn0/Cs1xA3JNXKV3NGfhdmmvs90w4dHSmw==} engines: {node: '>=18'} - '@speed-highlight/core@1.2.14': - resolution: {integrity: sha512-G4ewlBNhUtlLvrJTb88d2mdy2KRijzs4UhnlrOSRT4bmjh/IqNElZa3zkrZ+TC47TwtlDWzVLFADljF1Ijp5hA==} + '@sentry/vite-plugin@5.4.0': + resolution: {integrity: sha512-fFJgCxs5hDyAm9BbZJ+LbA+LK2tjX5OoD0v0ARU4StR6KQmGUduoPs69yJ9AfqZ0om3Rlp5JDliiwFcNkasORA==} + engines: {node: '>= 18'} '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@surma/rollup-plugin-off-main-thread@2.2.3': - resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==} - '@svgr/babel-plugin-add-jsx-attribute@8.0.0': resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} engines: {node: '>=14'} @@ -2789,124 +2458,153 @@ packages: peerDependencies: '@svgr/core': '*' - '@swc/core-darwin-arm64@1.15.18': - resolution: {integrity: sha512-+mIv7uBuSaywN3C9LNuWaX1jJJ3SKfiJuE6Lr3bd+/1Iv8oMU7oLBjYMluX1UrEPzwN2qCdY6Io0yVicABoCwQ==} - engines: {node: '>=10'} + '@swc/helpers@0.5.23': + resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==} + + '@tanstack/query-core@5.101.4': + resolution: {integrity: sha512-gNwcvOJcRbLWPOLG/2OBm+zM+Yv+MKsXKEOWC57USuZDEsI71hEErQsiEGx5wX9rzWWkfwM0fVSPoiIFSsxfiw==} + + '@tanstack/query-devtools@5.101.4': + resolution: {integrity: sha512-z5IPHnDX3aUWeTWlRKLyooBQekaCAw4xRpZqPQ390RiWTDBcTynjpPT221BArw0u2+pnQMdGvPQI9YNNubBcmA==} + + '@tanstack/react-query-devtools@5.101.4': + resolution: {integrity: sha512-VeK2gtmfj7kvRBjtxS7TKxt/6qKhn8VzabY4UiYMr7NV9CddjSRYRgeYyld+NpjAkgMV9dd+2Qdr8ah5I03NeA==} + peerDependencies: + '@tanstack/react-query': ^5.101.4 + react: ^18 || ^19 + + '@tanstack/react-query@5.101.4': + resolution: {integrity: sha512-yRg2pfOCxIs4ZJW3XYYHU/WgtD04FHSnfHlpRT7h7pR77hwkdRG4wxbKe4aq6P0RvXUTBSQpQeadS1SUYUe+KA==} + peerDependencies: + react: ^18 || ^19 + + '@tanstack/react-virtual@3.14.9': + resolution: {integrity: sha512-qZyr0FZDP8rDC4WBhsryIZmAd9bveJvFGUJJtskWaew6/0dTRS6wZxnR6VQ5bY2KwL3LjerrHqQLk3a0GKcPXQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@tanstack/virtual-core@3.17.7': + resolution: {integrity: sha512-bp+v10y65sp2H7WpWfIMyxTNfl8ZVfxFTLRjPIFRryi6FV/J33z4IS53WO4pTk36KlvJ4iLiQz+oaydDC1xbcA==} + + '@tauri-apps/api@2.11.1': + resolution: {integrity: sha512-M2FPuYND2m+wh5hfW9ZpSdxMPdEJovPBWwoHJmwUpysTYNHaOkVFN419m/K0LIgjb/7KU2vBgsUepJWugQCvAA==} + + '@tauri-apps/cli-darwin-arm64@2.11.4': + resolution: {integrity: sha512-1ryOF3ZhpZ/nemHV5zVwBQBz9jDGKmKPvWPADOhc83ig0P4bMc2iER4NbC6r9sjeIZ6RVQ4g3RZIYvezhcl4TQ==} + engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.15.18': - resolution: {integrity: sha512-wZle0eaQhnzxWX5V/2kEOI6Z9vl/lTFEC6V4EWcn+5pDjhemCpQv9e/TDJ0GIoiClX8EDWRvuZwh+Z3dhL1NAg==} - engines: {node: '>=10'} + '@tauri-apps/cli-darwin-x64@2.11.4': + resolution: {integrity: sha512-uFsGQAAfuyz1k/yGLmkWfkBlgKAqZfxqlHmLWx81QU27RJWfmbNHCIq8T8w1e+VClleIuZUjpHWfoE4E3DLo3A==} + engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.15.18': - resolution: {integrity: sha512-ao61HGXVqrJFHAcPtF4/DegmwEkVCo4HApnotLU8ognfmU8x589z7+tcf3hU+qBiU1WOXV5fQX6W9Nzs6hjxDw==} - engines: {node: '>=10'} + '@tauri-apps/cli-linux-arm-gnueabihf@2.11.4': + resolution: {integrity: sha512-IaHZn5CdBL21oUmjiVOS1ctw6Ip1O0pjp70FwOWmYz1myWe0SY96ZIj2FYf7pT0m8bI2h/hrs5ZbEXXh44/MkQ==} + engines: {node: '>= 10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.15.18': - resolution: {integrity: sha512-3xnctOBLIq3kj8PxOCgPrGjBLP/kNOddr6f5gukYt/1IZxsITQaU9TDyjeX6jG+FiCIHjCuWuffsyQDL5Ew1bg==} - engines: {node: '>=10'} + '@tauri-apps/cli-linux-arm64-gnu@2.11.4': + resolution: {integrity: sha512-N41/ukTRVe6XSuUTESuFdGeOW2i7k62tK+6gHK5Kd5/q5RPvvi19GaWAVPPb9u95HSGmTChSolBfzynUsssFaA==} + engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [glibc] - '@swc/core-linux-arm64-musl@1.15.18': - resolution: {integrity: sha512-0a+Lix+FSSHBSBOA0XznCcHo5/1nA6oLLjcnocvzXeqtdjnPb+SvchItHI+lfeiuj1sClYPDvPMLSLyXFaiIKw==} - engines: {node: '>=10'} + '@tauri-apps/cli-linux-arm64-musl@2.11.4': + resolution: {integrity: sha512-v277UnT/fB64xAfSroL5N3Km3tLmvATWqJJw/wRI+g6o+HkeD0slyE7gOhNs1MbjE41R7bQOTxMVoL3aomUJmw==} + engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [musl] - '@swc/core-linux-x64-gnu@1.15.18': - resolution: {integrity: sha512-wG9J8vReUlpaHz4KOD/5UE1AUgirimU4UFT9oZmupUDEofxJKYb1mTA/DrMj0s78bkBiNI+7Fo2EgPuvOJfuAA==} - engines: {node: '>=10'} + '@tauri-apps/cli-linux-riscv64-gnu@2.11.4': + resolution: {integrity: sha512-qqgNkQ2u1yZHxjhxsZaxUtRDW8dIqIYm33rx/mzwQv0SfY9x1B+iraj8vWeFiXjjSVVhEMepXSOts1TqPzvXNQ==} + engines: {node: '>= 10'} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@tauri-apps/cli-linux-x64-gnu@2.11.4': + resolution: {integrity: sha512-2VRNWl84FOH0m2giiDkO2h0QXlcMJeX+zJDpI5kDIQAx6s+geF3v48F4DXfJez4GS/FdoDGnPnw1C2iYGbQ7bQ==} + engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [glibc] - '@swc/core-linux-x64-musl@1.15.18': - resolution: {integrity: sha512-4nwbVvCphKzicwNWRmvD5iBaZj8JYsRGa4xOxJmOyHlMDpsvvJ2OR2cODlvWyGFH6BYL1MfIAK3qph3hp0Az6g==} - engines: {node: '>=10'} + '@tauri-apps/cli-linux-x64-musl@2.11.4': + resolution: {integrity: sha512-o9GyhYor/nc7xarmwDE3ka2szuW3uuZzXjHWh64Q8YX5AtSgxdQkFWzrY4O8KiGtVNvFBI14H3Q49Qj5TOIP/A==} + engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [musl] - '@swc/core-win32-arm64-msvc@1.15.18': - resolution: {integrity: sha512-zk0RYO+LjiBCat2RTMHzAWaMky0cra9loH4oRrLKLLNuL+jarxKLFDA8xTZWEkCPLjUTwlRN7d28eDLLMgtUcQ==} - engines: {node: '>=10'} + '@tauri-apps/cli-win32-arm64-msvc@2.11.4': + resolution: {integrity: sha512-ld5Ehb598m0VkYyylRPNeCFsBe/km0jxis6KgMpl3IGY6I/i1RwQXO05I1AsXUXO2WC6AvB/Lw4qTf/asiuEiQ==} + engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.15.18': - resolution: {integrity: sha512-yVuTrZ0RccD5+PEkpcLOBAuPbYBXS6rslENvIXfvJGXSdX5QGi1ehC4BjAMl5FkKLiam4kJECUI0l7Hq7T1vwg==} - engines: {node: '>=10'} + '@tauri-apps/cli-win32-ia32-msvc@2.11.4': + resolution: {integrity: sha512-12Hxi0XX/H5VFxO/bGgHkFWhml9VMgEOu9CidjeCeTNQ1l6fpUlbiGgSP7CLI3PFtW9/FfbeHieZ+kyWK5H7CA==} + engines: {node: '>= 10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.15.18': - resolution: {integrity: sha512-7NRmE4hmUQNCbYU3Hn9Tz57mK9Qq4c97ZS+YlamlK6qG9Fb5g/BB3gPDe0iLlJkns/sYv2VWSkm8c3NmbEGjbg==} - engines: {node: '>=10'} + '@tauri-apps/cli-win32-x64-msvc@2.11.4': + resolution: {integrity: sha512-+vDiqBIU5dMISg/wNvX3sF+ZHfgJGJ5T0AcO+EHNXV9GGAG+P5fzodlDXD3QdKCRgZxMoCm5PPvj3BqLNjBthw==} + engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@swc/core@1.15.18': - resolution: {integrity: sha512-z87aF9GphWp//fnkRsqvtY+inMVPgYW3zSlXH1kJFvRT5H/wiAn+G32qW5l3oEk63KSF1x3Ov0BfHCObAmT8RA==} - engines: {node: '>=10'} - peerDependencies: - '@swc/helpers': '>=0.5.17' - peerDependenciesMeta: - '@swc/helpers': - optional: true + '@tauri-apps/cli@2.11.4': + resolution: {integrity: sha512-R8xGtMpwyetawSqm9kYOuMmEqkhUbvcUy8n0aNXIxollKBLESUu5f4Fx+64hgASYm1H+jSWq6jCW6zqTnH6hqQ==} + engines: {node: '>= 10'} + hasBin: true - '@swc/counter@0.1.3': - resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + '@tauri-apps/plugin-clipboard-manager@2.3.2': + resolution: {integrity: sha512-CUlb5Hqi2oZbcZf4VUyUH53XWPPdtpw43EUpCza5HWZJwxEoDowFzNUDt1tRUXA8Uq+XPn17Ysfptip33sG4eQ==} - '@swc/helpers@0.5.19': - resolution: {integrity: sha512-QamiFeIK3txNjgUTNppE6MiG3p7TdninpZu0E0PbqVh1a9FNLT2FRhisaa4NcaX52XVhA5l7Pk58Ft7Sqi/2sA==} + '@tauri-apps/plugin-deep-link@2.4.9': + resolution: {integrity: sha512-u0SKOUHnJ1wqeqXsDFq2+kASCBj9xxbG0g9XZWPy9SOmU4wXtp6b/wiYpm6oH6/5fBTQsLqnLhIvqLBRpgHJlA==} - '@swc/types@0.1.25': - resolution: {integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==} + '@tauri-apps/plugin-dialog@2.7.2': + resolution: {integrity: sha512-pX0IGm1I3I6wc+zeKYcq1GSqogK6okCNX5fOdaNU5ab1AjGS6l1E5wFNjEb7meg7ZFSp0JUs+0jQGQNyOvLrsg==} - '@swc/wasm@1.15.18': - resolution: {integrity: sha512-zeSORFArxqUwfVMTRHu8AN9k9LlfSn0CKDSzLhJDITpgLoS0xpnocxsgMjQjUcVYDgO47r9zLP49HEjH/iGsFg==} + '@tauri-apps/plugin-fs@2.5.1': + resolution: {integrity: sha512-9Lz+Jopp6QyeEWhlpkMx4R/+P9HgR+AVAI4vOZhlT8Xaymtz8iVI/Ov984/XTqgJz/5gz5NretqPB/XEMS3NhQ==} - '@tanstack/query-core@5.90.20': - resolution: {integrity: sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==} + '@tauri-apps/plugin-http@2.5.9': + resolution: {integrity: sha512-lCiY0+vs4HvIUSvZrBs8TC3TiCB0MOPRmiUjTq4prW7SlcJE2jdLeT6KBsJrT9Tlplufl7W1pY6SFAO3gCWxDA==} - '@tanstack/query-devtools@5.93.0': - resolution: {integrity: sha512-+kpsx1NQnOFTZsw6HAFCW3HkKg0+2cepGtAWXjiiSOJJ1CtQpt72EE2nyZb+AjAbLRPoeRmPJ8MtQd8r8gsPdg==} + '@tauri-apps/plugin-opener@2.5.4': + resolution: {integrity: sha512-1HnPkb+AmgO29HBazm4uPLKB+r7zzcTBW1d0fyYp1uP+jwtpoiNDGKMMzz58SFp49nOIrxdE3aUJtT57lfO9CQ==} - '@tanstack/react-query-devtools@5.91.3': - resolution: {integrity: sha512-nlahjMtd/J1h7IzOOfqeyDh5LNfG0eULwlltPEonYy0QL+nqrBB+nyzJfULV+moL7sZyxc2sHdNJki+vLA9BSA==} - peerDependencies: - '@tanstack/react-query': ^5.90.20 - react: ^18 || ^19 + '@tauri-apps/plugin-os@2.3.2': + resolution: {integrity: sha512-n+nXWeuSeF9wcEsSPmRnBEGrRgOy6jjkSU+UVCOV8YUGKb2erhDOxis7IqRXiRVHhY8XMKks00BJ0OAdkpf6+A==} - '@tanstack/react-query@5.90.21': - resolution: {integrity: sha512-0Lu6y5t+tvlTJMTO7oh5NSpJfpg/5D41LlThfepTixPYkJ0sE2Jj0m0f6yYqujBwIXlId87e234+MxG3D3g7kg==} - peerDependencies: - react: ^18 || ^19 + '@tauri-apps/plugin-process@2.3.1': + resolution: {integrity: sha512-nCa4fGVaDL/B9ai03VyPOjfAHRHSBz5v6F/ObsB73r/dA3MHHhZtldaDMIc0V/pnUw9ehzr2iEG+XkSEyC0JJA==} - '@tanstack/react-virtual@3.13.21': - resolution: {integrity: sha512-SYXFrmrbPgXBvf+HsOsKhFgqSe4M6B29VHOsX9Jih9TlNkNkDWx0hWMiMLUghMEzyUz772ndzdEeCEBx+3GIZw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + '@tauri-apps/plugin-store@2.4.4': + resolution: {integrity: sha512-oxSMaj/QpVfJcBMYX5aOQV94fWvga0MwQMfD6TLlbK2dh+ShPWAzefd8HWXhvOKjPRJdGVAkW7ZGO76JzzjaDA==} - '@tanstack/virtual-core@3.13.21': - resolution: {integrity: sha512-ww+fmLHyCbPSf7JNbWZP3g7wl6SdNo3ah5Aiw+0e9FDErkVHLKprYUrwTm7dF646FtEkN/KkAKPYezxpmvOjxw==} + '@tauri-apps/plugin-updater@2.10.1': + resolution: {integrity: sha512-NFYMg+tWOZPJdzE/PpFj2qfqwAWwNS3kXrb1tm1gnBJ9mYzZ4WDRrwy8udzWoAnfGCHLuePNLY1WVCNHnh3eRA==} '@testing-library/dom@10.4.1': resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} engines: {node: '>=18'} - '@testing-library/jest-dom@6.9.1': - resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} - engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + '@testing-library/jest-dom@7.0.0': + resolution: {integrity: sha512-HKAH9C6mBo5yBG6yRO5i43L2iisencAo5z+o5P/saHUoY+miC5ivXRxHBJcFyB5ypPNxHJdK3BoF/3O4DIptMg==} + engines: {node: '>=22', npm: '>=6', yarn: '>=1'} + peerDependencies: + '@testing-library/dom': '>=10 <11' '@testing-library/react@16.3.2': resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==} @@ -2923,30 +2621,22 @@ packages: '@types/react-dom': optional: true - '@testing-library/user-event@14.6.1': - resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==} + '@testing-library/user-event@14.6.5': + resolution: {integrity: sha512-FhqjldLTpteueBaKflhNFlMT3+PM0O5fiBUivht6b9CZ1eesJyy7+g3Jr7XwJzt/Hip3ZG5hWwK1MX1FuDiE4w==} engines: {node: '>=12', npm: '>=6'} peerDependencies: '@testing-library/dom': '>=7.21.4' - '@tybys/wasm-util@0.10.1': - resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + '@trickfilm400/rollup-plugin-off-main-thread@3.0.0-pre1': + resolution: {integrity: sha512-/67zpWDBLV+oYAEL682s1ktXL0HgqX76f6gaVGkGnVZlBbm1zd0v4Bz8MFF2GGhoX9rvfq3KSQHubFHwa6w6/Q==} + engines: {node: '>=12'} + + '@tybys/wasm-util@0.10.3': + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} - '@types/babel__core@7.20.5': - resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} - - '@types/babel__generator@7.27.0': - resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} - - '@types/babel__template@7.4.4': - resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - - '@types/babel__traverse@7.28.0': - resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} - '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} @@ -2956,11 +2646,17 @@ packages: '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - '@types/estree@0.0.39': - resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==} + '@types/docker-modem@3.0.6': + resolution: {integrity: sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==} - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/dockerode@4.0.1': + resolution: {integrity: sha512-cmUpB+dPN955PxBEuXE3f6lKO1hHiIGYJA46IVF3BJpNsZGvtBDcRnlrHYHtOH/B6vtDOyl2kZ2ShAu3mgc27Q==} + + '@types/dom-mediacapture-record@1.0.22': + resolution: {integrity: sha512-mUMZLK3NvwRLcAAT9qmcK+9p7tpU2FHdDsntR3YI4+GY88XrgG4XiE7u1Q2LAN2/FZOz/tdMDC3GQCR4T8nFuw==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} '@types/events@3.0.3': resolution: {integrity: sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==} @@ -2968,34 +2664,166 @@ packages: '@types/file-saver@2.0.7': resolution: {integrity: sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==} + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + '@types/is-hotkey@0.1.10': resolution: {integrity: sha512-RvC8KMw5BCac1NvRRyaHgMMEtBaZ6wh0pyPTBu7izn4Sj/AX9Y4aXU5c7rX8PnM/knsuUpC1IeoBkANtxBypsQ==} - '@types/node@24.10.13': - resolution: {integrity: sha512-oH72nZRfDv9lADUBSo104Aq7gPHpQZc4BTx38r9xf9pg5LfP6EzSyH2n7qFmmxRQXh7YlUXODcYsg6PuTDSxGg==} + '@types/leaflet@1.9.22': + resolution: {integrity: sha512-h3lhECYEKDasG7LFHu+GiHqAvsgLuQvlJvVZzJDGONo3sEL+wUOqSFLnwkZlK0qVxnxbuGFW8iBlJNYs5wgndA==} + + '@types/node@18.19.130': + resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} - '@types/prop-types@15.7.15': - resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} + '@types/node@24.13.2': + resolution: {integrity: sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==} - '@types/react-dom@18.3.7': - resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==} + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: - '@types/react': ^18.0.0 + '@types/react': ^19.2.0 '@types/react-google-recaptcha@2.1.9': resolution: {integrity: sha512-nT31LrBDuoSZJN4QuwtQSF3O89FVHC4jLhM+NtKEmVF5R1e8OY0Jo4//x2Yapn2aNHguwgX5doAq8Zo+Ehd0ug==} - '@types/react@18.3.28': - resolution: {integrity: sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==} + '@types/react@19.2.17': + resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==} '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + '@types/ssh2-streams@0.1.13': + resolution: {integrity: sha512-faHyY3brO9oLEA0QlcO8N2wT7R0+1sHWZvQ+y3rMLwdY1ZyS1z0W3t65j9PqT4HmQ6ALzNe7RZlNuCNE0wBSWA==} + + '@types/ssh2@0.5.52': + resolution: {integrity: sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg==} + + '@types/ssh2@1.15.5': + resolution: {integrity: sha512-N1ASjp/nXH3ovBHddRJpli4ozpk6UdDYIX4RJWFa9L1YKnzdhTlVmiGHm4DZnj/jLbqZpes4aeR30EFGQtvhQQ==} + '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} - '@types/ua-parser-js@0.7.39': - resolution: {integrity: sha512-P/oDfpofrdtF5xw433SPALpdSchtJmY7nsJItf8h3KXqOslkbySh8zq4dSWXH2oTjRvJ5PczVEoCZPow6GicLg==} + '@typescript/typescript-aix-ppc64@7.0.2': + resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==} + engines: {node: '>=16.20.0'} + cpu: [ppc64] + os: [aix] + + '@typescript/typescript-darwin-arm64@7.0.2': + resolution: {integrity: sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [darwin] + + '@typescript/typescript-darwin-x64@7.0.2': + resolution: {integrity: sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [darwin] + + '@typescript/typescript-freebsd-arm64@7.0.2': + resolution: {integrity: sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [freebsd] + + '@typescript/typescript-freebsd-x64@7.0.2': + resolution: {integrity: sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [freebsd] + + '@typescript/typescript-linux-arm64@7.0.2': + resolution: {integrity: sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [linux] + + '@typescript/typescript-linux-arm@7.0.2': + resolution: {integrity: sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==} + engines: {node: '>=16.20.0'} + cpu: [arm] + os: [linux] + + '@typescript/typescript-linux-loong64@7.0.2': + resolution: {integrity: sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==} + engines: {node: '>=16.20.0'} + cpu: [loong64] + os: [linux] + + '@typescript/typescript-linux-mips64el@7.0.2': + resolution: {integrity: sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==} + engines: {node: '>=16.20.0'} + cpu: [mips64el] + os: [linux] + + '@typescript/typescript-linux-ppc64@7.0.2': + resolution: {integrity: sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==} + engines: {node: '>=16.20.0'} + cpu: [ppc64] + os: [linux] + + '@typescript/typescript-linux-riscv64@7.0.2': + resolution: {integrity: sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==} + engines: {node: '>=16.20.0'} + cpu: [riscv64] + os: [linux] + + '@typescript/typescript-linux-s390x@7.0.2': + resolution: {integrity: sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==} + engines: {node: '>=16.20.0'} + cpu: [s390x] + os: [linux] + + '@typescript/typescript-linux-x64@7.0.2': + resolution: {integrity: sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [linux] + + '@typescript/typescript-netbsd-arm64@7.0.2': + resolution: {integrity: sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [netbsd] + + '@typescript/typescript-netbsd-x64@7.0.2': + resolution: {integrity: sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [netbsd] + + '@typescript/typescript-openbsd-arm64@7.0.2': + resolution: {integrity: sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [openbsd] + + '@typescript/typescript-openbsd-x64@7.0.2': + resolution: {integrity: sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [openbsd] + + '@typescript/typescript-sunos-x64@7.0.2': + resolution: {integrity: sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [sunos] + + '@typescript/typescript-win32-arm64@7.0.2': + resolution: {integrity: sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [win32] + + '@typescript/typescript-win32-x64@7.0.2': + resolution: {integrity: sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [win32] '@use-gesture/core@10.3.1': resolution: {integrity: sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==} @@ -3008,14 +2836,14 @@ packages: '@vanilla-extract/babel-plugin-debug-ids@1.2.2': resolution: {integrity: sha512-MeDWGICAF9zA/OZLOKwhoRlsUW+fiMwnfuOAqFVohL31Agj7Q/RBWAYweqjHLgFBCsdnr6XIfwjJnmb2znEWxw==} - '@vanilla-extract/compiler@0.3.4': - resolution: {integrity: sha512-W9HXf9EAccpE1vEIATvSoBVj/bQnmHfYHfDJjUN8dcOHW6oMcnoGTqweDM9I66BHqlNH4d0IsaeZKSViOv7K4w==} + '@vanilla-extract/compiler@0.7.2': + resolution: {integrity: sha512-VslwMJ01taDhJmMo0+A0SWn2dloMnYi0+BbvLuXXlqK4ufVRwXsq/U/Fp8ANkw3jevmQXy+JL7VtIQdjEtylDQ==} - '@vanilla-extract/css@1.18.0': - resolution: {integrity: sha512-/p0dwOjr0o8gE5BRQ5O9P0u/2DjUd6Zfga2JGmE4KaY7ZITWMszTzk4x4CPlM5cKkRr2ZGzbE6XkuPNfp9shSQ==} + '@vanilla-extract/css@1.21.2': + resolution: {integrity: sha512-ehF/tmv2MxQwOJB1DicALUqnjZTnmY9Y7J2ccKB578JzZpYeL6sLAUqbaXo1taw1Erp0qBq0I4Kejs0uU/pBfg==} - '@vanilla-extract/integration@8.0.7': - resolution: {integrity: sha512-ILob4F9cEHXpbWAVt3Y2iaQJpqYq/c/5TJC8Fz58C2XmX3QW2Y589krvViiyJhQfydCGK3EbwPQhVFjQaBeKfg==} + '@vanilla-extract/integration@8.0.10': + resolution: {integrity: sha512-01IB5gbrgTe8IIrtfRXXTmACl5D8Enzqp2cKbCWaMKXmnoilXXVCPbJoA96q88PXkNDXsXepCxUugMvEmL3c7A==} '@vanilla-extract/private@1.0.9': resolution: {integrity: sha512-gT2jbfZuaaCLrAxwXbRgIhGhcXbRZCG3v4TTUnjw0EJ7ArdBRxkq4msNJkbuRkCgfIK5ATmprB5t9ljvLeFDEA==} @@ -3025,62 +2853,73 @@ packages: peerDependencies: '@vanilla-extract/css': ^1.0.0 - '@vanilla-extract/vite-plugin@5.1.4': - resolution: {integrity: sha512-fTYNKUK3n4ApkUf2FEcO7mpqNKEHf9kDGg8DXlkqHtPxgwPhjuaajmDfQCSBsNgnA2SLI+CB5EO6kLQuKsw2Rw==} + '@vanilla-extract/vite-plugin@5.2.6': + resolution: {integrity: sha512-mGk0YEan+NyeoxEO51LfxOdtw2JBI+Yb840FGg+zvG6tVU3YfDgKGPiGuk5oT8jqzfsm7H89DyjOVCpl6Bnn0w==} peerDependencies: - vite: ^5.0.0 || ^6.0.0 || ^7.0.0 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 - '@vitejs/plugin-react@5.1.4': - resolution: {integrity: sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA==} + '@vitejs/plugin-react@6.0.5': + resolution: {integrity: sha512-BOVzne/NL162sMdResB25mUv+vWMF5NoAjNf09TeGlE7ZpszZWSD3winycicLJw72yeVsoCn/2kOhEuCvEShMA==} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: - vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0 + babel-plugin-react-compiler: ^1.0.0 + vite: ^8.0.0 + peerDependenciesMeta: + '@rolldown/plugin-babel': + optional: true + babel-plugin-react-compiler: + optional: true - '@vitest/coverage-v8@4.1.0': - resolution: {integrity: sha512-nDWulKeik2bL2Va/Wl4x7DLuTKAXa906iRFooIRPR+huHkcvp9QDkPQ2RJdmjOFrqOqvNfoSQLF68deE3xC3CQ==} + '@vitest/coverage-v8@4.1.10': + resolution: {integrity: sha512-IM49HmthevbgAO4anp1hwtoT9wYe59w0LR00gr+eagHE+ZJ5lK4sLPeO0ubgoJcwLk6dehU3R24N+FbEEKDc8g==} peerDependencies: - '@vitest/browser': 4.1.0 - vitest: 4.1.0 + '@vitest/browser': 4.1.10 + vitest: 4.1.10 peerDependenciesMeta: '@vitest/browser': optional: true - '@vitest/expect@4.1.0': - resolution: {integrity: sha512-EIxG7k4wlWweuCLG9Y5InKFwpMEOyrMb6ZJ1ihYu02LVj/bzUwn2VMU+13PinsjRW75XnITeFrQBMH5+dLvCDA==} + '@vitest/expect@4.1.10': + resolution: {integrity: sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==} - '@vitest/mocker@4.1.0': - resolution: {integrity: sha512-evxREh+Hork43+Y4IOhTo+h5lGmVRyjqI739Rz4RlUPqwrkFFDF6EMvOOYjTx4E8Tl6gyCLRL8Mu7Ry12a13Tw==} + '@vitest/mocker@4.1.10': + resolution: {integrity: sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==} peerDependencies: msw: ^2.4.9 - vite: ^6.0.0 || ^7.0.0 || ^8.0.0-0 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: msw: optional: true vite: optional: true - '@vitest/pretty-format@4.1.0': - resolution: {integrity: sha512-3RZLZlh88Ib0J7NQTRATfc/3ZPOnSUn2uDBUoGNn5T36+bALixmzphN26OUD3LRXWkJu4H0s5vvUeqBiw+kS0A==} + '@vitest/pretty-format@4.1.10': + resolution: {integrity: sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==} - '@vitest/runner@4.1.0': - resolution: {integrity: sha512-Duvx2OzQ7d6OjchL+trw+aSrb9idh7pnNfxrklo14p3zmNL4qPCDeIJAK+eBKYjkIwG96Bc6vYuxhqDXQOWpoQ==} + '@vitest/runner@4.1.10': + resolution: {integrity: sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==} - '@vitest/snapshot@4.1.0': - resolution: {integrity: sha512-0Vy9euT1kgsnj1CHttwi9i9o+4rRLEaPRSOJ5gyv579GJkNpgJK+B4HSv/rAWixx2wdAFci1X4CEPjiu2bXIMg==} + '@vitest/snapshot@4.1.10': + resolution: {integrity: sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==} - '@vitest/spy@4.1.0': - resolution: {integrity: sha512-pz77k+PgNpyMDv2FV6qmk5ZVau6c3R8HC8v342T2xlFxQKTrSeYw9waIJG8KgV9fFwAtTu4ceRzMivPTH6wSxw==} + '@vitest/spy@4.1.10': + resolution: {integrity: sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==} - '@vitest/ui@4.1.0': - resolution: {integrity: sha512-sTSDtVM1GOevRGsCNhp1mBUHKo9Qlc55+HCreFT4fe99AHxl1QQNXSL3uj4Pkjh5yEuWZIx8E2tVC94nnBZECQ==} + '@vitest/ui@4.1.10': + resolution: {integrity: sha512-EOUqfXHTXtpSHsyLHH40ts3Ue+hRhSGwzwzMlK0dTEOLSDYyOXLyr5JDGmHQWhN2DYI30gw6dVx3cdgM9FZl+Q==} peerDependencies: - vitest: 4.1.0 + vitest: 4.1.10 + + '@vitest/utils@4.1.10': + resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==} - '@vitest/utils@4.1.0': - resolution: {integrity: sha512-XfPXT6a8TZY3dcGY8EdwsBulFCIw+BeeX0RZn2x/BtiY/75YGh8FeWGG8QISN/WhaqSrE2OrlDgtF8q5uhOTmw==} + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} - acorn@8.16.0: - resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + acorn@8.18.0: + resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} engines: {node: '>=0.4.0'} hasBin: true @@ -3088,8 +2927,8 @@ packages: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} - ajv@8.18.0: - resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} another-json@0.2.0: resolution: {integrity: sha512-/Ndrl68UQLhnCdsAzEXLMFuOR546o2qbYRqCglaNHbjXrwG1ayTcdwr3zkSGOGtGXDyR5X9nCFfnyG2AFJIsqg==} @@ -3098,17 +2937,41 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + ansi-styles@5.2.0: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + archiver-utils@5.0.2: + resolution: {integrity: sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==} + engines: {node: '>= 14'} + + archiver@7.0.1: + resolution: {integrity: sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==} + engines: {node: '>= 14'} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + aria-hidden@1.2.6: + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} + engines: {node: '>=10'} + aria-query@5.3.0: resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} @@ -3124,17 +2987,23 @@ packages: resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} engines: {node: '>= 0.4'} + asn1@0.2.6: + resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - ast-v8-to-istanbul@1.0.0: - resolution: {integrity: sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==} + ast-v8-to-istanbul@1.0.5: + resolution: {integrity: sha512-UPAgKJFSEGMWSDr3LX4tqnAb4f7KGT8O40Tyx8wbYmmZ/yn58lNCm8h3svs3eXgiGd5AXxz8NDOvXWvicq+rJA==} async-function@1.0.0: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} + async-lock@1.4.1: + resolution: {integrity: sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==} + async@3.2.6: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} @@ -3150,6 +3019,14 @@ packages: resolution: {integrity: sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g==} engines: {node: '>=6.0.0'} + b4a@1.8.1: + resolution: {integrity: sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==} + peerDependencies: + react-native-b4a: '*' + peerDependenciesMeta: + react-native-b4a: + optional: true + babel-plugin-polyfill-corejs2@0.4.17: resolution: {integrity: sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==} peerDependencies: @@ -3172,17 +3049,57 @@ packages: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} + bare-events@2.9.1: + resolution: {integrity: sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==} + peerDependencies: + bare-abort-controller: '*' + peerDependenciesMeta: + bare-abort-controller: + optional: true + + bare-fs@4.7.4: + resolution: {integrity: sha512-y1kC+ffIx/tPLdTE693uNjHfzTfr+ravR5tvWlMXe25nELbkqV400S71qHDwbkAQ1FVEZobB1NFRzFbCCcyBCQ==} + engines: {bare: '>=1.16.0'} + peerDependencies: + bare-buffer: '*' + peerDependenciesMeta: + bare-buffer: + optional: true + + bare-path@3.1.1: + resolution: {integrity: sha512-JprUlveX3QjApC1cTpsUOiscADftCGVWkzitbHsRqv84hzYwYHw2mbluddsq5TvI8mH/8Ov1f4BiMAdcB0oYnQ==} + + bare-stream@2.13.3: + resolution: {integrity: sha512-Kc+brLqvEqGkjyfiwJmImAOqLZL7OsoLKuavx+hJjgVV3nLTOjloJyPMFxjUPerGGHrNH0fLU06jjykMLWrERQ==} + peerDependencies: + bare-abort-controller: '*' + bare-buffer: '*' + bare-events: '*' + peerDependenciesMeta: + bare-abort-controller: + optional: true + bare-buffer: + optional: true + bare-events: + optional: true + + bare-url@2.4.6: + resolution: {integrity: sha512-iQxPClE07hETVpbRoX7JXX3v/ZQViCxe/SYCxylRLzdEx1xJAufPptfiOqR8tqiCtmbtMDANKWszzjLu1PMAZQ==} + base-x@5.0.1: resolution: {integrity: sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg==} base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.10.0: - resolution: {integrity: sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==} + baseline-browser-mapping@2.11.8: + resolution: {integrity: sha512-zAgkquC2WYF0PIc6XbNYkA2uuxxFavzgmX61R+dHDUa558V8Ejf8ozTZFR6QzM24RWu4kBcRkhJ5kpz77j9fnQ==} engines: {node: '>=6.0.0'} hasBin: true + bcrypt-pbkdf@1.0.2: + resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} + bidi-js@1.0.3: resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} @@ -3193,15 +3110,15 @@ packages: bind-event-listener@3.0.0: resolution: {integrity: sha512-PJvH288AWQhKs2v9zyfYdPzlPqf5bXbGMmhmUIY9x4dAUGIWgomO771oBQNwJnMQSnUIXhKu6sgzpBRXTlvb8Q==} - blake3-wasm@2.1.5: - resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} blurhash@2.0.5: resolution: {integrity: sha512-cRygWd7kGBQO3VEhPiTgq4Wc43ctsM+o46urrmPOiuAe+07fzlSB9OJVdpgDL0jPqXUVQ9ht7aq7kxOeJHRK+w==} - brace-expansion@5.0.4: - resolution: {integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==} - engines: {node: 18 || 20 || >=22} + brace-expansion@5.0.9: + resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} + engines: {node: 20 || >=22} braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} @@ -3210,30 +3127,45 @@ packages: browser-encrypt-attachment@0.3.0: resolution: {integrity: sha512-L7siI766UCH6+arP9yT5wpA5AFxnmGbKiGSsxEVACl1tE0pvDJeQvMmbY2UmJiuffrr0ZJ2+U6Om46wQBqh1Lw==} - browserslist@4.28.1: - resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + browserslist@4.28.7: + resolution: {integrity: sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true bs58@6.0.0: resolution: {integrity: sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==} + buffer-crc32@1.0.0: + resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==} + engines: {node: '>=8.0.0'} + buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - cac@6.7.14: - resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} - engines: {node: '>=8'} + buildcheck@0.0.7: + resolution: {integrity: sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA==} + engines: {node: '>=10.0.0'} + + byline@5.0.0: + resolution: {integrity: sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==} + engines: {node: '>=0.10.0'} + + cac@7.0.0: + resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} + engines: {node: '>=20.19.0'} call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} - call-bind@1.0.8: - resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + call-bind@1.0.9: + resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==} engines: {node: '>= 0.4'} call-bound@1.0.4: @@ -3248,8 +3180,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001777: - resolution: {integrity: sha512-tmN+fJxroPndC74efCdp12j+0rk0RHwV5Jwa1zWaFVyw2ZxAuPeG8ZgWC3Wz7uSjT3qMRQ5XHZ4COgQmsCMJAQ==} + caniuse-lite@1.0.30001806: + resolution: {integrity: sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==} chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} @@ -3259,47 +3191,68 @@ packages: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} + chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + chroma-js@3.2.0: resolution: {integrity: sha512-os/OippSlX1RlWWr+QDPcGUZs0uoqr32urfxESG9U93lhUfbnlyckte84Q8P1UQY/qth983AS1JONKmLS4T0nw==} classnames@2.5.1: resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} - cloudflared@0.7.1: - resolution: {integrity: sha512-jJn1Gu9Tf4qnIu8tfiHZ25Hs8rNcRYSVf8zAd97wvYdOCzftm1CTs1S/RPhijjGi8gUT1p9yzfDi9zYlU/0RwA==} + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + cloudflared@0.7.3: + resolution: {integrity: sha512-dR4QMS4PbD287QqOV/vAoE1BcQCXphLOVrN9vjNUgkLrPDA5qxn9Dz0VId0P7/Czj4Mxu3hoLgbrVx5OtOWr5Q==} hasBin: true clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + common-tags@1.8.2: resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} engines: {node: '>=4.0.0'} - compute-scroll-into-view@3.1.1: - resolution: {integrity: sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==} + compress-commons@6.0.2: + resolution: {integrity: sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==} + engines: {node: '>= 14'} confbox@0.1.8: resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} - content-type@1.0.5: - resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} - engines: {node: '>= 0.6'} + content-type@2.0.0: + resolution: {integrity: sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==} + engines: {node: '>=18'} convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - cookie@1.1.1: - resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} - engines: {node: '>=18'} + cookie-es@3.1.1: + resolution: {integrity: sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==} core-js-compat@3.49.0: resolution: {integrity: sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==} + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cosmiconfig@8.3.6: resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} engines: {node: '>=14'} @@ -3309,8 +3262,22 @@ packages: typescript: optional: true - cross-fetch@4.0.0: - resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==} + cpu-features@0.0.10: + resolution: {integrity: sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==} + engines: {node: '>=10.0.0'} + + crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + + crc32-stream@6.0.0: + resolution: {integrity: sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==} + engines: {node: '>= 14'} + + create-web-stream@1.1.3: + resolution: {integrity: sha512-ocdWcsi6F8/8f0L/Fsbt3UmPSB+7H5hXR0aRr7Wiu7TXC1Z8vwlwxpeO4+gMgJ8g4TqwM5oTnSid43Jxm2ArwA==} + engines: {node: '>=18.9.0'} cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} @@ -3331,11 +3298,6 @@ packages: css.escape@1.5.1: resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} - cssesc@3.0.0: - resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} - engines: {node: '>=4'} - hasBin: true - csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} @@ -3355,8 +3317,8 @@ packages: resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} engines: {node: '>= 0.4'} - dayjs@1.11.19: - resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==} + dayjs@1.11.21: + resolution: {integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==} debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} @@ -3397,13 +3359,24 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + detect-europe-js@0.1.2: + resolution: {integrity: sha512-lgdERlL3u0aUdHocoouzT10d9I89VVhk0qNRmll7mXdGfJT1/wqZ2ZLA4oJAjeACPY5fT1wsbq2AT+GkuInsow==} + detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} - direction@1.0.4: - resolution: {integrity: sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ==} - hasBin: true + docker-compose@1.4.2: + resolution: {integrity: sha512-rPHigTKGaEHpkUmfd69QgaOp+Os5vGJwG/Ry8lcr8W/382AmI+z/D7qoa9BybKIkqNppaIbs8RYeHSevdQjWww==} + engines: {node: '>= 6.0.0'} + + docker-modem@5.0.7: + resolution: {integrity: sha512-XJgGhoR/CLpqshm4d3L7rzH6t8NgDFUIIpztYlLHIApeJjMZKYJMz2zxPsYxnejq5h3ELYSw/RBsi3t5h7gNTA==} + engines: {node: '>= 8.0'} + + dockerode@5.0.1: + resolution: {integrity: sha512-avsq/xk4YPIrn0CgleX5bjT9Y8IT1p9PxrNQ++RBQ2WEyFfHCTDsT9kmyxz+H/axnjAwg8wJWEIuPGOUuNupiA==} + engines: {node: '>= 14.17'} dom-accessibility-api@0.5.16: resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} @@ -3411,69 +3384,82 @@ packages: dom-accessibility-api@0.6.3: resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} - dom-serializer@2.0.0: - resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + dom-serializer@3.1.1: + resolution: {integrity: sha512-4MEa38/QexBob6gFNwu+EGdWvhJ1OKuNwdYY3Y3NyeWDQfnGeDYQUDfIRzWu5B5gsv03so2Uxd28YC6zrsx3Lw==} + engines: {node: '>=20.19.0'} - domelementtype@2.3.0: - resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + domelementtype@3.0.0: + resolution: {integrity: sha512-umCQid3jKbDmVjx8jGaW7uUykm4DEUeyV21hPxNMo2nV955DhUThwqyOIDtreepP31hl84X7G5U9ZfsWvIB3Pg==} + engines: {node: '>=20.19.0'} - domhandler@5.0.3: - resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} - engines: {node: '>= 4'} + domhandler@6.0.1: + resolution: {integrity: sha512-gYzvtM72ZtxQO0T048kd6HWSbbGCNOUwcnfQ01cqIJ4X2IYKFFHZ5mKvrQETcFXxsRObZulDaKmy//R7TPtsBg==} + engines: {node: '>=20.19.0'} - dompurify@3.3.3: - resolution: {integrity: sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==} + dompurify@3.4.13: + resolution: {integrity: sha512-2vmYIoqjze2d+kakP8S/nS5shfsl587kzwEjcGlTdiksUVgFHnFCsLYDVj/JNqJVOQZGSYBTmuycv0PodwmnMQ==} - domutils@3.2.2: - resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + domutils@4.0.2: + resolution: {integrity: sha512-qI4JLRKnSzqFqr7hAlS5xQDusBCjKSEG4t4+7aNrIQMHBcsC2TGEhuyABJdYkgSewL57PNLYEiibY2iPKhKpaA==} + engines: {node: '>=20.19.0'} dot-case@3.0.4: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} - dotenv@16.6.1: - resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + dotenv@17.4.2: + resolution: {integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==} engines: {node: '>=12'} dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + ejs@3.1.10: resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} engines: {node: '>=0.10.0'} hasBin: true - electron-to-chromium@1.5.307: - resolution: {integrity: sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg==} + electron-to-chromium@1.5.398: + resolution: {integrity: sha512-AsvhAxopJGh6museTDMIjn6JpDYOfgu4RLlygomt87MUwBUqTfd/1EiPtx10/LZE8xpTvkP2E9Gafq7lkLtodQ==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - emojibase-data@15.3.2: - resolution: {integrity: sha512-TpDyTDDTdqWIJixV5sTA6OQ0P0JfIIeK2tFRR3q56G9LK65ylAZ7z3KyBXokpvTTJ+mLUXQXbLNyVkjvnTLE+A==} + emojibase-data@17.0.0: + resolution: {integrity: sha512-Yvgb5AWoHViHV/gq1qr5ZAarcBip+B27/ZLRsUJkbgAEaLlZ/fof9g882LTpmEpyhBNEC0m2SEmItljHsTygjA==} peerDependencies: emojibase: '*' - emojibase@15.3.1: - resolution: {integrity: sha512-GNsjHnG2J3Ktg684Fs/vZR/6XpOSkZPMAv85EHrr6br2RN2cJNwdS4am/3YSK3y+/gOv2kmoK3GGdahXdMxg2g==} + emojibase@17.0.0: + resolution: {integrity: sha512-bXdpf4HPY3p41zK5swVKZdC/VynsMZ4LoLxdYDE+GucqkFwzcM1GVc4ODfYAlwoKaf2U2oNNUoOO78N96ovpBA==} + engines: {node: '>=18.12.0'} + + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} - entities@6.0.1: - resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} - engines: {node: '>=0.12'} - - entities@7.0.1: - resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} - engines: {node: '>=0.12'} + entities@8.0.0: + resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} + engines: {node: '>=20.19.0'} error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} - error-stack-parser-es@1.0.5: - resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} + es-abstract-get@1.0.0: + resolution: {integrity: sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==} + engines: {node: '>= 0.4'} - es-abstract@1.24.1: - resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} + es-abstract@1.24.2: + resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==} engines: {node: '>= 0.4'} es-define-property@1.0.1: @@ -3484,26 +3470,23 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-module-lexer@1.7.0: - resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-module-lexer@2.3.1: + resolution: {integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==} - es-module-lexer@2.0.0: - resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} - - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} engines: {node: '>= 0.4'} es-set-tostringtag@2.1.0: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} - es-to-primitive@1.3.0: - resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + es-to-primitive@1.3.4: + resolution: {integrity: sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==} engines: {node: '>= 0.4'} - esbuild@0.27.3: - resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} + esbuild@0.28.1: + resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} engines: {node: '>=18'} hasBin: true @@ -3511,9 +3494,6 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} - estree-walker@1.0.1: - resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==} - estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} @@ -3524,36 +3504,47 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + eta@4.6.0: + resolution: {integrity: sha512-lW6is4T1NFOYnmqGZIfvixqj7A7sSvScF+DN8EK6K58xI5MZ5UvYe0GjopxOXQtZvUn4eDdVuZ8XSoYWTMEKwA==} + engines: {node: '>=20'} + eval@0.1.8: resolution: {integrity: sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==} engines: {node: '>= 0.8'} + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + eventemitter3@5.0.4: resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + events-universal@1.0.1: + resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==} + events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} - expect-type@1.3.0: - resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} engines: {node: '>=12.0.0'} + fake-indexeddb@6.2.5: + resolution: {integrity: sha512-CGnyrvbhPlWYMngksqrSSUT1BAVP49dZocrHuK0SvtR0D5TMs5wP0o3j7jexDJW01KSadjBp1M/71o/KR3nD1w==} + engines: {node: '>=18'} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} + fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - fast-uri@3.1.0: - resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} - - fastq@1.20.1: - resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + fast-uri@4.1.2: + resolution: {integrity: sha512-TyGmBcbDTZXcb2cj5MV89DrF42DKvb3y5DDUNh95iO+IMeAzMkVSxK1PZRrRIpc9yg8U2GhGdbofNa0LS/a4Bw==} fd-package-json@2.0.0: resolution: {integrity: sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==} @@ -3567,8 +3558,8 @@ packages: picomatch: optional: true - fflate@0.8.2: - resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + fflate@0.8.3: + resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} file-saver@2.0.5: resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==} @@ -3584,21 +3575,22 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} - flatted@3.4.2: - resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + flatted@3.4.4: + resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} - focus-trap-react@10.3.1: - resolution: {integrity: sha512-PN4Ya9xf9nyj/Nd9VxBNMuD7IrlRbmaG6POAQ8VLqgtc6IY/Ln1tYakow+UIq4fihYYYFM70/2oyidE6bbiPgw==} + focus-trap-react@12.0.3: + resolution: {integrity: sha512-4eXtzhRTtFrxle9Tkl0Wkj8SFJnWz3i3yb5e53Mdn4E8XZQ/Lzqom9U4uUAJ8wd7yDmyGVP96dxwQZXLXd4sbg==} peerDependencies: - prop-types: ^15.8.1 - react: '>=16.3.0' - react-dom: '>=16.3.0' + '@types/react': ^18.0.0 || ^19.0.0 + '@types/react-dom': ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 - focus-trap@7.8.0: - resolution: {integrity: sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA==} + focus-trap@8.2.2: + resolution: {integrity: sha512-qV0g8hRYBqgACcFOH3f9wXc4zPKhr/0z9RI2a6ZijZ72EeBi4g8oBy8zAWuUR1TsMpOzwpUMFvjdasrC41Joug==} - folds@2.6.2: - resolution: {integrity: sha512-1HemxxSnBm8/U5kq1pDQrFkpltWgQN90DmWCZWkZb7D2pe8BhOJSwIRLjk9WxHcw6nn69oz2XNYIXtSw0LvX1w==} + folds@2.7.1: + resolution: {integrity: sha512-A1XGXJ4hp1W0cpFUIjYj9gPSzKVxqj9BlTBJTqmmtTUiEFDzXvhBITJdcqmlDsmcd756tu/iygSAzdky5fDcrg==} peerDependencies: '@vanilla-extract/css': 1.9.2 '@vanilla-extract/recipes': 0.3.0 @@ -3619,24 +3611,18 @@ packages: engines: {node: '>=18.3.0'} hasBin: true - framer-motion@12.34.3: - resolution: {integrity: sha512-v81ecyZKYO/DfpTwHivqkxSUBzvceOpoI+wLfgCgoUIKxlFKEXdg0oR9imxwXumT4SFy8vRk9xzJ5l3/Du/55Q==} - peerDependencies: - '@emotion/is-prop-valid': '*' - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@emotion/is-prop-valid': - optional: true - react: - optional: true - react-dom: - optional: true + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} fs-extra@9.1.0: resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} engines: {node: '>=10'} + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -3645,8 +3631,8 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - function.prototype.name@1.1.8: - resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + function.prototype.name@1.2.0: + resolution: {integrity: sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==} engines: {node: '>= 0.4'} functions-have-names@1.2.3: @@ -3660,6 +3646,10 @@ packages: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -3667,6 +3657,10 @@ packages: get-own-enumerable-property-symbols@3.0.2: resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==} + get-port@5.1.1: + resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} + engines: {node: '>=8'} + get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} @@ -3675,10 +3669,18 @@ packages: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} + get-tsconfig@4.14.1: + resolution: {integrity: sha512-Dz/6HxkrxgNehhxLVeyv8sad9UzF2xBVeaKBQNDfJ5XiSXmp2gTR0eO0RWiT2NCKS5aGP9jjkOMggTN90qU50A==} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} + glob@10.5.0: + resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + hasBin: true + glob@11.1.0: resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==} engines: {node: 20 || >=22} @@ -3723,18 +3725,18 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} - html-dom-parser@5.0.3: - resolution: {integrity: sha512-slsc6ipw88OUZjAayRs5NTmfOQCwcUa3hNyk6AdsbQxY09H5Lr1Y3CZ4ZlconMKql3Ga6sWg3HMoUzo7KSItaQ==} + html-dom-parser@8.0.0: + resolution: {integrity: sha512-cWLJ8mt930VceOzVfY/J+T1ou9v3ENT0BgWqy5aEZjdFp37TYLXULjX8u0vD2rBpFAkK5IofOPl0y4Gq0QLIyA==} - html-dom-parser@5.1.8: - resolution: {integrity: sha512-MCIUng//mF2qTtGHXJWr6OLfHWmg3Pm8ezpfiltF83tizPWY17JxT4dRLE8lykJ5bChJELoY3onQKPbufJHxYA==} + html-dom-parser@8.0.2: + resolution: {integrity: sha512-UnUW0rcyfkYo9OCgeo+5Pzc4lRaifZB4h8RlGRwYIJmEz97Q3OKIYhNVXhG53jmNKWvBrq9n4ZzZIZAECnatSw==} html-encoding-sniffer@6.0.0: resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} @@ -3743,19 +3745,21 @@ packages: html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - html-parse-stringify@3.0.1: - resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==} + html-parse-stringify@4.0.1: + resolution: {integrity: sha512-0zHsZJrK7S3K2aucXWL6ycoYJ/iNtIcFHC/nYQgFklPtrv5LpJctIiSCroWZWeuoXvuyFdzp6KzjJQ+OT5MfFw==} - html-react-parser@4.2.10: - resolution: {integrity: sha512-JyKZVQ+kQ8PdycISwkuLbEEvV/k4hWhU6cb6TT7yGaYwdqA7cPt4VRYXkCZcix2vlQtgDBSMJUmPI2jpNjPGvg==} + html-react-parser@6.1.5: + resolution: {integrity: sha512-jhdRR0fSkdkVlAmDIk5lS+IMSjCgFSfe+nI69DUwaxkWOgid2mk7RhoYxsQ7B6hzCghAI5YPz2B1jbskHhbaLg==} peerDependencies: - react: 0.14 || 15 || 16 || 17 || 18 - - htmlparser2@10.1.0: - resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==} + '@types/react': 0.14 || 15 || 16 || 17 || 18 || 19 + react: 0.14 || 15 || 16 || 17 || 18 || 19 + peerDependenciesMeta: + '@types/react': + optional: true - htmlparser2@9.0.0: - resolution: {integrity: sha512-uxbSI98wmFT/G4P2zXx4OVx04qWUmyFPrD2/CNepa2Zo3GPNaCaaxElDgwUrwYWkK1nr9fft0Ya8dws8coDLLQ==} + htmlparser2@12.0.0: + resolution: {integrity: sha512-Tz7u1i95/g2x2jz81+x0FBVhBhY5aRTvD3tXXdFaljuNdzDLJ8UGNRrTcj2cgQvAg3iW/h77Fz15nLW0L0CrZw==} + engines: {node: '>=20.19.0'} https-proxy-agent@5.0.1: resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} @@ -3764,13 +3768,14 @@ packages: i18next-browser-languagedetector@8.2.1: resolution: {integrity: sha512-bZg8+4bdmaOiApD7N7BPT9W8MLZG+nPTOFlLiJiT8uzKXFjhxw4v2ierCXOwB5sFDMtuA5G4kgYZ0AznZxQ/cw==} - i18next-http-backend@2.7.3: - resolution: {integrity: sha512-FgZxrXdRA5u44xfYsJlEBL4/KH3f2IluBpgV/7riW0YW2VEyM8FzVt2XHAOi6id0Ppj7vZvCZVpp5LrGXnc8Ig==} + i18next-http-backend@4.0.1: + resolution: {integrity: sha512-O+7MwPCJIKCu68vho8JtD1HF8QHyMPzoChIDFfYCrpaDqX6oq3RqASujnpKEdnNCEOb+kLMOHTNTcGKpj7B1BA==} + engines: {node: '>=18'} - i18next@25.8.17: - resolution: {integrity: sha512-vWtCttyn5bpOK4hWbRAe1ZXkA+Yzcn2OcACT+WJavtfGMcxzkfvXTLMeOU8MUhRmAySKjU4VVuKlo0sSGeBokA==} + i18next@26.3.6: + resolution: {integrity: sha512-Bu5Z2nAXgfVyM8xvW3jk9EKRIuX37PudsrBViThNFx7CR7aaYTpP01cxNB/E4c4UUzTDiAZRstEhsRfPOL/8xA==} peerDependencies: - typescript: ^5 + typescript: ^5 || ^6 || ^7 peerDependenciesMeta: typescript: optional: true @@ -3781,8 +3786,8 @@ packages: ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - immer@9.0.21: - resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==} + immer@11.1.18: + resolution: {integrity: sha512-EQyQtLiYW029lyoczMl/Hh4Xu7cDecSc58JRYpHyL4tIAu3eqd1yJzQX04d2BZHDkzFFvm6qJEJWOtfDSWAXbQ==} import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} @@ -3792,16 +3797,16 @@ packages: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} - inline-style-parser@0.2.2: - resolution: {integrity: sha512-EcKzdTHVe8wFVOGEYXiW9WmJXPjqi1T+234YpJr98RiFYKHV3cdy1+3mkTE+KHTHxFFLH51SfaGOoUdW+v7ViQ==} + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + inline-style-parser@0.2.9: + resolution: {integrity: sha512-IttN8BqKLxzUrnTqHI+/hwAJQItBAUjdoHERCDv+oOuX48VP6bzW8+QjOXSfoevzq3YrYAwX1uLHp8TqO9yIeg==} internal-slot@1.1.0: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} - intl-messageformat@10.7.18: - resolution: {integrity: sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g==} - is-array-buffer@3.0.5: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} @@ -3829,8 +3834,8 @@ packages: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} - is-core-module@2.16.1: - resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + is-core-module@2.16.2: + resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} engines: {node: '>= 0.4'} is-data-view@1.0.2: @@ -3841,6 +3846,10 @@ packages: resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} engines: {node: '>= 0.4'} + is-document.all@1.0.0: + resolution: {integrity: sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==} + engines: {node: '>= 0.4'} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -3849,6 +3858,10 @@ packages: resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} engines: {node: '>= 0.4'} + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + is-generator-function@1.1.2: resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} engines: {node: '>= 0.4'} @@ -3871,8 +3884,8 @@ packages: resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} engines: {node: '>= 0.4'} - is-network-error@1.3.1: - resolution: {integrity: sha512-6QCxa49rQbmUWLfk0nuGqzql9U8uaV2H6279bRErPBHe/109hCzsLUBUHfbEtvLIHBd6hyXbgedBSHevm43Edw==} + is-network-error@1.3.2: + resolution: {integrity: sha512-PhBY86zaxNZUuWP6h13Vu5oFe0XY6/UlKzQnYFELzGVHygP3MxmvTfYSG7GN3aIab/iWudSMgjSnG9Dq+nHrgA==} engines: {node: '>=16'} is-number-object@1.1.1: @@ -3887,10 +3900,6 @@ packages: resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==} engines: {node: '>=0.10.0'} - is-plain-object@5.0.0: - resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} - engines: {node: '>=0.10.0'} - is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} @@ -3910,6 +3919,9 @@ packages: resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} engines: {node: '>= 0.4'} + is-standalone-pwa@0.1.1: + resolution: {integrity: sha512-9Cbovsa52vNQCjdXOzeQq5CnCbAcRk05aU62K20WO372NrTv0NxibLFCK6lQ4/iZEFdEA3p3t2VNOn8AJ53F5g==} + is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -3938,6 +3950,9 @@ packages: resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} engines: {node: '>= 0.4'} + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} @@ -3956,6 +3971,9 @@ packages: resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} engines: {node: '>=8'} + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jackspeak@4.2.3: resolution: {integrity: sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==} engines: {node: 20 || >=22} @@ -3968,12 +3986,15 @@ packages: javascript-stringify@2.1.0: resolution: {integrity: sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==} - jiti@2.6.1: - resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true - jotai@2.18.1: - resolution: {integrity: sha512-e0NOzK+yRFwHo7DOp0DS0Ycq74KMEAObDWFGmfEL28PD9nLqBTt3/Ug7jf9ca72x0gC9LQZG9zH+0ISICmy3iA==} + jose@6.2.9: + resolution: {integrity: sha512-XrchZOFZUl/T3vTwRe8XK+cJrGtMF4th1ARnDfwbBXFKThGhlsxEE4Zu03AD/bjJSt/9jT/mxrOCkJWOg77aPA==} + + jotai@2.20.2: + resolution: {integrity: sha512-aHB4CNb9qRcyf0mwSB6EO5bCGAjx8cTwFgOFCE2leOnTzqACbnSWG8XoWB3LxCT1Qoj03I1OWAHszDmN4uHb/w==} engines: {node: '>=12.20.0'} peerDependencies: '@babel/core': '>=7.0.0' @@ -3996,15 +4017,15 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@4.1.1: - resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + js-yaml@5.3.0: + resolution: {integrity: sha512-muutsYr+e2+d3rTgUGslq5rxbBlUy3cJ61IsHag2QNDQV+7zXWjkUpmALIajhrlLlrgRUiymj6U3zUr/TMK84Q==} hasBin: true - jsdom@29.0.0: - resolution: {integrity: sha512-9FshNB6OepopZ08unmmGpsF7/qCjxGPbo3NbgfJAnPeHXnsODE9WWffXZtRFRFe0ntzaAOcSKNJFz8wiyvF1jQ==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0} + jsdom@30.0.1: + resolution: {integrity: sha512-52v7mUVUfNQVYYqE1lcdaymWL0njO7lTLUog6ZvW2U5KsbiLk/GnZlVJ+qx0xfNJZ6Gn+KSpPNE52vurbxZwrA==} + engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} peerDependencies: - canvas: ^3.0.0 + canvas: ^3.2.3 peerDependenciesMeta: canvas: optional: true @@ -4020,70 +4041,153 @@ packages: json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - json-schema@0.4.0: - resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} - json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} hasBin: true - jsonfile@6.2.0: - resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + jsonfile@6.2.1: + resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} jsonpointer@5.0.1: resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} engines: {node: '>=0.10.0'} - jwt-decode@4.0.0: - resolution: {integrity: sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==} - engines: {node: '>=18'} - - kleur@4.1.5: - resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} - engines: {node: '>=6'} + katex@0.18.1: + resolution: {integrity: sha512-Td8GCYSxDAoMhHOlKmCFMJ/hz5qlAAb71n66Dryw9nfCVfumLo7nhuotbvKom/XPADmrYC3O5QR71EPq4DarJQ==} + hasBin: true - knip@5.85.0: - resolution: {integrity: sha512-V2kyON+DZiYdNNdY6GALseiNCwX7dYdpz9Pv85AUn69Gk0UKCts+glOKWfe5KmaMByRjM9q17Mzj/KinTVOyxg==} - engines: {node: '>=18.18.0'} + knip@6.32.1: + resolution: {integrity: sha512-mIiIHMTJVUgSlz0mxEgPt7wg8DmfbCp1Txqab3WpbMCJF7YHvHtC9jeAHHXfISMl72N8WzhyG71SQlaqCOGZtg==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - peerDependencies: - '@types/node': '>=18' - typescript: '>=5.0.4 <7' + + lazystream@1.0.1: + resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} + engines: {node: '>= 0.6.3'} + + leaflet@1.9.4: + resolution: {integrity: sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==} leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - linkify-react@4.3.2: - resolution: {integrity: sha512-mi744h1hf+WDsr+paJgSBBgYNLMWNSHyM9V9LVUo03RidNGdw1VpI7Twnt+K3pEh3nIzB4xiiAgZxpd61ItKpQ==} + linkify-react@4.3.3: + resolution: {integrity: sha512-bMWuN1YJ3hyDyojk4hzUugecN2RsTuibKAQBplUT2o+3uK33THdRL2qD0Zf3ibCXuBLBCyOE6Uj/S/1cY48Ygw==} peerDependencies: - linkifyjs: ^4.0.0 + linkifyjs: '==4.3.3' react: '>= 15.0.0' - linkifyjs@4.3.2: - resolution: {integrity: sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA==} + linkifyjs@4.3.3: + resolution: {integrity: sha512-P8aEP5U/D1/IlTY2OeYsErdwh9bGuLE30NcXtKEjgdHcahveQoQwM2yZNsioQHsWFz0P7KKudisbrzCgR0sDHg==} + + livekit-client@2.21.0: + resolution: {integrity: sha512-RBUhPkV/sl1nzl8lokVlK5uATPwn0AlsudCBZXissw/kDl9yz8ac4pNJ43iPpMVoHOeBYH/BZ3vUC1adqa/zFQ==} + peerDependencies: + '@types/dom-mediacapture-record': ^1 locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} - lodash.sortby@4.7.0: - resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} - lodash@4.17.23: - resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} + loglevel@1.9.1: + resolution: {integrity: sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==} + engines: {node: '>= 0.6.0'} loglevel@1.9.2: resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==} engines: {node: '>= 0.6.0'} + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -4094,12 +4198,8 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@11.2.6: - resolution: {integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==} - engines: {node: 20 || >=22} - - lru-cache@11.2.7: - resolution: {integrity: sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==} + lru-cache@11.5.2: + resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} engines: {node: 20 || >=22} lru-cache@5.1.1: @@ -4109,19 +4209,21 @@ packages: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true - magic-string@0.25.9: - resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} - magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - magicast@0.5.2: - resolution: {integrity: sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==} + magicast@0.5.3: + resolution: {integrity: sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==} make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} + marked@18.0.10: + resolution: {integrity: sha512-FJeH4bRpYoXiggcgriCGItKCSv3xkngJc4QCZ/rkQCogU3VYaLxYJoZl8Nw/b4+x7iij/pd+09mZ6A1dXzpL0A==} + engines: {node: '>= 20'} + hasBin: true + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -4129,58 +4231,47 @@ packages: matrix-events-sdk@0.0.1: resolution: {integrity: sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA==} - matrix-js-sdk@38.4.0: - resolution: {integrity: sha512-Xs9/6pE1eL/F5bP11jrtsZXiPlCda+mW5UC21DifvpjHWvAZsz4rq24rXd4s5/oPrIZKJqP8Fnfcic870ben2w==} + matrix-js-sdk@42.0.0: + resolution: {integrity: sha512-i3SnhdkgnKA7O0iVlaZCo96GGaIZ0FGD93LQ34N2+Hj/kaC7Ffbch7OEkwYA//mYGCKRAMxduBacoy8WscfqVw==} engines: {node: '>=22.0.0'} matrix-widget-api@1.17.0: resolution: {integrity: sha512-5FHoo3iEP3Bdlv5jsYPWOqj+pGdFQNLWnJLiB0V7Ygne7bb+Gsj3ibyFyHWC6BVw+Z+tSW4ljHpO17I9TwStwQ==} + matrix-widget-api@1.18.0: + resolution: {integrity: sha512-4T2f2koWmx05p1BLcT/9YGGGPSXpPT+PA4Oap/5fjhXsWPxMGJiGL97YpANMYLKsnE1sScYFeuyxIgdc1Qo+Ew==} + mdn-data@2.27.1: resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} media-query-parser@2.0.2: resolution: {integrity: sha512-1N4qp+jE0pL5Xv4uEcwVUhIkwdUO3S/9gML90nqKA7v7FcOS5vUtatfzok9S9U1EJU8dHWlcv95WLnKmmxZI9w==} - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} - min-indent@1.0.1: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} - miniflare@4.20260310.0: - resolution: {integrity: sha512-uC5vNPenFpDSj5aUU3wGSABG6UUqMr+Xs1m4AkCrTHo37F4Z6xcQw5BXqViTfPDVT/zcYH1UgTVoXhr1l6ZMXw==} - engines: {node: '>=18.0.0'} - hasBin: true - - minimatch@10.2.4: - resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==} + minimatch@10.2.6: + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} engines: {node: 18 || 20 || >=22} - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - minipass@7.1.3: resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} - mlly@1.8.1: - resolution: {integrity: sha512-SnL6sNutTwRWWR/vcmCYHSADjiEesp5TGQQ0pXyLhW5IoeibRlF/CbSLailbB3CNqJUk9cVJ9dUDnbD7GrcHBQ==} + mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} - modern-ahocorasick@1.1.0: - resolution: {integrity: sha512-sEKPVl2rM+MNVkGQt3ChdmD8YsigmXdn5NifZn6jiwn9LRJpWm8F3guhaqrJT/JOat6pwpbXEk6kv+b9DMIjsQ==} + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true - motion-dom@12.35.2: - resolution: {integrity: sha512-pWXFMTwvGDbx1Fe9YL5HZebv2NhvGBzRtiNUv58aoK7+XrsuaydQ0JGRKK2r+bTKlwgSWwWxHbP5249Qr/BNpg==} + mlly@1.8.2: + resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} - motion-utils@12.29.2: - resolution: {integrity: sha512-G3kc34H2cX2gI63RqU+cZq+zWRRPSsNIOjpdl9TN4AQwC4sgwYPl/Q/Obf/d53nOm569T0fYK+tcoSV50BWx8A==} + modern-ahocorasick@1.1.0: + resolution: {integrity: sha512-sEKPVl2rM+MNVkGQt3ChdmD8YsigmXdn5NifZn6jiwn9LRJpWm8F3guhaqrJT/JOat6pwpbXEk6kv+b9DMIjsQ==} mrmime@2.0.1: resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} @@ -4189,9 +4280,12 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + nan@2.28.0: + resolution: {integrity: sha512-fTsDz99OTq2sVePhGdp4qQhggZFtKr64ZNVyVajRKtMOkJxYekplBh577PiJB12v/D3s2E5cGtOI45LWp6rnLQ==} + + nanoid@6.0.1: + resolution: {integrity: sha512-3wVS3i51pE2pi1k5FFL/95BGfVS0kSsvDVuGXHOtxox/TywUmtgq+3qiTOTbs9J7KfHaXPiN171k/A6dBnaXFw==} + engines: {node: ^22 || ^24 || >=26} hasBin: true no-case@3.0.4: @@ -4206,11 +4300,9 @@ packages: encoding: optional: true - node-readable-to-web-readable-stream@0.4.2: - resolution: {integrity: sha512-/cMZNI34v//jUTrI+UIo4ieHAB5EZRY/+7OmXZgBxaWBMcW2tGdceIw06RFxWxrKZ5Jp3sI2i5TsRo+CBhtVLQ==} - - node-releases@2.0.36: - resolution: {integrity: sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==} + node-releases@2.0.51: + resolution: {integrity: sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==} + engines: {node: '>=18'} normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} @@ -4232,38 +4324,56 @@ packages: resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} engines: {node: '>= 0.4'} - obug@2.1.1: - resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + obug@2.1.4: + resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} + engines: {node: '>=12.20.0'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - oidc-client-ts@3.4.1: - resolution: {integrity: sha512-jNdst/U28Iasukx/L5MP6b274Vr7ftQs6qAhPBCvz6Wt5rPCA+Q/tUmCzfCHHWweWw5szeMy2Gfrm1rITwUKrw==} - engines: {node: '>=18'} + orderedmap@2.1.1: + resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==} - own-keys@1.0.1: - resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + own-keys@1.0.2: + resolution: {integrity: sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==} engines: {node: '>= 0.4'} - oxc-resolver@11.19.1: - resolution: {integrity: sha512-qE/CIg/spwrTBFt5aKmwe3ifeDdLfA2NESN30E42X/lII5ClF8V7Wt6WIJhcGZjp0/Q+nQ+9vgxGk//xZNX2hg==} + oxc-parser@0.142.0: + resolution: {integrity: sha512-kKR+jPiRJYJDexVoziIg/FVGvr1fT1FZSSJOk6tVoMKKSlsf1Cso+cgGCJkOEDWOP174vRntCPFKg+AS7InWvw==} + engines: {node: ^20.19.0 || >=22.12.0} + + oxc-resolver@11.24.2: + resolution: {integrity: sha512-FY91FiDBj7ls5MsFS9jN3tjz2o0/zsdSsymlakySaBwVJZorHhkWyICLZMKxlu1R9vYo+sd3z1jwb4J8x7bNDw==} - oxfmt@0.45.0: - resolution: {integrity: sha512-0o/COoN9fY50bjVeM7PQsNgbhndKurBIeTIcspW033OumksjJJmIVDKjAk5HMwU/GHTxSOdGDdhJ6BRzGPmsHg==} + oxfmt@0.61.0: + resolution: {integrity: sha512-DxdHBEMYpcEnHoUHjjOigUqV2TYKsvxLwUPXnVYBjgFdqrcQ/91OtwubtZ2PUodCs3sStI8R5Qw3fKNGK4e8wQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + peerDependencies: + svelte: ^5.0.0 + vite-plus: '*' + peerDependenciesMeta: + svelte: + optional: true + vite-plus: + optional: true - oxlint-tsgolint@0.21.0: - resolution: {integrity: sha512-HiWPhANwRnN1pZJQ2SgNB3WRR+1etLJHmRzQ/MJhyINsEIaOUCjxhlXJKbEaVUwdnyXwRWqo/P9Fx21lz0/mSg==} + oxlint-tsgolint@7.0.2001: + resolution: {integrity: sha512-KjK/XLcXr1DSyonKhsuFqJRiuKqcyG9j3LJ8nkOsrLzGvodBPqzHOKauy10asLMDI0sUpvb+1sxlzff3udZvfg==} hasBin: true - oxlint@1.60.0: - resolution: {integrity: sha512-tnRzTWiWJ9pg3ftRWnD0+Oqh78L6ZSwcEudvCZaER0PIqiAnNyXj5N1dPwjmNpDalkKS9m/WMLN1CTPUBPmsgw==} + oxlint@1.76.0: + resolution: {integrity: sha512-6QoFioEU4fNdiUx/2Eo6TRd6NG7H7njnRCz8rhB66cZmMHDTqcm1Rjvl8Wry+ZTQMBAmyb4Mlf62Mk5X+eHSOw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - oxlint-tsgolint: '>=0.18.0' + oxlint-tsgolint: '>=7.0.2001' + vite-plus: '*' peerDependenciesMeta: oxlint-tsgolint: optional: true + vite-plus: + optional: true p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} @@ -4273,13 +4383,13 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} - p-map@7.0.4: - resolution: {integrity: sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==} + p-map@7.0.6: + resolution: {integrity: sha512-I4Prw6ivkd6p8PiYR1tXASOAOBzIJwu0TB7fqaX0c/8c3QAehNYmX57EijyGGGBt3c/BIowGwV03RVBtXvHEVg==} engines: {node: '>=18'} - p-retry@7.1.1: - resolution: {integrity: sha512-J5ApzjyRkkf601HpEeykoiCvzHQjWxPAHhyjFcEUP2SWq0+35NKh8TLhpLw+Dkq5TZBFvUM6UigdE9hIVYTl5w==} - engines: {node: '>=20'} + p-retry@8.0.0: + resolution: {integrity: sha512-kFVqH1HxOHp8LupNsOys7bSV09VYTRLxarH/mokO4Rqhk6wGi70E0jh4VzvVGXfEVNggHoHLAMWsQqHyU1Ey9A==} + engines: {node: '>=22'} package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} @@ -4292,8 +4402,8 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} - parse5@8.0.0: - resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==} + parse5@8.0.1: + resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==} path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} @@ -4306,13 +4416,14 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + path-scurry@2.0.2: resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} engines: {node: 18 || 20 || >=22} - path-to-regexp@6.3.0: - resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} - path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -4320,34 +4431,40 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - pdfjs-dist@5.5.207: - resolution: {integrity: sha512-WMqqw06w1vUt9ZfT0gOFhMf3wHsWhaCrxGrckGs5Cci6ybDW87IvPaOd2pnBwT6BJuP/CzXDZxjFgmSULLdsdw==} - engines: {node: '>=20.19.0 || >=22.13.0 || >=24'} + pdfjs-dist@6.2.108: + resolution: {integrity: sha512-YxFb+SQcodN2rnX9Tn3dHYlqfb7NjlzzfONPpJd+AKoKtUjEdevTfbC07d5TcczzOK6261auRkP/M8OBHs9vFQ==} + engines: {node: '>=22.13.0 || >=24'} picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - picomatch@2.3.2: resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} engines: {node: '>=8.6'} - picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} engines: {node: '>=12'} pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + playwright-core@1.62.1: + resolution: {integrity: sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==} + engines: {node: '>=20'} + hasBin: true + + playwright@1.62.1: + resolution: {integrity: sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==} + engines: {node: '>=20'} + hasBin: true + possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} - postcss@8.5.8: - resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} + postcss@8.5.25: + resolution: {integrity: sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==} engines: {node: ^10 || ^12 || >=14} pretty-bytes@5.6.0: @@ -4362,6 +4479,13 @@ packages: resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + progress@2.0.3: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} @@ -4369,21 +4493,53 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + proper-lockfile@4.1.2: + resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} + + properties-reader@3.0.1: + resolution: {integrity: sha512-WPn+h9RGEExOKdu4bsF4HksG/uzd3cFq3MFtq8PsFeExPse5Ha/VOjQNyHhjboBFwGXGev6muJYTSPAOkROq2g==} + engines: {node: '>=18'} + + prosemirror-commands@1.7.2: + resolution: {integrity: sha512-q6Q6szxqdu9Xd6EcdKsqXghu5nQdZTpB4Q9yd04WRc7/jt763e/rT60Owh0L1GYY+T46o5rD+9lEN36dZS43tw==} + + prosemirror-history@1.5.0: + resolution: {integrity: sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==} + + prosemirror-keymap@1.2.3: + resolution: {integrity: sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==} + + prosemirror-model@1.25.11: + resolution: {integrity: sha512-QWg9RhnpLlogAmp3p96uEFrE5txQpFynd4vhBAELkwgOCWQs/X0yCzB3/hrHqiPwf91RG5KyWq6553zs9JqIOQ==} + + prosemirror-state@1.4.4: + resolution: {integrity: sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==} + + prosemirror-transform@1.12.0: + resolution: {integrity: sha512-GxboyN4AMIsoHNtz5uf2r2Ru551i5hWeCMD6E2Ib4Eogqoub0NflniaBPVQ4MrGE5yZ8JV9tUHg9qcZTTrcN4w==} + + prosemirror-view@1.42.2: + resolution: {integrity: sha512-Pdg0l5kXm8aLDquFAnQFTCITg0q44sLqBlHlpsVLD9segdOao8TOfQdAhCrCXyVgPSRr6UDDROOIWA3bIrN9YQ==} + + protobufjs@7.6.5: + resolution: {integrity: sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw==} + engines: {node: '>=12.0.0'} + proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + pump@3.0.4: + resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - raf-schd@4.0.3: resolution: {integrity: sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==} - react-aria@3.47.0: - resolution: {integrity: sha512-nvahimIqdByl/PXk/xPkG30LPRzcin+/Uk0uFfwbbKRRFC9aa22a6BRULZLqVHwa9GaNyKe6CDUxO1Dde4v0kA==} + react-aria@3.50.0: + resolution: {integrity: sha512-S0Os6QZk33fzUAKu1QLT9afoUaCBt1ZNdoiq0n2YMVgKIdNIQS8zxiZ8O9hYE6QyDkHKjD6q39LQZ+qaSAIgjw==} peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 @@ -4399,30 +4555,30 @@ packages: blurhash: ^2.0.3 react: '>=15' - react-colorful@5.6.1: - resolution: {integrity: sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==} + react-colorful@5.8.0: + resolution: {integrity: sha512-Wy9OzPfjSN9bF12OB8N7UQvlsZ0I+7wHxpN+bV5BjNQGxOj6IiwkRjevJK9yOBjJWGQvAaf1OXtn8rUeEatAng==} peerDependencies: react: '>=16.8.0' react-dom: '>=16.8.0' - react-dom@18.3.1: - resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + react-dom@19.2.8: + resolution: {integrity: sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==} peerDependencies: - react: ^18.3.1 + react: ^19.2.8 - react-google-recaptcha@2.1.0: - resolution: {integrity: sha512-K9jr7e0CWFigi8KxC3WPvNqZZ47df2RrMAta6KmRoE4RUi7Ys6NmNjytpXpg4HI/svmQJLKR+PncEPaNJ98DqQ==} + react-google-recaptcha@3.1.0: + resolution: {integrity: sha512-cYW2/DWas8nEKZGD7SCu9BSuVz8iOcOLHChHyi7upUuVhkpkhYG/6N3KDiTQ3XAiZ2UAZkfvYKMfAHOzBOcGEg==} peerDependencies: react: '>=16.4.1' - react-i18next@16.5.7: - resolution: {integrity: sha512-t/si6ng+hMPvgRGNgGvHAkMuVRBsIBx5mN+exm/yiBPSFL7VooQ37YYfISxSE0LjvQjG+MTe+0htKdOJY0S/vw==} + react-i18next@17.0.11: + resolution: {integrity: sha512-cDtkXgxjuFTWUH6V+aQn1Ve5vDiUztCNPWW5GtSHDccsgRXO1nE6QFWCEmc1KAutrb3OUv87wFShJL5RhUwPXg==} peerDependencies: - i18next: '>= 25.6.2' + i18next: '>= 26.2.0' react: '>= 16.8.0' react-dom: '*' react-native: '*' - typescript: ^5 + typescript: ^5 || ^6 || ^7 peerDependenciesMeta: react-dom: optional: true @@ -4437,6 +4593,13 @@ packages: react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + react-leaflet@5.0.0: + resolution: {integrity: sha512-CWbTpr5vcHw5bt9i4zSlPEVQdTVcML390TjeDG0cK59z1ylexpqC6M1PJFjV8jD7CF+ACBFsLIDs6DRMoLEofw==} + peerDependencies: + leaflet: ^1.9.0 + react: ^19.0.0 + react-dom: ^19.0.0 + react-property@2.0.2: resolution: {integrity: sha512-+PbtI3VuDV0l6CleQMsx2gtK0JZbZKbpdu5ynr+lbsuvtmgbNcS3VM0tuY2QjFNOcWxvXeHjDpy42RO+4U2rug==} @@ -4446,27 +4609,39 @@ packages: react: '*' react-dom: '*' - react-refresh@0.18.0: - resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} - engines: {node: '>=0.10.0'} - - react-router-dom@6.30.3: - resolution: {integrity: sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==} - engines: {node: '>=14.0.0'} + react-router@8.3.0: + resolution: {integrity: sha512-qyPMvW83jGIct3yiieisxdk9M745anqhpIMKN5m1t6yBMfgVPpt77aHOqs5fUlEJRMCGffg9BaQLH9oPVOL7xQ==} + engines: {node: '>=22.22.0'} peerDependencies: - react: '>=16.8' - react-dom: '>=16.8' + react: '>=19.2.7' + react-dom: '>=19.2.7' + peerDependenciesMeta: + react-dom: + optional: true - react-router@6.30.3: - resolution: {integrity: sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==} - engines: {node: '>=14.0.0'} + react-stately@3.48.0: + resolution: {integrity: sha512-ImicSAG+lTotAe5izcs1fz49Zk48w7pDusqYg04WaPhCoej8BJ24soMu3iLXIrsi273s4P1gZrYGrqReMfgEEA==} peerDependencies: - react: '>=16.8' + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react@18.3.1: - resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + react@19.2.8: + resolution: {integrity: sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==} engines: {node: '>=0.10.0'} + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readable-stream@4.7.0: + resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + readdir-glob@1.1.3: + resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -4497,10 +4672,14 @@ packages: regjsgen@0.8.0: resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} - regjsparser@0.13.0: - resolution: {integrity: sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==} + regjsparser@0.13.2: + resolution: {integrity: sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ==} hasBin: true + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} @@ -4512,27 +4691,44 @@ packages: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} - resolve@1.22.11: - resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} engines: {node: '>= 0.4'} hasBin: true - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + + rolldown@1.2.1: + resolution: {integrity: sha512-4FKJhg8d3OiyQOA6Q1Q0hoFFpW9/OoX+VsHzpECsdsIZoOArrAK90gl59YK/Z+gnDel45bgJZK03ozH/9bCqEw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true - rollup@4.59.0: - resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} + rollup@4.62.3: + resolution: {integrity: sha512-Gu0c0iH9FzgX1L1t7ByIbbS3Vmdz+6KHm/EsqmmC71gUQ82yvZRkTK6XzrFObSka91WUVdynqp6nsfilzr5k6Q==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + rope-sequence@1.3.4: + resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==} - safe-array-concat@1.1.3: - resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + + safe-array-concat@1.1.4: + resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} engines: {node: '>=0.4'} + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safe-push-apply@1.0.0: resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} engines: {node: '>= 0.4'} @@ -4541,31 +4737,38 @@ packages: resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + saxes@6.0.0: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} - scheduler@0.23.2: - resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} - - scroll-into-view-if-needed@3.1.0: - resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==} + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} sdp-transform@2.15.0: resolution: {integrity: sha512-KrOH82c/W+GYQ0LHqtr3caRpM3ITglq3ljGUIb8LTki7ByacJZ9z+piSGiwZDsRyhQbYBOBJgr2k6X4BZXi3Kw==} hasBin: true + sdp-transform@3.0.0: + resolution: {integrity: sha512-gfYVRGxjHkGF2NPeUWHw5u6T/KGFtS5/drPms73gaSuMaVHKCY3lpLnGDfswVQO0kddeePoti09AwhYP4zA8dQ==} + hasBin: true + + sdp@3.2.2: + resolution: {integrity: sha512-xZocWwfyp4hkbN4hLWxMjmv2Q8aNa9MhmOZ7L9aCZPT+dZsgRr6wZRrSYE3HTdyk/2pZKPSgqI7ns7Een1xMSA==} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.7.4: - resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} engines: {node: '>=10'} hasBin: true - serialize-javascript@7.0.5: - resolution: {integrity: sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==} + serialize-javascript@7.0.7: + resolution: {integrity: sha512-YAy8Od6KV+uuwUuU50np8fGB/Aues6Y0nAhA9y/hId74PlKUcme4pXcBD46NWKr1Q4osN/iseZ17YqO1XfmI8g==} engines: {node: '>=20.0.0'} set-function-length@1.2.2: @@ -4580,10 +4783,6 @@ packages: resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} engines: {node: '>= 0.4'} - sharp@0.34.5: - resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -4592,8 +4791,8 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - side-channel-list@1.0.0: - resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} engines: {node: '>= 0.4'} side-channel-map@1.0.1: @@ -4604,13 +4803,16 @@ packages: resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} engines: {node: '>= 0.4'} - side-channel@1.1.0: - resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + side-channel@1.1.1: + resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} engines: {node: '>= 0.4'} siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} @@ -4619,33 +4821,12 @@ packages: resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} engines: {node: '>=18'} - slate-dom@0.123.0: - resolution: {integrity: sha512-OUinp4tvSrAlt64JL9y20Xin08jgnnj1gJmIuPdGvU5MELKXRNZh17a7EKKNOS6OZPAE8Dk9NI1MAIS/Qz0YBw==} - peerDependencies: - slate: '>=0.121.0' - - slate-history@0.113.1: - resolution: {integrity: sha512-J9NSJ+UG2GxoW0lw5mloaKcN0JI0x2IA5M5FxyGiInpn+QEutxT1WK7S/JneZCMFJBoHs1uu7S7e6pxQjubHmQ==} - peerDependencies: - slate: '>=0.65.3' - - slate-react@0.123.0: - resolution: {integrity: sha512-nQwXL1FEacrY9ZFmatRhoBnsySNUX2x6qB77V3oNHd7wWxBJWuzz4GMrBXcVoRE8Gac7Angf8xaNGzb6zcPlHg==} - peerDependencies: - react: '>=18.2.0' - react-dom: '>=18.2.0' - slate: '>=0.121.0' - slate-dom: '>=0.119.1' - - slate@0.123.0: - resolution: {integrity: sha512-Oon3HR/QzJQBjuOUJT1jGGlp8Ff7t3Bkr/rJ2lDqxNT4H+cBnXpEVQ/si6hn1ZCHhD2xY/2N91PQoH/rD7kxTg==} - - smob@1.6.1: - resolution: {integrity: sha512-KAkBqZl3c2GvNgNhcoyJae1aKldDW0LO279wF9bk1PnluRTETKBq0WyzRXxEhoQLk56yHaOY4JCBEKDuJIET5g==} + smob@1.6.2: + resolution: {integrity: sha512-RQsvleCbF8cVHEv+xuDGaA4pOizFqJ0GgjtMSRo6oP8pnN7WsigHgVGey6aILRBKv4W2YOMHLqbKdnB6hpB9fw==} engines: {node: '>=20.0.0'} - smol-toml@1.6.0: - resolution: {integrity: sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==} + smol-toml@1.7.1: + resolution: {integrity: sha512-PPlsspAZ4jbMBu5DMFhfUGDQLu/vrL4SyBROVS37x8ynnVmFIs1VPBz1Co8Xks3TvpIaZXmU85y4DrQ+UyVFoQ==} engines: {node: '>= 18'} snake-case@3.0.4: @@ -4662,45 +4843,75 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - source-map@0.8.0-beta.0: - resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} - engines: {node: '>= 8'} - deprecated: The work that was done in this beta branch won't be included in future versions + source-map@0.8.0: + resolution: {integrity: sha512-d8EqvL+k/SOXCreS/SUzg2ciyHqBBLcN/yuRjFsbvVhHTE2pgei7oAhmPM7kWFbkX6OSMQfUq4KbkF3au9lhYQ==} + engines: {node: '>= 12'} + + split-ca@1.0.1: + resolution: {integrity: sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==} - sourcemap-codec@1.4.8: - resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} - deprecated: Please use @jridgewell/sourcemap-codec instead + ssh-remote-port-forward@1.0.4: + resolution: {integrity: sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ==} + + ssh2@1.17.0: + resolution: {integrity: sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==} + engines: {node: '>=10.16.0'} stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - std-env@4.0.0: - resolution: {integrity: sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==} + std-env@4.2.0: + resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} stop-iteration-iterator@1.1.0: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} + streamx@2.28.0: + resolution: {integrity: sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + string.prototype.matchall@4.0.12: resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} engines: {node: '>= 0.4'} - string.prototype.trim@1.2.10: - resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + string.prototype.trim@1.2.11: + resolution: {integrity: sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==} engines: {node: '>= 0.4'} - string.prototype.trimend@1.0.9: - resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + string.prototype.trimend@1.0.10: + resolution: {integrity: sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==} engines: {node: '>= 0.4'} string.prototype.trimstart@1.0.8: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + stringify-object@3.3.0: resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==} engines: {node: '>=4'} + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + strip-comments@2.0.1: resolution: {integrity: sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==} engines: {node: '>=10'} @@ -4713,11 +4924,11 @@ packages: resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} engines: {node: '>=14.16'} - style-to-js@1.1.8: - resolution: {integrity: sha512-bPSspCXkkhETLXnEgDbaoWRWyv3lF2bj32YIc8IElok2IIMHUlZtQUrxYmAkKUNxpluhH0qnKWrmuoXUyTY12g==} + style-to-js@2.0.2: + resolution: {integrity: sha512-ctgbFvvi406Jvhi/s6TJtX6XYYCCTg6POgsL+SLmrl2RHFgwulepqtYPrtKuEmT8kmyk/+fxfNzuhRLO4zsASg==} - style-to-object@1.0.3: - resolution: {integrity: sha512-xOpx7S53E0V3DpVsvt7ySvoiumRpfXiC99PUXLqGB3wiAnN9ybEIpuzlZ8LAZg+h1sl9JkEUwtSQXxcCgFqbbg==} + style-to-object@2.0.2: + resolution: {integrity: sha512-QmbXPUylqU80HAgTOJeEOdbA40W9KtxmjMhQy+tq+b8F2aoh3aQLuy82hh2jm2qsC9hR9eI0+eQidTMydKcqRA==} supports-color@10.2.2: resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} @@ -4737,12 +4948,35 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - tabbable@6.4.0: - resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==} + tabbable@6.5.0: + resolution: {integrity: sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA==} + + tar-fs@2.1.5: + resolution: {integrity: sha512-OboTd8mmMhZDNPV+UjQcK9yKAatXu2aJ+r1w4im1Otd4M4fl2hwvdoXUxIYHFTHWK/3y3FarBP70v3vwmGlOxw==} + + tar-fs@3.1.3: + resolution: {integrity: sha512-/hU4AXnIdZu+Gvl1pk0oI5f5HxWsCJRtY2aFaJdk9VvyL48DWU6iU5WAIPG+wIi1YvWA6eTJvIviP/tMAZZNwQ==} tar-mini@0.2.0: resolution: {integrity: sha512-+qfUHz700DWnRutdUsxRRVZ38G1Qr27OetwaMYTdg8hcPxf46U0S1Zf76dQMWRBmusOt2ZCK5kbIaiLkoGO7WQ==} + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + + tar-stream@3.2.0: + resolution: {integrity: sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg==} + + tauri-plugin-android-fs-api@28.4.0: + resolution: {integrity: sha512-YOo1P+cRjuaoPbTNZprMhr56ruKO4hIDq7kJOum91NqkN+VWY9F+UPAdjZokhzJSsbkeY0nsb1VDLXb2/h3f3w==} + + tauri-plugin-app-icon-api@https://codeload.github.com/SableClient/tauri-plugin-app-icon/tar.gz/bab80ae17f6d018702e472c539d0f02eba480c21: + resolution: {gitHosted: true, integrity: sha512-qI9gtLAZiWkVSkX4lueDxDWOcqS7G/XshW5V7A5ySy+Yzkj55G3KnBtAVj0GqgfbY7IiV3EYmdTiGdQAX24FNA==, tarball: https://codeload.github.com/SableClient/tauri-plugin-app-icon/tar.gz/bab80ae17f6d018702e472c539d0f02eba480c21} + version: 0.1.0 + + teex@1.0.1: + resolution: {integrity: sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==} + temp-dir@2.0.0: resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} engines: {node: '>=8'} @@ -4751,40 +4985,47 @@ packages: resolution: {integrity: sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==} engines: {node: '>=10'} - terser@5.46.1: - resolution: {integrity: sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==} + terser@5.49.0: + resolution: {integrity: sha512-SNiDnXyHSrxVcIOtVbULzcTmniUiwcV7Nwdyj1twVubeTmbjoa8p69KKDpfkdoOavuM4/GRm1+ykI8qqnavHoA==} engines: {node: '>=10'} hasBin: true - tiny-invariant@1.3.1: - resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==} + testcontainers@12.0.4: + resolution: {integrity: sha512-QIR/8xF1+F/26cIM+9B4yyxNTbKJxAv3hygZyhPRgZ8Q2AhlPZjDdpXRuk16V37X4bgJRI3hXFhoEICMBA7Adg==} + + text-decoder@1.2.7: + resolution: {integrity: sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==} tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@1.0.4: - resolution: {integrity: sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==} + tinyexec@1.2.4: + resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} engines: {node: '>=18'} - tinyglobby@0.2.15: - resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} tinypool@2.1.0: resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==} engines: {node: ^20.0.0 || >=22.0.0} - tinyrainbow@3.1.0: - resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + tinyrainbow@3.1.1: + resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==} engines: {node: '>=14.0.0'} - tldts-core@7.0.25: - resolution: {integrity: sha512-ZjCZK0rppSBu7rjHYDYsEaMOIbbT+nWF57hKkv4IUmZWBNrBWBOjIElc0mKRgLM8bm7x/BBlof6t2gi/Oq/Asw==} + tldts-core@7.4.9: + resolution: {integrity: sha512-DxKfPBI52p2msTEu7MPhdpdDTBhhVQg1a/8PjQckeyAvO13eMYElX545grIp6nnTGIMZlRvFZPvFhvI/WIz2Vg==} - tldts@7.0.25: - resolution: {integrity: sha512-keinCnPbwXEUG3ilrWQZU+CqcTTzHq9m2HhoUP2l7Xmi8l1LuijAXLpAJ5zRW+ifKTNscs4NdCkfkDCBYm352w==} + tldts@7.4.9: + resolution: {integrity: sha512-3kZ8wQQ/k5DrChD4X4FVvr2D7E5uoRgAqkPyLpSCGUvqOvqu+JEdr3mwMUaVWb+vMHZaKhF5fp2PBigKsui7hA==} hasBin: true + tmp@0.2.7: + resolution: {integrity: sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==} + engines: {node: '>=14.14'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -4793,16 +5034,13 @@ packages: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} - tough-cookie@6.0.1: - resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==} + tough-cookie@6.0.2: + resolution: {integrity: sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==} engines: {node: '>=16'} tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - tr46@1.0.1: - resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} - tr46@6.0.0: resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} engines: {node: '>=20'} @@ -4810,6 +5048,9 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tweetnacl@0.14.5: + resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} + type-fest@0.16.0: resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==} engines: {node: '>=10'} @@ -4826,35 +5067,49 @@ packages: resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} engines: {node: '>= 0.4'} - typed-array-length@1.0.7: - resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + typed-array-length@1.0.8: + resolution: {integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==} engines: {node: '>= 0.4'} - typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} - engines: {node: '>=14.17'} + typed-emitter@2.1.0: + resolution: {integrity: sha512-g/KzbYKbH5C2vPkaXGu8DJlHrGKHLsM25Zg9WuC9pMGfuvT+X25tZQWo5fK1BjBm8+UrVE9LDCvaY0CQk+fXDA==} + + typescript@7.0.2: + resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==} + engines: {node: '>=16.20.0'} hasBin: true - ua-parser-js@1.0.41: - resolution: {integrity: sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug==} + ua-is-frozen@0.1.2: + resolution: {integrity: sha512-RwKDW2p3iyWn4UbaxpP2+VxwqXh0jpvdxsYpZ5j/MLLiQOfbsV5shpgQiw93+KMYQPcteeMQ289MaAFzs3G9pw==} + + ua-parser-js@2.0.10: + resolution: {integrity: sha512-t+3Ktbq0Ies2vaSezfOaWiolH4OigQIO1dk+1xDpOydB1COVPocVYOrEV5rqZ0kFY9XYG1v9LutCyMgYBpABcw==} hasBin: true - ufo@1.6.3: - resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==} + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + + unbash@4.0.4: + resolution: {integrity: sha512-60m9IVGbavD6jholbxt0jVBXZkEB/HsMZq7Tyaghseve2/Sf0zQRAIfWsD34sde+DKP2tBxJS2wP88ZM0D1FhA==} + engines: {node: '>=14'} unbox-primitive@1.1.0: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} - undici-types@7.16.0: - resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - undici@7.24.3: - resolution: {integrity: sha512-eJdUmK/Wrx2d+mnWWmwwLRyA7OQCkLap60sk3dOK4ViZR7DKwwptwuIvFBg2HaiP9ESaEdhtpSymQPvytpmkCA==} + undici-types@7.18.2: + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + + undici@7.29.0: + resolution: {integrity: sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==} engines: {node: '>=20.18.1'} - unenv@2.0.0-rc.24: - resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} + undici@8.9.0: + resolution: {integrity: sha512-aWZpUj7XoGonMClx4gdDRfgBjqeA+F473aDmROQQbM9n6PRfK/u1q/a0X4wMTgcHfT8H6fpbt98PFuDUwFg2YA==} + engines: {node: '>=22.19.0'} unhomoglyph@1.0.6: resolution: {integrity: sha512-7uvcWI3hWshSADBu4JpnyYbTVc7YlhF5GDW/oPD5AxIxl34k4wXR3WDkPnzLxkN32LiTCTKMQLtKVZiwki3zGg==} @@ -4898,23 +5153,30 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - uuid@10.0.0: - resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} - hasBin: true + usehooks-ts@3.1.1: + resolution: {integrity: sha512-I4diPp9Cq6ieSUH2wu+fDAVQO43xwtulo+fKEidHUwZPnYImbtkTjzIJYcDcJqxgmX31GVqNFURodvcgHcW0pA==} + engines: {node: '>=16.15.0'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 || ^19 || ^19.0.0-rc - uuid@13.0.0: - resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==} - hasBin: true + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - virtua@0.48.8: - resolution: {integrity: sha512-jpsxOw5V4B6hg44JePRLo9DL0TV7N1lBEVtPjKpAJebXyhI2s9lfiXJESaLapNtr3vtiSk/pWHiLf7B2a6UcgQ==} + virtua@0.50.2: + resolution: {integrity: sha512-hbIe+T0s/v6GHymFxu5S0j0Y6yLR4aZ3P59XWIxlz212SMDk6lN2qorbn1c9sCJ9BZb+u4hMWmY6XjKqTkfs1g==} peerDependencies: + '@angular/common': '>=20.0.0' + '@angular/core': '>=20.0.0' react: '>=16.14.0' react-dom: '>=16.14.0' solid-js: '>=1.0' svelte: '>=5.0' vue: '>=3.2' peerDependenciesMeta: + '@angular/common': + optional: true + '@angular/core': + optional: true react: optional: true react-dom: @@ -4926,51 +5188,47 @@ packages: vue: optional: true - vite-node@3.2.4: - resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + vite-node@6.0.0: + resolution: {integrity: sha512-oj4PVrT+pDh6GYf5wfUXkcZyekYS8kKPfLPXVl8qe324Ec6l4K2DUKNadRbZ3LQl0qGcDz+PyOo7ZAh00Y+JjQ==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - vite-plugin-compression2@2.5.0: - resolution: {integrity: sha512-bHxtBibPxxSn5eZSe0IAzvYucP/hg8Bz8ppjbH7lndU5kIHT+92qTkB4z9xWYfnyV0YHuir1SjOuyO0fzU4Vgg==} + vite-plugin-compression2@2.5.3: + resolution: {integrity: sha512-ItPgqQWkcnBbVw7is9OKwiZ8v6+ju9rYROl5Lp6QfQDEx/d55AwJQb/KLpsQqsU9HoigYBsZ8tK6I02UwJNvEw==} - vite-plugin-pwa@1.2.0: - resolution: {integrity: sha512-a2xld+SJshT9Lgcv8Ji4+srFJL4k/1bVbd1x06JIkvecpQkwkvCncD1+gSzcdm3s+owWLpMJerG3aN5jupJEVw==} + vite-plugin-pwa@1.3.0: + resolution: {integrity: sha512-c5kMgN+ITrOtHXp8PAtk2uOIEea6XjP/unCGxOWWBzQ6qa65qj/awHg0wf+QF9E/2u9vh86LqxPwzEPNbM2r5A==} engines: {node: '>=16.0.0'} peerDependencies: '@vite-pwa/assets-generator': ^1.0.0 - vite: ^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 - workbox-build: ^7.4.0 - workbox-window: ^7.4.0 + vite: ^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + workbox-build: ^7.4.1 + workbox-window: ^7.4.1 peerDependenciesMeta: '@vite-pwa/assets-generator': optional: true - vite-plugin-static-copy@3.2.0: - resolution: {integrity: sha512-g2k9z8B/1Bx7D4wnFjPLx9dyYGrqWMLTpwTtPHhcU+ElNZP2O4+4OsyaficiDClus0dzVhdGvoGFYMJxoXZ12Q==} - engines: {node: ^18.0.0 || >=20.0.0} - peerDependencies: - vite: ^5.0.0 || ^6.0.0 || ^7.0.0 - - vite-plugin-svgr@4.5.0: - resolution: {integrity: sha512-W+uoSpmVkSmNOGPSsDCWVW/DDAyv+9fap9AZXBvWiQqrboJ08j2vh0tFxTD/LjwqwAd3yYSVJgm54S/1GhbdnA==} + vite-plugin-static-copy@4.1.1: + resolution: {integrity: sha512-GrlA8YklrAfSyxJ4M3fdQLOo9oNkp56IM9FYgX/WtEgeIFkPwhu4wzpufBCIuNKCa6Fn77FkRdYxkHqV0FwjAw==} + engines: {node: ^22.0.0 || >=24.0.0} peerDependencies: - vite: '>=2.6.0' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 - vite-plugin-top-level-await@1.6.0: - resolution: {integrity: sha512-bNhUreLamTIkoulCR9aDXbTbhLk6n1YE8NJUTTxl5RYskNRtzOR0ASzSjBVRtNdjIfngDXo11qOsybGLNsrdww==} + vite-plugin-svgr@5.2.0: + resolution: {integrity: sha512-qj2eAKF8C6PZWemVTvQA0xgQIcP1hHU6Buh7fl6BhvayWwnuxE+z417miKxeDvRWbDrupQ1oK99hfxElopJ3sQ==} peerDependencies: - vite: '>=2.8' + vite: '>=3.0.0' - vite@7.3.1: - resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} + vite@8.2.0: + resolution: {integrity: sha512-pn+CFpM0lwDeKwmOq1ZaBK/9sjorZcgqxki6MbY/jPEVd9vichIlmlD4HmQ5wdP5EgqQCFRaACBxMC7uEGc6lQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.4.0 + esbuild: '>=0.28.1' jiti: '>=1.21.0' less: ^4.0.0 - lightningcss: ^1.21.0 sass: ^1.70.0 sass-embedded: ^1.70.0 stylus: '>=0.54.8' @@ -4981,12 +5239,14 @@ packages: peerDependenciesMeta: '@types/node': optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true jiti: optional: true less: optional: true - lightningcss: - optional: true sass: optional: true sass-embedded: @@ -5002,21 +5262,23 @@ packages: yaml: optional: true - vitest@4.1.0: - resolution: {integrity: sha512-YbDrMF9jM2Lqc++2530UourxZHmkKLxrs4+mYhEwqWS97WJ7wOYEkcr+QfRgJ3PW9wz3odRijLZjHEaRLTNbqw==} + vitest@4.1.10: + resolution: {integrity: sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.0 - '@vitest/browser-preview': 4.1.0 - '@vitest/browser-webdriverio': 4.1.0 - '@vitest/ui': 4.1.0 + '@vitest/browser-playwright': 4.1.10 + '@vitest/browser-preview': 4.1.10 + '@vitest/browser-webdriverio': 4.1.10 + '@vitest/coverage-istanbul': 4.1.10 + '@vitest/coverage-v8': 4.1.10 + '@vitest/ui': 4.1.10 happy-dom: '*' jsdom: '*' - vite: ^6.0.0 || ^7.0.0 || ^8.0.0-0 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: '@edge-runtime/vm': optional: true @@ -5030,6 +5292,10 @@ packages: optional: true '@vitest/browser-webdriverio': optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true '@vitest/ui': optional: true happy-dom: @@ -5037,9 +5303,8 @@ packages: jsdom: optional: true - void-elements@3.1.0: - resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} - engines: {node: '>=0.10.0'} + w3c-keyname@2.2.8: + resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} w3c-xmlserializer@5.0.0: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} @@ -5052,13 +5317,14 @@ packages: webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - webidl-conversions@4.0.2: - resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} - webidl-conversions@8.0.1: resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} engines: {node: '>=20'} + webrtc-adapter@9.0.6: + resolution: {integrity: sha512-CHbl2ZQbxx164IgWRgzJno4hWtM4tFbRam1QfI3Yxhs3w/DvqluVxVWeXs3oL5/fbGkSNLKo0Ty5MgUWceNhog==} + engines: {node: '>=6.0.0', npm: '>=3.10.0'} + whatwg-mimetype@5.0.0: resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==} engines: {node: '>=20'} @@ -5067,12 +5333,13 @@ packages: resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + whatwg-url@17.1.0: + resolution: {integrity: sha512-3GeworPmc2ZfEEHP7lEbUfBX/L75wdEsi0rLNhXcXxnoN5jyq0SL5gCy06SGW2cyTIZdTvWIDQNQoza++vKeaw==} + engines: {node: ^22.14.0 || >=24.0.0} + whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - whatwg-url@7.1.0: - resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} - which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -5085,8 +5352,8 @@ packages: resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} engines: {node: '>= 0.4'} - which-typed-array@1.1.20: - resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + which-typed-array@1.1.22: + resolution: {integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==} engines: {node: '>= 0.4'} which@2.0.2: @@ -5099,81 +5366,65 @@ packages: engines: {node: '>=8'} hasBin: true - workbox-background-sync@7.4.0: - resolution: {integrity: sha512-8CB9OxKAgKZKyNMwfGZ1XESx89GryWTfI+V5yEj8sHjFH8MFelUwYXEyldEK6M6oKMmn807GoJFUEA1sC4XS9w==} + workbox-background-sync@7.4.1: + resolution: {integrity: sha512-HhT7KE8tOWDm02wRNshXUnUPofMlhenF2DBdUnDPOubhizzPeItkYTmAB6td1Z2cjYPa98vzEiPLEuzn5hN66g==} - workbox-broadcast-update@7.4.0: - resolution: {integrity: sha512-+eZQwoktlvo62cI0b+QBr40v5XjighxPq3Fzo9AWMiAosmpG5gxRHgTbGGhaJv/q/MFVxwFNGh/UwHZ/8K88lA==} + workbox-broadcast-update@7.4.1: + resolution: {integrity: sha512-uAlgslKLvbQY+suirIdnBCSYrcgBhjp81Nj4l1lj/Jmj0MJO2CJERnCJjT0GFVwmReV0N+zs78K6gqd5gr9/+A==} - workbox-build@7.4.0: - resolution: {integrity: sha512-Ntk1pWb0caOFIvwz/hfgrov/OJ45wPEhI5PbTywQcYjyZiVhT3UrwwUPl6TRYbTm4moaFYithYnl1lvZ8UjxcA==} + workbox-build@7.4.1: + resolution: {integrity: sha512-SDhxIvEAde9Gy/5w4Yo1Jh/M49Z0qE3q0oteyE8zGq0DScxFqVBcCtIXFuLtmtxRQZCMbf0prco4VyEu3KBQuw==} engines: {node: '>=20.0.0'} - workbox-cacheable-response@7.4.0: - resolution: {integrity: sha512-0Fb8795zg/x23ISFkAc7lbWes6vbw34DGFIMw31cwuHPgDEC/5EYm6m/ZkylLX0EnEbbOyOCLjKgFS/Z5g0HeQ==} + workbox-cacheable-response@7.4.1: + resolution: {integrity: sha512-8xaFoJdDc2OjrlbbL3gEeBO1WKcMwRqwLRupgqahYXu75yXajPLuwrbXMrIGZuWYXrQwk0xDjOxZ/ujCy/oJYw==} - workbox-core@7.4.0: - resolution: {integrity: sha512-6BMfd8tYEnN4baG4emG9U0hdXM4gGuDU3ectXuVHnj71vwxTFI7WOpQJC4siTOlVtGqCUtj0ZQNsrvi6kZZTAQ==} + workbox-core@7.4.1: + resolution: {integrity: sha512-DT+vu46eh/2vRsSHTY4Xmc32Z1rr9PRlQUXr1Dx30ZuXRWwOsvZgGgcwxcasubQLQmbTNYZjv44LkBAQ4tT5tQ==} - workbox-expiration@7.4.0: - resolution: {integrity: sha512-V50p4BxYhtA80eOvulu8xVfPBgZbkxJ1Jr8UUn0rvqjGhLDqKNtfrDfjJKnLz2U8fO2xGQJTx/SKXNTzHOjnHw==} + workbox-expiration@7.4.1: + resolution: {integrity: sha512-lRKUF7b+OGbeXkQk1s6MHXOa3d7Xxf7Of31W6c6hCfipfIyrtdWZ89stq21AHZMaoG7VNFoHply4Ox+rU31TWg==} - workbox-google-analytics@7.4.0: - resolution: {integrity: sha512-MVPXQslRF6YHkzGoFw1A4GIB8GrKym/A5+jYDUSL+AeJw4ytQGrozYdiZqUW1TPQHW8isBCBtyFJergUXyNoWQ==} + workbox-google-analytics@7.4.1: + resolution: {integrity: sha512-Mks1JwLEt++ZAkF6sS1OpSh9RtAMIsiDgRpK+codiHGIPXeaUOgi4cPc3GFadUl8V5QPeypEk8Oxgl3HlwVzHw==} - workbox-navigation-preload@7.4.0: - resolution: {integrity: sha512-etzftSgdQfjMcfPgbfaZCfM2QuR1P+4o8uCA2s4rf3chtKTq/Om7g/qvEOcZkG6v7JZOSOxVYQiOu6PbAZgU6w==} + workbox-navigation-preload@7.4.1: + resolution: {integrity: sha512-C4KVsjPcYKJOhr631AxR9XoG2rLF3QiTk5aMv36MXOjtWvm8axwNFAtKUPGsWUwLXXAMgYM1En7fsvndaXeXRQ==} - workbox-precaching@7.4.0: - resolution: {integrity: sha512-VQs37T6jDqf1rTxUJZXRl3yjZMf5JX/vDPhmx2CPgDDKXATzEoqyRqhYnRoxl6Kr0rqaQlp32i9rtG5zTzIlNg==} + workbox-precaching@7.4.1: + resolution: {integrity: sha512-cdr/9qByww7yzEp7zg/qI4ukUrrNjQLgN+ONQRpjy/VqGQXwkgHwr00KksGJK8v0VifwDXBb8a4cWNZH71jn3Q==} - workbox-range-requests@7.4.0: - resolution: {integrity: sha512-3Vq854ZNuP6Y0KZOQWLaLC9FfM7ZaE+iuQl4VhADXybwzr4z/sMmnLgTeUZLq5PaDlcJBxYXQ3U91V7dwAIfvw==} + workbox-range-requests@7.4.1: + resolution: {integrity: sha512-7i2oxAUE82gHdAJBCAQ04JzNOdRPqzuOzGfoUyJpFSmeqBNYGPrAH8GPoPjUQTfp+NycwrD2H68VtuF8qxv0vQ==} - workbox-recipes@7.4.0: - resolution: {integrity: sha512-kOkWvsAn4H8GvAkwfJTbwINdv4voFoiE9hbezgB1sb/0NLyTG4rE7l6LvS8lLk5QIRIto+DjXLuAuG3Vmt3cxQ==} + workbox-recipes@7.4.1: + resolution: {integrity: sha512-gnbVfmV4/TtmQaM4x9AtuXhcdstJsep3XMVeztOrQVPT+R6+6DeBjGTCQ7fFCXm+4GEHUA5VEBTyi5+4gWGeog==} - workbox-routing@7.4.0: - resolution: {integrity: sha512-C/ooj5uBWYAhAqwmU8HYQJdOjjDKBp9MzTQ+otpMmd+q0eF59K+NuXUek34wbL0RFrIXe/KKT+tUWcZcBqxbHQ==} + workbox-routing@7.4.1: + resolution: {integrity: sha512-yubJGErZOusuidAenaL5ypfhQOa7urxP/f8E0ws7FPb4039RiWXUWBAyUkmUoOL/BcQGen3h0J8872d51IYxtA==} - workbox-strategies@7.4.0: - resolution: {integrity: sha512-T4hVqIi5A4mHi92+5EppMX3cLaVywDp8nsyUgJhOZxcfSV/eQofcOA6/EMo5rnTNmNTpw0rUgjAI6LaVullPpg==} + workbox-strategies@7.4.1: + resolution: {integrity: sha512-GZxpaw9NbmOelj7667uZ2kpk5BFpOGbO4X0qjwh5ls8XQ8C+Lha5LQchTiUzsTFSS+NlUpftYAyOVXvQUrcqOQ==} - workbox-streams@7.4.0: - resolution: {integrity: sha512-QHPBQrey7hQbnTs5GrEVoWz7RhHJXnPT+12qqWM378orDMo5VMJLCkCM1cnCk+8Eq92lccx/VgRZ7WAzZWbSLg==} + workbox-streams@7.4.1: + resolution: {integrity: sha512-HWWtraKUbJknd9kgqGcpQ3G114HOPYvqs8HaJMDs2ebLNAimDkVDaWfAXE6Ybl+m8U6KsCE6pWyLYuigWmnAXw==} - workbox-sw@7.4.0: - resolution: {integrity: sha512-ltU+Kr3qWR6BtbdlMnCjobZKzeV1hN+S6UvDywBrwM19TTyqA03X66dzw1tEIdJvQ4lYKkBFox6IAEhoSEZ8Xw==} + workbox-sw@7.4.1: + resolution: {integrity: sha512-fez5f2DUlDJWTFYkCWQpY10N8gtztd849NswCbVFk0QlcSM4HT5A8x4g4ii650yem4I8tHY0R7JZahwp3ltIPw==} - workbox-window@7.4.0: - resolution: {integrity: sha512-/bIYdBLAVsNR3v7gYGaV4pQW3M3kEPx5E8vDxGvxo6khTrGtSSCS7QiFKv9ogzBgZiy0OXLP9zO28U/1nF1mfw==} + workbox-window@7.4.1: + resolution: {integrity: sha512-notZDH2u8VXaqyuD7xaqIfEFi6SRM4SUSd7ewe9PDsVqADuepxX2ZMY3uvuZGxzY5ZOsGC/vD3A/3smFtJt4/A==} - workerd@1.20260310.1: - resolution: {integrity: sha512-yawXhypXXHtArikJj15HOMknNGikpBbSg2ZDe6lddUbqZnJXuCVSkgc/0ArUeVMG1jbbGvpst+REFtKwILvRTQ==} - engines: {node: '>=16'} - hasBin: true + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} - wrangler@4.72.0: - resolution: {integrity: sha512-bKkb8150JGzJZJWiNB2nu/33smVfawmfYiecA6rW4XH7xS23/jqMbgpdelM34W/7a1IhR66qeQGVqTRXROtAZg==} - engines: {node: '>=20.0.0'} - hasBin: true - peerDependencies: - '@cloudflare/workers-types': ^4.20260310.1 - peerDependenciesMeta: - '@cloudflare/workers-types': - optional: true + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} - ws@8.18.0: - resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} xml-name-validator@5.0.0: resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} @@ -5182,744 +5433,751 @@ packages: xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yaml@2.8.2: - resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} + yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} engines: {node: '>= 14.6'} hasBin: true + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.3: + resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==} + engines: {node: '>=12'} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - youch-core@0.3.3: - resolution: {integrity: sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==} + zip-stream@6.0.1: + resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} + engines: {node: '>= 14'} - youch@4.1.0-beta.10: - resolution: {integrity: sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==} - - zod@4.3.6: - resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} snapshots: - '@adobe/css-tools@4.4.4': {} + '@adobe/css-tools@4.5.0': {} - '@apideck/better-ajv-errors@0.3.6(ajv@8.18.0)': + '@apideck/better-ajv-errors@0.3.7(ajv@8.20.0)': dependencies: - ajv: 8.18.0 - json-schema: 0.4.0 + ajv: 8.20.0 jsonpointer: 5.0.1 leven: 3.1.0 - '@arborium/arborium@2.16.0': {} + '@arborium/arborium@2.18.1': {} - '@asamuzakjp/css-color@5.0.1': + '@asamuzakjp/css-color@6.0.5': dependencies: - '@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - '@csstools/css-color-parser': 4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-calc': 3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.10(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - lru-cache: 11.2.7 + lru-cache: 11.5.2 - '@asamuzakjp/dom-selector@7.0.3': + '@asamuzakjp/dom-selector@8.3.0': dependencies: - '@asamuzakjp/nwsapi': 2.3.9 bidi-js: 1.0.3 css-tree: 3.2.1 is-potential-custom-element-name: 1.0.1 - lru-cache: 11.2.7 - - '@asamuzakjp/nwsapi@2.3.9': {} + lru-cache: 11.5.2 - '@atlaskit/pragmatic-drag-and-drop-auto-scroll@1.4.0': + '@atlaskit/pragmatic-drag-and-drop-auto-scroll@3.0.0': dependencies: - '@atlaskit/pragmatic-drag-and-drop': 1.7.9 - '@babel/runtime': 7.28.6 + '@atlaskit/pragmatic-drag-and-drop': 2.0.2 + '@babel/runtime': 7.29.7 - '@atlaskit/pragmatic-drag-and-drop-hitbox@1.1.0': + '@atlaskit/pragmatic-drag-and-drop-hitbox@2.0.0': dependencies: - '@atlaskit/pragmatic-drag-and-drop': 1.7.9 - '@babel/runtime': 7.28.6 + '@atlaskit/pragmatic-drag-and-drop': 2.0.2 + '@babel/runtime': 7.29.7 - '@atlaskit/pragmatic-drag-and-drop@1.7.9': + '@atlaskit/pragmatic-drag-and-drop@2.0.2': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.7 bind-event-listener: 3.0.0 raf-schd: 4.0.3 - '@babel/code-frame@7.29.0': + '@babel/code-frame@7.29.7': dependencies: - '@babel/helper-validator-identifier': 7.28.5 + '@babel/helper-validator-identifier': 7.29.7 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.29.0': {} + '@babel/compat-data@7.29.7': {} - '@babel/core@7.29.0': + '@babel/core@7.29.7(supports-color@10.2.2)': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helpers': 7.28.6 - '@babel/parser': 7.29.0 - '@babel/template': 7.28.6 - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) + '@babel/types': 7.29.7 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/generator@7.29.1': + '@babel/generator@7.29.7': dependencies: - '@babel/parser': 7.29.0 - '@babel/types': 7.29.0 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 - '@babel/helper-annotate-as-pure@7.27.3': + '@babel/helper-annotate-as-pure@7.29.7': dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.29.7 - '@babel/helper-compilation-targets@7.28.6': + '@babel/helper-compilation-targets@7.29.7': dependencies: - '@babel/compat-data': 7.29.0 - '@babel/helper-validator-option': 7.27.1 - browserslist: 4.28.1 + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.28.7 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.28.6(@babel/core@7.29.0)': + '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-member-expression-to-functions': 7.28.5 - '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.29.0 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-member-expression-to-functions': 7.29.7(supports-color@10.2.2) + '@babel/helper-optimise-call-expression': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2) + '@babel/traverse': 7.29.7(supports-color@10.2.2) semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@7.29.0)': + '@babel/helper-create-regexp-features-plugin@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-annotate-as-pure': 7.29.7 regexpu-core: 6.4.0 semver: 6.3.1 - '@babel/helper-define-polyfill-provider@0.6.8(@babel/core@7.29.0)': + '@babel/helper-define-polyfill-provider@0.6.8(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 - debug: 4.4.3 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + debug: 4.4.3(supports-color@10.2.2) lodash.debounce: 4.0.8 - resolve: 1.22.11 + resolve: 1.22.12 transitivePeerDependencies: - supports-color - '@babel/helper-globals@7.28.0': {} + '@babel/helper-globals@7.29.7': {} - '@babel/helper-member-expression-to-functions@7.28.5': + '@babel/helper-member-expression-to-functions@7.29.7(supports-color@10.2.2)': dependencies: - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + '@babel/traverse': 7.29.7(supports-color@10.2.2) + '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helper-module-imports@7.28.6': + '@babel/helper-module-imports@7.29.7(supports-color@10.2.2)': dependencies: - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + '@babel/traverse': 7.29.7(supports-color@10.2.2) + '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-imports': 7.28.6 - '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.29.0 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-imports': 7.29.7(supports-color@10.2.2) + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/helper-optimise-call-expression@7.27.1': + '@babel/helper-optimise-call-expression@7.29.7': dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.29.7 - '@babel/helper-plugin-utils@7.28.6': {} + '@babel/helper-plugin-utils@7.29.7': {} - '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.29.0)': + '@babel/helper-remap-async-to-generator@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-wrap-function': 7.28.6 - '@babel/traverse': 7.29.0 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-wrap-function': 7.29.7(supports-color@10.2.2) + '@babel/traverse': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/helper-replace-supers@7.28.6(@babel/core@7.29.0)': + '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-member-expression-to-functions': 7.28.5 - '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.29.0 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-member-expression-to-functions': 7.29.7(supports-color@10.2.2) + '@babel/helper-optimise-call-expression': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + '@babel/helper-skip-transparent-expression-wrappers@7.29.7(supports-color@10.2.2)': dependencies: - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + '@babel/traverse': 7.29.7(supports-color@10.2.2) + '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-string-parser@7.29.7': {} - '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-identifier@7.29.7': {} - '@babel/helper-validator-option@7.27.1': {} + '@babel/helper-validator-option@7.29.7': {} - '@babel/helper-wrap-function@7.28.6': + '@babel/helper-wrap-function@7.29.7(supports-color@10.2.2)': dependencies: - '@babel/template': 7.28.6 - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) + '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helpers@7.28.6': + '@babel/helpers@7.29.7': dependencies: - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 - '@babel/parser@7.29.0': + '@babel/parser@7.29.7': dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.29.7 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.29.0)': + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/traverse': 7.29.0 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2) + '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/traverse': 7.29.0 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.0)': + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.7(supports-color@10.2.2) - '@babel/plugin-syntax-import-assertions@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-import-assertions@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-import-attributes@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-arrow-functions@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-async-generator-functions@7.29.0(@babel/core@7.29.0)': + '@babel/plugin-transform-async-generator-functions@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.29.0) - '@babel/traverse': 7.29.0 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-remap-async-to-generator': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/traverse': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-async-to-generator@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-async-to-generator@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-imports': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.29.0) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-imports': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-remap-async-to-generator': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-block-scoped-functions@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-block-scoping@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-block-scoping@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-class-properties@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-class-properties@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-class-static-block@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-class-static-block@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-classes@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-classes@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-globals': 7.28.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) - '@babel/traverse': 7.29.0 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/traverse': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-computed-properties@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-computed-properties@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/template': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/template': 7.29.7 - '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.29.0)': + '@babel/plugin-transform-destructuring@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/traverse': 7.29.0 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-dotall-regex@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-dotall-regex@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-duplicate-keys@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0(@babel/core@7.29.0)': + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-dynamic-import@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-explicit-resource-management@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-explicit-resource-management@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-exponentiation-operator@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-exponentiation-operator@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-export-namespace-from@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-for-of@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-function-name@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/traverse': 7.29.0 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-json-strings@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-json-strings@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-literals@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-literals@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-logical-assignment-operators@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-logical-assignment-operators@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-member-expression-literals@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-modules-amd@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-modules-commonjs@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-systemjs@7.29.0(@babel/core@7.29.0)': + '@babel/plugin-transform-modules-systemjs@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.29.0 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-modules-umd@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-named-capturing-groups-regex@7.29.0(@babel/core@7.29.0)': + '@babel/plugin-transform-named-capturing-groups-regex@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-new-target@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-nullish-coalescing-operator@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-nullish-coalescing-operator@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-numeric-separator@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-numeric-separator@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-object-rest-spread@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-object-rest-spread@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) - '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) - '@babel/traverse': 7.29.0 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-parameters': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/traverse': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-object-super@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-optional-catch-binding@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-optional-catch-binding@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-optional-chaining@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-optional-chaining@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.29.0)': + '@babel/plugin-transform-parameters@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-private-methods@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-private-methods@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-private-property-in-object@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-private-property-in-object@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-property-literals@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-regenerator@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-regenerator@7.29.0(@babel/core@7.29.0)': + '@babel/plugin-transform-regexp-modifiers@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-regexp-modifiers@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-reserved-words@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-shorthand-properties@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-spread@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-transform-spread@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-transform-unicode-property-regex@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-transform-unicode-sets-regex@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/preset-env@7.29.2(@babel/core@7.29.0)': - dependencies: - '@babel/compat-data': 7.29.0 - '@babel/core': 7.29.0 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.28.5(@babel/core@7.29.0) - '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.0) - '@babel/plugin-syntax-import-assertions': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.29.0) - '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-async-generator-functions': 7.29.0(@babel/core@7.29.0) - '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-class-static-block': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-computed-properties': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) - '@babel/plugin-transform-dotall-regex': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0) - '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-explicit-resource-management': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-exponentiation-operator': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-json-strings': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-logical-assignment-operators': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-modules-systemjs': 7.29.0(@babel/core@7.29.0) - '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0) - '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-numeric-separator': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-optional-catch-binding': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) - '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-regenerator': 7.29.0(@babel/core@7.29.0) - '@babel/plugin-transform-regexp-modifiers': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-spread': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-unicode-property-regex': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-unicode-sets-regex': 7.28.6(@babel/core@7.29.0) - '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.29.0) - babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.0) - babel-plugin-polyfill-corejs3: 0.14.2(@babel/core@7.29.0) - babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.0) + '@babel/plugin-transform-sticky-regex@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-template-literals@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-typeof-symbol@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-unicode-escapes@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-unicode-property-regex@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-unicode-regex@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-unicode-sets-regex@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-import-assertions': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-import-attributes': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-arrow-functions': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-async-generator-functions': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-async-to-generator': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-block-scoped-functions': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-block-scoping': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-class-static-block': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-classes': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-computed-properties': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-dotall-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-duplicate-keys': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-dynamic-import': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-explicit-resource-management': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-exponentiation-operator': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-export-namespace-from': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-for-of': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-function-name': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-json-strings': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-literals': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-logical-assignment-operators': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-member-expression-literals': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-modules-amd': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-modules-systemjs': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-modules-umd': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-new-target': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-numeric-separator': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-object-rest-spread': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-object-super': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-optional-catch-binding': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-parameters': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-private-methods': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-private-property-in-object': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-property-literals': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-regenerator': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-regexp-modifiers': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-reserved-words': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-shorthand-properties': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-spread': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-sticky-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-template-literals': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-typeof-symbol': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-unicode-escapes': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-unicode-property-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-unicode-sets-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.29.7(supports-color@10.2.2)) + babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + babel-plugin-polyfill-corejs3: 0.14.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) core-js-compat: 3.49.0 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.29.0)': + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/types': 7.29.0 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/types': 7.29.7 esutils: 2.0.3 - '@babel/runtime@7.28.6': {} + '@babel/runtime@7.29.7': {} - '@babel/runtime@7.29.2': {} + '@babel/runtime@8.0.0': {} - '@babel/template@7.28.6': + '@babel/template@7.29.7': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/parser': 7.29.0 - '@babel/types': 7.29.0 + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 - '@babel/traverse@7.29.0': + '@babel/traverse@7.29.7(supports-color@10.2.2)': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.29.0 - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 - debug: 4.4.3 + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + debug: 4.4.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/types@7.29.0': + '@babel/types@7.29.7': dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@balena/dockerignore@1.0.2': {} '@bcoe/v8-coverage@1.0.2': {} @@ -5927,57 +6185,23 @@ snapshots: dependencies: css-tree: 3.2.1 - '@cloudflare/kv-asset-handler@0.4.2': {} - - '@cloudflare/unenv-preset@2.15.0(unenv@2.0.0-rc.24)(workerd@1.20260310.1)': - dependencies: - unenv: 2.0.0-rc.24 - optionalDependencies: - workerd: 1.20260310.1 - - '@cloudflare/vite-plugin@1.27.0(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2))(workerd@1.20260310.1)(wrangler@4.72.0)': - dependencies: - '@cloudflare/unenv-preset': 2.15.0(unenv@2.0.0-rc.24)(workerd@1.20260310.1) - miniflare: 4.20260310.0 - unenv: 2.0.0-rc.24 - vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2) - wrangler: 4.72.0 - ws: 8.18.0 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - workerd - - '@cloudflare/workerd-darwin-64@1.20260310.1': - optional: true - - '@cloudflare/workerd-darwin-arm64@1.20260310.1': - optional: true - - '@cloudflare/workerd-linux-64@1.20260310.1': - optional: true - - '@cloudflare/workerd-linux-arm64@1.20260310.1': - optional: true - - '@cloudflare/workerd-windows-64@1.20260310.1': - optional: true + '@bufbuild/protobuf@1.10.1': {} - '@cspotcode/source-map-support@0.8.1': + '@choochmeque/tauri-plugin-sharekit-api@0.4.0-rc.5': dependencies: - '@jridgewell/trace-mapping': 0.3.9 + '@tauri-apps/api': 2.11.1 - '@csstools/color-helpers@6.0.2': {} + '@csstools/color-helpers@6.1.0': {} - '@csstools/css-calc@3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + '@csstools/css-calc@3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/css-color-parser@4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + '@csstools/css-color-parser@4.1.10(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: - '@csstools/color-helpers': 6.0.2 - '@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/color-helpers': 6.1.0 + '@csstools/css-calc': 3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 @@ -5985,256 +6209,184 @@ snapshots: dependencies: '@csstools/css-tokenizer': 4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.1.1(css-tree@3.2.1)': + '@csstools/css-syntax-patches-for-csstree@1.1.7(css-tree@3.2.1)': optionalDependencies: css-tree: 3.2.1 '@csstools/css-tokenizer@4.0.0': {} - '@emnapi/core@1.8.1': + '@emnapi/core@1.11.2': dependencies: - '@emnapi/wasi-threads': 1.1.0 + '@emnapi/wasi-threads': 1.2.2 tslib: 2.8.1 optional: true - '@emnapi/runtime@1.8.1': + '@emnapi/core@2.0.0-alpha.3': dependencies: + '@emnapi/wasi-threads': 2.0.1 tslib: 2.8.1 optional: true - '@emnapi/wasi-threads@1.1.0': + '@emnapi/runtime@1.11.2': dependencies: tslib: 2.8.1 optional: true - '@emotion/hash@0.9.2': {} - - '@esbuild-plugins/node-globals-polyfill@0.2.3(esbuild@0.27.3)': + '@emnapi/runtime@2.0.0-alpha.3': dependencies: - esbuild: 0.27.3 - - '@esbuild/aix-ppc64@0.27.3': - optional: true - - '@esbuild/android-arm64@0.27.3': - optional: true - - '@esbuild/android-arm@0.27.3': - optional: true - - '@esbuild/android-x64@0.27.3': - optional: true - - '@esbuild/darwin-arm64@0.27.3': - optional: true - - '@esbuild/darwin-x64@0.27.3': - optional: true - - '@esbuild/freebsd-arm64@0.27.3': - optional: true - - '@esbuild/freebsd-x64@0.27.3': - optional: true - - '@esbuild/linux-arm64@0.27.3': - optional: true - - '@esbuild/linux-arm@0.27.3': + tslib: 2.8.1 optional: true - '@esbuild/linux-ia32@0.27.3': + '@emnapi/wasi-threads@1.2.2': + dependencies: + tslib: 2.8.1 optional: true - '@esbuild/linux-loong64@0.27.3': + '@emnapi/wasi-threads@2.0.1': + dependencies: + tslib: 2.8.1 optional: true - '@esbuild/linux-mips64el@0.27.3': - optional: true + '@emotion/hash@0.9.2': {} - '@esbuild/linux-ppc64@0.27.3': + '@esbuild/aix-ppc64@0.28.1': optional: true - '@esbuild/linux-riscv64@0.27.3': + '@esbuild/android-arm64@0.28.1': optional: true - '@esbuild/linux-s390x@0.27.3': + '@esbuild/android-arm@0.28.1': optional: true - '@esbuild/linux-x64@0.27.3': + '@esbuild/android-x64@0.28.1': optional: true - '@esbuild/netbsd-arm64@0.27.3': + '@esbuild/darwin-arm64@0.28.1': optional: true - '@esbuild/netbsd-x64@0.27.3': + '@esbuild/darwin-x64@0.28.1': optional: true - '@esbuild/openbsd-arm64@0.27.3': + '@esbuild/freebsd-arm64@0.28.1': optional: true - '@esbuild/openbsd-x64@0.27.3': + '@esbuild/freebsd-x64@0.28.1': optional: true - '@esbuild/openharmony-arm64@0.27.3': + '@esbuild/linux-arm64@0.28.1': optional: true - '@esbuild/sunos-x64@0.27.3': + '@esbuild/linux-arm@0.28.1': optional: true - '@esbuild/win32-arm64@0.27.3': + '@esbuild/linux-ia32@0.28.1': optional: true - '@esbuild/win32-ia32@0.27.3': + '@esbuild/linux-loong64@0.28.1': optional: true - '@esbuild/win32-x64@0.27.3': + '@esbuild/linux-mips64el@0.28.1': optional: true - '@exodus/bytes@1.15.0': {} - - '@fontsource-variable/nunito@5.2.7': {} - - '@fontsource/space-mono@5.2.9': {} - - '@formatjs/ecma402-abstract@2.3.6': - dependencies: - '@formatjs/fast-memoize': 2.2.7 - '@formatjs/intl-localematcher': 0.6.2 - decimal.js: 10.6.0 - tslib: 2.8.1 - - '@formatjs/fast-memoize@2.2.7': - dependencies: - tslib: 2.8.1 - - '@formatjs/icu-messageformat-parser@2.11.4': - dependencies: - '@formatjs/ecma402-abstract': 2.3.6 - '@formatjs/icu-skeleton-parser': 1.8.16 - tslib: 2.8.1 - - '@formatjs/icu-skeleton-parser@1.8.16': - dependencies: - '@formatjs/ecma402-abstract': 2.3.6 - tslib: 2.8.1 - - '@formatjs/intl-localematcher@0.6.2': - dependencies: - tslib: 2.8.1 - - '@img/colour@1.1.0': {} - - '@img/sharp-darwin-arm64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@esbuild/linux-ppc64@0.28.1': optional: true - '@img/sharp-darwin-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.2.4 + '@esbuild/linux-riscv64@0.28.1': optional: true - '@img/sharp-libvips-darwin-arm64@1.2.4': + '@esbuild/linux-s390x@0.28.1': optional: true - '@img/sharp-libvips-darwin-x64@1.2.4': + '@esbuild/linux-x64@0.28.1': optional: true - '@img/sharp-libvips-linux-arm64@1.2.4': + '@esbuild/netbsd-arm64@0.28.1': optional: true - '@img/sharp-libvips-linux-arm@1.2.4': + '@esbuild/netbsd-x64@0.28.1': optional: true - '@img/sharp-libvips-linux-ppc64@1.2.4': + '@esbuild/openbsd-arm64@0.28.1': optional: true - '@img/sharp-libvips-linux-riscv64@1.2.4': + '@esbuild/openbsd-x64@0.28.1': optional: true - '@img/sharp-libvips-linux-s390x@1.2.4': + '@esbuild/openharmony-arm64@0.28.1': optional: true - '@img/sharp-libvips-linux-x64@1.2.4': + '@esbuild/sunos-x64@0.28.1': optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + '@esbuild/win32-arm64@0.28.1': optional: true - '@img/sharp-libvips-linuxmusl-x64@1.2.4': + '@esbuild/win32-ia32@0.28.1': optional: true - '@img/sharp-linux-arm64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.2.4 + '@esbuild/win32-x64@0.28.1': optional: true - '@img/sharp-linux-arm@0.34.5': + '@exodus/bytes@1.15.1(@noble/hashes@2.3.0)': optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.2.4 - optional: true + '@noble/hashes': 2.3.0 - '@img/sharp-linux-ppc64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.2.4 - optional: true + '@faker-js/faker@10.5.0': {} - '@img/sharp-linux-riscv64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-riscv64': 1.2.4 - optional: true + '@floating-ui/core@1.8.0': + dependencies: + '@floating-ui/utils': 0.2.12 - '@img/sharp-linux-s390x@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.2.4 - optional: true + '@floating-ui/dom@1.7.6': + dependencies: + '@floating-ui/core': 1.8.0 + '@floating-ui/utils': 0.2.12 - '@img/sharp-linux-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.2.4 - optional: true + '@floating-ui/utils@0.2.12': {} - '@img/sharp-linuxmusl-arm64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 - optional: true + '@fontsource-variable/nunito@5.3.0': {} - '@img/sharp-linuxmusl-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 - optional: true + '@fontsource/space-mono@5.3.0': {} - '@img/sharp-wasm32@0.34.5': + '@grpc/grpc-js@1.14.4': dependencies: - '@emnapi/runtime': 1.8.1 - optional: true - - '@img/sharp-win32-arm64@0.34.5': - optional: true + '@grpc/proto-loader': 0.8.1 + '@js-sdsl/ordered-map': 4.4.2 - '@img/sharp-win32-ia32@0.34.5': - optional: true + '@grpc/proto-loader@0.7.15': + dependencies: + lodash.camelcase: 4.3.0 + long: 5.3.2 + protobufjs: 7.6.5 + yargs: 17.7.3 - '@img/sharp-win32-x64@0.34.5': - optional: true + '@grpc/proto-loader@0.8.1': + dependencies: + lodash.camelcase: 4.3.0 + long: 5.3.2 + protobufjs: 7.6.5 + yargs: 17.7.3 - '@internationalized/date@3.12.0': + '@internationalized/date@3.12.2': dependencies: - '@swc/helpers': 0.5.19 + '@swc/helpers': 0.5.23 - '@internationalized/message@3.1.8': + '@internationalized/number@3.6.7': dependencies: - '@swc/helpers': 0.5.19 - intl-messageformat: 10.7.18 + '@swc/helpers': 0.5.23 - '@internationalized/number@3.6.5': + '@internationalized/string@3.2.9': dependencies: - '@swc/helpers': 0.5.19 + '@swc/helpers': 0.5.23 - '@internationalized/string@3.2.7': + '@isaacs/cliui@8.0.2': dependencies: - '@swc/helpers': 0.5.19 + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.2.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 '@isaacs/cliui@9.0.0': {} @@ -6262,1579 +6414,785 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping@0.3.9': + '@js-sdsl/ordered-map@4.4.2': {} + + '@kwsites/file-exists@1.1.1(supports-color@10.2.2)': dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 + debug: 4.4.3(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@livekit/components-core@0.12.14(livekit-client@2.21.0(@types/dom-mediacapture-record@1.0.22))(tslib@2.8.1)': + dependencies: + '@floating-ui/dom': 1.7.6 + livekit-client: 2.21.0(@types/dom-mediacapture-record@1.0.22) + loglevel: 1.9.1 + rxjs: 7.8.2 + tslib: 2.8.1 + + '@livekit/components-react@2.9.23(livekit-client@2.21.0(@types/dom-mediacapture-record@1.0.22))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(tslib@2.8.1)': + dependencies: + '@livekit/components-core': 0.12.14(livekit-client@2.21.0(@types/dom-mediacapture-record@1.0.22))(tslib@2.8.1) + clsx: 2.1.1 + events: 3.3.0 + jose: 6.2.9 + livekit-client: 2.21.0(@types/dom-mediacapture-record@1.0.22) + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + tslib: 2.8.1 + usehooks-ts: 3.1.1(react@19.2.8) + + '@livekit/mutex@1.1.1': {} + + '@livekit/protocol@1.50.4': + dependencies: + '@bufbuild/protobuf': 1.10.1 - '@juggle/resize-observer@3.4.0': {} + '@lottiefiles/dotlottie-react@0.19.15(react@19.2.8)': + dependencies: + '@lottiefiles/dotlottie-web': 0.79.2 + react: 19.2.8 + + '@lottiefiles/dotlottie-web@0.79.1': {} - '@matrix-org/matrix-sdk-crypto-wasm@15.3.0': {} + '@lottiefiles/dotlottie-web@0.79.2': {} - '@napi-rs/canvas-android-arm64@0.1.96': + '@matrix-org/matrix-sdk-crypto-wasm@18.4.0': {} + + '@napi-rs/canvas-android-arm64@1.0.3': optional: true - '@napi-rs/canvas-darwin-arm64@0.1.96': + '@napi-rs/canvas-darwin-arm64@1.0.3': optional: true - '@napi-rs/canvas-darwin-x64@0.1.96': + '@napi-rs/canvas-darwin-x64@1.0.3': optional: true - '@napi-rs/canvas-linux-arm-gnueabihf@0.1.96': + '@napi-rs/canvas-linux-arm-gnueabihf@1.0.3': optional: true - '@napi-rs/canvas-linux-arm64-gnu@0.1.96': + '@napi-rs/canvas-linux-arm64-gnu@1.0.3': optional: true - '@napi-rs/canvas-linux-arm64-musl@0.1.96': + '@napi-rs/canvas-linux-arm64-musl@1.0.3': optional: true - '@napi-rs/canvas-linux-riscv64-gnu@0.1.96': + '@napi-rs/canvas-linux-riscv64-gnu@1.0.3': optional: true - '@napi-rs/canvas-linux-x64-gnu@0.1.96': + '@napi-rs/canvas-linux-x64-gnu@1.0.3': optional: true - '@napi-rs/canvas-linux-x64-musl@0.1.96': + '@napi-rs/canvas-linux-x64-musl@1.0.3': optional: true - '@napi-rs/canvas-win32-arm64-msvc@0.1.96': + '@napi-rs/canvas-win32-arm64-msvc@1.0.3': optional: true - '@napi-rs/canvas-win32-x64-msvc@0.1.96': + '@napi-rs/canvas-win32-x64-msvc@1.0.3': optional: true - '@napi-rs/canvas@0.1.96': + '@napi-rs/canvas@1.0.3': optionalDependencies: - '@napi-rs/canvas-android-arm64': 0.1.96 - '@napi-rs/canvas-darwin-arm64': 0.1.96 - '@napi-rs/canvas-darwin-x64': 0.1.96 - '@napi-rs/canvas-linux-arm-gnueabihf': 0.1.96 - '@napi-rs/canvas-linux-arm64-gnu': 0.1.96 - '@napi-rs/canvas-linux-arm64-musl': 0.1.96 - '@napi-rs/canvas-linux-riscv64-gnu': 0.1.96 - '@napi-rs/canvas-linux-x64-gnu': 0.1.96 - '@napi-rs/canvas-linux-x64-musl': 0.1.96 - '@napi-rs/canvas-win32-arm64-msvc': 0.1.96 - '@napi-rs/canvas-win32-x64-msvc': 0.1.96 + '@napi-rs/canvas-android-arm64': 1.0.3 + '@napi-rs/canvas-darwin-arm64': 1.0.3 + '@napi-rs/canvas-darwin-x64': 1.0.3 + '@napi-rs/canvas-linux-arm-gnueabihf': 1.0.3 + '@napi-rs/canvas-linux-arm64-gnu': 1.0.3 + '@napi-rs/canvas-linux-arm64-musl': 1.0.3 + '@napi-rs/canvas-linux-riscv64-gnu': 1.0.3 + '@napi-rs/canvas-linux-x64-gnu': 1.0.3 + '@napi-rs/canvas-linux-x64-musl': 1.0.3 + '@napi-rs/canvas-win32-arm64-msvc': 1.0.3 + '@napi-rs/canvas-win32-x64-msvc': 1.0.3 optional: true - '@napi-rs/wasm-runtime@1.1.1': + '@napi-rs/wasm-runtime@1.2.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)': dependencies: - '@emnapi/core': 1.8.1 - '@emnapi/runtime': 1.8.1 - '@tybys/wasm-util': 0.10.1 + '@emnapi/core': 1.11.2 + '@emnapi/runtime': 1.11.2 + '@tybys/wasm-util': 0.10.3 optional: true - '@nodelib/fs.scandir@2.1.5': + '@napi-rs/wasm-runtime@1.2.1(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3)': dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} + '@emnapi/core': 2.0.0-alpha.3 + '@emnapi/runtime': 2.0.0-alpha.3 + '@tybys/wasm-util': 0.10.3 + optional: true - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.20.1 + '@noble/hashes@2.3.0': {} - '@oxc-resolver/binding-android-arm-eabi@11.19.1': + '@oxc-parser/binding-android-arm-eabi@0.142.0': optional: true - '@oxc-resolver/binding-android-arm64@11.19.1': + '@oxc-parser/binding-android-arm64@0.142.0': optional: true - '@oxc-resolver/binding-darwin-arm64@11.19.1': + '@oxc-parser/binding-darwin-arm64@0.142.0': optional: true - '@oxc-resolver/binding-darwin-x64@11.19.1': + '@oxc-parser/binding-darwin-x64@0.142.0': optional: true - '@oxc-resolver/binding-freebsd-x64@11.19.1': + '@oxc-parser/binding-freebsd-x64@0.142.0': optional: true - '@oxc-resolver/binding-linux-arm-gnueabihf@11.19.1': + '@oxc-parser/binding-linux-arm-gnueabihf@0.142.0': optional: true - '@oxc-resolver/binding-linux-arm-musleabihf@11.19.1': + '@oxc-parser/binding-linux-arm-musleabihf@0.142.0': optional: true - '@oxc-resolver/binding-linux-arm64-gnu@11.19.1': + '@oxc-parser/binding-linux-arm64-gnu@0.142.0': optional: true - '@oxc-resolver/binding-linux-arm64-musl@11.19.1': + '@oxc-parser/binding-linux-arm64-musl@0.142.0': optional: true - '@oxc-resolver/binding-linux-ppc64-gnu@11.19.1': + '@oxc-parser/binding-linux-ppc64-gnu@0.142.0': optional: true - '@oxc-resolver/binding-linux-riscv64-gnu@11.19.1': + '@oxc-parser/binding-linux-riscv64-gnu@0.142.0': optional: true - '@oxc-resolver/binding-linux-riscv64-musl@11.19.1': + '@oxc-parser/binding-linux-riscv64-musl@0.142.0': optional: true - '@oxc-resolver/binding-linux-s390x-gnu@11.19.1': + '@oxc-parser/binding-linux-s390x-gnu@0.142.0': optional: true - '@oxc-resolver/binding-linux-x64-gnu@11.19.1': + '@oxc-parser/binding-linux-x64-gnu@0.142.0': optional: true - '@oxc-resolver/binding-linux-x64-musl@11.19.1': + '@oxc-parser/binding-linux-x64-musl@0.142.0': optional: true - '@oxc-resolver/binding-openharmony-arm64@11.19.1': + '@oxc-parser/binding-openharmony-arm64@0.142.0': optional: true - '@oxc-resolver/binding-wasm32-wasi@11.19.1': + '@oxc-parser/binding-wasm32-wasi@0.142.0': dependencies: - '@napi-rs/wasm-runtime': 1.1.1 + '@emnapi/core': 1.11.2 + '@emnapi/runtime': 1.11.2 + '@napi-rs/wasm-runtime': 1.2.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) optional: true - '@oxc-resolver/binding-win32-arm64-msvc@11.19.1': + '@oxc-parser/binding-win32-arm64-msvc@0.142.0': optional: true - '@oxc-resolver/binding-win32-ia32-msvc@11.19.1': + '@oxc-parser/binding-win32-ia32-msvc@0.142.0': optional: true - '@oxc-resolver/binding-win32-x64-msvc@11.19.1': + '@oxc-parser/binding-win32-x64-msvc@0.142.0': optional: true - '@oxfmt/binding-android-arm-eabi@0.45.0': + '@oxc-project/types@0.142.0': {} + + '@oxc-resolver/binding-android-arm-eabi@11.24.2': optional: true - '@oxfmt/binding-android-arm64@0.45.0': + '@oxc-resolver/binding-android-arm64@11.24.2': optional: true - '@oxfmt/binding-darwin-arm64@0.45.0': + '@oxc-resolver/binding-darwin-arm64@11.24.2': optional: true - '@oxfmt/binding-darwin-x64@0.45.0': + '@oxc-resolver/binding-darwin-x64@11.24.2': optional: true - '@oxfmt/binding-freebsd-x64@0.45.0': + '@oxc-resolver/binding-freebsd-x64@11.24.2': optional: true - '@oxfmt/binding-linux-arm-gnueabihf@0.45.0': + '@oxc-resolver/binding-linux-arm-gnueabihf@11.24.2': optional: true - '@oxfmt/binding-linux-arm-musleabihf@0.45.0': + '@oxc-resolver/binding-linux-arm-musleabihf@11.24.2': optional: true - '@oxfmt/binding-linux-arm64-gnu@0.45.0': + '@oxc-resolver/binding-linux-arm64-gnu@11.24.2': optional: true - '@oxfmt/binding-linux-arm64-musl@0.45.0': + '@oxc-resolver/binding-linux-arm64-musl@11.24.2': optional: true - '@oxfmt/binding-linux-ppc64-gnu@0.45.0': + '@oxc-resolver/binding-linux-ppc64-gnu@11.24.2': optional: true - '@oxfmt/binding-linux-riscv64-gnu@0.45.0': + '@oxc-resolver/binding-linux-riscv64-gnu@11.24.2': optional: true - '@oxfmt/binding-linux-riscv64-musl@0.45.0': + '@oxc-resolver/binding-linux-riscv64-musl@11.24.2': optional: true - '@oxfmt/binding-linux-s390x-gnu@0.45.0': + '@oxc-resolver/binding-linux-s390x-gnu@11.24.2': optional: true - '@oxfmt/binding-linux-x64-gnu@0.45.0': + '@oxc-resolver/binding-linux-x64-gnu@11.24.2': optional: true - '@oxfmt/binding-linux-x64-musl@0.45.0': + '@oxc-resolver/binding-linux-x64-musl@11.24.2': optional: true - '@oxfmt/binding-openharmony-arm64@0.45.0': + '@oxc-resolver/binding-openharmony-arm64@11.24.2': optional: true - '@oxfmt/binding-win32-arm64-msvc@0.45.0': + '@oxc-resolver/binding-wasm32-wasi@11.24.2': + dependencies: + '@emnapi/core': 1.11.2 + '@emnapi/runtime': 1.11.2 + '@napi-rs/wasm-runtime': 1.2.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) optional: true - '@oxfmt/binding-win32-ia32-msvc@0.45.0': + '@oxc-resolver/binding-win32-arm64-msvc@11.24.2': optional: true - '@oxfmt/binding-win32-x64-msvc@0.45.0': + '@oxc-resolver/binding-win32-x64-msvc@11.24.2': optional: true - '@oxlint-tsgolint/darwin-arm64@0.21.0': + '@oxfmt/binding-android-arm-eabi@0.61.0': optional: true - '@oxlint-tsgolint/darwin-x64@0.21.0': + '@oxfmt/binding-android-arm64@0.61.0': optional: true - '@oxlint-tsgolint/linux-arm64@0.21.0': + '@oxfmt/binding-darwin-arm64@0.61.0': optional: true - '@oxlint-tsgolint/linux-x64@0.21.0': + '@oxfmt/binding-darwin-x64@0.61.0': optional: true - '@oxlint-tsgolint/win32-arm64@0.21.0': + '@oxfmt/binding-freebsd-x64@0.61.0': optional: true - '@oxlint-tsgolint/win32-x64@0.21.0': + '@oxfmt/binding-linux-arm-gnueabihf@0.61.0': optional: true - '@oxlint/binding-android-arm-eabi@1.60.0': + '@oxfmt/binding-linux-arm-musleabihf@0.61.0': optional: true - '@oxlint/binding-android-arm64@1.60.0': + '@oxfmt/binding-linux-arm64-gnu@0.61.0': optional: true - '@oxlint/binding-darwin-arm64@1.60.0': + '@oxfmt/binding-linux-arm64-musl@0.61.0': optional: true - '@oxlint/binding-darwin-x64@1.60.0': + '@oxfmt/binding-linux-ppc64-gnu@0.61.0': optional: true - '@oxlint/binding-freebsd-x64@1.60.0': + '@oxfmt/binding-linux-riscv64-gnu@0.61.0': optional: true - '@oxlint/binding-linux-arm-gnueabihf@1.60.0': + '@oxfmt/binding-linux-riscv64-musl@0.61.0': optional: true - '@oxlint/binding-linux-arm-musleabihf@1.60.0': + '@oxfmt/binding-linux-s390x-gnu@0.61.0': optional: true - '@oxlint/binding-linux-arm64-gnu@1.60.0': + '@oxfmt/binding-linux-x64-gnu@0.61.0': optional: true - '@oxlint/binding-linux-arm64-musl@1.60.0': + '@oxfmt/binding-linux-x64-musl@0.61.0': optional: true - '@oxlint/binding-linux-ppc64-gnu@1.60.0': + '@oxfmt/binding-openharmony-arm64@0.61.0': optional: true - '@oxlint/binding-linux-riscv64-gnu@1.60.0': + '@oxfmt/binding-win32-arm64-msvc@0.61.0': optional: true - '@oxlint/binding-linux-riscv64-musl@1.60.0': + '@oxfmt/binding-win32-ia32-msvc@0.61.0': optional: true - '@oxlint/binding-linux-s390x-gnu@1.60.0': + '@oxfmt/binding-win32-x64-msvc@0.61.0': optional: true - '@oxlint/binding-linux-x64-gnu@1.60.0': + '@oxlint-tsgolint/darwin-arm64@7.0.2001': optional: true - '@oxlint/binding-linux-x64-musl@1.60.0': + '@oxlint-tsgolint/darwin-x64@7.0.2001': optional: true - '@oxlint/binding-openharmony-arm64@1.60.0': + '@oxlint-tsgolint/linux-arm64@7.0.2001': optional: true - '@oxlint/binding-win32-arm64-msvc@1.60.0': + '@oxlint-tsgolint/linux-x64@7.0.2001': optional: true - '@oxlint/binding-win32-ia32-msvc@1.60.0': + '@oxlint-tsgolint/win32-arm64@7.0.2001': optional: true - '@oxlint/binding-win32-x64-msvc@1.60.0': + '@oxlint-tsgolint/win32-x64@7.0.2001': optional: true - '@phosphor-icons/react@2.1.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + '@oxlint/binding-android-arm-eabi@1.76.0': + optional: true - '@polka/url@1.0.0-next.29': {} + '@oxlint/binding-android-arm64@1.76.0': + optional: true - '@poppinss/colors@4.1.6': - dependencies: - kleur: 4.1.5 + '@oxlint/binding-darwin-arm64@1.76.0': + optional: true - '@poppinss/dumper@0.6.5': - dependencies: - '@poppinss/colors': 4.1.6 - '@sindresorhus/is': 7.2.0 - supports-color: 10.2.2 + '@oxlint/binding-darwin-x64@1.76.0': + optional: true - '@poppinss/exception@1.2.3': {} - - '@react-aria/breadcrumbs@3.5.32(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/i18n': 3.12.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/link': 3.8.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-types/breadcrumbs': 3.7.19(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/button@3.14.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/toolbar': 3.0.0-beta.24(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/toggle': 3.9.5(react@18.3.1) - '@react-types/button': 3.15.1(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/calendar@3.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@internationalized/date': 3.12.0 - '@react-aria/i18n': 3.12.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/live-announcer': 3.4.4 - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/calendar': 3.9.3(react@18.3.1) - '@react-types/button': 3.15.1(react@18.3.1) - '@react-types/calendar': 3.8.3(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/checkbox@3.16.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/form': 3.1.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/label': 3.7.25(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/toggle': 3.12.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/checkbox': 3.7.5(react@18.3.1) - '@react-stately/form': 3.2.4(react@18.3.1) - '@react-stately/toggle': 3.9.5(react@18.3.1) - '@react-types/checkbox': 3.10.4(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/color@3.1.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/i18n': 3.12.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/numberfield': 3.12.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/slider': 3.8.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/spinbutton': 3.7.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/textfield': 3.18.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/visually-hidden': 3.8.31(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/color': 3.9.5(react@18.3.1) - '@react-stately/form': 3.2.4(react@18.3.1) - '@react-types/color': 3.1.4(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/combobox@3.15.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/focus': 3.21.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/i18n': 3.12.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/listbox': 3.15.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/live-announcer': 3.4.4 - '@react-aria/menu': 3.21.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/overlays': 3.31.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/selection': 3.27.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/textfield': 3.18.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/collections': 3.12.10(react@18.3.1) - '@react-stately/combobox': 3.13.0(react@18.3.1) - '@react-stately/form': 3.2.4(react@18.3.1) - '@react-types/button': 3.15.1(react@18.3.1) - '@react-types/combobox': 3.14.0(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/datepicker@3.16.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@internationalized/date': 3.12.0 - '@internationalized/number': 3.6.5 - '@internationalized/string': 3.2.7 - '@react-aria/focus': 3.21.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/form': 3.1.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/i18n': 3.12.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/label': 3.7.25(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/spinbutton': 3.7.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/datepicker': 3.16.1(react@18.3.1) - '@react-stately/form': 3.2.4(react@18.3.1) - '@react-types/button': 3.15.1(react@18.3.1) - '@react-types/calendar': 3.8.3(react@18.3.1) - '@react-types/datepicker': 3.13.5(react@18.3.1) - '@react-types/dialog': 3.5.24(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/dialog@3.5.34(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/overlays': 3.31.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-types/dialog': 3.5.24(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/disclosure@3.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/ssr': 3.9.10(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/disclosure': 3.0.11(react@18.3.1) - '@react-types/button': 3.15.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/dnd@3.11.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@internationalized/string': 3.2.7 - '@react-aria/i18n': 3.12.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/live-announcer': 3.4.4 - '@react-aria/overlays': 3.31.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/collections': 3.12.10(react@18.3.1) - '@react-stately/dnd': 3.7.4(react@18.3.1) - '@react-types/button': 3.15.1(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/focus@3.21.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - clsx: 2.1.1 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/form@3.1.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/form': 3.2.4(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/grid@3.14.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/focus': 3.21.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/i18n': 3.12.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/live-announcer': 3.4.4 - '@react-aria/selection': 3.27.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/collections': 3.12.10(react@18.3.1) - '@react-stately/grid': 3.11.9(react@18.3.1) - '@react-stately/selection': 3.20.9(react@18.3.1) - '@react-types/checkbox': 3.10.4(react@18.3.1) - '@react-types/grid': 3.3.8(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/gridlist@3.14.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/focus': 3.21.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/grid': 3.14.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/i18n': 3.12.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/selection': 3.27.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/list': 3.13.4(react@18.3.1) - '@react-stately/tree': 3.9.6(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/i18n@3.12.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@internationalized/date': 3.12.0 - '@internationalized/message': 3.1.8 - '@internationalized/number': 3.6.5 - '@internationalized/string': 3.2.7 - '@react-aria/ssr': 3.9.10(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/interactions@3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/ssr': 3.9.10(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/flags': 3.1.2 - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/label@3.7.25(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/landmark@3.0.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - use-sync-external-store: 1.6.0(react@18.3.1) - - '@react-aria/link@3.8.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-types/link': 3.6.7(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/listbox@3.15.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/label': 3.7.25(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/selection': 3.27.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/collections': 3.12.10(react@18.3.1) - '@react-stately/list': 3.13.4(react@18.3.1) - '@react-types/listbox': 3.7.6(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/live-announcer@3.4.4': - dependencies: - '@swc/helpers': 0.5.19 - - '@react-aria/menu@3.21.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/focus': 3.21.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/i18n': 3.12.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/overlays': 3.31.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/selection': 3.27.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/collections': 3.12.10(react@18.3.1) - '@react-stately/menu': 3.9.11(react@18.3.1) - '@react-stately/selection': 3.20.9(react@18.3.1) - '@react-stately/tree': 3.9.6(react@18.3.1) - '@react-types/button': 3.15.1(react@18.3.1) - '@react-types/menu': 3.10.7(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/meter@3.4.30(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/progress': 3.4.30(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-types/meter': 3.4.15(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/numberfield@3.12.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/i18n': 3.12.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/live-announcer': 3.4.4 - '@react-aria/spinbutton': 3.7.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/textfield': 3.18.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/form': 3.2.4(react@18.3.1) - '@react-stately/numberfield': 3.11.0(react@18.3.1) - '@react-types/button': 3.15.1(react@18.3.1) - '@react-types/numberfield': 3.8.18(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/overlays@3.31.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/focus': 3.21.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/i18n': 3.12.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/ssr': 3.9.10(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/visually-hidden': 3.8.31(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/flags': 3.1.2 - '@react-stately/overlays': 3.6.23(react@18.3.1) - '@react-types/button': 3.15.1(react@18.3.1) - '@react-types/overlays': 3.9.4(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/progress@3.4.30(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/i18n': 3.12.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/label': 3.7.25(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-types/progress': 3.5.18(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/radio@3.12.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/focus': 3.21.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/form': 3.1.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/i18n': 3.12.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/label': 3.7.25(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/radio': 3.11.5(react@18.3.1) - '@react-types/radio': 3.9.4(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/searchfield@3.8.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/i18n': 3.12.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/textfield': 3.18.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/searchfield': 3.5.19(react@18.3.1) - '@react-types/button': 3.15.1(react@18.3.1) - '@react-types/searchfield': 3.6.8(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/select@3.17.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/form': 3.1.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/i18n': 3.12.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/label': 3.7.25(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/listbox': 3.15.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/menu': 3.21.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/selection': 3.27.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/visually-hidden': 3.8.31(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/select': 3.9.2(react@18.3.1) - '@react-types/button': 3.15.1(react@18.3.1) - '@react-types/select': 3.12.2(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/selection@3.27.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/focus': 3.21.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/i18n': 3.12.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/selection': 3.20.9(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/separator@3.4.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/slider@3.8.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/i18n': 3.12.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/label': 3.7.25(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/slider': 3.7.5(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@react-types/slider': 3.8.4(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/spinbutton@3.7.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/i18n': 3.12.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/live-announcer': 3.4.4 - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-types/button': 3.15.1(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/ssr@3.9.10(react@18.3.1)': - dependencies: - '@swc/helpers': 0.5.19 - react: 18.3.1 - - '@react-aria/switch@3.7.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/toggle': 3.12.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/toggle': 3.9.5(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@react-types/switch': 3.5.17(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/table@3.17.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/focus': 3.21.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/grid': 3.14.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/i18n': 3.12.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/live-announcer': 3.4.4 - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/visually-hidden': 3.8.31(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/collections': 3.12.10(react@18.3.1) - '@react-stately/flags': 3.1.2 - '@react-stately/table': 3.15.4(react@18.3.1) - '@react-types/checkbox': 3.10.4(react@18.3.1) - '@react-types/grid': 3.3.8(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@react-types/table': 3.13.6(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/tabs@3.11.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/focus': 3.21.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/i18n': 3.12.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/selection': 3.27.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/tabs': 3.8.9(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@react-types/tabs': 3.3.22(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/tag@3.8.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/gridlist': 3.14.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/i18n': 3.12.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/label': 3.7.25(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/selection': 3.27.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/list': 3.13.4(react@18.3.1) - '@react-types/button': 3.15.1(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/textfield@3.18.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/form': 3.1.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/label': 3.7.25(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/form': 3.2.4(react@18.3.1) - '@react-stately/utils': 3.11.0(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@react-types/textfield': 3.12.8(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/toast@3.0.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/i18n': 3.12.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/landmark': 3.0.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/toast': 3.1.3(react@18.3.1) - '@react-types/button': 3.15.1(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/toggle@3.12.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/toggle': 3.9.5(react@18.3.1) - '@react-types/checkbox': 3.10.4(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/toolbar@3.0.0-beta.24(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/focus': 3.21.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/i18n': 3.12.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/tooltip@3.9.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/tooltip': 3.5.11(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@react-types/tooltip': 3.5.2(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/tree@3.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/gridlist': 3.14.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/i18n': 3.12.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/selection': 3.27.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-stately/tree': 3.9.6(react@18.3.1) - '@react-types/button': 3.15.1(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/utils@3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/ssr': 3.9.10(react@18.3.1) - '@react-stately/flags': 3.1.2 - '@react-stately/utils': 3.11.0(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - clsx: 2.1.1 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-aria/visually-hidden@3.8.31(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@react-stately/calendar@3.9.3(react@18.3.1)': - dependencies: - '@internationalized/date': 3.12.0 - '@react-stately/utils': 3.11.0(react@18.3.1) - '@react-types/calendar': 3.8.3(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - - '@react-stately/checkbox@3.7.5(react@18.3.1)': - dependencies: - '@react-stately/form': 3.2.4(react@18.3.1) - '@react-stately/utils': 3.11.0(react@18.3.1) - '@react-types/checkbox': 3.10.4(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - - '@react-stately/collections@3.12.10(react@18.3.1)': - dependencies: - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - - '@react-stately/color@3.9.5(react@18.3.1)': - dependencies: - '@internationalized/number': 3.6.5 - '@internationalized/string': 3.2.7 - '@react-stately/form': 3.2.4(react@18.3.1) - '@react-stately/numberfield': 3.11.0(react@18.3.1) - '@react-stately/slider': 3.7.5(react@18.3.1) - '@react-stately/utils': 3.11.0(react@18.3.1) - '@react-types/color': 3.1.4(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - - '@react-stately/combobox@3.13.0(react@18.3.1)': - dependencies: - '@react-stately/collections': 3.12.10(react@18.3.1) - '@react-stately/form': 3.2.4(react@18.3.1) - '@react-stately/list': 3.13.4(react@18.3.1) - '@react-stately/overlays': 3.6.23(react@18.3.1) - '@react-stately/utils': 3.11.0(react@18.3.1) - '@react-types/combobox': 3.14.0(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - - '@react-stately/datepicker@3.16.1(react@18.3.1)': - dependencies: - '@internationalized/date': 3.12.0 - '@internationalized/number': 3.6.5 - '@internationalized/string': 3.2.7 - '@react-stately/form': 3.2.4(react@18.3.1) - '@react-stately/overlays': 3.6.23(react@18.3.1) - '@react-stately/utils': 3.11.0(react@18.3.1) - '@react-types/datepicker': 3.13.5(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - - '@react-stately/disclosure@3.0.11(react@18.3.1)': - dependencies: - '@react-stately/utils': 3.11.0(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - - '@react-stately/dnd@3.7.4(react@18.3.1)': - dependencies: - '@react-stately/selection': 3.20.9(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - - '@react-stately/flags@3.1.2': - dependencies: - '@swc/helpers': 0.5.19 - - '@react-stately/form@3.2.4(react@18.3.1)': - dependencies: - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - - '@react-stately/grid@3.11.9(react@18.3.1)': - dependencies: - '@react-stately/collections': 3.12.10(react@18.3.1) - '@react-stately/selection': 3.20.9(react@18.3.1) - '@react-types/grid': 3.3.8(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - - '@react-stately/list@3.13.4(react@18.3.1)': - dependencies: - '@react-stately/collections': 3.12.10(react@18.3.1) - '@react-stately/selection': 3.20.9(react@18.3.1) - '@react-stately/utils': 3.11.0(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - - '@react-stately/menu@3.9.11(react@18.3.1)': - dependencies: - '@react-stately/overlays': 3.6.23(react@18.3.1) - '@react-types/menu': 3.10.7(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - - '@react-stately/numberfield@3.11.0(react@18.3.1)': - dependencies: - '@internationalized/number': 3.6.5 - '@react-stately/form': 3.2.4(react@18.3.1) - '@react-stately/utils': 3.11.0(react@18.3.1) - '@react-types/numberfield': 3.8.18(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - - '@react-stately/overlays@3.6.23(react@18.3.1)': - dependencies: - '@react-stately/utils': 3.11.0(react@18.3.1) - '@react-types/overlays': 3.9.4(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 - - '@react-stately/radio@3.11.5(react@18.3.1)': - dependencies: - '@react-stately/form': 3.2.4(react@18.3.1) - '@react-stately/utils': 3.11.0(react@18.3.1) - '@react-types/radio': 3.9.4(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 + '@oxlint/binding-freebsd-x64@1.76.0': + optional: true - '@react-stately/searchfield@3.5.19(react@18.3.1)': - dependencies: - '@react-stately/utils': 3.11.0(react@18.3.1) - '@react-types/searchfield': 3.6.8(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 + '@oxlint/binding-linux-arm-gnueabihf@1.76.0': + optional: true - '@react-stately/select@3.9.2(react@18.3.1)': - dependencies: - '@react-stately/form': 3.2.4(react@18.3.1) - '@react-stately/list': 3.13.4(react@18.3.1) - '@react-stately/overlays': 3.6.23(react@18.3.1) - '@react-stately/utils': 3.11.0(react@18.3.1) - '@react-types/select': 3.12.2(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 + '@oxlint/binding-linux-arm-musleabihf@1.76.0': + optional: true - '@react-stately/selection@3.20.9(react@18.3.1)': - dependencies: - '@react-stately/collections': 3.12.10(react@18.3.1) - '@react-stately/utils': 3.11.0(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 + '@oxlint/binding-linux-arm64-gnu@1.76.0': + optional: true - '@react-stately/slider@3.7.5(react@18.3.1)': - dependencies: - '@react-stately/utils': 3.11.0(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@react-types/slider': 3.8.4(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 + '@oxlint/binding-linux-arm64-musl@1.76.0': + optional: true - '@react-stately/table@3.15.4(react@18.3.1)': - dependencies: - '@react-stately/collections': 3.12.10(react@18.3.1) - '@react-stately/flags': 3.1.2 - '@react-stately/grid': 3.11.9(react@18.3.1) - '@react-stately/selection': 3.20.9(react@18.3.1) - '@react-stately/utils': 3.11.0(react@18.3.1) - '@react-types/grid': 3.3.8(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@react-types/table': 3.13.6(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 + '@oxlint/binding-linux-ppc64-gnu@1.76.0': + optional: true - '@react-stately/tabs@3.8.9(react@18.3.1)': - dependencies: - '@react-stately/list': 3.13.4(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@react-types/tabs': 3.3.22(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 + '@oxlint/binding-linux-riscv64-gnu@1.76.0': + optional: true - '@react-stately/toast@3.1.3(react@18.3.1)': - dependencies: - '@swc/helpers': 0.5.19 - react: 18.3.1 - use-sync-external-store: 1.6.0(react@18.3.1) + '@oxlint/binding-linux-riscv64-musl@1.76.0': + optional: true - '@react-stately/toggle@3.9.5(react@18.3.1)': - dependencies: - '@react-stately/utils': 3.11.0(react@18.3.1) - '@react-types/checkbox': 3.10.4(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 + '@oxlint/binding-linux-s390x-gnu@1.76.0': + optional: true - '@react-stately/tooltip@3.5.11(react@18.3.1)': - dependencies: - '@react-stately/overlays': 3.6.23(react@18.3.1) - '@react-types/tooltip': 3.5.2(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 + '@oxlint/binding-linux-x64-gnu@1.76.0': + optional: true - '@react-stately/tree@3.9.6(react@18.3.1)': - dependencies: - '@react-stately/collections': 3.12.10(react@18.3.1) - '@react-stately/selection': 3.20.9(react@18.3.1) - '@react-stately/utils': 3.11.0(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - '@swc/helpers': 0.5.19 - react: 18.3.1 + '@oxlint/binding-linux-x64-musl@1.76.0': + optional: true - '@react-stately/utils@3.11.0(react@18.3.1)': - dependencies: - '@swc/helpers': 0.5.19 - react: 18.3.1 + '@oxlint/binding-openharmony-arm64@1.76.0': + optional: true - '@react-types/breadcrumbs@3.7.19(react@18.3.1)': - dependencies: - '@react-types/link': 3.6.7(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - react: 18.3.1 + '@oxlint/binding-win32-arm64-msvc@1.76.0': + optional: true - '@react-types/button@3.15.1(react@18.3.1)': - dependencies: - '@react-types/shared': 3.33.1(react@18.3.1) - react: 18.3.1 + '@oxlint/binding-win32-ia32-msvc@1.76.0': + optional: true - '@react-types/calendar@3.8.3(react@18.3.1)': - dependencies: - '@internationalized/date': 3.12.0 - '@react-types/shared': 3.33.1(react@18.3.1) - react: 18.3.1 + '@oxlint/binding-win32-x64-msvc@1.76.0': + optional: true - '@react-types/checkbox@3.10.4(react@18.3.1)': + '@phosphor-icons/react@2.1.10(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@react-types/shared': 3.33.1(react@18.3.1) - react: 18.3.1 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) - '@react-types/color@3.1.4(react@18.3.1)': - dependencies: - '@react-types/shared': 3.33.1(react@18.3.1) - '@react-types/slider': 3.8.4(react@18.3.1) - react: 18.3.1 + '@pkgjs/parseargs@0.11.0': + optional: true - '@react-types/combobox@3.14.0(react@18.3.1)': + '@playwright/test@1.62.1': dependencies: - '@react-types/shared': 3.33.1(react@18.3.1) - react: 18.3.1 + playwright: 1.62.1 - '@react-types/datepicker@3.13.5(react@18.3.1)': - dependencies: - '@internationalized/date': 3.12.0 - '@react-types/calendar': 3.8.3(react@18.3.1) - '@react-types/overlays': 3.9.4(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - react: 18.3.1 + '@polka/url@1.0.0-next.29': {} - '@react-types/dialog@3.5.24(react@18.3.1)': - dependencies: - '@react-types/overlays': 3.9.4(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - react: 18.3.1 + '@protobufjs/aspromise@1.1.2': {} - '@react-types/grid@3.3.8(react@18.3.1)': - dependencies: - '@react-types/shared': 3.33.1(react@18.3.1) - react: 18.3.1 + '@protobufjs/base64@1.1.2': {} - '@react-types/link@3.6.7(react@18.3.1)': - dependencies: - '@react-types/shared': 3.33.1(react@18.3.1) - react: 18.3.1 + '@protobufjs/codegen@2.0.5': {} - '@react-types/listbox@3.7.6(react@18.3.1)': - dependencies: - '@react-types/shared': 3.33.1(react@18.3.1) - react: 18.3.1 + '@protobufjs/eventemitter@1.1.1': {} - '@react-types/menu@3.10.7(react@18.3.1)': + '@protobufjs/fetch@1.1.1': dependencies: - '@react-types/overlays': 3.9.4(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - react: 18.3.1 + '@protobufjs/aspromise': 1.1.2 - '@react-types/meter@3.4.15(react@18.3.1)': - dependencies: - '@react-types/progress': 3.5.18(react@18.3.1) - react: 18.3.1 + '@protobufjs/float@1.0.2': {} - '@react-types/numberfield@3.8.18(react@18.3.1)': - dependencies: - '@react-types/shared': 3.33.1(react@18.3.1) - react: 18.3.1 + '@protobufjs/path@1.1.2': {} - '@react-types/overlays@3.9.4(react@18.3.1)': - dependencies: - '@react-types/shared': 3.33.1(react@18.3.1) - react: 18.3.1 + '@protobufjs/pool@1.1.0': {} - '@react-types/progress@3.5.18(react@18.3.1)': - dependencies: - '@react-types/shared': 3.33.1(react@18.3.1) - react: 18.3.1 + '@protobufjs/utf8@1.1.2': {} - '@react-types/radio@3.9.4(react@18.3.1)': + '@react-leaflet/core@3.0.0(leaflet@1.9.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@react-types/shared': 3.33.1(react@18.3.1) - react: 18.3.1 + leaflet: 1.9.4 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) - '@react-types/searchfield@3.6.8(react@18.3.1)': + '@react-types/shared@3.36.0(react@19.2.8)': dependencies: - '@react-types/shared': 3.33.1(react@18.3.1) - '@react-types/textfield': 3.12.8(react@18.3.1) - react: 18.3.1 + react: 19.2.8 - '@react-types/select@3.12.2(react@18.3.1)': - dependencies: - '@react-types/shared': 3.33.1(react@18.3.1) - react: 18.3.1 + '@rolldown/binding-android-arm64@1.2.1': + optional: true - '@react-types/shared@3.33.1(react@18.3.1)': - dependencies: - react: 18.3.1 + '@rolldown/binding-darwin-arm64@1.2.1': + optional: true - '@react-types/slider@3.8.4(react@18.3.1)': - dependencies: - '@react-types/shared': 3.33.1(react@18.3.1) - react: 18.3.1 + '@rolldown/binding-darwin-x64@1.2.1': + optional: true - '@react-types/switch@3.5.17(react@18.3.1)': - dependencies: - '@react-types/shared': 3.33.1(react@18.3.1) - react: 18.3.1 + '@rolldown/binding-freebsd-x64@1.2.1': + optional: true - '@react-types/table@3.13.6(react@18.3.1)': - dependencies: - '@react-types/grid': 3.3.8(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - react: 18.3.1 + '@rolldown/binding-linux-arm-gnueabihf@1.2.1': + optional: true - '@react-types/tabs@3.3.22(react@18.3.1)': - dependencies: - '@react-types/shared': 3.33.1(react@18.3.1) - react: 18.3.1 + '@rolldown/binding-linux-arm64-gnu@1.2.1': + optional: true - '@react-types/textfield@3.12.8(react@18.3.1)': - dependencies: - '@react-types/shared': 3.33.1(react@18.3.1) - react: 18.3.1 + '@rolldown/binding-linux-arm64-musl@1.2.1': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.2.1': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.2.1': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.2.1': + optional: true - '@react-types/tooltip@3.5.2(react@18.3.1)': + '@rolldown/binding-linux-x64-musl@1.2.1': + optional: true + + '@rolldown/binding-openharmony-arm64@1.2.1': + optional: true + + '@rolldown/binding-wasm32-wasi@1.2.1': dependencies: - '@react-types/overlays': 3.9.4(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - react: 18.3.1 + '@emnapi/core': 2.0.0-alpha.3 + '@emnapi/runtime': 2.0.0-alpha.3 + '@napi-rs/wasm-runtime': 1.2.1(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3) + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.2.1': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.2.1': + optional: true - '@remix-run/router@1.23.2': {} + '@rolldown/plugin-node-polyfills@1.0.3': {} - '@rolldown/pluginutils@1.0.0-rc.3': {} + '@rolldown/pluginutils@1.0.1': {} - '@rollup/plugin-babel@5.3.1(@babel/core@7.29.0)(@types/babel__core@7.20.5)(rollup@4.59.0)': + '@rollup/plugin-babel@6.1.0(@babel/core@7.29.7(supports-color@10.2.2))(rollup@4.62.3)(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-imports': 7.28.6 - '@rollup/pluginutils': 3.1.0(rollup@4.59.0) - rollup: 4.59.0 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-imports': 7.29.7(supports-color@10.2.2) + '@rollup/pluginutils': 5.4.0(rollup@4.62.3) optionalDependencies: - '@types/babel__core': 7.20.5 + rollup: 4.62.3 transitivePeerDependencies: - supports-color - '@rollup/plugin-inject@5.0.5(rollup@4.59.0)': + '@rollup/plugin-inject@5.0.5(rollup@4.62.3)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.59.0) + '@rollup/pluginutils': 5.4.0(rollup@4.62.3) estree-walker: 2.0.2 magic-string: 0.30.21 optionalDependencies: - rollup: 4.59.0 + rollup: 4.62.3 - '@rollup/plugin-node-resolve@15.3.1(rollup@4.59.0)': + '@rollup/plugin-node-resolve@16.0.3(rollup@4.62.3)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.59.0) + '@rollup/pluginutils': 5.4.0(rollup@4.62.3) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 - resolve: 1.22.11 + resolve: 1.22.12 optionalDependencies: - rollup: 4.59.0 - - '@rollup/plugin-replace@2.4.2(rollup@4.59.0)': - dependencies: - '@rollup/pluginutils': 3.1.0(rollup@4.59.0) - magic-string: 0.25.9 - rollup: 4.59.0 + rollup: 4.62.3 - '@rollup/plugin-terser@0.4.4(rollup@4.59.0)': + '@rollup/plugin-replace@6.0.3(rollup@4.62.3)': dependencies: - serialize-javascript: 7.0.5 - smob: 1.6.1 - terser: 5.46.1 - optionalDependencies: - rollup: 4.59.0 - - '@rollup/plugin-virtual@3.0.2(rollup@4.59.0)': + '@rollup/pluginutils': 5.4.0(rollup@4.62.3) + magic-string: 0.30.21 optionalDependencies: - rollup: 4.59.0 + rollup: 4.62.3 - '@rollup/plugin-wasm@6.2.2(rollup@4.59.0)': + '@rollup/plugin-terser@1.0.0(rollup@4.62.3)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.59.0) + serialize-javascript: 7.0.7 + smob: 1.6.2 + terser: 5.49.0 optionalDependencies: - rollup: 4.59.0 + rollup: 4.62.3 - '@rollup/pluginutils@3.1.0(rollup@4.59.0)': + '@rollup/plugin-wasm@6.2.2(rollup@4.62.3)': dependencies: - '@types/estree': 0.0.39 - estree-walker: 1.0.1 - picomatch: 2.3.2 - rollup: 4.59.0 + '@rollup/pluginutils': 5.4.0(rollup@4.62.3) + optionalDependencies: + rollup: 4.62.3 - '@rollup/pluginutils@5.3.0(rollup@4.59.0)': + '@rollup/pluginutils@5.4.0(rollup@4.62.3)': dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 estree-walker: 2.0.2 - picomatch: 4.0.3 + picomatch: 4.0.5 optionalDependencies: - rollup: 4.59.0 + rollup: 4.62.3 - '@rollup/rollup-android-arm-eabi@4.59.0': + '@rollup/rollup-android-arm-eabi@4.62.3': optional: true - '@rollup/rollup-android-arm64@4.59.0': + '@rollup/rollup-android-arm64@4.62.3': optional: true - '@rollup/rollup-darwin-arm64@4.59.0': + '@rollup/rollup-darwin-arm64@4.62.3': optional: true - '@rollup/rollup-darwin-x64@4.59.0': + '@rollup/rollup-darwin-x64@4.62.3': optional: true - '@rollup/rollup-freebsd-arm64@4.59.0': + '@rollup/rollup-freebsd-arm64@4.62.3': optional: true - '@rollup/rollup-freebsd-x64@4.59.0': + '@rollup/rollup-freebsd-x64@4.62.3': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': + '@rollup/rollup-linux-arm-gnueabihf@4.62.3': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.59.0': + '@rollup/rollup-linux-arm-musleabihf@4.62.3': optional: true - '@rollup/rollup-linux-arm64-gnu@4.59.0': + '@rollup/rollup-linux-arm64-gnu@4.62.3': optional: true - '@rollup/rollup-linux-arm64-musl@4.59.0': + '@rollup/rollup-linux-arm64-musl@4.62.3': optional: true - '@rollup/rollup-linux-loong64-gnu@4.59.0': + '@rollup/rollup-linux-loong64-gnu@4.62.3': optional: true - '@rollup/rollup-linux-loong64-musl@4.59.0': + '@rollup/rollup-linux-loong64-musl@4.62.3': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.59.0': + '@rollup/rollup-linux-ppc64-gnu@4.62.3': optional: true - '@rollup/rollup-linux-ppc64-musl@4.59.0': + '@rollup/rollup-linux-ppc64-musl@4.62.3': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.59.0': + '@rollup/rollup-linux-riscv64-gnu@4.62.3': optional: true - '@rollup/rollup-linux-riscv64-musl@4.59.0': + '@rollup/rollup-linux-riscv64-musl@4.62.3': optional: true - '@rollup/rollup-linux-s390x-gnu@4.59.0': + '@rollup/rollup-linux-s390x-gnu@4.62.3': optional: true - '@rollup/rollup-linux-x64-gnu@4.59.0': + '@rollup/rollup-linux-x64-gnu@4.62.3': optional: true - '@rollup/rollup-linux-x64-musl@4.59.0': + '@rollup/rollup-linux-x64-musl@4.62.3': optional: true - '@rollup/rollup-openbsd-x64@4.59.0': + '@rollup/rollup-openbsd-x64@4.62.3': optional: true - '@rollup/rollup-openharmony-arm64@4.59.0': + '@rollup/rollup-openharmony-arm64@4.62.3': optional: true - '@rollup/rollup-win32-arm64-msvc@4.59.0': + '@rollup/rollup-win32-arm64-msvc@4.62.3': optional: true - '@rollup/rollup-win32-ia32-msvc@4.59.0': + '@rollup/rollup-win32-ia32-msvc@4.62.3': optional: true - '@rollup/rollup-win32-x64-gnu@4.59.0': + '@rollup/rollup-win32-x64-gnu@4.62.3': optional: true - '@rollup/rollup-win32-x64-msvc@4.59.0': + '@rollup/rollup-win32-x64-msvc@4.62.3': optional: true - '@sableclient/sable-call-embedded@1.1.4': {} - - '@sentry-internal/browser-utils@10.43.0': + '@sableclient/matrixrtc@0.1.0(livekit-client@2.21.0(@types/dom-mediacapture-record@1.0.22))(matrix-js-sdk@42.0.0)': dependencies: - '@sentry/core': 10.43.0 + livekit-client: 2.21.0(@types/dom-mediacapture-record@1.0.22) + matrix-js-sdk: 42.0.0 - '@sentry-internal/feedback@10.43.0': - dependencies: - '@sentry/core': 10.43.0 + '@sableclient/sable-call-embedded@1.1.8': {} - '@sentry-internal/replay-canvas@10.43.0': + '@sableclient/tauri-plugin-livekit-mobile@0.2.0': dependencies: - '@sentry-internal/replay': 10.43.0 - '@sentry/core': 10.43.0 + '@tauri-apps/api': 2.11.1 - '@sentry-internal/replay@10.43.0': + '@sableclient/tauri-plugin-notifications-api@0.5.2': dependencies: - '@sentry-internal/browser-utils': 10.43.0 - '@sentry/core': 10.43.0 + '@tauri-apps/api': 2.11.1 - '@sentry/babel-plugin-component-annotate@5.1.1': {} + '@sableclient/twemoji-font@1.0.4': {} + + '@sentry/browser-utils@10.70.0': + dependencies: + '@sentry/conventions': 0.16.0 + '@sentry/core': 10.70.0 - '@sentry/browser@10.43.0': + '@sentry/browser@10.70.0': dependencies: - '@sentry-internal/browser-utils': 10.43.0 - '@sentry-internal/feedback': 10.43.0 - '@sentry-internal/replay': 10.43.0 - '@sentry-internal/replay-canvas': 10.43.0 - '@sentry/core': 10.43.0 + '@sentry/browser-utils': 10.70.0 + '@sentry/conventions': 0.16.0 + '@sentry/core': 10.70.0 + '@sentry/feedback': 10.70.0 + '@sentry/replay': 10.70.0 + '@sentry/replay-canvas': 10.70.0 - '@sentry/bundler-plugin-core@5.1.1': + '@sentry/bundler-plugins@10.69.0(rollup@4.62.3)(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@sentry/babel-plugin-component-annotate': 5.1.1 - '@sentry/cli': 2.58.5 - dotenv: 16.6.1 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@sentry/cli': 2.58.6(supports-color@10.2.2) + '@sentry/core': 10.69.0 + dotenv: 17.4.2 find-up: 5.0.0 glob: 13.0.6 magic-string: 0.30.21 + optionalDependencies: + rollup: 4.62.3 transitivePeerDependencies: - encoding - supports-color - '@sentry/cli-darwin@2.58.5': + '@sentry/cli-darwin@2.58.6': optional: true - '@sentry/cli-linux-arm64@2.58.5': + '@sentry/cli-linux-arm64@2.58.6': optional: true - '@sentry/cli-linux-arm@2.58.5': + '@sentry/cli-linux-arm@2.58.6': optional: true - '@sentry/cli-linux-i686@2.58.5': + '@sentry/cli-linux-i686@2.58.6': optional: true - '@sentry/cli-linux-x64@2.58.5': + '@sentry/cli-linux-x64@2.58.6': optional: true - '@sentry/cli-win32-arm64@2.58.5': + '@sentry/cli-win32-arm64@2.58.6': optional: true - '@sentry/cli-win32-i686@2.58.5': + '@sentry/cli-win32-i686@2.58.6': optional: true - '@sentry/cli-win32-x64@2.58.5': + '@sentry/cli-win32-x64@2.58.6': optional: true - '@sentry/cli@2.58.5': + '@sentry/cli@2.58.6(supports-color@10.2.2)': dependencies: - https-proxy-agent: 5.0.1 + https-proxy-agent: 5.0.1(supports-color@10.2.2) node-fetch: 2.7.0 progress: 2.0.3 proxy-from-env: 1.1.0 which: 2.0.2 optionalDependencies: - '@sentry/cli-darwin': 2.58.5 - '@sentry/cli-linux-arm': 2.58.5 - '@sentry/cli-linux-arm64': 2.58.5 - '@sentry/cli-linux-i686': 2.58.5 - '@sentry/cli-linux-x64': 2.58.5 - '@sentry/cli-win32-arm64': 2.58.5 - '@sentry/cli-win32-i686': 2.58.5 - '@sentry/cli-win32-x64': 2.58.5 + '@sentry/cli-darwin': 2.58.6 + '@sentry/cli-linux-arm': 2.58.6 + '@sentry/cli-linux-arm64': 2.58.6 + '@sentry/cli-linux-i686': 2.58.6 + '@sentry/cli-linux-x64': 2.58.6 + '@sentry/cli-win32-arm64': 2.58.6 + '@sentry/cli-win32-i686': 2.58.6 + '@sentry/cli-win32-x64': 2.58.6 transitivePeerDependencies: - encoding - supports-color - '@sentry/core@10.43.0': {} + '@sentry/conventions@0.16.0': {} - '@sentry/react@10.43.0(react@18.3.1)': + '@sentry/core@10.69.0': dependencies: - '@sentry/browser': 10.43.0 - '@sentry/core': 10.43.0 - react: 18.3.1 + '@sentry/conventions': 0.16.0 - '@sentry/rollup-plugin@5.1.1(rollup@4.59.0)': + '@sentry/core@10.70.0': dependencies: - '@sentry/bundler-plugin-core': 5.1.1 - magic-string: 0.30.21 - rollup: 4.59.0 - transitivePeerDependencies: - - encoding - - supports-color + '@sentry/conventions': 0.16.0 + + '@sentry/feedback@10.70.0': + dependencies: + '@sentry/core': 10.70.0 + + '@sentry/react@10.70.0(react@19.2.8)': + dependencies: + '@sentry/browser': 10.70.0 + '@sentry/conventions': 0.16.0 + '@sentry/core': 10.70.0 + react: 19.2.8 + + '@sentry/replay-canvas@10.70.0': + dependencies: + '@sentry/core': 10.70.0 + '@sentry/replay': 10.70.0 + + '@sentry/replay@10.70.0': + dependencies: + '@sentry/browser-utils': 10.70.0 + '@sentry/core': 10.70.0 - '@sentry/vite-plugin@5.1.1(rollup@4.59.0)': + '@sentry/vite-plugin@5.4.0(rollup@4.62.3)(supports-color@10.2.2)': dependencies: - '@sentry/bundler-plugin-core': 5.1.1 - '@sentry/rollup-plugin': 5.1.1(rollup@4.59.0) + '@sentry/bundler-plugins': 10.69.0(rollup@4.62.3)(supports-color@10.2.2) transitivePeerDependencies: - encoding - rollup - supports-color - - '@sindresorhus/is@7.2.0': {} - - '@speed-highlight/core@1.2.14': {} + - webpack '@standard-schema/spec@1.1.0': {} - '@surma/rollup-plugin-off-main-thread@2.2.3': - dependencies: - ejs: 3.1.10 - json5: 2.2.3 - magic-string: 0.25.9 - string.prototype.matchall: 4.0.12 - - '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.29.0)': + '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.7(supports-color@10.2.2) - '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.29.0)': + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.7(supports-color@10.2.2) - '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.29.0)': + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.7(supports-color@10.2.2) - '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.29.0)': + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.7(supports-color@10.2.2) - '@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.29.0)': + '@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.7(supports-color@10.2.2) - '@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.29.0)': + '@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.7(supports-color@10.2.2) - '@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.29.0)': + '@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.7(supports-color@10.2.2) - '@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.29.0)': + '@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.7(supports-color@10.2.2) - '@svgr/babel-preset@8.1.0(@babel/core@7.29.0)': + '@svgr/babel-preset@8.1.0(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.0 - '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.29.0) - '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.29.0) - '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.29.0) - '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.29.0) - '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.29.0) - '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.29.0) - '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.29.0) - '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.29.0) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.29.7(supports-color@10.2.2)) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.29.7(supports-color@10.2.2)) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.29.7(supports-color@10.2.2)) + '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.29.7(supports-color@10.2.2)) + '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.29.7(supports-color@10.2.2)) + '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.29.7(supports-color@10.2.2)) + '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.29.7(supports-color@10.2.2)) + '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.29.7(supports-color@10.2.2)) - '@svgr/core@8.1.0(typescript@5.9.3)': + '@svgr/core@8.1.0(supports-color@10.2.2)(typescript@7.0.2)': dependencies: - '@babel/core': 7.29.0 - '@svgr/babel-preset': 8.1.0(@babel/core@7.29.0) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@svgr/babel-preset': 8.1.0(@babel/core@7.29.7(supports-color@10.2.2)) camelcase: 6.3.0 - cosmiconfig: 8.3.6(typescript@5.9.3) + cosmiconfig: 8.3.6(typescript@7.0.2) snake-case: 3.0.4 transitivePeerDependencies: - supports-color @@ -7842,105 +7200,139 @@ snapshots: '@svgr/hast-util-to-babel-ast@8.0.0': dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.29.7 entities: 4.5.0 - '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.9.3))': + '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(supports-color@10.2.2)(typescript@7.0.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@svgr/babel-preset': 8.1.0(@babel/core@7.29.0) - '@svgr/core': 8.1.0(typescript@5.9.3) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@svgr/babel-preset': 8.1.0(@babel/core@7.29.7(supports-color@10.2.2)) + '@svgr/core': 8.1.0(supports-color@10.2.2)(typescript@7.0.2) '@svgr/hast-util-to-babel-ast': 8.0.0 svg-parser: 2.0.4 transitivePeerDependencies: - supports-color - '@swc/core-darwin-arm64@1.15.18': + '@swc/helpers@0.5.23': + dependencies: + tslib: 2.8.1 + + '@tanstack/query-core@5.101.4': {} + + '@tanstack/query-devtools@5.101.4': {} + + '@tanstack/react-query-devtools@5.101.4(@tanstack/react-query@5.101.4(react@19.2.8))(react@19.2.8)': + dependencies: + '@tanstack/query-devtools': 5.101.4 + '@tanstack/react-query': 5.101.4(react@19.2.8) + react: 19.2.8 + + '@tanstack/react-query@5.101.4(react@19.2.8)': + dependencies: + '@tanstack/query-core': 5.101.4 + react: 19.2.8 + + '@tanstack/react-virtual@3.14.9(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + dependencies: + '@tanstack/virtual-core': 3.17.7 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + + '@tanstack/virtual-core@3.17.7': {} + + '@tauri-apps/api@2.11.1': {} + + '@tauri-apps/cli-darwin-arm64@2.11.4': optional: true - '@swc/core-darwin-x64@1.15.18': + '@tauri-apps/cli-darwin-x64@2.11.4': optional: true - '@swc/core-linux-arm-gnueabihf@1.15.18': + '@tauri-apps/cli-linux-arm-gnueabihf@2.11.4': optional: true - '@swc/core-linux-arm64-gnu@1.15.18': + '@tauri-apps/cli-linux-arm64-gnu@2.11.4': optional: true - '@swc/core-linux-arm64-musl@1.15.18': + '@tauri-apps/cli-linux-arm64-musl@2.11.4': optional: true - '@swc/core-linux-x64-gnu@1.15.18': + '@tauri-apps/cli-linux-riscv64-gnu@2.11.4': optional: true - '@swc/core-linux-x64-musl@1.15.18': + '@tauri-apps/cli-linux-x64-gnu@2.11.4': optional: true - '@swc/core-win32-arm64-msvc@1.15.18': + '@tauri-apps/cli-linux-x64-musl@2.11.4': optional: true - '@swc/core-win32-ia32-msvc@1.15.18': + '@tauri-apps/cli-win32-arm64-msvc@2.11.4': optional: true - '@swc/core-win32-x64-msvc@1.15.18': + '@tauri-apps/cli-win32-ia32-msvc@2.11.4': optional: true - '@swc/core@1.15.18(@swc/helpers@0.5.19)': - dependencies: - '@swc/counter': 0.1.3 - '@swc/types': 0.1.25 + '@tauri-apps/cli-win32-x64-msvc@2.11.4': + optional: true + + '@tauri-apps/cli@2.11.4': optionalDependencies: - '@swc/core-darwin-arm64': 1.15.18 - '@swc/core-darwin-x64': 1.15.18 - '@swc/core-linux-arm-gnueabihf': 1.15.18 - '@swc/core-linux-arm64-gnu': 1.15.18 - '@swc/core-linux-arm64-musl': 1.15.18 - '@swc/core-linux-x64-gnu': 1.15.18 - '@swc/core-linux-x64-musl': 1.15.18 - '@swc/core-win32-arm64-msvc': 1.15.18 - '@swc/core-win32-ia32-msvc': 1.15.18 - '@swc/core-win32-x64-msvc': 1.15.18 - '@swc/helpers': 0.5.19 - - '@swc/counter@0.1.3': {} - - '@swc/helpers@0.5.19': + '@tauri-apps/cli-darwin-arm64': 2.11.4 + '@tauri-apps/cli-darwin-x64': 2.11.4 + '@tauri-apps/cli-linux-arm-gnueabihf': 2.11.4 + '@tauri-apps/cli-linux-arm64-gnu': 2.11.4 + '@tauri-apps/cli-linux-arm64-musl': 2.11.4 + '@tauri-apps/cli-linux-riscv64-gnu': 2.11.4 + '@tauri-apps/cli-linux-x64-gnu': 2.11.4 + '@tauri-apps/cli-linux-x64-musl': 2.11.4 + '@tauri-apps/cli-win32-arm64-msvc': 2.11.4 + '@tauri-apps/cli-win32-ia32-msvc': 2.11.4 + '@tauri-apps/cli-win32-x64-msvc': 2.11.4 + + '@tauri-apps/plugin-clipboard-manager@2.3.2': dependencies: - tslib: 2.8.1 + '@tauri-apps/api': 2.11.1 + + '@tauri-apps/plugin-deep-link@2.4.9': + dependencies: + '@tauri-apps/api': 2.11.1 - '@swc/types@0.1.25': + '@tauri-apps/plugin-dialog@2.7.2': dependencies: - '@swc/counter': 0.1.3 + '@tauri-apps/api': 2.11.1 - '@swc/wasm@1.15.18': {} + '@tauri-apps/plugin-fs@2.5.1': + dependencies: + '@tauri-apps/api': 2.11.1 - '@tanstack/query-core@5.90.20': {} + '@tauri-apps/plugin-http@2.5.9': + dependencies: + '@tauri-apps/api': 2.11.1 - '@tanstack/query-devtools@5.93.0': {} + '@tauri-apps/plugin-opener@2.5.4': + dependencies: + '@tauri-apps/api': 2.11.1 - '@tanstack/react-query-devtools@5.91.3(@tanstack/react-query@5.90.21(react@18.3.1))(react@18.3.1)': + '@tauri-apps/plugin-os@2.3.2': dependencies: - '@tanstack/query-devtools': 5.93.0 - '@tanstack/react-query': 5.90.21(react@18.3.1) - react: 18.3.1 + '@tauri-apps/api': 2.11.1 - '@tanstack/react-query@5.90.21(react@18.3.1)': + '@tauri-apps/plugin-process@2.3.1': dependencies: - '@tanstack/query-core': 5.90.20 - react: 18.3.1 + '@tauri-apps/api': 2.11.1 - '@tanstack/react-virtual@3.13.21(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@tauri-apps/plugin-store@2.4.4': dependencies: - '@tanstack/virtual-core': 3.13.21 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + '@tauri-apps/api': 2.11.1 - '@tanstack/virtual-core@3.13.21': {} + '@tauri-apps/plugin-updater@2.10.1': + dependencies: + '@tauri-apps/api': 2.11.1 '@testing-library/dom@10.4.1': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/runtime': 7.29.2 + '@babel/code-frame': 7.29.7 + '@babel/runtime': 7.29.7 '@types/aria-query': 5.0.4 aria-query: 5.3.0 dom-accessibility-api: 0.5.16 @@ -7948,57 +7340,44 @@ snapshots: picocolors: 1.1.1 pretty-format: 27.5.1 - '@testing-library/jest-dom@6.9.1': + '@testing-library/jest-dom@7.0.0(@testing-library/dom@10.4.1)': dependencies: - '@adobe/css-tools': 4.4.4 + '@adobe/css-tools': 4.5.0 + '@testing-library/dom': 10.4.1 aria-query: 5.3.2 css.escape: 1.5.1 dom-accessibility-api: 0.6.3 picocolors: 1.1.1 redent: 3.0.0 - '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.7 '@testing-library/dom': 10.4.1 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)': + '@testing-library/user-event@14.6.5(@testing-library/dom@10.4.1)': dependencies: '@testing-library/dom': 10.4.1 - '@tybys/wasm-util@0.10.1': + '@trickfilm400/rollup-plugin-off-main-thread@3.0.0-pre1': + dependencies: + ejs: 3.1.10 + json5: 2.2.3 + magic-string: 0.30.21 + string.prototype.matchall: 4.0.12 + + '@tybys/wasm-util@0.10.3': dependencies: tslib: 2.8.1 optional: true '@types/aria-query@5.0.4': {} - '@types/babel__core@7.20.5': - dependencies: - '@babel/parser': 7.29.0 - '@babel/types': 7.29.0 - '@types/babel__generator': 7.27.0 - '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.28.0 - - '@types/babel__generator@7.27.0': - dependencies: - '@babel/types': 7.29.0 - - '@types/babel__template@7.4.4': - dependencies: - '@babel/parser': 7.29.0 - '@babel/types': 7.29.0 - - '@types/babel__traverse@7.28.0': - dependencies: - '@babel/types': 7.29.0 - '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 @@ -8008,66 +7387,156 @@ snapshots: '@types/deep-eql@4.0.2': {} - '@types/estree@0.0.39': {} + '@types/docker-modem@3.0.6': + dependencies: + '@types/node': 24.13.2 + '@types/ssh2': 1.15.5 + + '@types/dockerode@4.0.1': + dependencies: + '@types/docker-modem': 3.0.6 + '@types/node': 24.13.2 + '@types/ssh2': 1.15.5 + + '@types/dom-mediacapture-record@1.0.22': {} - '@types/estree@1.0.8': {} + '@types/estree@1.0.9': {} '@types/events@3.0.3': {} '@types/file-saver@2.0.7': {} + '@types/geojson@7946.0.16': {} + '@types/is-hotkey@0.1.10': {} - '@types/node@24.10.13': + '@types/leaflet@1.9.22': dependencies: - undici-types: 7.16.0 + '@types/geojson': 7946.0.16 - '@types/prop-types@15.7.15': {} + '@types/node@18.19.130': + dependencies: + undici-types: 5.26.5 + + '@types/node@24.13.2': + dependencies: + undici-types: 7.18.2 - '@types/react-dom@18.3.7(@types/react@18.3.28)': + '@types/react-dom@19.2.3(@types/react@19.2.17)': dependencies: - '@types/react': 18.3.28 + '@types/react': 19.2.17 '@types/react-google-recaptcha@2.1.9': dependencies: - '@types/react': 18.3.28 + '@types/react': 19.2.17 - '@types/react@18.3.28': + '@types/react@19.2.17': dependencies: - '@types/prop-types': 15.7.15 csstype: 3.2.3 '@types/resolve@1.20.2': {} + '@types/ssh2-streams@0.1.13': + dependencies: + '@types/node': 24.13.2 + + '@types/ssh2@0.5.52': + dependencies: + '@types/node': 24.13.2 + '@types/ssh2-streams': 0.1.13 + + '@types/ssh2@1.15.5': + dependencies: + '@types/node': 18.19.130 + '@types/trusted-types@2.0.7': {} - '@types/ua-parser-js@0.7.39': {} + '@typescript/typescript-aix-ppc64@7.0.2': + optional: true + + '@typescript/typescript-darwin-arm64@7.0.2': + optional: true + + '@typescript/typescript-darwin-x64@7.0.2': + optional: true + + '@typescript/typescript-freebsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-freebsd-x64@7.0.2': + optional: true + + '@typescript/typescript-linux-arm64@7.0.2': + optional: true + + '@typescript/typescript-linux-arm@7.0.2': + optional: true + + '@typescript/typescript-linux-loong64@7.0.2': + optional: true + + '@typescript/typescript-linux-mips64el@7.0.2': + optional: true + + '@typescript/typescript-linux-ppc64@7.0.2': + optional: true + + '@typescript/typescript-linux-riscv64@7.0.2': + optional: true + + '@typescript/typescript-linux-s390x@7.0.2': + optional: true + + '@typescript/typescript-linux-x64@7.0.2': + optional: true + + '@typescript/typescript-netbsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-netbsd-x64@7.0.2': + optional: true + + '@typescript/typescript-openbsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-openbsd-x64@7.0.2': + optional: true + + '@typescript/typescript-sunos-x64@7.0.2': + optional: true + + '@typescript/typescript-win32-arm64@7.0.2': + optional: true + + '@typescript/typescript-win32-x64@7.0.2': + optional: true '@use-gesture/core@10.3.1': {} - '@use-gesture/react@10.3.1(react@18.3.1)': + '@use-gesture/react@10.3.1(react@19.2.8)': dependencies: '@use-gesture/core': 10.3.1 - react: 18.3.1 + react: 19.2.8 - '@vanilla-extract/babel-plugin-debug-ids@1.2.2': + '@vanilla-extract/babel-plugin-debug-ids@1.2.2(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@vanilla-extract/compiler@0.3.4(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2)': + '@vanilla-extract/compiler@0.7.2(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(supports-color@10.2.2)(terser@5.49.0)(yaml@2.9.0)': dependencies: - '@vanilla-extract/css': 1.18.0 - '@vanilla-extract/integration': 8.0.7 - vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2) - vite-node: 3.2.4(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2) + '@vanilla-extract/css': 1.21.2 + '@vanilla-extract/integration': 8.0.10(supports-color@10.2.2) + vite: 8.2.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0) + vite-node: 6.0.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0) transitivePeerDependencies: - '@types/node' + - '@vitejs/devtools' - babel-plugin-macros + - esbuild - jiti - less - - lightningcss - sass - sass-embedded - stylus @@ -8077,12 +7546,11 @@ snapshots: - tsx - yaml - '@vanilla-extract/css@1.18.0': + '@vanilla-extract/css@1.21.2': dependencies: '@emotion/hash': 0.9.2 '@vanilla-extract/private': 1.0.9 css-what: 6.2.2 - cssesc: 3.0.0 csstype: 3.2.3 dedent: 1.7.2 deep-object-diff: 1.1.9 @@ -8094,39 +7562,40 @@ snapshots: transitivePeerDependencies: - babel-plugin-macros - '@vanilla-extract/integration@8.0.7': + '@vanilla-extract/integration@8.0.10(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) - '@vanilla-extract/babel-plugin-debug-ids': 1.2.2 - '@vanilla-extract/css': 1.18.0 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@vanilla-extract/babel-plugin-debug-ids': 1.2.2(supports-color@10.2.2) + '@vanilla-extract/css': 1.21.2 dedent: 1.7.2 - esbuild: 0.27.3 + esbuild: 0.28.1 eval: 0.1.8 find-up: 5.0.0 javascript-stringify: 2.1.0 - mlly: 1.8.1 + mlly: 1.8.2 transitivePeerDependencies: - babel-plugin-macros - supports-color '@vanilla-extract/private@1.0.9': {} - '@vanilla-extract/recipes@0.5.7(@vanilla-extract/css@1.18.0)': + '@vanilla-extract/recipes@0.5.7(@vanilla-extract/css@1.21.2)': dependencies: - '@vanilla-extract/css': 1.18.0 + '@vanilla-extract/css': 1.21.2 - '@vanilla-extract/vite-plugin@5.1.4(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2))(yaml@2.8.2)': + '@vanilla-extract/vite-plugin@5.2.6(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(supports-color@10.2.2)(terser@5.49.0)(vite@8.2.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0))(yaml@2.9.0)': dependencies: - '@vanilla-extract/compiler': 0.3.4(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2) - '@vanilla-extract/integration': 8.0.7 - vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2) + '@vanilla-extract/compiler': 0.7.2(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(supports-color@10.2.2)(terser@5.49.0)(yaml@2.9.0) + '@vanilla-extract/integration': 8.0.10(supports-color@10.2.2) + vite: 8.2.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0) transitivePeerDependencies: - '@types/node' + - '@vitejs/devtools' - babel-plugin-macros + - esbuild - jiti - less - - lightningcss - sass - sass-embedded - stylus @@ -8136,96 +7605,93 @@ snapshots: - tsx - yaml - '@vitejs/plugin-react@5.1.4(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2))': + '@vitejs/plugin-react@6.0.5(vite@8.2.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0) - '@rolldown/pluginutils': 1.0.0-rc.3 - '@types/babel__core': 7.20.5 - react-refresh: 0.18.0 - vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2) - transitivePeerDependencies: - - supports-color + '@rolldown/pluginutils': 1.0.1 + vite: 8.2.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0) - '@vitest/coverage-v8@4.1.0(vitest@4.1.0)': + '@vitest/coverage-v8@4.1.10(vitest@4.1.10)': dependencies: '@bcoe/v8-coverage': 1.0.2 - '@vitest/utils': 4.1.0 - ast-v8-to-istanbul: 1.0.0 + '@vitest/utils': 4.1.10 + ast-v8-to-istanbul: 1.0.5 istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 istanbul-reports: 3.2.0 - magicast: 0.5.2 - obug: 2.1.1 - std-env: 4.0.0 - tinyrainbow: 3.1.0 - vitest: 4.1.0(@types/node@24.10.13)(@vitest/ui@4.1.0)(jsdom@29.0.0)(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2)) + magicast: 0.5.3 + obug: 2.1.4 + std-env: 4.2.0 + tinyrainbow: 3.1.1 + vitest: 4.1.10(@types/node@24.13.2)(@vitest/coverage-v8@4.1.10)(@vitest/ui@4.1.10)(jsdom@30.0.1(@noble/hashes@2.3.0))(vite@8.2.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0)) - '@vitest/expect@4.1.0': + '@vitest/expect@4.1.10': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.1.0 - '@vitest/utils': 4.1.0 + '@vitest/spy': 4.1.10 + '@vitest/utils': 4.1.10 chai: 6.2.2 - tinyrainbow: 3.1.0 + tinyrainbow: 3.1.1 - '@vitest/mocker@4.1.0(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2))': + '@vitest/mocker@4.1.10(vite@8.2.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0))': dependencies: - '@vitest/spy': 4.1.0 + '@vitest/spy': 4.1.10 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2) + vite: 8.2.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0) - '@vitest/pretty-format@4.1.0': + '@vitest/pretty-format@4.1.10': dependencies: - tinyrainbow: 3.1.0 + tinyrainbow: 3.1.1 - '@vitest/runner@4.1.0': + '@vitest/runner@4.1.10': dependencies: - '@vitest/utils': 4.1.0 + '@vitest/utils': 4.1.10 pathe: 2.0.3 - '@vitest/snapshot@4.1.0': + '@vitest/snapshot@4.1.10': dependencies: - '@vitest/pretty-format': 4.1.0 - '@vitest/utils': 4.1.0 + '@vitest/pretty-format': 4.1.10 + '@vitest/utils': 4.1.10 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.1.0': {} + '@vitest/spy@4.1.10': {} - '@vitest/ui@4.1.0(vitest@4.1.0)': + '@vitest/ui@4.1.10(vitest@4.1.10)': dependencies: - '@vitest/utils': 4.1.0 - fflate: 0.8.2 - flatted: 3.4.2 + '@vitest/utils': 4.1.10 + fflate: 0.8.3 + flatted: 3.4.4 pathe: 2.0.3 sirv: 3.0.2 - tinyglobby: 0.2.15 - tinyrainbow: 3.1.0 - vitest: 4.1.0(@types/node@24.10.13)(@vitest/ui@4.1.0)(jsdom@29.0.0)(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2)) + tinyglobby: 0.2.17 + tinyrainbow: 3.1.1 + vitest: 4.1.10(@types/node@24.13.2)(@vitest/coverage-v8@4.1.10)(@vitest/ui@4.1.10)(jsdom@30.0.1(@noble/hashes@2.3.0))(vite@8.2.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0)) - '@vitest/utils@4.1.0': + '@vitest/utils@4.1.10': dependencies: - '@vitest/pretty-format': 4.1.0 + '@vitest/pretty-format': 4.1.10 convert-source-map: 2.0.0 - tinyrainbow: 3.1.0 + tinyrainbow: 3.1.1 + + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 - acorn@8.16.0: {} + acorn@8.18.0: {} - agent-base@6.0.2: + agent-base@6.0.2(supports-color@10.2.2): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color - ajv@8.18.0: + ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.1.0 + fast-uri: 4.1.2 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 @@ -8233,15 +7699,51 @@ snapshots: ansi-regex@5.0.1: {} + ansi-regex@6.2.2: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + ansi-styles@5.2.0: {} + ansi-styles@6.2.3: {} + anymatch@3.1.3: dependencies: normalize-path: 3.0.0 - picomatch: 2.3.1 + picomatch: 2.3.2 + + archiver-utils@5.0.2: + dependencies: + glob: 10.5.0 + graceful-fs: 4.2.11 + is-stream: 2.0.1 + lazystream: 1.0.1 + lodash: 4.18.1 + normalize-path: 3.0.0 + readable-stream: 4.7.0 + + archiver@7.0.1: + dependencies: + archiver-utils: 5.0.2 + async: 3.2.6 + buffer-crc32: 1.0.0 + readable-stream: 4.7.0 + readdir-glob: 1.1.3 + tar-stream: 3.2.0 + zip-stream: 6.0.1 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - react-native-b4a argparse@2.0.1: {} + aria-hidden@1.2.6: + dependencies: + tslib: 2.8.1 + aria-query@5.3.0: dependencies: dequal: 2.0.3 @@ -8256,16 +7758,20 @@ snapshots: arraybuffer.prototype.slice@1.0.4: dependencies: array-buffer-byte-length: 1.0.2 - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 - es-abstract: 1.24.1 + es-abstract: 1.24.2 es-errors: 1.3.0 get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 + asn1@0.2.6: + dependencies: + safer-buffer: 2.1.2 + assertion-error@2.0.1: {} - ast-v8-to-istanbul@1.0.0: + ast-v8-to-istanbul@1.0.5: dependencies: '@jridgewell/trace-mapping': 0.3.31 estree-walker: 3.0.3 @@ -8273,6 +7779,8 @@ snapshots: async-function@1.0.0: {} + async-lock@1.4.1: {} + async@3.2.6: {} at-least-node@1.0.0: {} @@ -8283,27 +7791,29 @@ snapshots: await-to-js@3.0.0: {} - babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.0): + b4a@1.8.1: {} + + babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2): dependencies: - '@babel/compat-data': 7.29.0 - '@babel/core': 7.29.0 - '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + '@babel/compat-data': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) semver: 6.3.1 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-corejs3@0.14.2(@babel/core@7.29.0): + babel-plugin-polyfill-corejs3@0.14.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2): dependencies: - '@babel/core': 7.29.0 - '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) core-js-compat: 3.49.0 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-regenerator@0.6.8(@babel/core@7.29.0): + babel-plugin-polyfill-regenerator@0.6.8(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2): dependencies: - '@babel/core': 7.29.0 - '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -8311,11 +7821,44 @@ snapshots: balanced-match@4.0.4: {} + bare-events@2.9.1: {} + + bare-fs@4.7.4: + dependencies: + bare-events: 2.9.1 + bare-path: 3.1.1 + bare-stream: 2.13.3(bare-events@2.9.1) + bare-url: 2.4.6 + fast-fifo: 1.3.2 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + + bare-path@3.1.1: {} + + bare-stream@2.13.3(bare-events@2.9.1): + dependencies: + b4a: 1.8.1 + streamx: 2.28.0 + teex: 1.0.1 + optionalDependencies: + bare-events: 2.9.1 + transitivePeerDependencies: + - react-native-b4a + + bare-url@2.4.6: + dependencies: + bare-path: 3.1.1 + base-x@5.0.1: {} base64-js@1.5.1: {} - baseline-browser-mapping@2.10.0: {} + baseline-browser-mapping@2.11.8: {} + + bcrypt-pbkdf@1.0.2: + dependencies: + tweetnacl: 0.14.5 bidi-js@1.0.3: dependencies: @@ -8325,11 +7868,15 @@ snapshots: bind-event-listener@3.0.0: {} - blake3-wasm@2.1.5: {} + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 blurhash@2.0.5: {} - brace-expansion@5.0.4: + brace-expansion@5.0.9: dependencies: balanced-match: 4.0.4 @@ -8339,33 +7886,45 @@ snapshots: browser-encrypt-attachment@0.3.0: {} - browserslist@4.28.1: + browserslist@4.28.7: dependencies: - baseline-browser-mapping: 2.10.0 - caniuse-lite: 1.0.30001777 - electron-to-chromium: 1.5.307 - node-releases: 2.0.36 - update-browserslist-db: 1.2.3(browserslist@4.28.1) + baseline-browser-mapping: 2.11.8 + caniuse-lite: 1.0.30001806 + electron-to-chromium: 1.5.398 + node-releases: 2.0.51 + update-browserslist-db: 1.2.3(browserslist@4.28.7) bs58@6.0.0: dependencies: base-x: 5.0.1 + buffer-crc32@1.0.0: {} + buffer-from@1.1.2: {} + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + buffer@6.0.3: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - cac@6.7.14: {} + buildcheck@0.0.7: + optional: true + + byline@5.0.0: {} + + cac@7.0.0: {} call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 function-bind: 1.1.2 - call-bind@1.0.8: + call-bind@1.0.9: dependencies: call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 @@ -8381,7 +7940,7 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001777: {} + caniuse-lite@1.0.30001806: {} chai@6.2.2: {} @@ -8397,46 +7956,79 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + chownr@1.1.4: {} + chroma-js@3.2.0: {} classnames@2.5.1: {} - cloudflared@0.7.1: {} + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + cloudflared@0.7.3: {} clsx@2.1.1: {} + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + commander@2.20.3: {} + commander@8.3.0: {} + common-tags@1.8.2: {} - compute-scroll-into-view@3.1.1: {} + compress-commons@6.0.2: + dependencies: + crc-32: 1.2.2 + crc32-stream: 6.0.0 + is-stream: 2.0.1 + normalize-path: 3.0.0 + readable-stream: 4.7.0 confbox@0.1.8: {} - content-type@1.0.5: {} + content-type@2.0.0: {} convert-source-map@2.0.0: {} - cookie@1.1.1: {} + cookie-es@3.1.1: {} core-js-compat@3.49.0: dependencies: - browserslist: 4.28.1 + browserslist: 4.28.7 + + core-util-is@1.0.3: {} - cosmiconfig@8.3.6(typescript@5.9.3): + cosmiconfig@8.3.6(typescript@7.0.2): dependencies: import-fresh: 3.3.1 - js-yaml: 4.1.1 + js-yaml: 5.3.0 parse-json: 5.2.0 path-type: 4.0.0 optionalDependencies: - typescript: 5.9.3 + typescript: 7.0.2 - cross-fetch@4.0.0: + cpu-features@0.0.10: dependencies: - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding + buildcheck: 0.0.7 + nan: 2.28.0 + optional: true + + crc-32@1.2.2: {} + + crc32-stream@6.0.0: + dependencies: + crc-32: 1.2.2 + readable-stream: 4.7.0 + + create-web-stream@1.1.3: {} cross-spawn@7.0.6: dependencies: @@ -8455,14 +8047,12 @@ snapshots: css.escape@1.5.1: {} - cssesc@3.0.0: {} - csstype@3.2.3: {} - data-urls@7.0.0: + data-urls@7.0.0(@noble/hashes@2.3.0): dependencies: whatwg-mimetype: 5.0.0 - whatwg-url: 16.0.1 + whatwg-url: 16.0.1(@noble/hashes@2.3.0) transitivePeerDependencies: - '@noble/hashes' @@ -8484,11 +8074,13 @@ snapshots: es-errors: 1.3.0 is-data-view: 1.0.2 - dayjs@1.11.19: {} + dayjs@1.11.21: {} - debug@4.4.3: + debug@4.4.3(supports-color@10.2.2): dependencies: ms: 2.1.3 + optionalDependencies: + supports-color: 10.2.2 decimal.js@10.6.0: {} @@ -8512,42 +8104,66 @@ snapshots: dequal@2.0.3: {} + detect-europe-js@0.1.2: {} + detect-libc@2.1.2: {} - direction@1.0.4: {} + docker-compose@1.4.2: + dependencies: + yaml: 2.9.0 + + docker-modem@5.0.7(supports-color@10.2.2): + dependencies: + debug: 4.4.3(supports-color@10.2.2) + readable-stream: 3.6.2 + split-ca: 1.0.1 + ssh2: 1.17.0 + transitivePeerDependencies: + - supports-color + + dockerode@5.0.1(supports-color@10.2.2): + dependencies: + '@balena/dockerignore': 1.0.2 + '@grpc/grpc-js': 1.14.4 + '@grpc/proto-loader': 0.7.15 + docker-modem: 5.0.7(supports-color@10.2.2) + protobufjs: 7.6.5 + tar-fs: 2.1.5 + transitivePeerDependencies: + - supports-color dom-accessibility-api@0.5.16: {} dom-accessibility-api@0.6.3: {} - dom-serializer@2.0.0: + dom-serializer@3.1.1: dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - entities: 4.5.0 + domelementtype: 3.0.0 + domhandler: 6.0.1 + entities: 8.0.0 - domelementtype@2.3.0: {} + domelementtype@3.0.0: {} - domhandler@5.0.3: + domhandler@6.0.1: dependencies: - domelementtype: 2.3.0 + domelementtype: 3.0.0 - dompurify@3.3.3: + dompurify@3.4.13: optionalDependencies: '@types/trusted-types': 2.0.7 - domutils@3.2.2: + domutils@4.0.2: dependencies: - dom-serializer: 2.0.0 - domelementtype: 2.3.0 - domhandler: 5.0.3 + dom-serializer: 3.1.1 + domelementtype: 3.0.0 + domhandler: 6.0.1 dot-case@3.0.4: dependencies: no-case: 3.0.4 tslib: 2.8.1 - dotenv@16.6.1: {} + dotenv@17.4.2: {} dunder-proto@1.0.1: dependencies: @@ -8555,46 +8171,59 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 + eastasianwidth@0.2.0: {} + ejs@3.1.10: dependencies: jake: 10.9.4 - electron-to-chromium@1.5.307: {} + electron-to-chromium@1.5.398: {} + + emoji-regex@8.0.0: {} - emojibase-data@15.3.2(emojibase@15.3.1): + emoji-regex@9.2.2: {} + + emojibase-data@17.0.0(emojibase@17.0.0): dependencies: - emojibase: 15.3.1 + emojibase: 17.0.0 - emojibase@15.3.1: {} + emojibase@17.0.0: {} - entities@4.5.0: {} + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 - entities@6.0.1: {} + entities@4.5.0: {} - entities@7.0.1: {} + entities@8.0.0: {} error-ex@1.3.4: dependencies: is-arrayish: 0.2.1 - error-stack-parser-es@1.0.5: {} + es-abstract-get@1.0.0: + dependencies: + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + is-callable: 1.2.7 + object-inspect: 1.13.4 - es-abstract@1.24.1: + es-abstract@1.24.2: dependencies: array-buffer-byte-length: 1.0.2 arraybuffer.prototype.slice: 1.0.4 available-typed-arrays: 1.0.7 - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 data-view-buffer: 1.0.2 data-view-byte-length: 1.0.2 data-view-byte-offset: 1.0.1 es-define-property: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 es-set-tostringtag: 2.1.0 - es-to-primitive: 1.3.0 - function.prototype.name: 1.1.8 + es-to-primitive: 1.3.4 + function.prototype.name: 1.2.0 get-intrinsic: 1.3.0 get-proto: 1.0.1 get-symbol-description: 1.1.0 @@ -8603,7 +8232,7 @@ snapshots: has-property-descriptors: 1.0.2 has-proto: 1.2.0 has-symbols: 1.1.0 - hasown: 2.0.2 + hasown: 2.0.4 internal-slot: 1.1.0 is-array-buffer: 3.0.5 is-callable: 1.2.7 @@ -8619,32 +8248,30 @@ snapshots: object-inspect: 1.13.4 object-keys: 1.1.1 object.assign: 4.1.7 - own-keys: 1.0.1 + own-keys: 1.0.2 regexp.prototype.flags: 1.5.4 - safe-array-concat: 1.1.3 + safe-array-concat: 1.1.4 safe-push-apply: 1.0.0 safe-regex-test: 1.1.0 set-proto: 1.0.0 stop-iteration-iterator: 1.1.0 - string.prototype.trim: 1.2.10 - string.prototype.trimend: 1.0.9 + string.prototype.trim: 1.2.11 + string.prototype.trimend: 1.0.10 string.prototype.trimstart: 1.0.8 typed-array-buffer: 1.0.3 typed-array-byte-length: 1.0.3 typed-array-byte-offset: 1.0.4 - typed-array-length: 1.0.7 + typed-array-length: 1.0.8 unbox-primitive: 1.1.0 - which-typed-array: 1.1.20 + which-typed-array: 1.1.22 es-define-property@1.0.1: {} es-errors@1.3.0: {} - es-module-lexer@1.7.0: {} + es-module-lexer@2.3.1: {} - es-module-lexer@2.0.0: {} - - es-object-atoms@1.1.1: + es-object-atoms@1.1.2: dependencies: es-errors: 1.3.0 @@ -8653,99 +8280,102 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 has-tostringtag: 1.0.2 - hasown: 2.0.2 + hasown: 2.0.4 - es-to-primitive@1.3.0: + es-to-primitive@1.3.4: dependencies: + es-abstract-get: 1.0.0 + es-define-property: 1.0.1 + es-errors: 1.3.0 is-callable: 1.2.7 is-date-object: 1.1.0 is-symbol: 1.1.1 - esbuild@0.27.3: + esbuild@0.28.1: optionalDependencies: - '@esbuild/aix-ppc64': 0.27.3 - '@esbuild/android-arm': 0.27.3 - '@esbuild/android-arm64': 0.27.3 - '@esbuild/android-x64': 0.27.3 - '@esbuild/darwin-arm64': 0.27.3 - '@esbuild/darwin-x64': 0.27.3 - '@esbuild/freebsd-arm64': 0.27.3 - '@esbuild/freebsd-x64': 0.27.3 - '@esbuild/linux-arm': 0.27.3 - '@esbuild/linux-arm64': 0.27.3 - '@esbuild/linux-ia32': 0.27.3 - '@esbuild/linux-loong64': 0.27.3 - '@esbuild/linux-mips64el': 0.27.3 - '@esbuild/linux-ppc64': 0.27.3 - '@esbuild/linux-riscv64': 0.27.3 - '@esbuild/linux-s390x': 0.27.3 - '@esbuild/linux-x64': 0.27.3 - '@esbuild/netbsd-arm64': 0.27.3 - '@esbuild/netbsd-x64': 0.27.3 - '@esbuild/openbsd-arm64': 0.27.3 - '@esbuild/openbsd-x64': 0.27.3 - '@esbuild/openharmony-arm64': 0.27.3 - '@esbuild/sunos-x64': 0.27.3 - '@esbuild/win32-arm64': 0.27.3 - '@esbuild/win32-ia32': 0.27.3 - '@esbuild/win32-x64': 0.27.3 + '@esbuild/aix-ppc64': 0.28.1 + '@esbuild/android-arm': 0.28.1 + '@esbuild/android-arm64': 0.28.1 + '@esbuild/android-x64': 0.28.1 + '@esbuild/darwin-arm64': 0.28.1 + '@esbuild/darwin-x64': 0.28.1 + '@esbuild/freebsd-arm64': 0.28.1 + '@esbuild/freebsd-x64': 0.28.1 + '@esbuild/linux-arm': 0.28.1 + '@esbuild/linux-arm64': 0.28.1 + '@esbuild/linux-ia32': 0.28.1 + '@esbuild/linux-loong64': 0.28.1 + '@esbuild/linux-mips64el': 0.28.1 + '@esbuild/linux-ppc64': 0.28.1 + '@esbuild/linux-riscv64': 0.28.1 + '@esbuild/linux-s390x': 0.28.1 + '@esbuild/linux-x64': 0.28.1 + '@esbuild/netbsd-arm64': 0.28.1 + '@esbuild/netbsd-x64': 0.28.1 + '@esbuild/openbsd-arm64': 0.28.1 + '@esbuild/openbsd-x64': 0.28.1 + '@esbuild/openharmony-arm64': 0.28.1 + '@esbuild/sunos-x64': 0.28.1 + '@esbuild/win32-arm64': 0.28.1 + '@esbuild/win32-ia32': 0.28.1 + '@esbuild/win32-x64': 0.28.1 escalade@3.2.0: {} - estree-walker@1.0.1: {} - estree-walker@2.0.2: {} estree-walker@3.0.3: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 esutils@2.0.3: {} + eta@4.6.0: {} + eval@0.1.8: dependencies: - '@types/node': 24.10.13 + '@types/node': 24.13.2 require-like: 0.1.2 + event-target-shim@5.0.1: {} + eventemitter3@5.0.4: {} + events-universal@1.0.1: + dependencies: + bare-events: 2.9.1 + transitivePeerDependencies: + - bare-abort-controller + events@3.3.0: {} - expect-type@1.3.0: {} + expect-type@1.4.0: {} + + fake-indexeddb@6.2.5: {} fast-deep-equal@3.1.3: {} - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 + fast-fifo@1.3.2: {} fast-json-stable-stringify@2.1.0: {} - fast-uri@3.1.0: {} - - fastq@1.20.1: - dependencies: - reusify: 1.1.0 + fast-uri@4.1.2: {} fd-package-json@2.0.0: dependencies: walk-up-path: 4.0.0 - fdir@6.5.0(picomatch@4.0.3): + fdir@6.5.0(picomatch@4.0.5): optionalDependencies: - picomatch: 4.0.3 + picomatch: 4.0.5 - fflate@0.8.2: {} + fflate@0.8.3: {} file-saver@2.0.5: {} filelist@1.0.6: dependencies: - minimatch: 10.2.4 + minimatch: 10.2.6 fill-range@7.1.1: dependencies: @@ -8756,27 +8386,28 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 - flatted@3.4.2: {} + flatted@3.4.4: {} - focus-trap-react@10.3.1(prop-types@15.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + focus-trap-react@12.0.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): dependencies: - focus-trap: 7.8.0 - prop-types: 15.8.1 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - tabbable: 6.4.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + focus-trap: 8.2.2 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + tabbable: 6.5.0 - focus-trap@7.8.0: + focus-trap@8.2.2: dependencies: - tabbable: 6.4.0 + tabbable: 6.5.0 - folds@2.6.2(@vanilla-extract/css@1.18.0)(@vanilla-extract/recipes@0.5.7(@vanilla-extract/css@1.18.0))(classnames@2.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + folds@2.7.1(@vanilla-extract/css@1.21.2)(@vanilla-extract/recipes@0.5.7(@vanilla-extract/css@1.21.2))(classnames@2.5.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): dependencies: - '@vanilla-extract/css': 1.18.0 - '@vanilla-extract/recipes': 0.5.7(@vanilla-extract/css@1.18.0) + '@vanilla-extract/css': 1.21.2 + '@vanilla-extract/recipes': 0.5.7(@vanilla-extract/css@1.21.2) classnames: 2.5.1 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) for-each@0.3.5: dependencies: @@ -8791,35 +8422,34 @@ snapshots: dependencies: fd-package-json: 2.0.0 - framer-motion@12.34.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - motion-dom: 12.35.2 - motion-utils: 12.29.2 - tslib: 2.8.1 - optionalDependencies: - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + fs-constants@1.0.0: {} fs-extra@9.1.0: dependencies: at-least-node: 1.0.0 graceful-fs: 4.2.11 - jsonfile: 6.2.0 + jsonfile: 6.2.1 universalify: 2.0.1 + fsevents@2.3.2: + optional: true + fsevents@2.3.3: optional: true function-bind@1.1.2: {} - function.prototype.name@1.1.8: + function.prototype.name@1.2.0: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 - define-properties: 1.2.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 functions-have-names: 1.2.3 - hasown: 2.0.2 + has-property-descriptors: 1.0.2 + hasown: 2.0.4 is-callable: 1.2.7 + is-document.all: 1.0.0 functions-have-names@1.2.3: {} @@ -8827,25 +8457,29 @@ snapshots: gensync@1.0.0-beta.2: {} + get-caller-file@2.0.5: {} + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 function-bind: 1.1.2 get-proto: 1.0.1 gopd: 1.2.0 has-symbols: 1.1.0 - hasown: 2.0.2 + hasown: 2.0.4 math-intrinsics: 1.1.0 get-own-enumerable-property-symbols@3.0.2: {} + get-port@5.1.1: {} + get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 get-symbol-description@1.1.0: dependencies: @@ -8853,22 +8487,35 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 + get-tsconfig@4.14.1: + dependencies: + resolve-pkg-maps: 1.0.0 + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 + glob@10.5.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 10.2.6 + minipass: 7.1.3 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + glob@11.1.0: dependencies: foreground-child: 3.3.1 jackspeak: 4.2.3 - minimatch: 10.2.4 + minimatch: 10.2.6 minipass: 7.1.3 package-json-from-dist: 1.0.1 path-scurry: 2.0.2 glob@13.0.6: dependencies: - minimatch: 10.2.4 + minimatch: 10.2.6 minipass: 7.1.3 path-scurry: 2.0.2 @@ -8899,7 +8546,7 @@ snapshots: dependencies: has-symbols: 1.1.0 - hasown@2.0.2: + hasown@2.0.4: dependencies: function-bind: 1.1.2 @@ -8907,78 +8554,65 @@ snapshots: dependencies: react-is: 16.13.1 - html-dom-parser@5.0.3: + html-dom-parser@8.0.0: dependencies: - domhandler: 5.0.3 - htmlparser2: 9.0.0 + domhandler: 6.0.1 + htmlparser2: 12.0.0 - html-dom-parser@5.1.8: + html-dom-parser@8.0.2: dependencies: - domhandler: 5.0.3 - htmlparser2: 10.1.0 + domhandler: 6.0.1 + htmlparser2: 12.0.0 - html-encoding-sniffer@6.0.0: + html-encoding-sniffer@6.0.0(@noble/hashes@2.3.0): dependencies: - '@exodus/bytes': 1.15.0 + '@exodus/bytes': 1.15.1(@noble/hashes@2.3.0) transitivePeerDependencies: - '@noble/hashes' html-escaper@2.0.2: {} - html-parse-stringify@3.0.1: - dependencies: - void-elements: 3.1.0 + html-parse-stringify@4.0.1: {} - html-react-parser@4.2.10(react@18.3.1): + html-react-parser@6.1.5(@types/react@19.2.17)(react@19.2.8): dependencies: - domhandler: 5.0.3 - html-dom-parser: 5.0.3 - react: 18.3.1 + domhandler: 6.0.1 + html-dom-parser: 8.0.0 + react: 19.2.8 react-property: 2.0.2 - style-to-js: 1.1.8 - - htmlparser2@10.1.0: - dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - domutils: 3.2.2 - entities: 7.0.1 + style-to-js: 2.0.2 + optionalDependencies: + '@types/react': 19.2.17 - htmlparser2@9.0.0: + htmlparser2@12.0.0: dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - domutils: 3.2.2 - entities: 4.5.0 + domelementtype: 3.0.0 + domhandler: 6.0.1 + domutils: 4.0.2 + entities: 8.0.0 - https-proxy-agent@5.0.1: + https-proxy-agent@5.0.1(supports-color@10.2.2): dependencies: - agent-base: 6.0.2 - debug: 4.4.3 + agent-base: 6.0.2(supports-color@10.2.2) + debug: 4.4.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color i18next-browser-languagedetector@8.2.1: dependencies: - '@babel/runtime': 7.28.6 - - i18next-http-backend@2.7.3: - dependencies: - cross-fetch: 4.0.0 - transitivePeerDependencies: - - encoding - - i18next@25.8.17(typescript@5.9.3): - dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.7 + + i18next-http-backend@4.0.1: {} + + i18next@26.3.6(typescript@7.0.2): optionalDependencies: - typescript: 5.9.3 + typescript: 7.0.2 idb@7.1.1: {} ieee754@1.2.1: {} - immer@9.0.21: {} + immer@11.1.18: {} import-fresh@3.3.1: dependencies: @@ -8987,24 +8621,19 @@ snapshots: indent-string@4.0.0: {} - inline-style-parser@0.2.2: {} + inherits@2.0.4: {} + + inline-style-parser@0.2.9: {} internal-slot@1.1.0: dependencies: es-errors: 1.3.0 - hasown: 2.0.2 - side-channel: 1.1.0 - - intl-messageformat@10.7.18: - dependencies: - '@formatjs/ecma402-abstract': 2.3.6 - '@formatjs/fast-memoize': 2.2.7 - '@formatjs/icu-messageformat-parser': 2.11.4 - tslib: 2.8.1 + hasown: 2.0.4 + side-channel: 1.1.1 is-array-buffer@3.0.5: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 get-intrinsic: 1.3.0 @@ -9033,9 +8662,9 @@ snapshots: is-callable@1.2.7: {} - is-core-module@2.16.1: + is-core-module@2.16.2: dependencies: - hasown: 2.0.2 + hasown: 2.0.4 is-data-view@1.0.2: dependencies: @@ -9048,12 +8677,18 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 + is-document.all@1.0.0: + dependencies: + call-bound: 1.0.4 + is-extglob@2.1.1: {} is-finalizationregistry@1.1.1: dependencies: call-bound: 1.0.4 + is-fullwidth-code-point@3.0.0: {} + is-generator-function@1.1.2: dependencies: call-bound: 1.0.4 @@ -9074,7 +8709,7 @@ snapshots: is-negative-zero@2.0.3: {} - is-network-error@1.3.1: {} + is-network-error@1.3.2: {} is-number-object@1.1.1: dependencies: @@ -9085,8 +8720,6 @@ snapshots: is-obj@1.0.1: {} - is-plain-object@5.0.0: {} - is-potential-custom-element-name@1.0.1: {} is-regex@1.2.1: @@ -9094,7 +8727,7 @@ snapshots: call-bound: 1.0.4 gopd: 1.2.0 has-tostringtag: 1.0.2 - hasown: 2.0.2 + hasown: 2.0.4 is-regexp@1.0.0: {} @@ -9104,6 +8737,8 @@ snapshots: dependencies: call-bound: 1.0.4 + is-standalone-pwa@0.1.1: {} + is-stream@2.0.1: {} is-string@1.1.1: @@ -9119,7 +8754,7 @@ snapshots: is-typed-array@1.1.15: dependencies: - which-typed-array: 1.1.20 + which-typed-array: 1.1.22 is-weakmap@2.0.2: {} @@ -9132,6 +8767,8 @@ snapshots: call-bound: 1.0.4 get-intrinsic: 1.3.0 + isarray@1.0.0: {} + isarray@2.0.5: {} isexe@2.0.0: {} @@ -9149,6 +8786,12 @@ snapshots: html-escaper: 2.0.2 istanbul-lib-report: 3.0.1 + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + jackspeak@4.2.3: dependencies: '@isaacs/cliui': 9.0.0 @@ -9161,45 +8804,47 @@ snapshots: javascript-stringify@2.1.0: {} - jiti@2.6.1: {} + jiti@2.7.0: {} - jotai@2.18.1(@babel/core@7.29.0)(@babel/template@7.28.6)(@types/react@18.3.28)(react@18.3.1): + jose@6.2.9: {} + + jotai@2.20.2(@babel/core@7.29.7(supports-color@10.2.2))(@babel/template@7.29.7)(@types/react@19.2.17)(react@19.2.8): optionalDependencies: - '@babel/core': 7.29.0 - '@babel/template': 7.28.6 - '@types/react': 18.3.28 - react: 18.3.1 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/template': 7.29.7 + '@types/react': 19.2.17 + react: 19.2.8 js-tokens@10.0.0: {} js-tokens@4.0.0: {} - js-yaml@4.1.1: + js-yaml@5.3.0: dependencies: argparse: 2.0.1 - jsdom@29.0.0: + jsdom@30.0.1(@noble/hashes@2.3.0): dependencies: - '@asamuzakjp/css-color': 5.0.1 - '@asamuzakjp/dom-selector': 7.0.3 + '@asamuzakjp/css-color': 6.0.5 + '@asamuzakjp/dom-selector': 8.3.0 '@bramus/specificity': 2.4.2 - '@csstools/css-syntax-patches-for-csstree': 1.1.1(css-tree@3.2.1) - '@exodus/bytes': 1.15.0 + '@csstools/css-syntax-patches-for-csstree': 1.1.7(css-tree@3.2.1) + '@exodus/bytes': 1.15.1(@noble/hashes@2.3.0) css-tree: 3.2.1 - data-urls: 7.0.0 + data-urls: 7.0.0(@noble/hashes@2.3.0) decimal.js: 10.6.0 - html-encoding-sniffer: 6.0.0 + html-encoding-sniffer: 6.0.0(@noble/hashes@2.3.0) is-potential-custom-element-name: 1.0.1 - lru-cache: 11.2.7 - parse5: 8.0.0 + lru-cache: 11.5.2 + parse5: 8.0.1 saxes: 6.0.0 symbol-tree: 3.2.4 - tough-cookie: 6.0.1 - undici: 7.24.3 + tough-cookie: 6.0.2 + undici: 7.29.0 w3c-xmlserializer: 5.0.0 webidl-conversions: 8.0.1 whatwg-mimetype: 5.0.0 - whatwg-url: 16.0.1 + whatwg-url: 17.1.0(@noble/hashes@2.3.0) xml-name-validator: 5.0.0 transitivePeerDependencies: - '@noble/hashes' @@ -9210,11 +8855,9 @@ snapshots: json-schema-traverse@1.0.0: {} - json-schema@0.4.0: {} - json5@2.2.3: {} - jsonfile@6.2.0: + jsonfile@6.2.1: dependencies: universalify: 2.0.1 optionalDependencies: @@ -9222,50 +8865,121 @@ snapshots: jsonpointer@5.0.1: {} - jwt-decode@4.0.0: {} - - kleur@4.1.5: {} + katex@0.18.1: + dependencies: + commander: 8.3.0 - knip@5.85.0(@types/node@24.10.13)(typescript@5.9.3): + knip@6.32.1: dependencies: - '@nodelib/fs.walk': 1.2.8 - '@types/node': 24.10.13 - fast-glob: 3.3.3 + fdir: 6.5.0(picomatch@4.0.5) formatly: 0.3.0 - jiti: 2.6.1 - js-yaml: 4.1.1 - minimist: 1.2.8 - oxc-resolver: 11.19.1 - picocolors: 1.1.1 - picomatch: 4.0.3 - smol-toml: 1.6.0 + get-tsconfig: 4.14.1 + jiti: 2.7.0 + oxc-parser: 0.142.0 + oxc-resolver: 11.24.2 + picomatch: 4.0.5 + smol-toml: 1.7.1 strip-json-comments: 5.0.3 - typescript: 5.9.3 - zod: 4.3.6 + tinyglobby: 0.2.17 + unbash: 4.0.4 + yaml: 2.9.0 + zod: 4.4.3 + + lazystream@1.0.1: + dependencies: + readable-stream: 2.3.8 + + leaflet@1.9.4: {} leven@3.1.0: {} + lightningcss-android-arm64@1.33.0: + optional: true + + lightningcss-darwin-arm64@1.33.0: + optional: true + + lightningcss-darwin-x64@1.33.0: + optional: true + + lightningcss-freebsd-x64@1.33.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + + lightningcss-linux-arm64-musl@1.33.0: + optional: true + + lightningcss-linux-x64-gnu@1.33.0: + optional: true + + lightningcss-linux-x64-musl@1.33.0: + optional: true + + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + + lightningcss-win32-x64-msvc@1.33.0: + optional: true + + lightningcss@1.33.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + lines-and-columns@1.2.4: {} - linkify-react@4.3.2(linkifyjs@4.3.2)(react@18.3.1): + linkify-react@4.3.3(linkifyjs@4.3.3)(react@19.2.8): dependencies: - linkifyjs: 4.3.2 - react: 18.3.1 + linkifyjs: 4.3.3 + react: 19.2.8 - linkifyjs@4.3.2: {} + linkifyjs@4.3.3: {} + + livekit-client@2.21.0(@types/dom-mediacapture-record@1.0.22): + dependencies: + '@livekit/mutex': 1.1.1 + '@livekit/protocol': 1.50.4 + '@types/dom-mediacapture-record': 1.0.22 + events: 3.3.0 + jose: 6.2.9 + loglevel: 1.9.2 + sdp-transform: 2.15.0 + tslib: 2.8.1 + typed-emitter: 2.1.0 + webrtc-adapter: 9.0.6 locate-path@6.0.0: dependencies: p-locate: 5.0.0 + lodash.camelcase@4.3.0: {} + lodash.debounce@4.0.8: {} - lodash.sortby@4.7.0: {} + lodash@4.18.1: {} - lodash@4.17.23: {} + loglevel@1.9.1: {} loglevel@1.9.2: {} + long@5.3.2: {} + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -9276,9 +8990,7 @@ snapshots: lru-cache@10.4.3: {} - lru-cache@11.2.6: {} - - lru-cache@11.2.7: {} + lru-cache@11.5.2: {} lru-cache@5.1.1: dependencies: @@ -9286,105 +8998,85 @@ snapshots: lz-string@1.5.0: {} - magic-string@0.25.9: - dependencies: - sourcemap-codec: 1.4.8 - magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - magicast@0.5.2: + magicast@0.5.3: dependencies: - '@babel/parser': 7.29.0 - '@babel/types': 7.29.0 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 source-map-js: 1.2.1 make-dir@4.0.0: dependencies: - semver: 7.7.4 + semver: 7.8.5 + + marked@18.0.10: {} math-intrinsics@1.1.0: {} matrix-events-sdk@0.0.1: {} - matrix-js-sdk@38.4.0: + matrix-js-sdk@42.0.0: dependencies: - '@babel/runtime': 7.28.6 - '@matrix-org/matrix-sdk-crypto-wasm': 15.3.0 + '@babel/runtime': 8.0.0 + '@matrix-org/matrix-sdk-crypto-wasm': 18.4.0 another-json: 0.2.0 bs58: 6.0.0 - content-type: 1.0.5 - jwt-decode: 4.0.0 + content-type: 2.0.0 loglevel: 1.9.2 matrix-events-sdk: 0.0.1 - matrix-widget-api: 1.17.0 - oidc-client-ts: 3.4.1 - p-retry: 7.1.1 - sdp-transform: 2.15.0 + matrix-widget-api: 1.18.0 + p-retry: 8.0.0 + sdp-transform: 3.0.0 unhomoglyph: 1.0.6 - uuid: 13.0.0 matrix-widget-api@1.17.0: dependencies: '@types/events': 3.0.3 events: 3.3.0 - mdn-data@2.27.1: {} - - media-query-parser@2.0.2: + matrix-widget-api@1.18.0: dependencies: - '@babel/runtime': 7.28.6 + '@types/events': 3.0.3 + events: 3.3.0 - merge2@1.4.1: {} + mdn-data@2.27.1: {} - micromatch@4.0.8: + media-query-parser@2.0.2: dependencies: - braces: 3.0.3 - picomatch: 2.3.1 + '@babel/runtime': 7.29.7 min-indent@1.0.1: {} - miniflare@4.20260310.0: + minimatch@10.2.6: dependencies: - '@cspotcode/source-map-support': 0.8.1 - sharp: 0.34.5 - undici: 7.24.3 - workerd: 1.20260310.1 - ws: 8.18.0 - youch: 4.1.0-beta.10 - transitivePeerDependencies: - - bufferutil - - utf-8-validate + brace-expansion: 5.0.9 - minimatch@10.2.4: - dependencies: - brace-expansion: 5.0.4 + minipass@7.1.3: {} - minimist@1.2.8: {} + mkdirp-classic@0.5.3: {} - minipass@7.1.3: {} + mkdirp@3.0.1: {} - mlly@1.8.1: + mlly@1.8.2: dependencies: - acorn: 8.16.0 + acorn: 8.18.0 pathe: 2.0.3 pkg-types: 1.3.1 - ufo: 1.6.3 + ufo: 1.6.4 modern-ahocorasick@1.1.0: {} - motion-dom@12.35.2: - dependencies: - motion-utils: 12.29.2 - - motion-utils@12.29.2: {} - mrmime@2.0.1: {} ms@2.1.3: {} - nanoid@3.3.11: {} + nan@2.28.0: + optional: true + + nanoid@6.0.1: {} no-case@3.0.4: dependencies: @@ -9395,10 +9087,7 @@ snapshots: dependencies: whatwg-url: 5.0.0 - node-readable-to-web-readable-stream@0.4.2: - optional: true - - node-releases@2.0.36: {} + node-releases@2.0.51: {} normalize-path@3.0.0: {} @@ -9410,103 +9099,130 @@ snapshots: object.assign@4.1.7: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 has-symbols: 1.1.0 object-keys: 1.1.1 - obug@2.1.1: {} + obug@2.1.4: {} - oidc-client-ts@3.4.1: + once@1.4.0: dependencies: - jwt-decode: 4.0.0 + wrappy: 1.0.2 - own-keys@1.0.1: + orderedmap@2.1.1: {} + + own-keys@1.0.2: dependencies: + call-bound: 1.0.4 get-intrinsic: 1.3.0 object-keys: 1.1.1 safe-push-apply: 1.0.0 - oxc-resolver@11.19.1: + oxc-parser@0.142.0: + dependencies: + '@oxc-project/types': 0.142.0 + optionalDependencies: + '@oxc-parser/binding-android-arm-eabi': 0.142.0 + '@oxc-parser/binding-android-arm64': 0.142.0 + '@oxc-parser/binding-darwin-arm64': 0.142.0 + '@oxc-parser/binding-darwin-x64': 0.142.0 + '@oxc-parser/binding-freebsd-x64': 0.142.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.142.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.142.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.142.0 + '@oxc-parser/binding-linux-arm64-musl': 0.142.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.142.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.142.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.142.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.142.0 + '@oxc-parser/binding-linux-x64-gnu': 0.142.0 + '@oxc-parser/binding-linux-x64-musl': 0.142.0 + '@oxc-parser/binding-openharmony-arm64': 0.142.0 + '@oxc-parser/binding-wasm32-wasi': 0.142.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.142.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.142.0 + '@oxc-parser/binding-win32-x64-msvc': 0.142.0 + + oxc-resolver@11.24.2: optionalDependencies: - '@oxc-resolver/binding-android-arm-eabi': 11.19.1 - '@oxc-resolver/binding-android-arm64': 11.19.1 - '@oxc-resolver/binding-darwin-arm64': 11.19.1 - '@oxc-resolver/binding-darwin-x64': 11.19.1 - '@oxc-resolver/binding-freebsd-x64': 11.19.1 - '@oxc-resolver/binding-linux-arm-gnueabihf': 11.19.1 - '@oxc-resolver/binding-linux-arm-musleabihf': 11.19.1 - '@oxc-resolver/binding-linux-arm64-gnu': 11.19.1 - '@oxc-resolver/binding-linux-arm64-musl': 11.19.1 - '@oxc-resolver/binding-linux-ppc64-gnu': 11.19.1 - '@oxc-resolver/binding-linux-riscv64-gnu': 11.19.1 - '@oxc-resolver/binding-linux-riscv64-musl': 11.19.1 - '@oxc-resolver/binding-linux-s390x-gnu': 11.19.1 - '@oxc-resolver/binding-linux-x64-gnu': 11.19.1 - '@oxc-resolver/binding-linux-x64-musl': 11.19.1 - '@oxc-resolver/binding-openharmony-arm64': 11.19.1 - '@oxc-resolver/binding-wasm32-wasi': 11.19.1 - '@oxc-resolver/binding-win32-arm64-msvc': 11.19.1 - '@oxc-resolver/binding-win32-ia32-msvc': 11.19.1 - '@oxc-resolver/binding-win32-x64-msvc': 11.19.1 - - oxfmt@0.45.0: + '@oxc-resolver/binding-android-arm-eabi': 11.24.2 + '@oxc-resolver/binding-android-arm64': 11.24.2 + '@oxc-resolver/binding-darwin-arm64': 11.24.2 + '@oxc-resolver/binding-darwin-x64': 11.24.2 + '@oxc-resolver/binding-freebsd-x64': 11.24.2 + '@oxc-resolver/binding-linux-arm-gnueabihf': 11.24.2 + '@oxc-resolver/binding-linux-arm-musleabihf': 11.24.2 + '@oxc-resolver/binding-linux-arm64-gnu': 11.24.2 + '@oxc-resolver/binding-linux-arm64-musl': 11.24.2 + '@oxc-resolver/binding-linux-ppc64-gnu': 11.24.2 + '@oxc-resolver/binding-linux-riscv64-gnu': 11.24.2 + '@oxc-resolver/binding-linux-riscv64-musl': 11.24.2 + '@oxc-resolver/binding-linux-s390x-gnu': 11.24.2 + '@oxc-resolver/binding-linux-x64-gnu': 11.24.2 + '@oxc-resolver/binding-linux-x64-musl': 11.24.2 + '@oxc-resolver/binding-openharmony-arm64': 11.24.2 + '@oxc-resolver/binding-wasm32-wasi': 11.24.2 + '@oxc-resolver/binding-win32-arm64-msvc': 11.24.2 + '@oxc-resolver/binding-win32-x64-msvc': 11.24.2 + + oxfmt@0.61.0: dependencies: tinypool: 2.1.0 optionalDependencies: - '@oxfmt/binding-android-arm-eabi': 0.45.0 - '@oxfmt/binding-android-arm64': 0.45.0 - '@oxfmt/binding-darwin-arm64': 0.45.0 - '@oxfmt/binding-darwin-x64': 0.45.0 - '@oxfmt/binding-freebsd-x64': 0.45.0 - '@oxfmt/binding-linux-arm-gnueabihf': 0.45.0 - '@oxfmt/binding-linux-arm-musleabihf': 0.45.0 - '@oxfmt/binding-linux-arm64-gnu': 0.45.0 - '@oxfmt/binding-linux-arm64-musl': 0.45.0 - '@oxfmt/binding-linux-ppc64-gnu': 0.45.0 - '@oxfmt/binding-linux-riscv64-gnu': 0.45.0 - '@oxfmt/binding-linux-riscv64-musl': 0.45.0 - '@oxfmt/binding-linux-s390x-gnu': 0.45.0 - '@oxfmt/binding-linux-x64-gnu': 0.45.0 - '@oxfmt/binding-linux-x64-musl': 0.45.0 - '@oxfmt/binding-openharmony-arm64': 0.45.0 - '@oxfmt/binding-win32-arm64-msvc': 0.45.0 - '@oxfmt/binding-win32-ia32-msvc': 0.45.0 - '@oxfmt/binding-win32-x64-msvc': 0.45.0 - - oxlint-tsgolint@0.21.0: + '@oxfmt/binding-android-arm-eabi': 0.61.0 + '@oxfmt/binding-android-arm64': 0.61.0 + '@oxfmt/binding-darwin-arm64': 0.61.0 + '@oxfmt/binding-darwin-x64': 0.61.0 + '@oxfmt/binding-freebsd-x64': 0.61.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.61.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.61.0 + '@oxfmt/binding-linux-arm64-gnu': 0.61.0 + '@oxfmt/binding-linux-arm64-musl': 0.61.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.61.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.61.0 + '@oxfmt/binding-linux-riscv64-musl': 0.61.0 + '@oxfmt/binding-linux-s390x-gnu': 0.61.0 + '@oxfmt/binding-linux-x64-gnu': 0.61.0 + '@oxfmt/binding-linux-x64-musl': 0.61.0 + '@oxfmt/binding-openharmony-arm64': 0.61.0 + '@oxfmt/binding-win32-arm64-msvc': 0.61.0 + '@oxfmt/binding-win32-ia32-msvc': 0.61.0 + '@oxfmt/binding-win32-x64-msvc': 0.61.0 + + oxlint-tsgolint@7.0.2001: optionalDependencies: - '@oxlint-tsgolint/darwin-arm64': 0.21.0 - '@oxlint-tsgolint/darwin-x64': 0.21.0 - '@oxlint-tsgolint/linux-arm64': 0.21.0 - '@oxlint-tsgolint/linux-x64': 0.21.0 - '@oxlint-tsgolint/win32-arm64': 0.21.0 - '@oxlint-tsgolint/win32-x64': 0.21.0 - - oxlint@1.60.0(oxlint-tsgolint@0.21.0): + '@oxlint-tsgolint/darwin-arm64': 7.0.2001 + '@oxlint-tsgolint/darwin-x64': 7.0.2001 + '@oxlint-tsgolint/linux-arm64': 7.0.2001 + '@oxlint-tsgolint/linux-x64': 7.0.2001 + '@oxlint-tsgolint/win32-arm64': 7.0.2001 + '@oxlint-tsgolint/win32-x64': 7.0.2001 + + oxlint@1.76.0(oxlint-tsgolint@7.0.2001): optionalDependencies: - '@oxlint/binding-android-arm-eabi': 1.60.0 - '@oxlint/binding-android-arm64': 1.60.0 - '@oxlint/binding-darwin-arm64': 1.60.0 - '@oxlint/binding-darwin-x64': 1.60.0 - '@oxlint/binding-freebsd-x64': 1.60.0 - '@oxlint/binding-linux-arm-gnueabihf': 1.60.0 - '@oxlint/binding-linux-arm-musleabihf': 1.60.0 - '@oxlint/binding-linux-arm64-gnu': 1.60.0 - '@oxlint/binding-linux-arm64-musl': 1.60.0 - '@oxlint/binding-linux-ppc64-gnu': 1.60.0 - '@oxlint/binding-linux-riscv64-gnu': 1.60.0 - '@oxlint/binding-linux-riscv64-musl': 1.60.0 - '@oxlint/binding-linux-s390x-gnu': 1.60.0 - '@oxlint/binding-linux-x64-gnu': 1.60.0 - '@oxlint/binding-linux-x64-musl': 1.60.0 - '@oxlint/binding-openharmony-arm64': 1.60.0 - '@oxlint/binding-win32-arm64-msvc': 1.60.0 - '@oxlint/binding-win32-ia32-msvc': 1.60.0 - '@oxlint/binding-win32-x64-msvc': 1.60.0 - oxlint-tsgolint: 0.21.0 + '@oxlint/binding-android-arm-eabi': 1.76.0 + '@oxlint/binding-android-arm64': 1.76.0 + '@oxlint/binding-darwin-arm64': 1.76.0 + '@oxlint/binding-darwin-x64': 1.76.0 + '@oxlint/binding-freebsd-x64': 1.76.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.76.0 + '@oxlint/binding-linux-arm-musleabihf': 1.76.0 + '@oxlint/binding-linux-arm64-gnu': 1.76.0 + '@oxlint/binding-linux-arm64-musl': 1.76.0 + '@oxlint/binding-linux-ppc64-gnu': 1.76.0 + '@oxlint/binding-linux-riscv64-gnu': 1.76.0 + '@oxlint/binding-linux-riscv64-musl': 1.76.0 + '@oxlint/binding-linux-s390x-gnu': 1.76.0 + '@oxlint/binding-linux-x64-gnu': 1.76.0 + '@oxlint/binding-linux-x64-musl': 1.76.0 + '@oxlint/binding-openharmony-arm64': 1.76.0 + '@oxlint/binding-win32-arm64-msvc': 1.76.0 + '@oxlint/binding-win32-ia32-msvc': 1.76.0 + '@oxlint/binding-win32-x64-msvc': 1.76.0 + oxlint-tsgolint: 7.0.2001 p-limit@3.1.0: dependencies: @@ -9516,11 +9232,11 @@ snapshots: dependencies: p-limit: 3.1.0 - p-map@7.0.4: {} + p-map@7.0.6: {} - p-retry@7.1.1: + p-retry@8.0.0: dependencies: - is-network-error: 1.3.1 + is-network-error: 1.3.2 package-json-from-dist@1.0.1: {} @@ -9530,14 +9246,14 @@ snapshots: parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.29.0 + '@babel/code-frame': 7.29.7 error-ex: 1.3.4 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - parse5@8.0.0: + parse5@8.0.1: dependencies: - entities: 6.0.1 + entities: 8.0.0 path-exists@4.0.0: {} @@ -9545,41 +9261,49 @@ snapshots: path-parse@1.0.7: {} - path-scurry@2.0.2: + path-scurry@1.11.1: dependencies: - lru-cache: 11.2.6 + lru-cache: 10.4.3 minipass: 7.1.3 - path-to-regexp@6.3.0: {} + path-scurry@2.0.2: + dependencies: + lru-cache: 11.5.2 + minipass: 7.1.3 path-type@4.0.0: {} pathe@2.0.3: {} - pdfjs-dist@5.5.207: + pdfjs-dist@6.2.108: optionalDependencies: - '@napi-rs/canvas': 0.1.96 - node-readable-to-web-readable-stream: 0.4.2 + '@napi-rs/canvas': 1.0.3 picocolors@1.1.1: {} - picomatch@2.3.1: {} - picomatch@2.3.2: {} - picomatch@4.0.3: {} + picomatch@4.0.5: {} pkg-types@1.3.1: dependencies: confbox: 0.1.8 - mlly: 1.8.1 + mlly: 1.8.2 pathe: 2.0.3 + playwright-core@1.62.1: {} + + playwright@1.62.1: + dependencies: + playwright-core: 1.62.1 + optionalDependencies: + fsevents: 2.3.2 + possible-typed-array-names@1.1.0: {} - postcss@8.5.8: + postcss@8.5.25: dependencies: - nanoid: 3.3.11 + nanoid: 6.0.1 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -9593,6 +9317,10 @@ snapshots: ansi-styles: 5.2.0 react-is: 17.0.2 + process-nextick-args@2.0.1: {} + + process@0.11.10: {} + progress@2.0.3: {} prop-types@15.8.1: @@ -9601,132 +9329,202 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 + proper-lockfile@4.1.2: + dependencies: + graceful-fs: 4.2.11 + retry: 0.12.0 + signal-exit: 3.0.7 + + properties-reader@3.0.1(supports-color@10.2.2): + dependencies: + '@kwsites/file-exists': 1.1.1(supports-color@10.2.2) + mkdirp: 3.0.1 + transitivePeerDependencies: + - supports-color + + prosemirror-commands@1.7.2: + dependencies: + prosemirror-model: 1.25.11 + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.0 + + prosemirror-history@1.5.0: + dependencies: + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.0 + prosemirror-view: 1.42.2 + rope-sequence: 1.3.4 + + prosemirror-keymap@1.2.3: + dependencies: + prosemirror-state: 1.4.4 + w3c-keyname: 2.2.8 + + prosemirror-model@1.25.11: + dependencies: + orderedmap: 2.1.1 + + prosemirror-state@1.4.4: + dependencies: + prosemirror-model: 1.25.11 + prosemirror-transform: 1.12.0 + prosemirror-view: 1.42.2 + + prosemirror-transform@1.12.0: + dependencies: + prosemirror-model: 1.25.11 + + prosemirror-view@1.42.2: + dependencies: + prosemirror-model: 1.25.11 + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.0 + + protobufjs@7.6.5: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.5 + '@protobufjs/eventemitter': 1.1.1 + '@protobufjs/fetch': 1.1.1 + '@protobufjs/float': 1.0.2 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.2 + '@types/node': 24.13.2 + long: 5.3.2 + proxy-from-env@1.1.0: {} - punycode@2.3.1: {} + pump@3.0.4: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 - queue-microtask@1.2.3: {} + punycode@2.3.1: {} raf-schd@4.0.3: {} - react-aria@3.47.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@internationalized/string': 3.2.7 - '@react-aria/breadcrumbs': 3.5.32(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/button': 3.14.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/calendar': 3.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/checkbox': 3.16.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/color': 3.1.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/combobox': 3.15.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/datepicker': 3.16.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/dialog': 3.5.34(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/disclosure': 3.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/dnd': 3.11.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/focus': 3.21.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/gridlist': 3.14.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/i18n': 3.12.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/interactions': 3.27.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/label': 3.7.25(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/landmark': 3.0.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/link': 3.8.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/listbox': 3.15.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/menu': 3.21.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/meter': 3.4.30(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/numberfield': 3.12.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/overlays': 3.31.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/progress': 3.4.30(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/radio': 3.12.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/searchfield': 3.8.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/select': 3.17.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/selection': 3.27.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/separator': 3.4.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/slider': 3.8.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/ssr': 3.9.10(react@18.3.1) - '@react-aria/switch': 3.7.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/table': 3.17.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/tabs': 3.11.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/tag': 3.8.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/textfield': 3.18.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/toast': 3.0.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/tooltip': 3.9.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/tree': 3.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/visually-hidden': 3.8.31(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-types/shared': 3.33.1(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - react-async-script@1.2.0(react@18.3.1): + react-aria@3.50.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8): + dependencies: + '@internationalized/date': 3.12.2 + '@internationalized/number': 3.6.7 + '@internationalized/string': 3.2.9 + '@react-types/shared': 3.36.0(react@19.2.8) + '@swc/helpers': 0.5.23 + aria-hidden: 1.2.6 + clsx: 2.1.1 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + react-stately: 3.48.0(react@19.2.8) + use-sync-external-store: 1.6.0(react@19.2.8) + + react-async-script@1.2.0(react@19.2.8): dependencies: hoist-non-react-statics: 3.3.2 prop-types: 15.8.1 - react: 18.3.1 + react: 19.2.8 - react-blurhash@0.3.0(blurhash@2.0.5)(react@18.3.1): + react-blurhash@0.3.0(blurhash@2.0.5)(react@19.2.8): dependencies: blurhash: 2.0.5 - react: 18.3.1 + react: 19.2.8 - react-colorful@5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-colorful@5.8.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8): dependencies: - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) - react-dom@18.3.1(react@18.3.1): + react-dom@19.2.8(react@19.2.8): dependencies: - loose-envify: 1.4.0 - react: 18.3.1 - scheduler: 0.23.2 + react: 19.2.8 + scheduler: 0.27.0 - react-google-recaptcha@2.1.0(react@18.3.1): + react-google-recaptcha@3.1.0(react@19.2.8): dependencies: prop-types: 15.8.1 - react: 18.3.1 - react-async-script: 1.2.0(react@18.3.1) + react: 19.2.8 + react-async-script: 1.2.0(react@19.2.8) - react-i18next@16.5.7(i18next@25.8.17(typescript@5.9.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3): + react-i18next@17.0.11(i18next@26.3.6(typescript@7.0.2))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(typescript@7.0.2): dependencies: - '@babel/runtime': 7.28.6 - html-parse-stringify: 3.0.1 - i18next: 25.8.17(typescript@5.9.3) - react: 18.3.1 - use-sync-external-store: 1.6.0(react@18.3.1) + '@babel/runtime': 7.29.7 + html-parse-stringify: 4.0.1 + i18next: 26.3.6(typescript@7.0.2) + react: 19.2.8 + use-sync-external-store: 1.6.0(react@19.2.8) optionalDependencies: - react-dom: 18.3.1(react@18.3.1) - typescript: 5.9.3 + react-dom: 19.2.8(react@19.2.8) + typescript: 7.0.2 react-is@16.13.1: {} react-is@17.0.2: {} + react-leaflet@5.0.0(leaflet@1.9.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): + dependencies: + '@react-leaflet/core': 3.0.0(leaflet@1.9.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + leaflet: 1.9.4 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + react-property@2.0.2: {} - react-range@1.10.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-range@1.10.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8): + dependencies: + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + + react-router@8.3.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8): dependencies: - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + cookie-es: 3.1.1 + react: 19.2.8 + optionalDependencies: + react-dom: 19.2.8(react@19.2.8) + + react-stately@3.48.0(react@19.2.8): + dependencies: + '@internationalized/date': 3.12.2 + '@internationalized/number': 3.6.7 + '@internationalized/string': 3.2.9 + '@react-types/shared': 3.36.0(react@19.2.8) + '@swc/helpers': 0.5.23 + react: 19.2.8 + use-sync-external-store: 1.6.0(react@19.2.8) - react-refresh@0.18.0: {} + react@19.2.8: {} - react-router-dom@6.30.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + readable-stream@2.3.8: dependencies: - '@remix-run/router': 1.23.2 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-router: 6.30.3(react@18.3.1) + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 - react-router@6.30.3(react@18.3.1): + readable-stream@3.6.2: dependencies: - '@remix-run/router': 1.23.2 - react: 18.3.1 + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 - react@18.3.1: + readable-stream@4.7.0: dependencies: - loose-envify: 1.4.0 + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + + readdir-glob@1.1.3: + dependencies: + minimatch: 10.2.6 readdirp@3.6.0: dependencies: - picomatch: 2.3.1 + picomatch: 2.3.2 redent@3.0.0: dependencies: @@ -9735,11 +9533,11 @@ snapshots: reflect.getprototypeof@1.0.10: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 - es-abstract: 1.24.1 + es-abstract: 1.24.2 es-errors: 1.3.0 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 get-intrinsic: 1.3.0 get-proto: 1.0.1 which-builtin-type: 1.2.1 @@ -9752,7 +9550,7 @@ snapshots: regexp.prototype.flags@1.5.4: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 es-errors: 1.3.0 get-proto: 1.0.1 @@ -9764,73 +9562,105 @@ snapshots: regenerate: 1.4.2 regenerate-unicode-properties: 10.2.2 regjsgen: 0.8.0 - regjsparser: 0.13.0 + regjsparser: 0.13.2 unicode-match-property-ecmascript: 2.0.0 unicode-match-property-value-ecmascript: 2.2.1 regjsgen@0.8.0: {} - regjsparser@0.13.0: + regjsparser@0.13.2: dependencies: jsesc: 3.1.0 + require-directory@2.1.1: {} + require-from-string@2.0.2: {} require-like@0.1.2: {} resolve-from@4.0.0: {} - resolve@1.22.11: + resolve-pkg-maps@1.0.0: {} + + resolve@1.22.12: dependencies: - is-core-module: 2.16.1 + es-errors: 1.3.0 + is-core-module: 2.16.2 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - reusify@1.1.0: {} + retry@0.12.0: {} - rollup@4.59.0: + rolldown@1.2.1: dependencies: - '@types/estree': 1.0.8 + '@oxc-project/types': 0.142.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.2.1 + '@rolldown/binding-darwin-arm64': 1.2.1 + '@rolldown/binding-darwin-x64': 1.2.1 + '@rolldown/binding-freebsd-x64': 1.2.1 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.1 + '@rolldown/binding-linux-arm64-gnu': 1.2.1 + '@rolldown/binding-linux-arm64-musl': 1.2.1 + '@rolldown/binding-linux-ppc64-gnu': 1.2.1 + '@rolldown/binding-linux-s390x-gnu': 1.2.1 + '@rolldown/binding-linux-x64-gnu': 1.2.1 + '@rolldown/binding-linux-x64-musl': 1.2.1 + '@rolldown/binding-openharmony-arm64': 1.2.1 + '@rolldown/binding-wasm32-wasi': 1.2.1 + '@rolldown/binding-win32-arm64-msvc': 1.2.1 + '@rolldown/binding-win32-x64-msvc': 1.2.1 + + rollup@4.62.3: + dependencies: + '@types/estree': 1.0.9 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.59.0 - '@rollup/rollup-android-arm64': 4.59.0 - '@rollup/rollup-darwin-arm64': 4.59.0 - '@rollup/rollup-darwin-x64': 4.59.0 - '@rollup/rollup-freebsd-arm64': 4.59.0 - '@rollup/rollup-freebsd-x64': 4.59.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.59.0 - '@rollup/rollup-linux-arm-musleabihf': 4.59.0 - '@rollup/rollup-linux-arm64-gnu': 4.59.0 - '@rollup/rollup-linux-arm64-musl': 4.59.0 - '@rollup/rollup-linux-loong64-gnu': 4.59.0 - '@rollup/rollup-linux-loong64-musl': 4.59.0 - '@rollup/rollup-linux-ppc64-gnu': 4.59.0 - '@rollup/rollup-linux-ppc64-musl': 4.59.0 - '@rollup/rollup-linux-riscv64-gnu': 4.59.0 - '@rollup/rollup-linux-riscv64-musl': 4.59.0 - '@rollup/rollup-linux-s390x-gnu': 4.59.0 - '@rollup/rollup-linux-x64-gnu': 4.59.0 - '@rollup/rollup-linux-x64-musl': 4.59.0 - '@rollup/rollup-openbsd-x64': 4.59.0 - '@rollup/rollup-openharmony-arm64': 4.59.0 - '@rollup/rollup-win32-arm64-msvc': 4.59.0 - '@rollup/rollup-win32-ia32-msvc': 4.59.0 - '@rollup/rollup-win32-x64-gnu': 4.59.0 - '@rollup/rollup-win32-x64-msvc': 4.59.0 + '@rollup/rollup-android-arm-eabi': 4.62.3 + '@rollup/rollup-android-arm64': 4.62.3 + '@rollup/rollup-darwin-arm64': 4.62.3 + '@rollup/rollup-darwin-x64': 4.62.3 + '@rollup/rollup-freebsd-arm64': 4.62.3 + '@rollup/rollup-freebsd-x64': 4.62.3 + '@rollup/rollup-linux-arm-gnueabihf': 4.62.3 + '@rollup/rollup-linux-arm-musleabihf': 4.62.3 + '@rollup/rollup-linux-arm64-gnu': 4.62.3 + '@rollup/rollup-linux-arm64-musl': 4.62.3 + '@rollup/rollup-linux-loong64-gnu': 4.62.3 + '@rollup/rollup-linux-loong64-musl': 4.62.3 + '@rollup/rollup-linux-ppc64-gnu': 4.62.3 + '@rollup/rollup-linux-ppc64-musl': 4.62.3 + '@rollup/rollup-linux-riscv64-gnu': 4.62.3 + '@rollup/rollup-linux-riscv64-musl': 4.62.3 + '@rollup/rollup-linux-s390x-gnu': 4.62.3 + '@rollup/rollup-linux-x64-gnu': 4.62.3 + '@rollup/rollup-linux-x64-musl': 4.62.3 + '@rollup/rollup-openbsd-x64': 4.62.3 + '@rollup/rollup-openharmony-arm64': 4.62.3 + '@rollup/rollup-win32-arm64-msvc': 4.62.3 + '@rollup/rollup-win32-ia32-msvc': 4.62.3 + '@rollup/rollup-win32-x64-gnu': 4.62.3 + '@rollup/rollup-win32-x64-msvc': 4.62.3 fsevents: 2.3.3 - run-parallel@1.2.0: + rope-sequence@1.3.4: {} + + rxjs@7.8.2: dependencies: - queue-microtask: 1.2.3 + tslib: 2.8.1 - safe-array-concat@1.1.3: + safe-array-concat@1.1.4: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 get-intrinsic: 1.3.0 has-symbols: 1.1.0 isarray: 2.0.5 + safe-buffer@5.1.2: {} + + safe-buffer@5.2.1: {} + safe-push-apply@1.0.0: dependencies: es-errors: 1.3.0 @@ -9842,25 +9672,25 @@ snapshots: es-errors: 1.3.0 is-regex: 1.2.1 + safer-buffer@2.1.2: {} + saxes@6.0.0: dependencies: xmlchars: 2.2.0 - scheduler@0.23.2: - dependencies: - loose-envify: 1.4.0 - - scroll-into-view-if-needed@3.1.0: - dependencies: - compute-scroll-into-view: 3.1.1 + scheduler@0.27.0: {} sdp-transform@2.15.0: {} + sdp-transform@3.0.0: {} + + sdp@3.2.2: {} + semver@6.3.1: {} - semver@7.7.4: {} + semver@7.8.5: {} - serialize-javascript@7.0.5: {} + serialize-javascript@7.0.7: {} set-function-length@1.2.2: dependencies: @@ -9882,38 +9712,7 @@ snapshots: dependencies: dunder-proto: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.1.1 - - sharp@0.34.5: - dependencies: - '@img/colour': 1.1.0 - detect-libc: 2.1.2 - semver: 7.7.4 - optionalDependencies: - '@img/sharp-darwin-arm64': 0.34.5 - '@img/sharp-darwin-x64': 0.34.5 - '@img/sharp-libvips-darwin-arm64': 1.2.4 - '@img/sharp-libvips-darwin-x64': 1.2.4 - '@img/sharp-libvips-linux-arm': 1.2.4 - '@img/sharp-libvips-linux-arm64': 1.2.4 - '@img/sharp-libvips-linux-ppc64': 1.2.4 - '@img/sharp-libvips-linux-riscv64': 1.2.4 - '@img/sharp-libvips-linux-s390x': 1.2.4 - '@img/sharp-libvips-linux-x64': 1.2.4 - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 - '@img/sharp-linux-arm': 0.34.5 - '@img/sharp-linux-arm64': 0.34.5 - '@img/sharp-linux-ppc64': 0.34.5 - '@img/sharp-linux-riscv64': 0.34.5 - '@img/sharp-linux-s390x': 0.34.5 - '@img/sharp-linux-x64': 0.34.5 - '@img/sharp-linuxmusl-arm64': 0.34.5 - '@img/sharp-linuxmusl-x64': 0.34.5 - '@img/sharp-wasm32': 0.34.5 - '@img/sharp-win32-arm64': 0.34.5 - '@img/sharp-win32-ia32': 0.34.5 - '@img/sharp-win32-x64': 0.34.5 + es-object-atoms: 1.1.2 shebang-command@2.0.0: dependencies: @@ -9921,7 +9720,7 @@ snapshots: shebang-regex@3.0.0: {} - side-channel-list@1.0.0: + side-channel-list@1.0.1: dependencies: es-errors: 1.3.0 object-inspect: 1.13.4 @@ -9941,16 +9740,18 @@ snapshots: object-inspect: 1.13.4 side-channel-map: 1.0.1 - side-channel@1.1.0: + side-channel@1.1.1: dependencies: es-errors: 1.3.0 object-inspect: 1.13.4 - side-channel-list: 1.0.0 + side-channel-list: 1.0.1 side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 siginfo@2.0.0: {} + signal-exit@3.0.7: {} + signal-exit@4.1.0: {} sirv@3.0.2: @@ -9959,40 +9760,9 @@ snapshots: mrmime: 2.0.1 totalist: 3.0.1 - slate-dom@0.123.0(slate@0.123.0): - dependencies: - '@juggle/resize-observer': 3.4.0 - direction: 1.0.4 - is-hotkey: 0.2.0 - is-plain-object: 5.0.0 - lodash: 4.17.23 - scroll-into-view-if-needed: 3.1.0 - slate: 0.123.0 - tiny-invariant: 1.3.1 - - slate-history@0.113.1(slate@0.123.0): - dependencies: - is-plain-object: 5.0.0 - slate: 0.123.0 - - slate-react@0.123.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.123.0(slate@0.123.0))(slate@0.123.0): - dependencies: - '@juggle/resize-observer': 3.4.0 - direction: 1.0.4 - is-hotkey: 0.2.0 - lodash: 4.17.23 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - scroll-into-view-if-needed: 3.1.0 - slate: 0.123.0 - slate-dom: 0.123.0(slate@0.123.0) - tiny-invariant: 1.3.1 - - slate@0.123.0: {} + smob@1.6.2: {} - smob@1.6.1: {} - - smol-toml@1.6.0: {} + smol-toml@1.7.1: {} snake-case@3.0.4: dependencies: @@ -10008,59 +9778,100 @@ snapshots: source-map@0.6.1: {} - source-map@0.8.0-beta.0: + source-map@0.8.0: {} + + split-ca@1.0.1: {} + + ssh-remote-port-forward@1.0.4: dependencies: - whatwg-url: 7.1.0 + '@types/ssh2': 0.5.52 + ssh2: 1.17.0 - sourcemap-codec@1.4.8: {} + ssh2@1.17.0: + dependencies: + asn1: 0.2.6 + bcrypt-pbkdf: 1.0.2 + optionalDependencies: + cpu-features: 0.0.10 + nan: 2.28.0 stackback@0.0.2: {} - std-env@4.0.0: {} + std-env@4.2.0: {} stop-iteration-iterator@1.1.0: dependencies: es-errors: 1.3.0 internal-slot: 1.1.0 + streamx@2.28.0: + dependencies: + events-universal: 1.0.1 + fast-fifo: 1.3.2 + text-decoder: 1.2.7 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.2.0 + string.prototype.matchall@4.0.12: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.1 + es-abstract: 1.24.2 es-errors: 1.3.0 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 get-intrinsic: 1.3.0 gopd: 1.2.0 has-symbols: 1.1.0 internal-slot: 1.1.0 regexp.prototype.flags: 1.5.4 set-function-name: 2.0.2 - side-channel: 1.1.0 + side-channel: 1.1.1 - string.prototype.trim@1.2.10: + string.prototype.trim@1.2.11: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 define-data-property: 1.1.4 define-properties: 1.2.1 - es-abstract: 1.24.1 - es-object-atoms: 1.1.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.2 has-property-descriptors: 1.0.2 + safe-regex-test: 1.1.0 - string.prototype.trimend@1.0.9: + string.prototype.trimend@1.0.10: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 string.prototype.trimstart@1.0.8: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 stringify-object@3.3.0: dependencies: @@ -10068,6 +9879,14 @@ snapshots: is-obj: 1.0.1 is-regexp: 1.0.0 + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.2.2 + strip-comments@2.0.1: {} strip-indent@3.0.0: @@ -10076,15 +9895,16 @@ snapshots: strip-json-comments@5.0.3: {} - style-to-js@1.1.8: + style-to-js@2.0.2: dependencies: - style-to-object: 1.0.3 + style-to-object: 2.0.2 - style-to-object@1.0.3: + style-to-object@2.0.2: dependencies: - inline-style-parser: 0.2.2 + inline-style-parser: 0.2.9 - supports-color@10.2.2: {} + supports-color@10.2.2: + optional: true supports-color@7.2.0: dependencies: @@ -10096,10 +9916,64 @@ snapshots: symbol-tree@3.2.4: {} - tabbable@6.4.0: {} + tabbable@6.5.0: {} + + tar-fs@2.1.5: + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.4 + tar-stream: 2.2.0 + + tar-fs@3.1.3: + dependencies: + pump: 3.0.4 + tar-stream: 3.2.0 + optionalDependencies: + bare-fs: 4.7.4 + bare-path: 3.1.1 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - react-native-b4a tar-mini@0.2.0: {} + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.5 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + + tar-stream@3.2.0: + dependencies: + b4a: 1.8.1 + bare-fs: 4.7.4 + fast-fifo: 1.3.2 + streamx: 2.28.0 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - react-native-b4a + + tauri-plugin-android-fs-api@28.4.0: + dependencies: + '@tauri-apps/api': 2.11.1 + create-web-stream: 1.1.3 + + tauri-plugin-app-icon-api@https://codeload.github.com/SableClient/tauri-plugin-app-icon/tar.gz/bab80ae17f6d018702e472c539d0f02eba480c21: + dependencies: + '@tauri-apps/api': 2.11.1 + + teex@1.0.1: + dependencies: + streamx: 2.28.0 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + temp-dir@2.0.0: {} tempy@0.6.0: @@ -10109,33 +9983,62 @@ snapshots: type-fest: 0.16.0 unique-string: 2.0.0 - terser@5.46.1: + terser@5.49.0: dependencies: '@jridgewell/source-map': 0.3.11 - acorn: 8.16.0 + acorn: 8.18.0 commander: 2.20.3 source-map-support: 0.5.21 - tiny-invariant@1.3.1: {} + testcontainers@12.0.4(supports-color@10.2.2): + dependencies: + '@balena/dockerignore': 1.0.2 + '@types/dockerode': 4.0.1 + archiver: 7.0.1 + async-lock: 1.4.1 + byline: 5.0.0 + debug: 4.4.3(supports-color@10.2.2) + docker-compose: 1.4.2 + dockerode: 5.0.1(supports-color@10.2.2) + get-port: 5.1.1 + proper-lockfile: 4.1.2 + properties-reader: 3.0.1(supports-color@10.2.2) + ssh-remote-port-forward: 1.0.4 + tar-fs: 3.1.3 + tmp: 0.2.7 + undici: 8.9.0 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - react-native-b4a + - supports-color + + text-decoder@1.2.7: + dependencies: + b4a: 1.8.1 + transitivePeerDependencies: + - react-native-b4a tinybench@2.9.0: {} - tinyexec@1.0.4: {} + tinyexec@1.2.4: {} - tinyglobby@0.2.15: + tinyglobby@0.2.17: dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 tinypool@2.1.0: {} - tinyrainbow@3.1.0: {} + tinyrainbow@3.1.1: {} - tldts-core@7.0.25: {} + tldts-core@7.4.9: {} - tldts@7.0.25: + tldts@7.4.9: dependencies: - tldts-core: 7.0.25 + tldts-core: 7.4.9 + + tmp@0.2.7: {} to-regex-range@5.0.1: dependencies: @@ -10143,22 +10046,20 @@ snapshots: totalist@3.0.1: {} - tough-cookie@6.0.1: + tough-cookie@6.0.2: dependencies: - tldts: 7.0.25 + tldts: 7.4.9 tr46@0.0.3: {} - tr46@1.0.1: - dependencies: - punycode: 2.3.1 - tr46@6.0.0: dependencies: punycode: 2.3.1 tslib@2.8.1: {} + tweetnacl@0.14.5: {} + type-fest@0.16.0: {} typed-array-buffer@1.0.3: @@ -10169,7 +10070,7 @@ snapshots: typed-array-byte-length@1.0.3: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 for-each: 0.3.5 gopd: 1.2.0 has-proto: 1.2.0 @@ -10178,27 +10079,60 @@ snapshots: typed-array-byte-offset@1.0.4: dependencies: available-typed-arrays: 1.0.7 - call-bind: 1.0.8 + call-bind: 1.0.9 for-each: 0.3.5 gopd: 1.2.0 has-proto: 1.2.0 is-typed-array: 1.1.15 reflect.getprototypeof: 1.0.10 - typed-array-length@1.0.7: + typed-array-length@1.0.8: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 for-each: 0.3.5 gopd: 1.2.0 is-typed-array: 1.1.15 possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript@5.9.3: {} - - ua-parser-js@1.0.41: {} + typed-emitter@2.1.0: + optionalDependencies: + rxjs: 7.8.2 - ufo@1.6.3: {} + typescript@7.0.2: + optionalDependencies: + '@typescript/typescript-aix-ppc64': 7.0.2 + '@typescript/typescript-darwin-arm64': 7.0.2 + '@typescript/typescript-darwin-x64': 7.0.2 + '@typescript/typescript-freebsd-arm64': 7.0.2 + '@typescript/typescript-freebsd-x64': 7.0.2 + '@typescript/typescript-linux-arm': 7.0.2 + '@typescript/typescript-linux-arm64': 7.0.2 + '@typescript/typescript-linux-loong64': 7.0.2 + '@typescript/typescript-linux-mips64el': 7.0.2 + '@typescript/typescript-linux-ppc64': 7.0.2 + '@typescript/typescript-linux-riscv64': 7.0.2 + '@typescript/typescript-linux-s390x': 7.0.2 + '@typescript/typescript-linux-x64': 7.0.2 + '@typescript/typescript-netbsd-arm64': 7.0.2 + '@typescript/typescript-netbsd-x64': 7.0.2 + '@typescript/typescript-openbsd-arm64': 7.0.2 + '@typescript/typescript-openbsd-x64': 7.0.2 + '@typescript/typescript-sunos-x64': 7.0.2 + '@typescript/typescript-win32-arm64': 7.0.2 + '@typescript/typescript-win32-x64': 7.0.2 + + ua-is-frozen@0.1.2: {} + + ua-parser-js@2.0.10: + dependencies: + detect-europe-js: 0.1.2 + is-standalone-pwa: 0.1.1 + ua-is-frozen: 0.1.2 + + ufo@1.6.4: {} + + unbash@4.0.4: {} unbox-primitive@1.1.0: dependencies: @@ -10207,13 +10141,13 @@ snapshots: has-symbols: 1.1.0 which-boxed-primitive: 1.1.1 - undici-types@7.16.0: {} + undici-types@5.26.5: {} - undici@7.24.3: {} + undici-types@7.18.2: {} - unenv@2.0.0-rc.24: - dependencies: - pathe: 2.0.3 + undici@7.29.0: {} + + undici@8.9.0: {} unhomoglyph@1.0.6: {} @@ -10236,139 +10170,132 @@ snapshots: upath@1.2.0: {} - update-browserslist-db@1.2.3(browserslist@4.28.1): + update-browserslist-db@1.2.3(browserslist@4.28.7): dependencies: - browserslist: 4.28.1 + browserslist: 4.28.7 escalade: 3.2.0 picocolors: 1.1.1 - use-sync-external-store@1.6.0(react@18.3.1): + use-sync-external-store@1.6.0(react@19.2.8): dependencies: - react: 18.3.1 + react: 19.2.8 - uuid@10.0.0: {} + usehooks-ts@3.1.1(react@19.2.8): + dependencies: + lodash.debounce: 4.0.8 + react: 19.2.8 - uuid@13.0.0: {} + util-deprecate@1.0.2: {} - virtua@0.48.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + virtua@0.50.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8): optionalDependencies: - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) - vite-node@3.2.4(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2): + vite-node@6.0.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0): dependencies: - cac: 6.7.14 - debug: 4.4.3 - es-module-lexer: 1.7.0 + cac: 7.0.0 + es-module-lexer: 2.3.1 + obug: 2.1.4 pathe: 2.0.3 - vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2) + vite: 8.2.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0) transitivePeerDependencies: - '@types/node' + - '@vitejs/devtools' + - esbuild - jiti - less - - lightningcss - sass - sass-embedded - stylus - sugarss - - supports-color - terser - tsx - yaml - vite-plugin-compression2@2.5.0(rollup@4.59.0): + vite-plugin-compression2@2.5.3(rollup@4.62.3): dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.59.0) + '@rollup/pluginutils': 5.4.0(rollup@4.62.3) tar-mini: 0.2.0 transitivePeerDependencies: - rollup - vite-plugin-pwa@1.2.0(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2))(workbox-build@7.4.0(@types/babel__core@7.20.5))(workbox-window@7.4.0): + vite-plugin-pwa@1.3.0(supports-color@10.2.2)(vite@8.2.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0))(workbox-build@7.4.1(supports-color@10.2.2))(workbox-window@7.4.1): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) pretty-bytes: 6.1.1 - tinyglobby: 0.2.15 - vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2) - workbox-build: 7.4.0(@types/babel__core@7.20.5) - workbox-window: 7.4.0 + tinyglobby: 0.2.17 + vite: 8.2.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0) + workbox-build: 7.4.1(supports-color@10.2.2) + workbox-window: 7.4.1 transitivePeerDependencies: - supports-color - vite-plugin-static-copy@3.2.0(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2)): + vite-plugin-static-copy@4.1.1(vite@8.2.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0)): dependencies: chokidar: 3.6.0 - p-map: 7.0.4 + p-map: 7.0.6 picocolors: 1.1.1 - tinyglobby: 0.2.15 - vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2) + tinyglobby: 0.2.17 + vite: 8.2.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0) - vite-plugin-svgr@4.5.0(rollup@4.59.0)(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2)): + vite-plugin-svgr@5.2.0(rollup@4.62.3)(supports-color@10.2.2)(typescript@7.0.2)(vite@8.2.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0)): dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.59.0) - '@svgr/core': 8.1.0(typescript@5.9.3) - '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3)) - vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2) + '@rollup/pluginutils': 5.4.0(rollup@4.62.3) + '@svgr/core': 8.1.0(supports-color@10.2.2)(typescript@7.0.2) + '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(supports-color@10.2.2)(typescript@7.0.2))(supports-color@10.2.2) + vite: 8.2.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0) transitivePeerDependencies: - rollup - supports-color - typescript - vite-plugin-top-level-await@1.6.0(@swc/helpers@0.5.19)(rollup@4.59.0)(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2)): - dependencies: - '@rollup/plugin-virtual': 3.0.2(rollup@4.59.0) - '@swc/core': 1.15.18(@swc/helpers@0.5.19) - '@swc/wasm': 1.15.18 - uuid: 10.0.0 - vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2) - transitivePeerDependencies: - - '@swc/helpers' - - rollup - - vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2): + vite@8.2.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0): dependencies: - esbuild: 0.27.3 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - postcss: 8.5.8 - rollup: 4.59.0 - tinyglobby: 0.2.15 + lightningcss: 1.33.0 + picomatch: 4.0.5 + postcss: 8.5.25 + rolldown: 1.2.1 + tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 24.10.13 + '@types/node': 24.13.2 + esbuild: 0.28.1 fsevents: 2.3.3 - jiti: 2.6.1 - terser: 5.46.1 - yaml: 2.8.2 - - vitest@4.1.0(@types/node@24.10.13)(@vitest/ui@4.1.0)(jsdom@29.0.0)(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2)): - dependencies: - '@vitest/expect': 4.1.0 - '@vitest/mocker': 4.1.0(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2)) - '@vitest/pretty-format': 4.1.0 - '@vitest/runner': 4.1.0 - '@vitest/snapshot': 4.1.0 - '@vitest/spy': 4.1.0 - '@vitest/utils': 4.1.0 - es-module-lexer: 2.0.0 - expect-type: 1.3.0 + jiti: 2.7.0 + terser: 5.49.0 + yaml: 2.9.0 + + vitest@4.1.10(@types/node@24.13.2)(@vitest/coverage-v8@4.1.10)(@vitest/ui@4.1.10)(jsdom@30.0.1(@noble/hashes@2.3.0))(vite@8.2.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0)): + dependencies: + '@vitest/expect': 4.1.10 + '@vitest/mocker': 4.1.10(vite@8.2.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0)) + '@vitest/pretty-format': 4.1.10 + '@vitest/runner': 4.1.10 + '@vitest/snapshot': 4.1.10 + '@vitest/spy': 4.1.10 + '@vitest/utils': 4.1.10 + es-module-lexer: 2.3.1 + expect-type: 1.4.0 magic-string: 0.30.21 - obug: 2.1.1 + obug: 2.1.4 pathe: 2.0.3 - picomatch: 4.0.3 - std-env: 4.0.0 + picomatch: 4.0.5 + std-env: 4.2.0 tinybench: 2.9.0 - tinyexec: 1.0.4 - tinyglobby: 0.2.15 - tinyrainbow: 3.1.0 - vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2) + tinyexec: 1.2.4 + tinyglobby: 0.2.17 + tinyrainbow: 3.1.1 + vite: 8.2.0(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 24.10.13 - '@vitest/ui': 4.1.0(vitest@4.1.0) - jsdom: 29.0.0 + '@types/node': 24.13.2 + '@vitest/coverage-v8': 4.1.10(vitest@4.1.10) + '@vitest/ui': 4.1.10(vitest@4.1.10) + jsdom: 30.0.1(@noble/hashes@2.3.0) transitivePeerDependencies: - msw - void-elements@3.1.0: {} + w3c-keyname@2.2.8: {} w3c-xmlserializer@5.0.0: dependencies: @@ -10378,15 +10305,25 @@ snapshots: webidl-conversions@3.0.1: {} - webidl-conversions@4.0.2: {} - webidl-conversions@8.0.1: {} + webrtc-adapter@9.0.6: + dependencies: + sdp: 3.2.2 + whatwg-mimetype@5.0.0: {} - whatwg-url@16.0.1: + whatwg-url@16.0.1(@noble/hashes@2.3.0): + dependencies: + '@exodus/bytes': 1.15.1(@noble/hashes@2.3.0) + tr46: 6.0.0 + webidl-conversions: 8.0.1 + transitivePeerDependencies: + - '@noble/hashes' + + whatwg-url@17.1.0(@noble/hashes@2.3.0): dependencies: - '@exodus/bytes': 1.15.0 + '@exodus/bytes': 1.15.1(@noble/hashes@2.3.0) tr46: 6.0.0 webidl-conversions: 8.0.1 transitivePeerDependencies: @@ -10397,12 +10334,6 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 - whatwg-url@7.1.0: - dependencies: - lodash.sortby: 4.7.0 - tr46: 1.0.1 - webidl-conversions: 4.0.2 - which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 @@ -10414,7 +10345,7 @@ snapshots: which-builtin-type@1.2.1: dependencies: call-bound: 1.0.4 - function.prototype.name: 1.1.8 + function.prototype.name: 1.2.0 has-tostringtag: 1.0.2 is-async-function: 2.1.1 is-date-object: 1.1.0 @@ -10425,7 +10356,7 @@ snapshots: isarray: 2.0.5 which-boxed-primitive: 1.1.1 which-collection: 1.0.2 - which-typed-array: 1.1.20 + which-typed-array: 1.1.22 which-collection@1.0.2: dependencies: @@ -10434,10 +10365,10 @@ snapshots: is-weakmap: 2.0.2 is-weakset: 2.0.4 - which-typed-array@1.1.20: + which-typed-array@1.1.22: dependencies: available-typed-arrays: 1.0.7 - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 for-each: 0.3.5 get-proto: 1.0.1 @@ -10453,167 +10384,161 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 - workbox-background-sync@7.4.0: + workbox-background-sync@7.4.1: dependencies: idb: 7.1.1 - workbox-core: 7.4.0 + workbox-core: 7.4.1 - workbox-broadcast-update@7.4.0: + workbox-broadcast-update@7.4.1: dependencies: - workbox-core: 7.4.0 + workbox-core: 7.4.1 - workbox-build@7.4.0(@types/babel__core@7.20.5): + workbox-build@7.4.1(supports-color@10.2.2): dependencies: - '@apideck/better-ajv-errors': 0.3.6(ajv@8.18.0) - '@babel/core': 7.29.0 - '@babel/preset-env': 7.29.2(@babel/core@7.29.0) - '@babel/runtime': 7.29.2 - '@rollup/plugin-babel': 5.3.1(@babel/core@7.29.0)(@types/babel__core@7.20.5)(rollup@4.59.0) - '@rollup/plugin-node-resolve': 15.3.1(rollup@4.59.0) - '@rollup/plugin-replace': 2.4.2(rollup@4.59.0) - '@rollup/plugin-terser': 0.4.4(rollup@4.59.0) - '@surma/rollup-plugin-off-main-thread': 2.2.3 - ajv: 8.18.0 + '@apideck/better-ajv-errors': 0.3.7(ajv@8.20.0) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/preset-env': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/runtime': 7.29.7 + '@rollup/plugin-babel': 6.1.0(@babel/core@7.29.7(supports-color@10.2.2))(rollup@4.62.3)(supports-color@10.2.2) + '@rollup/plugin-node-resolve': 16.0.3(rollup@4.62.3) + '@rollup/plugin-replace': 6.0.3(rollup@4.62.3) + '@rollup/plugin-terser': 1.0.0(rollup@4.62.3) + '@trickfilm400/rollup-plugin-off-main-thread': 3.0.0-pre1 + ajv: 8.20.0 common-tags: 1.8.2 + eta: 4.6.0 fast-json-stable-stringify: 2.1.0 fs-extra: 9.1.0 glob: 11.1.0 - lodash: 4.17.23 pretty-bytes: 5.6.0 - rollup: 4.59.0 - source-map: 0.8.0-beta.0 + rollup: 4.62.3 + source-map: 0.8.0 stringify-object: 3.3.0 strip-comments: 2.0.1 tempy: 0.6.0 upath: 1.2.0 - workbox-background-sync: 7.4.0 - workbox-broadcast-update: 7.4.0 - workbox-cacheable-response: 7.4.0 - workbox-core: 7.4.0 - workbox-expiration: 7.4.0 - workbox-google-analytics: 7.4.0 - workbox-navigation-preload: 7.4.0 - workbox-precaching: 7.4.0 - workbox-range-requests: 7.4.0 - workbox-recipes: 7.4.0 - workbox-routing: 7.4.0 - workbox-strategies: 7.4.0 - workbox-streams: 7.4.0 - workbox-sw: 7.4.0 - workbox-window: 7.4.0 + workbox-background-sync: 7.4.1 + workbox-broadcast-update: 7.4.1 + workbox-cacheable-response: 7.4.1 + workbox-core: 7.4.1 + workbox-expiration: 7.4.1 + workbox-google-analytics: 7.4.1 + workbox-navigation-preload: 7.4.1 + workbox-precaching: 7.4.1 + workbox-range-requests: 7.4.1 + workbox-recipes: 7.4.1 + workbox-routing: 7.4.1 + workbox-strategies: 7.4.1 + workbox-streams: 7.4.1 + workbox-sw: 7.4.1 + workbox-window: 7.4.1 transitivePeerDependencies: - '@types/babel__core' - supports-color - workbox-cacheable-response@7.4.0: + workbox-cacheable-response@7.4.1: dependencies: - workbox-core: 7.4.0 + workbox-core: 7.4.1 - workbox-core@7.4.0: {} + workbox-core@7.4.1: {} - workbox-expiration@7.4.0: + workbox-expiration@7.4.1: dependencies: idb: 7.1.1 - workbox-core: 7.4.0 + workbox-core: 7.4.1 - workbox-google-analytics@7.4.0: + workbox-google-analytics@7.4.1: dependencies: - workbox-background-sync: 7.4.0 - workbox-core: 7.4.0 - workbox-routing: 7.4.0 - workbox-strategies: 7.4.0 + workbox-background-sync: 7.4.1 + workbox-core: 7.4.1 + workbox-routing: 7.4.1 + workbox-strategies: 7.4.1 - workbox-navigation-preload@7.4.0: + workbox-navigation-preload@7.4.1: dependencies: - workbox-core: 7.4.0 + workbox-core: 7.4.1 - workbox-precaching@7.4.0: + workbox-precaching@7.4.1: dependencies: - workbox-core: 7.4.0 - workbox-routing: 7.4.0 - workbox-strategies: 7.4.0 + workbox-core: 7.4.1 + workbox-routing: 7.4.1 + workbox-strategies: 7.4.1 - workbox-range-requests@7.4.0: + workbox-range-requests@7.4.1: dependencies: - workbox-core: 7.4.0 + workbox-core: 7.4.1 - workbox-recipes@7.4.0: + workbox-recipes@7.4.1: dependencies: - workbox-cacheable-response: 7.4.0 - workbox-core: 7.4.0 - workbox-expiration: 7.4.0 - workbox-precaching: 7.4.0 - workbox-routing: 7.4.0 - workbox-strategies: 7.4.0 + workbox-cacheable-response: 7.4.1 + workbox-core: 7.4.1 + workbox-expiration: 7.4.1 + workbox-precaching: 7.4.1 + workbox-routing: 7.4.1 + workbox-strategies: 7.4.1 - workbox-routing@7.4.0: + workbox-routing@7.4.1: dependencies: - workbox-core: 7.4.0 + workbox-core: 7.4.1 - workbox-strategies@7.4.0: + workbox-strategies@7.4.1: dependencies: - workbox-core: 7.4.0 + workbox-core: 7.4.1 - workbox-streams@7.4.0: + workbox-streams@7.4.1: dependencies: - workbox-core: 7.4.0 - workbox-routing: 7.4.0 + workbox-core: 7.4.1 + workbox-routing: 7.4.1 - workbox-sw@7.4.0: {} + workbox-sw@7.4.1: {} - workbox-window@7.4.0: + workbox-window@7.4.1: dependencies: '@types/trusted-types': 2.0.7 - workbox-core: 7.4.0 + workbox-core: 7.4.1 - workerd@1.20260310.1: - optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20260310.1 - '@cloudflare/workerd-darwin-arm64': 1.20260310.1 - '@cloudflare/workerd-linux-64': 1.20260310.1 - '@cloudflare/workerd-linux-arm64': 1.20260310.1 - '@cloudflare/workerd-windows-64': 1.20260310.1 - - wrangler@4.72.0: - dependencies: - '@cloudflare/kv-asset-handler': 0.4.2 - '@cloudflare/unenv-preset': 2.15.0(unenv@2.0.0-rc.24)(workerd@1.20260310.1) - blake3-wasm: 2.1.5 - esbuild: 0.27.3 - miniflare: 4.20260310.0 - path-to-regexp: 6.3.0 - unenv: 2.0.0-rc.24 - workerd: 1.20260310.1 - optionalDependencies: - fsevents: 2.3.3 - transitivePeerDependencies: - - bufferutil - - utf-8-validate + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 - ws@8.18.0: {} + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.2.0 + + wrappy@1.0.2: {} xml-name-validator@5.0.0: {} xmlchars@2.2.0: {} + y18n@5.0.8: {} + yallist@3.1.1: {} - yaml@2.8.2: - optional: true + yaml@2.9.0: {} - yocto-queue@0.1.0: {} + yargs-parser@21.1.1: {} - youch-core@0.3.3: + yargs@17.7.3: dependencies: - '@poppinss/exception': 1.2.3 - error-stack-parser-es: 1.0.5 + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yocto-queue@0.1.0: {} - youch@4.1.0-beta.10: + zip-stream@6.0.1: dependencies: - '@poppinss/colors': 4.1.6 - '@poppinss/dumper': 0.6.5 - '@speed-highlight/core': 1.2.14 - cookie: 1.1.1 - youch-core: 0.3.3 + archiver-utils: 5.0.2 + compress-commons: 6.0.2 + readable-stream: 4.7.0 - zod@4.3.6: {} + zod@4.4.3: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index fbcf9139d9..219a579b03 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,30 +1,45 @@ allowBuilds: + '@sableclient/tauri-plugin-notifications-api': true '@sentry/cli': true '@swc/core': true cloudflared: true + cpu-features: true esbuild: true + protobufjs: true sharp: true + ssh2: true unrs-resolver: true workerd: true engineStrict: true minimumReleaseAge: 1440 minimumReleaseAgeExclude: + - 'matrix-js-sdk' + - '@sableclient/matrixrtc' - '@sableclient/sable-call-embedded' + - '@sableclient/tauri-plugin-livekit-mobile' + - '@sableclient/tauri-plugin-notifications-api' + - '@sableclient/twemoji-font' overrides: - brace-expansion: '>=1.1.12' - esbuild: '>=0.25.0' + brace-expansion: '>=5.0.9' + esbuild: '>=0.28.1' + fast-uri: '>=3.1.5' flatted: '>=3.4.2' + js-yaml: '>=4.3.1' + jsdom>undici: '^7.29.0' lodash: '>=4.17.23' minimatch: '>=10.2.3' + nanoid@<6.0.1: '>=3.3.18' + postcss: '>=8.5.23' rollup: '>=4.59.0' serialize-javascript: '>=7.0.3' - undici: '>=7.24.0' + undici: '>=7.29.0' + uuid: '>=11.1.1' peerDependencyRules: allowedVersions: - 'folds>@vanilla-extract/css': '1.18.0' + 'folds>@vanilla-extract/css': '1.21.2' 'folds>@vanilla-extract/recipes': '0.5.7' 'folds>classnames': '2.5.1' - 'folds>react': '18.3.1' - 'folds>react-dom': '18.3.1' + 'folds>react': '19.2.8' + 'folds>react-dom': '19.2.8' diff --git a/public/favicon.png b/public/favicon.png index 4205ad01cf..4638bcb333 100644 Binary files a/public/favicon.png and b/public/favicon.png differ diff --git a/public/font/Twemoji.Mozilla.v15.1.0.ttf b/public/font/Twemoji.Mozilla.v15.1.0.ttf deleted file mode 100644 index efb3c89800..0000000000 Binary files a/public/font/Twemoji.Mozilla.v15.1.0.ttf and /dev/null differ diff --git a/public/font/Twemoji.Mozilla.v15.1.0.woff2 b/public/font/Twemoji.Mozilla.v15.1.0.woff2 deleted file mode 100644 index 5bfc425d66..0000000000 Binary files a/public/font/Twemoji.Mozilla.v15.1.0.woff2 and /dev/null differ diff --git a/public/full_res_sable.png b/public/full_res_sable.png index f63c2beabf..cb7d599e3e 100644 Binary files a/public/full_res_sable.png and b/public/full_res_sable.png differ diff --git a/public/full_res_sable_square.png b/public/full_res_sable_square.png new file mode 100644 index 0000000000..1c3980e228 Binary files /dev/null and b/public/full_res_sable_square.png differ diff --git a/public/manifest.json b/public/manifest.json index 134a5eb4b7..c171e23257 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -11,163 +11,163 @@ "theme_color": "#1b1a21", "icons": [ { - "src": "./public/svg/cinny-logo-maskable.svg", + "src": "./public/svg/logo-maskable.svg", "sizes": "any", "type": "image/svg+xml", "purpose": "maskable" }, { - "src": "./public/logo-maskable/cinny-logo-maskable-512x512.png", + "src": "./public/logo-maskable/logo-maskable-512x512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }, { - "src": "./public/logo-maskable/cinny-logo-maskable-384x384.png", + "src": "./public/logo-maskable/logo-maskable-384x384.png", "sizes": "384x384", "type": "image/png", "purpose": "maskable" }, { - "src": "./public/logo-maskable/cinny-logo-maskable-256x256.png", + "src": "./public/logo-maskable/logo-maskable-256x256.png", "sizes": "256x256", "type": "image/png", "purpose": "maskable" }, { - "src": "./public/logo-maskable/cinny-logo-maskable-192x192.png", + "src": "./public/logo-maskable/logo-maskable-192x192.png", "sizes": "192x192", "type": "image/png", "purpose": "maskable" }, { - "src": "./public/logo-maskable/cinny-logo-maskable-180x180.png", + "src": "./public/logo-maskable/logo-maskable-180x180.png", "sizes": "180x180", "type": "image/png", "purpose": "maskable" }, { - "src": "./public/logo-maskable/cinny-logo-maskable-167x167.png", + "src": "./public/logo-maskable/logo-maskable-167x167.png", "sizes": "167x167", "type": "image/png", "purpose": "maskable" }, { - "src": "./public/logo-maskable/cinny-logo-maskable-152x152.png", + "src": "./public/logo-maskable/logo-maskable-152x152.png", "sizes": "152x152", "type": "image/png", "purpose": "maskable" }, { - "src": "./public/logo-maskable/cinny-logo-maskable-144x144.png", + "src": "./public/logo-maskable/logo-maskable-144x144.png", "sizes": "144x144", "type": "image/png", "purpose": "maskable" }, { - "src": "./public/logo-maskable/cinny-logo-maskable-120x120.png", + "src": "./public/logo-maskable/logo-maskable-120x120.png", "sizes": "120x120", "type": "image/png", "purpose": "maskable" }, { - "src": "./public/logo-maskable/cinny-logo-maskable-114x114.png", + "src": "./public/logo-maskable/logo-maskable-114x114.png", "sizes": "114x114", "type": "image/png", "purpose": "maskable" }, { - "src": "./public/logo-maskable/cinny-logo-maskable-96x96.png", + "src": "./public/logo-maskable/logo-maskable-96x96.png", "sizes": "96x96", "type": "image/png", "purpose": "maskable" }, { - "src": "./public/logo-maskable/cinny-logo-maskable-76x76.png", + "src": "./public/logo-maskable/logo-maskable-76x76.png", "sizes": "76x76", "type": "image/png", "purpose": "maskable" }, { - "src": "./public/logo-maskable/cinny-logo-maskable-72x72.png", + "src": "./public/logo-maskable/logo-maskable-72x72.png", "sizes": "72x72", "type": "image/png", "purpose": "maskable" }, { - "src": "./public/logo-maskable/cinny-logo-maskable-60x60.png", + "src": "./public/logo-maskable/logo-maskable-60x60.png", "sizes": "60x60", "type": "image/png", "purpose": "maskable" }, { - "src": "./public/logo-maskable/cinny-logo-maskable-57x57.png", + "src": "./public/logo-maskable/logo-maskable-57x57.png", "sizes": "57x57", "type": "image/png", "purpose": "maskable" }, { - "src": "./public/logo-maskable/cinny-logo-maskable-48x48.png", + "src": "./public/logo-maskable/logo-maskable-48x48.png", "sizes": "48x48", "type": "image/png", "purpose": "maskable" }, { - "src": "./public/logo-maskable/cinny-logo-maskable-36x36.png", + "src": "./public/logo-maskable/logo-maskable-36x36.png", "sizes": "36x36", "type": "image/png", "purpose": "maskable" }, { - "src": "./public/svg/cinny-logo.svg", + "src": "./public/svg/logo.svg", "sizes": "any", "type": "image/svg+xml", "purpose": "any" }, { - "src": "./public/logo/cinny-logo-512x512.png", + "src": "./public/logo/logo-512x512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" }, { - "src": "./public/logo/cinny-logo-384x384.png", + "src": "./public/logo/logo-384x384.png", "sizes": "384x384", "type": "image/png", "purpose": "any" }, { - "src": "./public/logo/cinny-logo-256x256.png", + "src": "./public/logo/logo-256x256.png", "sizes": "256x256", "type": "image/png", "purpose": "any" }, { - "src": "./public/logo/cinny-logo-192x192.png", + "src": "./public/logo/logo-192x192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" }, { - "src": "./public/logo/cinny-logo-144x144.png", + "src": "./public/logo/logo-144x144.png", "sizes": "144x144", "type": "image/png", "purpose": "any" }, { - "src": "./public/logo/cinny-logo-96x96.png", + "src": "./public/logo/logo-96x96.png", "sizes": "96x96", "type": "image/png", "purpose": "any" }, { - "src": "./public/logo/cinny-logo-72x72.png", + "src": "./public/logo/logo-72x72.png", "sizes": "72x72", "type": "image/png", "purpose": "any" }, { - "src": "./public/logo/cinny-logo-48x48.png", + "src": "./public/logo/logo-48x48.png", "sizes": "48x48", "type": "image/png", "purpose": "any" diff --git a/public/res/logo-maskable/cinny-logo-maskable-114x114.png b/public/res/logo-maskable/cinny-logo-maskable-114x114.png deleted file mode 100644 index 59463adb80..0000000000 Binary files a/public/res/logo-maskable/cinny-logo-maskable-114x114.png and /dev/null differ diff --git a/public/res/logo-maskable/cinny-logo-maskable-120x120.png b/public/res/logo-maskable/cinny-logo-maskable-120x120.png deleted file mode 100644 index 4e185fba32..0000000000 Binary files a/public/res/logo-maskable/cinny-logo-maskable-120x120.png and /dev/null differ diff --git a/public/res/logo-maskable/cinny-logo-maskable-144x144.png b/public/res/logo-maskable/cinny-logo-maskable-144x144.png deleted file mode 100644 index f674110ff5..0000000000 Binary files a/public/res/logo-maskable/cinny-logo-maskable-144x144.png and /dev/null differ diff --git a/public/res/logo-maskable/cinny-logo-maskable-152x152.png b/public/res/logo-maskable/cinny-logo-maskable-152x152.png deleted file mode 100644 index 8de192a1ef..0000000000 Binary files a/public/res/logo-maskable/cinny-logo-maskable-152x152.png and /dev/null differ diff --git a/public/res/logo-maskable/cinny-logo-maskable-167x167.png b/public/res/logo-maskable/cinny-logo-maskable-167x167.png deleted file mode 100644 index 146db2de34..0000000000 Binary files a/public/res/logo-maskable/cinny-logo-maskable-167x167.png and /dev/null differ diff --git a/public/res/logo-maskable/cinny-logo-maskable-180x180.png b/public/res/logo-maskable/cinny-logo-maskable-180x180.png deleted file mode 100644 index 14e2764bd0..0000000000 Binary files a/public/res/logo-maskable/cinny-logo-maskable-180x180.png and /dev/null differ diff --git a/public/res/logo-maskable/cinny-logo-maskable-192x192.png b/public/res/logo-maskable/cinny-logo-maskable-192x192.png deleted file mode 100644 index 45b3f74d40..0000000000 Binary files a/public/res/logo-maskable/cinny-logo-maskable-192x192.png and /dev/null differ diff --git a/public/res/logo-maskable/cinny-logo-maskable-256x256.png b/public/res/logo-maskable/cinny-logo-maskable-256x256.png deleted file mode 100644 index ebfadb35b4..0000000000 Binary files a/public/res/logo-maskable/cinny-logo-maskable-256x256.png and /dev/null differ diff --git a/public/res/logo-maskable/cinny-logo-maskable-36x36.png b/public/res/logo-maskable/cinny-logo-maskable-36x36.png deleted file mode 100644 index f80ba44c3c..0000000000 Binary files a/public/res/logo-maskable/cinny-logo-maskable-36x36.png and /dev/null differ diff --git a/public/res/logo-maskable/cinny-logo-maskable-384x384.png b/public/res/logo-maskable/cinny-logo-maskable-384x384.png deleted file mode 100644 index e6fbb3dc0d..0000000000 Binary files a/public/res/logo-maskable/cinny-logo-maskable-384x384.png and /dev/null differ diff --git a/public/res/logo-maskable/cinny-logo-maskable-48x48.png b/public/res/logo-maskable/cinny-logo-maskable-48x48.png deleted file mode 100644 index 4dd14e8f88..0000000000 Binary files a/public/res/logo-maskable/cinny-logo-maskable-48x48.png and /dev/null differ diff --git a/public/res/logo-maskable/cinny-logo-maskable-512x512.png b/public/res/logo-maskable/cinny-logo-maskable-512x512.png deleted file mode 100644 index e4e31a7207..0000000000 Binary files a/public/res/logo-maskable/cinny-logo-maskable-512x512.png and /dev/null differ diff --git a/public/res/logo-maskable/cinny-logo-maskable-57x57.png b/public/res/logo-maskable/cinny-logo-maskable-57x57.png deleted file mode 100644 index cc23be00a3..0000000000 Binary files a/public/res/logo-maskable/cinny-logo-maskable-57x57.png and /dev/null differ diff --git a/public/res/logo-maskable/cinny-logo-maskable-60x60.png b/public/res/logo-maskable/cinny-logo-maskable-60x60.png deleted file mode 100644 index 6a11f20733..0000000000 Binary files a/public/res/logo-maskable/cinny-logo-maskable-60x60.png and /dev/null differ diff --git a/public/res/logo-maskable/cinny-logo-maskable-72x72.png b/public/res/logo-maskable/cinny-logo-maskable-72x72.png deleted file mode 100644 index 35e533ec30..0000000000 Binary files a/public/res/logo-maskable/cinny-logo-maskable-72x72.png and /dev/null differ diff --git a/public/res/logo-maskable/cinny-logo-maskable-76x76.png b/public/res/logo-maskable/cinny-logo-maskable-76x76.png deleted file mode 100644 index 1976581538..0000000000 Binary files a/public/res/logo-maskable/cinny-logo-maskable-76x76.png and /dev/null differ diff --git a/public/res/logo-maskable/cinny-logo-maskable-96x96.png b/public/res/logo-maskable/cinny-logo-maskable-96x96.png deleted file mode 100644 index 0cd72dbf2d..0000000000 Binary files a/public/res/logo-maskable/cinny-logo-maskable-96x96.png and /dev/null differ diff --git a/public/res/logo-maskable/logo-maskable-114x114.png b/public/res/logo-maskable/logo-maskable-114x114.png new file mode 100644 index 0000000000..def2336ae7 Binary files /dev/null and b/public/res/logo-maskable/logo-maskable-114x114.png differ diff --git a/public/res/logo-maskable/logo-maskable-120x120.png b/public/res/logo-maskable/logo-maskable-120x120.png new file mode 100644 index 0000000000..78e01535f3 Binary files /dev/null and b/public/res/logo-maskable/logo-maskable-120x120.png differ diff --git a/public/res/logo-maskable/logo-maskable-144x144.png b/public/res/logo-maskable/logo-maskable-144x144.png new file mode 100644 index 0000000000..d3cf3a09d5 Binary files /dev/null and b/public/res/logo-maskable/logo-maskable-144x144.png differ diff --git a/public/res/logo-maskable/logo-maskable-152x152.png b/public/res/logo-maskable/logo-maskable-152x152.png new file mode 100644 index 0000000000..f3e493de2d Binary files /dev/null and b/public/res/logo-maskable/logo-maskable-152x152.png differ diff --git a/public/res/logo-maskable/logo-maskable-167x167.png b/public/res/logo-maskable/logo-maskable-167x167.png new file mode 100644 index 0000000000..1bfa60cce7 Binary files /dev/null and b/public/res/logo-maskable/logo-maskable-167x167.png differ diff --git a/public/res/logo-maskable/logo-maskable-180x180.png b/public/res/logo-maskable/logo-maskable-180x180.png new file mode 100644 index 0000000000..86c707283f Binary files /dev/null and b/public/res/logo-maskable/logo-maskable-180x180.png differ diff --git a/public/res/logo-maskable/logo-maskable-192x192.png b/public/res/logo-maskable/logo-maskable-192x192.png new file mode 100644 index 0000000000..5c06f3a741 Binary files /dev/null and b/public/res/logo-maskable/logo-maskable-192x192.png differ diff --git a/public/res/logo-maskable/logo-maskable-256x256.png b/public/res/logo-maskable/logo-maskable-256x256.png new file mode 100644 index 0000000000..4fd058b09a Binary files /dev/null and b/public/res/logo-maskable/logo-maskable-256x256.png differ diff --git a/public/res/logo-maskable/logo-maskable-36x36.png b/public/res/logo-maskable/logo-maskable-36x36.png new file mode 100644 index 0000000000..88115eaef7 Binary files /dev/null and b/public/res/logo-maskable/logo-maskable-36x36.png differ diff --git a/public/res/logo-maskable/logo-maskable-384x384.png b/public/res/logo-maskable/logo-maskable-384x384.png new file mode 100644 index 0000000000..73d09b9225 Binary files /dev/null and b/public/res/logo-maskable/logo-maskable-384x384.png differ diff --git a/public/res/logo-maskable/logo-maskable-48x48.png b/public/res/logo-maskable/logo-maskable-48x48.png new file mode 100644 index 0000000000..389d4d402a Binary files /dev/null and b/public/res/logo-maskable/logo-maskable-48x48.png differ diff --git a/public/res/logo-maskable/logo-maskable-512x512.png b/public/res/logo-maskable/logo-maskable-512x512.png new file mode 100644 index 0000000000..30b58a211f Binary files /dev/null and b/public/res/logo-maskable/logo-maskable-512x512.png differ diff --git a/public/res/logo-maskable/logo-maskable-57x57.png b/public/res/logo-maskable/logo-maskable-57x57.png new file mode 100644 index 0000000000..699ef9324a Binary files /dev/null and b/public/res/logo-maskable/logo-maskable-57x57.png differ diff --git a/public/res/logo-maskable/logo-maskable-60x60.png b/public/res/logo-maskable/logo-maskable-60x60.png new file mode 100644 index 0000000000..92f2f74c33 Binary files /dev/null and b/public/res/logo-maskable/logo-maskable-60x60.png differ diff --git a/public/res/logo-maskable/logo-maskable-72x72.png b/public/res/logo-maskable/logo-maskable-72x72.png new file mode 100644 index 0000000000..90806e9b01 Binary files /dev/null and b/public/res/logo-maskable/logo-maskable-72x72.png differ diff --git a/public/res/logo-maskable/logo-maskable-76x76.png b/public/res/logo-maskable/logo-maskable-76x76.png new file mode 100644 index 0000000000..04645074ef Binary files /dev/null and b/public/res/logo-maskable/logo-maskable-76x76.png differ diff --git a/public/res/logo-maskable/logo-maskable-96x96.png b/public/res/logo-maskable/logo-maskable-96x96.png new file mode 100644 index 0000000000..b066c991d4 Binary files /dev/null and b/public/res/logo-maskable/logo-maskable-96x96.png differ diff --git a/public/res/logo/cinny-logo-144x144.png b/public/res/logo/cinny-logo-144x144.png deleted file mode 100644 index a2d3a55eb8..0000000000 Binary files a/public/res/logo/cinny-logo-144x144.png and /dev/null differ diff --git a/public/res/logo/cinny-logo-192x192.png b/public/res/logo/cinny-logo-192x192.png deleted file mode 100644 index 827a9d75b1..0000000000 Binary files a/public/res/logo/cinny-logo-192x192.png and /dev/null differ diff --git a/public/res/logo/cinny-logo-256x256.png b/public/res/logo/cinny-logo-256x256.png deleted file mode 100644 index 46e33befa3..0000000000 Binary files a/public/res/logo/cinny-logo-256x256.png and /dev/null differ diff --git a/public/res/logo/cinny-logo-36x36.png b/public/res/logo/cinny-logo-36x36.png deleted file mode 100644 index 5b769057aa..0000000000 Binary files a/public/res/logo/cinny-logo-36x36.png and /dev/null differ diff --git a/public/res/logo/cinny-logo-384x384.png b/public/res/logo/cinny-logo-384x384.png deleted file mode 100644 index ef2bfff132..0000000000 Binary files a/public/res/logo/cinny-logo-384x384.png and /dev/null differ diff --git a/public/res/logo/cinny-logo-48x48.png b/public/res/logo/cinny-logo-48x48.png deleted file mode 100644 index 3ceaf5f8d8..0000000000 Binary files a/public/res/logo/cinny-logo-48x48.png and /dev/null differ diff --git a/public/res/logo/cinny-logo-512x512.png b/public/res/logo/cinny-logo-512x512.png deleted file mode 100644 index 6de474dc16..0000000000 Binary files a/public/res/logo/cinny-logo-512x512.png and /dev/null differ diff --git a/public/res/logo/cinny-logo-72x72.png b/public/res/logo/cinny-logo-72x72.png deleted file mode 100644 index 5b12fd929a..0000000000 Binary files a/public/res/logo/cinny-logo-72x72.png and /dev/null differ diff --git a/public/res/logo/cinny-logo-96x96.png b/public/res/logo/cinny-logo-96x96.png deleted file mode 100644 index be29017f30..0000000000 Binary files a/public/res/logo/cinny-logo-96x96.png and /dev/null differ diff --git a/public/res/logo/logo-144x144.png b/public/res/logo/logo-144x144.png new file mode 100644 index 0000000000..85456a23d3 Binary files /dev/null and b/public/res/logo/logo-144x144.png differ diff --git a/public/res/logo/logo-192x192.png b/public/res/logo/logo-192x192.png new file mode 100644 index 0000000000..40004e6e7c Binary files /dev/null and b/public/res/logo/logo-192x192.png differ diff --git a/public/res/logo/logo-256x256.png b/public/res/logo/logo-256x256.png new file mode 100644 index 0000000000..f25497e848 Binary files /dev/null and b/public/res/logo/logo-256x256.png differ diff --git a/public/res/logo/logo-36x36.png b/public/res/logo/logo-36x36.png new file mode 100644 index 0000000000..a861a47175 Binary files /dev/null and b/public/res/logo/logo-36x36.png differ diff --git a/public/res/logo/logo-384x384.png b/public/res/logo/logo-384x384.png new file mode 100644 index 0000000000..f65fd79f78 Binary files /dev/null and b/public/res/logo/logo-384x384.png differ diff --git a/public/res/logo/logo-48x48.png b/public/res/logo/logo-48x48.png new file mode 100644 index 0000000000..4a1309e7ef Binary files /dev/null and b/public/res/logo/logo-48x48.png differ diff --git a/public/res/logo/logo-512x512.png b/public/res/logo/logo-512x512.png new file mode 100644 index 0000000000..4638bcb333 Binary files /dev/null and b/public/res/logo/logo-512x512.png differ diff --git a/public/res/logo/logo-72x72.png b/public/res/logo/logo-72x72.png new file mode 100644 index 0000000000..8dfdb80a25 Binary files /dev/null and b/public/res/logo/logo-72x72.png differ diff --git a/public/res/logo/logo-96x96.png b/public/res/logo/logo-96x96.png new file mode 100644 index 0000000000..97a5964046 Binary files /dev/null and b/public/res/logo/logo-96x96.png differ diff --git a/public/res/svg/cinny-highlight.svg b/public/res/svg/cinny-highlight.svg deleted file mode 100644 index f88f9d99d0..0000000000 --- a/public/res/svg/cinny-highlight.svg +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - diff --git a/public/res/svg/cinny-logo-maskable.svg b/public/res/svg/cinny-logo-maskable.svg deleted file mode 100644 index 1b3b15eb32..0000000000 --- a/public/res/svg/cinny-logo-maskable.svg +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - diff --git a/public/res/svg/cinny-logo.svg b/public/res/svg/cinny-logo.svg deleted file mode 100644 index f84fba3694..0000000000 --- a/public/res/svg/cinny-logo.svg +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - diff --git a/public/res/svg/cinny-unread.svg b/public/res/svg/cinny-unread.svg deleted file mode 100644 index 384abdfbf5..0000000000 --- a/public/res/svg/cinny-unread.svg +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - diff --git a/public/res/svg/highlight.svg b/public/res/svg/highlight.svg new file mode 100644 index 0000000000..1c2bfcf105 --- /dev/null +++ b/public/res/svg/highlight.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/res/svg/logo-maskable.svg b/public/res/svg/logo-maskable.svg new file mode 100644 index 0000000000..16ad163652 --- /dev/null +++ b/public/res/svg/logo-maskable.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/res/svg/logo.svg b/public/res/svg/logo.svg new file mode 100644 index 0000000000..8de551f504 --- /dev/null +++ b/public/res/svg/logo.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/res/svg/unread.svg b/public/res/svg/unread.svg new file mode 100644 index 0000000000..b320ace7b7 --- /dev/null +++ b/public/res/svg/unread.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000000..59277fee77 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "1.97.1" +components = ["clippy", "rustfmt"] diff --git a/scripts/cef/copy-libs.sh b/scripts/cef/copy-libs.sh new file mode 100755 index 0000000000..578478805f --- /dev/null +++ b/scripts/cef/copy-libs.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env bash +#MISE description="Copy CEF runtime libs next to the built binary" +# Copy the CEF runtime (libcef.so + resources) next to a built binary so the app +# is self-contained: works standalone, when launched by the OS via a sable:// +# deep link, or from a release package. Pair with the rpath=$ORIGIN that +# build.rs adds for CEF builds. +# +# Usage: scripts/cef/copy-libs.sh [debug|release] [dest-dir] +# dest-dir defaults to src-tauri/target/ (beside the built binary). +set -euo pipefail +PROFILE="${1:-debug}" +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +DEST="${2:-$ROOT/src-tauri/target/$PROFILE}" + +case "$(uname -m)" in + x86_64) CEF_ARCH=x86_64 ;; + aarch64 | arm64) CEF_ARCH=aarch64 ;; + *) echo "❌ unsupported architecture: $(uname -m)" >&2; exit 1 ;; +esac + +# --target moves build-script output under target//. +CEF_DIR="$( + find "$ROOT/src-tauri/target" -type d -name "cef_linux_$CEF_ARCH" \ + -path "*/$PROFILE/build/*" -print -quit 2>/dev/null || true +)" +if [ -z "$CEF_DIR" ]; then + echo "❌ cef_linux_$CEF_ARCH not found under target/**/$PROFILE/build — build with --features cef first." >&2 + exit 1 +fi + +CEF_VERSION="$(awk ' + /^\[\[package\]\]$/ { in_cef=0 } + /^name = "cef"$/ { in_cef=1 } + in_cef && /^version = / { + gsub(/"/, "", $3) + print $3 + exit + } +' "$ROOT/src-tauri/Cargo.lock")" +if [ -z "$CEF_VERSION" ]; then + echo "❌ Resolved cef version not found in src-tauri/Cargo.lock." >&2 + exit 1 +fi +CEF_MAJOR="${CEF_VERSION%%.*}" + +CEF_LIB="$CEF_DIR/libcef.so" +if [ ! -f "$CEF_LIB" ]; then + echo "❌ libcef.so not found in $CEF_DIR." >&2 + exit 1 +fi +RUNTIME_MAJOR="$( + strings -a "$CEF_LIB" | + grep -Eio '[0-9]{3}\.[0-9]+\.[0-9]+\+g[^[:space:]]+\+chromium-[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | + awk -F. 'NR == 1 { print $1; exit }' +)" || true +if [ -z "$RUNTIME_MAJOR" ]; then + echo "❌ CEF runtime version not found in $CEF_LIB." >&2 + exit 1 +fi +if [ "$RUNTIME_MAJOR" != "$CEF_MAJOR" ]; then + echo "❌ CEF runtime major $RUNTIME_MAJOR does not match resolved cef major $CEF_MAJOR." >&2 + exit 1 +fi + +mkdir -p "$DEST" +echo "→ copying CEF runtime from $CEF_DIR to $DEST" +# Libraries (libcef.so + ANGLE/SwiftShader), crashpad handler, then resources. +cp -f "$CEF_DIR"/*.so* "$DEST/" 2>/dev/null || true +cp -f "$CEF_DIR"/chrome_crashpad_handler "$DEST/" 2>/dev/null || true +cp -f "$CEF_DIR"/*.pak "$CEF_DIR"/*.dat "$CEF_DIR"/*.bin "$CEF_DIR"/*.json "$DEST/" 2>/dev/null || true + +# CEF's BSD license must accompany binary redistributions. The Rust crate's +# extracted runtime omits it, so package the upstream notice vendored here. +cp -f "$ROOT/packaging/licenses/CEF-LICENSE.txt" "$DEST/CEF-LICENSE.txt" + +# Strip debug symbols: CEF ships libcef.so unstripped (~1.3 GB → 241 MB). +# Same approach as OutSystems cef.redist.linux and Spotify's desktop client. +for lib in "$DEST"/libcef.so "$DEST"/libEGL.so "$DEST"/libGLESv2.so; do + [ -f "$lib" ] && strip -s "$lib" 2>/dev/null || true +done + +# Only ship en-US locale (49 MB → 570 KB); see CEF README.redistrib.txt. +mkdir -p "$DEST/locales" +cp -f "$CEF_DIR"/locales/en-US.pak "$DEST/locales/" 2>/dev/null || true + +# chrome-sandbox is not shipped: AppImages and snaps mount nosuid. +echo "✅ done." diff --git a/scripts/cef/package.sh b/scripts/cef/package.sh new file mode 100755 index 0000000000..6a0f1c2793 --- /dev/null +++ b/scripts/cef/package.sh @@ -0,0 +1,143 @@ +#!/usr/bin/env bash +#MISE description="Package the CEF build for Linux" +#MISE tools={nfpm="2.47.0", "github:AppImage/appimagetool" = {version = "1.9.1", matching = ".AppImage"}} +# Usage: scripts/cef/package.sh [version] [package-binary-path] [display-name] [appimage-binary-path] +set -euo pipefail +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +cd "$ROOT" + +VERSION="${1:-$(grep -m1 '"version":' src-tauri/tauri.conf.json | sed 's/.*: *"\(.*\)".*/\1/')}" +: "${VERSION:?version not found in src-tauri/tauri.conf.json}" +DEB_VERSION="$VERSION" +RPM_VERSION="$VERSION" +RPM_ITERATION=1 +if [[ "$VERSION" == *-* ]]; then + BASE_VERSION="${VERSION%%-*}" + PRERELEASE="${VERSION#*-}" + DEB_VERSION="${BASE_VERSION}~${PRERELEASE}" + RPM_VERSION="$BASE_VERSION" + RPM_ITERATION="0.${PRERELEASE}" +fi + +# ARCH is read by appimagetool. +case "$(uname -m)" in + x86_64) export ARCH=x86_64; NFPM_ARCH=amd64 ;; + aarch64 | arm64) export ARCH=aarch64; NFPM_ARCH=arm64 ;; + *) echo "unsupported architecture: $(uname -m)" >&2; exit 1 ;; +esac + +STAGE="$ROOT/src-tauri/target/release" +OUT="$STAGE/bundle" +WORK="$STAGE/cef-pkg" + +BIN_PATH="${2:-}" +DISPLAY_NAME="${3:-}" +APPIMAGE_BIN_PATH="${4:-}" +if [ -z "$BIN_PATH" ]; then + for candidate in "$STAGE/Sable Nightly" "$STAGE/Sable" "$STAGE/sable" \ + "$ROOT/src-tauri/target/$ARCH-unknown-linux-gnu/release/sable"; do + [ -x "$candidate" ] || continue + BIN_PATH="$candidate" + break + done +fi +[ -n "$BIN_PATH" ] && [ -x "$BIN_PATH" ] || { + echo "no CEF binary found; build it first (pnpm tauri:cef build)" >&2 + exit 1 +} +[ -n "$APPIMAGE_BIN_PATH" ] || APPIMAGE_BIN_PATH="$BIN_PATH" +[ -x "$APPIMAGE_BIN_PATH" ] || { + echo "AppImage binary is not executable: $APPIMAGE_BIN_PATH" >&2 + exit 1 +} +if [ -z "$DISPLAY_NAME" ]; then + case "$(basename "$BIN_PATH")" in + "Sable Nightly") DISPLAY_NAME="Sable Nightly" ;; + *) DISPLAY_NAME="Sable" ;; + esac +fi + +APPIMAGETOOL_CMD="" +if command -v appimagetool.AppImage >/dev/null 2>&1; then + APPIMAGETOOL_CMD="appimagetool.AppImage" +elif command -v appimagetool >/dev/null 2>&1; then + APPIMAGETOOL_CMD="appimagetool" +else + echo "appimagetool not found" >&2 + exit 1 +fi + +rm -rf "$WORK" +mkdir -p "$OUT/deb" "$OUT/rpm" "$OUT/appimage" + +bash scripts/cef/stage.sh "$WORK/stage" "$DISPLAY_NAME" + +APPDIR="$WORK/Sable.AppDir" +mkdir -p "$APPDIR/usr/bin" +cp -a "$WORK/stage/runtime/." "$APPDIR/usr/bin/" +cp -f "$APPIMAGE_BIN_PATH" "$APPDIR/usr/bin/sable" +chmod 755 "$APPDIR/usr/bin/sable" + +# Bundle the system-tray libraries (Tauri's linuxdeploy path normally does this, +# which the CEF build bypasses). Bundle libayatana-appindicator3 plus its +# ayatana/dbusmenu/indicator dependency closure; host libs (gtk, glib, X11, …) +# are left to the system, matching linuxdeploy-plugin-appindicator. +stage_appindicator() { + local dest="$1" main dep + # awk reads to EOF (no early exit) so ldconfig never gets SIGPIPE under pipefail. + main="$(ldconfig -p 2>/dev/null | awk '$1=="libayatana-appindicator3.so.1"{v=$NF} END{print v}')" + [ -n "$main" ] || main="$(find /usr/lib /usr/lib64 /lib -name libayatana-appindicator3.so.1 2>/dev/null | sort | tail -n1)" + if [ -z "$main" ] || [ ! -e "$main" ]; then + echo "warning: libayatana-appindicator3.so.1 not found; tray disabled in the AppImage" >&2 + return 0 + fi + { + echo "$main" + ldd "$main" 2>/dev/null | awk '/=>/ {print $3}' | grep -iE 'ayatana|dbusmenu|indicator|ido' || true + } | sort -u | while read -r dep; do + if [ -e "$dep" ]; then + cp -Lf "$dep" "$dest/$(basename "$dep")" + fi + done +} +stage_appindicator "$APPDIR/usr/bin" + +# The AppImage mount is nosuid, so Chrome's setuid sandbox cannot run there. +rm -f "$APPDIR/usr/bin/chrome-sandbox" + +if command -v nfpm >/dev/null 2>&1; then + PKGROOT="$WORK/pkgroot" + mkdir -p "$PKGROOT/opt/sable" "$PKGROOT/usr/bin" "$PKGROOT/usr/share/applications" + cp -a "$WORK/stage/runtime/." "$PKGROOT/opt/sable/" + cp -f "$BIN_PATH" "$PKGROOT/opt/sable/sable" + chmod 755 "$PKGROOT/opt/sable/sable" + cat > "$PKGROOT/usr/bin/sable" <<'EOF' +#!/bin/sh +exec /opt/sable/sable "$@" +EOF + chmod 755 "$PKGROOT/usr/bin/sable" + cp -a "$WORK/stage/share/." "$PKGROOT/usr/share/" + + PKGROOT="$PKGROOT" PKG_ARCH="$NFPM_ARCH" PKG_VERSION="$DEB_VERSION" PKG_RELEASE=1 nfpm pkg -f nfpm.yaml -p deb \ + -t "$OUT/deb/Sable-${VERSION}-linux-${ARCH}.deb" + PKGROOT="$PKGROOT" PKG_ARCH="$NFPM_ARCH" PKG_VERSION="$RPM_VERSION" PKG_RELEASE="$RPM_ITERATION" nfpm pkg -f nfpm.yaml -p rpm \ + -t "$OUT/rpm/Sable-${VERSION}-linux-${ARCH}.rpm" +else + echo "nfpm not found" >&2 + exit 1 +fi + +cp -f "$WORK/stage/share/applications/sable.desktop" "$APPDIR/sable.desktop" +cp -f src-tauri/icons/128x128.png "$APPDIR/sable.png" +cat > "$APPDIR/AppRun" <<'EOF' +#!/bin/sh +HERE="$(dirname "$(readlink -f "$0")")" +export LD_LIBRARY_PATH="$HERE/usr/bin${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" +exec "$HERE/usr/bin/sable" "$@" +EOF +chmod 755 "$APPDIR/AppRun" + +APPIMAGE_EXTRACT_AND_RUN=1 "$APPIMAGETOOL_CMD" "$APPDIR" \ + "$OUT/appimage/Sable-${VERSION}-linux-${ARCH}.AppImage" + +echo "Packages in: $OUT" diff --git a/scripts/cef/stage.sh b/scripts/cef/stage.sh new file mode 100755 index 0000000000..f1aaad2e2e --- /dev/null +++ b/scripts/cef/stage.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +#MISE description="Stage the CEF runtime and desktop files for packaging" +# Usage: scripts/cef/stage.sh [stage-dir] [display-name] [profile] +set -euo pipefail +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +cd "$ROOT" + +STAGE="${1:-$ROOT/src-tauri/target/cef-stage}" +DISPLAY_NAME="${2:-Sable}" +PROFILE="${3:-release}" + +RUNTIME="$STAGE/runtime" +SHARE="$STAGE/share" +rm -rf "$STAGE" +mkdir -p "$RUNTIME" "$SHARE/applications" + +bash scripts/cef/copy-libs.sh "$PROFILE" "$RUNTIME" + +cat > "$SHARE/applications/sable.desktop" < { + log.error(error instanceof Error ? error.message : String(error)); + process.exitCode = 1; +}); diff --git a/scripts/e2e-docker.js b/scripts/e2e-docker.js new file mode 100644 index 0000000000..ef9b61f3b0 --- /dev/null +++ b/scripts/e2e-docker.js @@ -0,0 +1,72 @@ +#!/usr/bin/env node +import { execFileSync, spawnSync } from 'child_process'; +import { createConnection } from 'net'; +import { readFileSync } from 'fs'; + +const { devDependencies } = JSON.parse(readFileSync(new URL('../package.json', import.meta.url))); +const version = devDependencies['@playwright/test'].replace(/^[^0-9]*/, ''); +const image = `mcr.microsoft.com/playwright:v${version}-noble`; +const name = 'sable-e2e-browser'; +const port = 3000; + +const waitForServer = () => + new Promise((resolve, reject) => { + const deadline = Date.now() + 60_000; + const attempt = () => { + const socket = createConnection({ host: '127.0.0.1', port }) + .on('connect', () => { + socket.end(); + resolve(); + }) + .on('error', () => { + if (Date.now() > deadline) { + reject(new Error(`playwright server did not start on port ${port}`)); + return; + } + setTimeout(attempt, 500); + }); + }; + attempt(); + }); + +const stop = () => spawnSync('docker', ['rm', '-f', name], { stdio: 'ignore' }); + +stop(); +execFileSync( + 'docker', + [ + 'run', + '--rm', + '--detach', + '--init', + '--name', + name, + '--network', + 'host', + '--workdir', + '/home/pwuser', + '--user', + 'pwuser', + image, + '/bin/sh', + '-c', + `npx -y playwright@${version} run-server --port ${port} --host 0.0.0.0`, + ], + { stdio: 'inherit' } +); + +process.on('exit', stop); +process.on('SIGINT', () => process.exit(130)); +process.on('SIGTERM', () => process.exit(143)); + +await waitForServer(); + +const args = process.argv.slice(2); +if (args[0] === '--') args.shift(); + +const result = spawnSync('pnpm', ['exec', 'playwright', 'test', ...args], { + stdio: 'inherit', + env: { ...process.env, PW_TEST_CONNECT_WS_ENDPOINT: `ws://127.0.0.1:${port}/` }, +}); + +process.exit(result.status ?? 1); diff --git a/scripts/icons/replace.js b/scripts/icons/replace.js new file mode 100755 index 0000000000..ba35bec148 --- /dev/null +++ b/scripts/icons/replace.js @@ -0,0 +1,183 @@ +#!/usr/bin/env node +//MISE description="Replace icon assets from SVG inputs" +//MISE tools={ffmpeg="7.1.1"} + +import { execFileSync } from 'node:child_process'; +import { existsSync, mkdirSync, copyFileSync } from 'node:fs'; +import { dirname, resolve, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { createInterface } from 'node:readline'; +import { PrefixedLogger, createTextHelpers } from '../utils/console-style.js'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const BASE_DIR = resolve(__dirname, '..'); + +function resolveBinary(name) { + try { + const root = execFileSync('mise', ['where', name], { encoding: 'utf-8' }).trim(); + const candidates = [ + join(root, 'Library', 'bin', `${name}.exe`), + join(root, 'bin', `${name}.exe`), + join(root, `${name}.exe`), + ]; + for (const exe of candidates) { + if (existsSync(exe)) return exe; + } + } catch { + /* fallback to path */ + } + return name; +} + +const FFMPEG = resolveBinary('ffmpeg'); + +const logger = new PrefixedLogger('[replace-logo]'); +const { red } = createTextHelpers({ useColor: logger.useColor }); + +/** + * Display a question on stdin and return the user's trimmed response. + * @param {string} question - The prompt text to display. + * @returns {Promise} The user's input with surrounding whitespace removed. + */ +function prompt(question) { + const rl = createInterface({ input: process.stdin, output: process.stdout }); + return new Promise((resolve_) => { + rl.question(question, (answer) => { + rl.close(); + resolve_(answer.trim()); + }); + }); +} + +/** Print CLI usage instructions to stdout. */ +function usage() { + logger.info(`Usage: run this script and provide four file paths when prompted. +It will replace icons in the repository public/ tree using ffmpeg. + +You will be prompted for: + - maskless (borderless) logo (used for maskable icons) + - circular logo (used for circular icons) + - highlight variant (icon for highlight indicator) + - unread variant (icon for unread indicator) + +Requires: ffmpeg, base64`); +} + +/** + * Render an SVG to a PNG at the given square dimension using ffmpeg. + * Creates the output directory if it doesn't already exist. + * @param {string} input - Absolute path to the source SVG file. + * @param {string} output - Absolute path for the generated PNG. + * @param {string | number} size - Width and height in pixels. + */ +function ffmpegCmd(input, output, size) { + mkdirSync(dirname(output), { recursive: true }); + logger.info(`Generating ${output} (${size} x ${size})`); + execFileSync( + FFMPEG, + ['-y', '-width', String(size), '-height', String(size), '-i', input, output], + { + stdio: 'pipe', + } + ); +} + +/** + * Check whether a file path has an SVG extension (case-insensitive). + * @param {string} filePath + * @returns {boolean} + */ +function isSvg(filePath) { + return filePath.toLowerCase().endsWith('.svg'); +} + +/** + * Entry point. Prompts for four SVG logo variants, validates them, + * then renders PNGs at every required size and copies SVGs into public/res/svg/. + * @param {string[]} args - CLI arguments (supports --help / -h). + */ +async function main(args) { + if (args.includes('--help') || args.includes('-h')) { + usage(); + process.exit(0); + } + + const maskless = await prompt('Path to maskless (borderless) logo: '); + const circular = await prompt('Path to circular logo: '); + const highlight = await prompt('Path to highlight variant: '); + const unread = await prompt('Path to unread variant: '); + + for (const f of [maskless, circular, highlight, unread]) { + if (!existsSync(f)) { + logger.error(`${red('File not found:')} ${f}`); + process.exit(2); + } + if (!isSvg(f)) { + logger.error(`${red('Input must be an SVG:')} ${f}`); + process.exit(3); + } + } + + /** @type {[number, string][]} */ + const CIRCULAR_TARGETS = [ + [512, 'public/favicon.png'], + [2560, 'public/full_res_sable.png'], + [144, 'public/res/logo/logo-144x144.png'], + [192, 'public/res/logo/logo-192x192.png'], + [256, 'public/res/logo/logo-256x256.png'], + [36, 'public/res/logo/logo-36x36.png'], + [384, 'public/res/logo/logo-384x384.png'], + [48, 'public/res/logo/logo-48x48.png'], + [512, 'public/res/logo/logo-512x512.png'], + [72, 'public/res/logo/logo-72x72.png'], + [96, 'public/res/logo/logo-96x96.png'], + ]; + + logger.info('Replacing circular icons...'); + for (const [size, out] of CIRCULAR_TARGETS) { + ffmpegCmd(circular, resolve(BASE_DIR, out), size); + } + + /** @type {[number, string][]} */ + const MASKABLE_TARGETS = [ + [114, 'public/res/logo-maskable/logo-maskable-114x114.png'], + [120, 'public/res/logo-maskable/logo-maskable-120x120.png'], + [144, 'public/res/logo-maskable/logo-maskable-144x144.png'], + [152, 'public/res/logo-maskable/logo-maskable-152x152.png'], + [167, 'public/res/logo-maskable/logo-maskable-167x167.png'], + [180, 'public/res/logo-maskable/logo-maskable-180x180.png'], + [192, 'public/res/logo-maskable/logo-maskable-192x192.png'], + [256, 'public/res/logo-maskable/logo-maskable-256x256.png'], + [36, 'public/res/logo-maskable/logo-maskable-36x36.png'], + [384, 'public/res/logo-maskable/logo-maskable-384x384.png'], + [48, 'public/res/logo-maskable/logo-maskable-48x48.png'], + [512, 'public/res/logo-maskable/logo-maskable-512x512.png'], + [57, 'public/res/logo-maskable/logo-maskable-57x57.png'], + [60, 'public/res/logo-maskable/logo-maskable-60x60.png'], + [72, 'public/res/logo-maskable/logo-maskable-72x72.png'], + [76, 'public/res/logo-maskable/logo-maskable-76x76.png'], + [96, 'public/res/logo-maskable/logo-maskable-96x96.png'], + ]; + + logger.info('Replacing maskable icons (using maskless input)...'); + for (const [size, out] of MASKABLE_TARGETS) { + ffmpegCmd(maskless, resolve(BASE_DIR, out), size); + } + + logger.info('Handling SVG assets in public/res/svg/...'); + const svgDir = resolve(BASE_DIR, 'public/res/svg'); + mkdirSync(svgDir, { recursive: true }); + + logger.info(`Copying circular SVG to ${svgDir}/logo.svg`); + copyFileSync(circular, resolve(svgDir, 'logo.svg')); + + logger.info(`Copying maskable SVG to ${svgDir}/logo-maskable.svg`); + copyFileSync(maskless, resolve(svgDir, 'logo-maskable.svg')); + + copyFileSync(highlight, resolve(svgDir, 'highlight.svg')); + copyFileSync(unread, resolve(svgDir, 'unread.svg')); + + logger.info('All done. Please review the generated files.'); +} + +main(process.argv.slice(2)); diff --git a/scripts/icons/symlink.js b/scripts/icons/symlink.js new file mode 100755 index 0000000000..1bb6d95d29 --- /dev/null +++ b/scripts/icons/symlink.js @@ -0,0 +1,182 @@ +#!/usr/bin/env node +//MISE description="Create Tauri icon symlinks" +//MISE raw_args=true +/* eslint-disable no-console */ + +import fs from 'node:fs'; +import path from 'node:path'; +import process from 'node:process'; +import { createTextHelpers } from '../utils/console-style.js'; + +const ROOT = process.cwd(); +const appIconsPath = path.join(ROOT, 'src-tauri', 'app-icons.json'); +const appIcons = fs.existsSync(appIconsPath) + ? JSON.parse(fs.readFileSync(appIconsPath, 'utf8')).icons + : []; + +const ANDROID_ICONS = [ + 'mipmap-hdpi', + 'mipmap-mdpi', + 'mipmap-xhdpi', + 'mipmap-xxhdpi', + 'mipmap-xxxhdpi', +] + .flatMap((dir) => [ + `${dir}/ic_launcher.png`, + `${dir}/ic_launcher_round.png`, + `${dir}/ic_launcher_foreground.png`, + `${dir}/ic_notification.png`, + ]) + .concat(['mipmap-anydpi-v26/ic_launcher.xml', 'values/ic_launcher_background.xml']) + .concat(['drawable/ic_notification.xml', 'drawable/notification_icon.xml']) + .concat( + appIcons.flatMap(({ id }) => [ + ...['mipmap-hdpi', 'mipmap-mdpi', 'mipmap-xhdpi', 'mipmap-xxhdpi', 'mipmap-xxxhdpi'].flatMap( + (dir) => [ + `${dir}/ic_launcher_${id}.png`, + `${dir}/ic_launcher_${id}_round.png`, + `${dir}/ic_launcher_${id}_foreground.png`, + ] + ), + `mipmap-anydpi-v26/ic_launcher_${id}.xml`, + ]) + ); + +const IOS_ICONS = [ + 'AppIcon-20x20@1x.png', + 'AppIcon-20x20@2x.png', + 'AppIcon-20x20@2x-1.png', + 'AppIcon-20x20@3x.png', + 'AppIcon-29x29@1x.png', + 'AppIcon-29x29@2x.png', + 'AppIcon-29x29@2x-1.png', + 'AppIcon-29x29@3x.png', + 'AppIcon-40x40@1x.png', + 'AppIcon-40x40@2x.png', + 'AppIcon-40x40@2x-1.png', + 'AppIcon-40x40@3x.png', + 'AppIcon-60x60@2x.png', + 'AppIcon-60x60@3x.png', + 'AppIcon-76x76@1x.png', + 'AppIcon-76x76@2x.png', + 'AppIcon-83.5x83.5@2x.png', + 'AppIcon-512@2x.png', +]; + +function parseArgs(argv) { + let write = false; + let force = false; + + argv.forEach((arg) => { + if (arg === '--write') write = true; + if (arg === '--force') force = true; + if (arg === '--help' || arg === '-h') { + console.log( + [ + 'Usage: node scripts/symlink-icons.js [--write] [--force]', + '', + 'Default mode is dry-run.', + '--write Apply changes (create symlinks).', + '--force Overwrite existing symlinks.', + ].join('\n') + ); + process.exit(0); + } + }); + + return { write, force }; +} + +function createSymlink(src, dest, write, force, helpers) { + const { dim, red, green } = helpers; + + if (!fs.existsSync(src)) { + console.log(` ${red('!')} ${dim('source missing, skipping:')} ${src}`); + return false; + } + + const exists = fs.existsSync(dest); + if (exists && !force) { + console.log( + ` ${dim('~')} ${dim(path.relative(process.cwd(), dest))} ${dim('(exists, skipping)')}` + ); + return false; + } + + if (write) { + fs.mkdirSync(path.dirname(dest), { recursive: true }); + + try { + fs.unlinkSync(dest); + } catch { + // dest does not exist — nothing to remove + } + + const rel = path.relative(path.dirname(dest), src).split(path.sep).join('/'); + fs.symlinkSync(rel, dest); + } + + console.log(` ${green('+')} ${dim(path.relative(process.cwd(), dest))}`); + return true; +} + +function processGroup(label, srcDir, destDir, files, write, force, helpers) { + const { dim, red } = helpers; + + if (!fs.existsSync(destDir)) { + console.log(`\n${label}: ${red('destination not found')}, skipping.\n ${dim(destDir)}`); + return; + } + + console.log(`\n${label}`); + + const results = files.map((file) => { + try { + return createSymlink( + path.join(srcDir, file), + path.join(destDir, file), + write, + force, + helpers + ); + } catch (err) { + console.log(` ${red('-')} ${file}: ${err.message}`); + return false; + } + }); + + const ok = results.filter(Boolean).length; + const verb = write ? 'created' : 'would create'; + console.log(` ${dim(`→ ${ok}/${files.length} symlinks ${verb}.`)}`); +} + +function main() { + const { write, force } = parseArgs(process.argv.slice(2)); + const helpers = createTextHelpers(); + + processGroup( + 'Android', + path.join(ROOT, 'src-tauri', 'icons', 'android'), + path.join(ROOT, 'src-tauri', 'gen', 'android', 'app', 'src', 'main', 'res'), + ANDROID_ICONS, + write, + force, + helpers + ); + + processGroup( + 'iOS', + path.join(ROOT, 'src-tauri', 'icons', 'ios'), + path.join(ROOT, 'src-tauri', 'gen', 'apple', 'Assets.xcassets', 'AppIcon.appiconset'), + IOS_ICONS, + write, + force, + helpers + ); + + const mode = write ? 'Applied' : 'Dry run'; + console.log(`\n${mode}.`); + if (!write) console.log('Re-run with --write to apply changes.'); +} + +main(); diff --git a/scripts/install-knope.js b/scripts/install-knope.js index cbda39e577..a3bb04045f 100644 --- a/scripts/install-knope.js +++ b/scripts/install-knope.js @@ -1,4 +1,6 @@ #!/usr/bin/env node +//MISE hide=true +//MISE description="Install knope" import { spawnSync } from 'node:child_process'; import process from 'node:process'; import { chmodSync, existsSync, mkdirSync, realpathSync, writeFileSync } from 'node:fs'; @@ -102,11 +104,12 @@ function getSystemKnopePath() { ); } +const toComparablePath = (value) => { + const resolved = resolve(value); + return process.platform === 'win32' ? resolved.toLowerCase() : resolved; +}; + function isPathWithin(candidatePath, rootPath) { - const toComparablePath = (value) => { - const resolved = resolve(value); - return process.platform === 'win32' ? resolved.toLowerCase() : resolved; - }; const candidate = toComparablePath(candidatePath); const root = toComparablePath(rootPath); return candidate === root || candidate.startsWith(`${root}${sep}`); diff --git a/scripts/normalize-imports.js b/scripts/normalize-imports.js index 77af442c38..415e366967 100644 --- a/scripts/normalize-imports.js +++ b/scripts/normalize-imports.js @@ -1,4 +1,6 @@ #!/usr/bin/env node +//MISE hide=true +//MISE description="Normalize import paths across the codebase" /* oxlint-disable no-console */ import fs from 'node:fs/promises'; diff --git a/scripts/obtainium-generate.js b/scripts/obtainium-generate.js new file mode 100644 index 0000000000..d887c6f1dc --- /dev/null +++ b/scripts/obtainium-generate.js @@ -0,0 +1,63 @@ +#!/usr/bin/env node +//MISE hide=true +//MISE description="Generate obtainium.json for one-click app import" +/* oxlint-disable no-console */ + +import { writeFileSync } from 'node:fs'; +import { resolve } from 'node:path'; +import process from 'node:process'; + +const version = process.argv[2]; +const tag = process.argv[3] || `v${version}`; +const outputPath = process.argv[4] || 'obtainium.json'; + +if (!version) { + console.error('Usage: node scripts/obtainium-generate.js [tag] [output-path]'); + process.exit(1); +} + +const GITHUB_REPO = 'SableClient/Sable'; +const APK_NAME = `Sable-${version}-android-universal.apk`; +const APK_URL = `https://github.com/${GITHUB_REPO}/releases/download/${tag}/${APK_NAME}`; +const isNightly = tag === 'nightly'; +const appId = isNightly ? 'moe.sable.client.nightly' : 'moe.sable.client'; + +// Obtainium fills in every other setting from its own defaults on import. +const additionalSettings = { + about: 'An almost stable Matrix client', + // The nightly tag name never changes, so the version has to come from the date instead. + ...(isNightly && { + includePrereleases: true, + useLatestAssetDateAsReleaseDate: true, + releaseDateAsVersion: true, + versionDetection: false, + }), +}; + +const config = { + apps: [ + { + id: appId, + url: `https://github.com/${GITHUB_REPO}`, + author: 'SableClient', + name: isNightly ? 'Sable Nightly' : 'Sable', + installedVersion: null, + latestVersion: version, + apkUrls: JSON.stringify([[APK_NAME, APK_URL]]), + otherAssetUrls: '[]', + preferredApkIndex: 0, + additionalSettings: JSON.stringify(additionalSettings), + lastUpdateCheck: null, + pinned: false, + categories: ['Communication'], + releaseDate: null, + changeLog: null, + overrideSource: 'GitHub', + allowIdChange: false, + pendingRepoRenameUrl: null, + }, + ], +}; + +writeFileSync(resolve(outputPath), JSON.stringify(config, null, 2) + '\n'); +console.log(`Generated ${outputPath}`); diff --git a/scripts/tauri.js b/scripts/tauri.js new file mode 100755 index 0000000000..9c0c71300f --- /dev/null +++ b/scripts/tauri.js @@ -0,0 +1,93 @@ +#!/usr/bin/env node +//MISE description="Run Tauri CLI" +//MISE depends="tauri:setup" +//MISE raw_args=true + +/** + * Passes through to the Tauri CLI. When the first argument is a desktop runtime + * (`wry` or `cef`) and the second is `dev` or `build`, injects the appropriate + * Cargo feature flags (`--features ,updater --no-default-features`). + * Everything else is forwarded to `tauri` as-is. + * + * script/tauri cef dev --verbose + * → tauri dev --features cef,updater -- --verbose --no-default-features + * + * Pass `--no-updater` to omit the bundled auto-updater, for distro packagers and + * anyone building from source who updates through their own channel: + * + * script/tauri wry build --no-updater + * → tauri build --features wry -- --no-default-features + */ + +import { run } from '@tauri-apps/cli'; +import { fileURLToPath } from 'url'; +import { dirname, join } from 'path'; +import process from 'node:process'; +import { PrefixedLogger, createTextHelpers } from './utils/console-style.js'; + +const logger = new PrefixedLogger('[tauri]'); +const { dim } = createTextHelpers({ useColor: logger.useColor }); + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const cmdlineArgs = process.argv.slice(2); +process.chdir(join(__dirname, '..')); + +const DESKTOP = new Set(['wry', 'cef']); + +async function runTauri(args) { + logger.info(`${dim('Running:')} tauri ${args.join(' ')}`); + try { + await run(args, 'tauri'); + } catch (error) { + logger.error(`Failed to run tauri: ${error?.message ?? error}`); + process.exit(1); + } +} + +async function main() { + if (cmdlineArgs.length === 0 || !DESKTOP.has(cmdlineArgs[0])) { + return runTauri(cmdlineArgs); + } + + const [platform, cmd, ...rawTauriArgs] = cmdlineArgs; + + if (!cmd) { + return runTauri(cmdlineArgs); + } + + if (!['dev', 'build'].includes(cmd)) { + return runTauri([cmd, ...rawTauriArgs]); + } + + // Consumed here, not forwarded: the tauri CLI does not know this flag. + const noUpdater = rawTauriArgs.includes('--no-updater'); + const tauriArgs = rawTauriArgs.filter((arg) => arg !== '--no-updater'); + if (noUpdater) { + logger.info('Building without the auto-updater (--no-updater)'); + } + + // The frontend is built before Cargo, so mirror the updater feature into Vite. + process.env.VITE_DESKTOP_UPDATER_ENABLED = String(!noUpdater); + + const features = noUpdater ? platform : `${platform},updater`; + const args = [cmd, '--features', features, ...tauriArgs]; + if (!tauriArgs.includes('--')) { + args.push('--'); + } + args.push('--no-default-features'); + + if (noUpdater && cmd === 'build') { + // Signed updater artifacts would otherwise demand TAURI_SIGNING_PRIVATE_KEY. + args.splice(1, 0, '--config', JSON.stringify({ bundle: { createUpdaterArtifacts: false } })); + } + + if (platform === 'cef' && cmd === 'build' && !tauriArgs.includes('--no-bundle')) { + args.splice(1, 0, '--no-bundle'); + } + + return runTauri(args); +} + +main(); diff --git a/scripts/utils/console-style.js b/scripts/utils/console-style.js index ae7e29b792..433a362027 100644 --- a/scripts/utils/console-style.js +++ b/scripts/utils/console-style.js @@ -11,7 +11,7 @@ export const ANSI = { export function shouldUseColor() { if (process.env.NO_COLOR !== undefined) return false; if (process.env.FORCE_COLOR && process.env.FORCE_COLOR !== '0') return true; - return Boolean(process.stdout.isTTY); + return process.stdout.isTTY; } export function styleText(text, color, enabled) { diff --git a/src-tauri/.gitignore b/src-tauri/.gitignore new file mode 100644 index 0000000000..4928739e7a --- /dev/null +++ b/src-tauri/.gitignore @@ -0,0 +1,7 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +/gen/schemas + +# Downloaded by scripts/download-ios-frameworks.js (pinned, checksummed binaries) +/Frameworks/ diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock new file mode 100644 index 0000000000..5fde54439e --- /dev/null +++ b/src-tauri/Cargo.lock @@ -0,0 +1,9788 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ab_glyph" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" + +[[package]] +name = "addr2line" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aes" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8eb277bec05f56a0e0591f155a484cbd0f4f07ff2905051a48c72f004f7ed58" +dependencies = [ + "cipher", + "cpubits", + "cpufeatures 0.3.0", +] + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.4", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "android-activity" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f2a1bb052857d5dd49572219344a7332b31b76405648eabac5bc68978251bcd" +dependencies = [ + "android-properties", + "bitflags 2.13.1", + "cc", + "jni 0.22.4", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "num_enum", + "thiserror 2.0.20", +] + +[[package]] +name = "android-properties" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" + +[[package]] +name = "android_log-sys" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84521a3cf562bc62942e294181d9eef17eb38ceb8c68677bc49f144e4c3d4f8d" + +[[package]] +name = "android_logger" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb4e440d04be07da1f1bf44fb4495ebd58669372fe0cffa6e48595ac5bd88a3" +dependencies = [ + "android_log-sys", + "env_filter", + "log", +] + +[[package]] +name = "android_system_properties" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" + +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "arboard" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf" +dependencies = [ + "clipboard-win", + "image", + "log", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "parking_lot", + "percent-encoding", + "windows-sys 0.60.2", + "wl-clipboard-rs", + "x11rb", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" + +[[package]] +name = "as-raw-xcb-connection" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" + +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "async-signal" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "atk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" +dependencies = [ + "atk-sys", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.32", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper 0.1.2", + "tower 0.4.13", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + +[[package]] +name = "backtrace" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-link 0.2.1", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" +dependencies = [ + "serde_core", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "borsh" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a88b7ea17d208c4193f2c1e6de3c35fe71f98c96982d5ced308bdcc749ff6e1f" +dependencies = [ + "bytes", + "cfg_aliases", +] + +[[package]] +name = "brotli" +version = "8.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc91aac060a7a1e25823bdccbfb6af1875b88f17c6daac97894eed8207166b3" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "5.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bstr" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f" +dependencies = [ + "memchr", + "serde_core", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytemuck" +version = "1.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "bytes" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" +dependencies = [ + "serde", +] + +[[package]] +name = "bzip2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c" +dependencies = [ + "libbz2-rs-sys", +] + +[[package]] +name = "cairo-rs" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +dependencies = [ + "bitflags 2.13.1", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror 1.0.69", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "calloop" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dbf9978365bac10f54d1d4b04f7ce4427e51f71d61f2fe15e3fed5166474df7" +dependencies = [ + "bitflags 2.13.1", + "polling", + "rustix", + "slab", + "tracing", +] + +[[package]] +name = "calloop-wayland-source" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138efcf0940a02ebf0cc8d1eff41a1682a46b431630f4c52450d6265876021fa" +dependencies = [ + "calloop", + "rustix", + "wayland-backend", + "wayland-client", +] + +[[package]] +name = "camino" +version = "1.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb1307f12aa967b5a58416e87b3653360e0fd614a016b6e970db08fecbb1b80d" +dependencies = [ + "serde_core", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0061da739915fae12ea00e16397555ed4371a6bb285431aab930f61b0aa4ba" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "cargo_metadata" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" +dependencies = [ + "camino", + "cargo-platform 0.1.9", + "semver", + "serde", + "serde_json", + "thiserror 2.0.20", +] + +[[package]] +name = "cargo_metadata" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef987d17b0a113becdd19d3d0022d04d7ef41f9efe4f3fb63ac44ba61df3ade9" +dependencies = [ + "camino", + "cargo-platform 0.3.3", + "semver", + "serde", + "serde_json", + "thiserror 2.0.20", +] + +[[package]] +name = "cargo_toml" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77" +dependencies = [ + "serde", + "toml 0.9.12+spec-1.1.0", +] + +[[package]] +name = "cc" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cef" +version = "150.2.1+150.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b912d9d13dd63b4bffa750f1f3c49271dc96d6c91ca2d1c1f68a0172832170c8" +dependencies = [ + "anyhow", + "cargo_metadata 0.23.1", + "cef-dll-sys", + "clap", + "libloading 0.9.0", + "objc2", + "plist", + "semver", + "serde", + "serde_json", + "thiserror 2.0.20", + "windows-sys 0.61.2", +] + +[[package]] +name = "cef-dll-sys" +version = "150.2.1+150.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b1059b49a0d5aa3872035f076210a31df6d70e039e12427b41ceba7c650b49" +dependencies = [ + "anyhow", + "cmake", + "download-cef", + "serde_json", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" + +[[package]] +name = "chacha20" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.1", +] + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link 0.2.1", +] + +[[package]] +name = "chrono-tz" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93698b29de5e97ad0ae26447b344c482a7284c737d9ddc5f9e52b74a336671bb" +dependencies = [ + "chrono", + "chrono-tz-build", + "phf 0.11.3", +] + +[[package]] +name = "chrono-tz-build" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c088aee841df9c3041febbb73934cfc39708749bf96dc827e3359cd39ef11b1" +dependencies = [ + "parse-zoneinfo", + "phf 0.11.3", + "phf_codegen 0.11.3", +] + +[[package]] +name = "cipher" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c" +dependencies = [ + "block-buffer 0.12.1", + "crypto-common 0.2.2", + "inout", +] + +[[package]] +name = "clap" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "clipboard-win" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4" +dependencies = [ + "error-code", +] + +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + +[[package]] +name = "cocoa" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad36507aeb7e16159dfe68db81ccc27571c3ccd4b76fb2fb72fc59e7a4b1b64c" +dependencies = [ + "bitflags 2.13.1", + "block", + "cocoa-foundation", + "core-foundation 0.10.1", + "core-graphics 0.24.0", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81411967c50ee9a1fc11365f8c585f863a22a9697c89239c452292c40ba79b0d" +dependencies = [ + "bitflags 2.13.1", + "block", + "core-foundation 0.10.1", + "core-graphics-types", + "objc", +] + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "colored" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" +dependencies = [ + "lazy_static", + "windows-sys 0.59.0", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "console" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "unicode-width", + "windows-sys 0.59.0", +] + +[[package]] +name = "console" +version = "0.16.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fe5f465a4f6fee88fad41b85d990f84c835335e85b5d9e6e63e0d06d28cba7c" +dependencies = [ + "encode_unicode", + "libc", + "unicode-width", + "windows-sys 0.61.2", +] + +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.17", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cookie" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a373e3602691c3cdea496d2f0ee5935151e6168fe87739483c463db1b2f2f87" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206" +dependencies = [ + "cookie", + "document-features", + "idna", + "indexmap 2.14.0", + "log", + "publicsuffix", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" +dependencies = [ + "bitflags 2.13.1", + "core-foundation 0.10.1", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" +dependencies = [ + "bitflags 2.13.1", + "core-foundation 0.10.1", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.13.1", + "core-foundation 0.10.1", + "libc", +] + +[[package]] +name = "cpubits" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "cssparser" +version = "0.29.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93d03419cb5950ccfd3daf3ff1c7a36ace64609a1a8746d493df1ca0afde0fa" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "matches", + "phf 0.10.1", + "proc-macro2", + "quote", + "smallvec", + "syn 1.0.109", +] + +[[package]] +name = "cssparser" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae61cf9c0abb83bd659dab65b7e4e38d8236824c85f0f804f173567bda257d2" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf 0.13.1", + "smallvec", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.119", +] + +[[package]] +name = "ctor" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "352d39c2f7bef1d6ad73db6f5160efcaed66d94ef8c6c573a8410c00bf909a98" +dependencies = [ + "ctor-proc-macro", + "dtor", +] + +[[package]] +name = "ctor-proc-macro" +version = "0.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1" + +[[package]] +name = "ctr" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baaca1c4b237092596f64d571e9db6ce4109c4ef9742e27590f1709594461f21" +dependencies = [ + "cipher", +] + +[[package]] +name = "cursor-icon" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.119", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "data-url" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" + +[[package]] +name = "dbus" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab69f03cc8c4340c9c8e315114e1658e6775a9b16a04357973aa21cec22b32e" +dependencies = [ + "libc", + "libdbus-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "serde", + "uuid", +] + +[[package]] +name = "defmt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" +dependencies = [ + "defmt-parser", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror 2.0.20", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "serde_core", +] + +[[package]] +name = "derive_arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "derive_more" +version = "0.99.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.119", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.119", +] + +[[package]] +name = "deunicode" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abd57806937c9cc163efc8ea3910e00a62e2aeb0b8119f1793a978088f8f6b04" + +[[package]] +name = "devtools-core" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7abafdcb55ff28587b551fd20408c65032c1b7f405e7c2b889f20e3b8289d8f" +dependencies = [ + "async-stream", + "bytes", + "devtools-wire-format", + "futures", + "http 0.2.12", + "hyper 0.14.32", + "log", + "prost-types", + "ringbuf", + "thiserror 1.0.69", + "tokio", + "tokio-stream", + "tonic", + "tonic-health", + "tonic-web", + "tower 0.4.13", + "tower-http 0.4.4", + "tower-layer", + "tracing", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "devtools-wire-format" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c06ffa9aeb3fb248b41d4e71ab3c0aa89177afc6669459da4320b97a4c77948" +dependencies = [ + "bitflags 2.13.1", + "prost", + "prost-types", + "tonic", + "tracing-core", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common 0.1.7", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", + "const-oid", + "crypto-common 0.2.2", +] + +[[package]] +name = "dioxus-debug-cell" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ea539174bb236e0e7dc9c12b19b88eae3cb574dedbd0252a2d43ea7e6de13e2" + +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.61.2", +] + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags 2.13.1", + "block2", + "libc", + "objc2", +] + +[[package]] +name = "displaydoc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "dlib" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a" +dependencies = [ + "libloading 0.8.9", +] + +[[package]] +name = "dlopen2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "dlv-list" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" +dependencies = [ + "const-random", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "dom_query" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521e380c0c8afb8d9a1e83a1822ee03556fc3e3e7dbc1fd30be14e37f9cb3f89" +dependencies = [ + "bit-set", + "cssparser 0.36.0", + "foldhash 0.2.0", + "html5ever 0.38.0", + "precomputed-hash", + "selectors 0.36.1", + "tendril 0.5.1", +] + +[[package]] +name = "dos-date-time" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6bf3a6a5a5068b83e2bc1251735e040c3c0d54bc0bb829bec86d76eb1197689" +dependencies = [ + "chrono", + "jiff", + "time", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "download-cef" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c169adf067a787e1f1c58ed62906a557de85388bee4b54fb878b722ff606b113" +dependencies = [ + "bzip2", + "clap", + "fs-err", + "indicatif 0.18.6", + "regex", + "semver", + "serde", + "serde_json", + "sha1_smol", + "tar", + "thiserror 2.0.20", + "ureq", +] + +[[package]] +name = "dpi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +dependencies = [ + "serde", +] + +[[package]] +name = "dtoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" + +[[package]] +name = "dtoa-short" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +dependencies = [ + "dtoa", +] + +[[package]] +name = "dtor" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1057d6c64987086ff8ed0fd3fbf377a6b7d205cc7715868cd401705f715cbe4" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "either" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" + +[[package]] +name = "embed-resource" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbfdaacccebec3b28e4866b8973543c7647797db5ada1bdab552e48fe665fbbd" +dependencies = [ + "cc", + "memchr", + "rustc_version", + "toml 1.1.4+spec-1.1.0", + "vswhom", + "winreg", +] + +[[package]] +name = "embed_plist" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" + +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "endi" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" + +[[package]] +name = "enigo" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71c6c56e50f7acae2906a0dcbb34529ca647e40421119ad5d12e7f8ba6e50010" +dependencies = [ + "core-foundation 0.10.1", + "core-graphics 0.25.0", + "foreign-types-shared", + "libc", + "log", + "nom", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "windows 0.61.3", + "x11rb", + "xkbcommon", + "xkeysym", +] + +[[package]] +name = "enumflags2" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "env_filter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "erased-serde" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" +dependencies = [ + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "error-code" +version = "3.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" + +[[package]] +name = "event-listener" +version = "5.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2" +dependencies = [ + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" + +[[package]] +name = "fax" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a" + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "fern" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4316185f709b23713e41e3195f90edef7fb00c3ed4adc79769cf09cc762a3b29" +dependencies = [ + "log", +] + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "filetime" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759" +dependencies = [ + "cfg-if", + "libc", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de" + +[[package]] +name = "findshlibs" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64" +dependencies = [ + "cc", + "lazy_static", + "libc", + "winapi", +] + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", + "zlib-rs", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea5190182e6915eb873ddbc16e23b711b6eb1f9c00a0d0a3a91b5f6228475225" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs-err" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91aa448ca50d7e79433bdf3ee8d99215430d2ec02ade5aefab2a073a1822e8a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + +[[package]] +name = "futures" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" + +[[package]] +name = "futures-executor" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" + +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "futures-sink" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" + +[[package]] +name = "futures-task" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" + +[[package]] +name = "futures-util" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "gdk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", + "once_cell", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkwayland-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "140071d506d223f7572b9f09b5e155afbd77428cd5cc7af8f2694c41d98dfe69" +dependencies = [ + "gdk-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3caa00e14351bebbc8183b3c36690327eb77c49abc2268dd4bd36b856db3fbfe" +dependencies = [ + "gdk", + "gdkx11-sys", + "gio", + "glib", + "libc", + "x11", +] + +[[package]] +name = "gdkx11-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e7445fe01ac26f11601db260dd8608fe172514eb63b3b5e261ea6b0f4428d" +dependencies = [ + "gdk-sys", + "glib-sys", + "libc", + "system-deps", + "x11", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gethostname" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" +dependencies = [ + "rustix", + "windows-link 0.2.1", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi 6.0.0", + "rand_core 0.10.1", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + +[[package]] +name = "gio" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "gio-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +dependencies = [ + "bitflags 2.13.1", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "once_cell", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "glib-macros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 2.0.2", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "glib-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "glob" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" + +[[package]] +name = "global-hotkey" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c386b0a4a70cb2d39fffd74480f985b6f0bfbcb934b6a6b6b7e630e448f242e" +dependencies = [ + "crossbeam-channel", + "keyboard-types 0.7.0", + "objc2", + "objc2-app-kit", + "once_cell", + "serde", + "thiserror 2.0.20", + "windows-sys 0.59.0", + "x11rb", + "xkeysym", +] + +[[package]] +name = "globset" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07c34a9410465b45bd9787443bc7370f37735bad04b0f0cd57ff1a3186c98988" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "globwalk" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" +dependencies = [ + "bitflags 2.13.1", + "ignore", + "walkdir", +] + +[[package]] +name = "gobject-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gtk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "h2" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap 2.14.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.5.0", + "indexmap 2.14.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hostname" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617aaa3557aef3810a6369d0a99fac8a080891b68bd9f9812a1eeda0c0730cbd" +dependencies = [ + "cfg-if", + "libc", + "windows-link 0.2.1", +] + +[[package]] +name = "html5ever" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b7410cae13cbc75623c98ac4cbfd1f0bedddf3227afc24f370cf0f50a44a11c" +dependencies = [ + "log", + "mac", + "markup5ever 0.14.1", + "match_token", +] + +[[package]] +name = "html5ever" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1054432bae2f14e0061e33d23402fbaa67a921d319d56adc6bcf887ddad1cbc2" +dependencies = [ + "log", + "markup5ever 0.38.0", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" +dependencies = [ + "bytes", + "http 1.5.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" +dependencies = [ + "bytes", + "futures-core", + "http 1.5.0", + "http-body 1.1.0", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humansize" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" +dependencies = [ + "libm", +] + +[[package]] +name = "hybrid-array" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b" +dependencies = [ + "typenum", +] + +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.27", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.10", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d22053281f852e11534f5198498373cbb59295120a20771d90f7ed1897490a72" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2 0.4.15", + "http 1.5.0", + "http-body 1.1.0", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" +dependencies = [ + "http 1.5.0", + "hyper 1.11.0", + "hyper-util", + "rustls", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper 0.14.32", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-util", + "http 1.5.0", + "http-body 1.1.0", + "hyper 1.11.0", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2 0.6.5", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core 0.62.2", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ico" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e795dff5605e0f04bff85ca41b51a96b83e80b281e96231bcaaf1ac35103371" +dependencies = [ + "byteorder", + "png 0.17.16", +] + +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "ignore" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b69833ed729dc5aa7d19541d96d6cf8e9137194207a04916d658e43168402f" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + +[[package]] +name = "image" +version = "0.25.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" +dependencies = [ + "bytemuck", + "byteorder-lite", + "moxcms", + "num-traits", + "png 0.18.1", + "tiff", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "indicatif" +version = "0.17.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" +dependencies = [ + "console 0.15.11", + "number_prefix", + "portable-atomic", + "unicode-width", + "web-time", +] + +[[package]] +name = "indicatif" +version = "0.18.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9433806cd6b4ec1aba79c021c7e4c58fb4c3b9977c085062e611ac929998fb0c" +dependencies = [ + "console 0.16.4", + "portable-atomic", + "unicode-width", + "unit-prefix", + "web-time", +] + +[[package]] +name = "infer" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" +dependencies = [ + "cfb", +] + +[[package]] +name = "infer" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4200d433cbd5178df7797c9c2e75b348b728e39631cf14520d1e2fc424201f4" + +[[package]] +name = "inout" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "ipnet" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a756c3fac73139e83f14c2d742155dd2b78d3ee56597b419a0579b7bdd6dd78" + +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "javascriptcore-rs" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" +dependencies = [ + "bitflags 1.3.2", + "glib", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "jiff" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668b7183bd07af9a4885f5c35b0cc5c83c4607a913c16b7e17291832910d2dcc" +dependencies = [ + "defmt", + "jiff-core", + "jiff-static", + "jiff-tzdb-platform", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", + "windows-link 0.2.1", +] + +[[package]] +name = "jiff-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7feca88439efe53da3754500c1851dedf3cb36c524dd5cf8225cc0794de95d09" +dependencies = [ + "defmt", +] + +[[package]] +name = "jiff-static" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a69dcb3a21cfb32ce1cd056169337ca284af0766dd766e7878819b251a49204" +dependencies = [ + "jiff-core", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142bd39932ad231f10513df9ab62661fead8719872150b7ad02a2df79f4e141e" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" +dependencies = [ + "jiff-tzdb", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys 0.3.1", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys 0.4.1", + "log", + "simd_cesu8", + "thiserror 2.0.20", + "walkdir", + "windows-link 0.2.1", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.119", +] + +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.119", +] + +[[package]] +name = "jobserver" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" +dependencies = [ + "getrandom 0.4.3", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "json-patch" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" +dependencies = [ + "jsonptr", + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "jsonptr" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.13.1", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "keyboard-types" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fbe853b403ae61a04233030ae8a79d94975281ed9770a1f9e246732b534b28d" +dependencies = [ + "bitflags 2.13.1", + "serde", +] + +[[package]] +name = "kuchikiki" +version = "0.8.8-speedreader" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02cb977175687f33fa4afa0c95c112b987ea1443e5a51c8f8ff27dc618270cc2" +dependencies = [ + "cssparser 0.29.6", + "html5ever 0.29.1", + "indexmap 2.14.0", + "selectors 0.24.0", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libappindicator" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" +dependencies = [ + "glib", + "gtk", + "gtk-sys", + "libappindicator-sys", + "log", +] + +[[package]] +name = "libappindicator-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" +dependencies = [ + "gtk-sys", + "libloading 0.7.4", + "once_cell", +] + +[[package]] +name = "libbz2-rs-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b357333733e8260735ba5894eb928c02ecc69c78715f01a8019e7fa7f2db4c" + +[[package]] +name = "libc" +version = "0.2.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + +[[package]] +name = "libdbus-sys" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "328c4789d42200f1eeec05bd86c9c13c7f091d2ba9a6ea35acdf51f31bc0f043" +dependencies = [ + "pkg-config", +] + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link 0.2.1", +] + +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link 0.2.1", +] + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "libredox" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2026a5056764a10b2bf5d56488cba40da507f5493a6a429340e2004d9ed085fa" +dependencies = [ + "bitflags 2.13.1", + "libc", + "plain", + "redox_syscall 0.9.2", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "local-ip-address" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612ed4ea9ce5acfb5d26339302528a5e1e59dfed95e9e11af3c083236ff1d15d" +dependencies = [ + "libc", + "neli", + "thiserror 1.0.69", + "windows-sys 0.48.0", +] + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[package]] +name = "mac-notification-sys" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd604973958ddcc11b561193c0fb96ba146506ef2f231ef2e7c35fd2cbc9beca" +dependencies = [ + "cc", + "log", + "objc2", + "objc2-foundation", + "time", + "uuid", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "markup5ever" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7a7213d12e1864c0f002f52c2923d4556935a43dec5e71355c2760e0f6e7a18" +dependencies = [ + "log", + "phf 0.11.3", + "phf_codegen 0.11.3", + "string_cache 0.8.9", + "string_cache_codegen 0.5.4", + "tendril 0.4.3", +] + +[[package]] +name = "markup5ever" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8983d30f2915feeaaab2d6babdd6bc7e9ed1a00b66b5e6d74df19aa9c0e91862" +dependencies = [ + "log", + "tendril 0.5.1", + "web_atoms", +] + +[[package]] +name = "match_token" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "memmap2" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minisign-verify" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f9645cb765ea72b8111f36c522475d2daa0d22c957a9826437e97534bc4e9e" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427" +dependencies = [ + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.61.2", +] + +[[package]] +name = "moxcms" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" +dependencies = [ + "num-traits", + "pxfm", +] + +[[package]] +name = "muda" +version = "0.19.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd04e60bc0b07438a6771710ee1698f98f6ebbc7f89b61264af1563b8aeb878" +dependencies = [ + "crossbeam-channel", + "dpi", + "gtk", + "keyboard-types 0.7.0", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "once_cell", + "png 0.18.1", + "serde", + "thiserror 2.0.20", + "windows-sys 0.61.2", +] + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.13.1", + "jni-sys 0.3.1", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle", + "thiserror 1.0.69", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys 0.3.1", +] + +[[package]] +name = "neli" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93062a0dce6da2517ea35f301dfc88184ce18d3601ec786a727a87bf535deca9" +dependencies = [ + "byteorder", + "libc", + "log", + "neli-proc-macros", +] + +[[package]] +name = "neli-proc-macros" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c8034b7fbb6f9455b2a96c19e6edf8dc9fc34c70449938d8ee3b4df363f61fe" +dependencies = [ + "either", + "proc-macro2", + "quote", + "serde", + "syn 1.0.109", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nix" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" +dependencies = [ + "bitflags 2.13.1", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "notify-rust" +version = "4.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5b4c1b4f2aa9f25f63a7a49d3dd0ed567b3670da15330a66b29434be899b891" +dependencies = [ + "futures-lite", + "log", + "mac-notification-sys", + "serde", + "tauri-winrt-notification", + "zbus", +] + +[[package]] +name = "nt-time" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "293dcb75775acfac821c0be754ea8489cc606689f1a23faa11425c0154e5a85c" +dependencies = [ + "chrono", + "dos-date-time", + "jiff", + "rand 0.10.2", + "time", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", + "objc2-exception-helper", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags 2.13.1", + "block2", + "libc", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-text", + "objc2-core-video", + "objc2-foundation", + "objc2-quartz-core", +] + +[[package]] +name = "objc2-application-services" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69282c2b5bc58fba07cb9de2113619532eb551e98efe3d8d695509ef45fbd53b" +dependencies = [ + "objc2-core-foundation", +] + +[[package]] +name = "objc2-avf-audio" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13a380031deed8e99db00065c45937da434ca987c034e13b87e4441f9e4090be" +dependencies = [ + "bitflags 2.13.1", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-call-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e33ab4dde42f7a6955a9ed8af82b9ff7d163b3791f5cf6288312742dc874403" +dependencies = [ + "block2", + "dispatch2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" +dependencies = [ + "bitflags 2.13.1", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" +dependencies = [ + "bitflags 2.13.1", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.13.1", + "block2", + "dispatch2", + "libc", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.13.1", + "dispatch2", + "libc", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-location" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-text" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +dependencies = [ + "bitflags 2.13.1", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", +] + +[[package]] +name = "objc2-core-video" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6" +dependencies = [ + "bitflags 2.13.1", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-io-surface", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-exception-helper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7a1c5fbb72d7735b076bb47b578523aedc40f3c439bea6dfd595c089d79d98a" +dependencies = [ + "cc", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.13.1", + "block2", + "libc", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags 2.13.1", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-osa-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f112d1746737b0da274ef79a23aac283376f335f4095a083a267a082f21db0c0" +dependencies = [ + "bitflags 2.13.1", + "objc2", + "objc2-app-kit", + "objc2-foundation", +] + +[[package]] +name = "objc2-photos" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6ea9a706f18e32cf8a0723e99855af4f135282891e07259301bae9135ae3872" +dependencies = [ + "bitflags 2.13.1", + "block2", + "dispatch2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags 2.13.1", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +dependencies = [ + "bitflags 2.13.1", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-core-text", + "objc2-foundation", + "objc2-quartz-core", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-web-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2e5aaab980c433cf470df9d7af96a7b46a9d892d521a2cbbb2f8a4c16751e7f" +dependencies = [ + "bitflags 2.13.1", + "block2", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "open" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9cfef937e9c486488c7e3d949ae31c0f1d06bdacd75b99c086cb35356e30408" +dependencies = [ + "dunce", + "is-wsl", + "libc", +] + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "orbclient" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5df339f526ea9a60e371768d50efc2f2508c7203290731565d1f7a6f71d21747" +dependencies = [ + "libc", + "libredox", +] + +[[package]] +name = "ordered-multimap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" +dependencies = [ + "dlv-list", + "hashbrown 0.14.5", +] + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "os_info" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf20a545b305cf1da722b236b5155c9bb35f1d5ceb28c048bd96ca842f41b5b" +dependencies = [ + "android_system_properties", + "log", + "nix", + "objc2", + "objc2-foundation", + "objc2-ui-kit", + "serde", + "windows-sys 0.61.2", +] + +[[package]] +name = "os_pipe" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "osakit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "732c71caeaa72c065bb69d7ea08717bd3f4863a4f451402fc9513e29dbd5261b" +dependencies = [ + "objc2", + "objc2-foundation", + "objc2-osa-kit", + "serde", + "serde_json", + "thiserror 2.0.20", +] + +[[package]] +name = "owned_ttf_parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b" +dependencies = [ + "ttf-parser", +] + +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.18", + "smallvec", + "windows-link 0.2.1", +] + +[[package]] +name = "parse-zoneinfo" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" +dependencies = [ + "regex", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pest" +version = "2.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7df728be843c7070fab6ab7c328c4e9e9d78e23bf749c0669c86ee7ebfa050a2" +dependencies = [ + "memchr", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e2dd6fc3b26b3462ee188aac870f5a41d398f1cd5e2408d16531bd71c9591fd" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a7a9205cfb6f596a9e8b689c0a15f9ceb7a1aafae7aaf788150ac65b29975b6" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "pest_meta" +version = "2.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85abd351c0de1e8384fc791a0737111a350394937e92b956b743dac12429f57c" +dependencies = [ + "pest", +] + +[[package]] +name = "petgraph" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" +dependencies = [ + "fixedbitset", + "hashbrown 0.15.5", + "indexmap 2.14.0", +] + +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_shared 0.8.0", +] + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_macros 0.10.0", + "phf_shared 0.10.0", + "proc-macro-hack", +] + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_shared 0.11.3", +] + +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros 0.13.1", + "phf_shared 0.13.1", + "serde", +] + +[[package]] +name = "phf_codegen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", +] + +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator 0.11.3", + "phf_shared 0.11.3", +] + +[[package]] +name = "phf_codegen" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared 0.8.0", + "rand 0.7.3", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand 0.8.7", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared 0.11.3", + "rand 0.8.7", +] + +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared 0.13.1", +] + +[[package]] +name = "phf_macros" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher 0.3.11", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher 0.3.11", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher 1.0.3", +] + +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher 1.0.3", +] + +[[package]] +name = "pin-project" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "piper" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "plist" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da1d65da6dd5d1e44199ac0f58712d241c0f439f80adea8924d832384087f85" +dependencies = [ + "base64 0.22.1", + "indexmap 2.14.0", + "quick-xml", + "serde", + "time", +] + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags 2.13.1", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "portable-atomic" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" + +[[package]] +name = "portable-atomic-util" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +dependencies = [ + "toml_datetime 0.6.3", + "toml_edit 0.20.2", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit 0.25.13+spec-1.1.0", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "prost-types" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" +dependencies = [ + "prost", +] + +[[package]] +name = "psl-types" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" + +[[package]] +name = "publicsuffix" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf" +dependencies = [ + "idna", + "psl-types", +] + +[[package]] +name = "pxfm" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea" + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quick-xml" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1" +dependencies = [ + "memchr", +] + +[[package]] +name = "quinn" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2 0.6.5", + "thiserror 2.0.20", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560" +dependencies = [ + "bytes", + "getrandom 0.4.3", + "lru-slab", + "rand 0.10.2", + "rand_pcg 0.10.2", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.20", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2 0.6.5", + "tracing", + "windows-sys 0.61.2", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg 0.2.1", +] + +[[package]] +name = "rand" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" +dependencies = [ + "chacha20", + "getrandom 0.4.3", + "rand_core 0.10.1", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core 0.10.1", +] + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.13.1", +] + +[[package]] +name = "redox_syscall" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1c93da5bb2c5d4e6c0ef7abeead62c89169a0a4882bfb83ac892f2423aea2fe" +dependencies = [ + "bitflags 2.13.1", +] + +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 2.0.20", +] + +[[package]] +name = "ref-cast" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216e8f773d7923bcba9ceb86a86c93cabb3903a11872fc3f138c49630e50b96d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9283685feec7d69af75fb0e858d5e7378f33fe4fc699383b2916ab9273e03c" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "regex" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64 0.22.1", + "bytes", + "cookie", + "cookie_store", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.4.15", + "http 1.5.0", + "http-body 1.1.0", + "http-body-util", + "hyper 1.11.0", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.2", + "tokio", + "tokio-rustls", + "tokio-util", + "tower 0.5.3", + "tower-http 0.6.11", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams 0.4.2", + "web-sys", + "webpki-roots", +] + +[[package]] +name = "reqwest" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "futures-util", + "http 1.5.0", + "http-body 1.1.0", + "http-body-util", + "hyper 1.11.0", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "serde", + "serde_json", + "sync_wrapper 1.0.2", + "tokio", + "tokio-rustls", + "tokio-util", + "tower 0.5.3", + "tower-http 0.6.11", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams 0.5.0", + "web-sys", +] + +[[package]] +name = "rfd" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15ad77d9e70a92437d8f74c35d99b4e4691128df018833e99f90bcd36152672" +dependencies = [ + "block2", + "dispatch2", + "glib-sys", + "gobject-sys", + "gtk-sys", + "js-sys", + "log", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "raw-window-handle", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.60.2", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "ringbuf" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe47b720588c8702e34b5979cb3271a8b1842c7cb6f57408efa70c779363488c" +dependencies = [ + "crossbeam-utils", + "portable-atomic", + "portable-atomic-util", +] + +[[package]] +name = "rust-ini" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "796e8d2b6696392a43bea58116b667fb4c29727dc5abd27d6acf338bb4f688c7" +dependencies = [ + "cfg-if", + "ordered-multimap", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b74b56ffa8bb2830709a538c2cbcae9aa062db0d2a42563bfb09bdaae44020eb" + +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.13.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-platform-verifier" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni 0.22.4", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + +[[package]] +name = "rustls-webpki" +version = "0.103.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0527518605e68109d875e248ea259b6758801cf165e4b2c2733ae3b51f12535a" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "sable" +version = "1.21.0" +dependencies = [ + "aes", + "async-stream", + "base64 0.23.1", + "block2", + "cef", + "ctr", + "enigo", + "futures-util", + "gtk", + "infer 0.22.0", + "jni 0.22.4", + "libloading 0.9.0", + "log", + "objc2", + "objc2-avf-audio", + "objc2-call-kit", + "objc2-foundation", + "objc2-photos", + "percent-encoding", + "regex", + "reqwest 0.12.28", + "sentry", + "serde", + "serde_json", + "sha2 0.11.0", + "tauri", + "tauri-build", + "tauri-plugin-android-fs", + "tauri-plugin-app-icon", + "tauri-plugin-clipboard-manager", + "tauri-plugin-deep-link", + "tauri-plugin-devtools", + "tauri-plugin-dialog", + "tauri-plugin-edge-to-edge", + "tauri-plugin-fs", + "tauri-plugin-global-shortcut", + "tauri-plugin-http", + "tauri-plugin-livekit-mobile", + "tauri-plugin-log", + "tauri-plugin-notifications", + "tauri-plugin-opener", + "tauri-plugin-os", + "tauri-plugin-process", + "tauri-plugin-sharekit", + "tauri-plugin-single-instance", + "tauri-plugin-store", + "tauri-plugin-updater", + "tauri-plugin-window-state", + "tauri-runtime-cef", + "tauri-typegen", + "tokio", + "tokio-util", + "ts-rs", + "webkit2gtk", + "windows 0.62.2", + "zbus", + "zip 8.6.0", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "indexmap 1.9.3", + "schemars_derive", + "serde", + "serde_json", + "url", + "uuid", +] + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "687274d293b6cdc6e73e0fee520bf2049650090d7164f87672d212a3c530cf4a" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.119", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sctk-adwaita" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2a4360b9abcdcee43809e6fc11d6d61ca5d25734026a8fc46c2883eea3f13f" +dependencies = [ + "ab_glyph", + "log", + "memmap2", + "smithay-client-toolkit", + "tiny-skia", +] + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.13.1", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "selectors" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c37578180969d00692904465fb7f6b3d50b9a2b952b87c23d0e2e5cb5013416" +dependencies = [ + "bitflags 1.3.2", + "cssparser 0.29.6", + "derive_more 0.99.20", + "fxhash", + "log", + "phf 0.8.0", + "phf_codegen 0.8.0", + "precomputed-hash", + "servo_arc 0.2.0", + "smallvec", +] + +[[package]] +name = "selectors" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5d9c0c92a92d33f08817311cf3f2c29a3538a8240e94a6a3c622ce652d7e00c" +dependencies = [ + "bitflags 2.13.1", + "cssparser 0.36.0", + "derive_more 2.1.1", + "log", + "new_debug_unreachable", + "phf 0.13.1", + "phf_codegen 0.13.1", + "precomputed-hash", + "rustc-hash", + "servo_arc 0.4.3", + "smallvec", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "sentry" +version = "0.49.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63207365db50cb817402f0ec8097d6dad3d644ef3fffd6ba30c75b3077e514da" +dependencies = [ + "cfg_aliases", + "httpdate", + "sentry-backtrace", + "sentry-contexts", + "sentry-core", + "sentry-debug-images", + "sentry-panic", + "sentry-tracing", + "ureq", +] + +[[package]] +name = "sentry-backtrace" +version = "0.49.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bcc2497c2327998146207b7600599ef7592233920f4d4e1d41ddeeba0e4210" +dependencies = [ + "backtrace", + "regex", + "sentry-core", +] + +[[package]] +name = "sentry-contexts" +version = "0.49.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cb04cba225b38f59d08f9e3c29ab7259d9cc94fbb2c46d613a51cb0a4a7ee05" +dependencies = [ + "hostname", + "libc", + "os_info", + "rustc_version", + "sentry-core", + "uname", +] + +[[package]] +name = "sentry-core" +version = "0.49.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48759bc392fb5e3b36b4efcb485f51074c0ba8479711cd5c8cf79e86f9f75d41" +dependencies = [ + "rand 0.9.5", + "sentry-types", + "serde", + "serde_json", + "url", +] + +[[package]] +name = "sentry-debug-images" +version = "0.49.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a5bd325059b70b21ca6e42b0f2409821272dddc1cf37923de37269af55389b5" +dependencies = [ + "findshlibs", + "sentry-core", +] + +[[package]] +name = "sentry-panic" +version = "0.49.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a685d22f672b1562ebeff3f2affceb5960595648cc936dae73da3e1d6a55fbd1" +dependencies = [ + "sentry-backtrace", + "sentry-core", +] + +[[package]] +name = "sentry-tracing" +version = "0.49.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8aa7d8e0db4cccddbac01ddabd5344ad03b7c2f954b3ff850cecb1d9cf5d4758" +dependencies = [ + "bitflags 2.13.1", + "sentry-backtrace", + "sentry-core", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "sentry-types" +version = "0.49.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fab146d15a30ab4897a95fd15d6a038b8d7fbf2661c5f8b13738ce8df7a095b7" +dependencies = [ + "debugid", + "hex", + "rand 0.9.5", + "serde", + "serde_json", + "thiserror 2.0.20", + "time", + "url", + "uuid", +] + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-rename-rule" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a059d895f1a31dd928f40abbea4e7177e3d8ff3aa4152fdb7a396ae1ef63a3" + +[[package]] +name = "serde-untagged" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" +dependencies = [ + "erased-serde", + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "serde_json" +version = "1.0.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_repr" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee78f1fbe43ac4a0e47aadb3dbd357b69eb0d3793e948624cd03dd2750ab1c0a" +dependencies = [ + "base64 0.22.1", + "bs58", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.14.0", + "jiff", + "schemars 0.9.0", + "schemars 1.2.2", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8705578779c2b6bd90d84d66eb2e206b708b1a4d7b9f17641b293545bf1c7e46" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "serialize-to-javascript" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04f3666a07a197cdb77cdf306c32be9b7f598d7060d50cfd4d5aa04bfd92f6c5" +dependencies = [ + "serde", + "serde_json", + "serialize-to-javascript-impl", +] + +[[package]] +name = "serialize-to-javascript-impl" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "servo_arc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52aa42f8fdf0fed91e5ce7f23d8138441002fa31dca008acf47e6fd4721f741" +dependencies = [ + "nodrop", + "stable_deref_trait", +] + +[[package]] +name = "servo_arc" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "170fb83ab34de17dc69aa7c67482b22218ddb85da56546f9bd6b929e32a05930" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "sha1_smol" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" + +[[package]] +name = "simd_cesu8" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "siphasher" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "slug" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "882a80f72ee45de3cc9a5afeb2da0331d58df69e4e7d8eeb5d3c7784ae67e724" +dependencies = [ + "deunicode", + "wasm-bindgen", +] + +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "smithay-client-toolkit" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0" +dependencies = [ + "bitflags 2.13.1", + "calloop", + "calloop-wayland-source", + "cursor-icon", + "libc", + "log", + "memmap2", + "rustix", + "thiserror 2.0.20", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols", + "wayland-protocols-experimental", + "wayland-protocols-misc", + "wayland-protocols-wlr", + "wayland-scanner", + "xkeysym", +] + +[[package]] +name = "smol_str" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aaa7368fcf4852a4c2dd92df0cace6a71f2091ca0a23391ce7f3a31833f1523" +dependencies = [ + "borsh", + "serde_core", +] + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "socks" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b" +dependencies = [ + "byteorder", + "libc", + "winapi", +] + +[[package]] +name = "softbuffer" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac18da81ebbf05109ab275b157c22a653bb3c12cf884450179942f81bcbf6c3" +dependencies = [ + "bytemuck", + "js-sys", + "ndk", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "objc2-quartz-core", + "raw-window-handle", + "redox_syscall 0.5.18", + "tracing", + "wasm-bindgen", + "web-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "soup3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", + "gio", + "glib", + "libc", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" + +[[package]] +name = "string_cache" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared 0.11.3", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared 0.13.1", + "precomputed-hash", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" +dependencies = [ + "phf_generator 0.11.3", + "phf_shared 0.11.3", + "proc-macro2", + "quote", +] + +[[package]] +name = "string_cache_codegen" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", + "proc-macro2", + "quote", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "swift-bridge" +version = "0.1.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384ed39ea10f1cefabb197b7d8e67f0034b15a94ccbb1038b8e020da59bfb0be" +dependencies = [ + "once_cell", + "swift-bridge-build", + "swift-bridge-macro", + "tokio", +] + +[[package]] +name = "swift-bridge-build" +version = "0.1.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71b36df21e7f8a8b5eeb718d2e71f9cfc308477bfb705981cca705de9767dcb7" +dependencies = [ + "proc-macro2", + "swift-bridge-ir", + "syn 1.0.109", + "tempfile", +] + +[[package]] +name = "swift-bridge-ir" +version = "0.1.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c73bd16155df50708b92306945656e57d62d321290a7db490f299f709fb31c83" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "swift-bridge-macro" +version = "0.1.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a13dc0dc875d85341dec5b5344a7d713f20eb5650b71086b27d09a6ece272f" +dependencies = [ + "proc-macro2", + "quote", + "swift-bridge-ir", + "syn 1.0.109", +] + +[[package]] +name = "swift-rs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4057c98e2e852d51fdcfca832aac7b571f6b351ad159f9eda5db1655f8d0c4d7" +dependencies = [ + "base64 0.21.7", + "serde", + "serde_json", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_async" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b815ef8e053247ad785b136d233ee9c9872eeda5319f2719d57cac88270c3dd9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "sys-locale" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" +dependencies = [ + "libc", +] + +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags 2.13.1", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml 0.8.2", + "version-compare", +] + +[[package]] +name = "tao" +version = "0.35.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1c93047acf68669466a34690ac58cca7010bd1b201e1ec86f1fd0a75d3dd4a9" +dependencies = [ + "bitflags 2.13.1", + "block2", + "core-foundation 0.10.1", + "core-graphics 0.25.0", + "crossbeam-channel", + "dbus", + "dispatch2", + "dlopen2", + "dpi", + "gdkwayland-sys", + "gdkx11-sys", + "gtk", + "jni 0.21.1", + "libc", + "log", + "ndk", + "ndk-sys", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "once_cell", + "parking_lot", + "percent-encoding", + "raw-window-handle", + "tao-macros", + "unicode-segmentation", + "url", + "windows 0.61.3", + "windows-core 0.61.2", + "windows-version", + "x11-dl", +] + +[[package]] +name = "tao-macros" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7eeb6d99155545da6150a1795945f16ac9c178deb2a5f2e74d776107bd5849" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "tar" +version = "0.4.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "tauri" +version = "2.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "667b20e2726d572dea2de7370da16e188eb06008faf9a92fab7cdc46791190b5" +dependencies = [ + "anyhow", + "bytes", + "cookie", + "dirs", + "dunce", + "embed_plist", + "getrandom 0.3.4", + "glob", + "gtk", + "heck 0.5.0", + "http 1.5.0", + "image", + "jni 0.21.1", + "libc", + "log", + "mime", + "muda", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "objc2-web-kit", + "percent-encoding", + "plist", + "raw-window-handle", + "reqwest 0.13.4", + "serde", + "serde_json", + "serde_repr", + "serialize-to-javascript", + "swift-rs", + "tauri-build", + "tauri-macros", + "tauri-runtime", + "tauri-runtime-wry", + "tauri-utils", + "thiserror 2.0.20", + "tokio", + "tracing", + "tray-icon", + "url", + "webkit2gtk", + "webview2-com", + "window-vibrancy", + "windows 0.61.3", +] + +[[package]] +name = "tauri-build" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc9ce40b16101cb6ea63d3e221567affd1c3a9205f95d7bc574941a10636b632" +dependencies = [ + "anyhow", + "cargo_toml", + "dirs", + "glob", + "heck 0.5.0", + "json-patch", + "schemars 0.8.22", + "semver", + "serde", + "serde_json", + "tauri-utils", + "tauri-winres", + "walkdir", +] + +[[package]] +name = "tauri-codegen" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08279169ff42f8fc45a1dbc9dcae888893ba95288142e5880c59b93a26d2cfc5" +dependencies = [ + "base64 0.22.1", + "brotli", + "ico", + "json-patch", + "plist", + "png 0.17.16", + "proc-macro2", + "quote", + "semver", + "serde", + "serde_json", + "sha2 0.10.9", + "syn 2.0.119", + "tauri-utils", + "thiserror 2.0.20", + "time", + "url", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-macros" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8b394794f399a421811d06966343e7933fcae92d59f5180b9388d1174497a45" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.119", + "tauri-codegen", + "tauri-utils", +] + +[[package]] +name = "tauri-plugin" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74be5dd4bed9afbd145e5716b5fa2ec28cbc29c34ffa61c258c9273d896c8020" +dependencies = [ + "anyhow", + "glob", + "plist", + "schemars 0.8.22", + "serde", + "serde_json", + "tauri-utils", + "walkdir", +] + +[[package]] +name = "tauri-plugin-android-fs" +version = "29.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca3a802951458c58c32e4605d43143ca92c205ba3eb7c989b03183cd88e9e6e" +dependencies = [ + "base64 0.22.1", + "glob", + "percent-encoding", + "schemars 0.8.22", + "serde", + "serde_json", + "sync_async", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.20", +] + +[[package]] +name = "tauri-plugin-app-icon" +version = "0.1.0" +source = "git+https://github.com/SableClient/tauri-plugin-app-icon?rev=bab80ae17f6d018702e472c539d0f02eba480c21#bab80ae17f6d018702e472c539d0f02eba480c21" +dependencies = [ + "serde", + "tauri", + "tauri-plugin", + "thiserror 2.0.20", +] + +[[package]] +name = "tauri-plugin-clipboard-manager" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206dc20af4ed210748ba945c2774e60fd0acd52b9a73a028402caf809e9b6ecf" +dependencies = [ + "arboard", + "log", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.20", +] + +[[package]] +name = "tauri-plugin-deep-link" +version = "2.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ee75bc5627f77bfdf40c913255ebc258117b10ebe2b2239a1a1cf40b0b58aa" +dependencies = [ + "dunce", + "plist", + "rust-ini", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-utils", + "thiserror 2.0.20", + "tracing", + "url", + "windows-registry", + "windows-result 0.3.4", +] + +[[package]] +name = "tauri-plugin-devtools" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8fa7b8275c5f3efed02591d5c080540bec4c3e5b3180586fcd824953328e8e" +dependencies = [ + "async-stream", + "bytes", + "cocoa", + "colored", + "devtools-core", + "futures", + "local-ip-address", + "log", + "objc", + "serde", + "serde_json", + "swift-rs", + "tauri", + "tauri-plugin", + "tokio", + "tonic", + "tonic-health", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tauri-plugin-dialog" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2d3c1dbe38037e7f590cdf2492594d5ceebe031e7bc7e827509b22a999d2940" +dependencies = [ + "log", + "raw-window-handle", + "rfd", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.20", + "url", +] + +[[package]] +name = "tauri-plugin-edge-to-edge" +version = "0.3.3" +source = "git+https://github.com/SableClient/tauri-plugin-edge-to-edge.git?rev=33c6116c27be28c06df5a9d02231ecc5fdeb93c5#33c6116c27be28c06df5a9d02231ecc5fdeb93c5" +dependencies = [ + "serde", + "tauri", + "tauri-plugin", + "thiserror 2.0.20", +] + +[[package]] +name = "tauri-plugin-fs" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ecc274121aca0c036a2b42d1cbe83d368d348f54e0bb8a735c2b1548e8f371" +dependencies = [ + "anyhow", + "dunce", + "glob", + "log", + "objc2-foundation", + "percent-encoding", + "schemars 0.8.22", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-plugin", + "tauri-utils", + "thiserror 2.0.20", + "toml 1.1.4+spec-1.1.0", + "url", +] + +[[package]] +name = "tauri-plugin-global-shortcut" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4dd9f4c5136c09cd962da0c86dc4accd4666db2ea591cf16e6597435843bd2b" +dependencies = [ + "global-hotkey", + "log", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.20", +] + +[[package]] +name = "tauri-plugin-http" +version = "2.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5bd512048e1985b7ec78f96d99083e2ddaf7e0d906b2b63c44ce5bb8b894067" +dependencies = [ + "bytes", + "cookie_store", + "data-url", + "http 1.5.0", + "regex", + "reqwest 0.12.28", + "schemars 0.8.22", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.20", + "tokio", + "url", + "urlpattern", +] + +[[package]] +name = "tauri-plugin-livekit-mobile" +version = "0.2.0" +source = "git+https://github.com/SableClient/tauri-plugin-livekit-mobile.git?rev=c6f0d02ab3da1174535bc1dc8e598a2e6873f977#c6f0d02ab3da1174535bc1dc8e598a2e6873f977" +dependencies = [ + "base64 0.22.1", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.20", + "tokio", +] + +[[package]] +name = "tauri-plugin-log" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6792296e6f389268016c77db21ebae1fc0568f2fccf88b1ec7e2ea71330afb4c" +dependencies = [ + "android_logger", + "fern", + "log", + "objc2", + "objc2-foundation", + "serde", + "serde_json", + "serde_repr", + "swift-rs", + "tauri", + "tauri-plugin", + "thiserror 2.0.20", + "time", +] + +[[package]] +name = "tauri-plugin-notifications" +version = "0.5.0-rc.11" +source = "git+https://github.com/SableClient/tauri-plugin-notifications.git?rev=301e4ac43970525764eb2f0c787884129db2847f#301e4ac43970525764eb2f0c787884129db2847f" +dependencies = [ + "log", + "notify-rust", + "nt-time", + "rand 0.10.2", + "serde", + "serde_json", + "serde_repr", + "swift-bridge", + "swift-bridge-build", + "tauri", + "tauri-plugin", + "thiserror 2.0.20", + "time", + "tokio", + "url", + "uuid", + "windows 0.61.3", + "windows-core 0.61.2", + "zbus", +] + +[[package]] +name = "tauri-plugin-opener" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17e1bea14edce6b793a04e2417e3fd924b9bc4faae83cdee7d714156cceeed29" +dependencies = [ + "dunce", + "glob", + "objc2-app-kit", + "objc2-foundation", + "open", + "schemars 0.8.22", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.20", + "url", + "windows 0.61.3", + "zbus", +] + +[[package]] +name = "tauri-plugin-os" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8f08346c8deb39e96f86973da0e2d76cbb933d7ac9b750f6dc4daf955a6f997" +dependencies = [ + "gethostname", + "log", + "os_info", + "serde", + "serde_json", + "serialize-to-javascript", + "sys-locale", + "tauri", + "tauri-plugin", + "thiserror 2.0.20", +] + +[[package]] +name = "tauri-plugin-process" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d55511a7bf6cd70c8767b02c97bf8134fa434daf3926cfc1be0a0f94132d165a" +dependencies = [ + "tauri", + "tauri-plugin", +] + +[[package]] +name = "tauri-plugin-sharekit" +version = "0.4.0-rc.5" +source = "git+https://github.com/Choochmeque/tauri-plugin-sharekit?rev=9f2b4c5d8a4f0ab910d900ba234ed8bae0ab854b#9f2b4c5d8a4f0ab910d900ba234ed8bae0ab854b" +dependencies = [ + "log", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-plugin", + "thiserror 2.0.20", + "tokio", + "windows 0.61.3", + "windows-collections 0.2.0", +] + +[[package]] +name = "tauri-plugin-single-instance" +version = "2.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3214becf9ef5783c0ae99a3bb25adf5353a7a16ebf53e74b909e29205735c6c" +dependencies = [ + "serde", + "serde_json", + "tauri", + "tauri-plugin-deep-link", + "thiserror 2.0.20", + "tokio", + "tracing", + "windows-sys 0.60.2", + "zbus", +] + +[[package]] +name = "tauri-plugin-store" +version = "2.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6708afbe549f176b712066e71648ba8fafba20789453718260c7ca356733cb0c" +dependencies = [ + "dunce", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.20", + "tokio", + "tracing", +] + +[[package]] +name = "tauri-plugin-updater" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806d9dac662c2e4594ff03c647a552f2c9bd544e7d0f683ec58f872f952ce4af" +dependencies = [ + "base64 0.22.1", + "dirs", + "flate2", + "futures-util", + "http 1.5.0", + "infer 0.19.0", + "log", + "minisign-verify", + "osakit", + "percent-encoding", + "reqwest 0.13.4", + "rustls", + "semver", + "serde", + "serde_json", + "tar", + "tauri", + "tauri-plugin", + "tempfile", + "thiserror 2.0.20", + "time", + "tokio", + "url", + "windows-sys 0.60.2", + "zip 4.6.1", +] + +[[package]] +name = "tauri-plugin-window-state" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73736611e14142408d15353e21e3cca2f12a3cfb523ad0ce85999b6d2ef1a704" +dependencies = [ + "bitflags 2.13.1", + "log", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.20", +] + +[[package]] +name = "tauri-runtime" +version = "2.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0b4bc95aed361b0019067d189a1174a603d460d0f6c72606512d59fc9c12ec8" +dependencies = [ + "cookie", + "dpi", + "gtk", + "http 1.5.0", + "jni 0.21.1", + "objc2", + "objc2-ui-kit", + "objc2-web-kit", + "raw-window-handle", + "serde", + "serde_json", + "tauri-utils", + "thiserror 2.0.20", + "url", + "webkit2gtk", + "webview2-com", + "windows 0.61.3", +] + +[[package]] +name = "tauri-runtime-cef" +version = "0.1.0" +source = "git+https://github.com/SableClient/tauri-runtime-cef?rev=6fb4fabcc759c1f5407def192540ce149f666fe8#6fb4fabcc759c1f5407def192540ce149f666fe8" +dependencies = [ + "base64 0.22.1", + "cef", + "cef-dll-sys", + "dioxus-debug-cell", + "dirs", + "dispatch2", + "dlopen2", + "gtk", + "html5ever 0.29.1", + "http 1.5.0", + "kuchikiki", + "libc", + "log", + "objc2", + "objc2-app-kit", + "objc2-application-services", + "objc2-foundation", + "objc2-quartz-core", + "raw-window-handle", + "serde", + "serde_json", + "sha2 0.10.9", + "softbuffer", + "tauri-runtime", + "tauri-utils", + "url", + "windows 0.61.3", + "winit", + "x11-dl", +] + +[[package]] +name = "tauri-runtime-wry" +version = "2.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e6fac707727b7a2f48e4ded90976324267371073edbb415ffb73bb0458d203f" +dependencies = [ + "gtk", + "http 1.5.0", + "jni 0.21.1", + "log", + "objc2", + "objc2-app-kit", + "once_cell", + "percent-encoding", + "raw-window-handle", + "softbuffer", + "tao", + "tauri-runtime", + "tauri-utils", + "tracing", + "url", + "webkit2gtk", + "webview2-com", + "windows 0.61.3", + "wry", +] + +[[package]] +name = "tauri-typegen" +version = "0.5.0" +source = "git+https://github.com/SableClient/tauri-typegen?branch=fix%2Fnondeterministic-generation-cache#645ac948bbd940704cda4419a203e307e7f0d429" +dependencies = [ + "chrono", + "clap", + "indicatif 0.17.11", + "proc-macro2", + "quote", + "regex", + "serde", + "serde-rename-rule", + "serde_json", + "syn 2.0.119", + "tera", + "thiserror 2.0.20", + "walkdir", +] + +[[package]] +name = "tauri-utils" +version = "2.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e176a18e67764923c4f1ce66f25ae4abe5f688384d5eb1a0fa6c77f3d90f887" +dependencies = [ + "anyhow", + "brotli", + "cargo_metadata 0.19.2", + "ctor", + "dom_query", + "dunce", + "glob", + "html5ever 0.29.1", + "http 1.5.0", + "infer 0.19.0", + "json-patch", + "kuchikiki", + "log", + "memchr", + "phf 0.13.1", + "plist", + "proc-macro2", + "quote", + "regex", + "schemars 0.8.22", + "semver", + "serde", + "serde-untagged", + "serde_json", + "serde_with", + "swift-rs", + "thiserror 2.0.20", + "toml 1.1.4+spec-1.1.0", + "url", + "urlpattern", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-winres" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc65d45c68858bfe420dd29e834b5d15dbecf8a07a8a16cf4d532c7b1f69d4b6" +dependencies = [ + "dunce", + "embed-resource", + "toml 1.1.4+spec-1.1.0", +] + +[[package]] +name = "tauri-winrt-notification" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed071c670382e85fc2f48ae706492d8c338f4f89bf72520d32f8abfe880aade" +dependencies = [ + "thiserror 2.0.20", + "windows 0.61.3", + "windows-version", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.3", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + +[[package]] +name = "tendril" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fed54709c5b3a53d09bb1c113ea4f5ceafd1e772ddcb0030a82e1d56c087b08" +dependencies = [ + "new_debug_unreachable", +] + +[[package]] +name = "tera" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8004bca281f2d32df3bacd59bc67b312cb4c70cea46cbd79dbe8ac5ed206722" +dependencies = [ + "chrono", + "chrono-tz", + "globwalk", + "humansize", + "lazy_static", + "percent-encoding", + "pest", + "pest_derive", + "rand 0.8.7", + "regex", + "serde", + "serde_json", + "slug", + "unicode-segmentation", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" +dependencies = [ + "thiserror-impl 2.0.20", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "thread_local" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "tiff" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" +dependencies = [ + "fax", + "flate2", + "half", + "quick-error", + "weezl", + "zune-jpeg", +] + +[[package]] +name = "time" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" +dependencies = [ + "deranged", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "time-macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tiny-skia" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47ffee5eaaf5527f630fb0e356b90ebdec84d5d18d937c5e440350f88c5a91ea" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edca365c3faccca67d06593c5980fa6c57687de727a03131735bb85f01fdeeb9" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.6.5", + "tokio-macros", + "tracing", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-io-timeout" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bd86198d9ee903fedd2f9a2e72014287c0d9167e4ae43b5853007205dda1b76" +dependencies = [ + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-macros" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d06f0b082ba57c26b79407372e57cf2a1e28124f78e9479fe80322cf53420b" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "libc", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +dependencies = [ + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.3", + "toml_edit 0.20.2", +] + +[[package]] +name = "toml" +version = "0.9.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" +dependencies = [ + "indexmap 2.14.0", + "serde_core", + "serde_spanned 1.1.1", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 0.7.15", +] + +[[package]] +name = "toml" +version = "1.1.4+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3aace63f4bbcdfc2c965b059de67119c89c4017a70d633be6c104910f67056f5" +dependencies = [ + "indexmap 2.14.0", + "serde_core", + "serde_spanned 1.1.1", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 1.0.4", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.14.0", + "toml_datetime 0.6.3", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.14.0", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.3", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.25.13+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b" +dependencies = [ + "indexmap 2.14.0", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "winnow 1.0.4", +] + +[[package]] +name = "toml_parser" +version = "1.1.3+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" +dependencies = [ + "winnow 1.0.4", +] + +[[package]] +name = "toml_writer" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" + +[[package]] +name = "tonic" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d560933a0de61cf715926b9cac824d4c883c2c43142f787595e48280c40a1d0e" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64 0.21.7", + "bytes", + "h2 0.3.27", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost", + "tokio", + "tokio-stream", + "tower 0.4.13", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic-health" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f80db390246dfb46553481f6024f0082ba00178ea495dbb99e70ba9a4fafb5e1" +dependencies = [ + "async-stream", + "prost", + "tokio", + "tokio-stream", + "tonic", +] + +[[package]] +name = "tonic-web" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fddb2a37b247e6adcb9f239f4e5cefdcc5ed526141a416b943929f13aea2cce" +dependencies = [ + "base64 0.21.7", + "bytes", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.32", + "pin-project", + "tokio-stream", + "tonic", + "tower-http 0.4.4", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap 1.9.3", + "pin-project", + "pin-project-lite", + "rand 0.8.7", + "slab", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 1.0.2", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" +dependencies = [ + "bitflags 2.13.1", + "bytes", + "futures-core", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "http-range-header", + "pin-project-lite", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags 2.13.1", + "bytes", + "futures-util", + "http 1.5.0", + "http-body 1.1.0", + "pin-project-lite", + "tower 0.5.3", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "tray-icon" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "045979e3f037cd18ad1cb2a419dfda133c5c29c9f3453370079f2255d46c257e" +dependencies = [ + "crossbeam-channel", + "dirs", + "libappindicator", + "muda", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "once_cell", + "png 0.18.1", + "serde", + "thiserror 2.0.20", + "windows-sys 0.61.2", +] + +[[package]] +name = "tree_magic_mini" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8765b90061cba6c22b5831f675da109ae5561588290f9fa2317adab2714d5a6" +dependencies = [ + "memchr", + "nom", + "petgraph", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "ts-rs" +version = "12.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756050066659291d47a554a9f558125db17428b073c5ffce1daf5dcb0f7231d8" +dependencies = [ + "thiserror 2.0.20", + "ts-rs-macros", +] + +[[package]] +name = "ts-rs-macros" +version = "12.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d90eea51bc7988ef9e674bf80a85ba6804739e535e9cab48e4bb34a8b652aa" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", + "termcolor", +] + +[[package]] +name = "ttf-parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" + +[[package]] +name = "typed-path" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e" + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "uds_windows" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" +dependencies = [ + "memoffset", + "tempfile", + "windows-sys 0.61.2", +] + +[[package]] +name = "uname" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8" +dependencies = [ + "libc", +] + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "unit-prefix" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "972d7902c8735f2695410b8aed7df6ed12a47394aa1c8d7af49f0497b731a94d" +dependencies = [ + "base64 0.23.1", + "cookie_store", + "flate2", + "log", + "percent-encoding", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "socks", + "ureq-proto", + "utf8-zero", + "webpki-roots", +] + +[[package]] +name = "ureq-proto" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da5f78b09e6941e1a0f2e30e695e4b120377b54d5e0aec11b594bb57b3971613" +dependencies = [ + "base64 0.23.1", + "http 1.5.0", + "httparse", + "log", +] + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", + "serde_derive", +] + +[[package]] +name = "urlpattern" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" +dependencies = [ + "regex", + "serde", + "unic-ucd-ident", + "url", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8-zero" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239" +dependencies = [ + "getrandom 0.4.3", + "js-sys", + "rand 0.10.2", + "serde_core", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "version-compare" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.4+wasi-0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b7777d5cc23d0e91404e53ce2d5e8ec7acae3026b16233dba62cd3246457950" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.119", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wasm-streams" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wayland-backend" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016ccf01d1c58b6f8999612813e17c9b2390f7d70671428869913310f83f54b8" +dependencies = [ + "cc", + "downcast-rs", + "rustix", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c36a0f861ad76d0901f2800b46321410d9f73f2ea88aac0650d86c32688073" +dependencies = [ + "bitflags 2.13.1", + "rustix", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-csd-frame" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" +dependencies = [ + "bitflags 2.13.1", + "cursor-icon", + "wayland-backend", +] + +[[package]] +name = "wayland-cursor" +version = "0.31.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a52d18780be9b1314328a3de5f930b73d2200112e3849ca6cb11822793fb34d" +dependencies = [ + "rustix", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6" +dependencies = [ + "bitflags 2.13.1", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-experimental" +version = "20250721.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40a1f863128dcaaec790d7b4b396cc9b9a7a079e878e18c47e6c2d2c5a8dcbb1" +dependencies = [ + "bitflags 2.13.1", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-misc" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9567599ef23e09b8dad6e429e5738d4509dfc46b3b21f32841a304d16b29c8" +dependencies = [ + "bitflags 2.13.1", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-plasma" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b6d8cf1eb2c1c31ed1f5643c88a6e53538129d4af80030c8cabd1f9fa884d91" +dependencies = [ + "bitflags 2.13.1", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234" +dependencies = [ + "bitflags 2.13.1", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "338e30461b3a2b67d70eb30a6d89f8e0c93a833e07d2ae89085cd070c4a00ac0" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be" +dependencies = [ + "dlib", + "log", + "once_cell", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c435338968042f4f59a557f690a253676d47ce13ceb55d70100e7facf6620a30" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web_atoms" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba8b815c1b593dc0baf78dd0f4fc8fdb2de53198fb1163738093e9a311c33fb3" +dependencies = [ + "phf 0.13.1", + "phf_codegen 0.13.1", + "string_cache 0.9.0", + "string_cache_codegen 0.6.1", +] + +[[package]] +name = "webkit2gtk" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1027150013530fb2eaf806408df88461ae4815a45c541c8975e61d6f2fc4793" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup3", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916a5f65c2ef0dfe12fff695960a2ec3d4565359fdbb2e9943c974e06c734ea5" +dependencies = [ + "bitflags 1.3.2", + "cairo-sys-rs", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pkg-config", + "soup3-sys", + "system-deps", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webpki-roots" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webview2-com" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows 0.61.3", + "windows-core 0.61.2", + "windows-implement", + "windows-interface", +] + +[[package]] +name = "webview2-com-macros" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a921c1b6914c367b2b823cd4cde6f96beec77d30a939c8199bb377cf9b9b54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "webview2-com-sys" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c" +dependencies = [ + "thiserror 2.0.20", + "windows 0.61.3", + "windows-core 0.61.2", +] + +[[package]] +name = "weezl" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "window-vibrancy" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" +dependencies = [ + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "raw-window-handle", + "windows-sys 0.59.0", + "windows-version", +] + +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections 0.2.0", + "windows-core 0.61.2", + "windows-future 0.2.1", + "windows-link 0.1.3", + "windows-numerics 0.2.0", +] + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections 0.3.2", + "windows-core 0.62.2", + "windows-future 0.3.2", + "windows-numerics 0.3.1", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core 0.62.2", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading 0.1.0", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", + "windows-threading 0.2.1", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", +] + +[[package]] +name = "windows-registry" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" +dependencies = [ + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link 0.2.1", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-version" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4060a1da109b9d0326b7262c8e12c84df67cc0dbc9e33cf49e01ccc2eb63631" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winit" +version = "0.31.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2879d2854d1a43e48f67322d4bd097afcb6eb8f8f775c8de0260a71aea1df1aa" +dependencies = [ + "bitflags 2.13.1", + "cfg_aliases", + "cursor-icon", + "dpi", + "libc", + "raw-window-handle", + "rustix", + "smol_str", + "tracing", + "winit-android", + "winit-appkit", + "winit-common", + "winit-core", + "winit-orbital", + "winit-uikit", + "winit-wayland", + "winit-web", + "winit-win32", + "winit-x11", +] + +[[package]] +name = "winit-android" +version = "0.31.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d9c0d2cd93efec3a9f9ad819cfaf0834782403af7c0d248c784ec0c61761df" +dependencies = [ + "android-activity", + "bitflags 2.13.1", + "dpi", + "ndk", + "raw-window-handle", + "smol_str", + "tracing", + "winit-core", +] + +[[package]] +name = "winit-appkit" +version = "0.31.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21310ca07851a49c348e0c2cc768e36b52ca65afda2c2354d78ed4b90074d8aa" +dependencies = [ + "bitflags 2.13.1", + "block2", + "dispatch2", + "dpi", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-video", + "objc2-foundation", + "raw-window-handle", + "smol_str", + "tracing", + "winit-common", + "winit-core", +] + +[[package]] +name = "winit-common" +version = "0.31.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45375fbac4cbb77260d83a30b1f9d8105880dbac99a9ae97f56656694680ff69" +dependencies = [ + "memmap2", + "objc2", + "objc2-core-foundation", + "smol_str", + "tracing", + "winit-core", + "x11-dl", + "xkbcommon-dl", +] + +[[package]] +name = "winit-core" +version = "0.31.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4f0ccd7abb43740e2c6124ac7cae7d865ecec74eec63783e8922577ac232583" +dependencies = [ + "bitflags 2.13.1", + "cursor-icon", + "dpi", + "keyboard-types 0.8.3", + "raw-window-handle", + "smol_str", + "web-time", +] + +[[package]] +name = "winit-orbital" +version = "0.31.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51ea1fb262e7209f265f12bd0cc792c399b14355675e65531e9c8a87db287d46" +dependencies = [ + "bitflags 2.13.1", + "dpi", + "orbclient", + "raw-window-handle", + "redox_syscall 0.5.18", + "smol_str", + "tracing", + "winit-core", +] + +[[package]] +name = "winit-uikit" +version = "0.31.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680a356e798837d8eb274d4556e83bceaf81698194e31aafc5cfb8a9f2fab643" +dependencies = [ + "bitflags 2.13.1", + "block2", + "dispatch2", + "dpi", + "objc2", + "objc2-core-foundation", + "objc2-foundation", + "objc2-ui-kit", + "raw-window-handle", + "smol_str", + "tracing", + "winit-common", + "winit-core", +] + +[[package]] +name = "winit-wayland" +version = "0.31.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce5afb2ba07da603f84b722c95f9f9396d2cedae3944fb6c0cda4a6f88de545" +dependencies = [ + "ahash", + "bitflags 2.13.1", + "calloop", + "cursor-icon", + "dpi", + "libc", + "memmap2", + "raw-window-handle", + "rustix", + "sctk-adwaita", + "smithay-client-toolkit", + "smol_str", + "tracing", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-plasma", + "winit-common", + "winit-core", +] + +[[package]] +name = "winit-web" +version = "0.31.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c2490a953fb776fbbd5e295d54f1c3847f4f15b6c3929ec53c09acda6487a92" +dependencies = [ + "atomic-waker", + "bitflags 2.13.1", + "concurrent-queue", + "cursor-icon", + "dpi", + "js-sys", + "pin-project", + "raw-window-handle", + "smol_str", + "tracing", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "web-time", + "winit-core", +] + +[[package]] +name = "winit-win32" +version = "0.31.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "644ea78af0e858aa3b092e5d1c67c41995a98220c81813f1353b28bc8bb91eaa" +dependencies = [ + "bitflags 2.13.1", + "cursor-icon", + "dpi", + "raw-window-handle", + "smol_str", + "tracing", + "unicode-segmentation", + "windows-sys 0.59.0", + "winit-core", +] + +[[package]] +name = "winit-x11" +version = "0.31.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa5b600756534c7041aa93cd0d244d44b09fca1b89e202bd1cd80dd9f3636c46" +dependencies = [ + "bitflags 2.13.1", + "bytemuck", + "calloop", + "cursor-icon", + "dpi", + "libc", + "percent-encoding", + "raw-window-handle", + "rustix", + "smol_str", + "tracing", + "winit-common", + "winit-core", + "x11-dl", + "x11rb", + "xkbcommon-dl", +] + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" + +[[package]] +name = "winnow" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.55.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" +dependencies = [ + "cfg-if", + "windows-sys 0.59.0", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wl-clipboard-rs" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9651471a32e87d96ef3a127715382b2d11cc7c8bb9822ded8a7cc94072eb0a3" +dependencies = [ + "libc", + "log", + "os_pipe", + "rustix", + "thiserror 2.0.20", + "tree_magic_mini", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-wlr", +] + +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "wry" +version = "0.55.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186f9871daa55fd9c016578b810d149de58367113db7fb72b462d2323ce19514" +dependencies = [ + "base64 0.22.1", + "block2", + "cookie", + "crossbeam-channel", + "dirs", + "dom_query", + "dpi", + "dunce", + "gdkx11", + "gtk", + "http 1.5.0", + "javascriptcore-rs", + "jni 0.21.1", + "libc", + "ndk", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "objc2-ui-kit", + "objc2-web-kit", + "once_cell", + "percent-encoding", + "raw-window-handle", + "sha2 0.10.9", + "soup3", + "tao-macros", + "thiserror 2.0.20", + "tracing", + "url", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows 0.61.3", + "windows-core 0.61.2", + "windows-version", + "x11-dl", +] + +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "x11rb" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" +dependencies = [ + "as-raw-xcb-connection", + "gethostname", + "libc", + "libloading 0.8.9", + "once_cell", + "rustix", + "x11rb-protocol", + "xcursor", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" + +[[package]] +name = "xattr" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" +dependencies = [ + "libc", + "rustix", +] + +[[package]] +name = "xcursor" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "163b33ed8786455e2fa5d72f554057ce3f3182425434f756cd39c99839d88e23" + +[[package]] +name = "xkbcommon" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a974f48060a14e95705c01f24ad9c3345022f4d97441b8a36beb7ed5c4a02d" +dependencies = [ + "libc", + "memmap2", + "xkeysym", +] + +[[package]] +name = "xkbcommon-dl" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" +dependencies = [ + "bitflags 2.13.1", + "dlib", + "log", + "once_cell", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" + +[[package]] +name = "yoke" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", + "synstructure", +] + +[[package]] +name = "zbus" +version = "5.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5db4be7c075cb421e4b7ee645541604239bd243ba7c357511f4ff3a74b555907" +dependencies = [ + "async-broadcast", + "async-executor", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-lite", + "hex", + "libc", + "ordered-stream", + "rustix", + "serde", + "serde_repr", + "tokio", + "tracing", + "uds_windows", + "uuid", + "windows-sys 0.61.2", + "winnow 1.0.4", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "5.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2990635d09ade6df1868f72f8cac69a876a90981e8bd3c40b1be413f8dc88f40" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 3.0.3", + "zbus_names", + "zvariant", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "4.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8bf88b4a3ff53e883001e0e0115b297a9d53c31b9c1edd2bfdd853e3428624e" +dependencies = [ + "serde", + "winnow 1.0.4", + "zvariant", +] + +[[package]] +name = "zcheapstr" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1afec51604565183aeb5c54c20aeab286120d4e4460f7f76e3e8bb8c0d99473" +dependencies = [ + "serde", +] + +[[package]] +name = "zerocopy" +version = "0.8.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "zip" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1" +dependencies = [ + "arbitrary", + "crc32fast", + "indexmap 2.14.0", + "memchr", +] + +[[package]] +name = "zip" +version = "8.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d04a6b5381502aa6087c94c669499eb1602eb9c5e8198e534de571f7154809b" +dependencies = [ + "crc32fast", + "flate2", + "indexmap 2.14.0", + "memchr", + "typed-path", +] + +[[package]] +name = "zlib-rs" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b31d188d9d685a4f9c7b46d6e36631b07058d2cfe190267adce54dc230bf12" + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" + +[[package]] +name = "zune-core" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56377fd46368984a170bc5aac5567e52ca5da874caa60bea39fcbca78fb658b" + +[[package]] +name = "zune-jpeg" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" +dependencies = [ + "zune-core", +] + +[[package]] +name = "zvariant" +version = "5.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e28c25bd8bb8da5a1f3e7065d0c156b9ee9a7973adf78b0e35eaefdf3b1b5c" +dependencies = [ + "endi", + "enumflags2", + "serde", + "winnow 1.0.4", + "zcheapstr", + "zvariant_derive", + "zvariant_utils", +] + +[[package]] +name = "zvariant_derive" +version = "5.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d496a145685283b67e232bd9e47377f6b60ad9d51e3601b23867f77c42477f96" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 3.0.3", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "629d80ece222cad20fe0e8741be493c4ab166acf3b85341bdc2cdbcfd8f3c2d6" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn 3.0.3", + "winnow 1.0.4", +] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml new file mode 100644 index 0000000000..0a3d180734 --- /dev/null +++ b/src-tauri/Cargo.toml @@ -0,0 +1,222 @@ +[package] +name = "sable" +version = "1.21.0" +description = "Yet another matrix client but better" +authors = ["Sable Maintainers"] +license = "AGPL-3.0-or-later" +repository = "https://github.com/SableClient/Sable" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +name = "app_lib" +crate-type = ["staticlib", "cdylib", "rlib"] + +[build-dependencies] +serde_json = "1.0" +tauri-build = { version = "2.6.3", features = [] } +tauri-typegen = "0.5" + +[dependencies] +serde_json = "1.0" +serde = { version = "1.0", features = ["derive"] } +log = "0.4" +# default-features = false drops reqwest + native-tls (openssl); ureq is a sync +# rustls transport with no async-runtime dependency. +sentry = { version = "0.49.0", default-features = false, features = [ + "backtrace", + "contexts", + "panic", + "debug-images", + "ureq", +] } +tokio = { version = "1", features = ["time", "sync", "fs", "io-util"] } +ts-rs = "12.0.0" +sha2 = "0.11" +aes = "0.9" +ctr = "0.10" +infer = { version = "0.22", default-features = false } +percent-encoding = "2" +# Enables `stream` (for native_upload's `Body::wrap_stream`) on the reqwest that +# tauri-plugin-http pulls in without it. default-features = false keeps +# openssl-sys out of the Android cross-compile. +reqwest = { version = "0.12", default-features = false, features = ["stream"] } +async-stream = "0.3" +base64 = "0.23" +tokio-util = { version = "0.7", features = ["codec"] } +futures-util = "0.3" +regex = "1" +zip = { version = "=8.6.0", default-features = false, features = ["deflate-flate2-zlib-rs"] } + +tauri = { version = "2.11.5", default-features = false, features = [ + "tray-icon", + "dynamic-acl", + "compression", + "x11", + "common-controls-v6", + "dbus", + "image-png", +] } + +tauri-plugin-log = "2.9.0" +tauri-plugin-opener = "2.5.4" +tauri-plugin-os = "2" +tauri-plugin-deep-link = "2.4.9" +tauri-plugin-http = "2.5.9" +tauri-plugin-store = "2.4.4" +tauri-plugin-clipboard-manager = "2.3.2" +tauri-plugin-dialog = "2" + +# Debug-only in practice: only initialized under #[cfg(debug_assertions)] in +# lib.rs, so the release build never calls into it. +# default-features = false drops the plugin's `wry` feature (which enables +# `tauri/wry`); Sable's own `wry` feature already enables `tauri/wry` for wry +# builds, and CEF builds (--no-default-features --features cef) must not pull +# wry/tauri-runtime-wry/webkit2gtk. +# Pinned to =2.0.0 because 2.1.0+ introduced an unconditional call to +# tauri::webview_version() which is gated behind tauri's `wry` feature. +tauri-plugin-devtools = { version = "=2.1.0", default-features = false, optional = true } + +[target.'cfg(target_os = "windows")'.dependencies] +enigo = "0.6.1" +windows = { version = "0.62", features = [ + "Win32_Foundation", + "Win32_UI_WindowsAndMessaging", + "Win32_System_Threading", + "Win32_System_ProcessStatus", +] } + +[target.'cfg(any(target_os = "macos", windows, target_os = "linux"))'.dependencies] +tauri-plugin-single-instance = { version = "2.4.3", features = ["deep-link"] } + +[target.'cfg(any(windows, target_os = "linux"))'.dependencies] +tauri-plugin-notifications = { git = "https://github.com/SableClient/tauri-plugin-notifications.git", rev = "301e4ac43970525764eb2f0c787884129db2847f" } + +# default-features = false drops notify-rust so macOS uses the native +# UNUserNotificationCenter backend (needs a signed .app to deliver). +[target.'cfg(target_os = "macos")'.dependencies] +tauri-plugin-notifications = { git = "https://github.com/SableClient/tauri-plugin-notifications.git", rev = "301e4ac43970525764eb2f0c787884129db2847f", default-features = false } + +[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] +tauri-plugin-updater = { version = "2", optional = true } +tauri-plugin-process = "2" +tauri-plugin-window-state = "2.4.1" +tauri-plugin-global-shortcut = "2" + +[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))'.dependencies] +gtk = "0.18" +# Used directly in lib.rs for WebKitGTK permission handling (wry only). +# Optional + gated via the `wry` feature so CEF builds don't pull it. +webkit2gtk = { version = "2.0", optional = true, features = ["v2_34"] } + +# CEF (Chromium) runtime — Linux only; other platforms use wry. +# Fork of tauri's unreleased feat/cef branch, ported onto published crates. +# https://github.com/SableClient/tauri-runtime-cef +[target.'cfg(target_os = "linux")'.dependencies] +tauri-runtime-cef = { git = "https://github.com/SableClient/tauri-runtime-cef", rev = "6fb4fabcc759c1f5407def192540ce149f666fe8", optional = true } +cef = { version = "=150.2.1", optional = true } +libloading = "0.9" +zbus = "5" + +[target.'cfg(any(target_os = "android", target_os = "ios"))'.dependencies] +tauri-plugin-notifications = { git = "https://github.com/SableClient/tauri-plugin-notifications.git", rev = "301e4ac43970525764eb2f0c787884129db2847f", features = [ + "push-notifications", +] } +tauri-plugin-edge-to-edge = { git = "https://github.com/SableClient/tauri-plugin-edge-to-edge.git", rev = "33c6116c27be28c06df5a9d02231ecc5fdeb93c5" } +tauri-plugin-sharekit = { git = "https://github.com/Choochmeque/tauri-plugin-sharekit", rev = "9f2b4c5d8a4f0ab910d900ba234ed8bae0ab854b" } +tauri-plugin-app-icon = { git = "https://github.com/SableClient/tauri-plugin-app-icon", rev = "bab80ae17f6d018702e472c539d0f02eba480c21" } +tauri-plugin-livekit-mobile = { git = "https://github.com/SableClient/tauri-plugin-livekit-mobile.git", rev = "c6f0d02ab3da1174535bc1dc8e598a2e6873f977" } + +[target.'cfg(target_os = "ios")'.dependencies] +objc2 = "0.6" +objc2-foundation = { version = "0.3", features = [ + "NSError", + "NSFileManager", + "NSString", + "NSURL", + "NSUUID", +] } +objc2-photos = { version = "0.3.2", default-features = false, features = [ + "std", + "PHPhotoLibrary", + "PHChangeRequest", + "PHAssetChangeRequest", + "PHAssetCreationRequest", + "PhotosTypes", + "bitflags", + "block2", + "dispatch2", +] } +block2 = "0.6" +tauri-plugin-fs = "2" +objc2-avf-audio = { version = "0.3", default-features = false, features = [ + "AVAudioSession", + "AVAudioSessionTypes", +] } +objc2-call-kit = { version = "0.3", default-features = false, features = [ + "std", + "alloc", + "block2", + "dispatch2", + "CXAction", + "CXProvider", + "CXProviderConfiguration", + "CXCallUpdate", + "CXHandle", + "CXCall", + "CXCallAction", + "CXStartCallAction", + "CXEndCallAction", + "CXSetMutedCallAction", + "CXTransaction", + "CXCallController", +] } + +[target.'cfg(target_os = "android")'.dependencies] +tauri-plugin-android-fs = { version = "=29.0.0", features = ["legacy-storage-permission"] } +jni = "0.22" + +[features] +default = ["wry", "updater"] +custom-protocol = ["tauri/custom-protocol"] +wry = ["tauri/wry", "dep:webkit2gtk"] +# Tauri auto-updater. Disable with --no-default-features --features wry,cef. +updater = ["dep:tauri-plugin-updater"] +# CEF (Chromium) runtime. Build with --no-default-features --features cef. +cef = ["dep:tauri-runtime-cef", "dep:cef"] +# CrabNebula devtools. Off by default: its aggregator retains every metadata record for the +# session, which costs hundreds of MB per minute once tauri-plugin-log feeds it. +devtools = ["dep:tauri-plugin-devtools"] +[patch.crates-io] +tauri-typegen = { git = "https://github.com/SableClient/tauri-typegen", branch = "fix/nondeterministic-generation-cache" } + +# To test unreleased tauri features +# tauri = { git = "https://github.com/tauri-apps/tauri", branch = "dev" } +# tauri-build = { git = "https://github.com/tauri-apps/tauri", branch = "dev" } +# tauri-plugin = { git = "https://github.com/tauri-apps/tauri", branch = "dev" } +# tauri-utils = { git = "https://github.com/tauri-apps/tauri", branch = "dev" } +# tauri-macros = { git = "https://github.com/tauri-apps/tauri", branch = "dev" } +# tauri-runtime = { git = "https://github.com/tauri-apps/tauri", branch = "dev" } +# tauri-runtime-wry = { git = "https://github.com/tauri-apps/tauri", branch = "dev" } + +# To test the CEF runtime path, switch this back to `cef = ["tauri/cef"]` +# and uncomment the patch scaffold below. +# cef = ["tauri/cef"] +# +# [patch.crates-io] +# tauri = { git = "https://github.com/tauri-apps/tauri", branch = "feat/cef" } +# tauri-build = { git = "https://github.com/tauri-apps/tauri", branch = "feat/cef" } +# tauri-plugin = { git = "https://github.com/tauri-apps/tauri", branch = "feat/cef" } +# tauri-utils = { git = "https://github.com/tauri-apps/tauri", branch = "feat/cef" } +# tauri-macros = { git = "https://github.com/tauri-apps/tauri", branch = "feat/cef" } +# tauri-runtime = { git = "https://github.com/tauri-apps/tauri", branch = "feat/cef" } +# tauri-runtime-wry = { git = "https://github.com/tauri-apps/tauri", branch = "feat/cef" } +# tauri-plugin-log = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "feat/cef" } +# tauri-plugin-opener = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "feat/cef" } +# tauri-plugin-os = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "feat/cef" } +# tauri-plugin-deep-link = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "feat/cef" } +# tauri-plugin-http = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "feat/cef" } +# tauri-plugin-store = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "feat/cef" } +# tauri-plugin-single-instance = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "feat/cef" } +# tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "feat/cef" } diff --git a/src-tauri/Info.plist b/src-tauri/Info.plist new file mode 100644 index 0000000000..0593c316af --- /dev/null +++ b/src-tauri/Info.plist @@ -0,0 +1,42 @@ + + + + + CFBundleDisplayName + Sable + CFBundleName + Sable + NSCameraUsageDescription + Sable needs camera access for video calls. + NSMicrophoneUsageDescription + Sable needs microphone access for voice and video calls. + + TauriAppIconAliases + + propeller + AppIconPropeller + agender + AppIconAgender + bisexual + AppIconBisexual + trans + AppIconTrans + transgradient + AppIconTransgradient + intersex + AppIconIntersex + lesbian + AppIconLesbian + mlm + AppIconMlm + pride + AppIconPride + + + UIBackgroundModes + + audio + voip + + + diff --git a/src-tauri/app-icons.json b/src-tauri/app-icons.json new file mode 100644 index 0000000000..3dbff731ee --- /dev/null +++ b/src-tauri/app-icons.json @@ -0,0 +1,30 @@ +{ + "android": { + "mainActivity": ".MainActivity", + "resourcesDirectory": "icons/android", + "manifest": "gen/android/app/src/main/AndroidManifest.xml" + }, + "ios": { + "assetsDirectory": "icons/ios/AppIcons.xcassets", + "infoPlist": "Info.plist" + }, + "icons": [ + { + "id": "propeller", + "source": "icons/app-icons/propeller.png", + "backgroundColor": "#1A1C28" + }, + { "id": "agender", "source": "icons/app-icons/agender.svg", "backgroundColor": "#1A1C28" }, + { "id": "bisexual", "source": "icons/app-icons/bisexual.svg", "backgroundColor": "#1A1C28" }, + { "id": "trans", "source": "icons/app-icons/trans.png", "backgroundColor": "#1A1C28" }, + { + "id": "transgradient", + "source": "icons/app-icons/trans-gradient.svg", + "backgroundColor": "#1A1C28" + }, + { "id": "intersex", "source": "icons/app-icons/intersex.svg", "backgroundColor": "#1A1C28" }, + { "id": "lesbian", "source": "icons/app-icons/lesbian.svg", "backgroundColor": "#1A1C28" }, + { "id": "mlm", "source": "icons/app-icons/mlm.svg", "backgroundColor": "#1A1C28" }, + { "id": "pride", "source": "icons/app-icons/pride.svg", "backgroundColor": "#1A1C28" } + ] +} diff --git a/src-tauri/build.rs b/src-tauri/build.rs new file mode 100644 index 0000000000..d4624f92b6 --- /dev/null +++ b/src-tauri/build.rs @@ -0,0 +1,74 @@ +fn main() { + println!("cargo:rustc-env=TS_RS_EXPORT_DIR=../src/app/generated/tauri"); + + if let Ok(dsn) = std::env::var("VITE_SENTRY_DSN") { + println!("cargo:rustc-env=SENTRY_DSN={dsn}"); + } + if let Ok(env) = std::env::var("VITE_SENTRY_ENVIRONMENT") { + println!("cargo:rustc-env=SENTRY_ENVIRONMENT={env}"); + } + if let Ok(version) = std::env::var("VITE_APP_VERSION") { + println!("cargo:rustc-env=SENTRY_APP_VERSION={version}"); + } + println!("cargo:rerun-if-env-changed=VITE_SENTRY_DSN"); + println!("cargo:rerun-if-env-changed=VITE_SENTRY_ENVIRONMENT"); + println!("cargo:rerun-if-env-changed=VITE_APP_VERSION"); + + let config_path = std::path::Path::new("../config.json"); + let base = std::fs::read_to_string(config_path) + .ok() + .and_then(|content| serde_json::from_str::(&content).ok()) + .and_then(|config| { + config + .get("productName") + .and_then(|v| v.as_str()) + .map(String::from) + }) + .unwrap_or_else(|| "Sable".into()); + + let full_name = if matches!( + std::env::var("SABLE_BUILD_FLAVOR").ok().as_deref(), + Some("dev" | "nightly") + ) { + format!("{} Nightly", base) + } else { + base.clone() + }; + + println!("cargo:rustc-env=SABLE_PRODUCT_NAME={}", base); + println!( + "cargo:rustc-env=TAURI_CONFIG={{\"productName\":\"{}\"}}", + full_name + ); + println!("cargo:rerun-if-env-changed=SABLE_BUILD_FLAVOR"); + // Find libcef.so next to the binary (CEF ships it there). + if std::env::var_os("CARGO_FEATURE_CEF").is_some() + && std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("linux") + { + println!("cargo:rustc-link-arg-bins=-Wl,-rpath,$ORIGIN"); + } + + // The notifications plugin links the Swift runtime via @rpath. + if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("macos") { + println!("cargo:rustc-link-arg-bins=-Wl,-rpath,/usr/lib/swift"); + } + + // Use the NDK's lld linker for faster Android linking. + if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("android") { + println!("cargo:rustc-link-arg=-fuse-ld=lld"); + } + + tauri_typegen::BuildSystem::generate_at_build_time() + .expect("Failed to generate TypeScript bindings"); + + // tauri-build fails on permissions from missing plugins, so only glob the + // updater capability file when the `updater` feature is on. + #[cfg(feature = "updater")] + let tauri_attrs = + tauri_build::Attributes::new().capabilities_path_pattern("./capabilities/**/*.json"); + #[cfg(not(feature = "updater"))] + let tauri_attrs = + tauri_build::Attributes::new().capabilities_path_pattern("./capabilities/*.json"); + + tauri_build::try_build(tauri_attrs).expect("tauri-build failed"); +} diff --git a/src-tauri/capabilities/android.json b/src-tauri/capabilities/android.json new file mode 100644 index 0000000000..82fbd4e44a --- /dev/null +++ b/src-tauri/capabilities/android.json @@ -0,0 +1,19 @@ +{ + "$schema": "../gen/schemas/mobile-schema.json", + "identifier": "android-capability", + "platforms": ["android"], + "windows": ["main"], + "permissions": [ + "android-fs:allow-show-open-file-picker", + "android-fs:allow-get-metadata", + "android-fs:allow-read-file", + "android-fs:allow-check-public-files-permission", + "android-fs:allow-create-new-public-file", + "android-fs:allow-create-new-public-image-file", + "android-fs:allow-request-public-files-permission", + "android-fs:allow-write-file", + "android-fs:allow-set-public-file-pending", + "android-fs:allow-scan-public-file", + "android-fs:allow-remove-file" + ] +} diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json new file mode 100644 index 0000000000..9307034c80 --- /dev/null +++ b/src-tauri/capabilities/default.json @@ -0,0 +1,45 @@ +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "default", + "description": "enables the default permissions", + "windows": ["main"], + "permissions": [ + "core:default", + "core:window:allow-close", + "core:window:allow-minimize", + "core:window:allow-maximize", + "core:window:allow-set-focus", + "core:window:allow-show", + "core:window:allow-is-maximized", + "core:window:allow-toggle-maximize", + "core:window:allow-start-dragging", + "deep-link:default", + "os:default", + "os:allow-platform", + "os:allow-version", + "os:allow-os-type", + "os:allow-family", + "os:allow-arch", + "os:allow-exe-extension", + "os:allow-locale", + "os:allow-hostname", + "opener:default", + "opener:allow-default-urls", + "clipboard-manager:allow-write-text", + "clipboard-manager:allow-read-text", + "clipboard-manager:allow-write-image", + { + "identifier": "http:default", + "allow": [{ "url": "https://*:*" }] + }, + { + "identifier": "opener:allow-open-url", + "allow": [ + { + "url": "https://*", + "app": "inAppBrowser" + } + ] + } + ] +} diff --git a/src-tauri/capabilities/desktop.json b/src-tauri/capabilities/desktop.json new file mode 100644 index 0000000000..f42f6888ea --- /dev/null +++ b/src-tauri/capabilities/desktop.json @@ -0,0 +1,14 @@ +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "desktop-capability", + "platforms": ["macOS", "windows", "linux"], + "windows": ["main"], + "permissions": [ + "core:window:allow-set-badge-count", + "window-state:default", + "store:default", + "notifications:default", + "process:default", + "dialog:default" + ] +} diff --git a/src-tauri/capabilities/ios.json b/src-tauri/capabilities/ios.json new file mode 100644 index 0000000000..823ad5a7d5 --- /dev/null +++ b/src-tauri/capabilities/ios.json @@ -0,0 +1,19 @@ +{ + "$schema": "../gen/schemas/mobile-schema.json", + "identifier": "ios-capability", + "platforms": ["iOS"], + "windows": ["main"], + "permissions": [ + "dialog:allow-save", + "dialog:allow-open", + "fs:allow-write-file", + { + "identifier": "fs:allow-read-file", + "allow": [{ "path": "$TEMP/**/*" }, { "path": "$APPCACHE/**/*" }] + }, + { + "identifier": "fs:allow-remove", + "allow": [{ "path": "$TEMP/**/*" }, { "path": "$APPCACHE/**/*" }] + } + ] +} diff --git a/src-tauri/capabilities/mobile.json b/src-tauri/capabilities/mobile.json new file mode 100644 index 0000000000..ced96b9c24 --- /dev/null +++ b/src-tauri/capabilities/mobile.json @@ -0,0 +1,15 @@ +{ + "$schema": "../gen/schemas/mobile-schema.json", + "identifier": "mobile-capability", + "platforms": ["android", "iOS"], + "windows": ["main"], + "permissions": [ + "core:window:allow-set-badge-count", + "app-icon:default", + "deep-link:default", + "edge-to-edge:default", + "livekit-mobile:default", + "notifications:default", + "sharekit:default" + ] +} diff --git a/src-tauri/capabilities/updater/updater.json b/src-tauri/capabilities/updater/updater.json new file mode 100644 index 0000000000..9a92dd18c4 --- /dev/null +++ b/src-tauri/capabilities/updater/updater.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../gen/schemas/desktop-schema.json", + "identifier": "updater-capability", + "platforms": ["macOS", "windows", "linux"], + "windows": ["main"], + "permissions": ["updater:default"] +} diff --git a/src-tauri/gen/android/.editorconfig b/src-tauri/gen/android/.editorconfig new file mode 100644 index 0000000000..ebe51d3bfa --- /dev/null +++ b/src-tauri/gen/android/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = false \ No newline at end of file diff --git a/src-tauri/gen/android/.gitignore b/src-tauri/gen/android/.gitignore new file mode 100644 index 0000000000..026c5af237 --- /dev/null +++ b/src-tauri/gen/android/.gitignore @@ -0,0 +1,21 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +.kotlin/ +build +/captures +.externalNativeBuild +.cxx +local.properties +key.properties +keystore.properties + +/.tauri +/tauri.settings.gradle \ No newline at end of file diff --git a/src-tauri/gen/android/app/.gitignore b/src-tauri/gen/android/app/.gitignore new file mode 100644 index 0000000000..6c4d56b4a2 --- /dev/null +++ b/src-tauri/gen/android/app/.gitignore @@ -0,0 +1,6 @@ +/src/main/**/generated +/src/main/jniLibs/**/*.so +/src/main/assets/tauri.conf.json +/tauri.build.gradle.kts +/proguard-tauri.pro +/tauri.properties \ No newline at end of file diff --git a/src-tauri/gen/android/app/build.gradle.kts b/src-tauri/gen/android/app/build.gradle.kts new file mode 100644 index 0000000000..90abfc67a0 --- /dev/null +++ b/src-tauri/gen/android/app/build.gradle.kts @@ -0,0 +1,110 @@ +import java.util.Properties +import java.io.FileInputStream + +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") + id("rust") +} + +val tauriProperties = Properties().apply { + val propFile = file("tauri.properties") + if (propFile.exists()) { + propFile.inputStream().use { load(it) } + } +} + +android { + compileSdk = 36 + namespace = "moe.sable.client" + defaultConfig { + manifestPlaceholders["usesCleartextTraffic"] = "false" + applicationId = "moe.sable.client" + minSdk = 24 + targetSdk = 36 + versionCode = tauriProperties.getProperty("tauri.android.versionCode", "1").toInt() + versionName = tauriProperties.getProperty("tauri.android.versionName", "1.0") + } + signingConfigs { + create("release") { + val keystorePropertiesFile = rootProject.file("keystore.properties") + if (keystorePropertiesFile.exists()) { + val keystoreProperties = Properties() + keystoreProperties.load(FileInputStream(keystorePropertiesFile)) + + keyAlias = keystoreProperties["keyAlias"] as String + keyPassword = keystoreProperties["password"] as String + storeFile = file(keystoreProperties["storeFile"] as String) + storePassword = keystoreProperties["password"] as String + } + } + } + buildTypes { + getByName("debug") { + manifestPlaceholders["usesCleartextTraffic"] = "true" + isDebuggable = true + isJniDebuggable = true + isMinifyEnabled = false + packaging { jniLibs.keepDebugSymbols.add("*/arm64-v8a/*.so") + jniLibs.keepDebugSymbols.add("*/armeabi-v7a/*.so") + jniLibs.keepDebugSymbols.add("*/x86/*.so") + jniLibs.keepDebugSymbols.add("*/x86_64/*.so") + } + } + getByName("release") { + signingConfig = signingConfigs.getByName("release") + isMinifyEnabled = true + proguardFiles( + *fileTree(".") { include("**/*.pro") } + .plus(getDefaultProguardFile("proguard-android-optimize.txt")) + .toList().toTypedArray() + ) + } + } + compileOptions { + // tauri-plugin-livekit-mobile requires it: LiveKit/WebRTC call java.time + // APIs that only exist from API 26, and minSdk here is 24. + isCoreLibraryDesugaringEnabled = true + } + kotlinOptions { + jvmTarget = "1.8" + } + buildFeatures { + buildConfig = true + } +} + +rust { + rootDirRel = "../../../" +} + +// tink (via the unifiedpush connector) pulls protobuf-java, which collides with +// the protobuf-javalite livekit-android is generated against: its Timestamp +// extends GeneratedMessage rather than GeneratedMessageLite, so livekit's +// classes fail dex verification at runtime. tink-android shades its own +// protobuf and needs none, which leaves javalite as the only protobuf. +configurations.all { + resolutionStrategy.dependencySubstitution { + substitute(module("com.google.crypto.tink:tink")) + .using(module("com.google.crypto.tink:tink-android:1.18.0")) + } +} + +dependencies { + implementation("androidx.webkit:webkit:1.14.0") + implementation("androidx.appcompat:appcompat:1.7.1") + implementation("androidx.activity:activity-ktx:1.10.1") + implementation("com.google.android.material:material:1.12.0") + coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5") + testImplementation("junit:junit:4.13.2") + androidTestImplementation("androidx.test.ext:junit:1.1.4") + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0") +} + +apply(from = "tauri.build.gradle.kts") + +// Native FCM push (Sygnal): applies only once google-services.json is added to this +// directory, so builds without Firebase configured still succeed. +if (file("google-services.json").exists()) { + apply(plugin = "com.google.gms.google-services") +} diff --git a/src-tauri/gen/android/app/proguard-rules.pro b/src-tauri/gen/android/app/proguard-rules.pro new file mode 100644 index 0000000000..b63990750b --- /dev/null +++ b/src-tauri/gen/android/app/proguard-rules.pro @@ -0,0 +1,56 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile + +# Keep Android bridge symbols used by Wry/Tauri JNI on release builds. +# Without these, R8 can rename/remove methods like WryActivity.getId(), +# which tao resolves by method name via JNI. +-keep class moe.sable.client.* { + native ; +} + +-keep class moe.sable.client.WryActivity { + public (...); + + void setWebView(moe.sable.client.RustWebView); + java.lang.Class getAppClass(...); + java.lang.String getVersion(); + int startActivity(...); + int getId(); +} + +-keep class moe.sable.client.Ipc { + public (...); + + @android.webkit.JavascriptInterface public ; +} + +-keep class moe.sable.client.RustWebView { + public (...); + + void loadUrlMainThread(...); + void loadHTMLMainThread(...); + void evalScript(...); +} + +-keep class moe.sable.client.RustWebChromeClient,moe.sable.client.RustWebViewClient { + public (...); +} \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/AndroidManifest.xml b/src-tauri/gen/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..ef0e729572 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,312 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src-tauri/gen/android/app/src/main/java/moe/sable/client/CallForegroundService.kt b/src-tauri/gen/android/app/src/main/java/moe/sable/client/CallForegroundService.kt new file mode 100644 index 0000000000..86f53694d9 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/java/moe/sable/client/CallForegroundService.kt @@ -0,0 +1,120 @@ +package moe.sable.client + +import android.Manifest +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.Service +import android.content.pm.PackageManager +import android.content.pm.ServiceInfo +import android.media.AudioAttributes +import android.media.AudioFocusRequest +import android.media.AudioManager +import android.os.Build +import android.os.IBinder +import android.os.PowerManager +import android.util.Log +import androidx.core.app.NotificationCompat +import androidx.core.app.ServiceCompat + +class CallForegroundService : Service() { + private var wakeLock: PowerManager.WakeLock? = null + private var audioFocusRequest: AudioFocusRequest? = null + + override fun onBind(intent: android.content.Intent?): IBinder? = null + + override fun onStartCommand(intent: android.content.Intent?, flags: Int, startId: Int): Int { + if (checkSelfPermission(Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { + Log.w(TAG, "Microphone permission is not granted; stopping foreground service") + stopSelf(startId) + return START_NOT_STICKY + } + + try { + createNotificationChannel() + ServiceCompat.startForeground( + this, + NOTIFICATION_ID, + NotificationCompat.Builder(this, CHANNEL_ID) + .setSmallIcon(R.mipmap.ic_notification) + .setContentTitle("Call in progress") + .setContentText("Sable is using your microphone") + .setCategory(NotificationCompat.CATEGORY_CALL) + .setOngoing(true) + .setOnlyAlertOnce(true) + .build(), + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE + } else { + 0 + } + ) + } catch (error: Exception) { + Log.e(TAG, "Failed to start microphone foreground service", error) + stopSelf(startId) + return START_NOT_STICKY + } + + acquireWakeLock() + requestAudioFocus() + + return START_NOT_STICKY + } + + override fun onDestroy() { + releaseAudioFocus() + releaseWakeLock() + stopForeground(STOP_FOREGROUND_REMOVE) + super.onDestroy() + } + + private fun acquireWakeLock() { + if (wakeLock?.isHeld == true) return + val pm = getSystemService(POWER_SERVICE) as PowerManager + wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Sable:CallForeground") + wakeLock?.acquire(WAKE_LOCK_TIMEOUT_MS) + } + + private fun releaseWakeLock() { + wakeLock?.let { if (it.isHeld) it.release() } + wakeLock = null + } + + private fun requestAudioFocus() { + val am = getSystemService(AUDIO_SERVICE) as AudioManager + val attributes = AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) + .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) + .build() + audioFocusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT) + .setAudioAttributes(attributes) + .setOnAudioFocusChangeListener { _ -> } + .build() + am.requestAudioFocus(audioFocusRequest!!) + } + + private fun releaseAudioFocus() { + audioFocusRequest?.let { + val am = getSystemService(AUDIO_SERVICE) as AudioManager + am.abandonAudioFocusRequest(it) + } + audioFocusRequest = null + } + + private fun createNotificationChannel() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return + + val channel = NotificationChannel( + CHANNEL_ID, + "Calls", + NotificationManager.IMPORTANCE_LOW + ) + getSystemService(NotificationManager::class.java).createNotificationChannel(channel) + } + + private companion object { + const val CHANNEL_ID = "call_foreground" + const val NOTIFICATION_ID = 1395 + const val TAG = "CallForegroundService" + const val WAKE_LOCK_TIMEOUT_MS = 60L * 60L * 1000L + } +} diff --git a/src-tauri/gen/android/app/src/main/java/moe/sable/client/MainActivity.kt b/src-tauri/gen/android/app/src/main/java/moe/sable/client/MainActivity.kt new file mode 100644 index 0000000000..0d635fc3b1 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/java/moe/sable/client/MainActivity.kt @@ -0,0 +1,261 @@ +package moe.sable.client + +import android.Manifest +import android.content.Intent +import android.content.pm.PackageManager +import android.graphics.Color +import android.media.AudioAttributes +import android.media.MediaPlayer +import android.net.Uri +import android.os.Bundle +import android.provider.OpenableColumns +import android.webkit.WebView +import androidx.activity.OnBackPressedCallback +import androidx.activity.enableEdgeToEdge +import androidx.core.content.ContextCompat +import androidx.core.content.IntentCompat +import androidx.core.view.WindowCompat +import androidx.core.view.WindowInsetsCompat +import java.io.File +import java.io.FileOutputStream +import java.util.UUID +import org.json.JSONArray +import org.json.JSONObject + +class MainActivity : TauriActivity() { + private external fun nativeInitStatusBar() + private external fun nativeShareReceived() + + // Route the hardware back button through the web app (see onWebViewCreate). + override val handleBackNavigation: Boolean = false + private var webView: WebView? = null + private var handlingBack = false + + override fun onCreate(savedInstanceState: Bundle?) { + enableEdgeToEdge() + super.onCreate(savedInstanceState) + instance = this + runCatching { nativeInitStatusBar() } + stageShareIntent(intent) + } + + override fun onNewIntent(intent: Intent) { + super.onNewIntent(intent) + stageShareIntent(intent) + } + + private fun stageShareIntent(intent: Intent?) { + val action = intent?.action ?: return + if (action != Intent.ACTION_SEND && action != Intent.ACTION_SEND_MULTIPLE) return + require(intent != null) + + val batchDir = File(File(dataDir, "share_inbox"), "${System.currentTimeMillis()}-${UUID.randomUUID()}") + batchDir.mkdirs() + val items = JSONArray() + + when (action) { + Intent.ACTION_SEND -> { + when (intent.type) { + "text/plain" -> intent.getStringExtra(Intent.EXTRA_TEXT)?.let { addTextItem(items, it) } + else -> { + IntentCompat.getParcelableExtra(intent, Intent.EXTRA_STREAM, Uri::class.java) + ?.let { stageFile(it, 0, batchDir, items) } + intent.getStringExtra(Intent.EXTRA_TEXT)?.let { addTextItem(items, it) } + } + } + } + Intent.ACTION_SEND_MULTIPLE -> { + IntentCompat.getParcelableArrayListExtra(intent, Intent.EXTRA_STREAM, Uri::class.java) + ?.forEachIndexed { i, uri -> stageFile(uri, i, batchDir, items) } + } + } + + if (items.length() == 0) { + batchDir.deleteRecursively() + return + } + File(batchDir, "share.json").writeText( + JSONObject().apply { + put("version", 1) + put("items", items) + }.toString() + ) + nativeShareReceived() + } + + private fun addTextItem(items: JSONArray, text: String) { + items.put(JSONObject().apply { + put("kind", if (text.startsWith("http://") || text.startsWith("https://")) "url" else "text") + put("text", text) + }) + } + + private fun stageFile(uri: Uri, index: Int, batchDir: File, items: JSONArray) { + val resolver = contentResolver + var displayName = "shared-$index" + resolver.query(uri, arrayOf(OpenableColumns.DISPLAY_NAME), null, null, null)?.use { c -> + if (c.moveToFirst()) { + val i = c.getColumnIndex(OpenableColumns.DISPLAY_NAME) + if (i >= 0) displayName = c.getString(i) + } + } + val sanitized = displayName + .replace("/", "_").replace("\\", "_").replace("\u0000", "") + .take(120) + .let { if (it.isEmpty() || it == "." || it == "..") "shared" else it } + + val fileName = "$index-$sanitized" + val dest = File(batchDir, fileName) + val input = resolver.openInputStream(uri) + if (input == null) { + android.util.Log.w("ShareTarget", "provider returned no stream for $uri") + return + } + try { + input.use { FileOutputStream(dest).use { output -> it.copyTo(output) } } + items.put(JSONObject().apply { + put("kind", "file") + put("fileName", fileName) + put("mime", resolver.getType(uri) ?: "application/octet-stream") + }) + } catch (e: Exception) { + android.util.Log.w("ShareTarget", "stage failed: ${e.message}") + dest.delete() + } + } + + override fun onWebViewCreate(webView: WebView) { + this.webView = webView + onBackPressedDispatcher.addCallback( + this, + object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + if (handlingBack) return + val wv = this@MainActivity.webView + if (wv == null) { + moveTaskToBack(true) + return + } + handlingBack = true + // If the web app didn't consume the back press, background the app. + wv.evaluateJavascript( + "(typeof window.__sableAndroidBack === 'function' && window.__sableAndroidBack() === true)" + ) { result -> + handlingBack = false + if (result != "true") moveTaskToBack(true) + } + } + } + ) + } + + override fun onDestroy() { + if (instance === this) instance = null + super.onDestroy() + } + + companion object { + private var instance: MainActivity? = null + private var immersiveSystemBarsBehavior: Int? = null + private var immersiveDepth = 0 + + // Bars stay transparent (edge-to-edge plugin) so the webview strips supply the color + // on every version; these only adapt icon contrast. setStatusBarColor/setNavigationBarColor + // are no-ops under enforced edge-to-edge on Android 15+, so we avoid them. + @JvmStatic + fun setStatusBarColorNative(color: Int) { + val activity = instance ?: return + activity.runOnUiThread { + val window = activity.window + WindowCompat.getInsetsController(window, window.decorView).isAppearanceLightStatusBars = + isLight(color) + } + } + + @JvmStatic + fun setNavigationBarColorNative(color: Int) { + val activity = instance ?: return + activity.runOnUiThread { + val window = activity.window + WindowCompat.getInsetsController(window, window.decorView).isAppearanceLightNavigationBars = + isLight(color) + } + } + + @JvmStatic + fun setImmersiveModeNative(enabled: Boolean) { + val activity = instance ?: return + activity.runOnUiThread { + val controller = WindowCompat.getInsetsController(activity.window, activity.window.decorView) + if (enabled) { + immersiveDepth += 1 + if (immersiveDepth > 1) return@runOnUiThread + if (immersiveSystemBarsBehavior == null) { + immersiveSystemBarsBehavior = controller.systemBarsBehavior + } + controller.systemBarsBehavior = + androidx.core.view.WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE + controller.hide(WindowInsetsCompat.Type.systemBars()) + } else { + immersiveDepth = maxOf(0, immersiveDepth - 1) + if (immersiveDepth > 0) return@runOnUiThread + controller.show(WindowInsetsCompat.Type.systemBars()) + immersiveSystemBarsBehavior?.let { controller.systemBarsBehavior = it } + immersiveSystemBarsBehavior = null + } + } + } + + @JvmStatic + fun startCallForegroundServiceNative() { + val activity = checkNotNull(instance) { "MainActivity is unavailable" } + check( + ContextCompat.checkSelfPermission(activity, Manifest.permission.RECORD_AUDIO) == + PackageManager.PERMISSION_GRANTED + ) { "Microphone permission is not granted" } + ContextCompat.startForegroundService(activity, Intent(activity, CallForegroundService::class.java)) + } + + @JvmStatic + fun stopCallForegroundServiceNative() { + val activity = checkNotNull(instance) { "MainActivity is unavailable" } + activity.stopService(Intent(activity, CallForegroundService::class.java)) + } + + private fun isLight(color: Int): Boolean { + val luminance = + (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255.0 + return luminance > 0.5 + } + + @JvmStatic + fun playNotificationSoundNative(code: Int) { + val activity = instance ?: return + val resId = if (code == 1) R.raw.invite else R.raw.notification + activity.runOnUiThread { + val mp = MediaPlayer() + try { + val attrs = AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_NOTIFICATION) + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .build() + mp.setAudioAttributes(attrs) + activity.resources.openRawResourceFd(resId).use { afd -> + mp.setDataSource(afd.fileDescriptor, afd.startOffset, afd.length) + } + mp.setOnCompletionListener { it.release() } + mp.setOnErrorListener { player, _, _ -> + player.release() + true + } + mp.prepare() + mp.start() + } catch (e: Exception) { + mp.release() + android.util.Log.w("NotificationSound", "play failed: ${e.message}") + } + } + } + + } +} diff --git a/src-tauri/gen/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/src-tauri/gen/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000000..cc14f035a6 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/drawable/ic_launcher_background.xml b/src-tauri/gen/android/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000000..a4f78de59d --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src-tauri/gen/android/app/src/main/res/drawable/ic_notification.xml b/src-tauri/gen/android/app/src/main/res/drawable/ic_notification.xml new file mode 120000 index 0000000000..94a4ffc173 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/drawable/ic_notification.xml @@ -0,0 +1 @@ +../../../../../../../icons/android/drawable/ic_notification.xml \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/drawable/notification_icon.xml b/src-tauri/gen/android/app/src/main/res/drawable/notification_icon.xml new file mode 120000 index 0000000000..f363fcecdc --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/drawable/notification_icon.xml @@ -0,0 +1 @@ +../../../../../../../icons/android/drawable/notification_icon.xml \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/layout/activity_main.xml b/src-tauri/gen/android/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000000..4fc244418b --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 120000 index 0000000000..eb916ab9de --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-anydpi-v26/ic_launcher.xml \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_agender.xml b/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_agender.xml new file mode 120000 index 0000000000..27cd14cb55 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_agender.xml @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-anydpi-v26/ic_launcher_agender.xml \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_bisexual.xml b/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_bisexual.xml new file mode 120000 index 0000000000..9cccf7bfdd --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_bisexual.xml @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-anydpi-v26/ic_launcher_bisexual.xml \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_intersex.xml b/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_intersex.xml new file mode 120000 index 0000000000..a6204ac7da --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_intersex.xml @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-anydpi-v26/ic_launcher_intersex.xml \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_lesbian.xml b/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_lesbian.xml new file mode 120000 index 0000000000..87aae0b40f --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_lesbian.xml @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-anydpi-v26/ic_launcher_lesbian.xml \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_mlm.xml b/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_mlm.xml new file mode 120000 index 0000000000..4b1e63dcac --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_mlm.xml @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-anydpi-v26/ic_launcher_mlm.xml \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_pride.xml b/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_pride.xml new file mode 120000 index 0000000000..bccf3b1a66 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_pride.xml @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-anydpi-v26/ic_launcher_pride.xml \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_propeller.xml b/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_propeller.xml new file mode 120000 index 0000000000..38f438cb42 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_propeller.xml @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-anydpi-v26/ic_launcher_propeller.xml \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_trans.xml b/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_trans.xml new file mode 120000 index 0000000000..135fbdd912 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_trans.xml @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-anydpi-v26/ic_launcher_trans.xml \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_transgradient.xml b/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_transgradient.xml new file mode 120000 index 0000000000..89862385ed --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_transgradient.xml @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-anydpi-v26/ic_launcher_transgradient.xml \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 120000 index 0000000000..e4011dc876 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_foreground.png new file mode 100644 index 0000000000..9f0452a13e Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_foreground.png differ diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_agender.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_agender.png new file mode 120000 index 0000000000..b87695f38d --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_agender.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_agender.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_agender_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_agender_foreground.png new file mode 120000 index 0000000000..62f96c3eb8 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_agender_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_agender_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_agender_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_agender_round.png new file mode 120000 index 0000000000..436d787661 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_agender_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_agender_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_bisexual.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_bisexual.png new file mode 120000 index 0000000000..bdcac5f352 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_bisexual.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_bisexual.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_bisexual_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_bisexual_foreground.png new file mode 120000 index 0000000000..bccea940c0 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_bisexual_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_bisexual_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_bisexual_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_bisexual_round.png new file mode 120000 index 0000000000..233ebc24a0 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_bisexual_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_bisexual_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 120000 index 0000000000..2bb464f07c --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_intersex.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_intersex.png new file mode 120000 index 0000000000..9d2ce5e93e --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_intersex.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_intersex.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_intersex_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_intersex_foreground.png new file mode 120000 index 0000000000..558f3f67ba --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_intersex_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_intersex_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_intersex_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_intersex_round.png new file mode 120000 index 0000000000..b38baba983 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_intersex_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_intersex_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_lesbian.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_lesbian.png new file mode 120000 index 0000000000..1a558cad14 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_lesbian.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_lesbian.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_lesbian_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_lesbian_foreground.png new file mode 120000 index 0000000000..e2eb7c55cf --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_lesbian_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_lesbian_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_lesbian_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_lesbian_round.png new file mode 120000 index 0000000000..4d6beb6c29 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_lesbian_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_lesbian_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_mlm.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_mlm.png new file mode 120000 index 0000000000..8c2510aa1e --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_mlm.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_mlm.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_mlm_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_mlm_foreground.png new file mode 120000 index 0000000000..ec4a07f5c2 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_mlm_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_mlm_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_mlm_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_mlm_round.png new file mode 120000 index 0000000000..7494607b74 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_mlm_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_mlm_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_pride.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_pride.png new file mode 120000 index 0000000000..b8c2374984 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_pride.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_pride.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_pride_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_pride_foreground.png new file mode 120000 index 0000000000..948d14bdac --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_pride_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_pride_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_pride_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_pride_round.png new file mode 120000 index 0000000000..846c650c69 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_pride_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_pride_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_propeller.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_propeller.png new file mode 120000 index 0000000000..f4070aa5b1 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_propeller.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_propeller.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_propeller_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_propeller_foreground.png new file mode 120000 index 0000000000..f03ec3eeb5 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_propeller_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_propeller_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_propeller_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_propeller_round.png new file mode 120000 index 0000000000..e3389925fb --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_propeller_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_propeller_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 120000 index 0000000000..0e1d1415ef --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_trans.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_trans.png new file mode 120000 index 0000000000..5287f2f470 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_trans.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_trans.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_trans_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_trans_foreground.png new file mode 120000 index 0000000000..3dd80c24f1 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_trans_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_trans_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_trans_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_trans_round.png new file mode 120000 index 0000000000..915c56ddf3 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_trans_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_trans_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_transgradient.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_transgradient.png new file mode 120000 index 0000000000..e957fd42d7 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_transgradient.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_transgradient.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_transgradient_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_transgradient_foreground.png new file mode 120000 index 0000000000..d72b7b867a --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_transgradient_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_transgradient_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_transgradient_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_transgradient_round.png new file mode 120000 index 0000000000..493cea8154 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_transgradient_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_launcher_transgradient_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_notification.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_notification.png new file mode 120000 index 0000000000..38bf128b24 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_notification.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-hdpi/ic_notification.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 120000 index 0000000000..c5c8af7d59 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_foreground.png new file mode 100644 index 0000000000..55da193da2 Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_foreground.png differ diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_agender.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_agender.png new file mode 120000 index 0000000000..8b580eb30e --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_agender.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_agender.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_agender_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_agender_foreground.png new file mode 120000 index 0000000000..10cfd33721 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_agender_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_agender_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_agender_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_agender_round.png new file mode 120000 index 0000000000..8617382450 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_agender_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_agender_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_bisexual.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_bisexual.png new file mode 120000 index 0000000000..8556e9a655 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_bisexual.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_bisexual.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_bisexual_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_bisexual_foreground.png new file mode 120000 index 0000000000..1994dcb225 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_bisexual_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_bisexual_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_bisexual_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_bisexual_round.png new file mode 120000 index 0000000000..719f95bc78 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_bisexual_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_bisexual_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png new file mode 120000 index 0000000000..1dc8bd6601 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_intersex.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_intersex.png new file mode 120000 index 0000000000..c42b892f08 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_intersex.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_intersex.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_intersex_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_intersex_foreground.png new file mode 120000 index 0000000000..ba800c06ea --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_intersex_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_intersex_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_intersex_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_intersex_round.png new file mode 120000 index 0000000000..50acdb762d --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_intersex_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_intersex_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_lesbian.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_lesbian.png new file mode 120000 index 0000000000..18263a800e --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_lesbian.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_lesbian.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_lesbian_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_lesbian_foreground.png new file mode 120000 index 0000000000..bc5d2212ee --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_lesbian_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_lesbian_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_lesbian_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_lesbian_round.png new file mode 120000 index 0000000000..75a96cc012 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_lesbian_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_lesbian_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_mlm.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_mlm.png new file mode 120000 index 0000000000..c9eeac3ea6 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_mlm.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_mlm.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_mlm_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_mlm_foreground.png new file mode 120000 index 0000000000..f9804e314c --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_mlm_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_mlm_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_mlm_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_mlm_round.png new file mode 120000 index 0000000000..793de56ee3 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_mlm_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_mlm_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_pride.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_pride.png new file mode 120000 index 0000000000..6502cd11c8 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_pride.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_pride.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_pride_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_pride_foreground.png new file mode 120000 index 0000000000..7ff3adae65 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_pride_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_pride_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_pride_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_pride_round.png new file mode 120000 index 0000000000..6ed642f91d --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_pride_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_pride_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_propeller.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_propeller.png new file mode 120000 index 0000000000..1b215f82d6 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_propeller.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_propeller.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_propeller_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_propeller_foreground.png new file mode 120000 index 0000000000..38949c59f4 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_propeller_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_propeller_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_propeller_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_propeller_round.png new file mode 120000 index 0000000000..76b81eb81d --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_propeller_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_propeller_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 120000 index 0000000000..de6c001a7e --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_trans.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_trans.png new file mode 120000 index 0000000000..c102fb0f0c --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_trans.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_trans.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_trans_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_trans_foreground.png new file mode 120000 index 0000000000..51acc91a23 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_trans_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_trans_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_trans_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_trans_round.png new file mode 120000 index 0000000000..1dd1a869e4 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_trans_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_trans_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_transgradient.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_transgradient.png new file mode 120000 index 0000000000..4f0d936c7c --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_transgradient.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_transgradient.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_transgradient_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_transgradient_foreground.png new file mode 120000 index 0000000000..30bf493e38 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_transgradient_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_transgradient_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_transgradient_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_transgradient_round.png new file mode 120000 index 0000000000..c84f336a40 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_transgradient_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_launcher_transgradient_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_notification.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_notification.png new file mode 120000 index 0000000000..04dcb612b3 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_notification.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-mdpi/ic_notification.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 120000 index 0000000000..c244d43ac8 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_foreground.png new file mode 100644 index 0000000000..5596dc90f2 Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_foreground.png differ diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_agender.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_agender.png new file mode 120000 index 0000000000..89d6f24c36 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_agender.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_agender.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_agender_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_agender_foreground.png new file mode 120000 index 0000000000..0e89b40c68 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_agender_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_agender_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_agender_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_agender_round.png new file mode 120000 index 0000000000..75edb4c7b5 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_agender_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_agender_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_bisexual.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_bisexual.png new file mode 120000 index 0000000000..c0068606cb --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_bisexual.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_bisexual.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_bisexual_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_bisexual_foreground.png new file mode 120000 index 0000000000..bfc2b99f46 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_bisexual_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_bisexual_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_bisexual_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_bisexual_round.png new file mode 120000 index 0000000000..d1d3779a40 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_bisexual_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_bisexual_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png new file mode 120000 index 0000000000..a9ba70dfaa --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_intersex.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_intersex.png new file mode 120000 index 0000000000..24956dc0c9 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_intersex.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_intersex.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_intersex_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_intersex_foreground.png new file mode 120000 index 0000000000..e7785c62c9 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_intersex_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_intersex_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_intersex_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_intersex_round.png new file mode 120000 index 0000000000..f3ab686f6f --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_intersex_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_intersex_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_lesbian.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_lesbian.png new file mode 120000 index 0000000000..46af7785b5 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_lesbian.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_lesbian.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_lesbian_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_lesbian_foreground.png new file mode 120000 index 0000000000..d0150dd72f --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_lesbian_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_lesbian_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_lesbian_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_lesbian_round.png new file mode 120000 index 0000000000..fabaa84260 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_lesbian_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_lesbian_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_mlm.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_mlm.png new file mode 120000 index 0000000000..68214bfe18 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_mlm.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_mlm.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_mlm_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_mlm_foreground.png new file mode 120000 index 0000000000..fc843bd983 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_mlm_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_mlm_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_mlm_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_mlm_round.png new file mode 120000 index 0000000000..83c8440628 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_mlm_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_mlm_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_pride.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_pride.png new file mode 120000 index 0000000000..b3ed867c06 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_pride.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_pride.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_pride_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_pride_foreground.png new file mode 120000 index 0000000000..567c0e451b --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_pride_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_pride_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_pride_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_pride_round.png new file mode 120000 index 0000000000..c468d9fd23 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_pride_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_pride_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_propeller.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_propeller.png new file mode 120000 index 0000000000..f81776979a --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_propeller.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_propeller.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_propeller_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_propeller_foreground.png new file mode 120000 index 0000000000..c0e8a7c5f1 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_propeller_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_propeller_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_propeller_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_propeller_round.png new file mode 120000 index 0000000000..fbcebc9733 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_propeller_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_propeller_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 120000 index 0000000000..9756cb1f2e --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_trans.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_trans.png new file mode 120000 index 0000000000..53b23f1334 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_trans.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_trans.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_trans_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_trans_foreground.png new file mode 120000 index 0000000000..941ffc32fe --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_trans_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_trans_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_trans_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_trans_round.png new file mode 120000 index 0000000000..f41774cac6 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_trans_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_trans_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_transgradient.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_transgradient.png new file mode 120000 index 0000000000..1027938aa7 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_transgradient.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_transgradient.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_transgradient_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_transgradient_foreground.png new file mode 120000 index 0000000000..a7c0c6204e --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_transgradient_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_transgradient_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_transgradient_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_transgradient_round.png new file mode 120000 index 0000000000..88f72fd062 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_transgradient_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_launcher_transgradient_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_notification.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_notification.png new file mode 120000 index 0000000000..d4b91921d9 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_notification.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xhdpi/ic_notification.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 120000 index 0000000000..1832021dc3 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_foreground.png new file mode 100644 index 0000000000..65c786a8da Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_foreground.png differ diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_agender.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_agender.png new file mode 120000 index 0000000000..60890a7cfb --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_agender.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_agender.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_agender_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_agender_foreground.png new file mode 120000 index 0000000000..5bbebeada1 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_agender_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_agender_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_agender_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_agender_round.png new file mode 120000 index 0000000000..bc0a0c43a8 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_agender_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_agender_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_bisexual.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_bisexual.png new file mode 120000 index 0000000000..d56927f6cf --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_bisexual.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_bisexual.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_bisexual_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_bisexual_foreground.png new file mode 120000 index 0000000000..03abb11702 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_bisexual_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_bisexual_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_bisexual_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_bisexual_round.png new file mode 120000 index 0000000000..1356ec1fec --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_bisexual_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_bisexual_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 120000 index 0000000000..20afdb7e59 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_intersex.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_intersex.png new file mode 120000 index 0000000000..e99a5b4ee2 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_intersex.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_intersex.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_intersex_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_intersex_foreground.png new file mode 120000 index 0000000000..706e1bded0 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_intersex_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_intersex_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_intersex_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_intersex_round.png new file mode 120000 index 0000000000..caa7905cb7 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_intersex_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_intersex_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_lesbian.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_lesbian.png new file mode 120000 index 0000000000..ad2aa85c63 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_lesbian.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_lesbian.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_lesbian_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_lesbian_foreground.png new file mode 120000 index 0000000000..cd29786ae2 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_lesbian_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_lesbian_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_lesbian_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_lesbian_round.png new file mode 120000 index 0000000000..37c2001a15 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_lesbian_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_lesbian_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_mlm.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_mlm.png new file mode 120000 index 0000000000..5583923731 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_mlm.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_mlm.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_mlm_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_mlm_foreground.png new file mode 120000 index 0000000000..ac549ea8ae --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_mlm_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_mlm_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_mlm_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_mlm_round.png new file mode 120000 index 0000000000..7194de680f --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_mlm_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_mlm_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_pride.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_pride.png new file mode 120000 index 0000000000..d94d4e413f --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_pride.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_pride.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_pride_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_pride_foreground.png new file mode 120000 index 0000000000..d4c47d8ef1 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_pride_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_pride_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_pride_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_pride_round.png new file mode 120000 index 0000000000..07f82375dd --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_pride_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_pride_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_propeller.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_propeller.png new file mode 120000 index 0000000000..b4a8fbe1a4 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_propeller.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_propeller.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_propeller_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_propeller_foreground.png new file mode 120000 index 0000000000..58343a4839 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_propeller_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_propeller_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_propeller_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_propeller_round.png new file mode 120000 index 0000000000..b8adf64699 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_propeller_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_propeller_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 120000 index 0000000000..f209887ca9 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_trans.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_trans.png new file mode 120000 index 0000000000..7ede754280 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_trans.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_trans.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_trans_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_trans_foreground.png new file mode 120000 index 0000000000..262bf24e11 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_trans_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_trans_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_trans_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_trans_round.png new file mode 120000 index 0000000000..a1f731ccc3 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_trans_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_trans_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_transgradient.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_transgradient.png new file mode 120000 index 0000000000..0eaa81d20b --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_transgradient.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_transgradient.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_transgradient_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_transgradient_foreground.png new file mode 120000 index 0000000000..0ad4d217ff --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_transgradient_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_transgradient_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_transgradient_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_transgradient_round.png new file mode 120000 index 0000000000..59ba26ca8e --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_transgradient_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_launcher_transgradient_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_notification.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_notification.png new file mode 120000 index 0000000000..dea0cf0353 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_notification.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxhdpi/ic_notification.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 120000 index 0000000000..2c20016b0d --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_foreground.png new file mode 100644 index 0000000000..03a758e7e5 Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_foreground.png differ diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_agender.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_agender.png new file mode 120000 index 0000000000..61a4a454a4 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_agender.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_agender.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_agender_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_agender_foreground.png new file mode 120000 index 0000000000..74713f7f9a --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_agender_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_agender_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_agender_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_agender_round.png new file mode 120000 index 0000000000..5d6ade925a --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_agender_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_agender_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_bisexual.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_bisexual.png new file mode 120000 index 0000000000..63f0f127b1 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_bisexual.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_bisexual.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_bisexual_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_bisexual_foreground.png new file mode 120000 index 0000000000..305f9cc644 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_bisexual_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_bisexual_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_bisexual_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_bisexual_round.png new file mode 120000 index 0000000000..35c83c3b08 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_bisexual_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_bisexual_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 120000 index 0000000000..18a184d9f9 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_intersex.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_intersex.png new file mode 120000 index 0000000000..8e8e10de4c --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_intersex.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_intersex.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_intersex_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_intersex_foreground.png new file mode 120000 index 0000000000..e20022cff9 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_intersex_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_intersex_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_intersex_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_intersex_round.png new file mode 120000 index 0000000000..f3c574e283 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_intersex_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_intersex_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_lesbian.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_lesbian.png new file mode 120000 index 0000000000..8a625ff2ac --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_lesbian.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_lesbian.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_lesbian_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_lesbian_foreground.png new file mode 120000 index 0000000000..87ad5575fe --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_lesbian_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_lesbian_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_lesbian_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_lesbian_round.png new file mode 120000 index 0000000000..02241ca0d2 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_lesbian_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_lesbian_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_mlm.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_mlm.png new file mode 120000 index 0000000000..ff467a129d --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_mlm.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_mlm.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_mlm_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_mlm_foreground.png new file mode 120000 index 0000000000..8a9c3c68fa --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_mlm_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_mlm_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_mlm_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_mlm_round.png new file mode 120000 index 0000000000..69c0bc752c --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_mlm_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_mlm_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_pride.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_pride.png new file mode 120000 index 0000000000..3555cd35f3 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_pride.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_pride.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_pride_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_pride_foreground.png new file mode 120000 index 0000000000..00c72651de --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_pride_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_pride_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_pride_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_pride_round.png new file mode 120000 index 0000000000..0e26dd50d8 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_pride_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_pride_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_propeller.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_propeller.png new file mode 120000 index 0000000000..b8cb180d0c --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_propeller.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_propeller.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_propeller_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_propeller_foreground.png new file mode 120000 index 0000000000..9551611dea --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_propeller_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_propeller_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_propeller_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_propeller_round.png new file mode 120000 index 0000000000..d74e0c7b2b --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_propeller_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_propeller_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 120000 index 0000000000..c7eff28348 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_trans.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_trans.png new file mode 120000 index 0000000000..d4225a479b --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_trans.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_trans.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_trans_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_trans_foreground.png new file mode 120000 index 0000000000..ff1100d338 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_trans_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_trans_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_trans_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_trans_round.png new file mode 120000 index 0000000000..d773b79eaf --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_trans_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_trans_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_transgradient.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_transgradient.png new file mode 120000 index 0000000000..1e11cf5dd1 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_transgradient.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_transgradient.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_transgradient_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_transgradient_foreground.png new file mode 120000 index 0000000000..2c2b77c00f --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_transgradient_foreground.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_transgradient_foreground.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_transgradient_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_transgradient_round.png new file mode 120000 index 0000000000..dadba44365 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_transgradient_round.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_launcher_transgradient_round.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_notification.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_notification.png new file mode 120000 index 0000000000..78d475ccc7 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_notification.png @@ -0,0 +1 @@ +../../../../../../../icons/android/mipmap-xxxhdpi/ic_notification.png \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/raw/invite.ogg b/src-tauri/gen/android/app/src/main/res/raw/invite.ogg new file mode 100755 index 0000000000..4a00af6109 Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/raw/invite.ogg differ diff --git a/src-tauri/gen/android/app/src/main/res/raw/notification.ogg b/src-tauri/gen/android/app/src/main/res/raw/notification.ogg new file mode 100755 index 0000000000..93b3a0144e Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/raw/notification.ogg differ diff --git a/src-tauri/gen/android/app/src/main/res/values-night/themes.xml b/src-tauri/gen/android/app/src/main/res/values-night/themes.xml new file mode 100644 index 0000000000..eec7242f5a --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/values-night/themes.xml @@ -0,0 +1,7 @@ + + + + diff --git a/src-tauri/gen/android/app/src/main/res/values/colors.xml b/src-tauri/gen/android/app/src/main/res/values/colors.xml new file mode 100644 index 0000000000..f8c6127d32 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/values/ic_launcher_background.xml b/src-tauri/gen/android/app/src/main/res/values/ic_launcher_background.xml new file mode 120000 index 0000000000..3aa4ae6f10 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1 @@ +../../../../../../../icons/android/values/ic_launcher_background.xml \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/values/strings.xml b/src-tauri/gen/android/app/src/main/res/values/strings.xml new file mode 100644 index 0000000000..72d1fa7704 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + Sable + Sable + \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/values/themes.xml b/src-tauri/gen/android/app/src/main/res/values/themes.xml new file mode 100644 index 0000000000..eec7242f5a --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/values/themes.xml @@ -0,0 +1,7 @@ + + + + diff --git a/src-tauri/gen/android/app/src/main/res/xml/file_paths.xml b/src-tauri/gen/android/app/src/main/res/xml/file_paths.xml new file mode 100644 index 0000000000..782d63b993 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/xml/file_paths.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src-tauri/gen/android/app/src/main/res/xml/network_security_config.xml b/src-tauri/gen/android/app/src/main/res/xml/network_security_config.xml new file mode 100644 index 0000000000..626e7e34c0 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/xml/network_security_config.xml @@ -0,0 +1,6 @@ + + + + 127.0.0.1 + + diff --git a/src-tauri/gen/android/build.gradle.kts b/src-tauri/gen/android/build.gradle.kts new file mode 100644 index 0000000000..d5b85e453b --- /dev/null +++ b/src-tauri/gen/android/build.gradle.kts @@ -0,0 +1,26 @@ +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath("com.android.tools.build:gradle:8.11.0") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:2.2.0") + classpath("com.google.gms:google-services:4.5.0") + } +} + +allprojects { + repositories { + google() + mavenCentral() + // livekit-android pulls com.github.davidliu:audioswitch, published only + // on JitPack. + maven { setUrl("https://jitpack.io") } + } +} + +tasks.register("clean").configure { + delete("build") +} + diff --git a/src-tauri/gen/android/buildSrc/build.gradle.kts b/src-tauri/gen/android/buildSrc/build.gradle.kts new file mode 100644 index 0000000000..5c55bba71c --- /dev/null +++ b/src-tauri/gen/android/buildSrc/build.gradle.kts @@ -0,0 +1,23 @@ +plugins { + `kotlin-dsl` +} + +gradlePlugin { + plugins { + create("pluginsForCoolKids") { + id = "rust" + implementationClass = "RustPlugin" + } + } +} + +repositories { + google() + mavenCentral() +} + +dependencies { + compileOnly(gradleApi()) + implementation("com.android.tools.build:gradle:8.11.0") +} + diff --git a/src-tauri/gen/android/buildSrc/src/main/java/moe/sable/client/kotlin/BuildTask.kt b/src-tauri/gen/android/buildSrc/src/main/java/moe/sable/client/kotlin/BuildTask.kt new file mode 100644 index 0000000000..f764e2ad9b --- /dev/null +++ b/src-tauri/gen/android/buildSrc/src/main/java/moe/sable/client/kotlin/BuildTask.kt @@ -0,0 +1,68 @@ +import java.io.File +import org.apache.tools.ant.taskdefs.condition.Os +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.api.logging.LogLevel +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.TaskAction + +open class BuildTask : DefaultTask() { + @Input + var rootDirRel: String? = null + @Input + var target: String? = null + @Input + var release: Boolean? = null + + @TaskAction + fun assemble() { + val executable = """pnpm"""; + try { + runTauriCli(executable) + } catch (e: Exception) { + if (Os.isFamily(Os.FAMILY_WINDOWS)) { + // Try different Windows-specific extensions + val fallbacks = listOf( + "$executable.exe", + "$executable.cmd", + "$executable.bat", + ) + + var lastException: Exception = e + for (fallback in fallbacks) { + try { + runTauriCli(fallback) + return + } catch (fallbackException: Exception) { + lastException = fallbackException + } + } + throw lastException + } else { + throw e; + } + } + } + + fun runTauriCli(executable: String) { + val rootDirRel = rootDirRel ?: throw GradleException("rootDirRel cannot be null") + val target = target ?: throw GradleException("target cannot be null") + val release = release ?: throw GradleException("release cannot be null") + val args = listOf("tauri", "android", "android-studio-script"); + + project.exec { + workingDir(File(project.projectDir, rootDirRel)) + executable(executable) + args(args) + if (project.logger.isEnabled(LogLevel.DEBUG)) { + args("-vv") + } else if (project.logger.isEnabled(LogLevel.INFO)) { + args("-v") + } + if (release) { + args("--release") + } + args(listOf("--target", target)) + }.assertNormalExitValue() + } +} \ No newline at end of file diff --git a/src-tauri/gen/android/buildSrc/src/main/java/moe/sable/client/kotlin/RustPlugin.kt b/src-tauri/gen/android/buildSrc/src/main/java/moe/sable/client/kotlin/RustPlugin.kt new file mode 100644 index 0000000000..4aa7fcaf67 --- /dev/null +++ b/src-tauri/gen/android/buildSrc/src/main/java/moe/sable/client/kotlin/RustPlugin.kt @@ -0,0 +1,85 @@ +import com.android.build.api.dsl.ApplicationExtension +import org.gradle.api.DefaultTask +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.get + +const val TASK_GROUP = "rust" + +open class Config { + lateinit var rootDirRel: String +} + +open class RustPlugin : Plugin { + private lateinit var config: Config + + override fun apply(project: Project) = with(project) { + config = extensions.create("rust", Config::class.java) + + val defaultAbiList = listOf("arm64-v8a", "armeabi-v7a", "x86", "x86_64"); + val abiList = (findProperty("abiList") as? String)?.split(',') ?: defaultAbiList + + val defaultArchList = listOf("arm64", "arm", "x86", "x86_64"); + val archList = (findProperty("archList") as? String)?.split(',') ?: defaultArchList + + val targetsList = (findProperty("targetList") as? String)?.split(',') ?: listOf("aarch64", "armv7", "i686", "x86_64") + + extensions.configure { + @Suppress("UnstableApiUsage") + flavorDimensions.add("abi") + productFlavors { + create("universal") { + dimension = "abi" + ndk { + abiFilters += abiList + } + } + defaultArchList.forEachIndexed { index, arch -> + create(arch) { + dimension = "abi" + ndk { + abiFilters.add(defaultAbiList[index]) + } + } + } + } + } + + afterEvaluate { + for (profile in listOf("debug", "release")) { + val profileCapitalized = profile.replaceFirstChar { it.uppercase() } + val buildTask = tasks.maybeCreate( + "rustBuildUniversal$profileCapitalized", + DefaultTask::class.java + ).apply { + group = TASK_GROUP + description = "Build dynamic library in $profile mode for all targets" + } + + tasks["mergeUniversal${profileCapitalized}JniLibFolders"].dependsOn(buildTask) + + for (targetPair in targetsList.withIndex()) { + val targetName = targetPair.value + val targetArch = archList[targetPair.index] + val targetArchCapitalized = targetArch.replaceFirstChar { it.uppercase() } + val targetBuildTask = project.tasks.maybeCreate( + "rustBuild$targetArchCapitalized$profileCapitalized", + BuildTask::class.java + ).apply { + group = TASK_GROUP + description = "Build dynamic library in $profile mode for $targetArch" + rootDirRel = config.rootDirRel + target = targetName + release = profile == "release" + } + + buildTask.dependsOn(targetBuildTask) + tasks["merge$targetArchCapitalized${profileCapitalized}JniLibFolders"].dependsOn( + targetBuildTask + ) + } + } + } + } +} \ No newline at end of file diff --git a/src-tauri/gen/android/gradle.properties b/src-tauri/gen/android/gradle.properties new file mode 100644 index 0000000000..6ef904e755 --- /dev/null +++ b/src-tauri/gen/android/gradle.properties @@ -0,0 +1,26 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +org.gradle.parallel=true +# Reuse task outputs from the build cache (persisted across CI runs by gradle/actions/setup-gradle). +org.gradle.caching=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app"s APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true +android.nonFinalResIds=false \ No newline at end of file diff --git a/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.jar b/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000..e708b1c023 Binary files /dev/null and b/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.properties b/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..c5f9a53c27 --- /dev/null +++ b/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Tue May 10 19:22:52 CST 2022 +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/src-tauri/gen/android/gradlew b/src-tauri/gen/android/gradlew new file mode 100755 index 0000000000..4f906e0c81 --- /dev/null +++ b/src-tauri/gen/android/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/src-tauri/gen/android/gradlew.bat b/src-tauri/gen/android/gradlew.bat new file mode 100644 index 0000000000..107acd32c4 --- /dev/null +++ b/src-tauri/gen/android/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/src-tauri/gen/android/settings.gradle b/src-tauri/gen/android/settings.gradle new file mode 100644 index 0000000000..39391166fb --- /dev/null +++ b/src-tauri/gen/android/settings.gradle @@ -0,0 +1,3 @@ +include ':app' + +apply from: 'tauri.settings.gradle' diff --git a/src-tauri/icons/128x128.png b/src-tauri/icons/128x128.png new file mode 100644 index 0000000000..a2ea9ba0ee Binary files /dev/null and b/src-tauri/icons/128x128.png differ diff --git a/src-tauri/icons/128x128@2x.png b/src-tauri/icons/128x128@2x.png new file mode 100644 index 0000000000..8e5fc5c0a7 Binary files /dev/null and b/src-tauri/icons/128x128@2x.png differ diff --git a/src-tauri/icons/32x32.png b/src-tauri/icons/32x32.png new file mode 100644 index 0000000000..edf0662cc2 Binary files /dev/null and b/src-tauri/icons/32x32.png differ diff --git a/src-tauri/icons/64x64.png b/src-tauri/icons/64x64.png new file mode 100644 index 0000000000..355c0b8391 Binary files /dev/null and b/src-tauri/icons/64x64.png differ diff --git a/src-tauri/icons/Square107x107Logo.png b/src-tauri/icons/Square107x107Logo.png new file mode 100644 index 0000000000..54757a7b4a Binary files /dev/null and b/src-tauri/icons/Square107x107Logo.png differ diff --git a/src-tauri/icons/Square142x142Logo.png b/src-tauri/icons/Square142x142Logo.png new file mode 100644 index 0000000000..9327e3a0bc Binary files /dev/null and b/src-tauri/icons/Square142x142Logo.png differ diff --git a/src-tauri/icons/Square150x150Logo.png b/src-tauri/icons/Square150x150Logo.png new file mode 100644 index 0000000000..aad167f443 Binary files /dev/null and b/src-tauri/icons/Square150x150Logo.png differ diff --git a/src-tauri/icons/Square284x284Logo.png b/src-tauri/icons/Square284x284Logo.png new file mode 100644 index 0000000000..54492f07a0 Binary files /dev/null and b/src-tauri/icons/Square284x284Logo.png differ diff --git a/src-tauri/icons/Square30x30Logo.png b/src-tauri/icons/Square30x30Logo.png new file mode 100644 index 0000000000..3e788c74a8 Binary files /dev/null and b/src-tauri/icons/Square30x30Logo.png differ diff --git a/src-tauri/icons/Square310x310Logo.png b/src-tauri/icons/Square310x310Logo.png new file mode 100644 index 0000000000..040a8726d6 Binary files /dev/null and b/src-tauri/icons/Square310x310Logo.png differ diff --git a/src-tauri/icons/Square44x44Logo.png b/src-tauri/icons/Square44x44Logo.png new file mode 100644 index 0000000000..eee9570985 Binary files /dev/null and b/src-tauri/icons/Square44x44Logo.png differ diff --git a/src-tauri/icons/Square71x71Logo.png b/src-tauri/icons/Square71x71Logo.png new file mode 100644 index 0000000000..1a029990b7 Binary files /dev/null and b/src-tauri/icons/Square71x71Logo.png differ diff --git a/src-tauri/icons/Square89x89Logo.png b/src-tauri/icons/Square89x89Logo.png new file mode 100644 index 0000000000..f33f3ebc0b Binary files /dev/null and b/src-tauri/icons/Square89x89Logo.png differ diff --git a/src-tauri/icons/StoreLogo.png b/src-tauri/icons/StoreLogo.png new file mode 100644 index 0000000000..1ce3a49676 Binary files /dev/null and b/src-tauri/icons/StoreLogo.png differ diff --git a/src-tauri/icons/android/drawable/ic_notification.xml b/src-tauri/icons/android/drawable/ic_notification.xml new file mode 100644 index 0000000000..6aebe88af3 --- /dev/null +++ b/src-tauri/icons/android/drawable/ic_notification.xml @@ -0,0 +1,6 @@ + + + diff --git a/src-tauri/icons/android/drawable/notification_icon.xml b/src-tauri/icons/android/drawable/notification_icon.xml new file mode 100644 index 0000000000..29b2f4cde0 --- /dev/null +++ b/src-tauri/icons/android/drawable/notification_icon.xml @@ -0,0 +1,6 @@ + + + diff --git a/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000000..a7f85a4a57 --- /dev/null +++ b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher_agender.xml b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher_agender.xml new file mode 100644 index 0000000000..0a3a80e507 --- /dev/null +++ b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher_agender.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher_bisexual.xml b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher_bisexual.xml new file mode 100644 index 0000000000..fd90e53652 --- /dev/null +++ b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher_bisexual.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher_intersex.xml b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher_intersex.xml new file mode 100644 index 0000000000..6e8fff005d --- /dev/null +++ b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher_intersex.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher_lesbian.xml b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher_lesbian.xml new file mode 100644 index 0000000000..9fb51c7528 --- /dev/null +++ b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher_lesbian.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher_mlm.xml b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher_mlm.xml new file mode 100644 index 0000000000..86167b87ea --- /dev/null +++ b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher_mlm.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher_pride.xml b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher_pride.xml new file mode 100644 index 0000000000..d88d021f40 --- /dev/null +++ b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher_pride.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher_propeller.xml b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher_propeller.xml new file mode 100644 index 0000000000..b3508a3252 --- /dev/null +++ b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher_propeller.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher_trans.xml b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher_trans.xml new file mode 100644 index 0000000000..2f33199625 --- /dev/null +++ b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher_trans.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher_transgradient.xml b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher_transgradient.xml new file mode 100644 index 0000000000..7101264cce --- /dev/null +++ b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher_transgradient.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..06d7abf55e Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_agender.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_agender.png new file mode 100644 index 0000000000..310d926155 Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_agender.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_agender_foreground.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_agender_foreground.png new file mode 100644 index 0000000000..bceca5ad1c Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_agender_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_agender_round.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_agender_round.png new file mode 100644 index 0000000000..44fa8e6e71 Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_agender_round.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_bisexual.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_bisexual.png new file mode 100644 index 0000000000..af1faf3710 Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_bisexual.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_bisexual_foreground.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_bisexual_foreground.png new file mode 100644 index 0000000000..36e2d27a1b Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_bisexual_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_bisexual_round.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_bisexual_round.png new file mode 100644 index 0000000000..da3883afb9 Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_bisexual_round.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000..aa3bdd1a59 Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_intersex.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_intersex.png new file mode 100644 index 0000000000..27001cc175 Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_intersex.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_intersex_foreground.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_intersex_foreground.png new file mode 100644 index 0000000000..9fb9f5295c Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_intersex_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_intersex_round.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_intersex_round.png new file mode 100644 index 0000000000..f2331a3da1 Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_intersex_round.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_lesbian.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_lesbian.png new file mode 100644 index 0000000000..e2cc578573 Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_lesbian.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_lesbian_foreground.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_lesbian_foreground.png new file mode 100644 index 0000000000..2f4bd74b5b Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_lesbian_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_lesbian_round.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_lesbian_round.png new file mode 100644 index 0000000000..257c5cf065 Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_lesbian_round.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_mlm.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_mlm.png new file mode 100644 index 0000000000..0f29efd097 Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_mlm.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_mlm_foreground.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_mlm_foreground.png new file mode 100644 index 0000000000..c3357061cd Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_mlm_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_mlm_round.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_mlm_round.png new file mode 100644 index 0000000000..82bf0b9e9a Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_mlm_round.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_pride.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_pride.png new file mode 100644 index 0000000000..d35b2d34ff Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_pride.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_pride_foreground.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_pride_foreground.png new file mode 100644 index 0000000000..b21abe3e3c Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_pride_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_pride_round.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_pride_round.png new file mode 100644 index 0000000000..cb073ad2cf Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_pride_round.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_propeller.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_propeller.png new file mode 100644 index 0000000000..43d87197b4 Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_propeller.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_propeller_foreground.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_propeller_foreground.png new file mode 100644 index 0000000000..5b09c8abaa Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_propeller_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_propeller_round.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_propeller_round.png new file mode 100644 index 0000000000..977d2a4a9c Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_propeller_round.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000000..a866e4f1c6 Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_trans.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_trans.png new file mode 100644 index 0000000000..1de54eed12 Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_trans.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_trans_foreground.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_trans_foreground.png new file mode 100644 index 0000000000..e45e4645fb Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_trans_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_trans_round.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_trans_round.png new file mode 100644 index 0000000000..b886445fda Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_trans_round.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_transgradient.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_transgradient.png new file mode 100644 index 0000000000..7c85f2ddc9 Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_transgradient.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_transgradient_foreground.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_transgradient_foreground.png new file mode 100644 index 0000000000..764344261a Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_transgradient_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_transgradient_round.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_transgradient_round.png new file mode 100644 index 0000000000..73b45d8b72 Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_transgradient_round.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_notification.png b/src-tauri/icons/android/mipmap-hdpi/ic_notification.png new file mode 100644 index 0000000000..1663c86a45 Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_notification.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..c080497e08 Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_agender.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_agender.png new file mode 100644 index 0000000000..990ba87230 Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_agender.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_agender_foreground.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_agender_foreground.png new file mode 100644 index 0000000000..0ab98ad07a Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_agender_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_agender_round.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_agender_round.png new file mode 100644 index 0000000000..172bad7fb9 Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_agender_round.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_bisexual.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_bisexual.png new file mode 100644 index 0000000000..a4842fc321 Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_bisexual.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_bisexual_foreground.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_bisexual_foreground.png new file mode 100644 index 0000000000..059ed54dfb Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_bisexual_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_bisexual_round.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_bisexual_round.png new file mode 100644 index 0000000000..9e3d93de06 Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_bisexual_round.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000..cb6e67baeb Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_intersex.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_intersex.png new file mode 100644 index 0000000000..3a628578bd Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_intersex.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_intersex_foreground.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_intersex_foreground.png new file mode 100644 index 0000000000..d12b98163a Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_intersex_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_intersex_round.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_intersex_round.png new file mode 100644 index 0000000000..ef1a8b1fdc Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_intersex_round.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_lesbian.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_lesbian.png new file mode 100644 index 0000000000..07ebb7a80a Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_lesbian.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_lesbian_foreground.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_lesbian_foreground.png new file mode 100644 index 0000000000..f563bef224 Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_lesbian_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_lesbian_round.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_lesbian_round.png new file mode 100644 index 0000000000..9a8dd68895 Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_lesbian_round.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_mlm.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_mlm.png new file mode 100644 index 0000000000..2ea1e2ae59 Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_mlm.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_mlm_foreground.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_mlm_foreground.png new file mode 100644 index 0000000000..8f8b0c9263 Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_mlm_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_mlm_round.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_mlm_round.png new file mode 100644 index 0000000000..cf1dbd4953 Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_mlm_round.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_pride.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_pride.png new file mode 100644 index 0000000000..5631f1b564 Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_pride.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_pride_foreground.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_pride_foreground.png new file mode 100644 index 0000000000..aea2b5a529 Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_pride_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_pride_round.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_pride_round.png new file mode 100644 index 0000000000..1bb3c815ec Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_pride_round.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_propeller.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_propeller.png new file mode 100644 index 0000000000..940c434092 Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_propeller.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_propeller_foreground.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_propeller_foreground.png new file mode 100644 index 0000000000..80c034faf6 Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_propeller_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_propeller_round.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_propeller_round.png new file mode 100644 index 0000000000..bcfecef8d2 Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_propeller_round.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000000..417de2fa8c Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_trans.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_trans.png new file mode 100644 index 0000000000..48300a4703 Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_trans.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_trans_foreground.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_trans_foreground.png new file mode 100644 index 0000000000..d0087511d1 Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_trans_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_trans_round.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_trans_round.png new file mode 100644 index 0000000000..f02c666096 Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_trans_round.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_transgradient.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_transgradient.png new file mode 100644 index 0000000000..2036ea6158 Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_transgradient.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_transgradient_foreground.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_transgradient_foreground.png new file mode 100644 index 0000000000..c21ff66b1b Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_transgradient_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_transgradient_round.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_transgradient_round.png new file mode 100644 index 0000000000..b695357290 Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_transgradient_round.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_notification.png b/src-tauri/icons/android/mipmap-mdpi/ic_notification.png new file mode 100644 index 0000000000..aa597d6aea Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_notification.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..b33e134fb9 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_agender.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_agender.png new file mode 100644 index 0000000000..e5f52ac328 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_agender.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_agender_foreground.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_agender_foreground.png new file mode 100644 index 0000000000..6fc7f9058f Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_agender_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_agender_round.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_agender_round.png new file mode 100644 index 0000000000..dcc92131c3 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_agender_round.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_bisexual.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_bisexual.png new file mode 100644 index 0000000000..2bb8380ac5 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_bisexual.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_bisexual_foreground.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_bisexual_foreground.png new file mode 100644 index 0000000000..1c99932b0d Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_bisexual_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_bisexual_round.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_bisexual_round.png new file mode 100644 index 0000000000..5b1ae55996 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_bisexual_round.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000..1e2b2c45ad Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_intersex.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_intersex.png new file mode 100644 index 0000000000..6992fa6363 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_intersex.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_intersex_foreground.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_intersex_foreground.png new file mode 100644 index 0000000000..4d172cc646 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_intersex_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_intersex_round.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_intersex_round.png new file mode 100644 index 0000000000..c7c84a2bed Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_intersex_round.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_lesbian.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_lesbian.png new file mode 100644 index 0000000000..a59281fabc Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_lesbian.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_lesbian_foreground.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_lesbian_foreground.png new file mode 100644 index 0000000000..aff850c6d2 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_lesbian_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_lesbian_round.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_lesbian_round.png new file mode 100644 index 0000000000..3f75aae99e Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_lesbian_round.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_mlm.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_mlm.png new file mode 100644 index 0000000000..3c2212a69e Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_mlm.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_mlm_foreground.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_mlm_foreground.png new file mode 100644 index 0000000000..e2afe08405 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_mlm_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_mlm_round.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_mlm_round.png new file mode 100644 index 0000000000..2e3a0cc916 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_mlm_round.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_pride.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_pride.png new file mode 100644 index 0000000000..008660563f Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_pride.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_pride_foreground.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_pride_foreground.png new file mode 100644 index 0000000000..14ad5c4283 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_pride_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_pride_round.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_pride_round.png new file mode 100644 index 0000000000..69d35a1a68 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_pride_round.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_propeller.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_propeller.png new file mode 100644 index 0000000000..cd4a855b1c Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_propeller.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_propeller_foreground.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_propeller_foreground.png new file mode 100644 index 0000000000..268d412125 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_propeller_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_propeller_round.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_propeller_round.png new file mode 100644 index 0000000000..0d8ce90995 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_propeller_round.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000..09f18a61e4 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_trans.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_trans.png new file mode 100644 index 0000000000..116a4b4603 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_trans.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_trans_foreground.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_trans_foreground.png new file mode 100644 index 0000000000..da10b0e02a Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_trans_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_trans_round.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_trans_round.png new file mode 100644 index 0000000000..b2f4cb160c Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_trans_round.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_transgradient.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_transgradient.png new file mode 100644 index 0000000000..d8b98b1cdd Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_transgradient.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_transgradient_foreground.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_transgradient_foreground.png new file mode 100644 index 0000000000..e8da90f391 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_transgradient_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_transgradient_round.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_transgradient_round.png new file mode 100644 index 0000000000..7b230607b7 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_transgradient_round.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_notification.png b/src-tauri/icons/android/mipmap-xhdpi/ic_notification.png new file mode 100644 index 0000000000..047324fff6 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_notification.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..a865d7cb9e Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_agender.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_agender.png new file mode 100644 index 0000000000..1073e4079b Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_agender.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_agender_foreground.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_agender_foreground.png new file mode 100644 index 0000000000..f25c5c710f Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_agender_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_agender_round.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_agender_round.png new file mode 100644 index 0000000000..c8c1390632 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_agender_round.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_bisexual.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_bisexual.png new file mode 100644 index 0000000000..a15ae32964 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_bisexual.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_bisexual_foreground.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_bisexual_foreground.png new file mode 100644 index 0000000000..7773a57096 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_bisexual_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_bisexual_round.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_bisexual_round.png new file mode 100644 index 0000000000..cc91726f9a Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_bisexual_round.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000..1c3ccf97dd Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_intersex.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_intersex.png new file mode 100644 index 0000000000..6dd0b9dec0 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_intersex.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_intersex_foreground.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_intersex_foreground.png new file mode 100644 index 0000000000..5b7b76d363 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_intersex_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_intersex_round.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_intersex_round.png new file mode 100644 index 0000000000..9d618a8594 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_intersex_round.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_lesbian.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_lesbian.png new file mode 100644 index 0000000000..cd679884be Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_lesbian.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_lesbian_foreground.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_lesbian_foreground.png new file mode 100644 index 0000000000..6dcd594489 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_lesbian_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_lesbian_round.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_lesbian_round.png new file mode 100644 index 0000000000..bc58bbbd41 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_lesbian_round.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_mlm.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_mlm.png new file mode 100644 index 0000000000..88d76c020d Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_mlm.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_mlm_foreground.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_mlm_foreground.png new file mode 100644 index 0000000000..c2e7946e80 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_mlm_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_mlm_round.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_mlm_round.png new file mode 100644 index 0000000000..26cdfaf6a9 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_mlm_round.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_pride.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_pride.png new file mode 100644 index 0000000000..2433fa3d31 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_pride.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_pride_foreground.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_pride_foreground.png new file mode 100644 index 0000000000..8040faf584 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_pride_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_pride_round.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_pride_round.png new file mode 100644 index 0000000000..57ace53b10 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_pride_round.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_propeller.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_propeller.png new file mode 100644 index 0000000000..1cd82c553f Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_propeller.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_propeller_foreground.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_propeller_foreground.png new file mode 100644 index 0000000000..3fc8c5fe12 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_propeller_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_propeller_round.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_propeller_round.png new file mode 100644 index 0000000000..7b5a73756d Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_propeller_round.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000..7013b3e534 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_trans.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_trans.png new file mode 100644 index 0000000000..e33b0148fb Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_trans.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_trans_foreground.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_trans_foreground.png new file mode 100644 index 0000000000..761ebb2058 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_trans_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_trans_round.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_trans_round.png new file mode 100644 index 0000000000..b6ca9ee574 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_trans_round.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_transgradient.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_transgradient.png new file mode 100644 index 0000000000..1bbb9b6533 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_transgradient.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_transgradient_foreground.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_transgradient_foreground.png new file mode 100644 index 0000000000..e3c2d4da9c Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_transgradient_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_transgradient_round.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_transgradient_round.png new file mode 100644 index 0000000000..32f95415dc Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_transgradient_round.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_notification.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_notification.png new file mode 100644 index 0000000000..facf5d62d3 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_notification.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..6a119ae908 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_agender.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_agender.png new file mode 100644 index 0000000000..1b25a87e5a Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_agender.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_agender_foreground.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_agender_foreground.png new file mode 100644 index 0000000000..6274da0e75 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_agender_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_agender_round.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_agender_round.png new file mode 100644 index 0000000000..6c6f6a093f Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_agender_round.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_bisexual.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_bisexual.png new file mode 100644 index 0000000000..ecbc7cbcd6 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_bisexual.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_bisexual_foreground.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_bisexual_foreground.png new file mode 100644 index 0000000000..74ee3ab5d3 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_bisexual_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_bisexual_round.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_bisexual_round.png new file mode 100644 index 0000000000..daee14db38 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_bisexual_round.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000..f99ea54a08 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_intersex.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_intersex.png new file mode 100644 index 0000000000..fc022cc5d2 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_intersex.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_intersex_foreground.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_intersex_foreground.png new file mode 100644 index 0000000000..5bfae70dc7 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_intersex_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_intersex_round.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_intersex_round.png new file mode 100644 index 0000000000..3aaf5f34fe Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_intersex_round.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_lesbian.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_lesbian.png new file mode 100644 index 0000000000..cd80fcea47 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_lesbian.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_lesbian_foreground.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_lesbian_foreground.png new file mode 100644 index 0000000000..c4c4a3cddd Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_lesbian_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_lesbian_round.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_lesbian_round.png new file mode 100644 index 0000000000..e2ca99f6e5 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_lesbian_round.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_mlm.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_mlm.png new file mode 100644 index 0000000000..47e4762446 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_mlm.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_mlm_foreground.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_mlm_foreground.png new file mode 100644 index 0000000000..08518a0c86 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_mlm_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_mlm_round.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_mlm_round.png new file mode 100644 index 0000000000..48915ddf39 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_mlm_round.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_pride.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_pride.png new file mode 100644 index 0000000000..102d24cce2 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_pride.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_pride_foreground.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_pride_foreground.png new file mode 100644 index 0000000000..4fe9ab13df Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_pride_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_pride_round.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_pride_round.png new file mode 100644 index 0000000000..d0d56cffcd Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_pride_round.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_propeller.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_propeller.png new file mode 100644 index 0000000000..9b6c073c2f Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_propeller.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_propeller_foreground.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_propeller_foreground.png new file mode 100644 index 0000000000..229ea99c39 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_propeller_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_propeller_round.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_propeller_round.png new file mode 100644 index 0000000000..feb1ac6989 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_propeller_round.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000..dab373e053 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_trans.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_trans.png new file mode 100644 index 0000000000..586cd01d78 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_trans.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_trans_foreground.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_trans_foreground.png new file mode 100644 index 0000000000..f346f79319 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_trans_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_trans_round.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_trans_round.png new file mode 100644 index 0000000000..5236246d8b Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_trans_round.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_transgradient.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_transgradient.png new file mode 100644 index 0000000000..b0d53eced4 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_transgradient.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_transgradient_foreground.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_transgradient_foreground.png new file mode 100644 index 0000000000..1642b726bc Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_transgradient_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_transgradient_round.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_transgradient_round.png new file mode 100644 index 0000000000..436bc12fa7 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_transgradient_round.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_notification.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_notification.png new file mode 100644 index 0000000000..5f39b5f1a4 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_notification.png differ diff --git a/src-tauri/icons/android/values/ic_launcher_background.xml b/src-tauri/icons/android/values/ic_launcher_background.xml new file mode 100644 index 0000000000..652c0c8d54 --- /dev/null +++ b/src-tauri/icons/android/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #1A1C28 + \ No newline at end of file diff --git a/src-tauri/icons/app-icons/agender.svg b/src-tauri/icons/app-icons/agender.svg new file mode 100644 index 0000000000..42b856f414 --- /dev/null +++ b/src-tauri/icons/app-icons/agender.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src-tauri/icons/app-icons/bisexual.svg b/src-tauri/icons/app-icons/bisexual.svg new file mode 100644 index 0000000000..10b78ee2d5 --- /dev/null +++ b/src-tauri/icons/app-icons/bisexual.svg @@ -0,0 +1 @@ + diff --git a/src-tauri/icons/app-icons/intersex.svg b/src-tauri/icons/app-icons/intersex.svg new file mode 100644 index 0000000000..327d77a218 --- /dev/null +++ b/src-tauri/icons/app-icons/intersex.svg @@ -0,0 +1 @@ + diff --git a/src-tauri/icons/app-icons/lesbian.svg b/src-tauri/icons/app-icons/lesbian.svg new file mode 100644 index 0000000000..5e3d6fca4e --- /dev/null +++ b/src-tauri/icons/app-icons/lesbian.svg @@ -0,0 +1 @@ + diff --git a/src-tauri/icons/app-icons/mlm.svg b/src-tauri/icons/app-icons/mlm.svg new file mode 100644 index 0000000000..5210b85817 --- /dev/null +++ b/src-tauri/icons/app-icons/mlm.svg @@ -0,0 +1 @@ + diff --git a/src-tauri/icons/app-icons/pride.svg b/src-tauri/icons/app-icons/pride.svg new file mode 100644 index 0000000000..731ed24004 --- /dev/null +++ b/src-tauri/icons/app-icons/pride.svg @@ -0,0 +1 @@ + diff --git a/src-tauri/icons/app-icons/propeller.png b/src-tauri/icons/app-icons/propeller.png new file mode 100644 index 0000000000..446ba2bfe6 Binary files /dev/null and b/src-tauri/icons/app-icons/propeller.png differ diff --git a/src-tauri/icons/app-icons/trans-gradient.svg b/src-tauri/icons/app-icons/trans-gradient.svg new file mode 100644 index 0000000000..e1d6b7387c --- /dev/null +++ b/src-tauri/icons/app-icons/trans-gradient.svg @@ -0,0 +1 @@ + diff --git a/src-tauri/icons/app-icons/trans.png b/src-tauri/icons/app-icons/trans.png new file mode 100644 index 0000000000..87f7f10801 Binary files /dev/null and b/src-tauri/icons/app-icons/trans.png differ diff --git a/src-tauri/icons/icon.icns b/src-tauri/icons/icon.icns new file mode 100644 index 0000000000..ed9ac5e8af Binary files /dev/null and b/src-tauri/icons/icon.icns differ diff --git a/src-tauri/icons/icon.ico b/src-tauri/icons/icon.ico new file mode 100644 index 0000000000..8af2357e5a Binary files /dev/null and b/src-tauri/icons/icon.ico differ diff --git a/src-tauri/icons/icon.png b/src-tauri/icons/icon.png new file mode 100644 index 0000000000..32db07c0d1 Binary files /dev/null and b/src-tauri/icons/icon.png differ diff --git a/src-tauri/icons/ios/AppIcon-20x20@1x.png b/src-tauri/icons/ios/AppIcon-20x20@1x.png new file mode 100644 index 0000000000..d0dc8dbb64 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-20x20@1x.png differ diff --git a/src-tauri/icons/ios/AppIcon-20x20@2x-1.png b/src-tauri/icons/ios/AppIcon-20x20@2x-1.png new file mode 100644 index 0000000000..fd053f3918 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-20x20@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcon-20x20@2x.png b/src-tauri/icons/ios/AppIcon-20x20@2x.png new file mode 100644 index 0000000000..fd053f3918 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-20x20@2x.png differ diff --git a/src-tauri/icons/ios/AppIcon-20x20@3x.png b/src-tauri/icons/ios/AppIcon-20x20@3x.png new file mode 100644 index 0000000000..ec9197293a Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-20x20@3x.png differ diff --git a/src-tauri/icons/ios/AppIcon-29x29@1x.png b/src-tauri/icons/ios/AppIcon-29x29@1x.png new file mode 100644 index 0000000000..49d8569209 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-29x29@1x.png differ diff --git a/src-tauri/icons/ios/AppIcon-29x29@2x-1.png b/src-tauri/icons/ios/AppIcon-29x29@2x-1.png new file mode 100644 index 0000000000..d7bbc3f232 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-29x29@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcon-29x29@2x.png b/src-tauri/icons/ios/AppIcon-29x29@2x.png new file mode 100644 index 0000000000..d7bbc3f232 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-29x29@2x.png differ diff --git a/src-tauri/icons/ios/AppIcon-29x29@3x.png b/src-tauri/icons/ios/AppIcon-29x29@3x.png new file mode 100644 index 0000000000..d97d1f1aa9 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-29x29@3x.png differ diff --git a/src-tauri/icons/ios/AppIcon-40x40@1x.png b/src-tauri/icons/ios/AppIcon-40x40@1x.png new file mode 100644 index 0000000000..fd053f3918 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-40x40@1x.png differ diff --git a/src-tauri/icons/ios/AppIcon-40x40@2x-1.png b/src-tauri/icons/ios/AppIcon-40x40@2x-1.png new file mode 100644 index 0000000000..639d4898fb Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-40x40@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcon-40x40@2x.png b/src-tauri/icons/ios/AppIcon-40x40@2x.png new file mode 100644 index 0000000000..639d4898fb Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-40x40@2x.png differ diff --git a/src-tauri/icons/ios/AppIcon-40x40@3x.png b/src-tauri/icons/ios/AppIcon-40x40@3x.png new file mode 100644 index 0000000000..0030a8adab Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-40x40@3x.png differ diff --git a/src-tauri/icons/ios/AppIcon-512@2x.png b/src-tauri/icons/ios/AppIcon-512@2x.png new file mode 100644 index 0000000000..4bb49bd7b9 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-512@2x.png differ diff --git a/src-tauri/icons/ios/AppIcon-60x60@2x.png b/src-tauri/icons/ios/AppIcon-60x60@2x.png new file mode 100644 index 0000000000..0030a8adab Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-60x60@2x.png differ diff --git a/src-tauri/icons/ios/AppIcon-60x60@3x.png b/src-tauri/icons/ios/AppIcon-60x60@3x.png new file mode 100644 index 0000000000..fc6144024e Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-60x60@3x.png differ diff --git a/src-tauri/icons/ios/AppIcon-76x76@1x.png b/src-tauri/icons/ios/AppIcon-76x76@1x.png new file mode 100644 index 0000000000..ab85d4e6f3 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-76x76@1x.png differ diff --git a/src-tauri/icons/ios/AppIcon-76x76@2x.png b/src-tauri/icons/ios/AppIcon-76x76@2x.png new file mode 100644 index 0000000000..83d677b848 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-76x76@2x.png differ diff --git a/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png b/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png new file mode 100644 index 0000000000..01b78d3ef1 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-20x20@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-20x20@1x.png new file mode 100644 index 0000000000..76addfd583 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-20x20@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-20x20@2x-1.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-20x20@2x-1.png new file mode 100644 index 0000000000..c4be94ffb0 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-20x20@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-20x20@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-20x20@2x.png new file mode 100644 index 0000000000..c4be94ffb0 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-20x20@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-20x20@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-20x20@3x.png new file mode 100644 index 0000000000..6c87189a6a Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-20x20@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-29x29@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-29x29@1x.png new file mode 100644 index 0000000000..6489ebf820 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-29x29@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-29x29@2x-1.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-29x29@2x-1.png new file mode 100644 index 0000000000..844d35f9b8 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-29x29@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-29x29@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-29x29@2x.png new file mode 100644 index 0000000000..844d35f9b8 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-29x29@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-29x29@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-29x29@3x.png new file mode 100644 index 0000000000..53fadffc37 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-29x29@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-40x40@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-40x40@1x.png new file mode 100644 index 0000000000..c4be94ffb0 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-40x40@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-40x40@2x-1.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-40x40@2x-1.png new file mode 100644 index 0000000000..1d95f9e513 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-40x40@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-40x40@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-40x40@2x.png new file mode 100644 index 0000000000..1d95f9e513 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-40x40@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-40x40@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-40x40@3x.png new file mode 100644 index 0000000000..1afe872983 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-40x40@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-512@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-512@2x.png new file mode 100644 index 0000000000..8a798ff4bd Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-512@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-60x60@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-60x60@2x.png new file mode 100644 index 0000000000..1afe872983 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-60x60@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-60x60@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-60x60@3x.png new file mode 100644 index 0000000000..358201fce7 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-60x60@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-76x76@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-76x76@1x.png new file mode 100644 index 0000000000..ea4c252dba Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-76x76@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-76x76@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-76x76@2x.png new file mode 100644 index 0000000000..c50fa45bd7 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-76x76@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-83.5x83.5@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-83.5x83.5@2x.png new file mode 100644 index 0000000000..20ebe4644f Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/AppIcon-83.5x83.5@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/Contents.json b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/Contents.json new file mode 100644 index 0000000000..dd3b8bcc55 --- /dev/null +++ b/src-tauri/icons/ios/AppIcons.xcassets/AppIconAgender.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "images": [ + { + "size": "20x20", + "idiom": "iphone", + "filename": "AppIcon-20x20@2x.png", + "scale": "2x" + }, + { + "size": "20x20", + "idiom": "iphone", + "filename": "AppIcon-20x20@3x.png", + "scale": "3x" + }, + { + "size": "29x29", + "idiom": "iphone", + "filename": "AppIcon-29x29@2x-1.png", + "scale": "2x" + }, + { + "size": "29x29", + "idiom": "iphone", + "filename": "AppIcon-29x29@3x.png", + "scale": "3x" + }, + { + "size": "40x40", + "idiom": "iphone", + "filename": "AppIcon-40x40@2x.png", + "scale": "2x" + }, + { + "size": "40x40", + "idiom": "iphone", + "filename": "AppIcon-40x40@3x.png", + "scale": "3x" + }, + { + "size": "60x60", + "idiom": "iphone", + "filename": "AppIcon-60x60@2x.png", + "scale": "2x" + }, + { + "size": "60x60", + "idiom": "iphone", + "filename": "AppIcon-60x60@3x.png", + "scale": "3x" + }, + { + "size": "20x20", + "idiom": "ipad", + "filename": "AppIcon-20x20@1x.png", + "scale": "1x" + }, + { + "size": "20x20", + "idiom": "ipad", + "filename": "AppIcon-20x20@2x-1.png", + "scale": "2x" + }, + { + "size": "29x29", + "idiom": "ipad", + "filename": "AppIcon-29x29@1x.png", + "scale": "1x" + }, + { + "size": "29x29", + "idiom": "ipad", + "filename": "AppIcon-29x29@2x.png", + "scale": "2x" + }, + { + "size": "40x40", + "idiom": "ipad", + "filename": "AppIcon-40x40@1x.png", + "scale": "1x" + }, + { + "size": "40x40", + "idiom": "ipad", + "filename": "AppIcon-40x40@2x-1.png", + "scale": "2x" + }, + { + "size": "76x76", + "idiom": "ipad", + "filename": "AppIcon-76x76@1x.png", + "scale": "1x" + }, + { + "size": "76x76", + "idiom": "ipad", + "filename": "AppIcon-76x76@2x.png", + "scale": "2x" + }, + { + "size": "83.5x83.5", + "idiom": "ipad", + "filename": "AppIcon-83.5x83.5@2x.png", + "scale": "2x" + }, + { + "size": "1024x1024", + "idiom": "ios-marketing", + "filename": "AppIcon-512@2x.png", + "scale": "1x" + } + ], + "info": { + "version": 1, + "author": "xcode" + } +} diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-20x20@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-20x20@1x.png new file mode 100644 index 0000000000..ce0592f663 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-20x20@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-20x20@2x-1.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-20x20@2x-1.png new file mode 100644 index 0000000000..d5c1a9ce69 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-20x20@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-20x20@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-20x20@2x.png new file mode 100644 index 0000000000..d5c1a9ce69 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-20x20@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-20x20@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-20x20@3x.png new file mode 100644 index 0000000000..737c96ea26 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-20x20@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-29x29@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-29x29@1x.png new file mode 100644 index 0000000000..72bc21f077 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-29x29@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-29x29@2x-1.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-29x29@2x-1.png new file mode 100644 index 0000000000..ed0edfd719 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-29x29@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-29x29@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-29x29@2x.png new file mode 100644 index 0000000000..ed0edfd719 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-29x29@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-29x29@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-29x29@3x.png new file mode 100644 index 0000000000..19e0afc958 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-29x29@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-40x40@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-40x40@1x.png new file mode 100644 index 0000000000..d5c1a9ce69 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-40x40@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-40x40@2x-1.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-40x40@2x-1.png new file mode 100644 index 0000000000..9b2cb61c3f Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-40x40@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-40x40@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-40x40@2x.png new file mode 100644 index 0000000000..9b2cb61c3f Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-40x40@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-40x40@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-40x40@3x.png new file mode 100644 index 0000000000..ce1cc0e5b6 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-40x40@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-512@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-512@2x.png new file mode 100644 index 0000000000..17435d942c Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-512@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-60x60@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-60x60@2x.png new file mode 100644 index 0000000000..ce1cc0e5b6 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-60x60@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-60x60@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-60x60@3x.png new file mode 100644 index 0000000000..092f1db91f Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-60x60@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-76x76@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-76x76@1x.png new file mode 100644 index 0000000000..f99a04f1d5 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-76x76@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-76x76@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-76x76@2x.png new file mode 100644 index 0000000000..ebd904f57b Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-76x76@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-83.5x83.5@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-83.5x83.5@2x.png new file mode 100644 index 0000000000..2e19d729a3 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/AppIcon-83.5x83.5@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/Contents.json b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/Contents.json new file mode 100644 index 0000000000..dd3b8bcc55 --- /dev/null +++ b/src-tauri/icons/ios/AppIcons.xcassets/AppIconBisexual.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "images": [ + { + "size": "20x20", + "idiom": "iphone", + "filename": "AppIcon-20x20@2x.png", + "scale": "2x" + }, + { + "size": "20x20", + "idiom": "iphone", + "filename": "AppIcon-20x20@3x.png", + "scale": "3x" + }, + { + "size": "29x29", + "idiom": "iphone", + "filename": "AppIcon-29x29@2x-1.png", + "scale": "2x" + }, + { + "size": "29x29", + "idiom": "iphone", + "filename": "AppIcon-29x29@3x.png", + "scale": "3x" + }, + { + "size": "40x40", + "idiom": "iphone", + "filename": "AppIcon-40x40@2x.png", + "scale": "2x" + }, + { + "size": "40x40", + "idiom": "iphone", + "filename": "AppIcon-40x40@3x.png", + "scale": "3x" + }, + { + "size": "60x60", + "idiom": "iphone", + "filename": "AppIcon-60x60@2x.png", + "scale": "2x" + }, + { + "size": "60x60", + "idiom": "iphone", + "filename": "AppIcon-60x60@3x.png", + "scale": "3x" + }, + { + "size": "20x20", + "idiom": "ipad", + "filename": "AppIcon-20x20@1x.png", + "scale": "1x" + }, + { + "size": "20x20", + "idiom": "ipad", + "filename": "AppIcon-20x20@2x-1.png", + "scale": "2x" + }, + { + "size": "29x29", + "idiom": "ipad", + "filename": "AppIcon-29x29@1x.png", + "scale": "1x" + }, + { + "size": "29x29", + "idiom": "ipad", + "filename": "AppIcon-29x29@2x.png", + "scale": "2x" + }, + { + "size": "40x40", + "idiom": "ipad", + "filename": "AppIcon-40x40@1x.png", + "scale": "1x" + }, + { + "size": "40x40", + "idiom": "ipad", + "filename": "AppIcon-40x40@2x-1.png", + "scale": "2x" + }, + { + "size": "76x76", + "idiom": "ipad", + "filename": "AppIcon-76x76@1x.png", + "scale": "1x" + }, + { + "size": "76x76", + "idiom": "ipad", + "filename": "AppIcon-76x76@2x.png", + "scale": "2x" + }, + { + "size": "83.5x83.5", + "idiom": "ipad", + "filename": "AppIcon-83.5x83.5@2x.png", + "scale": "2x" + }, + { + "size": "1024x1024", + "idiom": "ios-marketing", + "filename": "AppIcon-512@2x.png", + "scale": "1x" + } + ], + "info": { + "version": 1, + "author": "xcode" + } +} diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-20x20@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-20x20@1x.png new file mode 100644 index 0000000000..fd0c89e1da Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-20x20@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-20x20@2x-1.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-20x20@2x-1.png new file mode 100644 index 0000000000..a707652d49 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-20x20@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-20x20@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-20x20@2x.png new file mode 100644 index 0000000000..a707652d49 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-20x20@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-20x20@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-20x20@3x.png new file mode 100644 index 0000000000..64e5895018 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-20x20@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-29x29@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-29x29@1x.png new file mode 100644 index 0000000000..8ad119b955 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-29x29@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-29x29@2x-1.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-29x29@2x-1.png new file mode 100644 index 0000000000..21e40818af Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-29x29@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-29x29@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-29x29@2x.png new file mode 100644 index 0000000000..21e40818af Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-29x29@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-29x29@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-29x29@3x.png new file mode 100644 index 0000000000..350035c6df Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-29x29@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-40x40@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-40x40@1x.png new file mode 100644 index 0000000000..a707652d49 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-40x40@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-40x40@2x-1.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-40x40@2x-1.png new file mode 100644 index 0000000000..37a0671449 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-40x40@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-40x40@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-40x40@2x.png new file mode 100644 index 0000000000..37a0671449 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-40x40@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-40x40@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-40x40@3x.png new file mode 100644 index 0000000000..41fc2bf898 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-40x40@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-512@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-512@2x.png new file mode 100644 index 0000000000..d5e2ac2e62 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-512@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-60x60@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-60x60@2x.png new file mode 100644 index 0000000000..41fc2bf898 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-60x60@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-60x60@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-60x60@3x.png new file mode 100644 index 0000000000..0fdedcb65c Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-60x60@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-76x76@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-76x76@1x.png new file mode 100644 index 0000000000..152cbc1886 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-76x76@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-76x76@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-76x76@2x.png new file mode 100644 index 0000000000..7ee19f672e Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-76x76@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-83.5x83.5@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-83.5x83.5@2x.png new file mode 100644 index 0000000000..db0017fc1a Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/AppIcon-83.5x83.5@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/Contents.json b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/Contents.json new file mode 100644 index 0000000000..dd3b8bcc55 --- /dev/null +++ b/src-tauri/icons/ios/AppIcons.xcassets/AppIconIntersex.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "images": [ + { + "size": "20x20", + "idiom": "iphone", + "filename": "AppIcon-20x20@2x.png", + "scale": "2x" + }, + { + "size": "20x20", + "idiom": "iphone", + "filename": "AppIcon-20x20@3x.png", + "scale": "3x" + }, + { + "size": "29x29", + "idiom": "iphone", + "filename": "AppIcon-29x29@2x-1.png", + "scale": "2x" + }, + { + "size": "29x29", + "idiom": "iphone", + "filename": "AppIcon-29x29@3x.png", + "scale": "3x" + }, + { + "size": "40x40", + "idiom": "iphone", + "filename": "AppIcon-40x40@2x.png", + "scale": "2x" + }, + { + "size": "40x40", + "idiom": "iphone", + "filename": "AppIcon-40x40@3x.png", + "scale": "3x" + }, + { + "size": "60x60", + "idiom": "iphone", + "filename": "AppIcon-60x60@2x.png", + "scale": "2x" + }, + { + "size": "60x60", + "idiom": "iphone", + "filename": "AppIcon-60x60@3x.png", + "scale": "3x" + }, + { + "size": "20x20", + "idiom": "ipad", + "filename": "AppIcon-20x20@1x.png", + "scale": "1x" + }, + { + "size": "20x20", + "idiom": "ipad", + "filename": "AppIcon-20x20@2x-1.png", + "scale": "2x" + }, + { + "size": "29x29", + "idiom": "ipad", + "filename": "AppIcon-29x29@1x.png", + "scale": "1x" + }, + { + "size": "29x29", + "idiom": "ipad", + "filename": "AppIcon-29x29@2x.png", + "scale": "2x" + }, + { + "size": "40x40", + "idiom": "ipad", + "filename": "AppIcon-40x40@1x.png", + "scale": "1x" + }, + { + "size": "40x40", + "idiom": "ipad", + "filename": "AppIcon-40x40@2x-1.png", + "scale": "2x" + }, + { + "size": "76x76", + "idiom": "ipad", + "filename": "AppIcon-76x76@1x.png", + "scale": "1x" + }, + { + "size": "76x76", + "idiom": "ipad", + "filename": "AppIcon-76x76@2x.png", + "scale": "2x" + }, + { + "size": "83.5x83.5", + "idiom": "ipad", + "filename": "AppIcon-83.5x83.5@2x.png", + "scale": "2x" + }, + { + "size": "1024x1024", + "idiom": "ios-marketing", + "filename": "AppIcon-512@2x.png", + "scale": "1x" + } + ], + "info": { + "version": 1, + "author": "xcode" + } +} diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-20x20@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-20x20@1x.png new file mode 100644 index 0000000000..0c03f3844c Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-20x20@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-20x20@2x-1.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-20x20@2x-1.png new file mode 100644 index 0000000000..0e5bec470a Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-20x20@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-20x20@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-20x20@2x.png new file mode 100644 index 0000000000..0e5bec470a Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-20x20@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-20x20@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-20x20@3x.png new file mode 100644 index 0000000000..69c1c4c1a8 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-20x20@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-29x29@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-29x29@1x.png new file mode 100644 index 0000000000..ce374d4062 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-29x29@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-29x29@2x-1.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-29x29@2x-1.png new file mode 100644 index 0000000000..4cf819f189 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-29x29@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-29x29@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-29x29@2x.png new file mode 100644 index 0000000000..4cf819f189 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-29x29@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-29x29@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-29x29@3x.png new file mode 100644 index 0000000000..c759633ec0 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-29x29@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-40x40@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-40x40@1x.png new file mode 100644 index 0000000000..0e5bec470a Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-40x40@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-40x40@2x-1.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-40x40@2x-1.png new file mode 100644 index 0000000000..326f93ffde Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-40x40@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-40x40@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-40x40@2x.png new file mode 100644 index 0000000000..326f93ffde Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-40x40@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-40x40@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-40x40@3x.png new file mode 100644 index 0000000000..1a21693685 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-40x40@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-512@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-512@2x.png new file mode 100644 index 0000000000..95fd730d2e Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-512@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-60x60@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-60x60@2x.png new file mode 100644 index 0000000000..1a21693685 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-60x60@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-60x60@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-60x60@3x.png new file mode 100644 index 0000000000..6977390d9c Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-60x60@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-76x76@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-76x76@1x.png new file mode 100644 index 0000000000..c762fc9947 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-76x76@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-76x76@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-76x76@2x.png new file mode 100644 index 0000000000..6b95e40480 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-76x76@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-83.5x83.5@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-83.5x83.5@2x.png new file mode 100644 index 0000000000..2f8303c87d Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/AppIcon-83.5x83.5@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/Contents.json b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/Contents.json new file mode 100644 index 0000000000..dd3b8bcc55 --- /dev/null +++ b/src-tauri/icons/ios/AppIcons.xcassets/AppIconLesbian.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "images": [ + { + "size": "20x20", + "idiom": "iphone", + "filename": "AppIcon-20x20@2x.png", + "scale": "2x" + }, + { + "size": "20x20", + "idiom": "iphone", + "filename": "AppIcon-20x20@3x.png", + "scale": "3x" + }, + { + "size": "29x29", + "idiom": "iphone", + "filename": "AppIcon-29x29@2x-1.png", + "scale": "2x" + }, + { + "size": "29x29", + "idiom": "iphone", + "filename": "AppIcon-29x29@3x.png", + "scale": "3x" + }, + { + "size": "40x40", + "idiom": "iphone", + "filename": "AppIcon-40x40@2x.png", + "scale": "2x" + }, + { + "size": "40x40", + "idiom": "iphone", + "filename": "AppIcon-40x40@3x.png", + "scale": "3x" + }, + { + "size": "60x60", + "idiom": "iphone", + "filename": "AppIcon-60x60@2x.png", + "scale": "2x" + }, + { + "size": "60x60", + "idiom": "iphone", + "filename": "AppIcon-60x60@3x.png", + "scale": "3x" + }, + { + "size": "20x20", + "idiom": "ipad", + "filename": "AppIcon-20x20@1x.png", + "scale": "1x" + }, + { + "size": "20x20", + "idiom": "ipad", + "filename": "AppIcon-20x20@2x-1.png", + "scale": "2x" + }, + { + "size": "29x29", + "idiom": "ipad", + "filename": "AppIcon-29x29@1x.png", + "scale": "1x" + }, + { + "size": "29x29", + "idiom": "ipad", + "filename": "AppIcon-29x29@2x.png", + "scale": "2x" + }, + { + "size": "40x40", + "idiom": "ipad", + "filename": "AppIcon-40x40@1x.png", + "scale": "1x" + }, + { + "size": "40x40", + "idiom": "ipad", + "filename": "AppIcon-40x40@2x-1.png", + "scale": "2x" + }, + { + "size": "76x76", + "idiom": "ipad", + "filename": "AppIcon-76x76@1x.png", + "scale": "1x" + }, + { + "size": "76x76", + "idiom": "ipad", + "filename": "AppIcon-76x76@2x.png", + "scale": "2x" + }, + { + "size": "83.5x83.5", + "idiom": "ipad", + "filename": "AppIcon-83.5x83.5@2x.png", + "scale": "2x" + }, + { + "size": "1024x1024", + "idiom": "ios-marketing", + "filename": "AppIcon-512@2x.png", + "scale": "1x" + } + ], + "info": { + "version": 1, + "author": "xcode" + } +} diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-20x20@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-20x20@1x.png new file mode 100644 index 0000000000..51e99c7241 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-20x20@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-20x20@2x-1.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-20x20@2x-1.png new file mode 100644 index 0000000000..a5262206fd Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-20x20@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-20x20@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-20x20@2x.png new file mode 100644 index 0000000000..a5262206fd Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-20x20@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-20x20@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-20x20@3x.png new file mode 100644 index 0000000000..402d882ee3 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-20x20@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-29x29@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-29x29@1x.png new file mode 100644 index 0000000000..b86f9b12f1 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-29x29@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-29x29@2x-1.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-29x29@2x-1.png new file mode 100644 index 0000000000..75dccd4bac Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-29x29@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-29x29@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-29x29@2x.png new file mode 100644 index 0000000000..75dccd4bac Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-29x29@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-29x29@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-29x29@3x.png new file mode 100644 index 0000000000..1bc154d343 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-29x29@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-40x40@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-40x40@1x.png new file mode 100644 index 0000000000..a5262206fd Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-40x40@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-40x40@2x-1.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-40x40@2x-1.png new file mode 100644 index 0000000000..bee1126139 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-40x40@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-40x40@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-40x40@2x.png new file mode 100644 index 0000000000..bee1126139 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-40x40@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-40x40@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-40x40@3x.png new file mode 100644 index 0000000000..a6f485c032 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-40x40@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-512@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-512@2x.png new file mode 100644 index 0000000000..25bdba1461 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-512@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-60x60@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-60x60@2x.png new file mode 100644 index 0000000000..a6f485c032 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-60x60@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-60x60@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-60x60@3x.png new file mode 100644 index 0000000000..59c7c7cd23 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-60x60@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-76x76@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-76x76@1x.png new file mode 100644 index 0000000000..e3564ae697 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-76x76@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-76x76@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-76x76@2x.png new file mode 100644 index 0000000000..69f07e3b9d Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-76x76@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-83.5x83.5@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-83.5x83.5@2x.png new file mode 100644 index 0000000000..cf614b9a5e Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/AppIcon-83.5x83.5@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/Contents.json b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/Contents.json new file mode 100644 index 0000000000..dd3b8bcc55 --- /dev/null +++ b/src-tauri/icons/ios/AppIcons.xcassets/AppIconMlm.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "images": [ + { + "size": "20x20", + "idiom": "iphone", + "filename": "AppIcon-20x20@2x.png", + "scale": "2x" + }, + { + "size": "20x20", + "idiom": "iphone", + "filename": "AppIcon-20x20@3x.png", + "scale": "3x" + }, + { + "size": "29x29", + "idiom": "iphone", + "filename": "AppIcon-29x29@2x-1.png", + "scale": "2x" + }, + { + "size": "29x29", + "idiom": "iphone", + "filename": "AppIcon-29x29@3x.png", + "scale": "3x" + }, + { + "size": "40x40", + "idiom": "iphone", + "filename": "AppIcon-40x40@2x.png", + "scale": "2x" + }, + { + "size": "40x40", + "idiom": "iphone", + "filename": "AppIcon-40x40@3x.png", + "scale": "3x" + }, + { + "size": "60x60", + "idiom": "iphone", + "filename": "AppIcon-60x60@2x.png", + "scale": "2x" + }, + { + "size": "60x60", + "idiom": "iphone", + "filename": "AppIcon-60x60@3x.png", + "scale": "3x" + }, + { + "size": "20x20", + "idiom": "ipad", + "filename": "AppIcon-20x20@1x.png", + "scale": "1x" + }, + { + "size": "20x20", + "idiom": "ipad", + "filename": "AppIcon-20x20@2x-1.png", + "scale": "2x" + }, + { + "size": "29x29", + "idiom": "ipad", + "filename": "AppIcon-29x29@1x.png", + "scale": "1x" + }, + { + "size": "29x29", + "idiom": "ipad", + "filename": "AppIcon-29x29@2x.png", + "scale": "2x" + }, + { + "size": "40x40", + "idiom": "ipad", + "filename": "AppIcon-40x40@1x.png", + "scale": "1x" + }, + { + "size": "40x40", + "idiom": "ipad", + "filename": "AppIcon-40x40@2x-1.png", + "scale": "2x" + }, + { + "size": "76x76", + "idiom": "ipad", + "filename": "AppIcon-76x76@1x.png", + "scale": "1x" + }, + { + "size": "76x76", + "idiom": "ipad", + "filename": "AppIcon-76x76@2x.png", + "scale": "2x" + }, + { + "size": "83.5x83.5", + "idiom": "ipad", + "filename": "AppIcon-83.5x83.5@2x.png", + "scale": "2x" + }, + { + "size": "1024x1024", + "idiom": "ios-marketing", + "filename": "AppIcon-512@2x.png", + "scale": "1x" + } + ], + "info": { + "version": 1, + "author": "xcode" + } +} diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-20x20@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-20x20@1x.png new file mode 100644 index 0000000000..fa62a7abef Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-20x20@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-20x20@2x-1.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-20x20@2x-1.png new file mode 100644 index 0000000000..7f05f9bd07 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-20x20@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-20x20@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-20x20@2x.png new file mode 100644 index 0000000000..7f05f9bd07 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-20x20@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-20x20@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-20x20@3x.png new file mode 100644 index 0000000000..aeac52198f Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-20x20@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-29x29@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-29x29@1x.png new file mode 100644 index 0000000000..1eafa1677a Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-29x29@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-29x29@2x-1.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-29x29@2x-1.png new file mode 100644 index 0000000000..6165af9027 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-29x29@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-29x29@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-29x29@2x.png new file mode 100644 index 0000000000..6165af9027 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-29x29@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-29x29@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-29x29@3x.png new file mode 100644 index 0000000000..272bb4e492 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-29x29@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-40x40@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-40x40@1x.png new file mode 100644 index 0000000000..7f05f9bd07 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-40x40@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-40x40@2x-1.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-40x40@2x-1.png new file mode 100644 index 0000000000..584f386f75 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-40x40@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-40x40@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-40x40@2x.png new file mode 100644 index 0000000000..584f386f75 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-40x40@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-40x40@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-40x40@3x.png new file mode 100644 index 0000000000..16a341163a Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-40x40@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-512@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-512@2x.png new file mode 100644 index 0000000000..d0365a7412 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-512@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-60x60@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-60x60@2x.png new file mode 100644 index 0000000000..16a341163a Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-60x60@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-60x60@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-60x60@3x.png new file mode 100644 index 0000000000..e05b6a927d Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-60x60@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-76x76@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-76x76@1x.png new file mode 100644 index 0000000000..d879b763f5 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-76x76@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-76x76@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-76x76@2x.png new file mode 100644 index 0000000000..91e89b78f7 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-76x76@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-83.5x83.5@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-83.5x83.5@2x.png new file mode 100644 index 0000000000..3ce5a9c8b2 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/AppIcon-83.5x83.5@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/Contents.json b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/Contents.json new file mode 100644 index 0000000000..dd3b8bcc55 --- /dev/null +++ b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPride.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "images": [ + { + "size": "20x20", + "idiom": "iphone", + "filename": "AppIcon-20x20@2x.png", + "scale": "2x" + }, + { + "size": "20x20", + "idiom": "iphone", + "filename": "AppIcon-20x20@3x.png", + "scale": "3x" + }, + { + "size": "29x29", + "idiom": "iphone", + "filename": "AppIcon-29x29@2x-1.png", + "scale": "2x" + }, + { + "size": "29x29", + "idiom": "iphone", + "filename": "AppIcon-29x29@3x.png", + "scale": "3x" + }, + { + "size": "40x40", + "idiom": "iphone", + "filename": "AppIcon-40x40@2x.png", + "scale": "2x" + }, + { + "size": "40x40", + "idiom": "iphone", + "filename": "AppIcon-40x40@3x.png", + "scale": "3x" + }, + { + "size": "60x60", + "idiom": "iphone", + "filename": "AppIcon-60x60@2x.png", + "scale": "2x" + }, + { + "size": "60x60", + "idiom": "iphone", + "filename": "AppIcon-60x60@3x.png", + "scale": "3x" + }, + { + "size": "20x20", + "idiom": "ipad", + "filename": "AppIcon-20x20@1x.png", + "scale": "1x" + }, + { + "size": "20x20", + "idiom": "ipad", + "filename": "AppIcon-20x20@2x-1.png", + "scale": "2x" + }, + { + "size": "29x29", + "idiom": "ipad", + "filename": "AppIcon-29x29@1x.png", + "scale": "1x" + }, + { + "size": "29x29", + "idiom": "ipad", + "filename": "AppIcon-29x29@2x.png", + "scale": "2x" + }, + { + "size": "40x40", + "idiom": "ipad", + "filename": "AppIcon-40x40@1x.png", + "scale": "1x" + }, + { + "size": "40x40", + "idiom": "ipad", + "filename": "AppIcon-40x40@2x-1.png", + "scale": "2x" + }, + { + "size": "76x76", + "idiom": "ipad", + "filename": "AppIcon-76x76@1x.png", + "scale": "1x" + }, + { + "size": "76x76", + "idiom": "ipad", + "filename": "AppIcon-76x76@2x.png", + "scale": "2x" + }, + { + "size": "83.5x83.5", + "idiom": "ipad", + "filename": "AppIcon-83.5x83.5@2x.png", + "scale": "2x" + }, + { + "size": "1024x1024", + "idiom": "ios-marketing", + "filename": "AppIcon-512@2x.png", + "scale": "1x" + } + ], + "info": { + "version": 1, + "author": "xcode" + } +} diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-20x20@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-20x20@1x.png new file mode 100644 index 0000000000..b8c97eb422 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-20x20@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-20x20@2x-1.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-20x20@2x-1.png new file mode 100644 index 0000000000..79789cf157 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-20x20@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-20x20@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-20x20@2x.png new file mode 100644 index 0000000000..79789cf157 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-20x20@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-20x20@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-20x20@3x.png new file mode 100644 index 0000000000..54448f1bc5 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-20x20@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-29x29@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-29x29@1x.png new file mode 100644 index 0000000000..f55b6c9aec Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-29x29@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-29x29@2x-1.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-29x29@2x-1.png new file mode 100644 index 0000000000..4b36cc3950 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-29x29@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-29x29@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-29x29@2x.png new file mode 100644 index 0000000000..4b36cc3950 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-29x29@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-29x29@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-29x29@3x.png new file mode 100644 index 0000000000..4e6f5a055d Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-29x29@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-40x40@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-40x40@1x.png new file mode 100644 index 0000000000..79789cf157 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-40x40@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-40x40@2x-1.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-40x40@2x-1.png new file mode 100644 index 0000000000..4ea7c2c5ba Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-40x40@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-40x40@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-40x40@2x.png new file mode 100644 index 0000000000..4ea7c2c5ba Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-40x40@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-40x40@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-40x40@3x.png new file mode 100644 index 0000000000..7f2a6c9f61 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-40x40@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-512@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-512@2x.png new file mode 100644 index 0000000000..b61fab52f3 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-512@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-60x60@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-60x60@2x.png new file mode 100644 index 0000000000..7f2a6c9f61 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-60x60@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-60x60@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-60x60@3x.png new file mode 100644 index 0000000000..a15a75ee96 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-60x60@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-76x76@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-76x76@1x.png new file mode 100644 index 0000000000..c1c5d8d131 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-76x76@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-76x76@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-76x76@2x.png new file mode 100644 index 0000000000..161dc493f4 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-76x76@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-83.5x83.5@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-83.5x83.5@2x.png new file mode 100644 index 0000000000..59471a6fec Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/AppIcon-83.5x83.5@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/Contents.json b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/Contents.json new file mode 100644 index 0000000000..dd3b8bcc55 --- /dev/null +++ b/src-tauri/icons/ios/AppIcons.xcassets/AppIconPropeller.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "images": [ + { + "size": "20x20", + "idiom": "iphone", + "filename": "AppIcon-20x20@2x.png", + "scale": "2x" + }, + { + "size": "20x20", + "idiom": "iphone", + "filename": "AppIcon-20x20@3x.png", + "scale": "3x" + }, + { + "size": "29x29", + "idiom": "iphone", + "filename": "AppIcon-29x29@2x-1.png", + "scale": "2x" + }, + { + "size": "29x29", + "idiom": "iphone", + "filename": "AppIcon-29x29@3x.png", + "scale": "3x" + }, + { + "size": "40x40", + "idiom": "iphone", + "filename": "AppIcon-40x40@2x.png", + "scale": "2x" + }, + { + "size": "40x40", + "idiom": "iphone", + "filename": "AppIcon-40x40@3x.png", + "scale": "3x" + }, + { + "size": "60x60", + "idiom": "iphone", + "filename": "AppIcon-60x60@2x.png", + "scale": "2x" + }, + { + "size": "60x60", + "idiom": "iphone", + "filename": "AppIcon-60x60@3x.png", + "scale": "3x" + }, + { + "size": "20x20", + "idiom": "ipad", + "filename": "AppIcon-20x20@1x.png", + "scale": "1x" + }, + { + "size": "20x20", + "idiom": "ipad", + "filename": "AppIcon-20x20@2x-1.png", + "scale": "2x" + }, + { + "size": "29x29", + "idiom": "ipad", + "filename": "AppIcon-29x29@1x.png", + "scale": "1x" + }, + { + "size": "29x29", + "idiom": "ipad", + "filename": "AppIcon-29x29@2x.png", + "scale": "2x" + }, + { + "size": "40x40", + "idiom": "ipad", + "filename": "AppIcon-40x40@1x.png", + "scale": "1x" + }, + { + "size": "40x40", + "idiom": "ipad", + "filename": "AppIcon-40x40@2x-1.png", + "scale": "2x" + }, + { + "size": "76x76", + "idiom": "ipad", + "filename": "AppIcon-76x76@1x.png", + "scale": "1x" + }, + { + "size": "76x76", + "idiom": "ipad", + "filename": "AppIcon-76x76@2x.png", + "scale": "2x" + }, + { + "size": "83.5x83.5", + "idiom": "ipad", + "filename": "AppIcon-83.5x83.5@2x.png", + "scale": "2x" + }, + { + "size": "1024x1024", + "idiom": "ios-marketing", + "filename": "AppIcon-512@2x.png", + "scale": "1x" + } + ], + "info": { + "version": 1, + "author": "xcode" + } +} diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-20x20@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-20x20@1x.png new file mode 100644 index 0000000000..2173a9d4e1 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-20x20@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-20x20@2x-1.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-20x20@2x-1.png new file mode 100644 index 0000000000..2ca94d1c85 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-20x20@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-20x20@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-20x20@2x.png new file mode 100644 index 0000000000..2ca94d1c85 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-20x20@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-20x20@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-20x20@3x.png new file mode 100644 index 0000000000..667ef8ab5c Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-20x20@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-29x29@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-29x29@1x.png new file mode 100644 index 0000000000..f2af93a6af Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-29x29@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-29x29@2x-1.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-29x29@2x-1.png new file mode 100644 index 0000000000..46549374d7 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-29x29@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-29x29@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-29x29@2x.png new file mode 100644 index 0000000000..46549374d7 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-29x29@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-29x29@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-29x29@3x.png new file mode 100644 index 0000000000..f9c988b3fc Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-29x29@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-40x40@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-40x40@1x.png new file mode 100644 index 0000000000..2ca94d1c85 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-40x40@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-40x40@2x-1.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-40x40@2x-1.png new file mode 100644 index 0000000000..47305deb54 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-40x40@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-40x40@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-40x40@2x.png new file mode 100644 index 0000000000..47305deb54 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-40x40@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-40x40@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-40x40@3x.png new file mode 100644 index 0000000000..388e97bbc9 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-40x40@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-512@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-512@2x.png new file mode 100644 index 0000000000..161f0f8890 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-512@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-60x60@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-60x60@2x.png new file mode 100644 index 0000000000..388e97bbc9 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-60x60@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-60x60@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-60x60@3x.png new file mode 100644 index 0000000000..357f6c2d9e Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-60x60@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-76x76@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-76x76@1x.png new file mode 100644 index 0000000000..632af85199 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-76x76@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-76x76@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-76x76@2x.png new file mode 100644 index 0000000000..d89ed32076 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-76x76@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-83.5x83.5@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-83.5x83.5@2x.png new file mode 100644 index 0000000000..1a4f976f17 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/AppIcon-83.5x83.5@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/Contents.json b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/Contents.json new file mode 100644 index 0000000000..dd3b8bcc55 --- /dev/null +++ b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTrans.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "images": [ + { + "size": "20x20", + "idiom": "iphone", + "filename": "AppIcon-20x20@2x.png", + "scale": "2x" + }, + { + "size": "20x20", + "idiom": "iphone", + "filename": "AppIcon-20x20@3x.png", + "scale": "3x" + }, + { + "size": "29x29", + "idiom": "iphone", + "filename": "AppIcon-29x29@2x-1.png", + "scale": "2x" + }, + { + "size": "29x29", + "idiom": "iphone", + "filename": "AppIcon-29x29@3x.png", + "scale": "3x" + }, + { + "size": "40x40", + "idiom": "iphone", + "filename": "AppIcon-40x40@2x.png", + "scale": "2x" + }, + { + "size": "40x40", + "idiom": "iphone", + "filename": "AppIcon-40x40@3x.png", + "scale": "3x" + }, + { + "size": "60x60", + "idiom": "iphone", + "filename": "AppIcon-60x60@2x.png", + "scale": "2x" + }, + { + "size": "60x60", + "idiom": "iphone", + "filename": "AppIcon-60x60@3x.png", + "scale": "3x" + }, + { + "size": "20x20", + "idiom": "ipad", + "filename": "AppIcon-20x20@1x.png", + "scale": "1x" + }, + { + "size": "20x20", + "idiom": "ipad", + "filename": "AppIcon-20x20@2x-1.png", + "scale": "2x" + }, + { + "size": "29x29", + "idiom": "ipad", + "filename": "AppIcon-29x29@1x.png", + "scale": "1x" + }, + { + "size": "29x29", + "idiom": "ipad", + "filename": "AppIcon-29x29@2x.png", + "scale": "2x" + }, + { + "size": "40x40", + "idiom": "ipad", + "filename": "AppIcon-40x40@1x.png", + "scale": "1x" + }, + { + "size": "40x40", + "idiom": "ipad", + "filename": "AppIcon-40x40@2x-1.png", + "scale": "2x" + }, + { + "size": "76x76", + "idiom": "ipad", + "filename": "AppIcon-76x76@1x.png", + "scale": "1x" + }, + { + "size": "76x76", + "idiom": "ipad", + "filename": "AppIcon-76x76@2x.png", + "scale": "2x" + }, + { + "size": "83.5x83.5", + "idiom": "ipad", + "filename": "AppIcon-83.5x83.5@2x.png", + "scale": "2x" + }, + { + "size": "1024x1024", + "idiom": "ios-marketing", + "filename": "AppIcon-512@2x.png", + "scale": "1x" + } + ], + "info": { + "version": 1, + "author": "xcode" + } +} diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-20x20@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-20x20@1x.png new file mode 100644 index 0000000000..4b8113b198 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-20x20@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-20x20@2x-1.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-20x20@2x-1.png new file mode 100644 index 0000000000..864e65a2bb Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-20x20@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-20x20@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-20x20@2x.png new file mode 100644 index 0000000000..864e65a2bb Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-20x20@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-20x20@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-20x20@3x.png new file mode 100644 index 0000000000..27e4784b10 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-20x20@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-29x29@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-29x29@1x.png new file mode 100644 index 0000000000..9d256cbc61 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-29x29@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-29x29@2x-1.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-29x29@2x-1.png new file mode 100644 index 0000000000..79aa3c08d7 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-29x29@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-29x29@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-29x29@2x.png new file mode 100644 index 0000000000..79aa3c08d7 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-29x29@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-29x29@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-29x29@3x.png new file mode 100644 index 0000000000..0dd4272af7 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-29x29@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-40x40@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-40x40@1x.png new file mode 100644 index 0000000000..864e65a2bb Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-40x40@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-40x40@2x-1.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-40x40@2x-1.png new file mode 100644 index 0000000000..58c5009fe5 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-40x40@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-40x40@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-40x40@2x.png new file mode 100644 index 0000000000..58c5009fe5 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-40x40@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-40x40@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-40x40@3x.png new file mode 100644 index 0000000000..3b71203a8d Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-40x40@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-512@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-512@2x.png new file mode 100644 index 0000000000..ec15f1eb7c Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-512@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-60x60@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-60x60@2x.png new file mode 100644 index 0000000000..3b71203a8d Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-60x60@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-60x60@3x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-60x60@3x.png new file mode 100644 index 0000000000..e0515c5d17 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-60x60@3x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-76x76@1x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-76x76@1x.png new file mode 100644 index 0000000000..e1059c533a Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-76x76@1x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-76x76@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-76x76@2x.png new file mode 100644 index 0000000000..bae11e1687 Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-76x76@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-83.5x83.5@2x.png b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-83.5x83.5@2x.png new file mode 100644 index 0000000000..4446bdedcf Binary files /dev/null and b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/AppIcon-83.5x83.5@2x.png differ diff --git a/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/Contents.json b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/Contents.json new file mode 100644 index 0000000000..dd3b8bcc55 --- /dev/null +++ b/src-tauri/icons/ios/AppIcons.xcassets/AppIconTransgradient.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "images": [ + { + "size": "20x20", + "idiom": "iphone", + "filename": "AppIcon-20x20@2x.png", + "scale": "2x" + }, + { + "size": "20x20", + "idiom": "iphone", + "filename": "AppIcon-20x20@3x.png", + "scale": "3x" + }, + { + "size": "29x29", + "idiom": "iphone", + "filename": "AppIcon-29x29@2x-1.png", + "scale": "2x" + }, + { + "size": "29x29", + "idiom": "iphone", + "filename": "AppIcon-29x29@3x.png", + "scale": "3x" + }, + { + "size": "40x40", + "idiom": "iphone", + "filename": "AppIcon-40x40@2x.png", + "scale": "2x" + }, + { + "size": "40x40", + "idiom": "iphone", + "filename": "AppIcon-40x40@3x.png", + "scale": "3x" + }, + { + "size": "60x60", + "idiom": "iphone", + "filename": "AppIcon-60x60@2x.png", + "scale": "2x" + }, + { + "size": "60x60", + "idiom": "iphone", + "filename": "AppIcon-60x60@3x.png", + "scale": "3x" + }, + { + "size": "20x20", + "idiom": "ipad", + "filename": "AppIcon-20x20@1x.png", + "scale": "1x" + }, + { + "size": "20x20", + "idiom": "ipad", + "filename": "AppIcon-20x20@2x-1.png", + "scale": "2x" + }, + { + "size": "29x29", + "idiom": "ipad", + "filename": "AppIcon-29x29@1x.png", + "scale": "1x" + }, + { + "size": "29x29", + "idiom": "ipad", + "filename": "AppIcon-29x29@2x.png", + "scale": "2x" + }, + { + "size": "40x40", + "idiom": "ipad", + "filename": "AppIcon-40x40@1x.png", + "scale": "1x" + }, + { + "size": "40x40", + "idiom": "ipad", + "filename": "AppIcon-40x40@2x-1.png", + "scale": "2x" + }, + { + "size": "76x76", + "idiom": "ipad", + "filename": "AppIcon-76x76@1x.png", + "scale": "1x" + }, + { + "size": "76x76", + "idiom": "ipad", + "filename": "AppIcon-76x76@2x.png", + "scale": "2x" + }, + { + "size": "83.5x83.5", + "idiom": "ipad", + "filename": "AppIcon-83.5x83.5@2x.png", + "scale": "2x" + }, + { + "size": "1024x1024", + "idiom": "ios-marketing", + "filename": "AppIcon-512@2x.png", + "scale": "1x" + } + ], + "info": { + "version": 1, + "author": "xcode" + } +} diff --git a/src-tauri/icons/ios/AppIcons.xcassets/Contents.json b/src-tauri/icons/ios/AppIcons.xcassets/Contents.json new file mode 100644 index 0000000000..ac502da0a1 --- /dev/null +++ b/src-tauri/icons/ios/AppIcons.xcassets/Contents.json @@ -0,0 +1 @@ +{ "info": { "author": "xcode", "version": 1 } } diff --git a/src-tauri/ios-entitlements/sable.debug.entitlements b/src-tauri/ios-entitlements/sable.debug.entitlements new file mode 100644 index 0000000000..6631ffa6f2 --- /dev/null +++ b/src-tauri/ios-entitlements/sable.debug.entitlements @@ -0,0 +1,6 @@ + + + + + + diff --git a/src-tauri/ios-entitlements/sable.release.entitlements b/src-tauri/ios-entitlements/sable.release.entitlements new file mode 100644 index 0000000000..723ff878de --- /dev/null +++ b/src-tauri/ios-entitlements/sable.release.entitlements @@ -0,0 +1,12 @@ + + + + + aps-environment + development + com.apple.security.application-groups + + group.moe.sable.client + + + diff --git a/src-tauri/ios-entitlements/sable.share-extension.debug.entitlements b/src-tauri/ios-entitlements/sable.share-extension.debug.entitlements new file mode 100644 index 0000000000..6631ffa6f2 --- /dev/null +++ b/src-tauri/ios-entitlements/sable.share-extension.debug.entitlements @@ -0,0 +1,6 @@ + + + + + + diff --git a/src-tauri/ios-entitlements/sable.share-extension.release.entitlements b/src-tauri/ios-entitlements/sable.share-extension.release.entitlements new file mode 100644 index 0000000000..6707d84a11 --- /dev/null +++ b/src-tauri/ios-entitlements/sable.share-extension.release.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.moe.sable.client + + + diff --git a/src-tauri/ios-project.yml b/src-tauri/ios-project.yml new file mode 100644 index 0000000000..9d10cbc86a --- /dev/null +++ b/src-tauri/ios-project.yml @@ -0,0 +1,229 @@ +name: {{app.name}} +options: + bundleIdPrefix: {{app.identifier}} + deploymentTarget: + iOS: {{apple.ios-version}} +fileGroups: [{{join file-groups}}] +configs: + debug: debug + release: release +settingGroups: + app: + base: + PRODUCT_NAME: {{app.stylized-name}} + PRODUCT_BUNDLE_IDENTIFIER: {{app.identifier}} + {{#if apple.development-team}} + DEVELOPMENT_TEAM: {{apple.development-team}} + {{/if}} +targetTemplates: + app: + type: application + sources: + - path: Sources + scheme: + environmentVariables: + RUST_BACKTRACE: full + RUST_LOG: info + settings: + groups: [app] +targets: + share-extension: + type: app-extension + platform: iOS + sources: + - path: ../../share-extension-ios + info: + path: share-extension/Info.plist + properties: + CFBundleDisplayName: {{app.stylized-name}} + CFBundleShortVersionString: {{apple.bundle-version-short}} + CFBundleVersion: "{{apple.bundle-version}}" + NSExtension: + NSExtensionPointIdentifier: com.apple.share-services + NSExtensionPrincipalClass: $(PRODUCT_MODULE_NAME).ShareViewController + NSExtensionAttributes: + NSExtensionActivationRule: + NSExtensionActivationSupportsText: true + NSExtensionActivationSupportsWebURLWithMaxCount: 1 + NSExtensionActivationSupportsImageWithMaxCount: 10 + NSExtensionActivationSupportsMovieWithMaxCount: 1 + NSExtensionActivationSupportsFileWithMaxCount: 10 + entitlements: + path: share-extension/share-extension.entitlements + properties: + com.apple.security.application-groups: + - group.{{app.identifier}} + settings: + base: + PRODUCT_NAME: ShareExtension + PRODUCT_BUNDLE_IDENTIFIER: {{app.identifier}}.share-extension + # APPLICATION_EXTENSION_API_ONLY must stay unset: NO fails Xcode's + # validation, YES breaks UIApplication.open in ShareViewController. + SKIP_INSTALL: YES + DEVELOPMENT_TEAM: PK66NJM372 + {{app.name}}_iOS: + type: application + platform: iOS + sources: + - path: Sources + - path: Assets.xcassets + - path: ../../icons/ios/AppIcons.xcassets + - path: Externals + buildPhase: none + - path: {{app.name}}_iOS + - path: {{app.asset-dir}} + buildPhase: resources + type: folder + {{~#each asset-catalogs}} + - {{prefix-path this}}{{/each}} + {{~#each ios-additional-targets}} + - path: {{prefix-path this}}{{/each}} + - path: LaunchScreen.storyboard + - path: ../../ios/PrivacyInfo.xcprivacy + buildPhase: resources + # UNNotificationSound resolves names from the bundle root, not tauri's assets/ dir. + - path: ../../resources/notification.caf + buildPhase: resources + - path: ../../resources/invite.caf + buildPhase: resources + info: + path: {{app.name}}_iOS/Info.plist + properties: + LSRequiresIPhoneOS: true + UILaunchStoryboardName: LaunchScreen + UIBackgroundModes: [audio, voip] + NSMicrophoneUsageDescription: Sable uses your microphone for calls. + NSCameraUsageDescription: Sable uses your camera for video calls. + UIRequiredDeviceCapabilities: [arm64, metal] + UISupportedInterfaceOrientations: + - UIInterfaceOrientationPortrait + - UIInterfaceOrientationLandscapeLeft + - UIInterfaceOrientationLandscapeRight + UISupportedInterfaceOrientations~ipad: + - UIInterfaceOrientationPortrait + - UIInterfaceOrientationPortraitUpsideDown + - UIInterfaceOrientationLandscapeLeft + - UIInterfaceOrientationLandscapeRight + CFBundleShortVersionString: {{apple.bundle-version-short}} + CFBundleVersion: "{{apple.bundle-version}}" + NSPhotoLibraryAddUsageDescription: Sable saves images you download to your photo library. + # Mirrors plugins.deep-link in tauri.conf.json: the plugin only patches + # these in when cargo reruns its build script, so a fresh `tauri ios + # init` silently loses them unless they live here. + CFBundleURLTypes: + - CFBundleURLName: sable + CFBundleURLSchemes: + - sable + - moe.sable.app + {{~#each apple.plist-pairs}} + {{this.key}}: {{this.value}}{{/each}} + entitlements: + path: {{app.name}}_iOS/{{app.name}}_iOS.entitlements + scheme: + environmentVariables: + RUST_BACKTRACE: full + RUST_LOG: info + {{~#if ios-command-line-arguments}} + commandLineArguments: + {{~#each ios-command-line-arguments}} + "{{this}}": true + {{/each}}{{~/if}} + settings: + base: + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS: YES + ENABLE_BITCODE: false + ARCHS: [{{join ios-valid-archs}}] + VALID_ARCHS: {{~#each ios-valid-archs}} {{this}} {{/each}} + LIBRARY_SEARCH_PATHS[arch=x86_64]: $(inherited) $(PROJECT_DIR)/Externals/x86_64/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(DEVELOPER_DIR)/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/$(PLATFORM_NAME) $(DEVELOPER_DIR)/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/$(PLATFORM_NAME) + LIBRARY_SEARCH_PATHS[arch=arm64]: $(inherited) $(PROJECT_DIR)/Externals/arm64/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(DEVELOPER_DIR)/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/$(PLATFORM_NAME) $(DEVELOPER_DIR)/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/$(PLATFORM_NAME) + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES: true + EXCLUDED_ARCHS[sdk=iphoneos*]: x86_64 + groups: [app] + dependencies: + - target: share-extension + embed: true + - framework: {{ lib-output-file-name }} + embed: false + {{~#each ios-libraries}} + - framework: {{this}} + embed: false{{/each}}{{#if ios-vendor-frameworks}}{{~#each ios-vendor-frameworks}} + - framework: {{this}}{{/each}}{{/if}}{{#if ios-vendor-sdks}}{{~#each ios-vendor-sdks}} + - sdk: {{prefix-path this}}{{/each}}{{/if}} + - sdk: CoreGraphics.framework + - sdk: Metal.framework + - sdk: MetalKit.framework + - sdk: QuartzCore.framework + - sdk: Security.framework + - sdk: UIKit.framework + - sdk: CallKit.framework + - sdk: AVFoundation.framework{{#if this.ios-frameworks}}{{~#each ios-frameworks}} + - sdk: {{this}}.framework{{/each}}{{/if}} + - sdk: WebKit.framework + preBuildScripts: + {{~#each ios-pre-build-scripts}}{{#if this.path}} + - path {{this.path}}{{/if}}{{#if this.script}} + - script: {{this.script}}{{/if}}{{#if this.name}} + name: {{this.name}}{{/if}}{{#if this.input-files}} + inputFiles: {{~#each this.input-files}} + - {{this}}{{/each}}{{/if}}{{#if this.output-files}} + outputFiles: {{~#each this.output-files}} + - {{this}}{{/each}}{{/if}}{{#if this.input-file-lists}} + inputFileLists: {{~#each this.output-files}} + - {{this}}{{/each}}{{/if}}{{#if this.output-file-lists}} + outputFileLists: {{~#each this.output-files}} + - {{this}}{{/each}}{{/if}}{{#if this.shell}} + shell: {{this.shell}}{{/if}}{{#if this.show-env-vars}} + showEnvVars: {{this.show_env_vars}}{{/if}}{{#if this.run-only-when-installing}} + runOnlyWhenInstalling: {{this.run-only-when-installing}}{{/if}}{{#if this.based-on-dependency-analysis}} + basedOnDependencyAnalysis: {{this.based-on-dependency-analysis}}{{/if}}{{#if this.discovered-dependency-file}} + discoveredDependencyFile: {{this.discovered-dependency-file}}{{/if}} + {{~/each}} + + - script: {{ tauri-binary }} {{ tauri-binary-args-str }} -v --platform ${PLATFORM_DISPLAY_NAME:?} --sdk-root ${SDKROOT:?} --framework-search-paths "${FRAMEWORK_SEARCH_PATHS:?}" --header-search-paths "${HEADER_SEARCH_PATHS:?}" --gcc-preprocessor-definitions "${GCC_PREPROCESSOR_DEFINITIONS:-}" --configuration ${CONFIGURATION:?} ${FORCE_COLOR} ${ARCHS:?} + name: Build Rust Code + basedOnDependencyAnalysis: false + outputFiles: + - $(SRCROOT)/Externals/x86_64/${CONFIGURATION}/{{ lib-output-file-name }} + - $(SRCROOT)/Externals/arm64/${CONFIGURATION}/{{ lib-output-file-name }} + {{~#if ios-post-compile-scripts}} + postCompileScripts: + {{~#each ios-post-compile-scripts}}{{#if this.path}} + - path {{this.path}}{{/if}}{{#if this.script}} + - script: {{this.script}}{{/if}}{{#if this.name}} + name: {{this.name}}{{/if}}{{#if this.input-files}} + inputFiles: {{~#each this.input-files}} + - {{this}}{{/each}}{{/if}}{{#if this.output-files}} + outputFiles: {{~#each this.output-files}} + - {{this}}{{/each}}{{/if}}{{#if this.input-file-lists}} + inputFileLists: {{~#each this.output-files}} + - {{this}}{{/each}}{{/if}}{{#if this.output-file-lists}} + outputFileLists: {{~#each this.output-files}} + - {{this}}{{/each}}{{/if}}{{#if this.shell}} + shell: {{this.shell}}{{/if}}{{#if this.show-env-vars}} + showEnvVars: {{this.show_env_vars}}{{/if}}{{#if this.run-only-when-installing}} + runOnlyWhenInstalling: {{this.run-only-when-installing}}{{/if}}{{#if this.based-on-dependency-analysis}} + basedOnDependencyAnalysis: {{this.based-on-dependency-analysis}}{{/if}}{{#if this.discovered-dependency-file}} + discoveredDependencyFile: {{this.discovered-dependency-file}}{{/if}} + {{~/each~}} + {{~/if~}} + {{~#if ios-post-build-scripts}} + postBuildScripts: + {{~#each ios-post-build-scripts}}{{#if this.path}} + - path {{this.path}}{{/if}}{{#if this.script}} + - script: {{this.script}}{{/if}}{{#if this.name}} + name: {{this.name}}{{/if}}{{#if this.input-files}} + inputFiles: {{~#each this.input-files}} + - {{this}}{{/each}}{{/if}}{{#if this.output-files}} + outputFiles: {{~#each this.output-files}} + - {{this}}{{/each}}{{/if}}{{#if this.input-file-lists}} + inputFileLists: {{~#each this.output-files}} + - {{this}}{{/each}}{{/if}}{{#if this.output-file-lists}} + outputFileLists: {{~#each this.output-files}} + - {{this}}{{/each}}{{/if}}{{#if this.shell}} + shell: {{this.shell}}{{/if}}{{#if this.show-env-vars}} + showEnvVars: {{this.show_env_vars}}{{/if}}{{#if this.run-only-when-installing}} + runOnlyWhenInstalling: {{this.run-only-when-installing}}{{/if}}{{#if this.based-on-dependency-analysis}} + basedOnDependencyAnalysis: {{this.based-on-dependency-analysis}}{{/if}}{{#if this.discovered-dependency-file}} + discoveredDependencyFile: {{this.discovered-dependency-file}}{{/if}} + {{~/each~}} + {{~/if}} diff --git a/src-tauri/ios/PrivacyInfo.xcprivacy b/src-tauri/ios/PrivacyInfo.xcprivacy new file mode 100644 index 0000000000..fe840a043e --- /dev/null +++ b/src-tauri/ios/PrivacyInfo.xcprivacy @@ -0,0 +1,17 @@ + + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPITypeReasons + + C617.1 + + + + + diff --git a/src-tauri/packaging/deb/apparmor/sable b/src-tauri/packaging/deb/apparmor/sable new file mode 100644 index 0000000000..be6771b831 --- /dev/null +++ b/src-tauri/packaging/deb/apparmor/sable @@ -0,0 +1,6 @@ +abi , +include + +profile sable /opt/sable/sable flags=(unconfined) { + userns, +} diff --git a/src-tauri/packaging/deb/postinst b/src-tauri/packaging/deb/postinst new file mode 100755 index 0000000000..36a98a8dd5 --- /dev/null +++ b/src-tauri/packaging/deb/postinst @@ -0,0 +1,5 @@ +#!/bin/sh + +if command -v apparmor_parser >/dev/null 2>&1; then + apparmor_parser -r /etc/apparmor.d/sable || exit $? +fi diff --git a/src-tauri/packaging/deb/prerm b/src-tauri/packaging/deb/prerm new file mode 100755 index 0000000000..d04c00b56c --- /dev/null +++ b/src-tauri/packaging/deb/prerm @@ -0,0 +1,5 @@ +#!/bin/sh + +if command -v apparmor_parser >/dev/null 2>&1; then + apparmor_parser -R /etc/apparmor.d/sable || true +fi diff --git a/src-tauri/resources/invite.caf b/src-tauri/resources/invite.caf new file mode 100644 index 0000000000..4f6c5908b9 Binary files /dev/null and b/src-tauri/resources/invite.caf differ diff --git a/src-tauri/resources/notification.caf b/src-tauri/resources/notification.caf new file mode 100644 index 0000000000..edc7de46e7 Binary files /dev/null and b/src-tauri/resources/notification.caf differ diff --git a/src-tauri/share-extension-ios/ShareViewController.swift b/src-tauri/share-extension-ios/ShareViewController.swift new file mode 100644 index 0000000000..f1ff906eff --- /dev/null +++ b/src-tauri/share-extension-ios/ShareViewController.swift @@ -0,0 +1,241 @@ +import UIKit +import UniformTypeIdentifiers + +// Stages incoming shares into the app group container +// (share_inbox//share.json + files), then opens the main app via +// sable://share. UIApplication.open only compiles while +// APPLICATION_EXTENSION_API_ONLY stays unset (see ios-project.yml). +final class ShareViewController: UIViewController { + private var processing = false + + // group., derived from our own bundle id. + private var appGroupId: String { + let bundleId = Bundle.main.bundleIdentifier ?? "" + return "group." + bundleId.split(separator: ".").dropLast().joined(separator: ".") + } + + override func viewDidLoad() { + super.viewDidLoad() + view.isHidden = true + view.backgroundColor = .clear + view.isOpaque = false + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + guard !processing else { return } + processing = true + stageShare() + } + + private struct StagedItem { + let kind: String + var text: String? + var fileName: String? + var mime: String? + } + + private func stageShare() { + guard + let container = FileManager.default.containerURL( + forSecurityApplicationGroupIdentifier: appGroupId) + else { + NSLog("[share-extension] app group container unavailable: \(appGroupId)") + complete() + return + } + + let batchId = "\(Int(Date().timeIntervalSince1970 * 1000))-\(UUID().uuidString)" + let batchDir = container.appendingPathComponent("share_inbox/\(batchId)", isDirectory: true) + do { + try FileManager.default.createDirectory(at: batchDir, withIntermediateDirectories: true) + } catch { + NSLog("[share-extension] cannot create batch dir: \(error)") + complete() + return + } + + let attachments = (extensionContext?.inputItems as? [NSExtensionItem] ?? []) + .flatMap { $0.attachments ?? [] } + + var items: [StagedItem] = [] + var fileIndex = 0 + let group = DispatchGroup() + let lock = NSLock() + let append: (StagedItem) -> Void = { item in + lock.lock() + items.append(item) + lock.unlock() + } + + for provider in attachments { + let types = [UTType.movie, UTType.image, UTType.fileURL, UTType.url, UTType.plainText] + guard let type = types.first(where: { + provider.hasItemConformingToTypeIdentifier($0.identifier) + }) else { continue } + + lock.lock() + let index = fileIndex + fileIndex += 1 + lock.unlock() + + group.enter() + if type == .image || type == .movie { + provider.loadFileRepresentation(forTypeIdentifier: type.identifier) { url, error in + defer { group.leave() } + if let error { + NSLog("[share-extension] load file failed for \(type.identifier): \(error)") + return + } + guard let url else { + NSLog("[share-extension] no file returned for \(type.identifier)") + return + } + self.stageFile(at: url, index: index, into: batchDir, append: append) + } + continue + } + provider.loadItem(forTypeIdentifier: type.identifier, options: nil) { item, error in + defer { group.leave() } + if let error { + NSLog("[share-extension] loadItem failed for \(type.identifier): \(error)") + return + } + switch type { + case .url: + if let url = item as? URL { + if url.isFileURL { + self.stageFile(at: url, index: index, into: batchDir, append: append) + } else { + append(StagedItem(kind: "url", text: url.absoluteString)) + } + } + case .plainText: + if let text = item as? String { + append(StagedItem(kind: "text", text: text)) + } + default: + self.stagePayload( + item, provider: provider, type: type, index: index, into: batchDir, + append: append) + } + } + } + + group.notify(queue: .main) { + self.finishStaging(items: items, batchDir: batchDir) + } + } + + private func stagePayload( + _ item: NSSecureCoding?, provider: NSItemProvider, type: UTType, index: Int, + into batchDir: URL, append: (StagedItem) -> Void + ) { + if let url = item as? URL { + stageFile(at: url, index: index, into: batchDir, append: append) + } else if let data = item as? Data { + let resolved = + provider.registeredTypeIdentifiers + .compactMap { UTType($0) } + .first { $0.isSubtype(of: type) || $0 == type } ?? type + let ext = resolved.preferredFilenameExtension ?? "bin" + let name = "shared-\(index).\(ext)" + writeData(data, name: name, mime: mimeType(for: resolved), into: batchDir, append: append) + } else { + NSLog("[share-extension] unsupported payload \(String(describing: item))") + } + } + + private func stageFile(at url: URL, index: Int, into batchDir: URL, append: (StagedItem) -> Void) { + let sanitized = sanitize(url.lastPathComponent) + let name = "\(index)-\(sanitized)" + let destination = batchDir.appendingPathComponent(name) + do { + try FileManager.default.copyItem(at: url, to: destination) + } catch { + NSLog("[share-extension] copy failed: \(error)") + return + } + let type = UTType(filenameExtension: url.pathExtension) + append(StagedItem(kind: "file", fileName: name, mime: type.map(mimeType(for:)))) + } + + private func writeData( + _ data: Data, name: String, mime: String?, into batchDir: URL, + append: (StagedItem) -> Void + ) { + let destination = batchDir.appendingPathComponent(name) + do { + try data.write(to: destination) + } catch { + NSLog("[share-extension] write failed: \(error)") + return + } + append(StagedItem(kind: "file", fileName: name, mime: mime)) + } + + private func mimeType(for type: UTType) -> String { + type.preferredMIMEType ?? "application/octet-stream" + } + + private func sanitize(_ name: String) -> String { + let cleaned = name + .replacingOccurrences(of: "/", with: "_") + .replacingOccurrences(of: "\\", with: "_") + .replacingOccurrences(of: "\0", with: "") + let capped = String(cleaned.suffix(120)) + return capped.isEmpty || capped == "." || capped == ".." ? "shared" : capped + } + + private func finishStaging(items: [StagedItem], batchDir: URL) { + guard !items.isEmpty else { + try? FileManager.default.removeItem(at: batchDir) + complete() + return + } + + let manifest: [String: Any] = [ + "version": 1, + "items": items.map { item in + var entry: [String: Any] = ["kind": item.kind] + if let text = item.text { entry["text"] = text } + if let fileName = item.fileName { entry["fileName"] = fileName } + if let mime = item.mime { entry["mime"] = mime } + return entry + }, + ] + do { + let data = try JSONSerialization.data(withJSONObject: manifest) + try data.write(to: batchDir.appendingPathComponent("share.json")) + } catch { + NSLog("[share-extension] manifest write failed: \(error)") + try? FileManager.default.removeItem(at: batchDir) + complete() + return + } + + openHostApp() + // Completing immediately can cancel the open on some iOS builds. + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + self.complete() + } + } + + // extensionContext.open never calls its completion for share extensions. + private func openHostApp() { + guard let url = URL(string: "sable://share") else { return } + var responder: UIResponder? = self + while let current = responder { + if let application = current as? UIApplication { + application.open(url, options: [:], completionHandler: nil) + return + } + responder = current.next + } + NSLog("[share-extension] no UIApplication in responder chain") + } + + private func complete() { + extensionContext?.completeRequest(returningItems: nil, completionHandler: nil) + } +} diff --git a/src-tauri/src/deep_link_ipc.rs b/src-tauri/src/deep_link_ipc.rs new file mode 100644 index 0000000000..82978229b7 --- /dev/null +++ b/src-tauri/src/deep_link_ipc.rs @@ -0,0 +1,191 @@ +//! Deep-link forwarding for the Linux CEF build. +//! +//! CEF is one-process-per-cache, so a deep-link relaunch can't init Chromium to +//! forward itself. The primary binds a socket; the secondary forwards the URL +//! and exits before touching CEF. Delivery re-emits `deep-link://new-url`, the +//! event `@tauri-apps/plugin-deep-link`'s `onOpenUrl` already listens to. + +use std::{ + io::{BufRead, BufReader, Write}, + os::unix::net::{UnixListener, UnixStream}, + path::PathBuf, + sync::{Arc, Mutex, OnceLock}, + time::Duration, +}; + +// OIDC uses the private-use `moe.sable.app:/login?...` form; other flows `sable://`. +const SCHEMES: &[&str] = &["moe.sable.app:", "sable:"]; + +fn is_deep_link(arg: &str) -> bool { + SCHEMES.iter().any(|scheme| arg.starts_with(scheme)) +} + +/// Stable socket path. Prefers $XDG_RUNTIME_DIR (per-user tmpfs, cleaned on +/// reboot); falls back to the temp dir. +fn socket_path() -> PathBuf { + if let Ok(dir) = std::env::var("XDG_RUNTIME_DIR") { + return PathBuf::from(dir).join("moe.sable.client-deeplink.sock"); + } + std::env::temp_dir().join("moe.sable.client-deeplink.sock") +} + +fn collect_deep_link_urls_from_args(args: I) -> Vec +where + I: IntoIterator, + S: AsRef, +{ + args.into_iter() + .skip(1) + .filter_map(|arg| { + let arg = arg.as_ref(); + is_deep_link(arg).then(|| arg.to_string()) + }) + .collect() +} + +pub enum ForwardResult { + /// URLs were written to the primary's socket; the caller must exit(0). + Forwarded, + /// A deep-link URL was in argv but no primary is listening → become primary. + NoPrimary, + /// No deep-link URLs in argv; a normal launch. + NoUrls, +} + +/// Forward any `sable://` URLs in argv to the primary instance. Call BEFORE +/// any CEF initialization. +pub fn try_forward_deep_links() -> ForwardResult { + let urls = collect_deep_link_urls_from_args(std::env::args()); + if urls.is_empty() { + return ForwardResult::NoUrls; + } + + let path = socket_path(); + match UnixStream::connect(&path) { + Ok(mut stream) => { + stream.set_write_timeout(Some(Duration::from_secs(2))).ok(); + for url in &urls { + let _ = writeln!(stream, "{url}"); + } + log::info!("[deep-link-ipc] forwarded {} URL(s) to primary", urls.len()); + ForwardResult::Forwarded + } + Err(_) => ForwardResult::NoPrimary, + } +} + +static PENDING_URLS: OnceLock>>> = OnceLock::new(); +type LiveHandler = Box; +static LIVE_HANDLER: OnceLock>> = OnceLock::new(); + +fn pending_queue() -> &'static Arc>> { + PENDING_URLS.get_or_init(|| Arc::new(Mutex::new(Vec::new()))) +} +fn live_handler() -> &'static Mutex> { + LIVE_HANDLER.get_or_init(|| Mutex::new(None)) +} + +/// Query/fragment carry OIDC tokens — never log them. +fn redact_for_log(url: &str) -> String { + url.split(['?', '#']) + .next() + .unwrap_or("") + .to_string() +} + +fn dispatch_url(url: String) { + if let Ok(guard) = live_handler().lock() { + if let Some(handler) = guard.as_ref() { + handler(url); + return; + } + } + if let Ok(mut q) = pending_queue().lock() { + q.push(url); + } +} + +/// Removes the socket file on drop. +pub struct DeepLinkSocketGuard { + path: PathBuf, +} +impl Drop for DeepLinkSocketGuard { + fn drop(&mut self) { + let _ = std::fs::remove_file(&self.path); + } +} + +/// Bind the socket and spawn the listener thread. Returns `None` if a live +/// primary already holds it (or on error) — non-fatal. +pub fn bind_and_listen() -> Option { + let path = socket_path(); + let listener = match UnixListener::bind(&path) { + Ok(l) => l, + Err(e) if e.kind() == std::io::ErrorKind::AddrInUse => match UnixStream::connect(&path) { + Ok(_) => return None, // live primary already running + Err(_) => { + let _ = std::fs::remove_file(&path); // stale socket from a crash + UnixListener::bind(&path).ok()? + } + }, + Err(e) => { + log::warn!("[deep-link-ipc] failed to bind {}: {e}", path.display()); + return None; + } + }; + + std::thread::Builder::new() + .name("deep-link-ipc".into()) + .spawn(move || { + for stream in listener.incoming() { + match stream { + Ok(stream) => handle_connection(stream), + Err(_) => break, + } + } + }) + .ok(); + Some(DeepLinkSocketGuard { path }) +} + +fn handle_connection(stream: UnixStream) { + stream.set_read_timeout(Some(Duration::from_secs(3))).ok(); + for line in BufReader::new(stream).lines() { + match line { + Ok(url) if is_deep_link(&url) => { + log::info!("[deep-link-ipc] received {}", redact_for_log(&url)); + dispatch_url(url); + } + Ok(_) => {} + Err(_) => break, + } + } +} + +// The event tauri-plugin-deep-link's onOpenUrl listens to. Kept out of the +// emit call as a const so tauri-typegen doesn't emit a binding for it (the +// `://` can't form a valid identifier). +const NEW_URL_EVENT: &str = "deep-link://new-url"; + +/// Install a live handler that emits `NEW_URL_EVENT` and drain anything queued +/// before now. Call from `setup()`. +pub fn drain_pending_urls(app: &tauri::AppHandle) { + use tauri::Emitter; + + let app_for_handler = app.clone(); + if let Ok(mut guard) = live_handler().lock() { + *guard = Some(Box::new(move |url: String| { + if let Err(e) = app_for_handler.emit(NEW_URL_EVENT, vec![url]) { + log::warn!("[deep-link-ipc] emit failed: {e}"); + } + })); + } + + let pending: Vec = pending_queue() + .lock() + .map(|mut q| std::mem::take(&mut *q)) + .unwrap_or_default(); + for url in pending { + let _ = app.emit(NEW_URL_EVENT, vec![url]); + } +} diff --git a/src-tauri/src/desktop/diagnostics.rs b/src-tauri/src/desktop/diagnostics.rs new file mode 100644 index 0000000000..6d93b35551 --- /dev/null +++ b/src-tauri/src/desktop/diagnostics.rs @@ -0,0 +1,81 @@ +use std::io; +use std::path::Path; + +use tauri::{AppHandle, Manager}; +use tauri_plugin_dialog::DialogExt; + +const DEFAULT_ARCHIVE_NAME: &str = "sable-diagnostics.zip"; + +#[tauri::command] +pub async fn export_diagnostics( + app: AppHandle, + frontend_logs: Option, +) -> Result, String> { + let (sender, receiver) = tokio::sync::oneshot::channel(); + app.dialog() + .file() + .set_file_name(DEFAULT_ARCHIVE_NAME) + .add_filter("ZIP archive", &["zip"]) + .save_file(move |path| { + let _ = sender.send(path); + }); + + let Some(path) = receiver + .await + .map_err(|error| format!("Save dialog did not return a result: {error}"))? + else { + return Ok(None); + }; + + let archive_path = path.into_path().map_err(|err| err.to_string())?; + validate_zip_extension(&archive_path).map_err(|err| err.to_string())?; + let log_dir = app.path().app_log_dir().map_err(|err| err.to_string())?; + let system_info = crate::diagnostics::system_info( + &app.package_info().version.to_string(), + std::env::consts::OS, + std::env::consts::ARCH, + ); + crate::diagnostics::create_archive( + &archive_path, + &log_dir, + &system_info, + frontend_logs.as_deref(), + ) + .map_err(|error| error.to_string())?; + + Ok(Some(format!( + "Diagnostics exported to {}", + archive_path.display() + ))) +} + +fn validate_zip_extension(path: &Path) -> io::Result<()> { + let is_zip = path + .extension() + .and_then(|extension| extension.to_str()) + .is_some_and(|extension| extension.eq_ignore_ascii_case("zip")); + if !is_zip { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "Diagnostics destination must have a .zip extension", + )); + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::validate_zip_extension; + use std::path::Path; + + #[test] + fn zip_extension_is_required() { + assert!(validate_zip_extension(Path::new("support.zip")).is_ok()); + assert!(validate_zip_extension(Path::new("support.ZIP")).is_ok()); + let error = validate_zip_extension(Path::new("support.tar")).unwrap_err(); + assert_eq!( + error.to_string(), + "Diagnostics destination must have a .zip extension" + ); + } +} diff --git a/src-tauri/src/desktop/download.rs b/src-tauri/src/desktop/download.rs new file mode 100644 index 0000000000..cba83d6b36 --- /dev/null +++ b/src-tauri/src/desktop/download.rs @@ -0,0 +1,28 @@ +use tauri::AppHandle; +use tauri_plugin_dialog::DialogExt; + +#[tauri::command] +pub async fn save_download( + app: AppHandle, + filename: String, + bytes: Vec, +) -> Result { + let (sender, receiver) = tokio::sync::oneshot::channel(); + app.dialog() + .file() + .set_file_name(&filename) + .save_file(move |path| { + let _ = sender.send(path); + }); + + let Some(path) = receiver + .await + .map_err(|error| format!("Save dialog did not return a result: {error}"))? + else { + return Ok(false); + }; + + let path = path.into_path().map_err(|err| err.to_string())?; + std::fs::write(&path, &bytes).map_err(|err| err.to_string())?; + Ok(true) +} diff --git a/src-tauri/src/desktop/logging.rs b/src-tauri/src/desktop/logging.rs new file mode 100644 index 0000000000..58c1fee373 --- /dev/null +++ b/src-tauri/src/desktop/logging.rs @@ -0,0 +1,113 @@ +use tauri::AppHandle; +use tauri_plugin_log::{Builder, FileOpenStrategy, RotationStrategy, Target, TargetKind}; + +const MAX_LOG_FILE_SIZE: u128 = 5 * 1024 * 1024; +const ROTATED_LOG_COUNT: usize = 5; + +fn is_reviewed_verbose_target(target: &str) -> bool { + target == "app_lib" + || target.starts_with("app_lib::") + || target == "sable" + || target.starts_with("sable::") + || target == "tauri" + || target.starts_with("tauri::") + || target.starts_with("tauri_runtime_") + || target.starts_with("tauri_plugin_") + || target == tauri_plugin_log::WEBVIEW_TARGET +} + +fn file_log_record_allowed(metadata: &log::Metadata) -> bool { + metadata.level() <= log::Level::Warn || is_reviewed_verbose_target(metadata.target()) +} + +fn builder() -> Builder { + Builder::default() + .level(log::LevelFilter::Info) + .max_file_size(MAX_LOG_FILE_SIZE) + .rotation_strategy(RotationStrategy::KeepSome(ROTATED_LOG_COUNT)) + .file_open_strategy(FileOpenStrategy::Append) + .clear_targets() + .target(Target::new(TargetKind::Stdout)) + .target(Target::new(TargetKind::LogDir { file_name: None }).filter(file_log_record_allowed)) +} + +pub fn setup(app: &AppHandle) -> Result<(), Box> { + #[cfg(all(debug_assertions, feature = "devtools"))] + { + let (log_plugin, _level, logger) = builder().split(app)?; + let mut devtools = tauri_plugin_devtools::Builder::default(); + devtools.attach_logger(logger); + app.plugin(devtools.init())?; + app.plugin(log_plugin)?; + } + + #[cfg(not(all(debug_assertions, feature = "devtools")))] + app.plugin(builder().build())?; + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::{file_log_record_allowed, is_reviewed_verbose_target}; + + fn allowed(target: &str, level: log::Level) -> bool { + let metadata = log::Metadata::builder().target(target).level(level).build(); + file_log_record_allowed(&metadata) + } + + #[test] + fn reviewed_targets_keep_verbose_levels() { + for target in [ + "app_lib", + "app_lib::desktop::diagnostics", + "app_lib::network::loopback_http", + "sable", + "tauri", + "tauri::manager", + "tauri_runtime_cef", + "tauri_runtime_cef::cef_impl::ipc", + "tauri_runtime_wry", + "tauri_plugin_updater", + "tauri_plugin_window_state", + tauri_plugin_log::WEBVIEW_TARGET, + ] { + assert!(is_reviewed_verbose_target(target), "expected {target}"); + for level in [log::Level::Info, log::Level::Debug, log::Level::Trace] { + assert!(allowed(target, level), "{level} from {target} dropped"); + } + } + } + + #[test] + fn noisy_dependency_targets_are_suppressed_below_warn() { + for target in [ + "reqwest", + "reqwest::connect", + "hyper::client::pool", + "rustls::client", + "tokio", + "mio", + "tracing", + "cef", + "tao", + "wry", + "webkit2gtk", + "soup3", + "taurine", // similar-looking prefixes must not match + "sablefish::module", + ] { + for level in [log::Level::Info, log::Level::Debug, log::Level::Trace] { + assert!(!allowed(target, level), "{level} from {target} kept"); + } + } + } + + #[test] + fn dependency_warnings_and_errors_are_kept() { + for target in ["reqwest", "hyper::client", "rustls", "cef", "unknown_crate"] { + assert!(allowed(target, log::Level::Warn), "{target} warn dropped"); + assert!(allowed(target, log::Level::Error), "{target} error dropped"); + } + } +} diff --git a/src-tauri/src/desktop/menu.rs b/src-tauri/src/desktop/menu.rs new file mode 100644 index 0000000000..c52c69ae6b --- /dev/null +++ b/src-tauri/src/desktop/menu.rs @@ -0,0 +1,72 @@ +#[cfg(target_os = "macos")] +use tauri::Emitter; +use tauri::{AppHandle, Manager}; + +#[cfg(target_os = "macos")] +pub const SETTINGS_MENU_ID: &str = "settings"; + +pub const TOGGLE_WINDOW_ACCELERATOR: &str = "CmdOrCtrl+Shift+S"; + +// Extend the standard menu (Edit submenu for webview copy/paste, Quit, Close) +// with a Settings item. +#[cfg(target_os = "macos")] +pub fn build_app_menu( + handle: &AppHandle, +) -> tauri::Result> { + use tauri::menu::{Menu, MenuItem, Submenu}; + + let menu = Menu::default(handle)?; + let settings = MenuItem::with_id( + handle, + SETTINGS_MENU_ID, + "Settings…", + true, + Some("CmdOrCtrl+,"), + )?; + let preferences = Submenu::with_items(handle, "Preferences", true, &[&settings])?; + menu.append(&preferences)?; + Ok(menu) +} + +#[cfg(target_os = "macos")] +pub fn handle_menu_event(app: &AppHandle, event: &tauri::menu::MenuEvent) { + if event.id().as_ref() == SETTINGS_MENU_ID { + let _ = app.emit("open-settings", ()); + } +} + +pub fn toggle_main_window(app: &AppHandle) { + if let Some(window) = app.get_webview_window(crate::MAIN_WINDOW_LABEL) { + let visible = window.is_visible().unwrap_or(false); + let focused = window.is_focused().unwrap_or(false); + if visible && focused { + let _ = window.hide(); + } else { + let _ = window.unminimize(); + let _ = window.show(); + let _ = window.set_focus(); + } + } else { + let _ = crate::show_or_create_main_window(app); + } +} + +pub fn global_shortcut_plugin() -> tauri::plugin::TauriPlugin { + use tauri_plugin_global_shortcut::{Builder, ShortcutState}; + + Builder::new() + .with_handler(|app, _shortcut, event| { + if event.state() == ShortcutState::Pressed { + toggle_main_window(app); + } + }) + .build() +} + +pub fn register_global_shortcuts(app: &AppHandle) { + use tauri_plugin_global_shortcut::GlobalShortcutExt; + + if let Err(error) = app.global_shortcut().register(TOGGLE_WINDOW_ACCELERATOR) { + log::warn!("Failed to register global show/hide shortcut: {error}"); + } +} diff --git a/src-tauri/src/desktop/mod.rs b/src-tauri/src/desktop/mod.rs new file mode 100644 index 0000000000..829b8e375e --- /dev/null +++ b/src-tauri/src/desktop/mod.rs @@ -0,0 +1,12 @@ +pub mod diagnostics; +pub mod download; +pub mod logging; +pub mod menu; +pub mod runtime_state; +pub mod settings; +pub mod tray; +#[cfg(target_os = "linux")] +pub mod tray_badge; + +#[cfg(windows)] +pub mod windows; diff --git a/src-tauri/src/desktop/runtime_state.rs b/src-tauri/src/desktop/runtime_state.rs new file mode 100644 index 0000000000..f81982b826 --- /dev/null +++ b/src-tauri/src/desktop/runtime_state.rs @@ -0,0 +1,38 @@ +use serde::{Deserialize, Serialize}; +use ts_rs::TS; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export, export_to = "desktop/", rename_all = "camelCase")] +pub struct DesktopRuntimeState { + pub tray_available: bool, +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + use ts_rs::{Config, TS}; + + #[test] + fn desktop_runtime_state_serializes_with_camel_case_keys() { + let state = DesktopRuntimeState { + tray_available: true, + }; + + assert_eq!( + serde_json::to_value(state).unwrap(), + json!({ + "trayAvailable": true, + }) + ); + } + + #[test] + fn desktop_runtime_state_exports_to_typescript() { + let output = DesktopRuntimeState::export_to_string(&Config::new()).unwrap(); + + assert!(output.contains("type DesktopRuntimeState")); + assert!(output.contains("trayAvailable: boolean")); + } +} diff --git a/src-tauri/src/desktop/settings.rs b/src-tauri/src/desktop/settings.rs new file mode 100644 index 0000000000..21598694da --- /dev/null +++ b/src-tauri/src/desktop/settings.rs @@ -0,0 +1,142 @@ +use serde::{Deserialize, Serialize}; +use ts_rs::TS; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export, export_to = "desktop/", rename_all = "camelCase")] +pub struct DesktopSettings { + pub close_to_background_on_close: bool, + pub show_system_tray_icon: bool, + pub use_custom_title_bar: bool, + pub spellcheck: bool, +} + +pub(crate) const DESKTOP_SETTINGS_PATH: &str = "desktop-preferences.json"; +pub(crate) const CLOSE_TO_BACKGROUND_ON_CLOSE_KEY: &str = "closeToBackgroundOnClose"; +pub(crate) const SHOW_SYSTEM_TRAY_ICON_KEY: &str = "showSystemTrayIcon"; +pub(crate) const USE_CUSTOM_TITLE_BAR_KEY: &str = "useCustomTitleBar"; +pub(crate) const SPELLCHECK_KEY: &str = "spellcheck"; +pub(crate) const LEGACY_KEEP_BACKGROUND_RUNNING_KEY: &str = "keepBackgroundRunning"; + +pub(crate) const fn use_custom_title_bar_default() -> bool { + cfg!(target_os = "windows") +} + +pub(crate) fn tray_available_for_session(settings: DesktopSettings, tray_created: bool) -> bool { + settings.show_system_tray_icon && tray_created +} + +pub(crate) fn desktop_settings_from_values( + close_to_background_on_close: Option, + show_system_tray_icon: Option, + use_custom_title_bar: Option, + spellcheck: Option, + keep_background_running: Option, +) -> DesktopSettings { + DesktopSettings { + close_to_background_on_close: close_to_background_on_close.unwrap_or(true) + || keep_background_running.unwrap_or(false), + show_system_tray_icon: show_system_tray_icon.unwrap_or(true), + use_custom_title_bar: use_custom_title_bar.unwrap_or(use_custom_title_bar_default()), + spellcheck: spellcheck.unwrap_or(true), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + use ts_rs::{Config, TS}; + + #[test] + fn desktop_settings_serialize_with_camel_case_keys() { + let settings = DesktopSettings { + close_to_background_on_close: true, + show_system_tray_icon: false, + use_custom_title_bar: true, + spellcheck: false, + }; + + assert_eq!( + serde_json::to_value(settings).unwrap(), + json!({ + "closeToBackgroundOnClose": true, + "showSystemTrayIcon": false, + "useCustomTitleBar": true, + "spellcheck": false, + }) + ); + } + + #[test] + fn desktop_settings_deserialize_from_camel_case_keys() { + assert_eq!( + serde_json::from_value::(json!({ + "closeToBackgroundOnClose": true, + "showSystemTrayIcon": false, + "useCustomTitleBar": true, + "spellcheck": false, + })) + .unwrap(), + DesktopSettings { + close_to_background_on_close: true, + show_system_tray_icon: false, + use_custom_title_bar: true, + spellcheck: false, + } + ); + } + + #[test] + fn desktop_settings_export_to_typescript_with_camel_case_keys() { + let output = DesktopSettings::export_to_string(&Config::new()).unwrap(); + + assert!(output.contains("type DesktopSettings")); + assert!(output.contains("closeToBackgroundOnClose: boolean")); + assert!(output.contains("showSystemTrayIcon: boolean")); + assert!(output.contains("useCustomTitleBar: boolean")); + assert!(output.contains("spellcheck: boolean")); + assert!(!output.contains("keepBackgroundRunning: boolean")); + } + + #[test] + fn legacy_background_setting_keeps_close_behavior_enabled() { + assert_eq!( + desktop_settings_from_values(Some(false), Some(false), Some(false), None, Some(true)), + DesktopSettings { + close_to_background_on_close: true, + show_system_tray_icon: false, + use_custom_title_bar: false, + spellcheck: true, + } + ); + } + + #[test] + fn explicit_close_setting_stays_disabled_when_legacy_background_is_off() { + assert_eq!( + desktop_settings_from_values(Some(false), Some(true), Some(true), None, Some(false)), + DesktopSettings { + close_to_background_on_close: false, + show_system_tray_icon: true, + use_custom_title_bar: true, + spellcheck: true, + } + ); + } + + #[test] + fn missing_custom_title_bar_setting_uses_platform_default() { + assert_eq!( + desktop_settings_from_values(Some(true), Some(true), None, None, None) + .use_custom_title_bar, + cfg!(target_os = "windows") + ); + } + + #[test] + fn missing_spellcheck_setting_defaults_to_enabled() { + assert!(desktop_settings_from_values(None, None, None, None, None).spellcheck); + assert!(!desktop_settings_from_values(None, None, None, Some(false), None).spellcheck); + } +} diff --git a/src-tauri/src/desktop/tray.rs b/src-tauri/src/desktop/tray.rs new file mode 100644 index 0000000000..0fbc4d245a --- /dev/null +++ b/src-tauri/src/desktop/tray.rs @@ -0,0 +1,518 @@ +use std::sync::atomic::{AtomicBool, Ordering}; + +use crate::desktop::runtime_state::DesktopRuntimeState; +use crate::desktop::settings::{ + desktop_settings_from_values, tray_available_for_session, use_custom_title_bar_default, + DesktopSettings, CLOSE_TO_BACKGROUND_ON_CLOSE_KEY, DESKTOP_SETTINGS_PATH, + LEGACY_KEEP_BACKGROUND_RUNNING_KEY, SHOW_SYSTEM_TRAY_ICON_KEY, SPELLCHECK_KEY, + USE_CUSTOM_TITLE_BAR_KEY, +}; +use serde_json::json; +use tauri::{ + menu::{Menu, MenuEvent, MenuItem}, + tray::TrayIconBuilder, + AppHandle, Emitter, Manager, RunEvent, WebviewWindow, +}; +use tauri_plugin_store::StoreExt; + +#[cfg(not(target_os = "linux"))] +use tauri::tray::{MouseButton, TrayIconEvent}; + +pub(crate) const MAIN_TRAY_ID: &str = "main"; +pub(crate) const WINDOW_HIDDEN_TO_TRAY_EVENT: &str = "window-hidden-to-tray"; +const TRAY_MENU_SHOW_ID: &str = "tray_show"; +const TRAY_MENU_QUIT_ID: &str = "tray_quit"; + +pub struct DesktopSettingsState { + close_to_background_on_close: AtomicBool, + show_system_tray_icon: AtomicBool, + use_custom_title_bar: AtomicBool, + spellcheck: AtomicBool, + tray_available: AtomicBool, +} + +impl Default for DesktopSettingsState { + fn default() -> Self { + Self { + close_to_background_on_close: AtomicBool::new(true), + show_system_tray_icon: AtomicBool::new(true), + use_custom_title_bar: AtomicBool::new(use_custom_title_bar_default()), + spellcheck: AtomicBool::new(true), + tray_available: AtomicBool::new(false), + } + } +} + +pub fn setup_close_to_background(webview_window: &WebviewWindow) { + let window = webview_window.clone(); + webview_window.on_window_event(move |event| { + let tauri::WindowEvent::CloseRequested { api, .. } = event else { + return; + }; + let state = window.state::(); + if state.close_to_background_on_close.load(Ordering::Relaxed) + && can_restore_from_background(DesktopRuntimeState { + tray_available: state.tray_available.load(Ordering::Relaxed), + }) + { + api.prevent_close(); + // Hiding does not reliably emit a native blur event, so notify the + // webview before it can process new timeline events as read. + let _ = window.emit(WINDOW_HIDDEN_TO_TRAY_EVENT, ()); + let _ = window.hide(); + } + }); +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum ExitRequestAction { + AllowExit, + CloseWindowsToBackground, +} + +fn can_restore_from_background(runtime: DesktopRuntimeState) -> bool { + cfg!(target_os = "macos") || runtime.tray_available +} + +fn exit_request_action( + settings: DesktopSettings, + runtime: DesktopRuntimeState, + code: Option, +) -> ExitRequestAction { + if code.is_some() + || !settings.close_to_background_on_close + || !can_restore_from_background(runtime) + { + ExitRequestAction::AllowExit + } else { + ExitRequestAction::CloseWindowsToBackground + } +} + +pub(crate) fn load_desktop_settings( + app: &AppHandle, +) -> tauri::Result { + let store = app + .store_builder(DESKTOP_SETTINGS_PATH) + .defaults(std::collections::HashMap::from([ + (CLOSE_TO_BACKGROUND_ON_CLOSE_KEY.into(), json!(true)), + (SHOW_SYSTEM_TRAY_ICON_KEY.into(), json!(true)), + ( + USE_CUSTOM_TITLE_BAR_KEY.into(), + json!(use_custom_title_bar_default()), + ), + (SPELLCHECK_KEY.into(), json!(true)), + ])) + .build() + .map_err(|error| tauri::Error::PluginInitialization("store".into(), error.to_string()))?; + + Ok(desktop_settings_from_values( + store + .get(CLOSE_TO_BACKGROUND_ON_CLOSE_KEY) + .and_then(|value| value.as_bool()), + store + .get(SHOW_SYSTEM_TRAY_ICON_KEY) + .and_then(|value| value.as_bool()), + store + .get(USE_CUSTOM_TITLE_BAR_KEY) + .and_then(|value| value.as_bool()), + store.get(SPELLCHECK_KEY).and_then(|value| value.as_bool()), + store + .get(LEGACY_KEEP_BACKGROUND_RUNNING_KEY) + .and_then(|value| value.as_bool()), + )) +} + +fn current_desktop_settings(app: &AppHandle) -> DesktopSettings { + let state = app.state::(); + DesktopSettings { + close_to_background_on_close: state.close_to_background_on_close.load(Ordering::Relaxed), + show_system_tray_icon: state.show_system_tray_icon.load(Ordering::Relaxed), + use_custom_title_bar: state.use_custom_title_bar.load(Ordering::Relaxed), + spellcheck: state.spellcheck.load(Ordering::Relaxed), + } +} + +fn desktop_runtime_state(app: &AppHandle) -> DesktopRuntimeState { + DesktopRuntimeState { + tray_available: app + .state::() + .tray_available + .load(Ordering::Relaxed), + } +} + +#[tauri::command] +pub fn get_desktop_runtime_state(app: AppHandle) -> DesktopRuntimeState { + desktop_runtime_state(&app) +} + +#[tauri::command] +pub fn sync_desktop_settings( + app: AppHandle, + settings: DesktopSettings, +) -> Result { + apply_desktop_settings(&app, settings).map_err(|error| error.to_string()) +} + +pub(crate) fn sync_desktop_settings_inner( + app: &AppHandle, +) -> tauri::Result { + let settings = load_desktop_settings(app)?; + apply_desktop_settings(app, settings) +} + +fn apply_desktop_settings( + app: &AppHandle, + settings: DesktopSettings, +) -> tauri::Result { + let state = app.state::(); + + state + .close_to_background_on_close + .store(settings.close_to_background_on_close, Ordering::Relaxed); + state + .show_system_tray_icon + .store(settings.show_system_tray_icon, Ordering::Relaxed); + state + .use_custom_title_bar + .store(settings.use_custom_title_bar, Ordering::Relaxed); + state + .spellcheck + .store(settings.spellcheck, Ordering::Relaxed); + + apply_main_window_title_bar_settings(app, settings)?; + + if settings.show_system_tray_icon && cfg!(not(target_os = "macos")) { + if app.tray_by_id(MAIN_TRAY_ID).is_none() { + match create_system_tray(app) { + Ok(()) => state.tray_available.store( + tray_available_for_session(settings, true), + Ordering::Relaxed, + ), + Err(error) => { + log::warn!("Failed to initialize system tray: {error}"); + state.tray_available.store( + tray_available_for_session(settings, false), + Ordering::Relaxed, + ); + } + } + } else { + state.tray_available.store( + tray_available_for_session(settings, true), + Ordering::Relaxed, + ); + } + } else { + let _ = app.remove_tray_by_id(MAIN_TRAY_ID); + state.tray_available.store(false, Ordering::Relaxed); + } + + Ok(desktop_runtime_state(app)) +} + +fn apply_main_window_title_bar_settings( + app: &AppHandle, + settings: DesktopSettings, +) -> tauri::Result<()> { + let Some(window) = app.get_webview_window(crate::MAIN_WINDOW_LABEL) else { + return Ok(()); + }; + + #[cfg(any(target_os = "windows", target_os = "linux"))] + window.set_decorations(!settings.use_custom_title_bar)?; + + #[cfg(target_os = "macos")] + window.set_title_bar_style(if settings.use_custom_title_bar { + tauri::TitleBarStyle::Overlay + } else { + tauri::TitleBarStyle::Visible + })?; + + Ok(()) +} +fn close_all_windows(app: &AppHandle) { + for (_label, window) in app.webview_windows() { + let _ = window.close(); + } +} + +fn handle_exit_request( + app: &AppHandle, + code: Option, + api: &tauri::ExitRequestApi, +) { + let settings = current_desktop_settings(app); + let runtime = desktop_runtime_state(app); + if exit_request_action(settings, runtime, code) == ExitRequestAction::CloseWindowsToBackground { + api.prevent_exit(); + close_all_windows(app); + } +} + +#[cfg(not(target_os = "linux"))] +fn handle_tray_double_click(app: &AppHandle, event: &TrayIconEvent) { + if let TrayIconEvent::DoubleClick { + button: MouseButton::Left, + .. + } = event + { + if let Some(window) = app.get_webview_window(crate::MAIN_WINDOW_LABEL) { + if window.is_visible().unwrap_or(false) { + let _ = window.close(); + } else { + let _ = window.show(); + let _ = window.set_focus(); + } + } else { + let _ = crate::show_or_create_main_window(app); + } + } +} + +pub fn handle_run_event(app: &AppHandle, event: RunEvent) { + if let RunEvent::ExitRequested { code, api, .. } = event { + handle_exit_request(app, code, &api); + } +} + +#[cfg(not(target_os = "linux"))] +fn configure_tray_icon_interactions( + builder: TrayIconBuilder, +) -> TrayIconBuilder { + builder + .show_menu_on_left_click(false) + .on_tray_icon_event(|tray, event| { + let app = tray.app_handle(); + handle_tray_double_click(app, &event); + }) +} + +#[cfg(target_os = "linux")] +fn configure_tray_icon_interactions( + builder: TrayIconBuilder, +) -> TrayIconBuilder { + builder +} + +// libappindicator panics (rather than returning an error) when the appindicator +// library is missing, which would crash the whole app. Probe for it first and +// treat its absence as a recoverable tray failure. +#[cfg(target_os = "linux")] +fn appindicator_available() -> bool { + const CANDIDATES: [&str; 4] = [ + "libayatana-appindicator3.so.1", + "libappindicator3.so.1", + "libayatana-appindicator3.so", + "libappindicator3.so", + ]; + CANDIDATES + .iter() + .any(|name| unsafe { libloading::Library::new(*name) }.is_ok()) +} + +// The AppImage bundles libayatana-appindicator, so the library probe passes on +// every desktop. Without a StatusNotifierItem host the icon is created and never +// rendered, which strands the window with no way to restore it. +#[cfg(target_os = "linux")] +fn status_notifier_host_available() -> bool { + const WATCHER: &str = "org.kde.StatusNotifierWatcher"; + let Ok(name) = zbus::names::BusName::try_from(WATCHER) else { + return false; + }; + let Ok(connection) = zbus::blocking::Connection::session() else { + return false; + }; + let Ok(dbus) = zbus::blocking::fdo::DBusProxy::new(&connection) else { + return false; + }; + dbus.name_has_owner(name).unwrap_or(false) +} + +pub fn create_system_tray(app: &AppHandle) -> tauri::Result<()> { + #[cfg(target_os = "linux")] + if !appindicator_available() { + return Err(std::io::Error::new( + std::io::ErrorKind::NotFound, + "appindicator library not found; skipping system tray", + ) + .into()); + } + + #[cfg(target_os = "linux")] + if !status_notifier_host_available() { + return Err(std::io::Error::new( + std::io::ErrorKind::NotFound, + "no StatusNotifierWatcher on the session bus; skipping system tray", + ) + .into()); + } + + let show_item = MenuItem::with_id(app, TRAY_MENU_SHOW_ID, "Show", true, None::<&str>)?; + let quit_item = MenuItem::with_id(app, TRAY_MENU_QUIT_ID, "Quit", true, None::<&str>)?; + let tray_menu = Menu::with_items(app, &[&show_item, &quit_item])?; + + let mut tray_builder = configure_tray_icon_interactions( + TrayIconBuilder::with_id(MAIN_TRAY_ID) + .menu(&tray_menu) + .on_menu_event(|app, event: MenuEvent| match event.id().as_ref() { + TRAY_MENU_SHOW_ID => { + let _ = crate::show_or_create_main_window(app); + } + TRAY_MENU_QUIT_ID => { + app.exit(0); + } + _ => {} + }), + ); + + if let Some(icon) = app.default_window_icon() { + tray_builder = tray_builder.icon(icon.clone()); + } + + tray_builder.build(app)?; + + #[cfg(target_os = "linux")] + crate::desktop::tray_badge::reset_badge_state(); + + Ok(()) +} + +#[cfg(test)] +fn tray_icon_events_supported() -> bool { + !cfg!(target_os = "linux") +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::desktop::settings::{ + desktop_settings_from_values, tray_available_for_session, DesktopSettings, + }; + + const TRAY_UP: DesktopRuntimeState = DesktopRuntimeState { + tray_available: true, + }; + const NO_TRAY: DesktopRuntimeState = DesktopRuntimeState { + tray_available: false, + }; + + #[test] + fn close_behavior_keeps_sable_running() { + let settings = DesktopSettings { + close_to_background_on_close: true, + show_system_tray_icon: true, + use_custom_title_bar: false, + spellcheck: true, + }; + + assert_eq!( + exit_request_action(settings, TRAY_UP, None), + ExitRequestAction::CloseWindowsToBackground + ); + } + + #[test] + fn tray_setting_does_not_keep_sable_running_on_its_own() { + let settings = DesktopSettings { + show_system_tray_icon: true, + close_to_background_on_close: false, + use_custom_title_bar: false, + spellcheck: true, + }; + + assert_eq!( + exit_request_action(settings, TRAY_UP, None), + ExitRequestAction::AllowExit + ); + } + + #[test] + fn tray_failure_allows_exit_instead_of_stranding_the_process() { + let settings = DesktopSettings { + close_to_background_on_close: true, + show_system_tray_icon: true, + use_custom_title_bar: false, + spellcheck: true, + }; + + assert!(!tray_available_for_session(settings, false)); + assert_eq!( + exit_request_action(settings, NO_TRAY, None), + if cfg!(target_os = "macos") { + ExitRequestAction::CloseWindowsToBackground + } else { + ExitRequestAction::AllowExit + } + ); + } + + #[test] + fn macos_closes_to_background_without_a_tray() { + assert_eq!( + can_restore_from_background(NO_TRAY), + cfg!(target_os = "macos") + ); + assert!(can_restore_from_background(TRAY_UP)); + } + + #[test] + fn explicit_quit_bypasses_background_mode() { + let settings = DesktopSettings { + close_to_background_on_close: true, + show_system_tray_icon: true, + use_custom_title_bar: false, + spellcheck: true, + }; + + assert_eq!( + exit_request_action(settings, TRAY_UP, Some(0)), + ExitRequestAction::AllowExit + ); + } + + #[test] + fn missing_store_values_default_to_enabled() { + assert_eq!( + desktop_settings_from_values(None, None, None, None, None), + DesktopSettings { + close_to_background_on_close: true, + show_system_tray_icon: true, + use_custom_title_bar: use_custom_title_bar_default(), + spellcheck: true, + } + ); + } + + #[test] + fn legacy_background_store_value_migrates_to_close_behavior() { + assert_eq!( + desktop_settings_from_values(Some(false), Some(false), Some(false), None, Some(true)), + DesktopSettings { + close_to_background_on_close: true, + show_system_tray_icon: false, + use_custom_title_bar: false, + spellcheck: true, + } + ); + } + + #[test] + fn explicit_store_values_are_preserved_when_legacy_background_is_off() { + assert_eq!( + desktop_settings_from_values(Some(false), Some(false), Some(true), None, Some(false)), + DesktopSettings { + show_system_tray_icon: false, + close_to_background_on_close: false, + use_custom_title_bar: true, + spellcheck: true, + } + ); + } + + #[test] + fn tray_icon_event_support_matches_platform() { + assert_eq!(tray_icon_events_supported(), !cfg!(target_os = "linux")); + } +} diff --git a/src-tauri/src/desktop/tray_badge.rs b/src-tauri/src/desktop/tray_badge.rs new file mode 100644 index 0000000000..e73b8631b8 --- /dev/null +++ b/src-tauri/src/desktop/tray_badge.rs @@ -0,0 +1,128 @@ +//! Unread dot for the Linux system tray icon. Linux trays have no native +//! badge API and the CEF webview never exposes the web Badging API, so the +//! unread indicator is painted onto the icon bitmap and swapped in via +//! `TrayIcon::set_icon`. + +use std::sync::atomic::{AtomicBool, Ordering}; + +use tauri::image::Image; +use tauri::AppHandle; + +use crate::desktop::tray::MAIN_TRAY_ID; + +static BADGE_SHOWN: AtomicBool = AtomicBool::new(false); + +/// Clears the tracked dot state after the tray icon is (re)built. +pub fn reset_badge_state() { + BADGE_SHOWN.store(false, Ordering::Relaxed); +} + +fn render_badged_icon(base: &Image<'_>) -> Image<'static> { + let width = base.width(); + let height = base.height(); + let mut rgba = base.rgba().to_vec(); + + if width == 0 || height == 0 { + return Image::new_owned(rgba, width, height); + } + + let w = width as f32; + let h = height as f32; + + let diameter = w.min(h) * 0.4; + let radius = diameter / 2.0; + let cx = w - radius - w * 0.04; + let cy = h - radius - h * 0.04; + + let red: [f32; 3] = [0.878, 0.176, 0.176]; + + let x0 = (cx - radius - 1.0).floor().max(0.0) as u32; + let y0 = (cy - radius - 1.0).floor().max(0.0) as u32; + let x1 = ((cx + radius + 1.0).ceil() as u32).min(width); + let y1 = ((cy + radius + 1.0).ceil() as u32).min(height); + + for py in y0..y1 { + for px in x0..x1 { + let dx = px as f32 + 0.5 - cx; + let dy = py as f32 + 0.5 - cy; + let dist = (dx * dx + dy * dy).sqrt(); + + // Hard edge: antialiased pixels turn into a visible ring once the + // tray premultiplies the pixmap, so keep every dot pixel opaque. + if dist > radius { + continue; + } + + let idx = ((py * width + px) * 4) as usize; + rgba[idx] = (red[0] * 255.0).round() as u8; + rgba[idx + 1] = (red[1] * 255.0).round() as u8; + rgba[idx + 2] = (red[2] * 255.0).round() as u8; + rgba[idx + 3] = 255; + } + } + + Image::new_owned(rgba, width, height) +} + +/// Redraws the tray icon with or without the unread dot. `count` of `None` or +/// `Some(0)` restores the plain icon. +pub fn apply_tray_badge(app: &AppHandle, count: Option) { + let want_dot = matches!(count, Some(count) if count > 0); + + // Swapping the pixmap makes the icon blink, so only touch it on a change. + if BADGE_SHOWN.swap(want_dot, Ordering::Relaxed) == want_dot { + return; + } + + let Some(tray) = app.tray_by_id(MAIN_TRAY_ID) else { + BADGE_SHOWN.store(false, Ordering::Relaxed); + return; + }; + let Some(base) = app.default_window_icon() else { + BADGE_SHOWN.store(false, Ordering::Relaxed); + return; + }; + + let result = if want_dot { + tray.set_icon(Some(render_badged_icon(base))) + } else { + tray.set_icon(Some(base.to_owned())) + }; + + if let Err(error) = result { + log::warn!("Failed to update tray badge: {error}"); + BADGE_SHOWN.store(false, Ordering::Relaxed); + } +} + +#[tauri::command] +pub fn set_tray_badge( + app: AppHandle, + count: Option, +) -> Result<(), String> { + apply_tray_badge(&app, count); + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn badge_modifies_bottom_right_pixels() { + let width = 32; + let height = 32; + let base = Image::new_owned(vec![0u8; (width * height * 4) as usize], width, height); + let out = render_badged_icon(&base); + let idx = (((height - 7) * width + (width - 7)) * 4) as usize; + assert!(out.rgba()[idx + 3] > 0); + assert_eq!(&out.rgba()[0..4], &[0, 0, 0, 0]); + } + + #[test] + fn dimensions_are_preserved() { + let base = Image::new_owned([10u8, 20, 30, 255].repeat(16), 4, 4); + let out = render_badged_icon(&base); + assert_eq!((out.width(), out.height()), (4, 4)); + } +} diff --git a/src-tauri/src/desktop/windows/mod.rs b/src-tauri/src/desktop/windows/mod.rs new file mode 100644 index 0000000000..f5ac1c7f6e --- /dev/null +++ b/src-tauri/src/desktop/windows/mod.rs @@ -0,0 +1,5 @@ +#[cfg(windows)] +pub mod snap_overlay; + +#[cfg(windows)] +pub mod window_tracking; diff --git a/src-tauri/src/desktop/windows/snap_overlay.rs b/src-tauri/src/desktop/windows/snap_overlay.rs new file mode 100644 index 0000000000..11ebe7d780 --- /dev/null +++ b/src-tauri/src/desktop/windows/snap_overlay.rs @@ -0,0 +1,37 @@ +use enigo::{ + Direction::{Click, Press, Release}, + Enigo, Key, Keyboard, Settings, +}; + +fn new_enigo() -> Result { + Enigo::new(&Settings::default()).map_err(|error| error.to_string()) +} + +#[tauri::command] +pub async fn show_snap_overlay() -> Result<(), String> { + let mut enigo = new_enigo()?; + + enigo + .key(Key::Meta, Press) + .map_err(|error| error.to_string())?; + + // Release Meta even if the chord fails, or it stays held for the session. + let chord = enigo.key(Key::Unicode('z'), Click); + let release = enigo.key(Key::Meta, Release); + chord.map_err(|error| error.to_string())?; + release.map_err(|error| error.to_string())?; + + std::thread::sleep(std::time::Duration::from_millis(100)); + + enigo + .key(Key::Alt, Click) + .map_err(|error| error.to_string()) +} + +#[tauri::command] +pub async fn hide_snap_overlay() -> Result<(), String> { + let mut enigo = new_enigo()?; + enigo + .key(Key::Escape, Click) + .map_err(|error| error.to_string()) +} diff --git a/src-tauri/src/desktop/windows/window_tracking.rs b/src-tauri/src/desktop/windows/window_tracking.rs new file mode 100644 index 0000000000..fa00afc7cb --- /dev/null +++ b/src-tauri/src/desktop/windows/window_tracking.rs @@ -0,0 +1,294 @@ +use std::{ + ffi::OsString, + os::windows::ffi::OsStringExt, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + time::{Duration, Instant}, +}; + +use serde::{Deserialize, Serialize}; +use tauri::{AppHandle, Emitter, State}; +use windows::Win32::{ + Foundation::{CloseHandle, HWND, POINT}, + System::{ + ProcessStatus::GetModuleBaseNameW, + Threading::{OpenProcess, PROCESS_QUERY_INFORMATION, PROCESS_VM_READ}, + }, + UI::WindowsAndMessaging::{ + GetClassNameW, GetCursorPos, GetWindowThreadProcessId, WindowFromPoint, + }, +}; + +#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] +pub struct WindowInfo { + pub mouse_x: i32, + pub mouse_y: i32, + pub window_class: Option, + pub exe_name: Option, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] +pub struct WindowTarget { + pub window_class: Option, + pub exe_name: Option, +} + +impl WindowTarget { + pub fn new(window_class: Option, exe_name: Option) -> Self { + Self { + window_class, + exe_name, + } + } + + pub fn matches(&self, window_info: &WindowInfo) -> bool { + let class_matches = match &self.window_class { + Some(target_class) => window_info + .window_class + .as_ref() + .map(|class| class.eq_ignore_ascii_case(target_class)) + .unwrap_or(false), + None => true, + }; + + let exe_matches = match &self.exe_name { + Some(target_exe) => window_info + .exe_name + .as_ref() + .map(|exe| exe.eq_ignore_ascii_case(target_exe)) + .unwrap_or(false), + None => true, + }; + + class_matches && exe_matches + } +} + +impl WindowInfo { + pub fn matches_target(&self, target: &WindowTarget) -> bool { + target.matches(self) + } +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] +pub enum EventType { + Started, + TargetLost, + Timeout, + Stopped, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] +pub struct TrackingEvent { + pub event_type: EventType, + pub window_info: Option, + pub target: WindowTarget, + pub message: String, +} + +pub struct TrackingState { + active: AtomicBool, +} + +impl TrackingState { + pub fn new() -> Self { + Self { + active: AtomicBool::new(false), + } + } + + pub fn is_active(&self) -> bool { + self.active.load(Ordering::Relaxed) + } + + pub fn set_active(&self, active: bool) { + self.active.store(active, Ordering::Relaxed); + } +} + +impl Default for TrackingState { + fn default() -> Self { + Self::new() + } +} + +fn get_window_class_name(hwnd: HWND) -> Result> { + let mut buffer = [0u16; 256]; + let len = unsafe { GetClassNameW(hwnd, &mut buffer) }; + if len == 0 { + return Err("Failed to get class name".into()); + } + Ok(OsString::from_wide(&buffer[..len as usize]) + .to_string_lossy() + .into_owned()) +} + +fn get_exe_name_from_window(hwnd: HWND) -> Result> { + let mut process_id = 0; + unsafe { + GetWindowThreadProcessId(hwnd, Some(&mut process_id)); + } + if process_id == 0 { + return Err("Failed to get process id".into()); + } + + let process_handle = unsafe { + OpenProcess( + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + false, + process_id, + ) + }?; + + let mut buffer = [0u16; 260]; + let len = unsafe { GetModuleBaseNameW(process_handle, None, &mut buffer) }; + let _ = unsafe { CloseHandle(process_handle) }; + + if len == 0 { + return Err("Failed to get process name".into()); + } + + Ok(OsString::from_wide(&buffer[..len as usize]) + .to_string_lossy() + .into_owned()) +} + +fn get_window_info() -> Result> { + let mut point = POINT { x: 0, y: 0 }; + unsafe { GetCursorPos(&raw mut point) }?; + + let hwnd = unsafe { WindowFromPoint(point) }; + + let window_class = if !hwnd.0.is_null() { + get_window_class_name(hwnd).ok() + } else { + None + }; + + let exe_name = if !hwnd.0.is_null() { + get_exe_name_from_window(hwnd).ok() + } else { + None + }; + + Ok(WindowInfo { + mouse_x: point.x, + mouse_y: point.y, + window_class, + exe_name, + }) +} + +async fn track_window_hover_with_target( + app_handle: AppHandle, + target: WindowTarget, + tracking_state: Arc, +) { + let start_time = Instant::now(); + let timeout_duration = Duration::from_millis(200); + let check_interval = Duration::from_millis(100); + + let mut was_on_target = false; + + while tracking_state.is_active() { + if start_time.elapsed() >= timeout_duration && !was_on_target { + let event = TrackingEvent { + event_type: EventType::Timeout, + window_info: None, + target: target.clone(), + message: "Tracking timed out before reaching target".to_owned(), + }; + + let _ = app_handle.emit("window-tracking", &event); + tracking_state.set_active(false); + break; + } + + if let Ok(window_info) = get_window_info() { + let is_on_target = window_info.matches_target(&target); + + if was_on_target && !is_on_target { + let event = TrackingEvent { + event_type: EventType::TargetLost, + window_info: Some(window_info), + target: target.clone(), + message: "Pointer moved away from snap popup".to_owned(), + }; + let _ = app_handle.emit("window-tracking", &event); + tracking_state.set_active(false); + break; + } + + was_on_target = is_on_target; + } + + tokio::time::sleep(check_interval).await; + } +} + +#[tauri::command] +pub async fn start_window_tracking_with_target( + app_handle: AppHandle, + target: WindowTarget, + tracking_state: State<'_, Arc>, +) -> Result<(), String> { + if tracking_state.is_active() { + return Ok(()); + } + + tracking_state.set_active(true); + + let event = TrackingEvent { + event_type: EventType::Started, + window_info: None, + target: target.clone(), + message: "Window tracking started".to_owned(), + }; + + app_handle + .emit("window-tracking", &event) + .map_err(|error| format!("Failed to emit start event: {error}"))?; + + let app_handle_clone = app_handle.clone(); + let tracking_state_clone = tracking_state.inner().clone(); + + tauri::async_runtime::spawn(async move { + track_window_hover_with_target(app_handle_clone, target, tracking_state_clone).await; + }); + + Ok(()) +} + +#[tauri::command] +pub async fn stop_window_tracking( + app_handle: AppHandle, + tracking_state: State<'_, Arc>, +) -> Result<(), String> { + if !tracking_state.is_active() { + return Ok(()); + } + + tracking_state.set_active(false); + + let event = TrackingEvent { + event_type: EventType::Stopped, + window_info: None, + target: WindowTarget::new(None, None), + message: "Window tracking stopped".to_owned(), + }; + + app_handle + .emit("window-tracking", &event) + .map_err(|error| format!("Failed to emit stop event: {error}"))?; + + Ok(()) +} + +#[tauri::command] +pub async fn is_window_tracking_active( + tracking_state: State<'_, Arc>, +) -> Result { + Ok(tracking_state.is_active()) +} diff --git a/src-tauri/src/diagnostics.rs b/src-tauri/src/diagnostics.rs new file mode 100644 index 0000000000..65299b684f --- /dev/null +++ b/src-tauri/src/diagnostics.rs @@ -0,0 +1,398 @@ +use std::fs::{self, File}; +#[cfg(any(target_os = "android", target_os = "ios", test))] +use std::io::Cursor; +use std::io::{self, Seek, Write}; +use std::path::{Path, PathBuf}; +use std::time::{SystemTime, UNIX_EPOCH}; + +use regex::Regex; +use serde_json::{Map, Value}; +use zip::write::SimpleFileOptions; +use zip::{CompressionMethod, ZipWriter}; + +const MAX_LOG_FILES: usize = 6; + +pub(crate) fn system_info(version: &str, os: &str, architecture: &str) -> String { + format!("Sable diagnostics\nApp version: {version}\nOS: {os}\nArchitecture: {architecture}\n") +} + +fn sensitive_paths() -> Vec { + let mut paths = vec![std::env::temp_dir()]; + for variable in ["HOME", "USERPROFILE"] { + if let Some(path) = std::env::var_os(variable).map(PathBuf::from) { + paths.push(path); + } + } + paths +} + +fn replace_pattern(text: String, pattern: &str, replacement: &str) -> String { + Regex::new(pattern) + .expect("diagnostics redaction pattern must be valid") + .replace_all(&text, replacement) + .into_owned() +} + +fn redact_text_with_paths(text: &str, paths: &[PathBuf]) -> String { + let mut sanitized = replace_pattern( + text.to_owned(), + r#"(?i)\b[a-z][a-z0-9+.-]{1,15}://[^\s<>\"']+"#, + "[REDACTED_URL]", + ); + sanitized = replace_pattern( + sanitized, + r#"(?i)\b(?:Bearer|Basic)\s+[A-Za-z0-9._~+/=-]+"#, + "[REDACTED_CREDENTIAL]", + ); + sanitized = replace_pattern( + sanitized, + r#"(?i)([\"']?(?:authorization|proxy[-_]?authorization|cookie|set[-_]?cookie|access[-_]?token|refresh[-_]?token|id[-_]?token|x[-_]?auth[-_]?token|x[-_]?access[-_]?token|api[-_]?key|client[-_]?secret|password|passwd|secret|credential|token)[\"']?\s*[:=]\s*)(?:\"[^\"]*\"|'[^']*'|[^\s,;}\]]+)"#, + "$1[REDACTED]", + ); + sanitized = replace_pattern( + sanitized, + r#"@[A-Za-z0-9._=/\\-]+:(?:\[[0-9A-Fa-f:]+\]|[A-Za-z0-9.-]+)(?::[0-9]+)?"#, + "[REDACTED_MATRIX_USER_ID]", + ); + sanitized = replace_pattern( + sanitized, + r#"[!#][A-Za-z0-9._=/\\-]+:(?:\[[0-9A-Fa-f:]+\]|[A-Za-z0-9.-]+)(?::[0-9]+)?"#, + "[REDACTED_MATRIX_ROOM_ID]", + ); + sanitized = replace_pattern( + sanitized, + r#"\$[A-Za-z0-9._~=/\\-]+(?::(?:\[[0-9A-Fa-f:]+\]|[A-Za-z0-9.-]+)(?::[0-9]+)?)?"#, + "[REDACTED_MATRIX_EVENT_ID]", + ); + let mut path_strings = paths + .iter() + .filter(|path| path.is_absolute()) + .map(|path| path.to_string_lossy().into_owned()) + .filter(|path| path.len() > 1) + .collect::>(); + path_strings.sort_by_key(|path| std::cmp::Reverse(path.len())); + for path in path_strings { + sanitized = sanitized.replace(&path, "[REDACTED_PATH]"); + } + sanitized +} + +fn redact_text(text: &str) -> String { + redact_text_with_paths(text, &sensitive_paths()) +} + +fn is_sensitive_json_key(key: &str) -> bool { + let normalized = key + .chars() + .filter(|character| character.is_ascii_alphanumeric()) + .flat_map(char::to_lowercase) + .collect::(); + matches!( + normalized.as_str(), + "authorization" + | "proxyauthorization" + | "cookie" + | "setcookie" + | "token" + | "accesstoken" + | "refreshtoken" + | "idtoken" + | "xauthtoken" + | "xaccesstoken" + | "apikey" + | "clientsecret" + | "password" + | "passwd" + | "secret" + | "credential" + | "credentials" + | "deviceid" + | "sessionid" + ) || normalized.ends_with("token") + || normalized.ends_with("secret") + || normalized.ends_with("password") +} + +fn sanitize_headers(value: &Value, paths: &[PathBuf]) -> Value { + match value { + Value::Object(headers) => Value::Object( + headers + .keys() + .map(|key| (key.clone(), Value::String("[REDACTED]".into()))) + .collect(), + ), + Value::Array(headers) => Value::Array( + headers + .iter() + .map(|header| match header { + Value::Array(pair) if pair.len() >= 2 => Value::Array(vec![ + Value::String(redact_text_with_paths( + pair.first().and_then(Value::as_str).unwrap_or("header"), + paths, + )), + Value::String("[REDACTED]".into()), + ]), + _ => sanitize_json_value(header, paths).unwrap_or(Value::Null), + }) + .collect(), + ), + _ => sanitize_json_value(value, paths).unwrap_or(Value::Null), + } +} + +fn sanitize_json_value(value: &Value, paths: &[PathBuf]) -> Option { + match value { + Value::Object(object) => { + let mut sanitized = Map::new(); + for (key, value) in object { + if key.eq_ignore_ascii_case("data") { + continue; + } + let value = if key.eq_ignore_ascii_case("headers") { + sanitize_headers(value, paths) + } else if is_sensitive_json_key(key) { + Value::String("[REDACTED]".into()) + } else { + sanitize_json_value(value, paths)? + }; + sanitized.insert(key.clone(), value); + } + Some(Value::Object(sanitized)) + } + Value::Array(array) => Some(Value::Array( + array + .iter() + .filter_map(|value| sanitize_json_value(value, paths)) + .collect(), + )), + Value::String(string) => Some(Value::String(redact_text_with_paths(string, paths))), + Value::Null | Value::Bool(_) | Value::Number(_) => Some(value.clone()), + } +} + +fn sanitized_frontend_logs(serialized: &str) -> Option { + let value = serde_json::from_str::(serialized).ok()?; + if value.is_null() { + return None; + } + let sanitized = sanitize_json_value(&value, &sensitive_paths())?; + serde_json::to_string_pretty(&sanitized).ok() +} + +fn recent_log_files(log_dir: Option<&Path>) -> io::Result> { + let Some(log_dir) = log_dir else { + return Ok(Vec::new()); + }; + if !log_dir.is_dir() { + return Ok(Vec::new()); + } + let mut files = fs::read_dir(log_dir)? + .filter_map(Result::ok) + .filter_map(|entry| { + let file_type = entry.file_type().ok()?; + if !file_type.is_file() + || entry + .path() + .extension() + .and_then(|extension| extension.to_str()) + != Some("log") + { + return None; + } + Some(entry.path()) + }) + .collect::>(); + files.sort_by(|left, right| { + let left_modified = fs::metadata(left) + .and_then(|metadata| metadata.modified()) + .unwrap_or(SystemTime::UNIX_EPOCH); + let right_modified = fs::metadata(right) + .and_then(|metadata| metadata.modified()) + .unwrap_or(SystemTime::UNIX_EPOCH); + right_modified + .cmp(&left_modified) + .then_with(|| left.file_name().cmp(&right.file_name())) + }); + files.truncate(MAX_LOG_FILES); + Ok(files) +} + +fn write_archive( + output: W, + log_dir: Option<&Path>, + system_info: &str, + frontend_logs: Option, +) -> io::Result { + let mut writer = ZipWriter::new(output); + let options = SimpleFileOptions::default().compression_method(CompressionMethod::Deflated); + for log_path in recent_log_files(log_dir)? { + let Some(file_name) = log_path.file_name().and_then(|name| name.to_str()) else { + continue; + }; + let Ok(log_contents) = fs::read_to_string(&log_path) else { + continue; + }; + writer + .start_file(format!("logs/{file_name}"), options) + .map_err(io::Error::other)?; + writer.write_all(redact_text(&log_contents).as_bytes())?; + } + writer + .start_file("system-info.txt", options) + .map_err(io::Error::other)?; + writer.write_all(system_info.as_bytes())?; + if let Some(frontend_logs) = frontend_logs { + writer + .start_file("logs/frontend-debug-logs.json", options) + .map_err(io::Error::other)?; + writer.write_all(frontend_logs.as_bytes())?; + } + writer.finish().map_err(io::Error::other) +} + +#[cfg(any(target_os = "android", target_os = "ios", test))] +pub(crate) fn build_archive( + log_dir: Option<&Path>, + system_info: &str, + frontend_logs: Option<&str>, +) -> io::Result> { + let output = write_archive( + Cursor::new(Vec::new()), + log_dir, + system_info, + frontend_logs.and_then(sanitized_frontend_logs), + )?; + Ok(output.into_inner()) +} + +pub(crate) fn create_archive( + archive_path: &Path, + log_dir: &Path, + system_info: &str, + frontend_logs: Option<&str>, +) -> io::Result<()> { + let file_name = archive_path + .file_name() + .and_then(|name| name.to_str()) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Invalid archive filename"))?; + let suffix = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_err(io::Error::other)? + .as_nanos(); + let temporary_path = archive_path.with_file_name(format!( + ".{file_name}.{}.{}.tmp", + std::process::id(), + suffix + )); + let result = (|| { + let file = File::create(&temporary_path)?; + write_archive( + file, + Some(log_dir), + system_info, + frontend_logs.and_then(sanitized_frontend_logs), + )?; + fs::rename(&temporary_path, archive_path) + })(); + if let Err(error) = result { + let _ = fs::remove_file(&temporary_path); + return Err(error); + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::{build_archive, create_archive, system_info}; + use serde_json::json; + use std::fs::{self, File}; + use std::io::Read; + use std::path::PathBuf; + use std::time::{SystemTime, UNIX_EPOCH}; + + struct TemporaryDirectory(PathBuf); + impl Drop for TemporaryDirectory { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.0); + } + } + fn temporary_directory() -> TemporaryDirectory { + let suffix = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("system time must be after the Unix epoch") + .as_nanos(); + let path = std::env::temp_dir().join(format!( + "sable-diagnostics-test-{}-{suffix}", + std::process::id() + )); + fs::create_dir(&path).expect("temporary diagnostics directory should be creatable"); + TemporaryDirectory(path) + } + + #[test] + fn mobile_archive_contains_system_info_without_native_logs() { + let bytes = build_archive(None, &system_info("1.2.3", "android", "aarch64"), None) + .expect("diagnostics archive should be created"); + let mut archive = + zip::ZipArchive::new(std::io::Cursor::new(bytes)).expect("archive should be valid ZIP"); + let mut info = String::new(); + archive + .by_name("system-info.txt") + .expect("system info should be archived") + .read_to_string(&mut info) + .expect("system info should be readable"); + assert_eq!( + info, + "Sable diagnostics\nApp version: 1.2.3\nOS: android\nArchitecture: aarch64\n" + ); + assert!(archive.by_name("logs/frontend-debug-logs.json").is_err()); + } + + #[test] + fn archive_sanitizes_frontend_logs() { + let logs = serde_json::to_string(&json!([{ + "message": "accessToken=frontend-token https://example.org/path", + "data": { "secret": "must not be included" }, + "headers": { "authorization": "header-secret" } + }])) + .expect("frontend logs should serialize"); + let bytes = build_archive(None, "Sable diagnostics\n", Some(&logs)) + .expect("diagnostics archive should be created"); + let mut archive = + zip::ZipArchive::new(std::io::Cursor::new(bytes)).expect("archive should be valid ZIP"); + let mut sanitized = String::new(); + archive + .by_name("logs/frontend-debug-logs.json") + .expect("frontend logs should be archived") + .read_to_string(&mut sanitized) + .expect("frontend logs should be readable"); + assert!(!sanitized.contains("frontend-token")); + assert!(!sanitized.contains("https://example.org")); + assert!(!sanitized.contains("must not be included")); + assert!(!sanitized.contains("header-secret")); + } + + #[test] + fn desktop_archive_includes_sanitized_native_logs() { + let directory = temporary_directory(); + let archive_path = directory.0.join("diagnostics.zip"); + fs::write( + directory.0.join("sable.log"), + "token=native-token https://example.org/path\n", + ) + .expect("native log should be writable"); + create_archive(&archive_path, &directory.0, "Sable diagnostics\n", None) + .expect("diagnostics archive should be created"); + let mut archive = + zip::ZipArchive::new(File::open(archive_path).expect("archive should open")) + .expect("archive should be valid ZIP"); + let mut log = String::new(); + archive + .by_name("logs/sable.log") + .expect("native log should be archived") + .read_to_string(&mut log) + .expect("native log should be readable"); + assert!(!log.contains("native-token")); + assert!(!log.contains("https://example.org")); + } +} diff --git a/src-tauri/src/ios.rs b/src-tauri/src/ios.rs new file mode 100644 index 0000000000..ac88bc6295 --- /dev/null +++ b/src-tauri/src/ios.rs @@ -0,0 +1,502 @@ +// iOS shows a form accessory bar (prev/next arrows + Done) above the keyboard +// for web inputs. WKWebView exposes no API to disable it, so swap the private +// WKContentView's class for a runtime subclass whose inputAccessoryView is nil, +// the same approach as Capacitor's hideFormAccessoryBar. + +use std::ffi::CString; +use std::sync::OnceLock; + +use objc2::rc::{Allocated, Retained}; +use objc2::runtime::{ + AnyClass, AnyObject, ClassBuilder, NSObject, NSObjectProtocol, ProtocolObject, Sel, +}; +use objc2::{define_class, msg_send, sel, AnyThread, ClassType}; +use objc2_avf_audio::AVAudioSession; +use objc2_call_kit::{ + CXCallController, CXCallUpdate, CXEndCallAction, CXHandle, CXHandleType, CXProvider, + CXProviderConfiguration, CXProviderDelegate, CXStartCallAction, CXTransaction, +}; +use objc2_foundation::{NSString, NSUUID}; +use tauri::webview::WebviewWindow; + +extern "C-unwind" fn input_accessory_view_nil(_this: &AnyObject, _cmd: Sel) -> *mut AnyObject { + std::ptr::null_mut() +} + +// Edge-swipe back, matching Android's system back gesture. wry leaves +// allowsBackForwardNavigationGestures off; react-router entries are +// same-document navigations, which WKWebView tracks in its back-forward list. +pub fn enable_swipe_back_navigation(window: &WebviewWindow) { + let _ = window.with_webview(|webview| unsafe { + let webview: *mut AnyObject = webview.inner().cast(); + let _: () = msg_send![&*webview, setAllowsBackForwardNavigationGestures: true]; + }); +} + +// UIFeedbackGenerator must run on the main thread, where Tauri schedules mobile +// commands. +#[tauri::command] +pub fn haptic_feedback(style: String) { + unsafe { + if style == "selection" { + let Some(cls) = AnyClass::get(c"UISelectionFeedbackGenerator") else { + return; + }; + let allocated: Allocated = msg_send![cls, alloc]; + let generator: Retained = msg_send![allocated, init]; + let _: () = msg_send![&*generator, prepare]; + let _: () = msg_send![&*generator, selectionChanged]; + } else { + // UIImpactFeedbackStyle: light = 0, medium = 1, heavy = 2. + let intensity: isize = match style.as_str() { + "medium" => 1, + "heavy" => 2, + _ => 0, + }; + let Some(cls) = AnyClass::get(c"UIImpactFeedbackGenerator") else { + return; + }; + let allocated: Allocated = msg_send![cls, alloc]; + let generator: Retained = msg_send![allocated, initWithStyle: intensity]; + let _: () = msg_send![&*generator, prepare]; + let _: () = msg_send![&*generator, impactOccurred]; + } + } +} + +#[tauri::command] +pub fn activate_call_audio_session() -> Result<(), String> { + callkit::start_call() +} + +#[tauri::command] +pub fn deactivate_call_audio_session() -> Result<(), String> { + callkit::end_call() +} + +// CallKit owns the audio session and keeps the process alive in the +// background via the `voip` UIBackgroundMode (Apple rejects `voip` without it). + +use std::cell::RefCell; + +thread_local! { + static CALLKIT: RefCell = RefCell::new(CallKitState::default()); +} + +#[derive(Default)] +struct CallKitState { + provider: Option>, + controller: Option>, + uuid: Option>, +} + +// Minimal CXProviderDelegate: calls are controlled from Sable's UI, so the +// perform* methods just fulfill. +define_class!( + #[unsafe(super(NSObject))] + #[name = "SableCallKitDelegate"] + struct SableCallKitDelegate; + + unsafe impl NSObjectProtocol for SableCallKitDelegate {} + + unsafe impl CXProviderDelegate for SableCallKitDelegate { + #[unsafe(method(providerDidReset:))] + fn providerDidReset(&self, _provider: &CXProvider) { + CALLKIT.with(|s| s.borrow_mut().uuid = None); + } + + #[unsafe(method(provider:didActivateAudioSession:))] + fn provider_didActivateAudioSession( + &self, + _provider: &CXProvider, + _session: &AVAudioSession, + ) { + } + + #[unsafe(method(provider:didDeactivateAudioSession:))] + fn provider_didDeactivateAudioSession( + &self, + _provider: &CXProvider, + _session: &AVAudioSession, + ) { + } + + #[unsafe(method(provider:performStartCallAction:))] + fn provider_performStartCallAction( + &self, + _provider: &CXProvider, + action: &CXStartCallAction, + ) { + unsafe { action.fulfill() }; + } + + #[unsafe(method(provider:performEndCallAction:))] + fn provider_performEndCallAction(&self, _provider: &CXProvider, action: &CXEndCallAction) { + unsafe { action.fulfill() }; + } + } +); + +mod callkit { + use super::*; + + fn ensure_provider() -> Result<(), String> { + CALLKIT.with(|s| -> Result<(), String> { + let mut s = s.borrow_mut(); + if s.provider.is_some() && s.controller.is_some() { + return Ok(()); + } + + let config = unsafe { + let allocated = CXProviderConfiguration::alloc(); + let cfg = CXProviderConfiguration::init(allocated); + cfg.setSupportsVideo(true); + cfg.setMaximumCallsPerCallGroup(1); + cfg.setMaximumCallGroups(1); + cfg.setIncludesCallsInRecents(true); + cfg + }; + + let delegate = { + let allocated = SableCallKitDelegate::alloc(); + let delegate: Retained = + unsafe { msg_send![allocated, init] }; + ProtocolObject::from_retained(delegate) + }; + + let provider = unsafe { + let allocated = CXProvider::alloc(); + CXProvider::initWithConfiguration(allocated, &config) + }; + unsafe { provider.setDelegate_queue(Some(&delegate), None) }; + + let controller = unsafe { + let allocated = CXCallController::alloc(); + CXCallController::init(allocated) + }; + + s.provider = Some(provider); + s.controller = Some(controller); + Ok(()) + }) + } + + pub fn start_call() -> Result<(), String> { + ensure_provider()?; + + CALLKIT.with(|s| -> Result<(), String> { + let mut s = s.borrow_mut(); + + if s.uuid.is_none() { + s.uuid = Some(NSUUID::from_bytes(random_uuid_bytes())); + } + let uuid = s.uuid.as_ref().unwrap().clone(); + let controller = s.controller.as_ref().unwrap().clone(); + + let handle = unsafe { + let allocated = CXHandle::alloc(); + CXHandle::initWithType_value( + allocated, + CXHandleType::Generic, + &NSString::from_str("Sable"), + ) + }; + let action = unsafe { + let allocated = CXStartCallAction::alloc(); + CXStartCallAction::initWithCallUUID_handle(allocated, &uuid, &handle) + }; + let transaction = unsafe { + let allocated = CXTransaction::alloc(); + CXTransaction::initWithAction(allocated, &action) + }; + + let completion: RcBlock = + RcBlock::new(move |_error: *mut objc2_foundation::NSError| {}); + unsafe { + controller.requestTransactionWithAction_completion(&action, &completion); + } + Ok(()) + }) + } + + pub fn end_call() -> Result<(), String> { + CALLKIT.with(|s| -> Result<(), String> { + let mut s = s.borrow_mut(); + let uuid = match s.uuid.take() { + Some(uuid) => uuid, + None => return Ok(()), + }; + let controller = match s.controller.as_ref() { + Some(c) => c.clone(), + None => return Ok(()), + }; + + let action = unsafe { + let allocated = CXEndCallAction::alloc(); + CXEndCallAction::initWithCallUUID(allocated, &uuid) + }; + let completion: RcBlock = + RcBlock::new(move |_error: *mut objc2_foundation::NSError| {}); + unsafe { + controller.requestTransactionWithAction_completion(&action, &completion); + } + Ok(()) + }) + } + + fn random_uuid_bytes() -> [u8; 16] { + use std::time::{SystemTime, UNIX_EPOCH}; + let mut bytes = [0u8; 16]; + let nanos = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|d| d.as_nanos()) + .unwrap_or(0); + let n = nanos.to_le_bytes(); + for i in 0..16 { + bytes[i] = n[i % 16].wrapping_add(i as u8); + } + bytes[6] = (bytes[6] & 0x0F) | 0x40; // RFC 4122 v4 + bytes[8] = (bytes[8] & 0x3F) | 0x80; + bytes + } +} + +pub fn hide_form_accessory_bar(window: &WebviewWindow) { + let _ = window.with_webview(|webview| unsafe { + let webview: *mut AnyObject = webview.inner().cast(); + let scroll_view: *mut AnyObject = msg_send![&*webview, scrollView]; + let subviews: *mut AnyObject = msg_send![&*scroll_view, subviews]; + let count: usize = msg_send![&*subviews, count]; + for index in 0..count { + let subview: *mut AnyObject = msg_send![&*subviews, objectAtIndex: index]; + let class = (*subview).class(); + if !class.name().to_bytes().starts_with(b"WKContent") { + continue; + } + let Ok(subclass_name) = + CString::new(format!("{}_NoAccessoryBar", class.name().to_string_lossy())) + else { + continue; + }; + let subclass = AnyClass::get(&subclass_name).unwrap_or_else(|| { + let mut builder = ClassBuilder::new(&subclass_name, class) + .expect("accessory bar subclass already registered"); + builder.add_method( + sel!(inputAccessoryView), + input_accessory_view_nil as extern "C-unwind" fn(_, _) -> _, + ); + builder.register() + }); + AnyObject::set_class(&*subview, subclass); + } + }); +} + +// WKWebView plays HTMLAudioElement on .playback, ignoring the silent switch. +// AudioServicesPlaySystemSound respects the switch and uses ringer volume. + +use objc2_foundation::NSURL; + +// This library is linked by Cargo before Xcode creates the application bundle, +// so declaring AudioToolbox only in tauri.conf.json is not sufficient. +#[link(name = "AudioToolbox", kind = "framework")] +extern "C" { + fn AudioServicesCreateSystemSoundID( + in_file_url: *const objc2::runtime::AnyObject, + out_sound_id: *mut u32, + ) -> i32; + fn AudioServicesPlaySystemSound(sound_id: u32); +} + +fn load_system_sound(caf_bytes: &[u8], temp_name: &str) -> Result { + // AudioServicesCreateSystemSoundID needs a file URL, so write the + // embedded .caf to the app's temp directory on first use. + let mut path = std::env::temp_dir(); + path.push(temp_name); + if !path.exists() { + std::fs::write(&path, caf_bytes) + .map_err(|error| format!("failed to write {}: {error}", path.display()))?; + } + unsafe { + let path_str = NSString::from_str(&path.to_string_lossy()); + let url = NSURL::fileURLWithPath(&path_str); + let mut sound_id: u32 = 0; + let status = AudioServicesCreateSystemSoundID(Retained::as_ptr(&url).cast(), &mut sound_id); + if status != 0 || sound_id == 0 { + return Err(format!( + "AudioServicesCreateSystemSoundID failed for {} with status {status}", + path.display() + )); + } + Ok(sound_id) + } +} + +pub(crate) fn play_notification_sound(kind: String) -> Result<(), String> { + static NOTIFICATION_SOUND: OnceLock = OnceLock::new(); + static INVITE_SOUND: OnceLock = OnceLock::new(); + + let cache = if kind == "invite" { + &INVITE_SOUND + } else { + &NOTIFICATION_SOUND + }; + let caf: &[u8] = if kind == "invite" { + include_bytes!("../resources/invite.caf") + } else { + include_bytes!("../resources/notification.caf") + }; + let name = if kind == "invite" { + "sable_invite.caf" + } else { + "sable_notification.caf" + }; + + // Not get_or_init: a failed load must not be cached as permanent silence. + let sound_id = match cache.get() { + Some(id) => *id, + None => { + let id = load_system_sound(caf, name)?; + let _ = cache.set(id); + id + } + }; + unsafe { AudioServicesPlaySystemSound(sound_id) }; + Ok(()) +} + +// PhotoKit lane for saving images to the camera roll with add-only access; +// the prompt text is NSPhotoLibraryAddUsageDescription in ios-project.yml. + +// Empty block only to emit the linker directive, same role as AudioToolbox above. +#[link(name = "Photos", kind = "framework")] +extern "C" {} + +use std::ffi::OsStr; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::Mutex; + +use block2::RcBlock; +use objc2_photos::{ + PHAccessLevel, PHAssetCreationRequest, PHAssetResourceType, PHAuthorizationStatus, + PHPhotoLibrary, +}; +use tokio::sync::oneshot; + +fn photo_access_allowed(status: PHAuthorizationStatus) -> bool { + // Add-only access: .limited still permits adds (it only restricts reads). + status == PHAuthorizationStatus::Authorized || status == PHAuthorizationStatus::Limited +} + +fn photo_authorization_error(status: PHAuthorizationStatus) -> String { + if status == PHAuthorizationStatus::Denied { + "permission denied: allow photo access in Settings > Apps > Sable > Photos".to_string() + } else if status == PHAuthorizationStatus::Restricted { + "saving to Photos is blocked by device management or parental controls".to_string() + } else { + format!("unexpected photo authorization status: {status:?}") + } +} + +async fn request_photo_add_authorization() -> Result<(), String> { + let status = + unsafe { PHPhotoLibrary::authorizationStatusForAccessLevel(PHAccessLevel::AddOnly) }; + if photo_access_allowed(status) { + return Ok(()); + } + if status != PHAuthorizationStatus::NotDetermined { + return Err(photo_authorization_error(status)); + } + + let (tx, rx) = oneshot::channel::(); + { + let tx = Mutex::new(Some(tx)); + let handler: RcBlock = RcBlock::new(move |status| { + if let Ok(mut guard) = tx.lock() { + if let Some(tx) = guard.take() { + let _ = tx.send(status); + } + } + }); + unsafe { + PHPhotoLibrary::requestAuthorizationForAccessLevel_handler( + PHAccessLevel::AddOnly, + &handler, + ); + } + // PhotoKit copies the handler; keep the !Send RcBlock out of the await below. + } + + let granted = rx + .await + .map_err(|_| "photo authorization request was cancelled".to_string())?; + if photo_access_allowed(granted) { + Ok(()) + } else { + Err(photo_authorization_error(granted)) + } +} + +fn write_media_to_photo_library(bytes: &[u8], filename: &str) -> Result<(), String> { + // PhotoKit infers the content type from the file URL, so the temp file + // keeps the extension; `file_name` strips any directory components. + static TEMP_COUNTER: AtomicU64 = AtomicU64::new(0); + let leaf = std::path::Path::new(filename) + .file_name() + .unwrap_or_else(|| OsStr::new("download")); + let mut path = std::env::temp_dir(); + path.push(format!( + "sable-photos-{}-{}-{}", + std::process::id(), + TEMP_COUNTER.fetch_add(1, Ordering::Relaxed), + leaf.to_string_lossy() + )); + + if let Err(error) = std::fs::write(&path, bytes) { + let _ = std::fs::remove_file(&path); + return Err(format!("failed to write {}: {error}", path.display())); + } + + let result = unsafe { + let path_str = NSString::from_str(&path.to_string_lossy()); + let url = NSURL::fileURLWithPath(&path_str); + let library = PHPhotoLibrary::sharedPhotoLibrary(); + // PhotoKit rejects creation requests instantiated outside the change block. + let change: RcBlock = RcBlock::new(move || { + let request = PHAssetCreationRequest::creationRequestForAsset(); + request.addResourceWithType_fileURL_options(PHAssetResourceType::Photo, &url, None); + }); + library + .performChangesAndWait_error(RcBlock::as_ptr(&change)) + .map_err(|error| { + format!( + "failed to save '{filename}' to Photos ({} error {}): {}", + error.domain(), + error.code(), + error.localizedDescription() + ) + }) + }; + let _ = std::fs::remove_file(&path); + result +} + +#[tauri::command] +pub async fn save_media_to_photos( + bytes: Vec, + filename: String, + mime_type: String, +) -> Result<(), String> { + if !mime_type.starts_with("image/") { + return Err(format!( + "unsupported media type '{mime_type}': only images can be saved to Photos" + )); + } + + request_photo_add_authorization().await?; + + // performChangesAndWait blocks until commit; it must not run on the main thread. + tauri::async_runtime::spawn_blocking(move || write_media_to_photo_library(&bytes, &filename)) + .await + .map_err(|error| format!("failed to run photo save: {error}"))? +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs new file mode 100644 index 0000000000..eed016b16f --- /dev/null +++ b/src-tauri/src/lib.rs @@ -0,0 +1,549 @@ +#[cfg(all(feature = "cef", target_os = "linux"))] +pub mod deep_link_ipc; +#[cfg(desktop)] +mod desktop; +mod diagnostics; +#[cfg(target_os = "ios")] +mod ios; +#[cfg(target_os = "android")] +mod mobile; +#[cfg(any(target_os = "android", target_os = "ios"))] +mod mobile_diagnostics; +mod network; +mod sentry; +mod share_inbox; + +use tauri::{AppHandle, Manager}; +#[cfg(desktop)] +use tauri_plugin_window_state::StateFlags; + +// CEF (Chromium) on Linux; wry everywhere else. +#[cfg(all(feature = "cef", target_os = "linux"))] +type BrowserEngine = tauri_runtime_cef::CefRuntime; +#[cfg(any( + all(not(feature = "cef"), feature = "wry"), + all(feature = "cef", not(target_os = "linux")) +))] +use tauri::Wry as BrowserEngine; + +pub const MAIN_WINDOW_LABEL: &str = "main"; + +#[cfg(target_os = "android")] +fn is_internal_navigation(url: &tauri::Url, dev: bool) -> bool { + url.scheme() == "tauri" + || url.scheme() == "javascript" + || url.host_str() == Some("tauri.localhost") + || (dev && url.host_str() == Some("localhost")) +} + +#[cfg(desktop)] +pub(crate) fn main_window_title(app: &AppHandle) -> &str { + app.config().product_name.as_deref().unwrap_or("Sable") +} + +#[cfg(all( + not(feature = "cef"), + any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "openbsd", + target_os = "netbsd" + ) +))] +fn prompt_webview_permission(message: &str, resolve: impl FnOnce(bool) + 'static) { + use gtk::prelude::*; + use gtk::{ButtonsType, DialogFlags, MessageDialog, MessageType, ResponseType}; + + let dialog = MessageDialog::new( + None::<>k::Window>, + DialogFlags::MODAL, + MessageType::Question, + ButtonsType::YesNo, + message, + ); + dialog.set_title("Permission request"); + let resolve = std::cell::Cell::new(Some(resolve)); + dialog.connect_response(move |dialog, response| { + if let Some(resolve) = resolve.take() { + resolve(matches!(response, ResponseType::Yes)); + } + dialog.close(); + }); + dialog.show(); +} + +// Return the remembered decision for a webview permission, or prompt the user +// once and persist their choice for next time. +#[cfg(all( + not(feature = "cef"), + any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "openbsd", + target_os = "netbsd" + ) +))] +fn resolve_webview_permission( + app: &AppHandle, + key: &str, + message: &str, + resolve: impl FnOnce(bool) + 'static, +) { + use tauri_plugin_store::StoreExt; + + let store = app.store("permissions.json").ok(); + if let Some(allowed) = store + .as_ref() + .and_then(|store| store.get(key)) + .and_then(|value| value.as_bool()) + { + resolve(allowed); + return; + } + + let key = key.to_owned(); + prompt_webview_permission(message, move |allowed| { + if let Some(store) = &store { + store.set(&key, allowed); + let _ = store.save(); + } + resolve(allowed); + }); +} + +/// Routes in-app notification sounds to the native volume stream on mobile. +/// `kind` is "notification" or "invite". +#[cfg(any(target_os = "android", target_os = "ios"))] +#[tauri::command] +fn play_notification_sound(kind: String) -> Result<(), String> { + #[cfg(target_os = "android")] + { + mobile::play_notification_sound(kind) + } + #[cfg(target_os = "ios")] + { + ios::play_notification_sound(kind) + } +} + +pub fn show_or_create_main_window(app: &AppHandle) -> tauri::Result<()> { + if let Some(_window) = app.get_webview_window(MAIN_WINDOW_LABEL) { + #[cfg(desktop)] + { + _window.unminimize()?; + _window.show()?; + _window.set_focus()?; + } + + return Ok(()); + } + + log::info!("Main window not found, creating a new one."); + + #[cfg(desktop)] + let desktop_settings = desktop::tray::load_desktop_settings(app)?; + + let builder = + tauri::WebviewWindowBuilder::new(app, MAIN_WINDOW_LABEL, tauri::WebviewUrl::default()) + .disable_drag_drop_handler() + .use_https_scheme(true) + .background_color(tauri::window::Color(0x1A, 0x1C, 0x28, 0xFF)); + + // Only android: intercept navigation to external URLs and open them in the system browser + // Other platforms: might need other URI schemes, and on_navigation might get called on iframe urls + // (as of now, the following is confirmed not to work with linux/webkit2gtk) + #[cfg(target_os = "android")] + let builder = { + let nav_handle = app.clone(); + builder.on_navigation(move |url| { + let internal = is_internal_navigation(url, cfg!(dev)); + if !internal { + // open in new thread + // open_url blocks on the ui thread but we are on the ui thread... + let handle = nav_handle.clone(); + let url = url.to_string(); + std::thread::spawn(move || { + use tauri_plugin_opener::OpenerExt; + let _ = handle.opener().open_url(url, None::<&str>); + }); + } + internal + }) + }; + + #[cfg(desktop)] + let title = main_window_title(app); + + #[cfg(desktop)] + let builder = builder + .title(title) + .resizable(true) + .fullscreen(false) + .inner_size(1280.0, 720.0) + .visible(false); + + #[cfg(target_os = "macos")] + let builder = + builder + .hidden_title(true) + .title_bar_style(if desktop_settings.use_custom_title_bar { + tauri::TitleBarStyle::Overlay + } else { + tauri::TitleBarStyle::Visible + }); + + #[cfg(any(target_os = "windows", target_os = "linux"))] + let builder = builder.decorations(!desktop_settings.use_custom_title_bar); + + #[cfg(desktop)] + let webview_window = builder.build()?; + #[cfg(not(desktop))] + builder.build()?; + + #[cfg(desktop)] + desktop::tray::setup_close_to_background(&webview_window); + + // WebKitGTK only (wry). Under CEF, permissions go through set_permission_policy. + #[cfg(all( + not(feature = "cef"), + any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "openbsd", + target_os = "netbsd" + ) + ))] + { + use tauri::Manager; + use webkit2gtk::glib::prelude::Cast; + use webkit2gtk::{ + GeolocationPermissionRequest, NotificationPermissionRequest, PermissionRequestExt, + SettingsExt, UserMediaPermissionRequest, WebViewExt, + }; + let app_handle = app.clone(); + if let Some(wv) = app.get_webview_window(MAIN_WINDOW_LABEL) { + let _ = wv.with_webview(move |webview| { + let gtk_webview = webview.inner(); + if let Some(settings) = gtk_webview.settings() { + settings.set_enable_webrtc(true); + settings.set_enable_media_stream(true); + settings.set_enable_mediasource(true); + settings.set_enable_media(true); + // Enable inspector via SABLE_DEVTOOLS env var. + if std::env::var("SABLE_DEVTOOLS").is_ok() { + settings.set_enable_developer_extras(true); + } + } + + // Bypass CORS for HTTP(S) targets so Element Call's iframe + // fetches to split-horizon DNS homeservers aren't PNA-blocked. + // Same-origin policy and CSP remain enforced. + gtk_webview.set_cors_allowlist(&["http://*/*", "https://*/*"]); + + gtk_webview.connect_permission_request(move |_wv, request| { + // Ask the user (once per permission type, then remember the choice) + // for the permissions Sable actually uses; deny anything else. + let request = request.clone(); + let decision = if request + .downcast_ref::() + .is_some() + { + Some(("media", "Allow Sable to use your camera and microphone?")) + } else if request + .downcast_ref::() + .is_some() + { + Some(( + "notifications", + "Allow Sable to show desktop notifications?", + )) + } else if request + .downcast_ref::() + .is_some() + { + Some(("geolocation", "Allow Sable to access your location?")) + } else { + None + }; + + match decision { + Some((key, message)) => { + let request = request.clone(); + resolve_webview_permission(&app_handle, key, message, move |allowed| { + if allowed { + request.allow(); + } else { + request.deny(); + } + }); + } + None => request.deny(), + } + true + }); + }); + } + } + + Ok(()) +} + +#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub fn run() { + let _sentry_guard = sentry::init(); + + let builder = tauri::Builder::::new(); + + // this should always be the first + #[cfg(desktop)] + let builder = builder.plugin(tauri_plugin_single_instance::init(|app, _argv, _cwd| { + #[cfg(windows)] + { + let handle = app.clone(); + std::thread::spawn(move || { + let app_handle = handle.clone(); + let scheduled = handle.run_on_main_thread(move || { + if let Err(error) = show_or_create_main_window(&app_handle) { + log::warn!("Failed to show main window for second instance: {error}"); + } + }); + if let Err(error) = scheduled { + log::warn!("Failed to schedule main window show for second instance: {error}"); + } + }); + } + + #[cfg(not(windows))] + if let Err(error) = show_or_create_main_window(app) { + log::warn!("Failed to show main window for second instance: {error}"); + } + })); + + // macOS needs a standard menu (with the Edit submenu) for keyboard + // copy/paste/select-all to work in the webview. It lives in the system menu + // bar, so it is macOS-only to avoid an in-window menu bar elsewhere. + #[cfg(target_os = "macos")] + let builder = builder + .menu(|handle| desktop::menu::build_app_menu(handle)) + .on_menu_event(|app, event| desktop::menu::handle_menu_event(app, &event)); + + #[cfg(desktop)] + let builder = builder.plugin(desktop::menu::global_shortcut_plugin()); + + #[cfg(desktop)] + let builder = builder.plugin( + tauri_plugin_window_state::Builder::default() + .with_state_flags(StateFlags::all() & !StateFlags::VISIBLE & !StateFlags::DECORATIONS) + .build(), + ); + + #[cfg(desktop)] + let builder = builder + .plugin(tauri_plugin_store::Builder::default().build()) + .manage(desktop::tray::DesktopSettingsState::default()); + + #[cfg(windows)] + let builder = builder.manage(std::sync::Arc::new( + desktop::windows::window_tracking::TrackingState::new(), + )); + + let builder = builder.plugin(tauri_plugin_notifications::init()); + + #[cfg(all(desktop, feature = "updater"))] + let builder = builder.plugin(tauri_plugin_updater::Builder::new().build()); + + #[cfg(desktop)] + let builder = builder + .plugin(tauri_plugin_process::init()) + .plugin(tauri_plugin_dialog::init()); + + #[cfg(target_os = "ios")] + let builder = builder + .plugin(tauri_plugin_dialog::init()) + .plugin(tauri_plugin_fs::init()); + + #[cfg(mobile)] + let builder = builder + .plugin(tauri_plugin_app_icon::init()) + .plugin(tauri_plugin_edge_to_edge::init()) + .plugin(tauri_plugin_sharekit::init()) + .plugin(tauri_plugin_livekit_mobile::init()); + + #[cfg(target_os = "android")] + let builder = builder.plugin(tauri_plugin_android_fs::init()); + + let app = builder + .plugin(tauri_plugin_clipboard_manager::init()) + .plugin(tauri_plugin_http::init()) + .plugin(tauri_plugin_opener::init()) + .plugin(tauri_plugin_deep_link::init()) + .plugin(tauri_plugin_os::init()) + .manage(network::media_protocol::MediaSessionState::default()) + .register_asynchronous_uri_scheme_protocol( + network::media_protocol::MEDIA_URI_SCHEME, + network::media_protocol::respond, + ) + .setup(|app| { + #[cfg(target_os = "android")] + mobile::set_app_handle(app.handle().clone()); + + // CEF is initialized during runtime construction; initialize GTK afterward + // on the main thread for the Linux tray menu. GTK's X11 backend replaces the + // X error handlers during init, so the runtime's have to go back on after. + #[cfg(all(feature = "cef", target_os = "linux"))] + { + gtk::init()?; + tauri_runtime_cef::install_x_error_handlers(); + } + + network::native_upload::cleanup_uploads(app.handle()); + + #[cfg(any(target_os = "linux", all(debug_assertions, windows)))] + { + use tauri_plugin_deep_link::DeepLinkExt; + app.deep_link().register_all()?; + } + + #[cfg(all(feature = "cef", target_os = "linux"))] + deep_link_ipc::drain_pending_urls(app.handle()); + + show_or_create_main_window(app.handle())?; + + // Failsafe: if the frontend never calls show() (hung webview), force-show + // the window after 5s so the app isn't invisible forever. + #[cfg(desktop)] + { + let handle = app.handle().clone(); + std::thread::spawn(move || { + std::thread::sleep(std::time::Duration::from_secs(5)); + if let Some(window) = handle.get_webview_window(MAIN_WINDOW_LABEL) { + if !window.is_visible().unwrap_or(true) { + let _ = window.show(); + let _ = window.set_focus(); + log::warn!( + "Frontend show() not received within 5s — force-showing window" + ); + } + } + }); + } + + #[cfg(target_os = "ios")] + if let Some(window) = app.get_webview_window(MAIN_WINDOW_LABEL) { + ios::hide_form_accessory_bar(&window); + ios::enable_swipe_back_navigation(&window); + } + + #[cfg(desktop)] + desktop::tray::sync_desktop_settings_inner(app.handle())?; + + #[cfg(desktop)] + desktop::menu::register_global_shortcuts(app.handle()); + + #[cfg(desktop)] + desktop::logging::setup(app.handle())?; + + Ok(()) + }) + .invoke_handler(tauri::generate_handler![ + network::loopback_http::abort_loopback_fetch, + network::loopback_http::loopback_fetch, + network::native_upload::native_upload, + network::native_upload::upload_write_chunk, + network::native_upload::abort_native_upload, + network::media_protocol::set_media_session, + network::media_protocol::clear_media_session, + network::media_protocol::set_media_encryption, + network::media_protocol::prepare_loopback_media, + sentry::set_native_sentry_enabled, + share_inbox::share_inbox_drain, + share_inbox::share_inbox_read, + share_inbox::share_inbox_clear, + #[cfg(target_os = "android")] + mobile::set_status_bar_color, + #[cfg(target_os = "android")] + mobile::set_navigation_bar_color, + #[cfg(target_os = "android")] + mobile::set_immersive_mode, + #[cfg(target_os = "android")] + mobile::start_call_foreground_service, + #[cfg(target_os = "android")] + mobile::stop_call_foreground_service, + #[cfg(target_os = "ios")] + ios::haptic_feedback, + #[cfg(target_os = "ios")] + ios::save_media_to_photos, + #[cfg(any(target_os = "android", target_os = "ios"))] + play_notification_sound, + #[cfg(target_os = "ios")] + ios::activate_call_audio_session, + #[cfg(target_os = "ios")] + ios::deactivate_call_audio_session, + #[cfg(desktop)] + desktop::download::save_download, + #[cfg(desktop)] + desktop::diagnostics::export_diagnostics, + #[cfg(any(target_os = "android", target_os = "ios"))] + mobile_diagnostics::build_diagnostics_archive, + #[cfg(desktop)] + desktop::tray::get_desktop_runtime_state, + #[cfg(desktop)] + desktop::tray::sync_desktop_settings, + #[cfg(target_os = "linux")] + desktop::tray_badge::set_tray_badge, + #[cfg(windows)] + desktop::windows::snap_overlay::show_snap_overlay, + #[cfg(windows)] + desktop::windows::snap_overlay::hide_snap_overlay, + #[cfg(windows)] + desktop::windows::window_tracking::start_window_tracking_with_target, + #[cfg(windows)] + desktop::windows::window_tracking::stop_window_tracking, + #[cfg(windows)] + desktop::windows::window_tracking::is_window_tracking_active, + ]) + .build(tauri::generate_context!()); + + let Ok(app) = app else { + // On Android this function is called through JNI. Panicking here causes + // "panic_cannot_unwind" and aborts the whole process without a useful + // message in logcat. + eprintln!("failed to build tauri application: {app:?}"); + return; + }; + + app.run(|app, event| { + #[cfg(desktop)] + desktop::tray::handle_run_event(app, event); + + #[cfg(not(any(desktop, mobile)))] + let _ = (app, event); + }); +} + +#[cfg(test)] +mod tests { + #[cfg(target_os = "android")] + #[test] + fn javascript_navigation_stays_in_webview() { + let url = tauri::Url::parse("javascript:window.scrollTo(0,0)").unwrap(); + + assert!(crate::is_internal_navigation(&url, false)); + } + + #[test] + fn desktop_modules_are_grouped_under_desktop() { + let _ = crate::desktop::settings::DesktopSettings { + close_to_background_on_close: true, + show_system_tray_icon: true, + use_custom_title_bar: false, + spellcheck: true, + }; + let _ = crate::desktop::runtime_state::DesktopRuntimeState { + tray_available: true, + }; + } +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs new file mode 100644 index 0000000000..95abc3522a --- /dev/null +++ b/src-tauri/src/main.rs @@ -0,0 +1,301 @@ +// Prevents additional console window on Windows in release, DO NOT REMOVE!! +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +#[cfg(all(feature = "cef", target_os = "linux"))] +fn prompt_cef_permission(message: String, tx: std::sync::mpsc::Sender) { + use gtk::glib; + use gtk::prelude::*; + use gtk::{ButtonsType, DialogFlags, MessageDialog, MessageType, ResponseType, WindowPosition}; + + // Non-blocking: show() + response signal instead of run(). + // dialog.run() blocks the GLib main loop, which CEF's message pump + // also uses on Linux, freezing the app. + glib::idle_add_once(move || { + let dialog = MessageDialog::new( + None::<>k::Window>, + DialogFlags::MODAL, + MessageType::Question, + ButtonsType::YesNo, + &message, + ); + dialog.set_title("Permission request"); + dialog.set_position(WindowPosition::CenterAlways); + + let tx = std::cell::RefCell::new(Some(tx)); + dialog.connect_response(move |dlg, response| { + if let Some(tx) = tx.take() { + let _ = tx.send(matches!(response, ResponseType::Yes)); + } + dlg.close(); + }); + + dialog.show(); + }); +} + +fn main() { + // CEF (Chromium) runtime, Linux only. Must run before anything else — CEF + // re-execs this binary for its subprocesses. + #[cfg(all(feature = "cef", target_os = "linux"))] + { + tauri_runtime_cef::configure(tauri_runtime_cef::CefConfig { + identifier: "moe.sable.client".into(), + custom_schemes: vec![ + "tauri".into(), + "ipc".into(), + "asset".into(), + "sable-media".into(), + ], + deep_link_schemes: vec!["moe.sable.app".into(), "sable".into()], + command_line_args: { + let mut args = vec![ + ("--disable-gpu-sandbox".into(), None), + ("--disable-font-subpixel-positioning".into(), None), + ("--enable-font-antialiasing".into(), None), + ( + "autoplay-policy".into(), + Some("no-user-gesture-required".into()), + ), + ( + "enable-features".into(), + Some("SharedArrayBuffer".into()), + ), + ("--disable-background-timer-throttling".into(), None), + ("--skia-resource-cache-limit-mb".into(), Some("64".into())), + ("--renderer-process-limit".into(), Some("2".into())), + ( + "disable-features".into(), + Some("SpareRendererForSitePerProcess,IntensiveWakeUpThrottling,AutofillActorMode,GlicActorUi,LensOverlay,LocalNetworkAccessChecks,LocalNetworkAccessChecksWebSocket,LocalNetworkAccessChecksWebRTC".into()), + ), + ]; + + // Remote debugging via SABLE_DEVTOOLS=port env var. + // Open chrome://inspect in Chrome to connect. + if let Ok(port) = std::env::var("SABLE_DEVTOOLS") { + args.push(("--remote-debugging-port".into(), Some(port))); + } + + // Diagnostic escape hatch for GPU/display-resume crashes. Keep + // acceleration enabled by default; this is for affected users to + // A/B test without rebuilding the application. + if std::env::var_os("SABLE_DISABLE_GPU").is_some() { + args.push(("--disable-gpu".into(), None)); + } + + // Extra Chromium switches, comma-separated `key=value` or bare `key`: + // SABLE_CEF_ARGS="disable-gpu-compositing,use-angle=vulkan" + if let Ok(extra) = std::env::var("SABLE_CEF_ARGS") { + for arg in extra.split(',').map(str::trim).filter(|a| !a.is_empty()) { + match arg.split_once('=') { + Some((key, value)) => { + args.push((key.to_owned(), Some(value.to_owned()))) + } + None => args.push((arg.to_owned(), None)), + } + } + } + args + }, + ..Default::default() + }); + + // Subprocess — hand off to CEF and exit. + if std::env::args().any(|arg| arg.starts_with("--type=")) { + tauri_runtime_cef::run_cef_helper_process(); + return; + } + + // Deep-link relaunch: forward to the running primary and exit before + // CEF init (a second instance can't hold the CEF cache lock). + if let app_lib::deep_link_ipc::ForwardResult::Forwarded = + app_lib::deep_link_ipc::try_forward_deep_links() + { + return; + } + + // Allow call media capture (mic, camera, screen-share) and geolocation for our webview. + // Cache granted permissions so we only prompt once per kind. + use std::collections::HashSet; + use std::sync::{Mutex, OnceLock}; + static GRANTED: OnceLock>> = OnceLock::new(); + let granted = GRANTED.get_or_init(|| Mutex::new(HashSet::new())); + + tauri_runtime_cef::set_permission_policy(move |request, responder| { + use tauri_runtime_cef::{DenyReason, PermissionKind}; + + if request.webview_label != "main" + || !request + .origin + .as_ref() + .is_some_and(|origin| origin.is_app_local()) + { + return responder.deny(DenyReason::NoPolicy); + } + + let permission_kinds: Vec = request + .kinds + .iter() + .filter(|kind| { + matches!( + kind, + PermissionKind::Microphone + | PermissionKind::Camera + | PermissionKind::CameraPanTiltZoom + | PermissionKind::ScreenCapture + | PermissionKind::CapturedSurfaceControl + | PermissionKind::Geolocation + ) + }) + .cloned() + .collect(); + + if permission_kinds.is_empty() || permission_kinds.len() != request.kinds.len() { + return responder.deny(DenyReason::NoPolicy); + } + + // Check cache: if all requested kinds were already granted, allow immediately. + let kind_names: Vec<&'static str> = permission_kinds + .iter() + .map(|k| match k { + PermissionKind::Microphone => "mic", + PermissionKind::Camera | PermissionKind::CameraPanTiltZoom => "cam", + PermissionKind::ScreenCapture | PermissionKind::CapturedSurfaceControl => { + "screen" + } + PermissionKind::Geolocation => "geolocation", + _ => "other", + }) + .collect(); + + { + let cache = granted.lock().unwrap(); + if kind_names.iter().all(|k| cache.contains(k)) { + return responder.allow(); + } + } + + let msg = match permission_kinds.as_slice() { + [PermissionKind::Microphone] => "Sable wants to access your microphone.", + [PermissionKind::Camera] => "Sable wants to access your camera.", + [PermissionKind::ScreenCapture] | [PermissionKind::CapturedSurfaceControl] => { + "Sable wants to share your screen." + } + [PermissionKind::Geolocation] => "Sable wants to access your location.", + _ if permission_kinds.contains(&PermissionKind::Microphone) + && permission_kinds.contains(&PermissionKind::Camera) => + { + "Sable wants to access your microphone and camera." + } + _ => "Sable wants to access media devices.", + }; + + // Defer the CEF callback and ask the user via a native GTK dialog. + // The policy runs on a CEF thread and must not block; defer() hands + // the answer to a worker thread that waits on the dialog result. + let deferred = responder.defer(tauri_runtime_cef::DEFAULT_PROMPT_TIMEOUT); + let (tx, rx) = std::sync::mpsc::channel(); + let kinds_to_cache = kind_names.clone(); + prompt_cef_permission(msg.to_string(), tx); + std::thread::spawn(move || { + let allowed = rx + .recv_timeout(tauri_runtime_cef::DEFAULT_PROMPT_TIMEOUT) + .unwrap_or(false); + if allowed { + let mut cache = granted.lock().unwrap(); + for k in &kinds_to_cache { + cache.insert(k); + } + deferred.allow(); + } else { + deferred.deny(DenyReason::PolicyDenied); + } + }); + }); + } + + // Force X11/XWayland: the tray's GTK needs it, and the CEF runtime's Wayland + // window path is unstable (crate verified on X11 only). + #[cfg(target_os = "linux")] + unsafe { + // https://github.com/tauri-apps/tao/issues/1046 + // https://github.com/tauri-apps/tauri/issues/11856 + // https://github.com/tauri-apps/tauri/issues/14251 + std::env::set_var("GDK_BACKEND", "x11"); + + // NVIDIA explicit sync is another upstream WebKitGTK/Wayland failure mode. Prefer this lower-cost workaround over WEBKIT_DISABLE_DMABUF_RENDERER=1, but don't stomp an explicit user override. + // https://github.com/tauri-apps/tauri/issues/10702 + // https://github.com/tauri-apps/tauri/issues/9394 + if std::env::var_os("__NV_DISABLE_EXPLICIT_SYNC").is_none() { + std::env::set_var("__NV_DISABLE_EXPLICIT_SYNC", "1"); + } + } + + // WebKitGTK/GStreamer workarounds, wry only; inert under CEF (Chromium). + #[cfg(all(not(feature = "cef"), target_os = "linux"))] + unsafe { + use std::path::{Path, PathBuf}; + + // WebKit2GTK can hit compositor/DMABUF bugs + // https://github.com/tauri-apps/tauri/issues/14424 + // https://github.com/tauri-apps/tauri/issues/9394 + if std::env::var_os("WEBKIT_DISABLE_COMPOSITING_MODE").is_none() { + std::env::set_var("WEBKIT_DISABLE_COMPOSITING_MODE", "1"); + } + if std::env::var_os("WEBKIT_DISABLE_DMABUF_RENDERER").is_none() { + std::env::set_var("WEBKIT_DISABLE_DMABUF_RENDERER", "1"); + } + + // AppImage can fail to discover host GStreamer plugins/scanner. Probe + // common distro layouts, but don't override explicit user config. + // Not finding these plugings prevents Sable from launching correctly. + // Maybe there's a better way to do this? + let plugin_dirs = [ + "/usr/lib/gstreamer-1.0", + "/usr/lib64/gstreamer-1.0", + "/usr/local/lib/gstreamer-1.0", + "/usr/local/lib64/gstreamer-1.0", + "/usr/lib/x86_64-linux-gnu/gstreamer-1.0", + "/usr/lib/aarch64-linux-gnu/gstreamer-1.0", + "/run/host/usr/lib/gstreamer-1.0", + "/run/host/usr/lib64/gstreamer-1.0", + ]; + let resolved_plugin_dir = plugin_dirs.iter().find(|dir| Path::new(dir).exists()); + + if std::env::var_os("GST_PLUGIN_SYSTEM_PATH_1_0").is_none() { + if let Some(dir) = resolved_plugin_dir { + std::env::set_var("GST_PLUGIN_SYSTEM_PATH_1_0", dir); + } + } + if std::env::var_os("GST_PLUGIN_PATH_1_0").is_none() { + if let Some(dir) = resolved_plugin_dir { + std::env::set_var("GST_PLUGIN_PATH_1_0", dir); + } + } + if std::env::var_os("GST_PLUGIN_SCANNER").is_none() { + let mut scanner_candidates: Vec = vec![ + PathBuf::from("/usr/lib/gstreamer-1.0/gst-plugin-scanner"), + PathBuf::from("/usr/lib64/gstreamer-1.0/gst-plugin-scanner"), + PathBuf::from("/usr/libexec/gstreamer-1.0/gst-plugin-scanner"), + PathBuf::from("/usr/lib/x86_64-linux-gnu/gstreamer-1.0/gst-plugin-scanner"), + PathBuf::from("/usr/lib/aarch64-linux-gnu/gstreamer-1.0/gst-plugin-scanner"), + PathBuf::from("/run/host/usr/lib/gstreamer-1.0/gst-plugin-scanner"), + PathBuf::from("/run/host/usr/lib64/gstreamer-1.0/gst-plugin-scanner"), + ]; + + if let Some(path_env) = std::env::var_os("PATH") { + scanner_candidates + .extend(std::env::split_paths(&path_env).map(|p| p.join("gst-plugin-scanner"))); + } + + if let Some(scanner) = scanner_candidates.iter().find(|path| path.exists()) { + std::env::set_var("GST_PLUGIN_SCANNER", scanner.as_os_str()); + } + } + } + + // Deep-link primary: hold the forwarding socket for the process lifetime. + #[cfg(all(feature = "cef", target_os = "linux"))] + let _deep_link_guard = app_lib::deep_link_ipc::bind_and_listen(); + + app_lib::run(); +} diff --git a/src-tauri/src/mobile.rs b/src-tauri/src/mobile.rs new file mode 100644 index 0000000000..f565fde5f3 --- /dev/null +++ b/src-tauri/src/mobile.rs @@ -0,0 +1,139 @@ +// Native status-bar tinting. The webview draws edge-to-edge under a transparent +// status bar, so only Window.setStatusBarColor can color that region. Rust can't +// reach the Activity directly (ndk-context isn't populated by Tauri), so +// MainActivity hands us the JavaVM on create and we call back into a static +// method that owns the window. + +use std::sync::OnceLock; + +use jni::objects::{JObject, JValue}; +use jni::strings::JNIString; +use jni::{jni_sig, jni_str, EnvUnowned, JavaVM}; +use tauri::{AppHandle, Emitter}; + +static JAVA_VM: OnceLock = OnceLock::new(); +static APP_HANDLE: OnceLock = OnceLock::new(); + +pub fn set_app_handle(app: AppHandle) { + let _ = APP_HANDLE.set(app); +} + +// Called by MainActivity.onCreate to cache the JavaVM for later callbacks. +#[no_mangle] +pub extern "system" fn Java_moe_sable_client_MainActivity_nativeInitStatusBar( + mut env: EnvUnowned, + _this: JObject, +) { + let _ = env.with_env(|env| { + let vm = env.get_java_vm()?; + let _ = JAVA_VM.set(vm); + Ok::<_, jni::errors::Error>(()) + }); +} + +#[no_mangle] +pub extern "system" fn Java_moe_sable_client_MainActivity_nativeShareReceived( + _env: EnvUnowned, + _this: JObject, +) { + if let Some(app) = APP_HANDLE.get() { + let _ = app.emit("share-received", ()); + } +} + +/// `color` is a packed ARGB int (as produced by Android's `Color`). +#[tauri::command] +pub fn set_status_bar_color(color: u32) -> Result<(), String> { + call_bar_color("setStatusBarColorNative", color) +} + +/// `color` is a packed ARGB int (as produced by Android's `Color`). +#[tauri::command] +pub fn set_navigation_bar_color(color: u32) -> Result<(), String> { + call_bar_color("setNavigationBarColorNative", color) +} + +#[tauri::command] +pub fn set_immersive_mode(enabled: bool) -> Result<(), String> { + let vm = JAVA_VM.get().ok_or("java vm not initialized")?; + vm.attach_current_thread(|env| { + let result = env.call_static_method( + jni_str!("moe/sable/client/MainActivity"), + jni_str!("setImmersiveModeNative"), + jni_sig!("(Z)V"), + &[JValue::Bool(enabled)], + ); + if result.is_err() { + let _ = env.exception_clear(); + } + result.map(|_| ()) + }) + .map_err(|e| e.to_string()) +} + +#[tauri::command] +pub fn start_call_foreground_service() -> Result<(), String> { + call_static_method("startCallForegroundServiceNative") +} + +#[tauri::command] +pub fn stop_call_foreground_service() -> Result<(), String> { + call_static_method("stopCallForegroundServiceNative") +} + +/// `kind` is "notification" or "invite"; mapped to an int to avoid JNI string +/// marshalling (mirrors set_*_bar_color). +pub(crate) fn play_notification_sound(kind: String) -> Result<(), String> { + let code = match kind.as_str() { + "invite" => 1, + _ => 0, + }; + let vm = JAVA_VM.get().ok_or("java vm not initialized")?; + vm.attach_current_thread(|env| { + let result = env.call_static_method( + jni_str!("moe/sable/client/MainActivity"), + jni_str!("playNotificationSoundNative"), + jni_sig!("(I)V"), + &[JValue::Int(code)], + ); + if result.is_err() { + let _ = env.exception_clear(); + } + result.map(|_| ()) + }) + .map_err(|e| e.to_string()) +} + +fn call_bar_color(method: &str, color: u32) -> Result<(), String> { + let vm = JAVA_VM.get().ok_or("java vm not initialized")?; + vm.attach_current_thread(|env| { + let result = env.call_static_method( + jni_str!("moe/sable/client/MainActivity"), + JNIString::new(method), + jni_sig!("(I)V"), + &[JValue::Int(color as i32)], + ); + if result.is_err() { + let _ = env.exception_clear(); + } + result.map(|_| ()) + }) + .map_err(|e| e.to_string()) +} + +fn call_static_method(method: &str) -> Result<(), String> { + let vm = JAVA_VM.get().ok_or("java vm not initialized")?; + vm.attach_current_thread(|env| { + let result = env.call_static_method( + jni_str!("moe/sable/client/MainActivity"), + JNIString::new(method), + jni_sig!("()V"), + &[], + ); + if result.is_err() { + let _ = env.exception_clear(); + } + result.map(|_| ()) + }) + .map_err(|e| e.to_string()) +} diff --git a/src-tauri/src/mobile_diagnostics.rs b/src-tauri/src/mobile_diagnostics.rs new file mode 100644 index 0000000000..a65fc2e1c9 --- /dev/null +++ b/src-tauri/src/mobile_diagnostics.rs @@ -0,0 +1,16 @@ +use tauri::{AppHandle, Manager}; + +#[tauri::command(rename = "export_diagnostics")] +pub fn build_diagnostics_archive( + app: AppHandle, + frontend_logs: Option, +) -> Result, String> { + let log_dir = app.path().app_log_dir().ok(); + let system_info = crate::diagnostics::system_info( + &app.package_info().version.to_string(), + std::env::consts::OS, + std::env::consts::ARCH, + ); + crate::diagnostics::build_archive(log_dir.as_deref(), &system_info, frontend_logs.as_deref()) + .map_err(|error| error.to_string()) +} diff --git a/src-tauri/src/network/loopback_http.rs b/src-tauri/src/network/loopback_http.rs new file mode 100644 index 0000000000..1a68438171 --- /dev/null +++ b/src-tauri/src/network/loopback_http.rs @@ -0,0 +1,240 @@ +use std::{ + collections::HashMap, + sync::{LazyLock, Mutex}, + time::Duration, +}; + +use serde::{Deserialize, Serialize}; +use tauri_plugin_http::reqwest::{ + header::{HeaderMap, HeaderName, HeaderValue}, + redirect, Client, ClientBuilder, Method, Url, +}; +use tokio::sync::watch; + +static LOOPBACK_ABORT_SENDERS: LazyLock>>> = + LazyLock::new(|| Mutex::new(HashMap::new())); + +static LOOPBACK_CLIENT: LazyLock = LazyLock::new(|| { + ClientBuilder::new() + .no_proxy() + // A followed 302 would escape validate_loopback_url. + .redirect(redirect::Policy::none()) + .timeout(Duration::from_secs(30)) + .connect_timeout(Duration::from_secs(10)) + .build() + .expect("failed to build loopback client") +}); + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct LoopbackFetchRequest { + request_id: String, + method: String, + url: String, + headers: Vec<(String, String)>, + body: Option>, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct LoopbackFetchResponse { + status: u16, + status_text: String, + url: String, + headers: Vec<(String, String)>, + body: Vec, +} + +fn validate_loopback_url(url: &str) -> Result { + let parsed = Url::parse(url.trim()).map_err(|err| err.to_string())?; + + if parsed.scheme() != "http" && parsed.scheme() != "https" { + return Err("loopback_fetch only allows loopback http:// or https:// URLs".into()); + } + + match parsed.host_str() { + Some("localhost") | Some("127.0.0.1") | Some("::1") | Some("[::1]") => Ok(parsed), + _ => Err("loopback_fetch only allows loopback http:// or https:// URLs".into()), + } +} + +fn build_headers(headers: Vec<(String, String)>) -> Result { + let mut header_map = HeaderMap::new(); + + for (name, value) in headers { + let header_name = HeaderName::from_bytes(name.as_bytes()).map_err(|err| err.to_string())?; + let header_value = HeaderValue::from_str(&value).map_err(|err| err.to_string())?; + header_map.append(header_name, header_value); + } + + Ok(header_map) +} + +fn register_abort_sender(request_id: &str) -> watch::Receiver { + let (sender, receiver) = watch::channel(false); + LOOPBACK_ABORT_SENDERS + .lock() + .expect("loopback abort senders poisoned") + .insert(request_id.to_owned(), sender); + receiver +} + +fn remove_abort_sender(request_id: &str) { + LOOPBACK_ABORT_SENDERS + .lock() + .expect("loopback abort senders poisoned") + .remove(request_id); +} + +async fn wait_for_abort_signal(receiver: &mut watch::Receiver) { + if *receiver.borrow() { + return; + } + + while receiver.changed().await.is_ok() { + if *receiver.borrow() { + return; + } + } + + std::future::pending::<()>().await; +} + +#[tauri::command] +pub fn abort_loopback_fetch(request_id: String) { + if let Some(sender) = LOOPBACK_ABORT_SENDERS + .lock() + .expect("loopback abort senders poisoned") + .remove(&request_id) + { + let _ = sender.send(true); + } +} + +#[tauri::command] +pub async fn loopback_fetch( + request: LoopbackFetchRequest, +) -> Result { + let request_id = request.request_id.clone(); + let url = validate_loopback_url(&request.url)?; + let mut abort_receiver = register_abort_sender(&request_id); + + let result = async { + let method = Method::from_bytes(request.method.as_bytes()).map_err(|err| err.to_string())?; + let headers = build_headers(request.headers)?; + let mut req = LOOPBACK_CLIENT.request(method, url).headers(headers); + + if let Some(body) = request.body { + req = req.body(body); + } + + let response = tokio::select! { + response = req.send() => response.map_err(|err| err.to_string())?, + _ = wait_for_abort_signal(&mut abort_receiver) => return Err("Loopback request aborted".into()), + }; + let status = response.status(); + let status_text = status + .canonical_reason() + .map(str::to_owned) + .unwrap_or_else(|| status.as_str().to_owned()); + let url = response.url().to_string(); + let headers = response + .headers() + .iter() + .filter_map(|(name, value)| value.to_str().ok().map(|text| (name.to_string(), text.to_owned()))) + .collect(); + let body = tokio::select! { + body = response.bytes() => body.map_err(|err| err.to_string())?.to_vec(), + _ = wait_for_abort_signal(&mut abort_receiver) => return Err("Loopback request aborted".into()), + }; + + Ok(LoopbackFetchResponse { + status: status.as_u16(), + status_text, + url, + headers, + body, + }) + } + .await; + + remove_abort_sender(&request_id); + result +} + +#[cfg(test)] +mod tests { + use super::{ + abort_loopback_fetch, loopback_fetch, register_abort_sender, validate_loopback_url, + LoopbackFetchRequest, + }; + use std::io::{Read, Write}; + use tokio::time::{timeout, Duration}; + + /// One-shot loopback server that answers with `response`, then closes. + fn spawn_once(response: &'static str) -> String { + let listener = std::net::TcpListener::bind("127.0.0.1:0").expect("bind loopback listener"); + let port = listener.local_addr().expect("listener addr").port(); + std::thread::spawn(move || { + if let Ok((mut stream, _)) = listener.accept() { + let mut buf = [0_u8; 1024]; + let _ = stream.read(&mut buf); + let _ = stream.write_all(response.as_bytes()); + } + }); + format!("http://127.0.0.1:{port}/") + } + + #[test] + fn allows_loopback_http_hosts() { + assert!(validate_loopback_url("http://localhost:8008").is_ok()); + assert!(validate_loopback_url("https://localhost:8448").is_ok()); + assert!(validate_loopback_url("http://127.0.0.1:8008").is_ok()); + assert!(validate_loopback_url("https://127.0.0.1:8448").is_ok()); + assert!(validate_loopback_url("http://[::1]:8008").is_ok()); + assert!(validate_loopback_url("https://[::1]:8448").is_ok()); + assert!(validate_loopback_url("http://localhost/_matrix/client/versions").is_ok()); + } + + #[test] + fn rejects_non_loopback_or_non_http_urls() { + assert!(validate_loopback_url("http://192.168.1.5:8008").is_err()); + assert!(validate_loopback_url("https://matrix.example.org").is_err()); + assert!(validate_loopback_url("http://localhost.evil.example.org").is_err()); + assert!(validate_loopback_url("http://localhost:8008@evil.example.org").is_err()); + } + + #[test] + fn does_not_follow_redirects_off_loopback() { + let url = spawn_once( + "HTTP/1.1 302 Found\r\nLocation: http://example.org/\r\nContent-Length: 0\r\n\r\n", + ); + + let response = tauri::async_runtime::block_on(loopback_fetch(LoopbackFetchRequest { + request_id: "redirect-1".into(), + method: "GET".into(), + url, + headers: Vec::new(), + body: None, + })) + .expect("loopback_fetch failed"); + + assert_eq!(response.status, 302); + assert!(response.body.is_empty()); + } + + #[test] + fn abort_command_signals_registered_requests() { + tauri::async_runtime::block_on(async { + let mut receiver = register_abort_sender("request-1"); + abort_loopback_fetch("request-1".into()); + + timeout(Duration::from_secs(1), receiver.changed()) + .await + .expect("abort signal timed out") + .expect("abort receiver unexpectedly closed"); + + assert!(*receiver.borrow()); + }); + } +} diff --git a/src-tauri/src/network/media_protocol.rs b/src-tauri/src/network/media_protocol.rs new file mode 100644 index 0000000000..e95975805f --- /dev/null +++ b/src-tauri/src/network/media_protocol.rs @@ -0,0 +1,1526 @@ +use std::{ + collections::HashMap, + fs, + io::Read, + path::{Path, PathBuf}, + sync::{Arc, Mutex, OnceLock, Weak}, + time::Duration, +}; + +use sha2::{Digest, Sha256}; +use tauri::{ + http::{header, Request, Response, StatusCode, Uri}, + AppHandle, Manager, Runtime, State, UriSchemeContext, UriSchemeResponder, +}; + +mod crypto; +mod lane; +mod loopback; +mod response; +mod session; + +use crypto::EncryptionStore; +use lane::{LanePermit, LifoLane}; +use loopback::LoopbackMediaServer; +use response::{ + apply_cors_headers, error_response, ok_response, read_full, serve_range, serve_range_memory, + session_unavailable_response, sniff_media_content_type, +}; +use session::{MediaSession, SessionStore}; +use tauri_plugin_http::reqwest::{ + header::{AUTHORIZATION, CONTENT_TYPE}, + Client, Url, +}; +use tokio::{sync::Mutex as AsyncMutex, time::Instant}; + +pub const MEDIA_URI_SCHEME: &str = "sable-media"; +const MEDIA_SESSION_MARKER: &str = "__sable_media_session"; + +const MEDIA_PATH_PREFIXES: [&str; 2] = ["/_matrix/media/", "/_matrix/client/v1/media/"]; +const CACHE_SUBDIR: &str = "sable-media"; +// Inactivity deadline between chunks, so a slow but progressing download is not killed. +const READ_TIMEOUT: Duration = Duration::from_secs(30); +const CONNECT_TIMEOUT: Duration = Duration::from_secs(10); +// Small and multiplexed over one HTTP/2 connection, so a tight cap only serialises the timeline. +const MAX_CONCURRENT_THUMBNAIL_REQUESTS: usize = 12; +const MAX_CONCURRENT_DOWNLOAD_REQUESTS: usize = 6; +// The frontend mounts (and starts requesting media) before it hands us the session, so a request +// may arrive first. `` never retries, so waiting beats answering 503. +const SESSION_WAIT: Duration = Duration::from_secs(5); +const SESSION_REFRESH_WAIT: Duration = Duration::from_millis(2500); +const MAX_CACHE_BYTES: u64 = 512 * 1024 * 1024; +// Short: a 4xx stops being true once an unreachable remote server comes back. +const NEGATIVE_CACHE_TTL: Duration = Duration::from_secs(600); +const MAX_NEGATIVE_CACHE_ENTRIES: usize = 512; + +const TEMP_CACHE_SUBDIR: &str = "sable-media-temp"; +const MAX_TEMP_CACHE_BYTES: u64 = 2 * 1024 * 1024 * 1024; // 2 GiB + +type FetchResult = Result<(String, Option>>, PathBuf), StatusCode>; + +pub struct MediaSessionState { + session_store: SessionStore, + encryption: EncryptionStore, + client: OnceLock, + thumbnail_lane: LifoLane, + download_lane: LifoLane, + cache_miss_gates: Mutex>>>>, + negative_cache: Mutex>, + loopback: Option, +} + +impl Default for MediaSessionState { + fn default() -> Self { + Self { + session_store: SessionStore::default(), + encryption: EncryptionStore::default(), + client: OnceLock::new(), + thumbnail_lane: LifoLane::new(MAX_CONCURRENT_THUMBNAIL_REQUESTS), + download_lane: LifoLane::new(MAX_CONCURRENT_DOWNLOAD_REQUESTS), + cache_miss_gates: Mutex::new(HashMap::new()), + negative_cache: Mutex::new(HashMap::new()), + loopback: LoopbackMediaServer::start().ok(), + } + } +} + +impl MediaSessionState { + #[cfg(test)] + fn session(&self) -> Option { + self.session_store.current() + } + + /// Waits out the startup window where media is requested before the session arrives. Fails + /// fast once a session has existed, so requests still in flight after a logout do not hang. + async fn wait_for_session(&self) -> Option { + self.session_store.wait_initial(SESSION_WAIT).await + } + + async fn wait_for_newer_session(&self, previous: &MediaSession) -> Option { + self.wait_for_newer_session_with_timeout(previous, SESSION_REFRESH_WAIT) + .await + } + + async fn wait_for_newer_session_with_timeout( + &self, + previous: &MediaSession, + wait: Duration, + ) -> Option { + self.session_store.wait_newer(previous, wait).await + } + + fn set_session(&self, session: MediaSession) -> Result<(), String> { + self.session_store.set(session, |changed| { + self.forget_client_errors(); + // Capabilities embed the access token, so only a real session change orphans them. + if changed { + self.clear_loopback_media(); + } + }) + } + + fn clear_session(&self) -> Result<(), String> { + self.session_store.clear(|| { + self.forget_client_errors(); + self.clear_loopback_media(); + }) + } + + fn clear_loopback_media(&self) { + if let Some(loopback) = &self.loopback { + loopback.clear(); + } + } + + // Shared across requests so the connection pool and TLS sessions stay warm. + fn client(&self) -> Client { + self.client + .get_or_init(|| { + // MSC3916: default policy strips Authorization on cross-origin redirects to a signed CDN URL. + Client::builder() + .read_timeout(READ_TIMEOUT) + .connect_timeout(CONNECT_TIMEOUT) + .redirect(tauri_plugin_http::reqwest::redirect::Policy::default()) + .build() + .unwrap_or_else(|_| Client::new()) + }) + .clone() + } + + fn cache_miss_gate( + &self, + key: &str, + ) -> Result>>, StatusCode> { + let mut gates = self + .cache_miss_gates + .lock() + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + gates.retain(|_, gate| gate.strong_count() > 0); + if let Some(gate) = gates.get(key).and_then(Weak::upgrade) { + return Ok(gate); + } + + let gate = Arc::new(AsyncMutex::new(None)); + gates.insert(key.to_owned(), Arc::downgrade(&gate)); + Ok(gate) + } + + fn recent_client_error(&self, key: &str) -> Option { + let mut cache = self.negative_cache.lock().ok()?; + let (status, seen_at) = *cache.get(key)?; + if seen_at.elapsed() < NEGATIVE_CACHE_TTL { + return Some(status); + } + cache.remove(key); + None + } + + /// 5xx and auth/rate-limit failures stay retryable. + fn note_client_error(&self, key: &str, status: StatusCode) { + let media_specific = status.is_client_error() + && !matches!( + status, + StatusCode::UNAUTHORIZED + | StatusCode::FORBIDDEN + | StatusCode::REQUEST_TIMEOUT + | StatusCode::TOO_MANY_REQUESTS + ); + if !media_specific { + return; + } + let Ok(mut cache) = self.negative_cache.lock() else { + return; + }; + cache.retain(|_, (_, seen_at)| seen_at.elapsed() < NEGATIVE_CACHE_TTL); + if cache.len() >= MAX_NEGATIVE_CACHE_ENTRIES { + return; + } + cache.insert(key.to_owned(), (status, Instant::now())); + } + + fn forget_client_errors(&self) { + if let Ok(mut cache) = self.negative_cache.lock() { + cache.clear(); + } + } +} + +#[tauri::command] +pub fn set_media_session( + state: tauri::State<'_, MediaSessionState>, + base_url: String, + token: String, + scope: Option, +) -> Result<(), String> { + let origin = Url::parse(&base_url) + .map_err(|err| err.to_string())? + .origin() + .ascii_serialization(); + + let scope = scope + .filter(|value| !value.is_empty()) + .unwrap_or_else(|| origin.clone()); + + state.set_session(MediaSession { + origin, + token, + scope, + generation: 0, + }) +} + +#[tauri::command] +pub fn clear_media_session( + app: AppHandle, + state: tauri::State<'_, MediaSessionState>, +) { + let _ = state.clear_session(); + if let Ok(dir) = cache_dir(&app) { + let _ = fs::remove_dir_all(dir); + } + if let Ok(temp_dir) = temp_cache_dir(&app) { + let _ = fs::remove_dir_all(temp_dir); + } + state.encryption.clear(); +} + +#[tauri::command] +pub fn set_media_encryption( + state: State, + url: String, + key: String, + iv: String, + sha256: String, + version: String, + mime_type: String, +) -> Result<(), String> { + state + .encryption + .register(&url, &key, &iv, &sha256, &version, mime_type) +} + +#[tauri::command] +pub async fn prepare_loopback_media( + app: AppHandle, + url: String, +) -> Result { + let uri = url.parse::().map_err(|err| err.to_string())?; + let response = handle_request(&app, uri, None, true) + .await + .map_err(|status| status.to_string())?; + response + .headers() + .get(header::LOCATION) + .and_then(|value| value.to_str().ok()) + .map(str::to_owned) + .ok_or_else(|| "loopback media server unavailable".to_owned()) +} + +fn media_session_marker(uri: &Uri) -> Result, StatusCode> { + let Some(query) = uri.query() else { + return Ok(None); + }; + + let mut marker = None; + for component in query.split('&') { + let (raw_key, raw_value) = component.split_once('=').unwrap_or((component, "")); + let key = percent_encoding::percent_decode_str(raw_key) + .decode_utf8() + .map_err(|_| StatusCode::BAD_REQUEST)?; + if key == MEDIA_SESSION_MARKER { + if marker.is_some() { + return Err(StatusCode::BAD_REQUEST); + } + marker = Some( + percent_encoding::percent_decode_str(raw_value) + .decode_utf8() + .map_err(|_| StatusCode::BAD_REQUEST)? + .into_owned(), + ); + } + } + + Ok(marker) +} + +fn session_marker_matches(uri: &Uri, scope: &str) -> Result { + Ok(media_session_marker(uri)?.is_none_or(|marker| marker == scope)) +} + +fn should_retry_with_session(failed: &MediaSession, updated: &MediaSession) -> bool { + updated.generation != failed.generation + && updated.token != failed.token + && updated.origin == failed.origin + && updated.scope == failed.scope +} + +pub fn respond( + ctx: UriSchemeContext<'_, R>, + request: Request>, + responder: UriSchemeResponder, +) { + let app = ctx.app_handle().clone(); + let uri = request.uri().clone(); + let header_value = |name: header::HeaderName| { + request + .headers() + .get(name) + .and_then(|value| value.to_str().ok()) + .map(str::to_owned) + }; + let range = header_value(header::RANGE); + let origin = header_value(header::ORIGIN); + tauri::async_runtime::spawn(async move { + let mut response = handle_request(&app, uri, range, false) + .await + .unwrap_or_else(error_response); + apply_cors_headers(&mut response, origin.as_deref()); + responder.respond(response); + }); +} + +async fn handle_request( + app: &AppHandle, + uri: Uri, + range: Option, + loopback: bool, +) -> Result>, StatusCode> { + let target = percent_encoding::percent_decode_str(uri.path().trim_start_matches('/')) + .decode_utf8() + .map_err(|_| StatusCode::BAD_REQUEST)? + .into_owned(); + + let state = app.state::(); + let Some(session) = state.wait_for_session().await else { + return Ok(session_unavailable_response()); + }; + + let mut media_url = Url::parse(&target).map_err(|_| StatusCode::BAD_REQUEST)?; + // A retry fragment never goes upstream and must not key encryption params, but stays + // in `target` below so the retry gets a fresh cache identity. + media_url.set_fragment(None); + if media_url.scheme() != "http" && media_url.scheme() != "https" { + return Err(StatusCode::FORBIDDEN); + } + if media_url.origin().ascii_serialization() != session.origin { + return Err(StatusCode::FORBIDDEN); + } + if !MEDIA_PATH_PREFIXES + .iter() + .any(|prefix| media_url.path().starts_with(prefix)) + { + return Err(StatusCode::FORBIDDEN); + } + if !session_marker_matches(&uri, &session.scope)? { + return Err(StatusCode::FORBIDDEN); + } + + let key = cache_key(&session.scope, &target); + if let Some(status) = state.recent_client_error(&key) { + return Err(status); + } + let dir = cache_dir(app).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + let temp_dir = temp_cache_dir(app).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + + let (content_type, in_memory_body, disk_path) = + ensure_cached(&state, &session, &key, media_url, dir, temp_dir).await?; + log_served_content_type(&content_type, range.is_some()); + + if loopback && in_memory_body.is_none() { + if let Some(loopback) = &state.loopback { + return Ok(loopback.redirect_response(&session, &key, disk_path, &content_type)); + } + } + + match (range, in_memory_body) { + (Some(range_header), Some(body)) => { + Ok(serve_range_memory(&body, &content_type, &range_header)) + } + (Some(range_header), None) => serve_range(disk_path, content_type, range_header).await, + (None, Some(body)) => { + let vec_body = Arc::try_unwrap(body).unwrap_or_else(|b| (*b).clone()); + Ok(ok_response(vec_body, &content_type)) + } + (None, None) => Ok(ok_response(read_full(disk_path).await?, &content_type)), + } +} + +// Ensure the body + content type are on disk (persistent or temporary), fetching from the homeserver on a miss. +async fn ensure_cached( + state: &MediaSessionState, + session: &MediaSession, + key: &str, + media_url: Url, + dir: PathBuf, + temp_dir: PathBuf, +) -> Result<(String, Option>>, PathBuf), StatusCode> { + ensure_cached_with_limits( + state, + session, + key, + media_url, + dir, + temp_dir, + MAX_CACHE_BYTES, + MAX_TEMP_CACHE_BYTES, + ) + .await +} + +#[allow(clippy::too_many_arguments)] +async fn ensure_cached_with_limits( + state: &MediaSessionState, + session: &MediaSession, + key: &str, + media_url: Url, + dir: PathBuf, + temp_dir: PathBuf, + max_persistent_cache_bytes: u64, + max_temp_cache_bytes: u64, +) -> Result<(String, Option>>, PathBuf), StatusCode> { + let body_path = dir.join(key); + let content_type_path = dir.join(format!("{key}.ct")); + let temp_body_path = temp_dir.join(key); + let temp_content_type_path = temp_dir.join(format!("{key}.ct")); + + // Fast path 1: Persistent cache hit + if let Some(content_type) = + read_content_type(body_path.clone(), content_type_path.clone()).await + { + let content_type = + sniff_and_fix_content_type(body_path.clone(), content_type_path.clone(), content_type) + .await; + return Ok((content_type, None, body_path)); + } + + // Fast path 2: Temporary cache hit (sequential range requests for oversized media) + if let Some(content_type) = + read_content_type(temp_body_path.clone(), temp_content_type_path.clone()).await + { + let content_type = sniff_and_fix_content_type( + temp_body_path.clone(), + temp_content_type_path.clone(), + content_type, + ) + .await; + return Ok((content_type, None, temp_body_path)); + } + + let gate = state.cache_miss_gate(key)?; + let mut gate_guard = gate.lock().await; + + // Double-check persistent cache inside gate lock + if let Some(content_type) = + read_content_type(body_path.clone(), content_type_path.clone()).await + { + let content_type = + sniff_and_fix_content_type(body_path.clone(), content_type_path.clone(), content_type) + .await; + return Ok((content_type, None, body_path)); + } + + // Double-check temporary cache inside gate lock + if let Some(content_type) = + read_content_type(temp_body_path.clone(), temp_content_type_path.clone()).await + { + let content_type = sniff_and_fix_content_type( + temp_body_path.clone(), + temp_content_type_path.clone(), + content_type, + ) + .await; + return Ok((content_type, None, temp_body_path)); + } + + // Double-check in-flight results + if let Some(res) = &*gate_guard { + return match res { + Ok((content_type, body_opt, path)) => { + Ok((content_type.clone(), body_opt.clone(), path.clone())) + } + Err(status) => Err(*status), + }; + } + + let fetch_res = fetch_and_cache( + state, + session, + media_url, + dir, + temp_dir, + body_path, + content_type_path, + temp_body_path, + temp_content_type_path, + max_persistent_cache_bytes, + max_temp_cache_bytes, + ) + .await; + + match &fetch_res { + Ok((content_type, body_opt, path)) => { + *gate_guard = Some(Ok((content_type.clone(), body_opt.clone(), path.clone()))); + fetch_res + } + Err(status) => { + state.note_client_error(key, *status); + *gate_guard = Some(Err(*status)); + Err(*status) + } + } +} + +// Thumbnails queue separately so a few large downloads cannot stall a painting timeline. +async fn acquire_lane<'a>(state: &'a MediaSessionState, media_url: &Url) -> LanePermit<'a> { + let lane = if is_thumbnail_request(media_url) { + &state.thumbnail_lane + } else { + &state.download_lane + }; + lane.acquire().await +} + +fn is_thumbnail_request(media_url: &Url) -> bool { + let Some(segments) = media_url.path_segments() else { + return false; + }; + let mut after_media = segments.skip_while(|segment| *segment != "media").skip(1); + match after_media.next() { + // The legacy endpoints carry a version segment before the action. + Some(segment) if is_media_api_version(segment) => after_media.next() == Some("thumbnail"), + segment => segment == Some("thumbnail"), + } +} + +fn is_media_api_version(segment: &str) -> bool { + matches!(segment, "v1" | "v3" | "r0") +} + +#[allow(clippy::too_many_arguments)] +async fn fetch_and_cache( + state: &MediaSessionState, + session: &MediaSession, + media_url: Url, + dir: PathBuf, + temp_dir: PathBuf, + body_path: PathBuf, + content_type_path: PathBuf, + temp_body_path: PathBuf, + temp_content_type_path: PathBuf, + max_persistent_cache_bytes: u64, + max_temp_cache_bytes: u64, +) -> Result<(String, Option>>, PathBuf), StatusCode> { + let permit = acquire_lane(state, &media_url).await; + + let mut upstream = state + .client() + .get(media_url.clone()) + .header(AUTHORIZATION, format!("Bearer {}", session.token)) + .send() + .await + .map_err(|_| StatusCode::BAD_GATEWAY)?; + + if matches!( + upstream.status(), + StatusCode::UNAUTHORIZED | StatusCode::FORBIDDEN + ) { + if let Some(updated) = state.wait_for_newer_session(session).await { + if should_retry_with_session(session, &updated) { + upstream = state + .client() + .get(media_url.clone()) + .header(AUTHORIZATION, format!("Bearer {}", updated.token)) + .send() + .await + .map_err(|_| StatusCode::BAD_GATEWAY)?; + } + } + } + + if !upstream.status().is_success() { + return Err( + StatusCode::from_u16(upstream.status().as_u16()).unwrap_or(StatusCode::BAD_GATEWAY) + ); + } + + let content_type = normalize_content_type( + upstream + .headers() + .get(CONTENT_TYPE) + .and_then(|value| value.to_str().ok()), + ); + + // Encrypted media stays buffered: its SHA-256 only verifies over the whole ciphertext. + if state.encryption.contains(media_url.as_str()) { + let body = upstream + .bytes() + .await + .map_err(|_| StatusCode::BAD_GATEWAY)? + .to_vec(); + let (body, content_type) = + state + .encryption + .decrypt(media_url.as_str(), body, &content_type)?; + drop(permit); + + return store_buffered_body( + body, + content_type, + dir, + temp_dir, + body_path, + content_type_path, + temp_body_path, + temp_content_type_path, + max_persistent_cache_bytes, + max_temp_cache_bytes, + ) + .await; + } + + // Plaintext media streams to disk, so peak memory is one chunk instead of the whole file. + let staging_path = temp_body_path.with_extension("part"); + match stream_to_staging_file(&mut upstream, temp_dir.clone(), staging_path.clone()).await { + StreamOutcome::Written(size) => { + drop(permit); + let (target_dir, target_body, target_ct, max_bytes) = + if size <= max_persistent_cache_bytes { + ( + dir, + body_path, + content_type_path, + max_persistent_cache_bytes, + ) + } else { + ( + temp_dir, + temp_body_path, + temp_content_type_path, + max_temp_cache_bytes, + ) + }; + + if promote_staging_file( + staging_path, + target_dir, + target_body.clone(), + target_ct, + content_type.clone(), + max_bytes, + ) + .await + { + Ok((content_type, None, target_body)) + } else { + Err(StatusCode::INTERNAL_SERVER_ERROR) + } + } + // Cache directory unusable (read-only, full): serve from memory instead of failing. + StreamOutcome::Unstorable => { + let body = upstream + .bytes() + .await + .map_err(|_| StatusCode::BAD_GATEWAY)? + .to_vec(); + drop(permit); + Ok((content_type, Some(Arc::new(body)), temp_body_path)) + } + StreamOutcome::Failed => Err(StatusCode::BAD_GATEWAY), + } +} + +enum StreamOutcome { + Written(u64), + /// The staging file could not be created; nothing was read from the body yet. + Unstorable, + Failed, +} + +async fn stream_to_staging_file( + upstream: &mut tauri_plugin_http::reqwest::Response, + temp_dir: PathBuf, + staging_path: PathBuf, +) -> StreamOutcome { + if tokio::fs::create_dir_all(&temp_dir).await.is_err() { + return StreamOutcome::Unstorable; + } + let Ok(file) = tokio::fs::File::create(&staging_path).await else { + return StreamOutcome::Unstorable; + }; + + let mut file = tokio::io::BufWriter::new(file); + let mut written: u64 = 0; + + loop { + match upstream.chunk().await { + Ok(Some(chunk)) => { + if tokio::io::AsyncWriteExt::write_all(&mut file, &chunk) + .await + .is_err() + { + break; + } + written += chunk.len() as u64; + } + Ok(None) => { + if tokio::io::AsyncWriteExt::flush(&mut file).await.is_ok() { + return StreamOutcome::Written(written); + } + break; + } + Err(_) => break, + } + } + + let _ = tokio::fs::remove_file(&staging_path).await; + StreamOutcome::Failed +} + +// Move a completed staging file into its cache directory and record its content type. +async fn promote_staging_file( + staging_path: PathBuf, + target_dir: PathBuf, + target_body: PathBuf, + target_content_type: PathBuf, + content_type: String, + max_bytes: u64, +) -> bool { + tokio::task::spawn_blocking(move || { + if fs::create_dir_all(&target_dir).is_err() { + let _ = fs::remove_file(&staging_path); + return false; + } + if fs::rename(&staging_path, &target_body).is_err() { + let _ = fs::remove_file(&staging_path); + return false; + } + if fs::write(&target_content_type, &content_type).is_err() { + let _ = fs::remove_file(&target_body); + return false; + } + evict_directory_if_needed(&target_dir, max_bytes); + target_body.is_file() + }) + .await + .unwrap_or(false) +} + +#[allow(clippy::too_many_arguments)] +async fn store_buffered_body( + body: Vec, + content_type: String, + dir: PathBuf, + temp_dir: PathBuf, + body_path: PathBuf, + content_type_path: PathBuf, + temp_body_path: PathBuf, + temp_content_type_path: PathBuf, + max_persistent_cache_bytes: u64, + max_temp_cache_bytes: u64, +) -> Result<(String, Option>>, PathBuf), StatusCode> { + if body.len() as u64 > max_temp_cache_bytes { + return Ok((content_type, Some(Arc::new(body)), temp_body_path)); + } + + if body.len() as u64 <= max_persistent_cache_bytes { + write_cache( + dir, + body_path.clone(), + content_type_path, + body, + content_type.clone(), + max_persistent_cache_bytes, + ) + .await; + Ok((content_type, None, body_path)) + } else { + let shared = Arc::new(body); + let written = write_cache( + temp_dir, + temp_body_path.clone(), + temp_content_type_path, + shared.as_ref().clone(), + content_type.clone(), + max_temp_cache_bytes, + ) + .await; + + if written { + Ok((content_type, None, temp_body_path)) + } else { + // Storage write failed or evicted; serve from memory fallback + Ok((content_type, Some(shared), temp_body_path)) + } + } +} + +async fn write_cache( + dir: PathBuf, + body_path: PathBuf, + content_type_path: PathBuf, + body: Vec, + content_type: String, + max_bytes: u64, +) -> bool { + tokio::task::spawn_blocking(move || { + if fs::create_dir_all(&dir).is_ok() { + let res_body = fs::write(&body_path, &body); + let res_ct = fs::write(&content_type_path, &content_type); + evict_directory_if_needed(&dir, max_bytes); + res_body.is_ok() && res_ct.is_ok() && body_path.is_file() + } else { + false + } + }) + .await + .unwrap_or(false) +} + +// Only a hit when the body file is also present, so a stray `.ct` counts as a miss. +async fn read_content_type(body_path: PathBuf, content_type_path: PathBuf) -> Option { + tokio::task::spawn_blocking(move || { + if !body_path.is_file() { + return None; + } + fs::read_to_string(&content_type_path) + .ok() + .map(|stored| normalize_content_type(Some(&stored))) + }) + .await + .unwrap_or(None) +} + +// Empty becomes octet-stream so it reaches the sniffer; an empty Content-Type is fatal +// under `nosniff`. +fn normalize_content_type(raw: Option<&str>) -> String { + let trimmed = raw.unwrap_or_default().trim(); + if trimmed.is_empty() { + return "application/octet-stream".to_owned(); + } + trimmed.to_owned() +} + +// Images are the bulk of the traffic and would drown the log. +fn log_served_content_type(content_type: &str, ranged: bool) { + if content_type.starts_with("image/") { + return; + } + if content_type == "application/octet-stream" { + log::warn!("[sable-media] serving application/octet-stream (ranged={ranged}); nosniff blocks decoding"); + } else { + log::info!("[sable-media] serving {content_type} (ranged={ranged})"); + } +} + +/// On a cache hit where the stored content type is octet-stream, re-sniff the +/// body file's magic bytes and rewrite the .ct file if a known media type is found. +async fn sniff_and_fix_content_type( + body_path: PathBuf, + content_type_path: PathBuf, + stored_ct: String, +) -> String { + if stored_ct != "application/octet-stream" { + return stored_ct; + } + let ct_for_closure = stored_ct.clone(); + tokio::task::spawn_blocking(move || { + let mut file = match fs::File::open(&body_path) { + Ok(f) => f, + Err(_) => return ct_for_closure, + }; + let mut buf = [0u8; 512]; + let n = file.read(&mut buf).unwrap_or(0); + if let Some(sniffed) = sniff_media_content_type(&buf[..n]) { + let _ = fs::write(&content_type_path, sniffed); + return sniffed.to_owned(); + } + ct_for_closure + }) + .await + .unwrap_or(stored_ct) +} + +// Trim oldest-first when the cache exceeds its byte budget. +fn evict_directory_if_needed(dir: &Path, max_bytes: u64) { + let Ok(entries) = fs::read_dir(dir) else { + return; + }; + + let mut files: Vec<(PathBuf, u64, std::time::SystemTime)> = entries + .flatten() + .filter_map(|entry| { + let path = entry.path(); + let meta = entry.metadata().ok()?; + if !meta.is_file() { + return None; + } + let modified = meta.modified().unwrap_or(std::time::UNIX_EPOCH); + Some((path, meta.len(), modified)) + }) + .collect(); + + let mut total: u64 = files.iter().map(|(_, len, _)| *len).sum(); + if total <= max_bytes { + return; + } + + files.sort_by_key(|(_, _, modified)| *modified); + for (path, len, _) in files { + if total <= max_bytes { + break; + } + if fs::remove_file(&path).is_ok() { + total = total.saturating_sub(len); + } + } +} + +fn cache_dir(app: &AppHandle) -> Result { + app.path() + .app_cache_dir() + .map(|dir| dir.join(CACHE_SUBDIR)) + .map_err(|err| err.to_string()) +} + +fn temp_cache_dir(app: &AppHandle) -> Result { + app.path() + .app_cache_dir() + .map(|dir| dir.join(TEMP_CACHE_SUBDIR)) + .map_err(|err| err.to_string()) +} + +fn cache_key(session_token: &str, url: &str) -> String { + let mut hasher = Sha256::new(); + hasher.update(session_token.as_bytes()); + hasher.update([0]); + hasher.update(url.as_bytes()); + let digest = hasher.finalize(); + digest.iter().map(|byte| format!("{byte:02x}")).collect() +} + +#[cfg(test)] +mod tests { + use std::{ + fs, + io::{Read, Write}, + net::TcpListener, + sync::{atomic::AtomicU64, mpsc, Arc}, + thread, + time::Duration, + }; + + use tauri::http::{StatusCode, Uri}; + + use super::{ + cache_key, normalize_content_type, session_marker_matches, should_retry_with_session, + MediaSession, MediaSessionState, Url, + }; + + static TEST_CACHE_ID: AtomicU64 = AtomicU64::new(0); + + fn test_session(origin: &str, token: &str, scope: &str, generation: u64) -> MediaSession { + MediaSession { + origin: origin.to_owned(), + token: token.to_owned(), + scope: scope.to_owned(), + generation, + } + } + + fn start_upstream( + statuses: Vec<(u16, &'static str)>, + ) -> (Url, mpsc::Receiver, thread::JoinHandle<()>) { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let address = listener.local_addr().unwrap(); + let (auth_sender, auth_receiver) = mpsc::channel(); + let server = thread::spawn(move || { + for (status, body) in statuses { + let (mut stream, _) = listener.accept().unwrap(); + let mut request = Vec::new(); + let mut chunk = [0_u8; 1024]; + while !request.windows(4).any(|window| window == b"\r\n\r\n") { + let Ok(read) = stream.read(&mut chunk) else { + break; + }; + if read == 0 { + break; + } + request.extend_from_slice(&chunk[..read]); + } + let authorization = String::from_utf8_lossy(&request) + .lines() + .find_map(|line| { + line.strip_prefix("Authorization: ") + .or_else(|| line.strip_prefix("authorization: ")) + }) + .unwrap_or_default() + .to_owned(); + auth_sender.send(authorization).unwrap(); + let reason = match status { + 200 => "OK", + 403 => "Forbidden", + _ => "Unauthorized", + }; + let response = format!( + "HTTP/1.1 {status} {reason}\r\nContent-Type: image/png\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{body}", + body.len() + ); + stream.write_all(response.as_bytes()).unwrap(); + } + }); + ( + Url::parse(&format!( + "http://{address}/_matrix/client/v1/media/download/matrix.org/id" + )) + .unwrap(), + auth_receiver, + server, + ) + } + + async fn fetch_test( + state: &MediaSessionState, + session: MediaSession, + media_url: Url, + ) -> Result<(String, Option>>, std::path::PathBuf), StatusCode> { + let root = std::env::temp_dir().join(format!( + "sable-media-test-{}-{}", + std::process::id(), + TEST_CACHE_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed) + )); + let temp = root.join("temp"); + let key = cache_key(&session.scope, media_url.as_str()); + let result = super::ensure_cached_with_limits( + state, + &session, + &key, + media_url, + root.clone(), + temp, + 1024 * 1024, + 1024 * 1024, + ) + .await; + fs::remove_dir_all(root).ok(); + result + } + + async fn receive_auth(receiver: &mpsc::Receiver) -> String { + tokio::time::timeout(Duration::from_secs(1), async { + loop { + if let Ok(auth) = receiver.try_recv() { + return auth; + } + tokio::task::yield_now().await; + } + }) + .await + .unwrap() + } + + #[test] + fn cache_miss_gates_are_reused_and_expired_entries_are_cleaned() { + let state = MediaSessionState::default(); + let first = state.cache_miss_gate("first").unwrap(); + let same = state.cache_miss_gate("first").unwrap(); + let different = state.cache_miss_gate("different").unwrap(); + + assert!(Arc::ptr_eq(&first, &same)); + assert!(!Arc::ptr_eq(&first, &different)); + + drop(first); + drop(same); + let _third = state.cache_miss_gate("third").unwrap(); + let gates = state.cache_miss_gates.lock().unwrap(); + assert!(!gates.contains_key("first")); + assert!(gates.contains_key("different")); + assert!(gates.contains_key("third")); + } + + #[tokio::test] + async fn waits_for_a_session_that_arrives_late() { + // Mirrors startup: media is requested before the frontend hands over the session. + let state = Arc::new(MediaSessionState::default()); + let writer = Arc::clone(&state); + tokio::spawn(async move { + tokio::time::sleep(std::time::Duration::from_millis(50)).await; + writer + .set_session(super::MediaSession { + origin: "https://matrix.example.org".into(), + token: "token".into(), + scope: "@a:example.org".into(), + generation: 0, + }) + .unwrap(); + }); + + let session = + tokio::time::timeout(std::time::Duration::from_secs(2), state.wait_for_session()) + .await + .expect("wait_for_session hung"); + assert_eq!(session.map(|s| s.scope), Some("@a:example.org".to_string())); + } + + #[tokio::test] + async fn retries_once_after_a_same_scope_token_update() { + let (media_url, auth_receiver, server) = start_upstream(vec![(401, ""), (200, "data")]); + let state = Arc::new(MediaSessionState::default()); + state + .set_session(test_session( + media_url.origin().ascii_serialization().as_str(), + "old-token", + "@a:example.org", + 0, + )) + .unwrap(); + let failed_session = state.session().unwrap(); + let fetch_state = Arc::clone(&state); + let fetch_url = media_url.clone(); + let fetch = + tokio::spawn(async move { fetch_test(&fetch_state, failed_session, fetch_url).await }); + + assert_eq!(receive_auth(&auth_receiver).await, "Bearer old-token"); + state + .set_session(test_session( + media_url.origin().ascii_serialization().as_str(), + "new-token", + "@a:example.org", + 0, + )) + .unwrap(); + + assert!(fetch.await.unwrap().is_ok()); + assert_eq!(receive_auth(&auth_receiver).await, "Bearer new-token"); + server.join().unwrap(); + } + + #[tokio::test] + async fn retries_once_after_same_token_then_changed_token_updates() { + let (media_url, auth_receiver, server) = start_upstream(vec![(401, ""), (200, "data")]); + let state = Arc::new(MediaSessionState::default()); + state + .set_session(test_session( + media_url.origin().ascii_serialization().as_str(), + "same-token", + "@a:example.org", + 0, + )) + .unwrap(); + let failed_session = state.session().unwrap(); + let fetch_state = Arc::clone(&state); + let fetch_url = media_url.clone(); + let fetch = + tokio::spawn(async move { fetch_test(&fetch_state, failed_session, fetch_url).await }); + + assert_eq!(receive_auth(&auth_receiver).await, "Bearer same-token"); + state + .set_session(test_session( + media_url.origin().ascii_serialization().as_str(), + "same-token", + "@a:example.org", + 0, + )) + .unwrap(); + tokio::time::sleep(Duration::from_millis(20)).await; + assert!(auth_receiver.try_recv().is_err()); + state + .set_session(test_session( + media_url.origin().ascii_serialization().as_str(), + "changed-token", + "@a:example.org", + 0, + )) + .unwrap(); + + assert!(fetch.await.unwrap().is_ok()); + assert_eq!(receive_auth(&auth_receiver).await, "Bearer changed-token"); + server.join().unwrap(); + } + + #[tokio::test] + async fn times_out_without_a_session_update() { + let state = MediaSessionState::default(); + let session = test_session("https://matrix.example.org", "token", "@a:example.org", 1); + assert!(state + .wait_for_newer_session_with_timeout(&session, Duration::from_millis(20)) + .await + .is_none()); + } + + #[tokio::test] + async fn a_failed_retry_does_not_loop() { + let (media_url, auth_receiver, server) = start_upstream(vec![(401, ""), (403, "")]); + let state = Arc::new(MediaSessionState::default()); + state + .set_session(test_session( + media_url.origin().ascii_serialization().as_str(), + "old-token", + "@a:example.org", + 0, + )) + .unwrap(); + let failed_session = state.session().unwrap(); + let fetch_state = Arc::clone(&state); + let fetch_url = media_url.clone(); + let fetch = + tokio::spawn(async move { fetch_test(&fetch_state, failed_session, fetch_url).await }); + + assert_eq!(receive_auth(&auth_receiver).await, "Bearer old-token"); + state + .set_session(test_session( + media_url.origin().ascii_serialization().as_str(), + "new-token", + "@a:example.org", + 0, + )) + .unwrap(); + + assert_eq!(fetch.await.unwrap().unwrap_err(), StatusCode::FORBIDDEN); + assert_eq!(receive_auth(&auth_receiver).await, "Bearer new-token"); + server.join().unwrap(); + } + + #[tokio::test] + async fn clear_while_waiting_terminates_without_a_retry() { + let (media_url, auth_receiver, server) = start_upstream(vec![(401, "")]); + let state = Arc::new(MediaSessionState::default()); + state + .set_session(test_session( + media_url.origin().ascii_serialization().as_str(), + "token", + "@a:example.org", + 0, + )) + .unwrap(); + let failed_session = state.session().unwrap(); + let fetch_state = Arc::clone(&state); + let fetch_url = media_url.clone(); + let fetch = + tokio::spawn(async move { fetch_test(&fetch_state, failed_session, fetch_url).await }); + + assert_eq!(receive_auth(&auth_receiver).await, "Bearer token"); + state.clear_session().unwrap(); + + assert_eq!(fetch.await.unwrap().unwrap_err(), StatusCode::UNAUTHORIZED); + assert!(auth_receiver.try_recv().is_err()); + server.join().unwrap(); + } + + #[tokio::test] + async fn origin_or_scope_switch_while_waiting_terminates_without_a_retry() { + for (origin, scope) in [ + ("https://other.example.org", "@a:example.org"), + ("https://matrix.example.org", "@b:example.org"), + ] { + let state = Arc::new(MediaSessionState::default()); + state + .set_session(test_session( + "https://matrix.example.org", + "old-token", + "@a:example.org", + 0, + )) + .unwrap(); + let previous = state.session().unwrap(); + let waiter_state = Arc::clone(&state); + let waiter = tokio::spawn(async move { + waiter_state + .wait_for_newer_session_with_timeout(&previous, Duration::from_secs(1)) + .await + }); + + state + .set_session(test_session(origin, "new-token", scope, 0)) + .unwrap(); + + assert!(tokio::time::timeout(Duration::from_millis(100), waiter) + .await + .unwrap() + .unwrap() + .is_none()); + } + } + + #[tokio::test] + async fn concurrent_cache_misses_share_the_initial_attempt_and_retry() { + let (media_url, auth_receiver, server) = start_upstream(vec![(401, ""), (200, "data")]); + let state = Arc::new(MediaSessionState::default()); + state + .set_session(test_session( + media_url.origin().ascii_serialization().as_str(), + "old-token", + "@a:example.org", + 0, + )) + .unwrap(); + let failed_session = state.session().unwrap(); + let first_state = Arc::clone(&state); + let first_url = media_url.clone(); + let first = + tokio::spawn(async move { fetch_test(&first_state, failed_session, first_url).await }); + let second_state = Arc::clone(&state); + let second_session = state.session().unwrap(); + let second_url = media_url.clone(); + let second = + tokio::spawn( + async move { fetch_test(&second_state, second_session, second_url).await }, + ); + + assert_eq!(receive_auth(&auth_receiver).await, "Bearer old-token"); + state + .set_session(test_session( + media_url.origin().ascii_serialization().as_str(), + "new-token", + "@a:example.org", + 0, + )) + .unwrap(); + + let first_result = first.await.unwrap(); + let second_result = second.await.unwrap(); + assert!(first_result.is_ok(), "first result: {first_result:?}"); + assert!(second_result.is_ok(), "second result: {second_result:?}"); + assert_eq!(receive_auth(&auth_receiver).await, "Bearer new-token"); + assert!(auth_receiver.try_recv().is_err()); + server.join().unwrap(); + } + + #[test] + fn mismatched_account_or_origin_never_qualifies_for_retry() { + let failed = test_session("https://matrix.example.org", "old", "@a:example.org", 1); + assert!(!should_retry_with_session( + &failed, + &test_session("https://matrix.example.org", "new", "@b:example.org", 2) + )); + assert!(!should_retry_with_session( + &failed, + &test_session("https://other.example.org", "new", "@a:example.org", 2) + )); + } + + #[test] + fn session_marker_is_an_equality_guard_and_validates_decoding() { + let matching: Uri = + "https://sable-media.localhost/media?__sable_media_session=%40a%3Aexample.org" + .parse() + .unwrap(); + let mismatched: Uri = + "https://sable-media.localhost/media?__sable_media_session=%40b%3Aexample.org" + .parse() + .unwrap(); + let malformed: Uri = "https://sable-media.localhost/media?__sable_media_session=%FF" + .parse() + .unwrap(); + let markerless: Uri = "https://sable-media.localhost/media".parse().unwrap(); + + assert!(session_marker_matches(&matching, "@a:example.org").unwrap()); + assert!(!session_marker_matches(&mismatched, "@a:example.org").unwrap()); + assert_eq!( + session_marker_matches(&malformed, "@a:example.org"), + Err(StatusCode::BAD_REQUEST) + ); + assert!(session_marker_matches(&markerless, "@a:example.org").unwrap()); + } + + #[tokio::test] + async fn does_not_wait_once_a_session_has_been_cleared() { + // After logout there is nothing to wait for, so in-flight requests must not hang. + let state = MediaSessionState::default(); + state + .set_session(test_session("https://example.org", "token", "scope", 0)) + .unwrap(); + state.clear_session().unwrap(); + + let waited = tokio::time::timeout( + std::time::Duration::from_millis(200), + state.wait_for_session(), + ) + .await + .expect("wait_for_session should fail fast after a clear"); + assert!(waited.is_none()); + } + + #[test] + fn client_errors_are_remembered_but_server_errors_are_not() { + let state = MediaSessionState::default(); + + state.note_client_error("cannot-thumbnail", StatusCode::BAD_REQUEST); + assert_eq!( + state.recent_client_error("cannot-thumbnail"), + Some(StatusCode::BAD_REQUEST) + ); + + state.note_client_error("server-down", StatusCode::BAD_GATEWAY); + assert_eq!(state.recent_client_error("server-down"), None); + } + + #[test] + fn auth_and_rate_limit_failures_stay_retryable() { + let state = MediaSessionState::default(); + for status in [ + StatusCode::UNAUTHORIZED, + StatusCode::FORBIDDEN, + StatusCode::REQUEST_TIMEOUT, + StatusCode::TOO_MANY_REQUESTS, + ] { + state.note_client_error("key", status); + assert_eq!( + state.recent_client_error("key"), + None, + "{status} must not be cached" + ); + } + } + + #[test] + fn a_new_session_forgets_remembered_failures() { + let state = MediaSessionState::default(); + state.note_client_error("key", StatusCode::NOT_FOUND); + state.forget_client_errors(); + assert_eq!(state.recent_client_error("key"), None); + } + + #[test] + fn negative_cache_is_bounded() { + let state = MediaSessionState::default(); + for index in 0..(super::MAX_NEGATIVE_CACHE_ENTRIES + 50) { + state.note_client_error(&format!("key-{index}"), StatusCode::NOT_FOUND); + } + assert_eq!( + state.negative_cache.lock().unwrap().len(), + super::MAX_NEGATIVE_CACHE_ENTRIES + ); + } + + #[test] + fn cache_key_is_stable_and_hex() { + let url = "https://matrix.example.org/_matrix/client/v1/media/download/x/y"; + let key = cache_key("@a:example.org", url); + assert_eq!(key.len(), 64); + assert!(key.chars().all(|c| c.is_ascii_hexdigit())); + assert_eq!(key, cache_key("@a:example.org", url)); + } + + #[test] + fn cache_key_is_scoped_per_account() { + let url = "https://matrix.example.org/_matrix/client/v1/media/download/x/y"; + assert_ne!( + cache_key("@a:example.org", url), + cache_key("@b:example.org", url) + ); + } + + #[test] + fn thumbnail_requests_use_their_own_lane() { + let thumbnail = super::Url::parse( + "https://matrix.example.org/_matrix/client/v1/media/thumbnail/x/y?width=96", + ) + .unwrap(); + let download = + super::Url::parse("https://matrix.example.org/_matrix/client/v1/media/download/x/y") + .unwrap(); + assert!(super::is_thumbnail_request(&thumbnail)); + assert!(!super::is_thumbnail_request(&download)); + + let legacy = + super::Url::parse("https://matrix.example.org/_matrix/media/v3/thumbnail/x/y").unwrap(); + assert!(super::is_thumbnail_request(&legacy)); + + // A media id spelled "thumbnail" is still a download. + let lookalike = + super::Url::parse("https://matrix.example.org/_matrix/media/v3/download/x/thumbnail") + .unwrap(); + assert!(!super::is_thumbnail_request(&lookalike)); + } + + #[test] + fn retry_fragment_gives_a_distinct_cache_identity() { + let base = "https://matrix.example.org/_matrix/client/v1/media/download/matrix.org/abc123"; + let original = cache_key("@a:example.org", base); + assert_ne!( + original, + cache_key("@a:example.org", &format!("{base}#retry=1")) + ); + assert_ne!( + original, + cache_key("@a:example.org", &format!("{base}#retry=2")) + ); + assert_eq!( + cache_key("@a:example.org", &format!("{base}#retry=1")), + cache_key("@a:example.org", &format!("{base}#retry=1")) + ); + } + + #[test] + fn blank_content_types_become_octet_stream() { + for raw in [None, Some(""), Some(" "), Some("\n")] { + assert_eq!(normalize_content_type(raw), "application/octet-stream"); + } + } + + #[test] + fn real_content_types_are_kept_and_trimmed() { + assert_eq!(normalize_content_type(Some("video/webm")), "video/webm"); + assert_eq!(normalize_content_type(Some(" video/mp4 ")), "video/mp4"); + } +} diff --git a/src-tauri/src/network/media_protocol/crypto.rs b/src-tauri/src/network/media_protocol/crypto.rs new file mode 100644 index 0000000000..f32f2de1a4 --- /dev/null +++ b/src-tauri/src/network/media_protocol/crypto.rs @@ -0,0 +1,229 @@ +use std::{collections::HashMap, sync::RwLock}; + +use aes::{ + cipher::{KeyIvInit, StreamCipher}, + Aes256, +}; +use base64::Engine; +use ctr::{Ctr128BE, Ctr64BE}; +use sha2::{Digest, Sha256}; +use tauri::http::{StatusCode, Uri}; +use tauri_plugin_http::reqwest::Url; + +use super::response::sniff_media_content_type; + +// How the webview spells this protocol: `sable-media://` on iOS/macOS, and +// `http(s)://sable-media.localhost/` on Windows/Android respectively. +const MEDIA_PROTOCOL_PREFIXES: [&str; 3] = [ + "sable-media://", + "http://sable-media.localhost/", + "https://sable-media.localhost/", +]; + +#[derive(Clone)] +struct EncryptionParams { + key: [u8; 32], + iv: [u8; 16], + counter_length: u8, + expected_sha256: Vec, + content_type: String, +} + +#[derive(Default)] +pub(super) struct EncryptionStore { + entries: RwLock>, +} + +impl EncryptionStore { + pub(super) fn register( + &self, + url: &str, + key: &str, + iv: &str, + sha256: &str, + version: &str, + content_type: String, + ) -> Result<(), String> { + let key_bytes = base64::engine::general_purpose::URL_SAFE_NO_PAD + .decode(key) + .map_err(|error| format!("invalid key base64url: {error}"))?; + let key = key_bytes + .try_into() + .map_err(|bytes: Vec| format!("key must be 32 bytes, got {}", bytes.len()))?; + + let iv_bytes = base64::engine::general_purpose::STANDARD_NO_PAD + .decode(iv) + .map_err(|error| format!("invalid iv base64: {error}"))?; + let iv = iv_bytes + .try_into() + .map_err(|bytes: Vec| format!("iv must be 16 bytes, got {}", bytes.len()))?; + + let expected_sha256 = base64::engine::general_purpose::STANDARD_NO_PAD + .decode(sha256) + .map_err(|error| format!("invalid sha256 base64: {error}"))?; + + let params = EncryptionParams { + key, + iv, + counter_length: if matches!(version, "v1" | "v2") { + 64 + } else { + 128 + }, + expected_sha256, + content_type, + }; + + self.entries + .write() + .map_err(|_| "encryption lock poisoned".to_owned())? + .insert(normalize_key(url), params); + Ok(()) + } + + pub(super) fn clear(&self) { + if let Ok(mut entries) = self.entries.write() { + entries.clear(); + } + } + + pub(super) fn contains(&self, url: &str) -> bool { + self.entries + .read() + .map(|entries| entries.contains_key(url)) + .unwrap_or(false) + } + + /// Decrypt the body if encryption params exist for this URL. + pub(super) fn decrypt( + &self, + url: &str, + ciphertext: Vec, + upstream_content_type: &str, + ) -> Result<(Vec, String), StatusCode> { + let params = self + .entries + .read() + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)? + .get(url) + .cloned(); + + let Some(params) = params else { + return Ok((ciphertext, upstream_content_type.to_owned())); + }; + + if Sha256::digest(&ciphertext).as_slice() != params.expected_sha256 { + return Err(StatusCode::BAD_GATEWAY); + } + + let mut plaintext = ciphertext; + match params.counter_length { + 64 => Ctr64BE::::new((¶ms.key).into(), (¶ms.iv).into()) + .apply_keystream(&mut plaintext), + 128 => Ctr128BE::::new((¶ms.key).into(), (¶ms.iv).into()) + .apply_keystream(&mut plaintext), + _ => return Err(StatusCode::BAD_REQUEST), + } + + let content_type = if params.content_type.is_empty() + || params.content_type == "application/octet-stream" + { + sniff_media_content_type(&plaintext) + .map(str::to_owned) + .unwrap_or(params.content_type) + } else { + params.content_type + }; + Ok((plaintext, content_type)) + } +} + +pub(super) fn normalize_key(url: &str) -> String { + if MEDIA_PROTOCOL_PREFIXES + .iter() + .any(|prefix| url.starts_with(prefix)) + { + if let Ok(uri) = Uri::try_from(url) { + let path = uri.path().trim_start_matches('/'); + let decoded = percent_encoding::percent_decode_str(path) + .decode_utf8() + .unwrap_or(std::borrow::Cow::Borrowed(path)); + if let Ok(mut parsed) = Url::parse(&decoded) { + // A retry fragment is transport metadata, never part of the params key. + parsed.set_fragment(None); + return parsed.to_string(); + } + return decoded.into_owned(); + } + } + + // Parse bare URLs too, so both sides of the map agree on one canonical form. + Url::parse(url) + .map(|mut parsed| { + parsed.set_fragment(None); + parsed.to_string() + }) + .unwrap_or_else(|_| url.to_owned()) +} + +#[cfg(test)] +mod tests { + use super::normalize_key; + + #[test] + fn normalize_key_strips_sable_media_prefix() { + let input = "sable-media://localhost/https%3A%2F%2Fmatrix.example.org%2F_matrix%2Fclient%2Fv1%2Fmedia%2Fdownload%2Fmatrix.org%2Fabc123"; + let result = normalize_key(input); + assert_eq!( + result, + "https://matrix.example.org/_matrix/client/v1/media/download/matrix.org/abc123" + ); + } + + #[test] + fn normalize_key_strips_android_prefix() { + // Android serves the protocol over https, which used to fall through to the bare-URL + // branch: the params were then keyed by the sable-media URL, never matched at fetch + // time, and encrypted media was served as ciphertext. + let input = "https://sable-media.localhost/https%3A%2F%2Fmatrix.example.org%2F_matrix%2Fclient%2Fv1%2Fmedia%2Fdownload%2Fmatrix.org%2Fabc123%3Fallow_redirect%3Dtrue?__sable_media_cache=3&__sable_media_session=%40a%3Aexample.org"; + let result = normalize_key(input); + assert_eq!( + result, + "https://matrix.example.org/_matrix/client/v1/media/download/matrix.org/abc123?allow_redirect=true" + ); + } + + #[test] + fn normalize_key_strips_windows_prefix() { + let input = "http://sable-media.localhost/https%3A%2F%2Fmatrix.example.org%2F_matrix%2Fclient%2Fv1%2Fmedia%2Fthumbnail%2Fmatrix.org%2Fxyz%3Fwidth%3D96%26height%3D96"; + let result = normalize_key(input); + assert_eq!( + result, + "https://matrix.example.org/_matrix/client/v1/media/thumbnail/matrix.org/xyz?width=96&height=96" + ); + } + + #[test] + fn normalize_key_passes_through_bare_url() { + let input = "https://matrix.example.org/_matrix/client/v1/media/download/matrix.org/abc123"; + let result = normalize_key(input); + assert_eq!(result, input); + } + + #[test] + fn retry_fragment_never_keys_encryption_params() { + let bare = "https://matrix.example.org/_matrix/client/v1/media/download/matrix.org/abc123"; + for input in [ + format!("{bare}#retry=1"), + format!( + "https://sable-media.localhost/{}?__sable_media_session=%40a%3Aexample.org", + percent_encoding::utf8_percent_encode( + &format!("{bare}#retry=1"), + percent_encoding::NON_ALPHANUMERIC + ) + ), + ] { + assert_eq!(normalize_key(&input), bare); + } + } +} diff --git a/src-tauri/src/network/media_protocol/lane.rs b/src-tauri/src/network/media_protocol/lane.rs new file mode 100644 index 0000000000..7ace82b445 --- /dev/null +++ b/src-tauri/src/network/media_protocol/lane.rs @@ -0,0 +1,101 @@ +use std::sync::{Mutex, MutexGuard, PoisonError}; + +use tokio::sync::oneshot; + +/// Hands a freed slot to the newest waiter. Media is requested for what is on screen, so +/// the latest request is the one being waited on; `Semaphore` is fair, which buries a +/// just-opened picker behind the avatar backlog of a member list that started first. +pub(super) struct LifoLane { + inner: Mutex, +} + +struct LaneInner { + available: usize, + waiters: Vec>, +} + +impl LifoLane { + pub(super) fn new(permits: usize) -> Self { + Self { + inner: Mutex::new(LaneInner { + available: permits, + waiters: Vec::new(), + }), + } + } + + fn lock(&self) -> MutexGuard<'_, LaneInner> { + self.inner.lock().unwrap_or_else(PoisonError::into_inner) + } + + pub(super) async fn acquire(&self) -> LanePermit<'_> { + let receiver = { + let mut inner = self.lock(); + if inner.available > 0 { + inner.available -= 1; + return LanePermit { lane: self }; + } + let (sender, receiver) = oneshot::channel(); + inner.waiters.push(sender); + receiver + }; + + let _ = receiver.await; + LanePermit { lane: self } + } + + fn release(&self) { + let mut inner = self.lock(); + while let Some(waiter) = inner.waiters.pop() { + // A waiter that went away passes its slot to the next one down. + if waiter.send(()).is_ok() { + return; + } + } + inner.available += 1; + } +} + +pub(super) struct LanePermit<'a> { + lane: &'a LifoLane, +} + +impl Drop for LanePermit<'_> { + fn drop(&mut self) { + self.lane.release(); + } +} + +#[cfg(test)] +mod tests { + use std::sync::{Arc, Mutex}; + + use super::LifoLane; + + #[tokio::test] + async fn lane_serves_the_newest_waiter_first() { + let lane = Arc::new(LifoLane::new(1)); + let started = Arc::new(Mutex::new(Vec::new())); + + let held = lane.acquire().await; + + let mut queued = Vec::new(); + for id in 0..3 { + let lane = lane.clone(); + let started = started.clone(); + queued.push(tokio::spawn(async move { + let _permit = lane.acquire().await; + started.lock().unwrap().push(id); + })); + // Queue in a known order. + tokio::task::yield_now().await; + } + + drop(held); + for task in queued { + task.await.unwrap(); + } + + assert_eq!(*started.lock().unwrap(), vec![2, 1, 0]); + } +} diff --git a/src-tauri/src/network/media_protocol/loopback.rs b/src-tauri/src/network/media_protocol/loopback.rs new file mode 100644 index 0000000000..47db530021 --- /dev/null +++ b/src-tauri/src/network/media_protocol/loopback.rs @@ -0,0 +1,301 @@ +use std::{ + collections::HashMap, + fs::File, + io::{BufRead, BufReader, Read, Seek, SeekFrom, Write}, + net::{TcpListener, TcpStream}, + path::PathBuf, + sync::{Arc, RwLock}, + thread, +}; + +use sha2::{Digest, Sha256}; +use tauri::http::{header, Response, StatusCode}; + +use super::response::is_webview_origin; +use super::session::MediaSession; + +pub(super) struct LoopbackMediaServer { + origin: String, + routes: Arc>>, +} + +#[derive(Clone)] +struct CachedMedia { + path: PathBuf, + content_type: String, +} + +impl LoopbackMediaServer { + pub(super) fn start() -> std::io::Result { + let listener = TcpListener::bind(("127.0.0.1", 0))?; + let origin = format!("http://127.0.0.1:{}", listener.local_addr()?.port()); + let routes = Arc::new(RwLock::new(HashMap::::new())); + let server_routes = Arc::clone(&routes); + thread::Builder::new() + .name("sable-media-loopback".into()) + .spawn(move || { + for stream in listener.incoming().flatten() { + let routes = Arc::clone(&server_routes); + let _ = thread::Builder::new() + .name("sable-media-request".into()) + .spawn(move || serve(stream, routes)); + } + })?; + + Ok(Self { origin, routes }) + } + + pub(super) fn clear(&self) { + if let Ok(mut routes) = self.routes.write() { + routes.clear(); + } + } + + pub(super) fn redirect_response( + &self, + session: &MediaSession, + cache_key: &str, + path: PathBuf, + content_type: &str, + ) -> Response> { + let capability = capability(session, cache_key); + if let Ok(mut routes) = self.routes.write() { + routes.insert( + capability.clone(), + CachedMedia { + path, + content_type: content_type.to_owned(), + }, + ); + } + Response::builder() + .status(StatusCode::FOUND) + .header(header::LOCATION, format!("{}/{}", self.origin, capability)) + .header(header::CACHE_CONTROL, "no-store") + .body(Vec::new()) + .unwrap_or_else(|_| Response::new(Vec::new())) + } +} + +fn capability(session: &MediaSession, cache_key: &str) -> String { + let mut hasher = Sha256::new(); + hasher.update(session.token.as_bytes()); + hasher.update([0]); + hasher.update(session.scope.as_bytes()); + hasher.update([0]); + hasher.update(cache_key.as_bytes()); + hasher + .finalize() + .iter() + .map(|byte| format!("{byte:02x}")) + .collect() +} + +fn serve(mut stream: TcpStream, routes: Arc>>) { + let Ok((method, capability, range, origin)) = parse_request(&stream) else { + let _ = write_status(&mut stream, 400, "Bad Request", &[]); + return; + }; + if method != "GET" && method != "HEAD" { + let _ = write_status(&mut stream, 405, "Method Not Allowed", &[]); + return; + } + let media = routes + .read() + .ok() + .and_then(|routes| routes.get(&capability).cloned()); + let Some(media) = media else { + let _ = write_status(&mut stream, 404, "Not Found", &[]); + return; + }; + let Ok(mut file) = File::open(media.path) else { + let _ = write_status(&mut stream, 404, "Not Found", &[]); + return; + }; + let Ok(total) = file.metadata().map(|metadata| metadata.len()) else { + let _ = write_status(&mut stream, 500, "Internal Server Error", &[]); + return; + }; + let selection = range.as_deref().and_then(|value| parse_range(value, total)); + if range.is_some() && selection.is_none() { + let _ = write_status( + &mut stream, + 416, + "Range Not Satisfiable", + &[("Content-Range", format!("bytes */{total}"))], + ); + return; + } + let (start, end, partial) = selection.unwrap_or((0, total.saturating_sub(1), false)); + let length = end.saturating_sub(start) + 1; + let mut headers = vec![ + ("Content-Type", media.content_type), + ("Content-Length", length.to_string()), + ("Accept-Ranges", "bytes".to_owned()), + ( + "Cache-Control", + "private, max-age=31536000, immutable".to_owned(), + ), + ]; + // The webview origin differs per platform, and media elements request with + // crossOrigin="anonymous". Cached `immutable`, so the cache must key on Origin. + if let Some(origin) = origin.filter(|origin| is_webview_origin(origin)) { + headers.push(("Access-Control-Allow-Origin", origin)); + headers.push(("Vary", "Origin".to_owned())); + } + if partial { + headers.push(("Content-Range", format!("bytes {start}-{end}/{total}"))); + } + if write_status( + &mut stream, + if partial { 206 } else { 200 }, + if partial { "Partial Content" } else { "OK" }, + &headers, + ) + .is_err() + || method == "HEAD" + { + return; + } + if file.seek(SeekFrom::Start(start)).is_err() { + return; + } + let mut left = length; + let mut buffer = [0_u8; 64 * 1024]; + while left > 0 { + let want = left.min(buffer.len() as u64) as usize; + let Ok(read) = file.read(&mut buffer[..want]) else { + return; + }; + if read == 0 || stream.write_all(&buffer[..read]).is_err() { + return; + } + left -= read as u64; + } +} + +type ParsedRequest = (String, String, Option, Option); + +fn parse_request(stream: &TcpStream) -> Result { + let mut reader = BufReader::new(stream); + let mut request_line = String::new(); + reader.read_line(&mut request_line).map_err(|_| ())?; + let mut parts = request_line.split_whitespace(); + let method = parts.next().ok_or(())?.to_owned(); + let path = parts.next().ok_or(())?; + if parts.next().is_none() || !path.starts_with('/') || path[1..].contains('/') { + return Err(()); + } + let mut range = None; + let mut origin = None; + let mut line = String::new(); + loop { + line.clear(); + reader.read_line(&mut line).map_err(|_| ())?; + if line == "\r\n" || line.is_empty() { + break; + } + if let Some((name, value)) = line.split_once(':') { + if name.eq_ignore_ascii_case("range") { + range = Some(value.trim().to_owned()); + } else if name.eq_ignore_ascii_case("origin") { + origin = Some(value.trim().to_owned()); + } + } + if line.len() > 8192 { + return Err(()); + } + } + Ok((method, path[1..].to_owned(), range, origin)) +} + +fn parse_range(value: &str, total: u64) -> Option<(u64, u64, bool)> { + let spec = value.strip_prefix("bytes=")?; + if spec.contains(',') || total == 0 { + return None; + } + let (start, end) = spec.split_once('-')?; + if start.is_empty() { + let length = end.parse::().ok()?.min(total); + (length > 0).then_some((total - length, total - 1, true)) + } else { + let start = start.parse::().ok()?; + let end = if end.is_empty() { + total.checked_sub(1)? + } else { + end.parse::().ok()?.min(total.checked_sub(1)?) + }; + (start <= end && start < total).then_some((start, end, true)) + } +} + +fn write_status( + stream: &mut TcpStream, + status: u16, + reason: &str, + headers: &[(&str, String)], +) -> std::io::Result<()> { + write!( + stream, + "HTTP/1.1 {status} {reason}\r\nConnection: close\r\n" + )?; + for (name, value) in headers { + write!(stream, "{name}: {value}\r\n")?; + } + stream.write_all(b"\r\n") +} + +#[cfg(test)] +mod tests { + use super::*; + + fn parse(raw: &'static str) -> Result { + let listener = TcpListener::bind(("127.0.0.1", 0)).expect("loopback test port"); + let addr = listener.local_addr().expect("loopback test address"); + let client = thread::spawn(move || { + let mut stream = TcpStream::connect(addr).expect("loopback test connect"); + stream + .write_all(raw.as_bytes()) + .expect("loopback test write"); + }); + let (stream, _) = listener.accept().expect("loopback test accept"); + let parsed = parse_request(&stream); + client.join().expect("loopback test client"); + parsed + } + + #[test] + fn extracts_range_and_origin_regardless_of_header_case() { + let (method, capability, range, origin) = parse( + "GET /abc123 HTTP/1.1\r\nrange: bytes=10-19\r\nORIGIN: tauri://localhost\r\n\r\n", + ) + .expect("request should parse"); + + assert_eq!(method, "GET"); + assert_eq!(capability, "abc123"); + assert_eq!(range.as_deref(), Some("bytes=10-19")); + assert_eq!(origin.as_deref(), Some("tauri://localhost")); + } + + #[test] + fn origin_is_absent_when_not_sent() { + let (_, _, range, origin) = + parse("GET /abc123 HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n").expect("request should parse"); + + assert!(range.is_none()); + assert!(origin.is_none()); + } + + #[test] + fn every_platform_webview_origin_is_granted_cors() { + for origin in [ + "tauri://localhost", + "http://tauri.localhost", + "https://tauri.localhost", + ] { + assert!(is_webview_origin(origin), "{origin} should be granted"); + } + assert!(!is_webview_origin("https://evil.example.org")); + assert!(!is_webview_origin("null")); + } +} diff --git a/src-tauri/src/network/media_protocol/response.rs b/src-tauri/src/network/media_protocol/response.rs new file mode 100644 index 0000000000..65cde1c20d --- /dev/null +++ b/src-tauri/src/network/media_protocol/response.rs @@ -0,0 +1,561 @@ +use std::{ + fs, + io::{Read, Seek, SeekFrom}, + path::PathBuf, +}; + +use tauri::http::{header, response::Builder as ResponseBuilder, Response, StatusCode}; + +// WRY's Android adapter copies custom-protocol bodies into a JNI byte array, so range +// responses must stay bounded even when WebView asks for the rest of a large video. +pub(super) const MAX_RANGE_CHUNK: u64 = 2 * 1024 * 1024; + +enum RangeSelection { + Partial { start: u64, end: u64 }, + Ignore, + Unsatisfiable, +} + +pub(super) async fn read_full(body_path: PathBuf) -> Result, StatusCode> { + tokio::task::spawn_blocking(move || fs::read(&body_path)) + .await + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)? + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR) +} + +pub(super) async fn serve_range( + body_path: PathBuf, + content_type: String, + range_header: String, +) -> Result>, StatusCode> { + tokio::task::spawn_blocking(move || { + let mut file = fs::File::open(&body_path).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + let total = file + .metadata() + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)? + .len(); + let (start, end) = match select_range(&range_header, total) { + RangeSelection::Partial { start, end } => (start, end), + RangeSelection::Ignore => { + let mut body = Vec::new(); + file.read_to_end(&mut body) + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + return Ok(ok_response(body, &content_type)); + } + RangeSelection::Unsatisfiable => return Ok(range_not_satisfiable(total)), + }; + let length = end - start + 1; + let mut body = vec![0; length as usize]; + file.seek(SeekFrom::Start(start)) + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + file.read_exact(&mut body) + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + Ok(partial_response(body, &content_type, start, end, total)) + }) + .await + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)? +} + +pub(super) fn serve_range_memory( + body: &[u8], + content_type: &str, + range_header: &str, +) -> Response> { + let total = body.len() as u64; + let (start, end) = match select_range(range_header, total) { + RangeSelection::Partial { start, end } => (start, end), + RangeSelection::Ignore => return ok_response(body.to_vec(), content_type), + RangeSelection::Unsatisfiable => return range_not_satisfiable(total), + }; + partial_response( + body[start as usize..=end as usize].to_vec(), + content_type, + start, + end, + total, + ) +} + +fn select_range(range_header: &str, total: u64) -> RangeSelection { + if total == 0 { + return RangeSelection::Unsatisfiable; + } + let Some(spec) = range_header.strip_prefix("bytes=") else { + return RangeSelection::Unsatisfiable; + }; + if spec.contains(',') { + return RangeSelection::Ignore; + } + let Some((start, end)) = spec.trim().split_once('-') else { + return RangeSelection::Unsatisfiable; + }; + let (start, end) = if start.is_empty() { + let Ok(suffix) = end.parse::() else { + return RangeSelection::Unsatisfiable; + }; + if suffix == 0 { + return RangeSelection::Unsatisfiable; + } + let length = suffix.min(total).min(MAX_RANGE_CHUNK); + (total - length, total - 1) + } else { + let Ok(start) = start.parse::() else { + return RangeSelection::Unsatisfiable; + }; + let requested_end = if end.is_empty() { + total - 1 + } else { + let Ok(end) = end.parse::() else { + return RangeSelection::Unsatisfiable; + }; + end.min(total - 1) + }; + let end = requested_end.min(start.saturating_add(MAX_RANGE_CHUNK - 1)); + (start, end) + }; + if start <= end && start < total { + RangeSelection::Partial { start, end } + } else { + RangeSelection::Unsatisfiable + } +} + +// Shared response headers. Media is content-addressed and the URL is session-scoped, so +// it is safe to let the webview cache it as immutable and advertise byte-range support. +pub(super) fn media_response_builder(status: StatusCode, content_type: &str) -> ResponseBuilder { + let cache_control = if content_type == "application/octet-stream" { + "no-store" + } else { + "private, max-age=31536000, immutable" + }; + Response::builder() + .status(status) + .header(header::CONTENT_TYPE, content_type) + .header(header::ACCEPT_RANGES, "bytes") + .header(header::CACHE_CONTROL, cache_control) + .header(header::X_CONTENT_TYPE_OPTIONS, "nosniff") + .header( + header::CONTENT_SECURITY_POLICY, + "sandbox; default-src 'none'; script-src 'none'; object-src 'none'", + ) +} + +pub(super) fn ok_response(body: Vec, content_type: &str) -> Response> { + let content_length = body.len(); + media_response_builder(StatusCode::OK, content_type) + .header(header::CONTENT_LENGTH, content_length) + .body(body) + .expect("failed to build media response") +} + +fn partial_response( + body: Vec, + content_type: &str, + start: u64, + end: u64, + total: u64, +) -> Response> { + let content_length = body.len(); + media_response_builder(StatusCode::PARTIAL_CONTENT, content_type) + .header(header::CONTENT_LENGTH, content_length) + .header( + header::CONTENT_RANGE, + format!("bytes {start}-{end}/{total}"), + ) + .body(body) + .expect("failed to build partial media response") +} + +pub(super) fn range_not_satisfiable(total: u64) -> Response> { + let mut response = error_response(StatusCode::RANGE_NOT_SATISFIABLE); + let headers = response.headers_mut(); + headers.insert( + header::CONTENT_RANGE, + header::HeaderValue::from_str(&format!("bytes */{total}")) + .expect("valid 416 content range"), + ); + headers.insert( + header::CONTENT_LENGTH, + header::HeaderValue::from_static("0"), + ); + headers.insert( + header::ACCEPT_RANGES, + header::HeaderValue::from_static("bytes"), + ); + response +} + +/// Sniff the MIME type from magic bytes when the registered one is missing or octet-stream. +/// Allowlisted so it can never name a scriptable type such as SVG or HTML. +pub(super) fn sniff_media_content_type(bytes: &[u8]) -> Option<&'static str> { + const ALLOWED: [&str; 16] = [ + "image/png", + "image/jpeg", + "image/gif", + "image/webp", + "image/avif", + "video/mp4", + "video/webm", + "video/quicktime", + "video/x-m4v", + "audio/mpeg", + "audio/m4a", + "audio/ogg", + "audio/opus", + "audio/aac", + "audio/x-flac", + "audio/x-wav", + ]; + infer::get(bytes) + .filter(|kind| ALLOWED.contains(&kind.mime_type())) + .map(|kind| kind.mime_type()) +} + +pub(super) fn session_unavailable_response() -> Response> { + Response::builder() + .status(StatusCode::SERVICE_UNAVAILABLE) + .header(header::RETRY_AFTER, "1") + .header(header::CACHE_CONTROL, "no-store") + .body(Vec::new()) + .expect("failed to build 503 media response") +} + +pub(super) fn error_response(status: StatusCode) -> Response> { + Response::builder() + .status(status) + .header(header::CACHE_CONTROL, "no-store") + .body(Vec::new()) + .expect("failed to build media error response") +} + +/// The app's own webview origins, which vary by platform and scheme. +pub(super) fn is_webview_origin(origin: &str) -> bool { + matches!( + origin, + "tauri://localhost" | "http://tauri.localhost" | "https://tauri.localhost" + ) || (cfg!(debug_assertions) && origin.starts_with("http://localhost:")) +} + +/// Grants CORS only to our own webview. A request with no `Origin` (an `` +/// load) is not a CORS request and needs no grant. +pub(super) fn apply_cors_headers(response: &mut Response>, request_origin: Option<&str>) { + let Some(origin) = request_origin.filter(|origin| is_webview_origin(origin)) else { + return; + }; + let Ok(value) = header::HeaderValue::from_str(origin) else { + return; + }; + let headers = response.headers_mut(); + headers.insert(header::ACCESS_CONTROL_ALLOW_ORIGIN, value); + // Responses are cached `immutable`, so the cache must key on Origin too. + headers.insert(header::VARY, header::HeaderValue::from_static("Origin")); +} + +#[cfg(test)] +mod tests { + use std::{fs, sync::atomic::AtomicU64}; + + use tauri::http::{header, StatusCode}; + + use super::*; + + static TEST_CACHE_ID: AtomicU64 = AtomicU64::new(0); + + fn selected_range(range_header: &str, total: u64) -> Option<(u64, u64)> { + match select_range(range_header, total) { + RangeSelection::Partial { start, end } => Some((start, end)), + _ => None, + } + } + + fn is_unsatisfiable(range_header: &str, total: u64) -> bool { + matches!( + select_range(range_header, total), + RangeSelection::Unsatisfiable + ) + } + + #[test] + fn ok_response_has_content_length_and_cache_headers() { + let response = ok_response(vec![0_u8; 42], "image/png"); + assert_eq!(response.status(), StatusCode::OK); + assert_eq!( + response.headers().get(header::CONTENT_LENGTH).unwrap(), + "42" + ); + assert_eq!( + response.headers().get(header::CACHE_CONTROL).unwrap(), + "private, max-age=31536000, immutable" + ); + assert_eq!( + response.headers().get(header::ACCEPT_RANGES).unwrap(), + "bytes" + ); + } + + #[test] + fn select_range_supports_video_metadata_and_seek_requests() { + assert_eq!(selected_range("bytes=0-499", 1000), Some((0, 499))); + assert_eq!(selected_range("bytes=500-", 1000), Some((500, 999))); + assert_eq!(selected_range("bytes=-200", 1000), Some((800, 999))); + assert_eq!(selected_range("bytes=990-5000", 1000), Some((990, 999))); + } + + #[test] + fn open_ended_ranges_are_capped_from_the_requested_offset() { + let total = MAX_RANGE_CHUNK * 2 + 100; + assert_eq!( + selected_range("bytes=0-", total), + Some((0, MAX_RANGE_CHUNK - 1)) + ); + assert_eq!( + selected_range(&format!("bytes={}-", MAX_RANGE_CHUNK), total), + Some((MAX_RANGE_CHUNK, MAX_RANGE_CHUNK * 2 - 1)) + ); + assert_eq!( + selected_range(&format!("bytes={}-", MAX_RANGE_CHUNK * 2), total), + Some((MAX_RANGE_CHUNK * 2, total - 1)) + ); + } + + #[test] + fn malformed_or_unsatisfiable_range_is_rejected() { + for range in [ + "bytes=1000-1001", + "bytes=500-400", + "bytes=abc-def", + "items=0-1", + ] { + assert!(is_unsatisfiable(range, 1000), "{range}"); + } + assert!(is_unsatisfiable("bytes=0-0", 0)); + } + + #[test] + fn multi_range_requests_fall_back_to_the_full_response() { + let body = [1_u8, 2, 3]; + let response = serve_range_memory(&body, "video/mp4", "bytes=0-0,2-2"); + + assert_eq!(response.status(), StatusCode::OK); + assert_eq!(response.body(), &body); + } + + #[test] + fn in_memory_ranges_are_partial_and_capped() { + let body: Vec = (0..200u8).collect(); + let response = serve_range_memory(&body, "video/mp4", "bytes=10-19"); + assert_eq!(response.status(), StatusCode::PARTIAL_CONTENT); + assert_eq!(response.body(), &body[10..=19]); + assert_eq!( + response.headers().get(header::CONTENT_RANGE).unwrap(), + "bytes 10-19/200" + ); + + let body = vec![7_u8; (MAX_RANGE_CHUNK * 2) as usize]; + let response = serve_range_memory(&body, "video/mp4", "bytes=0-"); + assert_eq!(response.body().len() as u64, MAX_RANGE_CHUNK); + assert_eq!( + response + .headers() + .get(header::CONTENT_RANGE) + .unwrap() + .to_str() + .unwrap(), + format!("bytes 0-{}/{}", MAX_RANGE_CHUNK - 1, body.len()) + ); + + let requested_suffix = MAX_RANGE_CHUNK + 100; + let response = + serve_range_memory(&body, "video/mp4", &format!("bytes=-{requested_suffix}")); + assert_eq!(response.body().len() as u64, MAX_RANGE_CHUNK); + let expected = format!( + "bytes {}-{}/{}", + body.len() as u64 - MAX_RANGE_CHUNK, + body.len() - 1, + body.len() + ); + assert_eq!( + response + .headers() + .get(header::CONTENT_RANGE) + .unwrap() + .to_str() + .unwrap(), + expected + ); + } + + #[test] + fn unsatisfiable_ranges_are_not_cacheable() { + let response = range_not_satisfiable(1000); + assert_eq!(response.status(), StatusCode::RANGE_NOT_SATISFIABLE); + assert_eq!( + response.headers().get(header::CONTENT_RANGE).unwrap(), + "bytes */1000" + ); + assert_eq!( + response.headers().get(header::CACHE_CONTROL).unwrap(), + "no-store" + ); + assert_eq!(response.headers().get(header::CONTENT_LENGTH).unwrap(), "0"); + } + + #[test] + fn partial_responses_include_webview_range_metadata() { + let response = serve_range_memory(&[0_u8; 100], "video/mp4", "bytes=10-19"); + + assert_eq!(response.status(), StatusCode::PARTIAL_CONTENT); + assert_eq!( + response.headers().get(header::CONTENT_TYPE).unwrap(), + "video/mp4" + ); + assert_eq!( + response.headers().get(header::CONTENT_LENGTH).unwrap(), + "10" + ); + assert_eq!( + response.headers().get(header::CONTENT_RANGE).unwrap(), + "bytes 10-19/100" + ); + assert_eq!( + response.headers().get(header::ACCEPT_RANGES).unwrap(), + "bytes" + ); + } + + #[tokio::test] + async fn disk_range_reads_only_the_requested_cached_bytes() { + let path = std::env::temp_dir().join(format!( + "sable-media-range-test-{}-{}", + std::process::id(), + TEST_CACHE_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed) + )); + let body: Vec = (0..200u8).collect(); + fs::write(&path, &body).unwrap(); + + let response = serve_range(path.clone(), "video/mp4".into(), "bytes=10-19".into()) + .await + .unwrap(); + fs::remove_file(path).ok(); + + assert_eq!(response.status(), StatusCode::PARTIAL_CONTENT); + assert_eq!(response.body(), &body[10..=19]); + } + + #[test] + fn sniff_detects_png() { + let png_header = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]; + assert_eq!(sniff_media_content_type(&png_header), Some("image/png")); + } + + #[test] + fn sniff_rejects_unknown() { + assert_eq!(sniff_media_content_type(&[0x00, 0x01, 0x02]), None); + } + + #[test] + fn sniff_detects_playable_video_and_audio() { + // infer scans the first 256 bytes for the EBML DocType, so a stub header is not enough. + let mut webm = [0_u8; 300]; + webm[..4].copy_from_slice(&[0x1A, 0x45, 0xDF, 0xA3]); + webm[8..15].copy_from_slice(b"\x42\x82\x84webm"); + assert_eq!(sniff_media_content_type(&webm), Some("video/webm")); + + let mut mp4 = [0_u8; 12]; + mp4[4..].copy_from_slice(b"ftypisom"); + assert_eq!(sniff_media_content_type(&mp4), Some("video/mp4")); + + let flac = b"fLaC\x00\x00\x00\x22"; + assert_eq!(sniff_media_content_type(flac), Some("audio/x-flac")); + } + + #[test] + fn sniff_does_not_detect_svg() { + let svg = b""; + assert_eq!(sniff_media_content_type(svg), None); + } + + #[test] + fn octet_stream_response_is_not_cached_immutable() { + let response = media_response_builder(StatusCode::OK, "application/octet-stream") + .body(Vec::::new()) + .unwrap(); + assert_eq!( + response.headers().get(header::CACHE_CONTROL).unwrap(), + "no-store" + ); + } + + #[test] + fn cors_is_granted_only_to_the_apps_own_webview_origins() { + for origin in [ + "tauri://localhost", + "http://tauri.localhost", + "https://tauri.localhost", + ] { + let mut response = ok_response(vec![0_u8; 4], "image/png"); + apply_cors_headers(&mut response, Some(origin)); + assert_eq!( + response + .headers() + .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) + .unwrap(), + origin, + "{origin} should be granted CORS" + ); + assert_eq!(response.headers().get(header::VARY).unwrap(), "Origin"); + } + } + + #[test] + fn cors_is_denied_to_foreign_origins() { + for origin in [ + "https://evil.example.org", + "https://tauri.localhost.evil.example.org", + "null", + ] { + let mut response = ok_response(vec![0_u8; 4], "image/png"); + apply_cors_headers(&mut response, Some(origin)); + assert!( + !response + .headers() + .contains_key(header::ACCESS_CONTROL_ALLOW_ORIGIN), + "{origin} must not be granted CORS" + ); + } + } + + #[test] + fn requests_without_an_origin_get_no_cors_header() { + let mut response = ok_response(vec![0_u8; 4], "image/png"); + apply_cors_headers(&mut response, None); + assert!(!response + .headers() + .contains_key(header::ACCESS_CONTROL_ALLOW_ORIGIN)); + } + + #[test] + fn error_responses_are_no_store() { + for response in [ + error_response(StatusCode::UNAUTHORIZED), + error_response(StatusCode::FORBIDDEN), + session_unavailable_response(), + ] { + assert_eq!( + response.headers().get(header::CACHE_CONTROL).unwrap(), + "no-store" + ); + } + } + + #[test] + fn image_response_is_cached_immutable() { + let response = media_response_builder(StatusCode::OK, "image/png") + .body(Vec::::new()) + .unwrap(); + assert_eq!( + response.headers().get(header::CACHE_CONTROL).unwrap(), + "private, max-age=31536000, immutable" + ); + } +} diff --git a/src-tauri/src/network/media_protocol/session.rs b/src-tauri/src/network/media_protocol/session.rs new file mode 100644 index 0000000000..24918afea0 --- /dev/null +++ b/src-tauri/src/network/media_protocol/session.rs @@ -0,0 +1,135 @@ +use std::{ + sync::{ + atomic::{AtomicBool, AtomicU64, Ordering}, + RwLock, + }, + time::Duration, +}; + +use tokio::{ + sync::Notify, + time::{timeout_at, Instant}, +}; + +#[derive(Clone)] +pub(super) struct MediaSession { + pub(super) origin: String, + pub(super) token: String, + // Cache key input. The Matrix user ID, not `token`, which rotates on every OIDC + // refresh and would orphan the whole on-disk cache. + pub(super) scope: String, + pub(super) generation: u64, +} + +pub(super) struct SessionStore { + current: RwLock>, + ready: Notify, + ever_set: AtomicBool, + generation: AtomicU64, +} + +impl Default for SessionStore { + fn default() -> Self { + Self { + current: RwLock::new(None), + ready: Notify::new(), + ever_set: AtomicBool::new(false), + generation: AtomicU64::new(0), + } + } +} + +impl SessionStore { + pub(super) fn current(&self) -> Option { + self.current.read().ok().and_then(|session| session.clone()) + } + + pub(super) async fn wait_initial(&self, wait: Duration) -> Option { + if let Some(session) = self.current() { + return Some(session); + } + if self.ever_set.load(Ordering::Acquire) { + return None; + } + + let deadline = Instant::now() + wait; + loop { + let mut notified = std::pin::pin!(self.ready.notified()); + // Register before re-checking, otherwise a session arriving in between is missed. + notified.as_mut().enable(); + if let Some(session) = self.current() { + return Some(session); + } + if timeout_at(deadline, notified).await.is_err() { + return None; + } + } + } + + pub(super) async fn wait_newer( + &self, + previous: &MediaSession, + wait: Duration, + ) -> Option { + let deadline = Instant::now() + wait; + loop { + let mut notified = std::pin::pin!(self.ready.notified()); + // Register before re-checking, otherwise a session arriving in between is missed. + notified.as_mut().enable(); + match self.current() { + Some(session) if session.generation > previous.generation => { + if session.origin != previous.origin || session.scope != previous.scope { + return None; + } + if session.token != previous.token { + return Some(session); + } + } + None if self.generation.load(Ordering::Acquire) > previous.generation => { + return None; + } + _ => {} + } + if timeout_at(deadline, notified).await.is_err() { + return None; + } + } + } + + // `before_notify` runs before waiters wake, so none observes the new session with stale state. + pub(super) fn set( + &self, + mut session: MediaSession, + before_notify: impl FnOnce(bool), + ) -> Result<(), String> { + let mut current = self + .current + .write() + .map_err(|_| "media session lock poisoned".to_owned())?; + let changed = current.as_ref().is_none_or(|existing| { + existing.origin != session.origin + || existing.token != session.token + || existing.scope != session.scope + }); + session.generation = self.generation.fetch_add(1, Ordering::AcqRel) + 1; + *current = Some(session); + drop(current); + before_notify(changed); + self.ever_set.store(true, Ordering::Release); + self.ready.notify_waiters(); + Ok(()) + } + + pub(super) fn clear(&self, before_notify: impl FnOnce()) -> Result<(), String> { + let mut current = self + .current + .write() + .map_err(|_| "media session lock poisoned".to_owned())?; + *current = None; + self.generation.fetch_add(1, Ordering::AcqRel); + drop(current); + before_notify(); + self.ready.notify_waiters(); + Ok(()) + } +} diff --git a/src-tauri/src/network/mod.rs b/src-tauri/src/network/mod.rs new file mode 100644 index 0000000000..ae26b7f556 --- /dev/null +++ b/src-tauri/src/network/mod.rs @@ -0,0 +1,3 @@ +pub mod loopback_http; +pub mod media_protocol; +pub mod native_upload; diff --git a/src-tauri/src/network/native_upload.rs b/src-tauri/src/network/native_upload.rs new file mode 100644 index 0000000000..fb1afaa0a8 --- /dev/null +++ b/src-tauri/src/network/native_upload.rs @@ -0,0 +1,232 @@ +use std::{ + collections::HashMap, + path::PathBuf, + sync::{LazyLock, Mutex}, + time::Duration, +}; + +use base64::Engine; +use futures_util::StreamExt; +use serde::Serialize; +use tauri::{ipc::Channel, AppHandle, Manager, Runtime}; +use tauri_plugin_http::reqwest::{ + header::{AUTHORIZATION, CONTENT_LENGTH, CONTENT_TYPE}, + Body, Client, ClientBuilder, Method, Url, +}; +use tokio::{ + fs::{File, OpenOptions}, + io::AsyncWriteExt, + sync::watch, +}; +use tokio_util::codec::{BytesCodec, FramedRead}; + +const UPLOAD_SUBDIR: &str = "native-uploads"; + +static UPLOAD_ABORT_SENDERS: LazyLock>>> = + LazyLock::new(|| Mutex::new(HashMap::new())); + +static UPLOAD_CLIENT: LazyLock = LazyLock::new(|| { + ClientBuilder::new() + .no_proxy() + .connect_timeout(Duration::from_secs(15)) + .build() + .expect("failed to build native upload client") +}); + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct NativeUploadResponse { + status: u16, + body: String, +} + +#[derive(Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ProgressPayload { + loaded: u64, + total: u64, +} + +fn upload_temp_path(app: &AppHandle, request_id: &str) -> Result { + if request_id.is_empty() + || !request_id + .chars() + .all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_') + { + return Err("invalid upload id".into()); + } + let dir = app + .path() + .app_cache_dir() + .map_err(|err| err.to_string())? + .join(UPLOAD_SUBDIR); + Ok(dir.join(request_id)) +} + +/// Drop leftover upload temp files from a session that ended without running +/// the normal cleanup (crash, OS kill, ...). No upload can be in flight yet at +/// startup, so removing the whole subdir is safe; it is recreated on demand by +/// `upload_write_chunk`. +pub fn cleanup_uploads(app: &AppHandle) { + let Ok(dir) = app.path().app_cache_dir() else { + return; + }; + let _ = std::fs::remove_dir_all(dir.join(UPLOAD_SUBDIR)); +} + +fn register_abort_sender(request_id: &str) -> watch::Receiver { + let (sender, receiver) = watch::channel(false); + UPLOAD_ABORT_SENDERS + .lock() + .expect("upload abort senders poisoned") + .insert(request_id.to_owned(), sender); + receiver +} + +fn remove_abort_sender(request_id: &str) { + UPLOAD_ABORT_SENDERS + .lock() + .expect("upload abort senders poisoned") + .remove(request_id); +} + +async fn wait_for_abort_signal(receiver: &mut watch::Receiver) { + if *receiver.borrow() { + return; + } + while receiver.changed().await.is_ok() { + if *receiver.borrow() { + return; + } + } + std::future::pending::<()>().await; +} + +/// Base64 rather than raw bytes because Android has no `InvokeBody::Raw`: a +/// `Uint8Array` degrades to a JSON `number[]` there and OOM-kills the renderer. +#[tauri::command] +pub async fn upload_write_chunk( + app: AppHandle, + request_id: String, + chunk: String, +) -> Result<(), String> { + let path = upload_temp_path(&app, &request_id)?; + if let Some(parent) = path.parent() { + tokio::fs::create_dir_all(parent) + .await + .map_err(|err| err.to_string())?; + } + let bytes = base64::engine::general_purpose::STANDARD + .decode(chunk.as_bytes()) + .map_err(|err| err.to_string())?; + let mut file = OpenOptions::new() + .create(true) + .append(true) + .open(&path) + .await + .map_err(|err| err.to_string())?; + file.write_all(&bytes) + .await + .map_err(|err| err.to_string())?; + Ok(()) +} + +#[tauri::command] +pub async fn abort_native_upload(app: AppHandle, request_id: String) { + if let Some(sender) = UPLOAD_ABORT_SENDERS + .lock() + .expect("upload abort senders poisoned") + .remove(&request_id) + { + let _ = sender.send(true); + } + if let Ok(path) = upload_temp_path(&app, &request_id) { + let _ = tokio::fs::remove_file(path).await; + } +} + +#[tauri::command] +pub async fn native_upload( + app: AppHandle, + request_id: String, + url: String, + content_type: String, + authorization: Option, + on_progress: Channel, +) -> Result { + let path = upload_temp_path(&app, &request_id)?; + let result = run_upload( + &path, + url, + content_type, + authorization, + &request_id, + on_progress, + ) + .await; + let _ = tokio::fs::remove_file(&path).await; + remove_abort_sender(&request_id); + result +} + +async fn run_upload( + path: &PathBuf, + url: String, + content_type: String, + authorization: Option, + request_id: &str, + on_progress: Channel, +) -> Result { + let url = Url::parse(url.trim()).map_err(|err| err.to_string())?; + if url.scheme() != "http" && url.scheme() != "https" { + return Err("native_upload only allows http(s) URLs".into()); + } + + // Registered before any awaiting work so an abort arriving during setup is not missed. + let mut abort_receiver = register_abort_sender(request_id); + + let file = File::open(path) + .await + .map_err(|_| "no upload data for this request".to_string())?; + let total = file.metadata().await.map_err(|err| err.to_string())?.len(); + + let progress_stream = async_stream::stream! { + let mut reader = FramedRead::new(file, BytesCodec::new()); + let mut loaded: u64 = 0; + let mut last_emitted_loaded: u64 = 0; + while let Some(item) = reader.next().await { + match item { + Ok(chunk) => { + loaded += chunk.len() as u64; + if loaded - last_emitted_loaded >= 256 * 1024 || loaded == total { + let _ = on_progress.send(ProgressPayload { loaded, total }); + last_emitted_loaded = loaded; + } + yield Ok::<_, std::io::Error>(chunk.freeze()); + } + Err(err) => yield Err(err), + } + } + }; + + let mut builder = UPLOAD_CLIENT + .request(Method::POST, url) + .header(CONTENT_TYPE, content_type) + .header(CONTENT_LENGTH, total) + .body(Body::wrap_stream(progress_stream)); + if let Some(auth) = authorization { + builder = builder.header(AUTHORIZATION, auth); + } + + let response = tokio::select! { + response = builder.send() => response.map_err(|err| err.to_string())?, + _ = wait_for_abort_signal(&mut abort_receiver) => return Err("Upload aborted".into()), + }; + let status = response.status().as_u16(); + let body = tokio::select! { + body = response.text() => body.map_err(|err| err.to_string())?, + _ = wait_for_abort_signal(&mut abort_receiver) => return Err("Upload aborted".into()), + }; + + Ok(NativeUploadResponse { status, body }) +} diff --git a/src-tauri/src/sentry.rs b/src-tauri/src/sentry.rs new file mode 100644 index 0000000000..74c1174679 --- /dev/null +++ b/src-tauri/src/sentry.rs @@ -0,0 +1,35 @@ +use std::sync::atomic::{AtomicBool, Ordering}; + +use sentry::protocol::Event; + +static CONSENT: AtomicBool = AtomicBool::new(false); + +/// Initialise Sentry before the Tauri runtime spawns threads. The guard +/// flushes pending events on drop. No-op when no DSN was baked in at build time. +pub fn init() -> Option { + let dsn = option_env!("SENTRY_DSN")?; + let environment = option_env!("SENTRY_ENVIRONMENT"); + let release = option_env!("SENTRY_APP_VERSION").map(|v| format!("sable@{v}")); + + // ClientOptions is #[non_exhaustive]; mutate a default instance instead. + let mut options = sentry::ClientOptions::default(); + options.dsn = dsn.parse().ok(); + options.release = release.map(Into::into); + options.environment = environment.map(Into::into); + options.send_default_pii = false; + options.before_send = Some(std::sync::Arc::new(|event: Event<'static>| { + if CONSENT.load(Ordering::Relaxed) { + Some(event) + } else { + None + } + })); + let guard = sentry::init(options); + + Some(guard) +} + +#[tauri::command] +pub fn set_native_sentry_enabled(enabled: bool) { + CONSENT.store(enabled, Ordering::Relaxed); +} diff --git a/src-tauri/src/share_inbox.rs b/src-tauri/src/share_inbox.rs new file mode 100644 index 0000000000..62a8374f53 --- /dev/null +++ b/src-tauri/src/share_inbox.rs @@ -0,0 +1,174 @@ +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; +use tauri::ipc::Response; +use tauri::{AppHandle, Runtime}; + +#[derive(Serialize, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct ShareItem { + pub kind: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub text: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub file_name: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub mime: Option, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ShareBatch { + pub batch_id: String, + pub items: Vec, +} + +#[derive(Deserialize)] +struct ShareManifest { + #[allow(dead_code)] + version: u32, + items: Vec, +} + +fn validate_component(value: &str) -> Result<(), String> { + if value.is_empty() || value == "." || value == ".." || value.contains(['/', '\\', '\0']) { + return Err(format!("invalid path component: {value:?}")); + } + Ok(()) +} + +fn parse_manifest(json: &str) -> Result, String> { + let manifest: ShareManifest = serde_json::from_str(json).map_err(|err| err.to_string())?; + Ok(manifest.items) +} + +#[cfg(target_os = "ios")] +fn inbox_root(app: &AppHandle) -> Result { + use objc2_foundation::{NSFileManager, NSString}; + + let group = format!("group.{}", app.config().identifier); + let url = NSFileManager::defaultManager() + .containerURLForSecurityApplicationGroupIdentifier(&NSString::from_str(&group)) + .ok_or_else(|| format!("app group container not available: {group}"))?; + let path = url + .path() + .ok_or_else(|| "app group container has no filesystem path".to_owned())?; + Ok(PathBuf::from(path.to_string()).join("share_inbox")) +} + +#[cfg(not(target_os = "ios"))] +fn inbox_root(app: &AppHandle) -> Result { + use tauri::Manager; + + Ok(app + .path() + .app_data_dir() + .map_err(|err| err.to_string())? + .join("share_inbox")) +} + +#[tauri::command] +pub fn share_inbox_drain(app: AppHandle) -> Result, String> { + let root = inbox_root(&app)?; + let entries = match std::fs::read_dir(&root) { + Ok(entries) => entries, + Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(Vec::new()), + Err(err) => return Err(err.to_string()), + }; + + let mut batches = Vec::new(); + for entry in entries.flatten() { + let path = entry.path(); + if !path.is_dir() { + continue; + } + let Some(batch_id) = path.file_name().and_then(|name| name.to_str()) else { + continue; + }; + let manifest = std::fs::read_to_string(path.join("share.json")) + .map_err(|err| err.to_string()) + .and_then(|json| parse_manifest(&json)); + match manifest { + Ok(items) => batches.push(ShareBatch { + batch_id: batch_id.to_owned(), + items, + }), + Err(err) => { + log::warn!("dropping unreadable share batch {batch_id}: {err}"); + let _ = std::fs::remove_dir_all(&path); + } + } + } + batches.sort_by(|a, b| a.batch_id.cmp(&b.batch_id)); + Ok(batches) +} + +#[tauri::command] +pub fn share_inbox_read( + app: AppHandle, + batch_id: String, + file_name: String, +) -> Result { + validate_component(&batch_id)?; + validate_component(&file_name)?; + let root = inbox_root(&app)?; + let path = root.join(&batch_id).join(&file_name); + let canonical = path.canonicalize().map_err(|err| err.to_string())?; + let canonical_root = root.canonicalize().map_err(|err| err.to_string())?; + if !canonical.starts_with(&canonical_root) { + return Err("path escapes share inbox".to_owned()); + } + std::fs::read(&canonical) + .map(Response::new) + .map_err(|err| err.to_string()) +} + +#[tauri::command] +pub fn share_inbox_clear(app: AppHandle, batch_id: String) -> Result<(), String> { + validate_component(&batch_id)?; + let root = inbox_root(&app)?; + match std::fs::remove_dir_all(root.join(&batch_id)) { + Ok(()) => Ok(()), + Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(()), + Err(err) => Err(err.to_string()), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn validate_component_rejects_traversal() { + assert!(validate_component("..").is_err()); + assert!(validate_component(".").is_err()); + assert!(validate_component("").is_err()); + assert!(validate_component("a/b").is_err()); + assert!(validate_component("a\\b").is_err()); + assert!(validate_component("a\0b").is_err()); + assert!(validate_component("1752-abc").is_ok()); + assert!(validate_component("image-0.jpg").is_ok()); + } + + #[test] + fn parse_manifest_reads_items() { + let items = parse_manifest( + r#"{"version":1,"items":[ + {"kind":"text","text":"hello"}, + {"kind":"url","text":"https://example.com"}, + {"kind":"file","fileName":"image-0.jpg","mime":"image/jpeg"} + ]}"#, + ) + .unwrap(); + assert_eq!(items.len(), 3); + assert_eq!(items[0].kind, "text"); + assert_eq!(items[1].text.as_deref(), Some("https://example.com")); + assert_eq!(items[2].file_name.as_deref(), Some("image-0.jpg")); + assert_eq!(items[2].mime.as_deref(), Some("image/jpeg")); + } + + #[test] + fn parse_manifest_rejects_garbage() { + assert!(parse_manifest("not json").is_err()); + assert!(parse_manifest(r#"{"version":1}"#).is_err()); + } +} diff --git a/src-tauri/tauri.android.conf.json b/src-tauri/tauri.android.conf.json new file mode 100644 index 0000000000..690a377366 --- /dev/null +++ b/src-tauri/tauri.android.conf.json @@ -0,0 +1,6 @@ +{ + "build": { + "beforeDevCommand": "pnpm generate:app-icons:android && pnpm dev", + "beforeBuildCommand": "pnpm generate:app-icons:android && pnpm build" + } +} diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json new file mode 100644 index 0000000000..5e24275035 --- /dev/null +++ b/src-tauri/tauri.conf.json @@ -0,0 +1,107 @@ +{ + "$schema": "../node_modules/@tauri-apps/cli/config.schema.json", + "productName": "Sable", + "version": "1.21.0", + "identifier": "moe.sable.client", + "build": { + "frontendDist": "../dist", + "devUrl": "http://localhost:8080", + "beforeDevCommand": "pnpm dev", + "beforeBuildCommand": "pnpm build" + }, + "app": { + "windows": [], + "security": { + "csp": "default-src 'self' blob: data: filesystem: tauri:; script-src 'self' 'wasm-unsafe-eval' 'unsafe-inline' filesystem: tauri:; worker-src 'self' blob:; style-src 'self' 'unsafe-inline' blob: data: filesystem: http: https: tauri:; font-src 'self' blob: data: filesystem: http: https: tauri:; img-src 'self' data: blob: filesystem: http: https: sable-media:; media-src 'self' blob: data: http: https: sable-media:; frame-src 'self' blob: http: https: tauri:; connect-src 'self' ipc: ws: wss: http: https: http://ipc.localhost sable-media: http://sable-media.localhost; object-src 'none'; base-uri 'self'; form-action 'self'" + } + }, + "bundle": { + "active": true, + "targets": "all", + "createUpdaterArtifacts": true, + "macOS": { + "infoPlist": "Info.plist" + }, + "iOS": { + "template": "src-tauri/ios-project.yml", + "minimumSystemVersion": "16.0", + "frameworks": [ + "AudioToolbox", + "Photos", + "Frameworks/LiveKitWebRTC.xcframework", + "Frameworks/RustLiveKitUniFFI.xcframework" + ] + }, + "linux": { + "deb": { + "depends": [ + "xwayland", + "gstreamer1.0-plugins-good", + "gstreamer1.0-plugins-bad", + "gstreamer1.0-libav", + "gstreamer1.0-nice", + "geoclue-2.0" + ] + }, + "rpm": { + "depends": [ + "xorg-x11-server-Xwayland", + "gstreamer1-plugins-good", + "gstreamer1-plugins-bad-free", + "gstreamer1-libav", + "gstreamer1-plugins-nice", + "geoclue2" + ] + } + }, + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ] + }, + "plugins": { + "updater": { + "endpoints": ["https://github.com/SableClient/Sable/releases/latest/download/latest.json"], + "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEM0NDc2QkI0NjdCODlEQUUKUldTdW5iaG50R3RIeFBQeGFwcDZNcGhCQ2Y4UFgzVnptRmNRZjRoVDl0MkdOZmREdC9pTUZrZnAK" + }, + "typegen": { + "projectPath": ".", + "outputPath": "../src/app/generated/tauri", + "validationLibrary": "none", + "verbose": false, + "typeMappings": { + "Response": "unknown" + } + }, + "deep-link": { + "desktop": { + "schemes": ["sable", "moe.sable.app"] + }, + "mobile": [ + { + "scheme": ["sable", "moe.sable.app"], + "appLink": false + } + ] + }, + "notifications": { + "actionTypes": [ + { + "id": "sable-message", + "actions": [ + { + "id": "sable-reply", + "title": "Reply", + "input": true, + "inputButtonTitle": "Send", + "inputPlaceholder": "Type a reply" + } + ] + } + ] + } + } +} diff --git a/src-tauri/tauri.ios.conf.json b/src-tauri/tauri.ios.conf.json new file mode 100644 index 0000000000..2b2199eb2a --- /dev/null +++ b/src-tauri/tauri.ios.conf.json @@ -0,0 +1,6 @@ +{ + "build": { + "beforeDevCommand": "pnpm generate:app-icons:ios && pnpm dev", + "beforeBuildCommand": "pnpm generate:app-icons:ios && pnpm build" + } +} diff --git a/src-tauri/tauri.local.ios.conf.json b/src-tauri/tauri.local.ios.conf.json new file mode 100644 index 0000000000..97948f54d3 --- /dev/null +++ b/src-tauri/tauri.local.ios.conf.json @@ -0,0 +1,3 @@ +{ + "identifier": "moe.sable.client.local" +} diff --git a/src-tauri/tauri.nightly.conf.json b/src-tauri/tauri.nightly.conf.json new file mode 100644 index 0000000000..acacede2e1 --- /dev/null +++ b/src-tauri/tauri.nightly.conf.json @@ -0,0 +1,4 @@ +{ + "productName": "Sable Nightly", + "identifier": "moe.sable.client.nightly" +} diff --git a/src-tauri/tauri.pr.conf.json b/src-tauri/tauri.pr.conf.json new file mode 100644 index 0000000000..deb61bb9a3 --- /dev/null +++ b/src-tauri/tauri.pr.conf.json @@ -0,0 +1,4 @@ +{ + "productName": "Sable Dev", + "identifier": "moe.sable.client.pr" +} diff --git a/src/app/components/AccountDataEditor.tsx b/src/app/components/AccountDataEditor.tsx index 131ae7fab3..fb2bf6fad1 100644 --- a/src/app/components/AccountDataEditor.tsx +++ b/src/app/components/AccountDataEditor.tsx @@ -3,27 +3,26 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { Box, Text, - Icon, - Icons, IconButton, Input, - Button, TextArea as TextAreaComponent, color, - Spinner, Chip, Scroll, config, } from 'folds'; +import { ArrowLeft, composerIcon, sizedIcon, X } from '$components/icons/phosphor'; import type { MatrixError } from '$types/matrix-sdk'; import { Cursor } from '$plugins/text-area'; import { syntaxErrorPosition } from '$utils/dom'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; +import { AsyncError } from '$components/AsyncError'; import { useAlive } from '$hooks/useAlive'; import { useTextAreaCodeEditor } from '$hooks/useTextAreaCodeEditor'; import { Page, PageHeader } from './page'; import { SequenceCard } from './sequence-card'; import { TextViewerContent } from './text-viewer'; +import { Button } from '$components/button'; const EDITOR_INTENT_SPACE_COUNT = 2; @@ -140,8 +139,10 @@ function AccountDataEdit({ size="400" radii="300" type="submit" - disabled={submitting} - before={submitting && } + loading={submitting} + spinnerSize="300" + spinnerVariant="Primary" + spinnerFill="Solid" > Save @@ -158,11 +159,7 @@ function AccountDataEdit({ - {submitState.status === AsyncStatus.Error && ( - - {submitState.error.message} - - )} + @@ -289,14 +286,14 @@ export function AccountDataEditor({ size="500" radii="Pill" onClick={requestClose} - before={} + before={sizedIcon(ArrowLeft, '100')} > Developer Tools - + {composerIcon(X)} diff --git a/src/app/components/ActionUIA.tsx b/src/app/components/ActionUIA.tsx index 4241a64df6..720cb8ef4e 100644 --- a/src/app/components/ActionUIA.tsx +++ b/src/app/components/ActionUIA.tsx @@ -5,13 +5,25 @@ import { getUIAFlowForStages } from '$utils/matrix-uia'; import { useSupportedUIAFlows, useUIACompleted, useUIAFlow } from '$hooks/useUIAFlows'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { UIAFlowOverlay } from './UIAFlowOverlay'; -import { PasswordStage, SSOStage } from './uia-stages'; +import { OAuthStage, PasswordStage, SSOStage } from './uia-stages'; -export const SUPPORTED_IN_APP_UIA_STAGES = [AuthType.Password, AuthType.Sso]; +// MSC4312 unstable prefix for m.oauth. +export const OAUTH_UNSTABLE_STAGE = 'org.matrix.cross_signing_reset'; -export const pickUIAFlow = (uiaFlows: UIAFlow[]): UIAFlow | undefined => { +const SUPPORTED_IN_APP_UIA_STAGES = [ + AuthType.Password, + AuthType.Sso, + AuthType.OAuth, + OAUTH_UNSTABLE_STAGE, +]; + +const pickUIAFlow = (uiaFlows: UIAFlow[]): UIAFlow | undefined => { const passwordFlow = getUIAFlowForStages(uiaFlows, [AuthType.Password]); if (passwordFlow) return passwordFlow; + const oauthFlow = + getUIAFlowForStages(uiaFlows, [AuthType.OAuth]) ?? + getUIAFlowForStages(uiaFlows, [OAUTH_UNSTABLE_STAGE]); + if (oauthFlow) return oauthFlow; return getUIAFlowForStages(uiaFlows, [AuthType.Sso]); }; @@ -51,6 +63,11 @@ export function ActionUIA({ authData, ongoingFlow, action, onCancel }: ActionUIA submitAuthDict={action} /> )} + {(stageToComplete.type === (AuthType.OAuth as string) || + stageToComplete.type === OAUTH_UNSTABLE_STAGE) && + stageToComplete.session && ( + + )} ); } @@ -66,7 +83,7 @@ export function ActionUIAFlowsLoader({ children, }: ActionUIAFlowsLoaderProps) { const supportedFlows = useSupportedUIAFlows(authData.flows ?? [], SUPPORTED_IN_APP_UIA_STAGES); - const ongoingFlow = supportedFlows.length > 0 ? supportedFlows[0] : undefined; + const ongoingFlow = supportedFlows.length > 0 ? pickUIAFlow(supportedFlows) : undefined; if (!ongoingFlow) return unsupported(); diff --git a/src/app/components/AsyncError.tsx b/src/app/components/AsyncError.tsx new file mode 100644 index 0000000000..d71a31c2d2 --- /dev/null +++ b/src/app/components/AsyncError.tsx @@ -0,0 +1,28 @@ +import { Text, color } from 'folds'; +import type { AsyncState } from '$hooks/useAsyncCallback'; +import { AsyncStatus } from '$hooks/useAsyncCallback'; + +type AsyncErrorProps = { + state: AsyncState; + prefix?: string; + size?: 'T200' | 'T300'; + bold?: boolean; +}; + +export function AsyncError({ state, prefix, size = 'T200', bold }: AsyncErrorProps) { + if (state.status !== AsyncStatus.Error) return null; + + const message = prefix + ? `${prefix}! ${state.error instanceof Error ? state.error.message : String(state.error)}` + : state.error instanceof Error + ? state.error.message + : String(state.error); + + const content = bold ? {message} : message; + + return ( + + {content} + + ); +} diff --git a/src/app/components/AuthFlowsLoader.tsx b/src/app/components/AuthFlowsLoader.tsx index 28478acf57..ef3f8c1f6c 100644 --- a/src/app/components/AuthFlowsLoader.tsx +++ b/src/app/components/AuthFlowsLoader.tsx @@ -5,25 +5,39 @@ import { createClient } from '$types/matrix-sdk'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; import { useAutoDiscoveryInfo } from '$hooks/useAutoDiscoveryInfo'; import { promiseFulfilledResult, promiseRejectedResult } from '$utils/common'; -import type { AuthFlows, RegisterFlowsResponse } from '$hooks/useAuthFlows'; -import { RegisterFlowStatus, parseRegisterErrResp } from '$hooks/useAuthFlows'; +import { + isUsableOAuthMetadata, + type AuthFlows, + RegisterFlowStatus, + parseRegisterErrResp, + type RegisterFlowsResponse, +} from '$hooks/useAuthFlows'; +import { fetch } from '$utils/fetch'; type AuthFlowsLoaderProps = { fallback?: () => ReactNode; - error?: (err: unknown) => ReactNode; + error?: (err: unknown, retry: () => void) => ReactNode; children: (authFlows: AuthFlows) => ReactNode; }; export function AuthFlowsLoader({ fallback, error, children }: AuthFlowsLoaderProps) { const autoDiscoveryInfo = useAutoDiscoveryInfo(); const baseUrl = autoDiscoveryInfo['m.homeserver'].base_url; - const mx = useMemo(() => createClient({ baseUrl }), [baseUrl]); + const mx = useMemo(() => createClient({ baseUrl, fetchFn: fetch }), [baseUrl]); const [state, load] = useAsyncCallback( useCallback(async () => { - const result = await Promise.allSettled([mx.loginFlows(), mx.registerRequest({})]); + const result = await Promise.allSettled([ + mx.loginFlows(), + mx.registerRequest({}), + mx.getAuthMetadata(), + ]); const loginFlows = promiseFulfilledResult(result[0]); const registerResp = promiseRejectedResult(result[1]) as MatrixError | undefined; + const discoveredAuthMetadata = promiseFulfilledResult(result[2]); + const authMetadata = isUsableOAuthMetadata(discoveredAuthMetadata) + ? discoveredAuthMetadata + : undefined; let registerFlows: RegisterFlowsResponse = { status: RegisterFlowStatus.InvalidRequest, }; @@ -32,16 +46,17 @@ export function AuthFlowsLoader({ fallback, error, children }: AuthFlowsLoaderPr registerFlows = parseRegisterErrResp(registerResp); } - if (!loginFlows) { + const validLoginFlows = loginFlows && !('errcode' in loginFlows) ? loginFlows : undefined; + + // OIDC-only servers reject GET /login; that is fine when the OAuth 2.0 API is available. + if (!validLoginFlows && !authMetadata) { throw new Error('Missing auth flow!'); } - if ('errcode' in loginFlows) { - throw new Error('Failed to load auth flow!'); - } const authFlows: AuthFlows = { - loginFlows, + loginFlows: validLoginFlows ?? { flows: [] }, registerFlows, + authMetadata, }; return authFlows; @@ -57,7 +72,7 @@ export function AuthFlowsLoader({ fallback, error, children }: AuthFlowsLoaderPr } if (state.status === AsyncStatus.Error) { - return error?.(state.error); + return error?.(state.error, load); } return children(state.data); diff --git a/src/app/components/BackRouteHandler.test.tsx b/src/app/components/BackRouteHandler.test.tsx new file mode 100644 index 0000000000..20a470d8ea --- /dev/null +++ b/src/app/components/BackRouteHandler.test.tsx @@ -0,0 +1,154 @@ +import { describe, expect, it, vi } from 'vitest'; +import { fireEvent, render, screen } from '@testing-library/react'; +import { MemoryRouter, useLocation } from 'react-router'; +import { Provider as JotaiProvider, createStore } from 'jotai'; +import type { MatrixClient, Room } from '$types/matrix-sdk'; +import { MatrixClientProvider } from '$hooks/useMatrixClient'; +import { allRoomsAtom } from '$state/room-list/roomList'; +import { useBackRoute } from './BackRouteHandler'; + +vi.mock('$utils/androidBack', () => ({ + useAndroidBackHandler: vi.fn<() => void>(), +})); + +function LocationProbe() { + const location = useLocation(); + return {location.pathname}; +} + +function BackButton() { + const goBack = useBackRoute(); + return ( + + ); +} + +const makeRoom = (roomId: string, canonicalAlias?: string) => + ({ + roomId, + getCanonicalAlias: () => canonicalAlias, + getLiveTimeline: () => ({ getState: () => undefined }), + }) as unknown as Room; + +const makeClient = (rooms: Room[] = []) => + ({ + getRooms: () => rooms, + }) as unknown as MatrixClient; + +function renderBackRoute(options: { + initialPath: string; + joinedRoomIds?: string[]; + clientRooms?: Room[]; +}) { + const store = createStore(); + store.set(allRoomsAtom, { type: 'INITIALIZE', rooms: options.joinedRoomIds ?? [] }); + + render( + + + + + + + + + ); + + return { + goBack: () => fireEvent.click(screen.getByRole('button', { name: 'go back' })), + pathname: () => screen.getByTestId('pathname').textContent, + }; +} + +describe('useBackRoute', () => { + it('backs out of a room to its section list', () => { + const { goBack, pathname } = renderBackRoute({ + initialPath: '/home/!room:example.org/', + joinedRoomIds: ['!room:example.org'], + }); + + goBack(); + + expect(pathname()).toBe('/home/'); + }); + + it('backs out of a space room to the space lobby when the space is joined', () => { + const { goBack, pathname } = renderBackRoute({ + initialPath: '/!space:example.org/!room:example.org/', + joinedRoomIds: ['!space:example.org', '!room:example.org'], + }); + + goBack(); + + expect(pathname()).toBe('/!space%3Aexample.org'); + }); + + it('backs out of a joined space lobby to the space root', () => { + const { goBack, pathname } = renderBackRoute({ + initialPath: '/!space:example.org/lobby/', + joinedRoomIds: ['!space:example.org'], + }); + + goBack(); + + expect(pathname()).toBe('/!space%3Aexample.org'); + }); + + it('backs out of a space preview to home instead of looping onto the preview', () => { + const { goBack, pathname } = renderBackRoute({ + initialPath: '/!space:example.org/lobby/', + joinedRoomIds: ['!other:example.org'], + }); + + goBack(); + + expect(pathname()).toBe('/home/'); + }); + + it('backs out of a space preview reached by bare address to home', () => { + const { goBack, pathname } = renderBackRoute({ + initialPath: '/!space:example.org/', + joinedRoomIds: ['!other:example.org'], + }); + + goBack(); + + expect(pathname()).toBe('/home/'); + }); + + it('backs out of a space preview reached by alias to home', () => { + const { goBack, pathname } = renderBackRoute({ + initialPath: '/%23space%3Aexample.org/lobby/', + joinedRoomIds: ['!other:example.org'], + }); + + goBack(); + + expect(pathname()).toBe('/home/'); + }); + + it('treats a space as joined when its canonical alias resolves to a joined room', () => { + const { goBack, pathname } = renderBackRoute({ + initialPath: '/%23space%3Aexample.org/lobby/', + joinedRoomIds: ['!space:example.org'], + clientRooms: [makeRoom('!space:example.org', '#space:example.org')], + }); + + goBack(); + + expect(pathname()).toBe('/%23space%3Aexample.org'); + }); + + it('backs from an unjoined space room preview up to the space preview', () => { + const { goBack, pathname } = renderBackRoute({ + initialPath: '/!space:example.org/!room:example.org/', + joinedRoomIds: ['!other:example.org'], + }); + + goBack(); + + expect(pathname()).toBe('/!space%3Aexample.org'); + }); +}); diff --git a/src/app/components/BackRouteHandler.tsx b/src/app/components/BackRouteHandler.tsx index 1af535ab63..465363daa8 100644 --- a/src/app/components/BackRouteHandler.tsx +++ b/src/app/components/BackRouteHandler.tsx @@ -1,114 +1,78 @@ import type { ReactNode } from 'react'; import { useCallback } from 'react'; -import { useSetAtom } from 'jotai'; -import { matchPath, useLocation, useNavigate } from 'react-router-dom'; +import { useAtomValue, useSetAtom } from 'jotai'; +import { matchPath, useLocation, useNavigate } from 'react-router'; +import { getHomePath, resolveSection } from '$pages/pathUtils'; import { - getDirectPath, - getExplorePath, - getHomePath, - getInboxPath, - getSpacePath, -} from '$pages/pathUtils'; -import { - DIRECT_PATH, - EXPLORE_PATH, - HOME_PATH, - INBOX_PATH, - SPACE_PATH, - HOME_ROOM_PATH, + DIRECT_ROOM_FORUM_PATH, DIRECT_ROOM_PATH, + HOME_ROOM_FORUM_PATH, + HOME_ROOM_PATH, + SPACE_ROOM_FORUM_PATH, SPACE_ROOM_PATH, } from '$pages/paths'; -import { lastVisitedRoomIdAtom } from '$state/room/lastRoom'; +import { lastVisitedRoomAtom } from '$state/room/lastRoom'; +import { allRoomsAtom } from '$state/room-list/roomList'; +import { useMatrixClient } from '$hooks/useMatrixClient'; +import { getCanonicalAliasRoomId, isRoomAlias, isRoomId } from '$utils/matrix'; +import { useAndroidBackHandler } from '$utils/androidBack'; type BackRouteHandlerProps = { children: (onBack: () => void) => ReactNode; }; -export function BackRouteHandler({ children }: BackRouteHandlerProps) { + +export function useBackRoute(): () => void { const navigate = useNavigate(); const location = useLocation(); - const setLastRoomId = useSetAtom(lastVisitedRoomIdAtom); + const mx = useMatrixClient(); + const allRooms = useAtomValue(allRoomsAtom); + const setLastRoom = useSetAtom(lastVisitedRoomAtom); - const goBack = useCallback(() => { - const roomPaths = [HOME_ROOM_PATH, DIRECT_ROOM_PATH, SPACE_ROOM_PATH]; + return useCallback(() => { + const section = resolveSection(location.pathname); + if (!section) return; + const roomPaths = [ + HOME_ROOM_FORUM_PATH, + DIRECT_ROOM_FORUM_PATH, + SPACE_ROOM_FORUM_PATH, + HOME_ROOM_PATH, + DIRECT_ROOM_PATH, + SPACE_ROOM_PATH, + ]; const roomMatch = roomPaths .map((path) => matchPath({ path, end: false }, location.pathname)) .find((match) => match !== null); const currentRoomIdOrAlias = roomMatch?.params.roomIdOrAlias; - if (currentRoomIdOrAlias) { - setLastRoomId(decodeURIComponent(currentRoomIdOrAlias)); + const decoded = currentRoomIdOrAlias && decodeURIComponent(currentRoomIdOrAlias); + const inRoomRoute = !!decoded && (isRoomId(decoded) || isRoomAlias(decoded)); + if (section.getRoomPath && inRoomRoute && decoded) { + setLastRoom((prev) => ({ ...prev, [section.key]: decoded })); } - if ( - matchPath( - { - path: HOME_PATH, - caseSensitive: true, - end: false, - }, - location.pathname - ) - ) { - navigate(getHomePath()); - return; - } - if ( - matchPath( - { - path: DIRECT_PATH, - caseSensitive: true, - end: false, - }, - location.pathname - ) - ) { - navigate(getDirectPath()); - return; + // An unjoined space renders the preview screen on every route of its section, + // so its own list path loops back onto the same screen: escape to home instead. + if (!inRoomRoute && section.spaceIdOrAlias) { + const spaceId = isRoomId(section.spaceIdOrAlias) + ? section.spaceIdOrAlias + : getCanonicalAliasRoomId(mx, section.spaceIdOrAlias); + if (!spaceId || !allRooms.includes(spaceId)) { + navigate(getHomePath(), { replace: true }); + return; + } } - const spaceMatch = matchPath( - { - path: SPACE_PATH, - caseSensitive: true, - end: false, - }, - location.pathname - ); - const encodedSpaceIdOrAlias = spaceMatch?.params.spaceIdOrAlias; - const decodedSpaceIdOrAlias = - encodedSpaceIdOrAlias && decodeURIComponent(encodedSpaceIdOrAlias); - if (decodedSpaceIdOrAlias) { - navigate(getSpacePath(decodedSpaceIdOrAlias)); - return; - } - if ( - matchPath( - { - path: EXPLORE_PATH, - caseSensitive: true, - end: false, - }, - location.pathname - ) - ) { - navigate(getExplorePath()); - return; - } - if ( - matchPath( - { - path: INBOX_PATH, - caseSensitive: true, - end: false, - }, - location.pathname - ) - ) { - navigate(getInboxPath()); - } - }, [navigate, location, setLastRoomId]); + navigate(section.listPath); + }, [navigate, location, setLastRoom, mx, allRooms]); +} + +export function BackRouteHandler({ children }: BackRouteHandlerProps) { + const goBack = useBackRoute(); + useAndroidBackHandler(() => { + goBack(); + return true; + }); return children(goBack); } diff --git a/src/app/components/BackupRestore.test.tsx b/src/app/components/BackupRestore.test.tsx new file mode 100644 index 0000000000..de7257b9e0 --- /dev/null +++ b/src/app/components/BackupRestore.test.tsx @@ -0,0 +1,100 @@ +import { render, screen, waitFor } from '@testing-library/react'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { TypedEventEmitter } from 'matrix-js-sdk/lib/models/typed-event-emitter'; +import type { CryptoApi, KeyBackupInfo } from '$types/matrix-sdk'; +import type { SecretStorageKeyContent } from '$types/matrix/accountData'; +import { BackupRestoreTile } from './BackupRestore'; + +const emitter = new TypedEventEmitter void>>(); +const mockClient = Object.assign(emitter, { + secretStorage: { checkKey: vi.fn<() => Promise>().mockResolvedValue(true) }, +}); + +vi.mock('$hooks/useMatrixClient', () => ({ + useMatrixClient: () => mockClient, +})); + +vi.mock('$client/secretStorageKeys', () => ({ + storePrivateKey: vi.fn<() => void>(), +})); + +const KEY_ID = 'key-id'; +const KEY_CONTENT = { algorithm: 'm.secret_storage.v1.aes-hmac-sha2' } as SecretStorageKeyContent; +const BACKUP_INFO = { version: '3', count: 42 } as KeyBackupInfo; + +type CryptoOverrides = { + backupInfo?: KeyBackupInfo | null; + backupKey?: Uint8Array | null; +}; + +const createCrypto = ({ backupInfo = BACKUP_INFO, backupKey = null }: CryptoOverrides = {}) => + ({ + getActiveSessionBackupVersion: vi + .fn() + .mockResolvedValue(backupInfo ? '3' : null), + getKeyBackupInfo: vi.fn().mockResolvedValue(backupInfo), + getSessionBackupPrivateKey: vi + .fn() + .mockResolvedValue(backupKey), + isKeyBackupTrusted: vi + .fn() + .mockResolvedValue({ trusted: true, matchesDecryptionKey: true }), + restoreKeyBackup: vi.fn<() => Promise>().mockResolvedValue(undefined), + loadSessionBackupPrivateKeyFromSecretStorage: vi + .fn() + .mockResolvedValue(undefined), + }) as unknown as CryptoApi; + +const recoveryPrompt = () => screen.queryByText(/does not hold the backup decryption key/i); + +describe('BackupRestoreTile', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('offers recovery when a backup exists but its key is not in the crypto store', async () => { + render( + + ); + + await waitFor(() => expect(recoveryPrompt()).not.toBeNull()); + expect(screen.getByText('Recovery Key')).toBeTruthy(); + }); + + it('stays out of the way once the key is cached', async () => { + render( + + ); + + await waitFor(() => expect(screen.getByText('Encryption Backup')).toBeTruthy()); + expect(recoveryPrompt()).toBeNull(); + }); + + it('does not offer recovery when the server has no backup at all', async () => { + render( + + ); + + await waitFor(() => expect(screen.getByText(/No backup present on server/i)).toBeTruthy()); + expect(recoveryPrompt()).toBeNull(); + }); + + it('cannot offer recovery without secret storage configured', async () => { + render(); + + await waitFor(() => expect(screen.getByText('Encryption Backup')).toBeTruthy()); + expect(recoveryPrompt()).toBeNull(); + }); +}); diff --git a/src/app/components/BackupRestore.tsx b/src/app/components/BackupRestore.tsx index 74f365eea2..e1602c6e3d 100644 --- a/src/app/components/BackupRestore.tsx +++ b/src/app/components/BackupRestore.tsx @@ -1,37 +1,120 @@ import type { MouseEventHandler } from 'react'; import { useCallback, useState } from 'react'; -import { useAtom } from 'jotai'; +import { useAtom, useAtomValue } from 'jotai'; import type { CryptoApi, KeyBackupInfo } from '$types/matrix-sdk'; import type { RectCords } from 'folds'; import { Badge, Box, Button, + Chip, color, config, - Icon, IconButton, - Icons, Menu, percent, - PopOut, ProgressBar, Spinner, Text, } from 'folds'; +import { PopOut } from '$components/overlay-stack'; import FocusTrap from 'focus-trap-react'; -import { BackupProgressStatus, backupRestoreProgressAtom } from '$state/backupRestore'; +import type { SecretStorageKeyContent } from '$types/matrix/accountData'; +import { storePrivateKey } from '$client/secretStorageKeys'; +import { + BackupProgressStatus, + backupRestoreErrorAtom, + backupRestoreProgressAtom, + isMissingBackupKeyError, +} from '$state/backupRestore'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; import { useKeyBackupInfo, useKeyBackupStatus, useKeyBackupSync, useKeyBackupTrust, + useSessionBackupKeyUsable, } from '$hooks/useKeyBackup'; +import { SecretStorageKeyMethod, SecretStorageKeyPrompt } from './SecretStorage'; import { stopPropagation } from '$utils/keyboard'; import { useRestoreBackupOnVerification } from '$hooks/useRestoreBackupOnVerification'; +import { + Download, + DotsThreeOutlineVerticalIcon, + sizedIcon, + menuIcon, +} from '$components/icons/phosphor'; import { InfoCard } from './info-card'; +type BackupKeyRecoveryProps = { + crypto: CryptoApi; + secretStorageKeyId: string; + secretStorageKeyContent: SecretStorageKeyContent; +}; +function BackupKeyRecovery({ + crypto, + secretStorageKeyId, + secretStorageKeyContent, +}: BackupKeyRecoveryProps) { + const hasPassphrase = !!secretStorageKeyContent.passphrase; + const [method, setMethod] = useState( + hasPassphrase ? SecretStorageKeyMethod.RecoveryPassphrase : SecretStorageKeyMethod.RecoveryKey + ); + + const [unlockState, unlockBackup] = useAsyncCallback( + useCallback( + async (recoveryKey: Uint8Array) => { + storePrivateKey(secretStorageKeyId, recoveryKey); + // Emits KeyBackupDecryptionKeyCached, which drives the restore. + await crypto.loadSessionBackupPrivateKeyFromSecretStorage(); + }, + [crypto, secretStorageKeyId] + ) + ); + + const otherMethod = + method === SecretStorageKeyMethod.RecoveryPassphrase + ? SecretStorageKeyMethod.RecoveryKey + : SecretStorageKeyMethod.RecoveryPassphrase; + + return ( + + + This device does not hold the backup decryption key. Provide your recovery details to unlock + the backup and restore your message history. + + + {hasPassphrase && ( + + setMethod(otherMethod)} + > + + {otherMethod === SecretStorageKeyMethod.RecoveryPassphrase + ? 'Use Recovery Passphrase' + : 'Use Recovery Key'} + + + + )} + {unlockState.status === AsyncStatus.Error && ( + + {unlockState.error.message} + + )} + + ); +} + type BackupStatusProps = { enabled: boolean; }; @@ -134,15 +217,23 @@ function BackupTrustInfo({ crypto, backupInfo }: BackupTrustInfoProps) { type BackupRestoreTileProps = { crypto: CryptoApi; + secretStorageKeyId?: string; + secretStorageKeyContent?: SecretStorageKeyContent; }; -export function BackupRestoreTile({ crypto }: BackupRestoreTileProps) { +export function BackupRestoreTile({ + crypto, + secretStorageKeyId, + secretStorageKeyContent, +}: BackupRestoreTileProps) { const [restoreProgress, setRestoreProgress] = useAtom(backupRestoreProgressAtom); + const autoRestoreError = useAtomValue(backupRestoreErrorAtom); const restoring = restoreProgress.status === BackupProgressStatus.Fetching || restoreProgress.status === BackupProgressStatus.Loading; const backupEnabled = useKeyBackupStatus(crypto); const backupInfo = useKeyBackupInfo(crypto); + const backupKeyUsable = useSessionBackupKeyUsable(crypto); const [remainingSession, syncFailure] = useKeyBackupSync(); const [menuCords, setMenuCords] = useState(); @@ -166,6 +257,13 @@ export function BackupRestoreTile({ crypto }: BackupRestoreTileProps) { restoreBackup(); }; + // backupKeyUsable is the structural signal; the error match only covers a + // restore that failed for this reason before the lookup settled. + const needsBackupKey = + !!backupInfo && + (backupKeyUsable === false || + (restoreState.status === AsyncStatus.Error && isMissingBackupKeyError(restoreState.error))); + return ( - + {menuIcon(DotsThreeOutlineVerticalIcon, { weight: menuCords ? 'fill' : 'regular' })} } + before={sizedIcon(Download, '100')} > Restore Backup @@ -271,6 +369,18 @@ export function BackupRestoreTile({ crypto }: BackupRestoreTileProps) { {restoreState.error.message} )} + {autoRestoreError && ( + + {autoRestoreError} + + )} + {needsBackupKey && secretStorageKeyId && secretStorageKeyContent && ( + + )} ); } diff --git a/src/app/components/BetaNoticeBadge.tsx b/src/app/components/BetaNoticeBadge.tsx index a6ba55e08b..6942579768 100644 --- a/src/app/components/BetaNoticeBadge.tsx +++ b/src/app/components/BetaNoticeBadge.tsx @@ -1,4 +1,5 @@ -import { TooltipProvider, Tooltip, Box, Text, Badge, toRem } from 'folds'; +import { Tooltip, Box, Text, Badge, toRem } from 'folds'; +import { TooltipProvider } from '$components/overlay-stack'; export function BetaNoticeBadge() { return ( diff --git a/src/app/components/CallEmbedProvider.tsx b/src/app/components/CallEmbedProvider.tsx index 611b27566b..fe75d1c486 100644 --- a/src/app/components/CallEmbedProvider.tsx +++ b/src/app/components/CallEmbedProvider.tsx @@ -1,6 +1,6 @@ import type { ReactNode } from 'react'; -import { useCallback, useRef } from 'react'; -import { useAtomValue, useSetAtom } from 'jotai'; +import { useCallback, useEffect, useRef } from 'react'; +import { useAtomValue, useSetAtom, useStore } from 'jotai'; import { useAutoJoinCall } from '$hooks/useAutoJoinCall'; import { CallEmbedContextProvider, @@ -12,13 +12,17 @@ import { } from '$hooks/useCallEmbed'; import type { CallEmbed } from '$plugins/call'; import { useClientWidgetApiEvent, ElementWidgetActions } from '$plugins/call'; -import { callChatAtom, callEmbedAtom } from '$state/callEmbed'; +import { callChatAtom, callEmbedAtom, callEmbedStartErrorAtom } from '$state/callEmbed'; import { useSelectedRoom } from '$hooks/router/useSelectedRoom'; import { ScreenSize, useScreenSizeContext } from '$hooks/useScreenSize'; -import { IncomingCallModal } from './IncomingCallModal'; +import { IncomingCallModal } from '$features/call/IncomingCallModal'; +import { toCallEmbedStartError } from '$plugins/call/callEmbedError'; +import { LivekitJsCallManagerProvider } from '$features/call/livekitJsCallManager'; +import { getNativeCallManager } from '$features/call/nativeCallManager'; function CallUtils({ embed }: { embed: CallEmbed }) { const setCallEmbed = useSetAtom(callEmbedAtom); + const setCallEmbedStartError = useSetAtom(callEmbedStartErrorAtom); useCallMemberSoundSync(embed); useCallThemeSync(embed); @@ -30,6 +34,24 @@ function CallUtils({ embed }: { embed: CallEmbed }) { useCallHangupEvent(embed, handleCallEnd); useClientWidgetApiEvent(embed.call, ElementWidgetActions.Close, handleCallEnd); + useEffect(() => { + const disposeOnReady = embed.onReady(() => { + setCallEmbedStartError(null); + }); + const disposeOnCapabilitiesNotified = embed.onCapabilitiesNotified(() => { + setCallEmbedStartError(null); + }); + const disposeOnPreparingError = embed.onPreparingError((error) => { + setCallEmbedStartError(toCallEmbedStartError(error)); + }); + + return () => { + disposeOnReady(); + disposeOnCapabilitiesNotified(); + disposeOnPreparingError(); + }; + }, [embed, setCallEmbedStartError]); + return null; } @@ -43,6 +65,8 @@ function AutoJoinManager() { } export function CallEmbedProvider({ children }: CallEmbedProviderProps) { + const store = useStore(); + getNativeCallManager(store); const callEmbed = useAtomValue(callEmbedAtom); const callEmbedRef = useRef(null); const joined = useCallJoined(callEmbed); @@ -60,8 +84,10 @@ export function CallEmbedProvider({ children }: CallEmbedProviderProps) { {callEmbed && } - - {children} + + + {children} +
- * {(config) => { setMatrixToBase(config.matrixToBaseUrl); ... }} - * - * - * We mock fetch so we don't need a real config.json or a live matrix.to instance. - */ -import { describe, it, expect, vi, afterEach } from 'vitest'; -import { render, screen, waitFor } from '@testing-library/react'; -import { setMatrixToBase, getMatrixToRoom, getMatrixToUser } from '$plugins/matrix-to'; -import { ClientConfigLoader } from './ClientConfigLoader'; +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { getMatrixToRoom, setMatrixToBase } from '$plugins/matrix-to'; +import { ClientConfigLoader, FALLBACK_CLIENT_CONFIG } from './ClientConfigLoader'; + +const successfulResponse = (config: unknown) => ({ + ok: true, + status: 200, + statusText: 'OK', + json: vi.fn<() => Promise>().mockResolvedValue(config), +}); afterEach(() => { - setMatrixToBase(); // reset module state to 'https://matrix.to' + setMatrixToBase(); vi.unstubAllGlobals(); }); -const mockFetch = (config: object) => - vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ json: () => Promise.resolve(config) })); - -describe('ClientConfigLoader + matrix-to wiring', () => { - it('generates a standard matrix.to URL when no custom base is configured', async () => { - mockFetch({}); +describe('ClientConfigLoader', () => { + it('does not render config-dependent children while loading', () => { + vi.stubGlobal( + 'fetch', + vi.fn(() => new Promise(() => undefined)) + ); render( - - {(config) => { - setMatrixToBase(config.matrixToBaseUrl); - return {getMatrixToRoom('!room:example.com')}; - }} + Loading}> + {() => Configured app} ); - await waitFor(() => - expect(screen.getByTestId('link')).toHaveTextContent('https://matrix.to/#/!room:example.com') - ); + expect(screen.getByText('Loading')).toBeInTheDocument(); + expect(screen.queryByText('Configured app')).not.toBeInTheDocument(); }); - it('generates a custom-base URL for rooms when matrixToBaseUrl is set', async () => { - mockFetch({ matrixToBaseUrl: 'https://custom.example.org' }); + it('loads the config before rendering children', async () => { + vi.stubGlobal( + 'fetch', + vi.fn().mockResolvedValue(successfulResponse({ allowCustomHomeservers: true })) + ); render( - {(config) => { - setMatrixToBase(config.matrixToBaseUrl); - return {getMatrixToRoom('!room:example.com')}; - }} + {(config) => {String(config.allowCustomHomeservers)}} ); - await waitFor(() => - expect(screen.getByTestId('link')).toHaveTextContent( - 'https://custom.example.org/#/!room:example.com' - ) + expect(await screen.findByText('true')).toBeInTheDocument(); + }); + + it('reports non-success responses and retries', async () => { + const fetchMock = vi + .fn() + .mockResolvedValueOnce({ + ok: false, + status: 503, + statusText: 'Service Unavailable', + json: vi.fn<() => Promise>(), + } as unknown as Response) + .mockResolvedValueOnce( + successfulResponse({ homeserverList: ['example.org'] }) as unknown as Response + ); + vi.stubGlobal('fetch', fetchMock); + + render( + ( + + )} + > + {(config) => {config.homeserverList?.[0]}} + ); + + fireEvent.click(await screen.findByRole('button', { name: /503 Service Unavailable/ })); + expect(await screen.findByText('example.org')).toBeInTheDocument(); + expect(fetchMock).toHaveBeenCalledTimes(2); }); - it('generates a custom-base URL for users when matrixToBaseUrl is set', async () => { - mockFetch({ matrixToBaseUrl: 'https://custom.example.org' }); + it('rejects non-object JSON', async () => { + vi.stubGlobal('fetch', vi.fn().mockResolvedValue(successfulResponse([]))); render( - - {(config) => { - setMatrixToBase(config.matrixToBaseUrl); - return {getMatrixToUser('@alice:example.com')}; - }} + {String(error)}}> + {() => Configured app} ); - await waitFor(() => - expect(screen.getByTestId('user')).toHaveTextContent( - 'https://custom.example.org/#/@alice:example.com' - ) + expect(await screen.findByText(/must contain a JSON object/)).toBeInTheDocument(); + expect(screen.queryByText('Configured app')).not.toBeInTheDocument(); + }); + + it('uses deliberate defaults only after continuing', async () => { + vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error('offline'))); + + render( + ( + + )} + > + {(config) => {config.homeserverList?.join(',')}} + ); + + fireEvent.click(await screen.findByRole('button', { name: 'Continue' })); + expect(screen.getByText(FALLBACK_CLIENT_CONFIG.homeserverList?.[0] ?? '')).toBeInTheDocument(); }); - it('strips a trailing slash from matrixToBaseUrl', async () => { - mockFetch({ matrixToBaseUrl: 'https://custom.example.org/' }); + it('applies matrix.to configuration before rendering links', async () => { + vi.stubGlobal( + 'fetch', + vi.fn().mockResolvedValue(successfulResponse({ matrixToBaseUrl: 'https://custom.example/' })) + ); render( {(config) => { setMatrixToBase(config.matrixToBaseUrl); - return {getMatrixToRoom('!room:example.com')}; + return {getMatrixToRoom('!room:example.com')}; }} ); await waitFor(() => - expect(screen.getByTestId('link')).toHaveTextContent( - 'https://custom.example.org/#/!room:example.com' - ) + expect(screen.getByText('https://custom.example/#/!room:example.com')).toBeInTheDocument() ); }); }); diff --git a/src/app/components/ClientConfigLoader.tsx b/src/app/components/ClientConfigLoader.tsx index f03b8398e0..f99d9f0cc4 100644 --- a/src/app/components/ClientConfigLoader.tsx +++ b/src/app/components/ClientConfigLoader.tsx @@ -1,39 +1,121 @@ import type { ReactNode } from 'react'; -import { useCallback, useEffect, useState } from 'react'; -import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; +import { useCallback, useEffect, useRef, useState } from 'react'; +import { Box, Button, Dialog, Text, color, config } from 'folds'; import type { ClientConfig } from '$hooks/useClientConfig'; +import { takePreloadedConfig } from '$utils/preload'; import { trimTrailingSlash } from '$utils/common'; +import { fetch } from '$utils/fetch'; +import { SplashScreen } from '$components/splash-screen'; + +export const FALLBACK_CLIENT_CONFIG: ClientConfig = { + defaultHomeserver: 0, + homeserverList: ['matrix.org'], + allowCustomHomeservers: false, + hashRouter: { enabled: false, basename: '/' }, +}; const getClientConfig = async (): Promise => { + const preloaded = takePreloadedConfig(); + if (preloaded !== undefined) { + const data = await preloaded.catch(() => undefined); + if (data && typeof data === 'object' && !Array.isArray(data)) { + return data as ClientConfig; + } + // Preload missed or invalid — fall through to fresh fetch. + } + const url = `${trimTrailingSlash(import.meta.env.BASE_URL)}/config.json`; - const config = await fetch(url, { method: 'GET' }); - return config.json(); + const response = await fetch(url, { method: 'GET' }); + if (!response.ok) { + throw new Error(`Failed to load config.json (${response.status} ${response.statusText})`); + } + + const data: unknown = await response.json(); + if (!data || typeof data !== 'object' || Array.isArray(data)) { + throw new Error('config.json must contain a JSON object.'); + } + return data as ClientConfig; }; type ClientConfigLoaderProps = { fallback?: () => ReactNode; - error?: (err: unknown, retry: () => void, ignore: () => void) => ReactNode; + error?: (error: unknown, retry: () => void, ignore: () => void) => ReactNode; children: (config: ClientConfig) => ReactNode; }; + +type ConfigLoadState = + | { status: 'loading' } + | { status: 'ready'; config: ClientConfig } + | { status: 'error'; error: unknown }; + +const renderConfigError = (error: unknown, retry: () => void, ignore: () => void) => ( + + + + + + Failed to load client configuration. + {error instanceof Error && ( + + {error.message} + + )} + + + + + + + +); + export function ClientConfigLoader({ fallback, error, children }: ClientConfigLoaderProps) { - const [state, load] = useAsyncCallback(getClientConfig); - const [ignoreError, setIgnoreError] = useState(false); + const [state, setState] = useState({ status: 'loading' }); + const [useFallback, setUseFallback] = useState(false); + const requestIdRef = useRef(0); + + const load = useCallback(() => { + const requestId = requestIdRef.current + 1; + requestIdRef.current = requestId; + setUseFallback(false); + setState({ status: 'loading' }); - const ignoreCallback = useCallback(() => setIgnoreError(true), []); + void getClientConfig().then( + (loadedConfig) => { + if (requestId === requestIdRef.current) { + setState({ status: 'ready', config: loadedConfig }); + } + }, + (loadError: unknown) => { + if (requestId === requestIdRef.current) setState({ status: 'error', error: loadError }); + } + ); + }, []); + + const ignoreError = useCallback(() => setUseFallback(true), []); useEffect(() => { load(); + return () => { + requestIdRef.current += 1; + }; }, [load]); - if (state.status === AsyncStatus.Idle || state.status === AsyncStatus.Loading) { + if (state.status === 'loading') { return fallback?.(); } - - if (!ignoreError && state.status === AsyncStatus.Error) { - return error?.(state.error, load, ignoreCallback); + if (state.status === 'error' && !useFallback) { + return (error ?? renderConfigError)(state.error, load, ignoreError); } - const config: ClientConfig = state.status === AsyncStatus.Success ? state.data : {}; - - return children(config); + const clientConfig = state.status === 'ready' ? state.config : FALLBACK_CLIENT_CONFIG; + return children(clientConfig); } diff --git a/src/app/components/ConfirmPasswordMatch.tsx b/src/app/components/ConfirmPasswordMatch.tsx index b004b38299..1b8d998e3e 100644 --- a/src/app/components/ConfirmPasswordMatch.tsx +++ b/src/app/components/ConfirmPasswordMatch.tsx @@ -7,8 +7,8 @@ type ConfirmPasswordMatchProps = { children: ( match: boolean, doMatch: () => void, - passRef: RefObject, - confPassRef: RefObject + passRef: RefObject, + confPassRef: RefObject ) => ReactNode; }; export function ConfirmPasswordMatch({ initialValue, children }: ConfirmPasswordMatchProps) { diff --git a/src/app/components/DefaultErrorPage.test.tsx b/src/app/components/DefaultErrorPage.test.tsx new file mode 100644 index 0000000000..78f7c9b906 --- /dev/null +++ b/src/app/components/DefaultErrorPage.test.tsx @@ -0,0 +1,69 @@ +import { fireEvent, render, screen } from '@testing-library/react'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { ErrorPage } from './DefaultErrorPage'; + +const { captureFeedback, isInitialized, isMobileTauri, openUrl, showReportDialog } = vi.hoisted( + () => ({ + captureFeedback: vi.fn<(feedback: unknown) => void>(), + isInitialized: vi.fn<() => boolean>(), + isMobileTauri: vi.fn<() => boolean>(), + openUrl: vi.fn<() => Promise>().mockResolvedValue(undefined), + showReportDialog: vi.fn<(options: { eventId?: string }) => void>(), + }) +); + +vi.mock('@sentry/react', () => ({ + captureFeedback, + isInitialized, + showReportDialog, +})); + +vi.mock('@tauri-apps/api/core', () => ({ + isTauri: () => true, +})); + +vi.mock('@tauri-apps/plugin-opener', () => ({ openUrl })); + +vi.mock('$utils/platform', () => ({ + isMobileOrTablet: () => false, + isMobileTauri, +})); + +vi.mock('$features/bug-report', () => ({ + buildGitHubUrl: () => 'https://github.com/SableClient/Sable/issues/new', +})); + +describe('ErrorPage', () => { + beforeEach(() => { + vi.clearAllMocks(); + isInitialized.mockReturnValue(true); + }); + + it('uses native feedback and URL opening on mobile Tauri', () => { + isMobileTauri.mockReturnValue(true); + render(); + + fireEvent.change(screen.getByPlaceholderText(/what were you doing/i), { + target: { value: 'I was opening a gallery.' }, + }); + fireEvent.click(screen.getByRole('button', { name: 'Add Details to Report' })); + + expect(captureFeedback).toHaveBeenCalledWith({ + message: 'I was opening a gallery.', + name: 'Crash report follow-up', + associatedEventId: 'event-id', + }); + + fireEvent.click(screen.getByRole('button', { name: 'Also Report on GitHub' })); + expect(openUrl).toHaveBeenCalledOnce(); + }); + + it('keeps the Sentry dialog on non-mobile platforms', () => { + isMobileTauri.mockReturnValue(false); + render(); + + fireEvent.click(screen.getByRole('button', { name: 'Add Details to Report' })); + + expect(showReportDialog).toHaveBeenCalledWith({ eventId: 'event-id' }); + }); +}); diff --git a/src/app/components/DefaultErrorPage.tsx b/src/app/components/DefaultErrorPage.tsx index 62042cef1e..914e20e58c 100644 --- a/src/app/components/DefaultErrorPage.tsx +++ b/src/app/components/DefaultErrorPage.tsx @@ -1,11 +1,16 @@ -import { Box, Button, Dialog, Icon, Icons, Text, color, config } from 'folds'; +import { useState } from 'react'; +import { isTauri } from '@tauri-apps/api/core'; +import { openUrl } from '@tauri-apps/plugin-opener'; +import { Box, Button, Dialog, Text, TextArea, color, config } from 'folds'; +import { Warning, sizedIcon } from '$components/icons/phosphor'; import * as Sentry from '@sentry/react'; import { SplashScreen } from '$components/splash-screen'; -import { buildGitHubUrl } from '$features/bug-report/BugReportModal'; +import { buildGitHubUrl } from '$features/bug-report'; +import { isMobileTauri } from '$utils/platform'; type ErrorPageProps = { error: Error; - /** Sentry event ID — present when Sentry.ErrorBoundary captured the crash */ + /** Sentry event ID  Epresent when Sentry.ErrorBoundary captured the crash */ eventId?: string; }; @@ -28,6 +33,19 @@ ${stacktrace} return buildGitHubUrl('bug', `Error: ${error.message}`, { context: automatedBugReport }); } +async function openIssueUrl(error: Error) { + const url = createIssueUrl(error); + if (isTauri()) { + try { + await openUrl(url); + } catch { + window.open(url, '_blank', 'noopener,noreferrer'); + } + return; + } + window.open(url, '_blank', 'noopener,noreferrer'); +} + // This component is used as the fallback for the ErrorBoundary in App.tsx, which means it will be rendered whenever an uncaught error is thrown in any of the child components and not handled locally. // It provides a user-friendly error message and options to report the issue or reload the page. // Motivation of the design is to encourage users to report issues while also providing them with the necessary information to do so, and to give them an easy way to recover by reloading the page. @@ -35,6 +53,19 @@ ${stacktrace} export function ErrorPage({ error, eventId }: ErrorPageProps) { const sentryEnabled = Sentry.isInitialized(); const reportedToSentry = sentryEnabled && !!eventId; + const [feedback, setFeedback] = useState(''); + const [feedbackSubmitted, setFeedbackSubmitted] = useState(false); + + const submitFeedback = () => { + if (!eventId || !feedback.trim()) return; + Sentry.captureFeedback({ + message: feedback.trim(), + name: 'Crash report follow-up', + associatedEventId: eventId, + }); + setFeedbackSubmitted(true); + }; + return ( @@ -48,12 +79,7 @@ export function ErrorPage({ error, eventId }: ErrorPageProps) { - + {sizedIcon(Warning, '300', { filled: true, style: { color: color.Critical.Main } })} Oops! Something went wrong @@ -63,21 +89,49 @@ export function ErrorPage({ error, eventId }: ErrorPageProps) { {reportedToSentry ? ( - + {isMobileTauri() ? ( + feedbackSubmitted ? ( + Thank you for adding details to the report. + ) : ( + +