Showing preview only (1,554K chars total). Download the full file or copy to clipboard to get everything.
Repository: cjpais/Handy
Branch: main
Commit: 8836d4553261
Files: 249
Total size: 1.4 MB
Directory structure:
gitextract_qhrvhh5a/
├── .cargo/
│ └── config.toml
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── config.yml
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── workflows/
│ ├── build-test.yml
│ ├── build.yml
│ ├── code-quality.yml
│ ├── main-build.yml
│ ├── nix-check.yml
│ ├── playwright.yml
│ ├── pr-test-build.yml
│ ├── release.yml
│ └── test.yml
├── .gitignore
├── .nix/
│ ├── bun-lock-hash
│ └── bun.nix
├── .prettierignore
├── .prettierrc
├── .vscode/
│ └── extensions.json
├── AGENTS.md
├── BUILD.md
├── CHANGELOG.md
├── CLAUDE.md
├── CONTRIBUTING.md
├── CONTRIBUTING_TRANSLATIONS.md
├── CRUSH.md
├── LICENSE
├── README.md
├── eslint.config.js
├── flake.nix
├── index.html
├── nix/
│ ├── hm-module.nix
│ └── module.nix
├── package.json
├── playwright.config.ts
├── scripts/
│ ├── check-nix-deps.ts
│ └── check-translations.ts
├── src/
│ ├── App.css
│ ├── App.tsx
│ ├── bindings.ts
│ ├── components/
│ │ ├── AccessibilityPermissions.tsx
│ │ ├── Sidebar.tsx
│ │ ├── footer/
│ │ │ ├── Footer.tsx
│ │ │ └── index.ts
│ │ ├── icons/
│ │ │ ├── CancelIcon.tsx
│ │ │ ├── HandyHand.tsx
│ │ │ ├── HandyTextLogo.tsx
│ │ │ ├── MicrophoneIcon.tsx
│ │ │ ├── ResetIcon.tsx
│ │ │ ├── TranscriptionIcon.tsx
│ │ │ └── index.ts
│ │ ├── model-selector/
│ │ │ ├── DownloadProgressDisplay.tsx
│ │ │ ├── ModelDropdown.tsx
│ │ │ ├── ModelSelector.tsx
│ │ │ ├── ModelStatusButton.tsx
│ │ │ └── index.ts
│ │ ├── onboarding/
│ │ │ ├── AccessibilityOnboarding.tsx
│ │ │ ├── ModelCard.tsx
│ │ │ ├── Onboarding.tsx
│ │ │ └── index.ts
│ │ ├── settings/
│ │ │ ├── AccelerationSelector.tsx
│ │ │ ├── AlwaysOnMicrophone.tsx
│ │ │ ├── AppDataDirectory.tsx
│ │ │ ├── AppLanguageSelector.tsx
│ │ │ ├── AppendTrailingSpace.tsx
│ │ │ ├── AudioFeedback.tsx
│ │ │ ├── AutoSubmit.tsx
│ │ │ ├── AutostartToggle.tsx
│ │ │ ├── ClamshellMicrophoneSelector.tsx
│ │ │ ├── ClipboardHandling.tsx
│ │ │ ├── CustomWords.tsx
│ │ │ ├── ExperimentalToggle.tsx
│ │ │ ├── GlobalShortcutInput.tsx
│ │ │ ├── HandyKeysShortcutInput.tsx
│ │ │ ├── HistoryLimit.tsx
│ │ │ ├── LanguageSelector.tsx
│ │ │ ├── LazyStreamClose.tsx
│ │ │ ├── MicrophoneSelector.tsx
│ │ │ ├── ModelUnloadTimeout.tsx
│ │ │ ├── MuteWhileRecording.tsx
│ │ │ ├── OutputDeviceSelector.tsx
│ │ │ ├── PasteMethod.tsx
│ │ │ ├── PostProcessingSettingsApi/
│ │ │ │ ├── ApiKeyField.tsx
│ │ │ │ ├── BaseUrlField.tsx
│ │ │ │ ├── ModelSelect.tsx
│ │ │ │ ├── ProviderSelect.tsx
│ │ │ │ ├── index.tsx
│ │ │ │ ├── types.ts
│ │ │ │ └── usePostProcessProviderState.ts
│ │ │ ├── PostProcessingSettingsPrompts.tsx
│ │ │ ├── PostProcessingToggle.tsx
│ │ │ ├── PushToTalk.tsx
│ │ │ ├── RecordingRetentionPeriod.tsx
│ │ │ ├── ShortcutInput.tsx
│ │ │ ├── ShowOverlay.tsx
│ │ │ ├── ShowTrayIcon.tsx
│ │ │ ├── SoundPicker.tsx
│ │ │ ├── StartHidden.tsx
│ │ │ ├── TranslateToEnglish.tsx
│ │ │ ├── TypingTool.tsx
│ │ │ ├── UpdateChecksToggle.tsx
│ │ │ ├── VolumeSlider.tsx
│ │ │ ├── about/
│ │ │ │ └── AboutSettings.tsx
│ │ │ ├── advanced/
│ │ │ │ └── AdvancedSettings.tsx
│ │ │ ├── debug/
│ │ │ │ ├── DebugPaths.tsx
│ │ │ │ ├── DebugSettings.tsx
│ │ │ │ ├── KeyboardImplementationSelector.tsx
│ │ │ │ ├── LogDirectory.tsx
│ │ │ │ ├── LogLevelSelector.tsx
│ │ │ │ ├── PasteDelay.tsx
│ │ │ │ ├── RecordingBuffer.tsx
│ │ │ │ ├── WordCorrectionThreshold.tsx
│ │ │ │ └── index.ts
│ │ │ ├── general/
│ │ │ │ ├── GeneralSettings.tsx
│ │ │ │ └── ModelSettingsCard.tsx
│ │ │ ├── history/
│ │ │ │ └── HistorySettings.tsx
│ │ │ ├── index.ts
│ │ │ ├── models/
│ │ │ │ ├── ModelsSettings.tsx
│ │ │ │ └── index.ts
│ │ │ └── post-processing/
│ │ │ └── PostProcessingSettings.tsx
│ │ ├── shared/
│ │ │ ├── ProgressBar.tsx
│ │ │ └── index.ts
│ │ ├── ui/
│ │ │ ├── Alert.tsx
│ │ │ ├── AudioPlayer.tsx
│ │ │ ├── Badge.tsx
│ │ │ ├── Button.tsx
│ │ │ ├── Dropdown.tsx
│ │ │ ├── Input.tsx
│ │ │ ├── PathDisplay.tsx
│ │ │ ├── ResetButton.tsx
│ │ │ ├── Select.tsx
│ │ │ ├── SettingContainer.tsx
│ │ │ ├── SettingsGroup.tsx
│ │ │ ├── Slider.tsx
│ │ │ ├── TextDisplay.tsx
│ │ │ ├── Textarea.tsx
│ │ │ ├── ToggleSwitch.tsx
│ │ │ ├── Tooltip.tsx
│ │ │ └── index.ts
│ │ └── update-checker/
│ │ ├── UpdateChecker.tsx
│ │ └── index.ts
│ ├── hooks/
│ │ ├── useOsType.ts
│ │ └── useSettings.ts
│ ├── i18n/
│ │ ├── index.ts
│ │ ├── languages.ts
│ │ └── locales/
│ │ ├── ar/
│ │ │ └── translation.json
│ │ ├── cs/
│ │ │ └── translation.json
│ │ ├── de/
│ │ │ └── translation.json
│ │ ├── en/
│ │ │ └── translation.json
│ │ ├── es/
│ │ │ └── translation.json
│ │ ├── fr/
│ │ │ └── translation.json
│ │ ├── it/
│ │ │ └── translation.json
│ │ ├── ja/
│ │ │ └── translation.json
│ │ ├── ko/
│ │ │ └── translation.json
│ │ ├── pl/
│ │ │ └── translation.json
│ │ ├── pt/
│ │ │ └── translation.json
│ │ ├── ru/
│ │ │ └── translation.json
│ │ ├── tr/
│ │ │ └── translation.json
│ │ ├── uk/
│ │ │ └── translation.json
│ │ ├── vi/
│ │ │ └── translation.json
│ │ ├── zh/
│ │ │ └── translation.json
│ │ └── zh-TW/
│ │ └── translation.json
│ ├── lib/
│ │ ├── constants/
│ │ │ └── languages.ts
│ │ ├── types/
│ │ │ └── events.ts
│ │ └── utils/
│ │ ├── format.ts
│ │ ├── keyboard.ts
│ │ ├── modelTranslation.ts
│ │ └── rtl.ts
│ ├── main.tsx
│ ├── overlay/
│ │ ├── RecordingOverlay.css
│ │ ├── RecordingOverlay.tsx
│ │ ├── index.html
│ │ └── main.tsx
│ ├── stores/
│ │ ├── modelStore.ts
│ │ └── settingsStore.ts
│ ├── utils/
│ │ └── dateFormat.ts
│ └── vite-env.d.ts
├── src-tauri/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── Entitlements.plist
│ ├── Info.plist
│ ├── build.rs
│ ├── capabilities/
│ │ ├── default.json
│ │ └── desktop.json
│ ├── gen/
│ │ └── apple/
│ │ └── PrivacyInfo.xcprivacy
│ ├── icons/
│ │ └── icon.icns
│ ├── nsis/
│ │ └── installer.nsi
│ ├── resources/
│ │ ├── default_settings.json
│ │ └── models/
│ │ ├── gigaam_vocab.txt
│ │ └── silero_vad_v4.onnx
│ ├── rustfmt.toml
│ ├── src/
│ │ ├── actions.rs
│ │ ├── apple_intelligence.rs
│ │ ├── audio_feedback.rs
│ │ ├── audio_toolkit/
│ │ │ ├── audio/
│ │ │ │ ├── device.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── recorder.rs
│ │ │ │ ├── resampler.rs
│ │ │ │ ├── utils.rs
│ │ │ │ └── visualizer.rs
│ │ │ ├── bin/
│ │ │ │ └── cli.rs
│ │ │ ├── constants.rs
│ │ │ ├── mod.rs
│ │ │ ├── text.rs
│ │ │ ├── utils.rs
│ │ │ └── vad/
│ │ │ ├── mod.rs
│ │ │ ├── silero.rs
│ │ │ └── smoothed.rs
│ │ ├── cli.rs
│ │ ├── clipboard.rs
│ │ ├── commands/
│ │ │ ├── audio.rs
│ │ │ ├── history.rs
│ │ │ ├── mod.rs
│ │ │ ├── models.rs
│ │ │ └── transcription.rs
│ │ ├── helpers/
│ │ │ ├── clamshell.rs
│ │ │ └── mod.rs
│ │ ├── input.rs
│ │ ├── lib.rs
│ │ ├── llm_client.rs
│ │ ├── main.rs
│ │ ├── managers/
│ │ │ ├── audio.rs
│ │ │ ├── history.rs
│ │ │ ├── mod.rs
│ │ │ ├── model.rs
│ │ │ ├── transcription.rs
│ │ │ └── transcription_mock.rs
│ │ ├── overlay.rs
│ │ ├── portable.rs
│ │ ├── settings.rs
│ │ ├── shortcut/
│ │ │ ├── handler.rs
│ │ │ ├── handy_keys.rs
│ │ │ ├── mod.rs
│ │ │ └── tauri_impl.rs
│ │ ├── signal_handle.rs
│ │ ├── transcription_coordinator.rs
│ │ ├── tray.rs
│ │ ├── tray_i18n.rs
│ │ └── utils.rs
│ ├── swift/
│ │ ├── apple_intelligence.swift
│ │ ├── apple_intelligence_bridge.h
│ │ └── apple_intelligence_stub.swift
│ └── tauri.conf.json
├── tailwind.config.js
├── tests/
│ └── app.spec.ts
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: .cargo/config.toml
================================================
[build]
================================================
FILE: .github/FUNDING.yml
================================================
github: cjpais
custom: ["https://handy.computer/donate", "https://www.paypal.me/cjpais"]
buy_me_a_coffee: cjpais
ko_fi: cjpais
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug Report
about: Create a report to help us improve Handy
title: "[BUG] "
labels: ["bug"]
assignees: ""
---
## Before You Submit
**Please search [existing issues](https://github.com/cjpais/Handy/issues) to avoid duplicates.** Your bug may already be reported! Right now it's just me maintaining this project so many issues can be overwhelming! Help me out by checking first.
## Bug Description
A clear and concise description of what the bug is.
## System Information
**App Version:**
<!-- You can find this in the app settings or about section -->
**Operating System:**
<!-- e.g., macOS 14.1, Windows 11, Ubuntu 22.04 -->
**CPU:**
<!-- e.g., Apple M2, Intel i7-12700K, AMD Ryzen 7 5800X -->
**GPU:**
<!-- e.g., Apple M2 GPU, NVIDIA RTX 4080, AMD RX 6800 XT, Intel UHD Graphics -->
## Logs
<!-- Please attach relevant logs to help us diagnose the issue. You can find the log directory by going to Settings > About in the app. -->
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
- name: ✏️ Post-processing / Editing Transcripts
url: https://github.com/cjpais/Handy/discussions/168
about: Looking to edit, format, or post-process transcripts? Join this discussion
- name: ⌨️ Keyboard Shortcuts / Hotkeys
url: https://github.com/cjpais/Handy/discussions/211
about: Want different keyboard shortcuts or hotkey configurations? Join this discussion
- name: 💡 Feature Request or Idea
url: https://github.com/cjpais/Handy/discussions
about: Please post feature requests and ideas in our Discussions tab
- name: 💬 General Discussion
url: https://github.com/cjpais/Handy/discussions
about: Ask questions and discuss Handy with the community
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
## Before Submitting This PR
<!--
HANDY IS UNDERGOING A FEATURE FREEZE. IF YOU ARE SUBMITTING A PR WHICH IS A NEW FEATURE THAT THE COMMUNITY HAS NOT ASKED FOR: PREPARE TO BE REJECTED. IF THE COMMUNITY HAS ASKED FOR IT, OR YOU HAVE EXPLICITLY GATEHRED SUPPORT IT MAY STILL BE CONSIDERED.
BUG FIXES ARE THE TOP PRIORITY. THERE ARE 60+ ISSUES TO FIX.
-->
**Please confirm you have done the following:**
- [ ] I have searched [existing issues](https://github.com/cjpais/Handy/issues) and [pull requests](https://github.com/cjpais/Handy/pulls) (including closed ones) to ensure this isn't a duplicate
- [ ] I have read [CONTRIBUTING.md](https://github.com/cjpais/Handy/blob/main/CONTRIBUTING.md)
**If this is a feature or change that was previously closed/rejected:**
- [ ] I have explained in the description below why this should be reconsidered
- [ ] I have gathered community feedback (link to discussion below)
## Human Written Description
<!-- Describe your changes clearly and concisely
Please write 2-3 sentences in your own words explaining:
- What problem you noticed or idea you had
- Why you think this change matters
This section should be YOUR thinking, not AI-generated text. Even if AI helped write the code, we want to hear from you directly. Your perspective as a human is what makes contributions meaningful. Your PR may be rejected if you do not
include a human-written description.
-->
## Related Issues/Discussions
<!-- Link to related issues, discussions, or previous PRs -->
<!-- If reopening something previously closed, explain why this should be reconsidered -->
Fixes #
Discussion:
## Community Feedback
<!--
PRs with community support are much more likely to be merged.
For features: Link to a discussion where community members have expressed interest.
For bug fixes: Link to the issue where others have confirmed the bug.
If you haven't gathered feedback yet, consider starting a discussion first:
https://github.com/cjpais/Handy/discussions
It is not explicitly required to gather feedback, but it certainly helps your PR get merged.
-->
## Testing
<!-- Describe how you tested your changes and if you need help getting additional testing -->
## Screenshots/Videos (if applicable)
<!-- Add screenshots or videos demonstrating the change -->
## AI Assistance
<!-- AI-assisted PRs are welcome! Just let us know so we can review appropriately. -->
- [ ] No AI was used in this PR
- [ ] AI was used (please describe below)
**If AI was used:**
- Tools used:
- How extensively:
================================================
FILE: .github/workflows/build-test.yml
================================================
name: "Build Test"
on: workflow_dispatch
jobs:
build-test:
permissions:
contents: write
strategy:
fail-fast: false
matrix:
include:
- platform: "macos-26" # for Arm based macs (M1 and above). Uses macOS 26 for Apple Intelligence SDK.
args: "--target aarch64-apple-darwin"
target: "aarch64-apple-darwin"
- platform: "macos-latest" # for Intel based macs.
args: "--target x86_64-apple-darwin"
target: "x86_64-apple-darwin"
- platform: "ubuntu-22.04" # Build .deb on 22.04
args: "--bundles deb"
target: "x86_64-unknown-linux-gnu"
- platform: "ubuntu-24.04" # Build AppImage and RPM on 24.04
args: "--bundles appimage,rpm"
target: "x86_64-unknown-linux-gnu"
- platform: "ubuntu-24.04-arm" # Build for ARM64 Linux
args: "--bundles appimage,deb,rpm"
target: "aarch64-unknown-linux-gnu"
- platform: "windows-latest"
args: ""
target: "x86_64-pc-windows-msvc"
- platform: "windows-11-arm" # for ARM64 Windows runner
args: "--target aarch64-pc-windows-msvc"
target: "aarch64-pc-windows-msvc"
uses: ./.github/workflows/build.yml
with:
platform: ${{ matrix.platform }}
target: ${{ matrix.target }}
build-args: ${{ matrix.args }}
sign-binaries: true
asset-prefix: "handy-test"
upload-artifacts: true
is-debug-build: ${{ contains(matrix.args, '--debug') }}
secrets: inherit
================================================
FILE: .github/workflows/build.yml
================================================
name: "Build"
on:
workflow_call:
inputs:
platform:
required: true
type: string
target:
required: true
type: string
build-args:
required: false
type: string
default: ""
release-id:
required: false
type: string
asset-prefix:
required: false
type: string
default: "handy"
asset-name-pattern:
required: false
type: string
default: ""
upload-artifacts:
required: false
type: boolean
default: false
sign-binaries:
required: false
type: boolean
default: false
repository:
required: false
type: string
ref:
required: false
type: string
default: ${{ github.ref }}
is-debug-build:
required: false
type: boolean
default: false
env:
TSC_VERSION: "0.9.0"
jobs:
build:
permissions:
contents: write
runs-on: ${{ inputs.platform }}
steps:
- name: Enable long paths (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" `
-Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force
git config --system core.longpaths true
- name: Checkout repository
uses: actions/checkout@v4
with:
repository: ${{ inputs.repository }}
ref: ${{ inputs.ref }}
fetch-depth: 0
- name: Get version from tauri.conf.json
id: get-version
shell: bash
run: |
VERSION=$(grep -o '"version": "[^"]*"' src-tauri/tauri.conf.json | cut -d'"' -f4)
echo "Application version from tauri.conf.json: $VERSION"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Determine build profile
id: build-profile
shell: bash
run: |
if [[ "${{ inputs.is-debug-build }}" == "true" ]]; then
echo "profile=debug" >> "$GITHUB_OUTPUT"
echo "Build profile: debug"
else
echo "profile=release" >> "$GITHUB_OUTPUT"
echo "Build profile: release"
fi
- name: Setup Bun
uses: oven-sh/setup-bun@v2
- name: install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
# Those targets are only used on macos runners so it's in an `if` to slightly speed up windows and linux builds.
targets: ${{ contains(inputs.platform, 'macos') && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
- name: Rust cache
uses: swatinem/rust-cache@v2
with:
workspaces: "./src-tauri -> target"
key: ${{ inputs.platform }}-${{ inputs.target }}
- name: install dependencies (ubuntu 24.04 x64)
if: contains(inputs.platform, 'ubuntu-24.04') && !contains(inputs.platform, 'arm')
run: |
sudo apt-get update
sudo apt-get install -y libappindicator3-dev librsvg2-dev patchelf libasound2-dev libopenblas-dev libx11-dev libxtst-dev libxrandr-dev libgtk-layer-shell0 libgtk-layer-shell-dev \
libwebkit2gtk-4.1-0=2.44.0-2 \
libwebkit2gtk-4.1-dev=2.44.0-2 \
libjavascriptcoregtk-4.1-0=2.44.0-2 \
libjavascriptcoregtk-4.1-dev=2.44.0-2 \
gir1.2-javascriptcoregtk-4.1=2.44.0-2 \
gir1.2-webkit2-4.1=2.44.0-2
- name: install dependencies (ubuntu 24.04 arm64)
if: contains(inputs.platform, 'ubuntu-24.04-arm')
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf libasound2-dev libopenblas-dev libx11-dev libxtst-dev libxrandr-dev libgtk-layer-shell0 libgtk-layer-shell-dev xdg-utils
- name: install dependencies (ubuntu 22.04)
if: contains(inputs.platform, 'ubuntu-22.04') && !contains(inputs.platform, 'arm')
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf libasound2-dev libopenblas-dev libx11-dev libxtst-dev libxrandr-dev libgtk-layer-shell0 libgtk-layer-shell-dev
- name: Verify gtk-layer-shell runtime dependency (Ubuntu)
if: contains(inputs.platform, 'ubuntu')
run: |
dpkg-query -W -f='${Status}\n' libgtk-layer-shell0 | grep -q "install ok installed"
ldconfig -p | grep -q "libgtk-layer-shell.so.0"
- name: Install Vulkan SDK (Windows x64)
if: contains(inputs.platform, 'windows') && !contains(inputs.target, 'aarch64')
uses: humbletim/install-vulkan-sdk@v1.2
with:
version: 1.4.309.0
cache: true
# humbletim/install-vulkan-sdk@v1.2 cannot target Windows ARM64 yet.
# Download prebuilt binaries (Bin) + build headers/libs from source.
# See https://github.com/humbletim/install-vulkan-sdk/pull/22 for prebuilt ARM64 support progress.
- name: Prepare Vulkan SDK env (Windows ARM64)
if: contains(inputs.platform, 'windows') && contains(inputs.target, 'aarch64')
shell: pwsh
run: |
$sdkDir = Join-Path $env:GITHUB_WORKSPACE "VULKAN_SDK"
New-Item -ItemType Directory -Force -Path $sdkDir | Out-Null
Add-Content -Path $env:GITHUB_ENV -Value "VULKAN_SDK=$sdkDir"
- name: Download Vulkan SDK binaries (Windows ARM64)
if: contains(inputs.platform, 'windows') && contains(inputs.target, 'aarch64')
shell: pwsh
run: |
$url = "https://sdk.lunarg.com/sdk/download/1.4.309.0/warm/InstallVulkanARM64-1.4.309.0.exe"
$outFile = "vulkan_sdk_arm.exe"
Write-Host "Downloading Vulkan SDK binaries from $url"
Invoke-WebRequest -Uri $url -OutFile $outFile
- name: Extract Vulkan SDK binaries (Windows ARM64)
if: contains(inputs.platform, 'windows') && contains(inputs.target, 'aarch64')
shell: pwsh
run: |
$sdkDir = $env:VULKAN_SDK
$sevenZip = (Get-Command 7z.exe -ErrorAction Stop).Source
Write-Host "Extracting binaries to $sdkDir"
& $sevenZip x "./vulkan_sdk_arm.exe" "-o$sdkDir" -aoa
$binPath = Join-Path $sdkDir "Bin"
Add-Content -Path $env:GITHUB_PATH -Value $binPath
Write-Host "Verifying glslc..."
& (Join-Path $binPath "glslc.exe") --version
- name: Build Vulkan SDK headers and libs (Windows ARM64)
if: contains(inputs.platform, 'windows') && contains(inputs.target, 'aarch64')
uses: humbletim/setup-vulkan-sdk@v1.2.1
with:
vulkan-query-version: 1.4.309.0
vulkan-components: Vulkan-Headers, Vulkan-Loader
vulkan-use-cache: true
- name: Cache trusted-signing-cli
if: contains(inputs.platform, 'windows') && inputs.sign-binaries
id: cache-tsc
uses: actions/cache@v4
with:
path: ~/.cargo/bin/trusted-signing-cli*
key: trusted-signing-cli-${{ env.TSC_VERSION }}-${{ runner.os }}-${{ runner.arch }}
- name: Install trusted-signing-cli
if: contains(inputs.platform, 'windows') && inputs.sign-binaries && steps.cache-tsc.outputs.cache-hit != 'true'
run: cargo install trusted-signing-cli@${{ env.TSC_VERSION }}
- name: Prepare Vulkan SDK for Ubuntu 24.04
if: contains(inputs.platform, 'ubuntu-24.04') && !contains(inputs.platform, 'arm')
run: |
wget -qO- https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo tee /etc/apt/trusted.gpg.d/lunarg.asc
sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-1.3.290-noble.list https://packages.lunarg.com/vulkan/1.3.290/lunarg-vulkan-1.3.290-noble.list
sudo apt update
sudo apt install vulkan-sdk -y
sudo apt-get install -y mesa-vulkan-drivers
- name: Prepare Vulkan SDK for Ubuntu ARM64
if: contains(inputs.platform, 'ubuntu') && contains(inputs.platform, 'arm')
uses: jakoch/install-vulkan-sdk-action@v1
with:
vulkan_version: 1.4.335.0
cache: true
- name: Install Vulkan runtime libraries (Ubuntu ARM64)
if: contains(inputs.platform, 'ubuntu') && contains(inputs.platform, 'arm')
run: |
sudo apt-get update
sudo apt-get install -y libvulkan-dev mesa-vulkan-drivers
- name: Prepare Vulkan SDK for Ubuntu 22.04
if: contains(inputs.platform, 'ubuntu-22.04') && !contains(inputs.platform, 'arm')
run: |
wget -qO- https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo tee /etc/apt/trusted.gpg.d/lunarg.asc
sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-1.3.290-jammy.list https://packages.lunarg.com/vulkan/1.3.290/lunarg-vulkan-1.3.290-jammy.list
sudo apt update
sudo apt install vulkan-sdk -y
sudo apt-get install -y mesa-vulkan-drivers
- name: install frontend dependencies
if: ${{ !(contains(inputs.platform, 'windows') && contains(inputs.target, 'aarch64')) }}
run: bun install
- name: install frontend dependencies (Windows ARM64)
if: ${{ contains(inputs.platform, 'windows') && contains(inputs.target, 'aarch64') }}
run: bun install --cpu=arm64
- name: rustup install target
if: ${{ inputs.target != '' && !contains(inputs.target, 'x86_64-unknown-linux-gnu') && !contains(inputs.target, 'aarch64-unknown-linux-gnu') && !contains(inputs.target, 'x86_64-pc-windows-msvc') }}
run: rustup target add ${{ inputs.target }}
- name: import Apple Developer Certificate
if: contains(inputs.platform, 'macos') && inputs.sign-binaries
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
echo $APPLE_CERTIFICATE | base64 --decode > certificate.p12
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security set-keychain-settings -t 3600 -u build.keychain
security import certificate.p12 -k build.keychain -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" build.keychain
security find-identity -v -p codesigning build.keychain
- name: verify certificate
if: contains(inputs.platform, 'macos') && inputs.sign-binaries
run: |
CERT_INFO=$(security find-identity -v -p codesigning build.keychain | grep "Developer ID Application")
CERT_ID=$(echo "$CERT_INFO" | awk -F'"' '{print $2}')
echo "CERT_ID=$CERT_ID" >> $GITHUB_ENV
echo "Certificate imported."
- name: Patch asset name pattern
id: patch-release-name
shell: bash
if: ${{ inputs.release-id != '' && inputs.asset-name-pattern != '' }}
run: |
platform="${{ inputs.platform }}"
replacement="$(echo ${platform} | sed -E 's/-latest//')"
patched_platform=$(echo '${{ inputs.asset-name-pattern }}' | sed -E "s/\[platform\]/${replacement}/")
if [[ -n "${{ inputs.asset-prefix }}" ]]; then
patched_platform="${{ inputs.asset-prefix }}_${patched_platform}"
fi
echo "platform=${patched_platform}" >> $GITHUB_OUTPUT
# whisper-rs-sys cmake builds create paths that exceed Windows MAX_PATH
# (260 chars). Shorten the target dir to keep paths under the limit.
- name: Shorten build path (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
$drive = Split-Path -Qualifier $env:GITHUB_WORKSPACE
$targetDir = "$drive\t"
New-Item -ItemType Directory -Force -Path $targetDir | Out-Null
echo "CARGO_TARGET_DIR=$targetDir" >> $env:GITHUB_ENV
# ggml requires clang for ARM. The VS generator determines the compiler
# via the toolset (-T flag) which can't be overridden from env vars.
# Use Ninja + clang-cl instead, which respects CMAKE_C_COMPILER.
- name: Configure cmake for ARM64 (Windows)
if: contains(inputs.platform, 'windows') && contains(inputs.target, 'aarch64')
shell: pwsh
run: |
echo "CMAKE_GENERATOR=Ninja" >> $env:GITHUB_ENV
echo "CMAKE_C_COMPILER=clang-cl" >> $env:GITHUB_ENV
echo "CMAKE_CXX_COMPILER=clang-cl" >> $env:GITHUB_ENV
echo "CMAKE_C_COMPILER_TARGET=aarch64-pc-windows-msvc" >> $env:GITHUB_ENV
echo "CMAKE_CXX_COMPILER_TARGET=aarch64-pc-windows-msvc" >> $env:GITHUB_ENV
echo "CL=/EHsc" >> $env:GITHUB_ENV
- name: Install ONNX Runtime (x86_64 macOS)
if: inputs.target == 'x86_64-apple-darwin'
shell: bash
run: |
ORT_VERSION="1.24.2"
curl -L -o ort.tgz "https://blob.handy.computer/onnxruntime-osx-x86_64-${ORT_VERSION}.tgz"
tar xzf ort.tgz
ORT_DIR="$(pwd)/onnxruntime-osx-x86_64-${ORT_VERSION}"
echo "ORT_LIB_LOCATION=$ORT_DIR/lib" >> $GITHUB_ENV
echo "ORT_PREFER_DYNAMIC_LINK=1" >> $GITHUB_ENV
# Bundle the versioned dylib (matches the install name @rpath/libonnxruntime.1.24.2.dylib)
jq --arg lib "$ORT_DIR/lib/libonnxruntime.${ORT_VERSION}.dylib" \
'.bundle.macOS.frameworks = [$lib]' \
src-tauri/tauri.conf.json > tmp.json && mv tmp.json src-tauri/tauri.conf.json
- name: Install ONNX Runtime (x86_64 Linux, Ubuntu 22.04)
if: contains(inputs.platform, 'ubuntu-22.04') && inputs.target == 'x86_64-unknown-linux-gnu'
shell: bash
run: |
ORT_VERSION="1.24.2"
curl -L -o ort.tgz "https://blob.handy.computer/onnxruntime-linux-x86_64-${ORT_VERSION}.tgz"
tar xzf ort.tgz
ORT_DIR="$(pwd)/onnxruntime-linux-x86_64-${ORT_VERSION}"
echo "ORT_LIB_LOCATION=$ORT_DIR/lib" >> $GITHUB_ENV
echo "ORT_PREFER_DYNAMIC_LINK=1" >> $GITHUB_ENV
# Resolve symlinks so the deb bundler gets real files (not broken symlinks)
for f in "$ORT_DIR"/lib/libonnxruntime.so*; do
if [ -L "$f" ]; then
cp -L "$f" "$f.real" && mv "$f.real" "$f"
fi
done
# Add the shared libs to the deb package under /usr/lib
# deb.files key = destination in package, value = source on disk
jq --arg so "$ORT_DIR/lib/libonnxruntime.so" \
--arg so1 "$ORT_DIR/lib/libonnxruntime.so.1" \
--arg sov "$ORT_DIR/lib/libonnxruntime.so.1.24.2" \
'.bundle.linux.deb.files["/usr/lib/libonnxruntime.so"] = $so | .bundle.linux.deb.files["/usr/lib/libonnxruntime.so.1"] = $so1 | .bundle.linux.deb.files["/usr/lib/libonnxruntime.so.1.24.2"] = $sov' \
src-tauri/tauri.conf.json > tmp.json && mv tmp.json src-tauri/tauri.conf.json
- name: Build with Tauri
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
APPLE_ID: ${{ inputs.sign-binaries && secrets.APPLE_ID || '' }}
APPLE_ID_PASSWORD: ${{ inputs.sign-binaries && secrets.APPLE_ID_PASSWORD || '' }}
APPLE_PASSWORD: ${{ inputs.sign-binaries && secrets.APPLE_PASSWORD || '' }}
APPLE_TEAM_ID: ${{ inputs.sign-binaries && secrets.APPLE_TEAM_ID || '' }}
APPLE_CERTIFICATE: ${{ inputs.sign-binaries && secrets.APPLE_CERTIFICATE || '' }}
APPLE_CERTIFICATE_PASSWORD: ${{ inputs.sign-binaries && secrets.APPLE_CERTIFICATE_PASSWORD || '' }}
APPLE_SIGNING_IDENTITY: ${{ inputs.sign-binaries && env.CERT_ID || '' }}
AZURE_CLIENT_ID: ${{ inputs.sign-binaries && secrets.AZURE_CLIENT_ID || '' }}
AZURE_CLIENT_SECRET: ${{ inputs.sign-binaries && secrets.AZURE_CLIENT_SECRET || '' }}
AZURE_TENANT_ID: ${{ inputs.sign-binaries && secrets.AZURE_TENANT_ID || '' }}
TAURI_SIGNING_PRIVATE_KEY: ${{ inputs.sign-binaries && secrets.TAURI_SIGNING_PRIVATE_KEY || '' }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ inputs.sign-binaries && secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD || '' }}
WHISPER_NO_AVX: ${{ contains(inputs.platform, 'ubuntu') && !contains(inputs.platform, 'arm') && 'ON' || '' }}
WHISPER_NO_AVX2: ${{ contains(inputs.platform, 'ubuntu') && !contains(inputs.platform, 'arm') && 'ON' || '' }}
with:
tagName: ${{ inputs.release-id && format('v{0}', steps.get-version.outputs.version) || '' }}
releaseName: ${{ inputs.release-id && format('v{0}', steps.get-version.outputs.version) || '' }}
releaseId: ${{ inputs.release-id }}
assetNamePattern: ${{ steps.patch-release-name.outputs.platform }}
args: ${{ inputs.build-args }}
- name: Verify macOS dylib bundling
if: inputs.target == 'x86_64-apple-darwin'
shell: bash
run: |
APP=$(find src-tauri/target -name "*.app" -type d | head -1)
echo "=== Frameworks contents ==="
ls -la "$APP/Contents/Frameworks/" | grep onnx || true
echo "=== Binary linked libs ==="
otool -L "$APP/Contents/MacOS/handy" | grep onnx || true
echo "=== Checking all @rpath deps are satisfied ==="
# Extract every @rpath lib the binary needs
otool -L "$APP/Contents/MacOS/handy" | grep '@rpath/' | awk '{print $1}' | while read dep; do
libname=$(basename "$dep")
if [ ! -f "$APP/Contents/Frameworks/$libname" ]; then
echo "MISSING: $libname not found in Frameworks/"
exit 1
else
echo "OK: $libname"
fi
done
- name: Upload artifacts (macOS)
if: inputs.upload-artifacts && contains(inputs.platform, 'macos')
uses: actions/upload-artifact@v4
with:
name: ${{ inputs.asset-prefix }}-${{ inputs.target }}
path: |
src-tauri/target/${{ inputs.target }}/${{ steps.build-profile.outputs.profile }}/bundle/dmg/*.dmg
src-tauri/target/${{ inputs.target }}/${{ steps.build-profile.outputs.profile }}/bundle/macos/*.app
retention-days: 30
- name: Install FUSE for AppImage processing
if: contains(inputs.platform, 'ubuntu')
run: |
sudo apt-get update
sudo apt-get install -y fuse libfuse2
- name: Remove libwayland-client.so from AppImage
if: contains(inputs.platform, 'ubuntu')
run: |
# Find the AppImage file
APPIMAGE_PATH=$(find src-tauri/target/${{ steps.build-profile.outputs.profile }}/bundle/appimage -name "*.AppImage" | head -1)
if [ -n "$APPIMAGE_PATH" ]; then
echo "Processing AppImage: $APPIMAGE_PATH"
# Make AppImage executable
chmod +x "$APPIMAGE_PATH"
# Extract AppImage
cd "$(dirname "$APPIMAGE_PATH")"
APPIMAGE_NAME=$(basename "$APPIMAGE_PATH")
# Extract using the AppImage itself
"./$APPIMAGE_NAME" --appimage-extract
# Remove libwayland-client.so files
echo "Removing libwayland-client.so files..."
find squashfs-root -name "libwayland-client.so*" -type f -delete
# List what was removed for verification
echo "Files remaining in lib directories:"
find squashfs-root -name "lib*" -type d | head -5 | while read dir; do
echo "Contents of $dir:"
ls "$dir" | grep -E "(wayland|fuse)" || echo " No wayland/fuse libraries found"
done
# Detect architecture and get appropriate appimagetool
if [[ "$(uname -m)" == "aarch64" ]]; then
APPIMAGETOOL_ARCH="aarch64"
else
APPIMAGETOOL_ARCH="x86_64"
fi
wget -q "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-${APPIMAGETOOL_ARCH}.AppImage"
chmod +x "appimagetool-${APPIMAGETOOL_ARCH}.AppImage"
# Repackage AppImage with no-appstream to avoid warnings
ARCH="${APPIMAGETOOL_ARCH}" "./appimagetool-${APPIMAGETOOL_ARCH}.AppImage" --no-appstream squashfs-root "$APPIMAGE_NAME"
# Clean up
rm -rf squashfs-root "appimagetool-${APPIMAGETOOL_ARCH}.AppImage"
echo "libwayland-client.so removed from AppImage successfully"
else
echo "No AppImage found to process"
fi
- name: Upload artifacts (Linux)
if: inputs.upload-artifacts && contains(inputs.platform, 'ubuntu')
uses: actions/upload-artifact@v4
with:
name: ${{ inputs.asset-prefix }}-${{ inputs.platform }}-${{ inputs.target }}
path: |
src-tauri/target/${{ steps.build-profile.outputs.profile }}/bundle/deb/*.deb
src-tauri/target/${{ steps.build-profile.outputs.profile }}/bundle/appimage/*.AppImage
src-tauri/target/${{ steps.build-profile.outputs.profile }}/bundle/rpm/*.rpm
retention-days: 30
- name: Resolve Windows artifact path
if: inputs.upload-artifacts && contains(inputs.platform, 'windows')
id: win-artifact-path
shell: pwsh
run: |
$base = if ($env:CARGO_TARGET_DIR) { $env:CARGO_TARGET_DIR } else { "src-tauri/target" }
echo "base=$base" >> $env:GITHUB_OUTPUT
- name: Upload artifacts (Windows)
if: inputs.upload-artifacts && contains(inputs.platform, 'windows')
uses: actions/upload-artifact@v4
with:
name: ${{ inputs.asset-prefix }}-${{ inputs.target }}
path: |
${{ steps.win-artifact-path.outputs.base }}/${{ inputs.target != '' && inputs.target != 'x86_64-pc-windows-msvc' && format('{0}/{1}', inputs.target, steps.build-profile.outputs.profile) || steps.build-profile.outputs.profile }}/bundle/msi/*.msi
${{ steps.win-artifact-path.outputs.base }}/${{ inputs.target != '' && inputs.target != 'x86_64-pc-windows-msvc' && format('{0}/{1}', inputs.target, steps.build-profile.outputs.profile) || steps.build-profile.outputs.profile }}/bundle/nsis/*.exe
retention-days: 30
================================================
FILE: .github/workflows/code-quality.yml
================================================
name: "code quality"
on:
workflow_dispatch:
push:
branches: [main]
paths:
- "src/**"
- "package.json"
- "bun.lock"
- ".eslintrc*"
- "eslint.config.*"
- ".prettierrc*"
- "tsconfig*"
- "tailwind.config.*"
- ".github/workflows/**"
pull_request:
paths:
- "src/**"
- "package.json"
- "bun.lock"
- ".eslintrc*"
- "eslint.config.*"
- ".prettierrc*"
- "tsconfig*"
- "tailwind.config.*"
- ".github/workflows/**"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
code-quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Check translation consistency
run: bun run check:translations
- name: Run ESLint
run: bun run lint
- name: Run prettier
run: bun run format:check
================================================
FILE: .github/workflows/main-build.yml
================================================
name: "Main Branch Build"
# Runs the full cross-platform build on every push to main so breakage is
# caught before a manual release is triggered. Artifacts are kept for 30 days
# so any commit on main has a downloadable, testable build.
on:
push:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
permissions:
contents: write
strategy:
fail-fast: false
matrix:
include:
- platform: "macos-26"
args: "--target aarch64-apple-darwin"
target: "aarch64-apple-darwin"
- platform: "macos-latest"
args: "--target x86_64-apple-darwin"
target: "x86_64-apple-darwin"
- platform: "ubuntu-22.04"
args: "--bundles deb"
target: "x86_64-unknown-linux-gnu"
- platform: "ubuntu-24.04"
args: "--bundles appimage,rpm"
target: "x86_64-unknown-linux-gnu"
- platform: "ubuntu-24.04-arm"
args: "--bundles appimage,deb,rpm"
target: "aarch64-unknown-linux-gnu"
- platform: "windows-latest"
args: ""
target: "x86_64-pc-windows-msvc"
- platform: "windows-11-arm"
args: "--target aarch64-pc-windows-msvc"
target: "aarch64-pc-windows-msvc"
uses: ./.github/workflows/build.yml
with:
platform: ${{ matrix.platform }}
target: ${{ matrix.target }}
build-args: ${{ matrix.args }}
upload-artifacts: true
sign-binaries: true
secrets: inherit
================================================
FILE: .github/workflows/nix-check.yml
================================================
# Nix CI — two tiers:
#
# 1. Quick checks (bun.nix sync, flake eval) run on ANY source change
# so compilation-breaking edits are caught by flake eval.
# 2. Full nix build (~25 min) only runs when nix packaging files change.
#
# Setting up a Cachix binary cache would further reduce full-build times.
name: "nix build check"
on:
workflow_dispatch:
push:
branches: [main]
paths:
- "flake.nix"
- "flake.lock"
- ".nix/**"
- "bun.lock"
- "src-tauri/**"
- "src/**"
- ".github/workflows/**"
pull_request:
paths:
- "flake.nix"
- "flake.lock"
- ".nix/**"
- "bun.lock"
- "src-tauri/**"
- "src/**"
- ".github/workflows/**"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
nix-build:
runs-on: ubuntu-24.04
continue-on-error: false
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: DeterminateSystems/magic-nix-cache-action@565684385bcd71bad329742eefe8d12f2e765b39 # v13
# Regenerate .nix/bun.nix from bun.lock and check if it matches
# what's committed. A diff means the developer forgot to run
# bun scripts/check-nix-deps.ts or bun install (which triggers it).
- name: Check bun.nix is up to date
id: bun-check
run: |
bunx bun2nix -o .nix/bun.nix
if ! git diff --quiet .nix/bun.nix; then
echo "outdated=true" >> "$GITHUB_OUTPUT"
fi
- name: Hint on outdated bun.nix
if: steps.bun-check.outputs.outdated == 'true'
run: |
echo ""
echo "::warning::.nix/bun.nix is out of sync with bun.lock"
echo ""
echo "┌──────────────────────────────────────────────────────────────┐"
echo "│ .nix/bun.nix is outdated. To fix, run: │"
echo "│ │"
echo "│ bun scripts/check-nix-deps.ts │"
echo "│ │"
echo "│ Or simply run 'bun install' — the postinstall hook will │"
echo "│ regenerate it automatically. Commit the resulting changes. │"
echo "└──────────────────────────────────────────────────────────────┘"
echo ""
echo "Diff:"
git diff .nix/bun.nix
exit 1
# Evaluate the flake to catch issues with cargo git dependency hashes,
# missing inputs, or other Nix expression errors.
# Skip if bun.nix is already outdated — nix eval would fail with a
# cryptic error, and the bun-check step already printed a clear message.
- name: Check flake evaluation
if: steps.bun-check.outputs.outdated != 'true'
id: eval
run: |
if ! nix eval .#packages.x86_64-linux.handy.drvPath 2>eval_err.log; then
echo "failed=true" >> "$GITHUB_OUTPUT"
cat eval_err.log
fi
- name: Hint on evaluation failure
if: steps.eval.outputs.failed == 'true'
run: |
echo ""
echo "::warning::flake.nix evaluation failed"
echo ""
cat eval_err.log
exit 1
# Detect whether nix packaging files changed — if only source code
# changed, the quick checks above are sufficient.
# Skipped on workflow_dispatch (no base ref) — full build runs instead.
- name: Check if nix files changed
if: github.event_name == 'pull_request'
id: nix-files
run: |
git fetch origin ${{ github.base_ref }} --depth=1
if git diff --name-only origin/${{ github.base_ref }}...HEAD | grep -qE '^(flake\.(nix|lock)|\.nix/|bun\.lock|src-tauri/(Cargo\.(toml|lock)|tauri\.conf\.json|build\.rs))'; then
echo "changed=true" >> "$GITHUB_OUTPUT"
fi
# Full build — catches runtime build errors (broken dependencies,
# sandbox issues, compilation failures) that flake eval alone misses.
# On PRs: only runs when nix packaging files change (~25 min with cold cache).
# On push to main and workflow_dispatch: always runs so every commit on
# main has a verified nix build before release.
- name: Build handy
if: steps.bun-check.outputs.outdated != 'true' && steps.eval.outputs.failed != 'true' && (steps.nix-files.outputs.changed == 'true' || github.event_name == 'workflow_dispatch' || github.event_name == 'push')
run: nix build .#handy -L --show-trace
================================================
FILE: .github/workflows/playwright.yml
================================================
name: "Playwright"
on:
workflow_dispatch:
pull_request:
paths:
- "src/**"
- "package.json"
- "bun.lock"
- "playwright.config.*"
- "tests/**"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
playwright:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Install Playwright browsers
run: bunx playwright install chromium
- name: Run Playwright tests
run: bun run test:playwright
- name: Upload test results
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
retention-days: 7
================================================
FILE: .github/workflows/pr-test-build.yml
================================================
name: "PR Test Build"
on:
workflow_dispatch:
inputs:
pr_number:
description: "PR number to build"
required: true
type: string
jobs:
build-test:
permissions:
contents: write
strategy:
fail-fast: false
matrix:
include:
- platform: "macos-26"
args: "--target aarch64-apple-darwin"
target: "aarch64-apple-darwin"
- platform: "macos-latest"
args: "--target x86_64-apple-darwin"
target: "x86_64-apple-darwin"
- platform: "ubuntu-22.04"
args: "--bundles deb"
target: "x86_64-unknown-linux-gnu"
- platform: "ubuntu-24.04"
args: "--bundles appimage,rpm"
target: "x86_64-unknown-linux-gnu"
- platform: "ubuntu-24.04-arm" # Build for ARM64 Linux
args: "--bundles appimage,deb,rpm"
target: "aarch64-unknown-linux-gnu"
- platform: "windows-latest"
args: ""
target: "x86_64-pc-windows-msvc"
- platform: "windows-11-arm"
args: "--target aarch64-pc-windows-msvc"
target: "aarch64-pc-windows-msvc"
uses: ./.github/workflows/build.yml
with:
platform: ${{ matrix.platform }}
target: ${{ matrix.target }}
build-args: ${{ matrix.args }}
sign-binaries: true
asset-prefix: "handy-pr-${{ inputs.pr_number }}"
upload-artifacts: true
is-debug-build: ${{ contains(matrix.args, '--debug') }}
ref: ${{ format('refs/pull/{0}/merge', inputs.pr_number) }}
secrets: inherit
comment-on-pr:
needs: build-test
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Post artifact links to PR
uses: actions/github-script@v7
with:
script: |
const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: ${{ inputs.pr_number }},
body: `## 🧪 Test Build Ready\n\nBuild artifacts for PR #${{ inputs.pr_number }} are available for testing.\n\n**[Download artifacts from workflow run](${runUrl})**\n\nArtifacts expire after 30 days.`
});
================================================
FILE: .github/workflows/release.yml
================================================
name: "Release"
on: workflow_dispatch
jobs:
create-release:
permissions:
contents: write
runs-on: ubuntu-latest
outputs:
release-id: ${{ steps.create-release.outputs.result }}
version: ${{ steps.get-version.outputs.version }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Get version from tauri.conf.json
id: get-version
shell: bash
run: |
VERSION=$(grep -o '"version": "[^"]*"' src-tauri/tauri.conf.json | cut -d'"' -f4)
echo "Application version from tauri.conf.json: $VERSION"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Create Draft Release
id: create-release
uses: actions/github-script@v7
with:
script: |
const { data } = await github.rest.repos.createRelease({
owner: context.repo.owner,
repo: context.repo.repo,
tag_name: `v${{ steps.get-version.outputs.version }}`,
name: `v${{ steps.get-version.outputs.version }}`,
draft: true,
prerelease: false,
generate_release_notes: true
})
return data.id
publish-tauri:
permissions:
contents: write
needs: create-release
strategy:
fail-fast: false
matrix:
include:
- platform: "macos-26" # for Arm based macs (M1 and above). Uses macOS 26 for Apple Intelligence SDK.
args: "--target aarch64-apple-darwin"
target: "aarch64-apple-darwin"
- platform: "macos-latest" # for Intel based macs.
args: "--target x86_64-apple-darwin"
target: "x86_64-apple-darwin"
- platform: "ubuntu-22.04" # Build .deb on 22.04
args: "--bundles deb"
target: "x86_64-unknown-linux-gnu"
- platform: "ubuntu-24.04" # Build AppImage and RPM on 24.04
args: "--bundles appimage,rpm"
target: "x86_64-unknown-linux-gnu"
- platform: "ubuntu-24.04-arm" # Build for ARM64 Linux
args: "--bundles appimage,deb,rpm"
target: "aarch64-unknown-linux-gnu"
- platform: "windows-latest"
args: ""
target: "x86_64-pc-windows-msvc"
- platform: "windows-11-arm" # for ARM64 Windows runner
args: "--target aarch64-pc-windows-msvc"
target: "aarch64-pc-windows-msvc"
uses: ./.github/workflows/build.yml
with:
platform: ${{ matrix.platform }}
target: ${{ matrix.target }}
build-args: ${{ matrix.args }}
sign-binaries: true
asset-prefix: "handy"
upload-artifacts: false
release-id: ${{ needs.create-release.outputs.release-id }}
secrets: inherit
================================================
FILE: .github/workflows/test.yml
================================================
name: "test"
on:
workflow_dispatch:
push:
branches: [main]
paths:
- "src-tauri/**"
pull_request:
paths:
- "src-tauri/**"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
rust-tests:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev libasound2-dev libssl-dev libgtk-layer-shell-dev
- uses: swatinem/rust-cache@v2
with:
workspaces: "./src-tauri -> target"
- name: Use mock TranscriptionManager (CI only)
working-directory: src-tauri
run: |
# Swap to mock adapter - avoids compiling whisper/Vulkan
cp src/managers/transcription_mock.rs src/managers/transcription.rs
sed -i '/^transcribe-rs/d' Cargo.toml
- name: Run Rust tests
working-directory: src-tauri
run: cargo test
================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
package-lock.json
node_modules
dist
dist-ssr
*.local
*.local.*
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
/target/
recording_*
.crush/
# Playwright
playwright-report/
test-results/
blob-report/
.direnv
.envrc
# Nix build output
result
================================================
FILE: .nix/bun-lock-hash
================================================
e00b12c719a762004194cec01f2ad0b78ae483c41452bcca8537179d28e704b1
================================================
FILE: .nix/bun.nix
================================================
# Autogenerated by `bun2nix`, editing manually is not recommended
#
# Set of Bun packages to install
#
# Consume this with `fetchBunDeps` (recommended)
# or `pkgs.callPackage` if you wish to handle
# it manually.
{
copyPathToStore,
fetchFromGitHub,
fetchgit,
fetchurl,
...
}:
{
"@babel/code-frame@7.27.1" = fetchurl {
url = "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz";
hash = "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==";
};
"@babel/compat-data@7.28.5" = fetchurl {
url = "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz";
hash = "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==";
};
"@babel/core@7.28.5" = fetchurl {
url = "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz";
hash = "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==";
};
"@babel/generator@7.28.5" = fetchurl {
url = "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz";
hash = "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==";
};
"@babel/helper-compilation-targets@7.27.2" = fetchurl {
url = "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz";
hash = "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==";
};
"@babel/helper-globals@7.28.0" = fetchurl {
url = "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz";
hash = "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==";
};
"@babel/helper-module-imports@7.27.1" = fetchurl {
url = "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz";
hash = "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==";
};
"@babel/helper-module-transforms@7.28.3" = fetchurl {
url = "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz";
hash = "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==";
};
"@babel/helper-plugin-utils@7.27.1" = fetchurl {
url = "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz";
hash = "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==";
};
"@babel/helper-string-parser@7.27.1" = fetchurl {
url = "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz";
hash = "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==";
};
"@babel/helper-validator-identifier@7.28.5" = fetchurl {
url = "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz";
hash = "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==";
};
"@babel/helper-validator-option@7.27.1" = fetchurl {
url = "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz";
hash = "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==";
};
"@babel/helpers@7.28.4" = fetchurl {
url = "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz";
hash = "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==";
};
"@babel/parser@7.28.5" = fetchurl {
url = "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz";
hash = "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==";
};
"@babel/plugin-transform-react-jsx-self@7.27.1" = fetchurl {
url = "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz";
hash = "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==";
};
"@babel/plugin-transform-react-jsx-source@7.27.1" = fetchurl {
url = "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz";
hash = "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==";
};
"@babel/runtime@7.28.4" = fetchurl {
url = "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz";
hash = "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==";
};
"@babel/template@7.27.2" = fetchurl {
url = "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz";
hash = "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==";
};
"@babel/traverse@7.28.5" = fetchurl {
url = "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz";
hash = "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==";
};
"@babel/types@7.28.5" = fetchurl {
url = "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz";
hash = "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==";
};
"@emnapi/core@1.6.0" = fetchurl {
url = "https://registry.npmjs.org/@emnapi/core/-/core-1.6.0.tgz";
hash = "sha512-zq/ay+9fNIJJtJiZxdTnXS20PllcYMX3OE23ESc4HK/bdYu3cOWYVhsOhVnXALfU/uqJIxn5NBPd9z4v+SfoSg==";
};
"@emnapi/runtime@1.6.0" = fetchurl {
url = "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.6.0.tgz";
hash = "sha512-obtUmAHTMjll499P+D9A3axeJFlhdjOWdKUNs/U6QIGT7V5RjcUW1xToAzjvmgTSQhDbYn/NwfTRoJcQ2rNBxA==";
};
"@emnapi/wasi-threads@1.1.0" = fetchurl {
url = "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz";
hash = "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==";
};
"@emotion/babel-plugin@11.13.5" = fetchurl {
url = "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz";
hash = "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==";
};
"@emotion/cache@11.14.0" = fetchurl {
url = "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz";
hash = "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==";
};
"@emotion/hash@0.9.2" = fetchurl {
url = "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz";
hash = "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==";
};
"@emotion/memoize@0.9.0" = fetchurl {
url = "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz";
hash = "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==";
};
"@emotion/react@11.14.0" = fetchurl {
url = "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz";
hash = "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==";
};
"@emotion/serialize@1.3.3" = fetchurl {
url = "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz";
hash = "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==";
};
"@emotion/sheet@1.4.0" = fetchurl {
url = "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz";
hash = "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==";
};
"@emotion/unitless@0.10.0" = fetchurl {
url = "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz";
hash = "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==";
};
"@emotion/use-insertion-effect-with-fallbacks@1.2.0" = fetchurl {
url = "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz";
hash = "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==";
};
"@emotion/utils@1.4.2" = fetchurl {
url = "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz";
hash = "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==";
};
"@emotion/weak-memoize@0.4.0" = fetchurl {
url = "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz";
hash = "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==";
};
"@esbuild/aix-ppc64@0.25.11" = fetchurl {
url = "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz";
hash = "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==";
};
"@esbuild/android-arm64@0.25.11" = fetchurl {
url = "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.11.tgz";
hash = "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==";
};
"@esbuild/android-arm@0.25.11" = fetchurl {
url = "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.11.tgz";
hash = "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==";
};
"@esbuild/android-x64@0.25.11" = fetchurl {
url = "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.11.tgz";
hash = "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==";
};
"@esbuild/darwin-arm64@0.25.11" = fetchurl {
url = "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.11.tgz";
hash = "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==";
};
"@esbuild/darwin-x64@0.25.11" = fetchurl {
url = "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.11.tgz";
hash = "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==";
};
"@esbuild/freebsd-arm64@0.25.11" = fetchurl {
url = "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.11.tgz";
hash = "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==";
};
"@esbuild/freebsd-x64@0.25.11" = fetchurl {
url = "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.11.tgz";
hash = "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==";
};
"@esbuild/linux-arm64@0.25.11" = fetchurl {
url = "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.11.tgz";
hash = "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==";
};
"@esbuild/linux-arm@0.25.11" = fetchurl {
url = "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.11.tgz";
hash = "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==";
};
"@esbuild/linux-ia32@0.25.11" = fetchurl {
url = "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.11.tgz";
hash = "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==";
};
"@esbuild/linux-loong64@0.25.11" = fetchurl {
url = "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.11.tgz";
hash = "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==";
};
"@esbuild/linux-mips64el@0.25.11" = fetchurl {
url = "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.11.tgz";
hash = "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==";
};
"@esbuild/linux-ppc64@0.25.11" = fetchurl {
url = "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.11.tgz";
hash = "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==";
};
"@esbuild/linux-riscv64@0.25.11" = fetchurl {
url = "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.11.tgz";
hash = "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==";
};
"@esbuild/linux-s390x@0.25.11" = fetchurl {
url = "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.11.tgz";
hash = "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==";
};
"@esbuild/linux-x64@0.25.11" = fetchurl {
url = "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.11.tgz";
hash = "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==";
};
"@esbuild/netbsd-arm64@0.25.11" = fetchurl {
url = "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.11.tgz";
hash = "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==";
};
"@esbuild/netbsd-x64@0.25.11" = fetchurl {
url = "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.11.tgz";
hash = "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==";
};
"@esbuild/openbsd-arm64@0.25.11" = fetchurl {
url = "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.11.tgz";
hash = "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==";
};
"@esbuild/openbsd-x64@0.25.11" = fetchurl {
url = "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.11.tgz";
hash = "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==";
};
"@esbuild/openharmony-arm64@0.25.11" = fetchurl {
url = "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.11.tgz";
hash = "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==";
};
"@esbuild/sunos-x64@0.25.11" = fetchurl {
url = "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.11.tgz";
hash = "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==";
};
"@esbuild/win32-arm64@0.25.11" = fetchurl {
url = "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.11.tgz";
hash = "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==";
};
"@esbuild/win32-ia32@0.25.11" = fetchurl {
url = "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.11.tgz";
hash = "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==";
};
"@esbuild/win32-x64@0.25.11" = fetchurl {
url = "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.11.tgz";
hash = "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==";
};
"@eslint-community/eslint-utils@4.9.0" = fetchurl {
url = "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz";
hash = "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==";
};
"@eslint-community/regexpp@4.12.2" = fetchurl {
url = "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz";
hash = "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==";
};
"@eslint/config-array@0.21.1" = fetchurl {
url = "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz";
hash = "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==";
};
"@eslint/config-helpers@0.4.2" = fetchurl {
url = "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz";
hash = "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==";
};
"@eslint/core@0.17.0" = fetchurl {
url = "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz";
hash = "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==";
};
"@eslint/eslintrc@3.3.3" = fetchurl {
url = "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz";
hash = "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==";
};
"@eslint/js@9.39.1" = fetchurl {
url = "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz";
hash = "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==";
};
"@eslint/object-schema@2.1.7" = fetchurl {
url = "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz";
hash = "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==";
};
"@eslint/plugin-kit@0.4.1" = fetchurl {
url = "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz";
hash = "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==";
};
"@floating-ui/core@1.7.3" = fetchurl {
url = "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz";
hash = "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==";
};
"@floating-ui/dom@1.7.4" = fetchurl {
url = "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz";
hash = "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==";
};
"@floating-ui/utils@0.2.10" = fetchurl {
url = "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz";
hash = "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==";
};
"@humanfs/core@0.19.1" = fetchurl {
url = "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz";
hash = "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==";
};
"@humanfs/node@0.16.7" = fetchurl {
url = "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz";
hash = "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==";
};
"@humanwhocodes/module-importer@1.0.1" = fetchurl {
url = "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz";
hash = "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==";
};
"@humanwhocodes/retry@0.4.3" = fetchurl {
url = "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz";
hash = "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==";
};
"@jridgewell/gen-mapping@0.3.13" = fetchurl {
url = "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz";
hash = "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==";
};
"@jridgewell/remapping@2.3.5" = fetchurl {
url = "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz";
hash = "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==";
};
"@jridgewell/resolve-uri@3.1.2" = fetchurl {
url = "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz";
hash = "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==";
};
"@jridgewell/sourcemap-codec@1.5.5" = fetchurl {
url = "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz";
hash = "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==";
};
"@jridgewell/trace-mapping@0.3.31" = fetchurl {
url = "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz";
hash = "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==";
};
"@napi-rs/wasm-runtime@1.0.7" = fetchurl {
url = "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.7.tgz";
hash = "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==";
};
"@playwright/test@1.58.0" = fetchurl {
url = "https://registry.npmjs.org/@playwright/test/-/test-1.58.0.tgz";
hash = "sha512-fWza+Lpbj6SkQKCrU6si4iu+fD2dD3gxNHFhUPxsfXBPhnv3rRSQVd0NtBUT9Z/RhF/boCBcuUaMUSTRTopjZg==";
};
"@rolldown/pluginutils@1.0.0-beta.27" = fetchurl {
url = "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz";
hash = "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==";
};
"@rollup/rollup-android-arm-eabi@4.52.5" = fetchurl {
url = "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.5.tgz";
hash = "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==";
};
"@rollup/rollup-android-arm64@4.52.5" = fetchurl {
url = "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.5.tgz";
hash = "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==";
};
"@rollup/rollup-darwin-arm64@4.52.5" = fetchurl {
url = "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.5.tgz";
hash = "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==";
};
"@rollup/rollup-darwin-x64@4.52.5" = fetchurl {
url = "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.5.tgz";
hash = "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==";
};
"@rollup/rollup-freebsd-arm64@4.52.5" = fetchurl {
url = "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.5.tgz";
hash = "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==";
};
"@rollup/rollup-freebsd-x64@4.52.5" = fetchurl {
url = "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.5.tgz";
hash = "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==";
};
"@rollup/rollup-linux-arm-gnueabihf@4.52.5" = fetchurl {
url = "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.5.tgz";
hash = "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==";
};
"@rollup/rollup-linux-arm-musleabihf@4.52.5" = fetchurl {
url = "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.5.tgz";
hash = "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==";
};
"@rollup/rollup-linux-arm64-gnu@4.52.5" = fetchurl {
url = "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.5.tgz";
hash = "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==";
};
"@rollup/rollup-linux-arm64-musl@4.52.5" = fetchurl {
url = "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.5.tgz";
hash = "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==";
};
"@rollup/rollup-linux-loong64-gnu@4.52.5" = fetchurl {
url = "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.5.tgz";
hash = "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==";
};
"@rollup/rollup-linux-ppc64-gnu@4.52.5" = fetchurl {
url = "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.5.tgz";
hash = "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==";
};
"@rollup/rollup-linux-riscv64-gnu@4.52.5" = fetchurl {
url = "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.5.tgz";
hash = "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==";
};
"@rollup/rollup-linux-riscv64-musl@4.52.5" = fetchurl {
url = "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.5.tgz";
hash = "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==";
};
"@rollup/rollup-linux-s390x-gnu@4.52.5" = fetchurl {
url = "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.5.tgz";
hash = "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==";
};
"@rollup/rollup-linux-x64-gnu@4.52.5" = fetchurl {
url = "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.5.tgz";
hash = "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==";
};
"@rollup/rollup-linux-x64-musl@4.52.5" = fetchurl {
url = "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.5.tgz";
hash = "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==";
};
"@rollup/rollup-openharmony-arm64@4.52.5" = fetchurl {
url = "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.5.tgz";
hash = "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==";
};
"@rollup/rollup-win32-arm64-msvc@4.52.5" = fetchurl {
url = "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.5.tgz";
hash = "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==";
};
"@rollup/rollup-win32-ia32-msvc@4.52.5" = fetchurl {
url = "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.5.tgz";
hash = "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==";
};
"@rollup/rollup-win32-x64-gnu@4.52.5" = fetchurl {
url = "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.5.tgz";
hash = "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==";
};
"@rollup/rollup-win32-x64-msvc@4.52.5" = fetchurl {
url = "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.5.tgz";
hash = "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==";
};
"@tailwindcss/node@4.1.16" = fetchurl {
url = "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.16.tgz";
hash = "sha512-BX5iaSsloNuvKNHRN3k2RcCuTEgASTo77mofW0vmeHkfrDWaoFAFvNHpEgtu0eqyypcyiBkDWzSMxJhp3AUVcw==";
};
"@tailwindcss/oxide-android-arm64@4.1.16" = fetchurl {
url = "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.16.tgz";
hash = "sha512-8+ctzkjHgwDJ5caq9IqRSgsP70xhdhJvm+oueS/yhD5ixLhqTw9fSL1OurzMUhBwE5zK26FXLCz2f/RtkISqHA==";
};
"@tailwindcss/oxide-darwin-arm64@4.1.16" = fetchurl {
url = "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.16.tgz";
hash = "sha512-C3oZy5042v2FOALBZtY0JTDnGNdS6w7DxL/odvSny17ORUnaRKhyTse8xYi3yKGyfnTUOdavRCdmc8QqJYwFKA==";
};
"@tailwindcss/oxide-darwin-x64@4.1.16" = fetchurl {
url = "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.16.tgz";
hash = "sha512-vjrl/1Ub9+JwU6BP0emgipGjowzYZMjbWCDqwA2Z4vCa+HBSpP4v6U2ddejcHsolsYxwL5r4bPNoamlV0xDdLg==";
};
"@tailwindcss/oxide-freebsd-x64@4.1.16" = fetchurl {
url = "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.16.tgz";
hash = "sha512-TSMpPYpQLm+aR1wW5rKuUuEruc/oOX3C7H0BTnPDn7W/eMw8W+MRMpiypKMkXZfwH8wqPIRKppuZoedTtNj2tg==";
};
"@tailwindcss/oxide-linux-arm-gnueabihf@4.1.16" = fetchurl {
url = "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.16.tgz";
hash = "sha512-p0GGfRg/w0sdsFKBjMYvvKIiKy/LNWLWgV/plR4lUgrsxFAoQBFrXkZ4C0w8IOXfslB9vHK/JGASWD2IefIpvw==";
};
"@tailwindcss/oxide-linux-arm64-gnu@4.1.16" = fetchurl {
url = "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.16.tgz";
hash = "sha512-DoixyMmTNO19rwRPdqviTrG1rYzpxgyYJl8RgQvdAQUzxC1ToLRqtNJpU/ATURSKgIg6uerPw2feW0aS8SNr/w==";
};
"@tailwindcss/oxide-linux-arm64-musl@4.1.16" = fetchurl {
url = "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.16.tgz";
hash = "sha512-H81UXMa9hJhWhaAUca6bU2wm5RRFpuHImrwXBUvPbYb+3jo32I9VIwpOX6hms0fPmA6f2pGVlybO6qU8pF4fzQ==";
};
"@tailwindcss/oxide-linux-x64-gnu@4.1.16" = fetchurl {
url = "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.16.tgz";
hash = "sha512-ZGHQxDtFC2/ruo7t99Qo2TTIvOERULPl5l0K1g0oK6b5PGqjYMga+FcY1wIUnrUxY56h28FxybtDEla+ICOyew==";
};
"@tailwindcss/oxide-linux-x64-musl@4.1.16" = fetchurl {
url = "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.16.tgz";
hash = "sha512-Oi1tAaa0rcKf1Og9MzKeINZzMLPbhxvm7rno5/zuP1WYmpiG0bEHq4AcRUiG2165/WUzvxkW4XDYCscZWbTLZw==";
};
"@tailwindcss/oxide-wasm32-wasi@4.1.16" = fetchurl {
url = "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.16.tgz";
hash = "sha512-B01u/b8LteGRwucIBmCQ07FVXLzImWESAIMcUU6nvFt/tYsQ6IHz8DmZ5KtvmwxD+iTYBtM1xwoGXswnlu9v0Q==";
};
"@tailwindcss/oxide-win32-arm64-msvc@4.1.16" = fetchurl {
url = "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.16.tgz";
hash = "sha512-zX+Q8sSkGj6HKRTMJXuPvOcP8XfYON24zJBRPlszcH1Np7xuHXhWn8qfFjIujVzvH3BHU+16jBXwgpl20i+v9A==";
};
"@tailwindcss/oxide-win32-x64-msvc@4.1.16" = fetchurl {
url = "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.16.tgz";
hash = "sha512-m5dDFJUEejbFqP+UXVstd4W/wnxA4F61q8SoL+mqTypId2T2ZpuxosNSgowiCnLp2+Z+rivdU0AqpfgiD7yCBg==";
};
"@tailwindcss/oxide@4.1.16" = fetchurl {
url = "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.16.tgz";
hash = "sha512-2OSv52FRuhdlgyOQqgtQHuCgXnS8nFSYRp2tJ+4WZXKgTxqPy7SMSls8c3mPT5pkZ17SBToGM5LHEJBO7miEdg==";
};
"@tailwindcss/vite@4.1.16" = fetchurl {
url = "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.16.tgz";
hash = "sha512-bbguNBcDxsRmi9nnlWJxhfDWamY3lmcyACHcdO1crxfzuLpOhHLLtEIN/nCbbAtj5rchUgQD17QVAKi1f7IsKg==";
};
"@tauri-apps/api@2.10.1" = fetchurl {
url = "https://registry.npmjs.org/@tauri-apps/api/-/api-2.10.1.tgz";
hash = "sha512-hKL/jWf293UDSUN09rR69hrToyIXBb8CjGaWC7gfinvnQrBVvnLr08FeFi38gxtugAVyVcTa5/FD/Xnkb1siBw==";
};
"@tauri-apps/api@2.9.0" = fetchurl {
url = "https://registry.npmjs.org/@tauri-apps/api/-/api-2.9.0.tgz";
hash = "sha512-qD5tMjh7utwBk9/5PrTA/aGr3i5QaJ/Mlt7p8NilQ45WgbifUNPyKWsA63iQ8YfQq6R8ajMapU+/Q8nMcPRLNw==";
};
"@tauri-apps/cli-darwin-arm64@2.10.0" = fetchurl {
url = "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.10.0.tgz";
hash = "sha512-avqHD4HRjrMamE/7R/kzJPcAJnZs0IIS+1nkDP5b+TNBn3py7N2aIo9LIpy+VQq0AkN8G5dDpZtOOBkmWt/zjA==";
};
"@tauri-apps/cli-darwin-x64@2.10.0" = fetchurl {
url = "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.10.0.tgz";
hash = "sha512-keDmlvJRStzVFjZTd0xYkBONLtgBC9eMTpmXnBXzsHuawV2q9PvDo2x6D5mhuoMVrJ9QWjgaPKBBCFks4dK71Q==";
};
"@tauri-apps/cli-linux-arm-gnueabihf@2.10.0" = fetchurl {
url = "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.10.0.tgz";
hash = "sha512-e5u0VfLZsMAC9iHaOEANumgl6lfnJx0Dtjkd8IJpysZ8jp0tJ6wrIkto2OzQgzcYyRCKgX72aKE0PFgZputA8g==";
};
"@tauri-apps/cli-linux-arm64-gnu@2.10.0" = fetchurl {
url = "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.10.0.tgz";
hash = "sha512-YrYYk2dfmBs5m+OIMCrb+JH/oo+4FtlpcrTCgiFYc7vcs6m3QDd1TTyWu0u01ewsCtK2kOdluhr/zKku+KP7HA==";
};
"@tauri-apps/cli-linux-arm64-musl@2.10.0" = fetchurl {
url = "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.10.0.tgz";
hash = "sha512-GUoPdVJmrJRIXFfW3Rkt+eGK9ygOdyISACZfC/bCSfOnGt8kNdQIQr5WRH9QUaTVFIwxMlQyV3m+yXYP+xhSVA==";
};
"@tauri-apps/cli-linux-riscv64-gnu@2.10.0" = fetchurl {
url = "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.10.0.tgz";
hash = "sha512-JO7s3TlSxshwsoKNCDkyvsx5gw2QAs/Y2GbR5UE2d5kkU138ATKoPOtxn8G1fFT1aDW4LH0rYAAfBpGkDyJJnw==";
};
"@tauri-apps/cli-linux-x64-gnu@2.10.0" = fetchurl {
url = "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.10.0.tgz";
hash = "sha512-Uvh4SUUp4A6DVRSMWjelww0GnZI3PlVy7VS+DRF5napKuIehVjGl9XD0uKoCoxwAQBLctvipyEK+pDXpJeoHng==";
};
"@tauri-apps/cli-linux-x64-musl@2.10.0" = fetchurl {
url = "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.10.0.tgz";
hash = "sha512-AP0KRK6bJuTpQ8kMNWvhIpKUkQJfcPFeba7QshOQZjJ8wOS6emwTN4K5g/d3AbCMo0RRdnZWwu67MlmtJyxC1Q==";
};
"@tauri-apps/cli-win32-arm64-msvc@2.10.0" = fetchurl {
url = "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.10.0.tgz";
hash = "sha512-97DXVU3dJystrq7W41IX+82JEorLNY+3+ECYxvXWqkq7DBN6FsA08x/EFGE8N/b0LTOui9X2dvpGGoeZKKV08g==";
};
"@tauri-apps/cli-win32-ia32-msvc@2.10.0" = fetchurl {
url = "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.10.0.tgz";
hash = "sha512-EHyQ1iwrWy1CwMalEm9z2a6L5isQ121pe7FcA2xe4VWMJp+GHSDDGvbTv/OPdkt2Lyr7DAZBpZHM6nvlHXEc4A==";
};
"@tauri-apps/cli-win32-x64-msvc@2.10.0" = fetchurl {
url = "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.10.0.tgz";
hash = "sha512-NTpyQxkpzGmU6ceWBTY2xRIEaS0ZLbVx1HE1zTA3TY/pV3+cPoPPOs+7YScr4IMzXMtOw7tLw5LEXo5oIG3qaQ==";
};
"@tauri-apps/cli@2.10.0" = fetchurl {
url = "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.10.0.tgz";
hash = "sha512-ZwT0T+7bw4+DPCSWzmviwq5XbXlM0cNoleDKOYPFYqcZqeKY31KlpoMW/MOON/tOFBPgi31a2v3w9gliqwL2+Q==";
};
"@tauri-apps/plugin-autostart@2.5.1" = fetchurl {
url = "https://registry.npmjs.org/@tauri-apps/plugin-autostart/-/plugin-autostart-2.5.1.tgz";
hash = "sha512-zS/xx7yzveCcotkA+8TqkI2lysmG2wvQXv2HGAVExITmnFfHAdj1arGsbbfs3o6EktRHf6l34pJxc3YGG2mg7w==";
};
"@tauri-apps/plugin-clipboard-manager@2.3.2" = fetchurl {
url = "https://registry.npmjs.org/@tauri-apps/plugin-clipboard-manager/-/plugin-clipboard-manager-2.3.2.tgz";
hash = "sha512-CUlb5Hqi2oZbcZf4VUyUH53XWPPdtpw43EUpCza5HWZJwxEoDowFzNUDt1tRUXA8Uq+XPn17Ysfptip33sG4eQ==";
};
"@tauri-apps/plugin-dialog@2.6.0" = fetchurl {
url = "https://registry.npmjs.org/@tauri-apps/plugin-dialog/-/plugin-dialog-2.6.0.tgz";
hash = "sha512-q4Uq3eY87TdcYzXACiYSPhmpBA76shgmQswGkSVio4C82Sz2W4iehe9TnKYwbq7weHiL88Yw19XZm7v28+Micg==";
};
"@tauri-apps/plugin-fs@2.4.4" = fetchurl {
url = "https://registry.npmjs.org/@tauri-apps/plugin-fs/-/plugin-fs-2.4.4.tgz";
hash = "sha512-MTorXxIRmOnOPT1jZ3w96vjSuScER38ryXY88vl5F0uiKdnvTKKTtaEjTEo8uPbl4e3gnUtfsDVwC7h77GQLvQ==";
};
"@tauri-apps/plugin-global-shortcut@2.3.1" = fetchurl {
url = "https://registry.npmjs.org/@tauri-apps/plugin-global-shortcut/-/plugin-global-shortcut-2.3.1.tgz";
hash = "sha512-vr40W2N6G63dmBPaha1TsBQLLURXG538RQbH5vAm0G/ovVZyXJrmZR1HF1W+WneNloQvwn4dm8xzwpEXRW560g==";
};
"@tauri-apps/plugin-opener@2.5.2" = fetchurl {
url = "https://registry.npmjs.org/@tauri-apps/plugin-opener/-/plugin-opener-2.5.2.tgz";
hash = "sha512-ei/yRRoCklWHImwpCcDK3VhNXx+QXM9793aQ64YxpqVF0BDuuIlXhZgiAkc15wnPVav+IbkYhmDJIv5R326Mew==";
};
"@tauri-apps/plugin-os@2.3.2" = fetchurl {
url = "https://registry.npmjs.org/@tauri-apps/plugin-os/-/plugin-os-2.3.2.tgz";
hash = "sha512-n+nXWeuSeF9wcEsSPmRnBEGrRgOy6jjkSU+UVCOV8YUGKb2erhDOxis7IqRXiRVHhY8XMKks00BJ0OAdkpf6+A==";
};
"@tauri-apps/plugin-process@2.3.1" = fetchurl {
url = "https://registry.npmjs.org/@tauri-apps/plugin-process/-/plugin-process-2.3.1.tgz";
hash = "sha512-nCa4fGVaDL/B9ai03VyPOjfAHRHSBz5v6F/ObsB73r/dA3MHHhZtldaDMIc0V/pnUw9ehzr2iEG+XkSEyC0JJA==";
};
"@tauri-apps/plugin-sql@2.3.1" = fetchurl {
url = "https://registry.npmjs.org/@tauri-apps/plugin-sql/-/plugin-sql-2.3.1.tgz";
hash = "sha512-iNgHnFIR+jRkx9INKVKepzMlxXtNkJUaWuhagFjT4dOttPaNyRnVHgwTjpqZhyVjiklDh2UdEPAJkQKiCPAekw==";
};
"@tauri-apps/plugin-store@2.4.1" = fetchurl {
url = "https://registry.npmjs.org/@tauri-apps/plugin-store/-/plugin-store-2.4.1.tgz";
hash = "sha512-ckGSEzZ5Ii4Hf2D5x25Oqnm2Zf9MfDWAzR+volY0z/OOBz6aucPKEY0F649JvQ0Vupku6UJo7ugpGRDOFOunkA==";
};
"@tauri-apps/plugin-updater@2.10.0" = fetchurl {
url = "https://registry.npmjs.org/@tauri-apps/plugin-updater/-/plugin-updater-2.10.0.tgz";
hash = "sha512-ljN8jPlnT0aSn8ecYhuBib84alxfMx6Hc8vJSKMJyzGbTPFZAC44T2I1QNFZssgWKrAlofvJqCC6Rr472JWfkQ==";
};
"@tybys/wasm-util@0.10.1" = fetchurl {
url = "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz";
hash = "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==";
};
"@types/babel__core@7.20.5" = fetchurl {
url = "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz";
hash = "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==";
};
"@types/babel__generator@7.27.0" = fetchurl {
url = "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz";
hash = "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==";
};
"@types/babel__template@7.4.4" = fetchurl {
url = "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz";
hash = "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==";
};
"@types/babel__traverse@7.28.0" = fetchurl {
url = "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz";
hash = "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==";
};
"@types/estree@1.0.8" = fetchurl {
url = "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz";
hash = "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==";
};
"@types/json-schema@7.0.15" = fetchurl {
url = "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz";
hash = "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==";
};
"@types/node@24.9.1" = fetchurl {
url = "https://registry.npmjs.org/@types/node/-/node-24.9.1.tgz";
hash = "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==";
};
"@types/parse-json@4.0.2" = fetchurl {
url = "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz";
hash = "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==";
};
"@types/prop-types@15.7.15" = fetchurl {
url = "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz";
hash = "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==";
};
"@types/react-dom@18.3.7" = fetchurl {
url = "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz";
hash = "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==";
};
"@types/react-select@5.0.1" = fetchurl {
url = "https://registry.npmjs.org/@types/react-select/-/react-select-5.0.1.tgz";
hash = "sha512-h5Im0AP0dr4AVeHtrcvQrLV+gmPa7SA0AGdxl2jOhtwiE6KgXBFSogWw8az32/nusE6AQHlCOHQWjP1S/+oMWA==";
};
"@types/react-transition-group@4.4.12" = fetchurl {
url = "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz";
hash = "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==";
};
"@types/react@18.3.26" = fetchurl {
url = "https://registry.npmjs.org/@types/react/-/react-18.3.26.tgz";
hash = "sha512-RFA/bURkcKzx/X9oumPG9Vp3D3JUgus/d0b67KB0t5S/raciymilkOa66olh78MUI92QLbEJevO7rvqU/kjwKA==";
};
"@typescript-eslint/eslint-plugin@8.49.0" = fetchurl {
url = "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.49.0.tgz";
hash = "sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A==";
};
"@typescript-eslint/parser@8.49.0" = fetchurl {
url = "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.49.0.tgz";
hash = "sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==";
};
"@typescript-eslint/project-service@8.49.0" = fetchurl {
url = "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.49.0.tgz";
hash = "sha512-/wJN0/DKkmRUMXjZUXYZpD1NEQzQAAn9QWfGwo+Ai8gnzqH7tvqS7oNVdTjKqOcPyVIdZdyCMoqN66Ia789e7g==";
};
"@typescript-eslint/scope-manager@8.49.0" = fetchurl {
url = "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.49.0.tgz";
hash = "sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg==";
};
"@typescript-eslint/tsconfig-utils@8.49.0" = fetchurl {
url = "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.49.0.tgz";
hash = "sha512-8prixNi1/6nawsRYxet4YOhnbW+W9FK/bQPxsGB1D3ZrDzbJ5FXw5XmzxZv82X3B+ZccuSxo/X8q9nQ+mFecWA==";
};
"@typescript-eslint/type-utils@8.49.0" = fetchurl {
url = "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.49.0.tgz";
hash = "sha512-KTExJfQ+svY8I10P4HdxKzWsvtVnsuCifU5MvXrRwoP2KOlNZ9ADNEWWsQTJgMxLzS5VLQKDjkCT/YzgsnqmZg==";
};
"@typescript-eslint/types@8.49.0" = fetchurl {
url = "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.49.0.tgz";
hash = "sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==";
};
"@typescript-eslint/typescript-estree@8.49.0" = fetchurl {
url = "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.49.0.tgz";
hash = "sha512-jrLdRuAbPfPIdYNppHJ/D0wN+wwNfJ32YTAm10eJVsFmrVpXQnDWBn8niCSMlWjvml8jsce5E/O+86IQtTbJWA==";
};
"@typescript-eslint/utils@8.49.0" = fetchurl {
url = "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.49.0.tgz";
hash = "sha512-N3W7rJw7Rw+z1tRsHZbK395TWSYvufBXumYtEGzypgMUthlg0/hmCImeA8hgO2d2G4pd7ftpxxul2J8OdtdaFA==";
};
"@typescript-eslint/visitor-keys@8.49.0" = fetchurl {
url = "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.49.0.tgz";
hash = "sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==";
};
"@vitejs/plugin-react@4.7.0" = fetchurl {
url = "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz";
hash = "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==";
};
"acorn-jsx@5.3.2" = fetchurl {
url = "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz";
hash = "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==";
};
"acorn@8.15.0" = fetchurl {
url = "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz";
hash = "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==";
};
"ajv@6.12.6" = fetchurl {
url = "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz";
hash = "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==";
};
"ansi-styles@4.3.0" = fetchurl {
url = "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz";
hash = "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==";
};
"argparse@2.0.1" = fetchurl {
url = "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz";
hash = "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==";
};
"babel-plugin-macros@3.1.0" = fetchurl {
url = "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz";
hash = "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==";
};
"balanced-match@1.0.2" = fetchurl {
url = "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz";
hash = "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==";
};
"baseline-browser-mapping@2.8.20" = fetchurl {
url = "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.20.tgz";
hash = "sha512-JMWsdF+O8Orq3EMukbUN1QfbLK9mX2CkUmQBcW2T0s8OmdAUL5LLM/6wFwSrqXzlXB13yhyK9gTKS1rIizOduQ==";
};
"brace-expansion@1.1.12" = fetchurl {
url = "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz";
hash = "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==";
};
"brace-expansion@2.0.2" = fetchurl {
url = "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz";
hash = "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==";
};
"browserslist@4.27.0" = fetchurl {
url = "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz";
hash = "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==";
};
"callsites@3.1.0" = fetchurl {
url = "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz";
hash = "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==";
};
"caniuse-lite@1.0.30001751" = fetchurl {
url = "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001751.tgz";
hash = "sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==";
};
"chalk@4.1.2" = fetchurl {
url = "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz";
hash = "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==";
};
"color-convert@2.0.1" = fetchurl {
url = "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz";
hash = "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==";
};
"color-name@1.1.4" = fetchurl {
url = "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz";
hash = "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==";
};
"concat-map@0.0.1" = fetchurl {
url = "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz";
hash = "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==";
};
"convert-source-map@1.9.0" = fetchurl {
url = "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz";
hash = "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==";
};
"convert-source-map@2.0.0" = fetchurl {
url = "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz";
hash = "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==";
};
"cosmiconfig@7.1.0" = fetchurl {
url = "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz";
hash = "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==";
};
"cross-spawn@7.0.6" = fetchurl {
url = "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz";
hash = "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==";
};
"csstype@3.1.3" = fetchurl {
url = "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz";
hash = "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==";
};
"debug@4.4.3" = fetchurl {
url = "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz";
hash = "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==";
};
"deep-is@0.1.4" = fetchurl {
url = "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz";
hash = "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==";
};
"detect-libc@2.1.2" = fetchurl {
url = "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz";
hash = "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==";
};
"dom-helpers@5.2.1" = fetchurl {
url = "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz";
hash = "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==";
};
"electron-to-chromium@1.5.240" = fetchurl {
url = "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.240.tgz";
hash = "sha512-OBwbZjWgrCOH+g6uJsA2/7Twpas2OlepS9uvByJjR2datRDuKGYeD+nP8lBBks2qnB7bGJNHDUx7c/YLaT3QMQ==";
};
"enhanced-resolve@5.18.3" = fetchurl {
url = "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz";
hash = "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==";
};
"error-ex@1.3.4" = fetchurl {
url = "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz";
hash = "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==";
};
"esbuild@0.25.11" = fetchurl {
url = "https://registry.npmjs.org/esbuild/-/esbuild-0.25.11.tgz";
hash = "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==";
};
"escalade@3.2.0" = fetchurl {
url = "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz";
hash = "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==";
};
"escape-string-regexp@4.0.0" = fetchurl {
url = "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz";
hash = "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==";
};
"eslint-plugin-i18next@6.1.3" = fetchurl {
url = "https://registry.npmjs.org/eslint-plugin-i18next/-/eslint-plugin-i18next-6.1.3.tgz";
hash = "sha512-z/h4oBRd9wI1ET60HqcLSU6XPeAh/EPOrBBTyCdkWeMoYrWAaUVA+DOQkWTiNIyCltG4NTmy62SQisVXxoXurw==";
};
"eslint-scope@8.4.0" = fetchurl {
url = "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz";
hash = "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==";
};
"eslint-visitor-keys@3.4.3" = fetchurl {
url = "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz";
hash = "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==";
};
"eslint-visitor-keys@4.2.1" = fetchurl {
url = "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz";
hash = "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==";
};
"eslint@9.39.1" = fetchurl {
url = "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz";
hash = "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==";
};
"espree@10.4.0" = fetchurl {
url = "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz";
hash = "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==";
};
"esquery@1.6.0" = fetchurl {
url = "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz";
hash = "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==";
};
"esrecurse@4.3.0" = fetchurl {
url = "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz";
hash = "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==";
};
"estraverse@5.3.0" = fetchurl {
url = "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz";
hash = "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==";
};
"esutils@2.0.3" = fetchurl {
url = "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz";
hash = "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==";
};
"fast-deep-equal@3.1.3" = fetchurl {
url = "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz";
hash = "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==";
};
"fast-json-stable-stringify@2.1.0" = fetchurl {
url = "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz";
hash = "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==";
};
"fast-levenshtein@2.0.6" = fetchurl {
url = "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz";
hash = "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==";
};
"fdir@6.5.0" = fetchurl {
url = "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz";
hash = "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==";
};
"file-entry-cache@8.0.0" = fetchurl {
url = "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz";
hash = "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==";
};
"find-root@1.1.0" = fetchurl {
url = "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz";
hash = "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==";
};
"find-up@5.0.0" = fetchurl {
url = "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz";
hash = "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==";
};
"flat-cache@4.0.1" = fetchurl {
url = "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz";
hash = "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==";
};
"flatted@3.3.3" = fetchurl {
url = "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz";
hash = "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==";
};
"fsevents@2.3.2" = fetchurl {
url = "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz";
hash = "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==";
};
"fsevents@2.3.3" = fetchurl {
url = "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz";
hash = "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==";
};
"function-bind@1.1.2" = fetchurl {
url = "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz";
hash = "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==";
};
"gensync@1.0.0-beta.2" = fetchurl {
url = "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz";
hash = "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==";
};
"glob-parent@6.0.2" = fetchurl {
url = "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz";
hash = "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==";
};
"globals@14.0.0" = fetchurl {
url = "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz";
hash = "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==";
};
"graceful-fs@4.2.11" = fetchurl {
url = "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz";
hash = "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==";
};
"has-flag@4.0.0" = fetchurl {
url = "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz";
hash = "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==";
};
"hasown@2.0.2" = fetchurl {
url = "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz";
hash = "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==";
};
"hoist-non-react-statics@3.3.2" = fetchurl {
url = "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz";
hash = "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==";
};
"html-parse-stringify@3.0.1" = fetchurl {
url = "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz";
hash = "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==";
};
"i18next@25.7.2" = fetchurl {
url = "https://registry.npmjs.org/i18next/-/i18next-25.7.2.tgz";
hash = "sha512-58b4kmLpLv1buWUEwegMDUqZVR5J+rT+WTRFaBGL7lxDuJQQ0NrJFrq+eT2N94aYVR1k1Sr13QITNOL88tZCuw==";
};
"ignore@5.3.2" = fetchurl {
url = "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz";
hash = "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==";
};
"ignore@7.0.5" = fetchurl {
url = "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz";
hash = "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==";
};
"immer@11.1.3" = fetchurl {
url = "https://registry.npmjs.org/immer/-/immer-11.1.3.tgz";
hash = "sha512-6jQTc5z0KJFtr1UgFpIL3N9XSC3saRaI9PwWtzM2pSqkNGtiNkYY2OSwkOGDK2XcTRcLb1pi/aNkKZz0nxVH4Q==";
};
"import-fresh@3.3.1" = fetchurl {
url = "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz";
hash = "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==";
};
"imurmurhash@0.1.4" = fetchurl {
url = "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz";
hash = "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==";
};
"is-arrayish@0.2.1" = fetchurl {
url = "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz";
hash = "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==";
};
"is-core-module@2.16.1" = fetchurl {
url = "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz";
hash = "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==";
};
"is-extglob@2.1.1" = fetchurl {
url = "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz";
hash = "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==";
};
"is-glob@4.0.3" = fetchurl {
url = "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz";
hash = "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==";
};
"isexe@2.0.0" = fetchurl {
url = "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz";
hash = "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==";
};
"jiti@2.6.1" = fetchurl {
url = "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz";
hash = "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==";
};
"js-tokens@4.0.0" = fetchurl {
url = "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz";
hash = "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==";
};
"js-yaml@4.1.1" = fetchurl {
url = "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz";
hash = "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==";
};
"jsesc@3.1.0" = fetchurl {
url = "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz";
hash = "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==";
};
"json-buffer@3.0.1" = fetchurl {
url = "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz";
hash = "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==";
};
"json-parse-even-better-errors@2.3.1" = fetchurl {
url = "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz";
hash = "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==";
};
"json-schema-traverse@0.4.1" = fetchurl {
url = "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz";
hash = "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==";
};
"json-stable-stringify-without-jsonify@1.0.1" = fetchurl {
url = "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz";
hash = "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==";
};
"json5@2.2.3" = fetchurl {
url = "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz";
hash = "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==";
};
"keyv@4.5.4" = fetchurl {
url = "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz";
hash = "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==";
};
"levn@0.4.1" = fetchurl {
url = "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz";
hash = "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==";
};
"lightningcss-android-arm64@1.30.2" = fetchurl {
url = "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz";
hash = "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==";
};
"lightningcss-darwin-arm64@1.30.2" = fetchurl {
url = "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz";
hash = "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==";
};
"lightningcss-darwin-x64@1.30.2" = fetchurl {
url = "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz";
hash = "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==";
};
"lightningcss-freebsd-x64@1.30.2" = fetchurl {
url = "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz";
hash = "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==";
};
"lightningcss-linux-arm-gnueabihf@1.30.2" = fetchurl {
url = "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz";
hash = "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==";
};
"lightningcss-linux-arm64-gnu@1.30.2" = fetchurl {
url = "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz";
hash = "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==";
};
"lightningcss-linux-arm64-musl@1.30.2" = fetchurl {
url = "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz";
hash = "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==";
};
"lightningcss-linux-x64-gnu@1.30.2" = fetchurl {
url = "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz";
hash = "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==";
};
"lightningcss-linux-x64-musl@1.30.2" = fetchurl {
url = "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz";
hash = "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==";
};
"lightningcss-win32-arm64-msvc@1.30.2" = fetchurl {
url = "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz";
hash = "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==";
};
"lightningcss-win32-x64-msvc@1.30.2" = fetchurl {
url = "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz";
hash = "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==";
};
"lightningcss@1.30.2" = fetchurl {
url = "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz";
hash = "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==";
};
"lines-and-columns@1.2.4" = fetchurl {
url = "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz";
hash = "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==";
};
"locate-path@6.0.0" = fetchurl {
url = "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz";
hash = "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==";
};
"lodash.merge@4.6.2" = fetchurl {
url = "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz";
hash = "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==";
};
"lodash@4.17.21" = fetchurl {
url = "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz";
hash = "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==";
};
"loose-envify@1.4.0" = fetchurl {
url = "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz";
hash = "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==";
};
"lru-cache@5.1.1" = fetchurl {
url = "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz";
hash = "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==";
};
"lucide-react@0.542.0" = fetchurl {
url = "https://registry.npmjs.org/lucide-react/-/lucide-react-0.542.0.tgz";
hash = "sha512-w3hD8/SQB7+lzU2r4VdFyzzOzKnUjTZIF/MQJGSSvni7Llewni4vuViRppfRAa2guOsY5k4jZyxw/i9DQHv+dw==";
};
"magic-string@0.30.21" = fetchurl {
url = "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz";
hash = "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==";
};
"memoize-one@6.0.0" = fetchurl {
url = "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz";
hash = "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==";
};
"minimatch@3.1.2" = fetchurl {
url = "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz";
hash = "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==";
};
"minimatch@9.0.5" = fetchurl {
url = "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz";
hash = "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==";
};
"ms@2.1.3" = fetchurl {
url = "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz";
hash = "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==";
};
"nanoid@3.3.11" = fetchurl {
url = "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz";
hash = "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==";
};
"natural-compare@1.4.0" = fetchurl {
url = "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz";
hash = "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==";
};
"node-releases@2.0.26" = fetchurl {
url = "https://registry.npmjs.org/node-releases/-/node-releases-2.0.26.tgz";
hash = "sha512-S2M9YimhSjBSvYnlr5/+umAnPHE++ODwt5e2Ij6FoX45HA/s4vHdkDx1eax2pAPeAOqu4s9b7ppahsyEFdVqQA==";
};
"object-assign@4.1.1" = fetchurl {
url = "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz";
hash = "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==";
};
"optionator@0.9.4" = fetchurl {
url = "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz";
hash = "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==";
};
"p-limit@3.1.0" = fetchurl {
url = "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz";
hash = "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==";
};
"p-locate@5.0.0" = fetchurl {
url = "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz";
hash = "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==";
};
"parent-module@1.0.1" = fetchurl {
url = "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz";
hash = "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==";
};
"parse-json@5.2.0" = fetchurl {
url = "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz";
hash = "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==";
};
"path-exists@4.0.0" = fetchurl {
url = "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz";
hash = "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==";
};
"path-key@3.1.1" = fetchurl {
url = "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz";
hash = "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==";
};
"path-parse@1.0.7" = fetchurl {
url = "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz";
hash = "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==";
};
"path-type@4.0.0" = fetchurl {
url = "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz";
hash = "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==";
};
"picocolors@1.1.1" = fetchurl {
url = "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz";
hash = "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==";
};
"picomatch@4.0.3" = fetchurl {
url = "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz";
hash = "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==";
};
"playwright-core@1.58.0" = fetchurl {
url = "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.0.tgz";
hash = "sha512-aaoB1RWrdNi3//rOeKuMiS65UCcgOVljU46At6eFcOFPFHWtd2weHRRow6z/n+Lec0Lvu0k9ZPKJSjPugikirw==";
};
"playwright@1.58.0" = fetchurl {
url = "https://registry.npmjs.org/playwright/-/playwright-1.58.0.tgz";
hash = "sha512-2SVA0sbPktiIY/MCOPX8e86ehA/e+tDNq+e5Y8qjKYti2Z/JG7xnronT/TXTIkKbYGWlCbuucZ6dziEgkoEjQQ==";
};
"postcss@8.5.6" = fetchurl {
url = "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz";
hash = "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==";
};
"prelude-ls@1.2.1" = fetchurl {
url = "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz";
hash = "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==";
};
"prettier@3.6.2" = fetchurl {
url = "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz";
hash = "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==";
};
"prop-types@15.8.1" = fetchurl {
url = "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz";
hash = "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==";
};
"punycode@2.3.1" = fetchurl {
url = "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz";
hash = "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==";
};
"react-dom@18.3.1" = fetchurl {
url = "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz";
hash = "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==";
};
"react-i18next@16.4.1" = fetchurl {
url = "https://registry.npmjs.org/react-i18next/-/react-i18next-16.4.1.tgz";
hash = "sha512-GzsYomxb1/uE7nlJm0e1qQ8f+W9I3Xirh9VoycZIahk6C8Pmv/9Fd0ek6zjf1FSgtGLElDGqwi/4FOHEGUbsEQ==";
};
"react-is@16.13.1" = fetchurl {
url = "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz";
hash = "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==";
};
"react-refresh@0.17.0" = fetchurl {
url = "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz";
hash = "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==";
};
"react-select@5.10.2" = fetchurl {
url = "https://registry.npmjs.org/react-select/-/react-select-5.10.2.tgz";
hash = "sha512-Z33nHdEFWq9tfnfVXaiM12rbJmk+QjFEztWLtmXqQhz6Al4UZZ9xc0wiatmGtUOCCnHN0WizL3tCMYRENX4rVQ==";
};
"react-transition-group@4.4.5" = fetchurl {
url = "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz";
hash = "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==";
};
"react@18.3.1" = fetchurl {
url = "https://registry.npmjs.org/react/-/react-18.3.1.tgz";
hash = "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==";
};
"requireindex@1.1.0" = fetchurl {
url = "https://registry.npmjs.org/requireindex/-/requireindex-1.1.0.tgz";
hash = "sha512-LBnkqsDE7BZKvqylbmn7lTIVdpx4K/QCduRATpO5R+wtPmky/a8pN1bO2D6wXppn1497AJF9mNjqAXr6bdl9jg==";
};
"resolve-from@4.0.0" = fetchurl {
url = "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz";
hash = "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==";
};
"resolve@1.22.11" = fetchurl {
url = "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz";
hash = "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==";
};
"rollup@4.52.5" = fetchurl {
url = "https://registry.npmjs.org/rollup/-/rollup-4.52.5.tgz";
hash = "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==";
};
"scheduler@0.23.2" = fetchurl {
url = "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz";
hash = "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==";
};
"semver@6.3.1" = fetchurl {
url = "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz";
hash = "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==";
};
"semver@7.7.3" = fetchurl {
url = "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz";
hash = "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==";
};
"shebang-command@2.0.0" = fetchurl {
url = "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz";
hash = "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==";
};
"shebang-regex@3.0.0" = fetchurl {
url = "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz";
hash = "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==";
};
"sonner@2.0.7" = fetchurl {
url = "https://registry.npmjs.org/sonner/-/sonner-2.0.7.tgz";
hash = "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==";
};
"source-map-js@1.2.1" = fetchurl {
url = "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz";
hash = "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==";
};
"source-map@0.5.7" = fetchurl {
url = "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz";
hash = "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==";
};
"strip-json-comments@3.1.1" = fetchurl {
url = "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz";
hash = "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==";
};
"stylis@4.2.0" = fetchurl {
url = "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz";
hash = "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==";
};
"supports-color@7.2.0" = fetchurl {
url = "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz";
hash = "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==";
};
"supports-preserve-symlinks-flag@1.0.0" = fetchurl {
url = "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz";
hash = "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==";
};
"tailwindcss@4.1.16" = fetchurl {
url = "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.16.tgz";
hash = "sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA==";
};
"tapable@2.3.0" = fetchurl {
url = "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz";
hash = "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==";
};
"tauri-plugin-macos-permissions-api@2.3.0" = fetchurl {
url = "https://registry.npmjs.org/tauri-plugin-macos-permissions-api/-/tauri-plugin-macos-permissions-api-2.3.0.tgz";
hash = "sha512-pZp0jmDySysBqrGueknd1a7Rr4XEO9aXpMv9TNrT2PDHP0MSH20njieOagsFYJ5MCVb8A+wcaK0cIkjUC2dOww==";
};
"tinyglobby@0.2.15" = fetchurl {
url = "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz";
hash = "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==";
};
"ts-api-utils@2.1.0" = fetchurl {
url = "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz";
hash = "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==";
};
"tslib@2.8.1" = fetchurl {
url = "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz";
hash = "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==";
};
"type-check@0.4.0" = fetchurl {
url = "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz";
hash = "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==";
};
"typescript@5.6.3" = fetchurl {
url = "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz";
hash = "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==";
};
"undici-types@7.16.0" = fetchurl {
url = "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz";
hash = "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==";
};
"update-browserslist-db@1.1.4" = fetchurl {
url = "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz";
hash = "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==";
};
"uri-js@4.4.1" = fetchurl {
url = "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz";
hash = "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==";
};
"use-isomorphic-layout-effect@1.2.1" = fetchurl {
url = "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz";
hash = "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==";
};
"use-sync-external-store@1.6.0" = fetchurl {
url = "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz";
hash = "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==";
};
"vite@6.4.1" = fetchurl {
url = "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz";
hash = "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==";
};
"void-elements@3.1.0" = fetchurl {
url = "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz";
hash = "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==";
};
"which@2.0.2" = fetchurl {
url = "https://registry.npmjs.org/which/-/which-2.0.2.tgz";
hash = "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==";
};
"word-wrap@1.2.5" = fetchurl {
url = "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz";
hash = "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==";
};
"yallist@3.1.1" = fetchurl {
url = "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz";
hash = "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==";
};
"yaml@1.10.2" = fetchurl {
url = "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz";
hash = "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==";
};
"yocto-queue@0.1.0" = fetchurl {
url = "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz";
hash = "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==";
};
"zod@3.25.76" = fetchurl {
url = "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz";
hash = "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==";
};
"zustand@5.0.8" = fetchurl {
url = "https://registry.npmjs.org/zustand/-/zustand-5.0.8.tgz";
hash = "sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==";
};
}
================================================
FILE: .prettierignore
================================================
# Dependencies
node_modules
bun.lock
package-lock.json
# Build outputs
dist
target
*.bundle.*
# Tauri
src-tauri/target
src-tauri/gen
# Generated files
src/bindings.ts
# Misc
.DS_Store
*.log
================================================
FILE: .prettierrc
================================================
{
"endOfLine": "lf"
}
================================================
FILE: .vscode/extensions.json
================================================
{
"recommendations": [
"tauri-apps.tauri-vscode",
"rust-lang.rust-analyzer",
"esbenp.prettier-vscode"
]
}
================================================
FILE: AGENTS.md
================================================
# AGENTS.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Development Commands
**Prerequisites:**
- [Rust](https://rustup.rs/) (latest stable)
- [Bun](https://bun.sh/) package manager
**Core Development:**
```bash
# Install dependencies
bun install
# Run in development mode
bun run tauri dev
# If cmake error on macOS:
CMAKE_POLICY_VERSION_MINIMUM=3.5 bun run tauri dev
# Build for production
bun run tauri build
# Frontend only development
bun run dev # Start Vite dev server
bun run build # Build frontend (TypeScript + Vite)
bun run preview # Preview built frontend
```
**Model Setup (Required for Development):**
```bash
# Create models directory
mkdir -p src-tauri/resources/models
# Download required VAD model
curl -o src-tauri/resources/models/silero_vad_v4.onnx https://blob.handy.computer/silero_vad_v4.onnx
```
## Architecture Overview
Handy is a cross-platform desktop speech-to-text application built with Tauri (Rust backend + React/TypeScript frontend).
### Core Components
**Backend (Rust - src-tauri/src/):**
- `lib.rs` - Main application entry point with Tauri setup, tray menu, and managers
- `managers/` - Core business logic managers:
- `audio.rs` - Audio recording and device management
- `model.rs` - Whisper model downloading and management
- `transcription.rs` - Speech-to-text processing pipeline
- `audio_toolkit/` - Low-level audio processing:
- `audio/` - Device enumeration, recording, resampling
- `vad/` - Voice Activity Detection using Silero VAD
- `commands/` - Tauri command handlers for frontend communication
- `shortcut.rs` - Global keyboard shortcut handling
- `settings.rs` - Application settings management
**Frontend (React/TypeScript - src/):**
- `App.tsx` - Main application component with onboarding flow
- `components/settings/` - Settings UI components
- `components/model-selector/` - Model management interface
- `hooks/` - React hooks for settings and model management
- `lib/types.ts` - Shared TypeScript type definitions
### Key Architecture Patterns
**Manager Pattern:** Core functionality is organized into managers (Audio, Model, Transcription) that are initialized at startup and managed by Tauri's state system.
**Command-Event Architecture:** Frontend communicates with backend via Tauri commands, backend sends updates via events.
**Pipeline Processing:** Audio → VAD → Whisper → Text output with configurable components at each stage.
### Technology Stack
**Core Libraries:**
- `whisper-rs` - Local Whisper inference with GPU acceleration
- `cpal` - Cross-platform audio I/O
- `vad-rs` - Voice Activity Detection
- `rdev` - Global keyboard shortcuts
- `rubato` - Audio resampling
- `rodio` - Audio playback for feedback sounds
**Platform-Specific Features:**
- macOS: Metal acceleration for Whisper, accessibility permissions
- Windows: Vulkan acceleration, code signing
- Linux: OpenBLAS + Vulkan acceleration
### Application Flow
1. **Initialization:** App starts minimized to tray, loads settings, initializes managers
2. **Model Setup:** First-run downloads preferred Whisper model (Small/Medium/Turbo/Large)
3. **Recording:** Global shortcut triggers audio recording with VAD filtering
4. **Processing:** Audio sent to Whisper model for transcription
5. **Output:** Text pasted to active application via system clipboard
### Settings System
Settings are stored using Tauri's store plugin with reactive updates:
- Keyboard shortcuts (configurable, supports push-to-talk)
- Audio devices (microphone/output selection)
- Model preferences (Small/Medium/Turbo/Large Whisper variants)
- Audio feedback and translation options
### Single Instance Architecture
The app enforces single instance behavior - launching when already running brings the settings window to front rather than creating a new process.
================================================
FILE: BUILD.md
================================================
# Build Instructions
This guide covers how to set up the development environment and build Handy from source across different platforms.
## Prerequisites
### All Platforms
- [Rust](https://rustup.rs/) (latest stable)
- [Bun](https://bun.sh/) package manager
- [Tauri Prerequisites](https://tauri.app/start/prerequisites/)
### Platform-Specific Requirements
#### macOS
- Xcode Command Line Tools
- Install with: `xcode-select --install`
#### Windows
- Microsoft C++ Build Tools
- Visual Studio 2019/2022 with C++ development tools
- Or Visual Studio Build Tools 2019/2022
#### Linux
- Build essentials
- ALSA development libraries
- Install with:
```bash
# Ubuntu/Debian
sudo apt update
sudo apt install build-essential libasound2-dev pkg-config libssl-dev libvulkan-dev vulkan-tools glslc libgtk-3-dev libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev libgtk-layer-shell0 libgtk-layer-shell-dev patchelf cmake
# Fedora/RHEL
sudo dnf groupinstall "Development Tools"
sudo dnf install alsa-lib-devel pkgconf openssl-devel vulkan-devel \
gtk3-devel webkit2gtk4.1-devel libappindicator-gtk3-devel librsvg2-devel \
gtk-layer-shell gtk-layer-shell-devel \
cmake
# Arch Linux
sudo pacman -S base-devel alsa-lib pkgconf openssl vulkan-devel \
gtk3 webkit2gtk-4.1 libappindicator-gtk3 librsvg gtk-layer-shell \
cmake
```
## Setup Instructions
### 1. Clone the Repository
```bash
git clone git@github.com:cjpais/Handy.git
cd Handy
```
### 2. Install Dependencies
```bash
bun install
```
### 3. Start Dev Server
```bash
bun tauri dev
```
### 4. Build for Production
```bash
bun run tauri build
```
This compiles a release binary and generates platform-specific bundles (deb, rpm, AppImage on Linux; dmg on macOS; msi on Windows).
## Linux Install (from source)
The raw binary (`src-tauri/target/release/handy`) cannot run standalone — it needs Tauri resource files (tray icons, sounds, VAD model) to be co-located at the expected path.
**Install from the deb bundle** (works on any Linux distro):
```bash
cd /tmp
ar x /path/to/Handy/src-tauri/target/release/bundle/deb/Handy_*_amd64.deb data.tar.gz
tar xzf data.tar.gz
sudo cp usr/bin/handy /usr/bin/
sudo cp -r usr/lib/Handy /usr/lib/
sudo cp -r usr/share/icons/hicolor/* /usr/share/icons/hicolor/
sudo cp usr/share/applications/Handy.desktop /usr/share/applications/
```
After subsequent rebuilds, only the binary needs re-copying:
```bash
sudo cp src-tauri/target/release/handy /usr/bin/
```
Resources only need re-copying if they change upstream (new icons, sounds, etc.).
## Troubleshooting
### AppImage build fails on Arch / rolling-release distros
`linuxdeploy` bundles its own `strip` binary which is too old to process system libraries built with newer toolchains on rolling-release distros (Arch, CachyOS, Manjaro, EndeavourOS).
The error from Tauri:
```
Bundling Handy_*_amd64.AppImage
failed to bundle project `failed to run linuxdeploy`
```
Tauri swallows the real linuxdeploy error. To see it, run linuxdeploy manually:
```bash
cd src-tauri/target/release/bundle/appimage
~/.cache/tauri/linuxdeploy-x86_64.AppImage --appimage-extract-and-run \
--appdir Handy.AppDir --plugin gtk --output appimage
```
**Workaround:** The binary, deb, and rpm bundles all build fine — only the AppImage step fails. To skip it:
```bash
bun run tauri build -- --bundles deb
```
Then install using the deb extraction method above.
================================================
FILE: CHANGELOG.md
================================================
# Changelog
## [0.3.0] - 2025-07-11
### Added
- **Translate to English** setting: Added automatic translation of speech to English
- Settings refactored into React hooks for better state management
- Audio device switching capability
- Hysteresis to VAD (Voice Activity Detection) for more stable recording
### Changed
- Major audio backend refactor for improved performance and reliability
- Moved audio toolkit into src-tauri directory for better permissions handling
- Model files no longer need to be downloaded separately for releases
- Updated settings components and transcription logic
### Fixed
- Audio toolkit permissions issues
- Various stability improvements
## [0.2.3] - 2025-07-03
### Fixed
- Keycode bug that was causing input issues
- Whisper model optimization: switched to unquantized Whisper Turbo, updated Whisper Medium quantization to 4_1
## [0.2.2] - 2025-07-02
### Fixed
- Removed 50ms delay feature flag for Windows (now applies to all platforms for consistency)
## [0.2.1] - 2025-07-01
### Added
- Ctrl+Space key binding for Windows platform
### Fixed
- Windows crash issue
- Model loading on startup when available
- Windows paste functionality bug
## [0.2.0] - 2025-06-30
### Added
- **Microphone activation on demand**: More efficient resource usage
- Less permissive VAD settings for better accuracy
### Changed
- Improved microphone management and activation system
## [0.1.6] - 2025-06-30
### Added
- **Multiple models support**: Users can now select from different transcription models
- Model selection onboarding flow
- Cleanup and refactoring of model management
### Changed
- Enhanced user experience with model selection interface
- Better language and UI tweaks
## [0.1.5] - 2025-06-27
### Added
- **Different start and stop recording sounds**: Enhanced audio feedback
- Recording sound samples for better user experience
## [0.1.4] - 2025-06-27
### Fixed
- Build issues
- Auto-update functionality improvements
## [0.1.3] - 2025-06-26
### Fixed
- Paste functionality using enigo library for better cross-platform compatibility
## [0.1.2] - 2025-06-26
### Added
- **Auto-update functionality**: Application can now automatically update itself
- Footer displaying current version
- Improved menu system
### Changed
- Better user interface for version management
- Enhanced update workflow
## [0.1.1] - 2025-06-25
### Added
- **Comprehensive build system**: Support for Windows, macOS, and Linux
- Windows code signing for trusted installation
- Ubuntu/Linux build support with Vulkan
- Model file download and packaging for releases
- GitHub Actions CI/CD workflow
### Changed
- Improved build process and release workflow
- Better cross-platform compatibility
### Fixed
- Various build-related issues across platforms
## [0.1.0] - 2025-05-16
### Added
- **Initial release** of Handy
- Basic speech-to-text transcription functionality
- Voice Activity Detection (VAD) for automatic recording
- Cross-platform support (macOS, Windows, Linux)
- **Tauri-based desktop application** with React frontend
- **Global keyboard shortcuts** for activation
- **Clipboard integration** for automatic text insertion
- **LLM integration** for enhanced transcription processing
- **Configurable settings** including:
- Custom key bindings
- Audio device selection
- Microphone settings
- Push-to-talk functionality
- **System tray integration** with recording indicators
- **Accessibility permissions** handling for macOS
- **Settings persistence** with unified settings store
- **Background operation** capability
- **Multiple audio format support** with on-the-fly resampling
- **Whisper model integration** for high-quality transcription
- **MIT License** for open-source distribution
### Technical Implementation
- Built with Tauri (Rust backend) and React (TypeScript frontend)
- Audio processing with cpal and whisper-rs
- Real-time transcription with performance optimizations
- Cross-platform keyboard event handling
- Modular architecture with managers for audio, models, and transcription
================================================
FILE: CLAUDE.md
================================================
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Development Commands
**Prerequisites:** [Rust](https://rustup.rs/) (latest stable), [Bun](https://bun.sh/)
```bash
# Install dependencies
bun install
# Run in development mode
bun run tauri dev
# If cmake error on macOS:
CMAKE_POLICY_VERSION_MINIMUM=3.5 bun run tauri dev
# Build for production
bun run tauri build
# Linting and formatting (run before committing)
bun run lint # ESLint for frontend
bun run lint:fix # ESLint with auto-fix
bun run format # Prettier + cargo fmt
bun run format:check # Check formatting without changes
```
**Model Setup (Required for Development):**
```bash
mkdir -p src-tauri/resources/models
curl -o src-tauri/resources/models/silero_vad_v4.onnx https://blob.handy.computer/silero_vad_v4.onnx
```
## Architecture Overview
Handy is a cross-platform desktop speech-to-text app built with Tauri 2.x (Rust backend + React/TypeScript frontend).
### Backend Structure (src-tauri/src/)
- `lib.rs` - Main entry point, Tauri setup, manager initialization
- `managers/` - Core business logic:
- `audio.rs` - Audio recording and device management
- `model.rs` - Model downloading and management
- `transcription.rs` - Speech-to-text processing pipeline
- `history.rs` - Transcription history storage
- `audio_toolkit/` - Low-level audio processing:
- `audio/` - Device enumeration, recording, resampling
- `vad/` - Voice Activity Detection (Silero VAD)
- `commands/` - Tauri command handlers for frontend communication
- `shortcut.rs` - Global keyboard shortcut handling
- `settings.rs` - Application settings management
### Frontend Structure (src/)
- `App.tsx` - Main component with onboarding flow
- `components/settings/` - Settings UI (35+ files)
- `components/model-selector/` - Model management interface
- `components/onboarding/` - First-run experience
- `hooks/useSettings.ts`, `useModels.ts` - State management hooks
- `stores/settingsStore.ts` - Zustand store for settings
- `bindings.ts` - Auto-generated Tauri type bindings (via tauri-specta)
- `overlay/` - Recording overlay window code
### Key Patterns
**Manager Pattern:** Core functionality organized into managers (Audio, Model, Transcription) initialized at startup and managed via Tauri state.
**Command-Event Architecture:** Frontend → Backend via Tauri commands; Backend → Frontend via events.
**Pipeline Processing:** Audio → VAD → Whisper/Parakeet → Text output → Clipboard/Paste
**State Flow:** Zustand → Tauri Command → Rust State → Persistence (tauri-plugin-store)
## Internationalization (i18n)
All user-facing strings must use i18next translations. ESLint enforces this (no hardcoded strings in JSX).
**Adding new text:**
1. Add key to `src/i18n/locales/en/translation.json`
2. Use in component: `const { t } = useTranslation(); t('key.path')`
**File structure:**
```
src/i18n/
├── index.ts # i18n setup
├── languages.ts # Language metadata
└── locales/
├── en/translation.json # English (source)
├── es/translation.json # Spanish
├── fr/translation.json # French
└── vi/translation.json # Vietnamese
```
## Code Style
**Rust:**
- Run `cargo fmt` and `cargo clippy` before committing
- Handle errors explicitly (avoid unwrap in production)
- Use descriptive names, add doc comments for public APIs
**TypeScript/React:**
- Strict TypeScript, avoid `any` types
- Functional components with hooks
- Tailwind CSS for styling
- Path aliases: `@/` → `./src/`
## Commit Guidelines
Use conventional commits:
- `feat:` new features
- `fix:` bug fixes
- `docs:` documentation
- `refactor:` code refactoring
- `chore:` maintenance
## CLI Parameters
Handy supports command-line parameters on all platforms for integration with scripts, window managers, and autostart configurations.
**Implementation files:**
- `src-tauri/src/cli.rs` - CLI argument definitions (clap derive)
- `src-tauri/src/main.rs` - Argument parsing before Tauri launch
- `src-tauri/src/lib.rs` - Applying CLI overrides (setup closure + single-instance callback)
- `src-tauri/src/signal_handle.rs` - `send_transcription_input()` reusable function
**Available flags:**
| Flag | Description |
| ------------------------ | ---------------------------------------------------------------------------------- |
| `--toggle-transcription` | Toggle recording on/off on a running instance (via `tauri_plugin_single_instance`) |
| `--toggle-post-process` | Toggle recording with post-processing on/off on a running instance |
| `--cancel` | Cancel the current operation on a running instance |
| `--start-hidden` | Launch without showing the main window (tray icon still visible) |
| `--no-tray` | Launch without the system tray icon (closing window quits the app) |
| `--debug` | Enable debug mode with verbose (Trace) logging |
**Key design decisions:**
- CLI flags are runtime-only overrides — they do NOT modify persisted settings
- Remote control flags (`--toggle-transcription`, `--toggle-post-process`, `--cancel`) work by launching a second instance that sends its args to the running instance via `tauri_plugin_single_instance`, then exits
- `send_transcription_input()` in `signal_handle.rs` is shared between signal handlers and CLI to avoid code duplication
- `CliArgs` is stored in Tauri managed state (`.manage()`) so it's accessible in `on_window_event` and other handlers
## Debug Mode
Access debug features: `Cmd+Shift+D` (macOS) or `Ctrl+Shift+D` (Windows/Linux)
## Platform Notes
- **macOS**: Metal acceleration, accessibility permissions required
- **Windows**: Vulkan acceleration, code signing
- **Linux**: OpenBLAS + Vulkan, limited Wayland support, overlay disabled by default
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Handy
Thank you for your interest in contributing to Handy! This guide will help you get started with contributing to this open source speech-to-text application.
## 📖 Philosophy
Handy aims to be the most forkable speech-to-text app. The goal is to create both a useful tool and a foundation for others to build upon—a well-patterned, simple codebase that serves the community. We prioritize:
- **Simplicity**: Clear, maintainable code over clever solutions
- **Extensibility**: Make it easy for others to fork and customize
- **Privacy**: Keep everything local and offline
- **Accessibility**: Free tooling that belongs in everyone's hands
## 🚀 Getting Started
### Prerequisites
Before you begin, ensure you have the following installed:
- [Rust](https://rustup.rs/) (latest stable)
- [Bun](https://bun.sh/) package manager
- Platform-specific build tools (see [BUILD.md](BUILD.md))
### Setting Up Your Development Environment
1. **Fork the repository** on GitHub
2. **Clone your fork**:
```bash
git clone git@github.com:YOUR_USERNAME/Handy.git
cd Handy
```
3. **Add upstream remote**:
```bash
git remote add upstream git@github.com:cjpais/Handy.git
```
4. **Install dependencies**:
```bash
bun install
```
5. **Download required models**:
```bash
mkdir -p src-tauri/resources/models
curl -o src-tauri/resources/models/silero_vad_v4.onnx https://blob.handy.computer/silero_vad_v4.onnx
```
6. **Run in development mode**:
```bash
bun run tauri dev
# On macOS if you encounter cmake errors:
CMAKE_POLICY_VERSION_MINIMUM=3.5 bun run tauri dev
```
For detailed platform-specific setup instructions, see [BUILD.md](BUILD.md).
### Understanding the Codebase
Handy follows a clean architecture pattern:
**Backend (Rust - `src-tauri/src/`):**
- `lib.rs` - Main application entry point with Tauri setup
- `managers/` - Core business logic (audio, model, transcription)
- `audio_toolkit/` - Low-level audio processing (recording, VAD)
- `commands/` - Tauri command handlers for frontend communication
- `shortcut.rs` - Global keyboard shortcut handling
- `settings.rs` - Application settings management
**Frontend (React/TypeScript - `src/`):**
- `App.tsx` - Main application component
- `components/` - React UI components
- `hooks/` - Reusable React hooks
- `lib/types.ts` - Shared TypeScript types
For more details, see the Architecture section in [README.md](README.md) or [AGENTS.md](AGENTS.md).
## 🐛 Reporting Bugs
### Before Submitting a Bug Report
1. **Search existing issues** at [github.com/cjpais/Handy/issues](https://github.com/cjpais/Handy/issues)
2. **Check discussions** at [github.com/cjpais/Handy/discussions](https://github.com/cjpais/Handy/discussions)
3. **Try the latest release** to see if the issue has been fixed
4. **Enable debug mode** (`Cmd/Ctrl+Shift+D`) to gather diagnostic information
### Submitting a Bug Report
When creating a bug report, please include:
**System Information:**
- App version (found in settings or about section)
- Operating System (e.g., macOS 14.1, Windows 11, Ubuntu 22.04)
- CPU (e.g., Apple M2, Intel i7-12700K, AMD Ryzen 7 5800X)
- GPU (e.g., Apple M2 GPU, NVIDIA RTX 4080, Intel UHD Graphics)
**Bug Details:**
- Clear description of the bug
- Steps to reproduce
- Expected behavior
- Actual behavior
- Screenshots or logs if applicable
- Information from debug mode if relevant
Use the [Bug Report template](.github/ISSUE_TEMPLATE/bug_report.md) when creating an issue.
## 💡 Suggesting Features
We use GitHub Discussions for feature requests rather than issues. This keeps issues focused on bugs and actionable tasks while allowing more open-ended conversations about features.
### Before Suggesting a Feature
1. **Search existing discussions** at [github.com/cjpais/Handy/discussions](https://github.com/cjpais/Handy/discussions)
2. **Check common feature requests**:
- [Post-processing / Editing Transcripts](https://github.com/cjpais/Handy/discussions/168)
- [Keyboard Shortcuts / Hotkeys](https://github.com/cjpais/Handy/discussions/211)
### Submitting a Feature Request
1. Go to [Discussions](https://github.com/cjpais/Handy/discussions)
2. Click "New discussion"
3. Choose the appropriate category (Ideas, Feature Requests, etc.)
4. Describe your feature idea including:
- The problem you're trying to solve
- Your proposed solution
- Any alternatives you've considered
- How it fits with Handy's philosophy
## 🔧 Making Code Contributions
### Before You Start
**This is critical:** Before writing any code, please do the following:
1. **Search existing issues and PRs** - Check both open AND closed issues and pull requests. Someone may have already addressed this, or there may be a reason it was closed.
- [Open issues](https://github.com/cjpais/Handy/issues)
- [Closed issues](https://github.com/cjpais/Handy/issues?q=is%3Aissue+is%3Aclosed)
- [Open PRs](https://github.com/cjpais/Handy/pulls)
- [Closed PRs](https://github.com/cjpais/Handy/pulls?q=is%3Apr+is%3Aclosed)
2. **If something was previously closed** - If you want to revisit a closed issue or PR, you need to:
- Provide a strong argument for why it should be reconsidered
- Gather community feedback first via [Discussions](https://github.com/cjpais/Handy/discussions)
- Link to that discussion in your PR
3. **Get community feedback for features** - PRs with demonstrated community interest are **much more likely to be merged**. Start a discussion, get feedback, and link to it in your PR. This helps ensure Handy stays focused and useful for the most people without becoming bloated.
Community feedback is essential to keeping Handy the best it can be for everyone. It helps prioritize what matters most and prevents feature creep.
### Development Workflow
1. **Create a feature branch**:
```bash
git checkout -b feature/your-feature-name
# or
git checkout -b fix/your-bug-fix
```
2. **Make your changes**:
- Write clean, maintainable code
- Follow existing code style and patterns
- Add comments for complex logic
- Keep commits focused and atomic
3. **Test thoroughly**:
- Test on your target platform(s)
- Verify existing functionality still works
- Test edge cases and error conditions
- Use debug mode to verify audio/transcription behavior
4. **Commit your changes**:
```bash
git add .
git commit -m "feat: add your feature description"
# or
git commit -m "fix: describe the bug fix"
```
Use conventional commit messages:
- `feat:` for new features
- `fix:` for bug fixes
- `docs:` for documentation changes
- `refactor:` for code refactoring
- `test:` for test additions/changes
- `chore:` for maintenance tasks
5. **Keep your fork updated**:
```bash
git fetch upstream
git rebase upstream/main
```
6. **Push to your fork**:
```bash
git push origin feature/your-feature-name
```
7. **Create a Pull Request**:
- Go to the [Handy repository](https://github.com/cjpais/Handy)
- Click "New Pull Request"
- Select your fork and branch
- Fill out the PR template completely, including:
- Clear description of changes
- Links to related issues or discussions
- **Community feedback** (especially important for features)
- How you tested the changes
- Screenshots/videos if applicable
- Breaking changes (if any)
**Remember:** PRs with community support are prioritized. If you haven't already, start a [discussion](https://github.com/cjpais/Handy/discussions) to gather feedback before or alongside your PR. It is not explicitly required to gather feedback, but it certainly helps your PR get merged faster.
### AI Assistance Disclosure
**AI-assisted PRs are welcome!** Use whatever tools help you contribute, just be upfront about it.
In your PR description, please include:
- Whether AI was used (yes/no)
- Which tools were used (e.g., "Claude Code", "GitHub Copilot", "ChatGPT")
- How extensively it was used (e.g., "generated boilerplate", "helped debug", "wrote most of the code")
### Code Style Guidelines
**Rust:**
- Follow standard Rust formatting (`cargo fmt`)
- Run `cargo clippy` and address warnings
- Use descriptive variable and function names
- Add doc comments for public APIs
- Handle errors explicitly (avoid unwrap in production code)
**TypeScript/React:**
- Use TypeScript strictly, avoid `any` types
- Follow React hooks best practices
- Use functional components
- Keep components small and focused
- Use Tailwind CSS for styling
**General:**
- Write self-documenting code
- Add comments for non-obvious logic
- Keep functions small and single-purpose
- Prioritize readability over cleverness
### Testing Your Changes
**Manual Testing:**
- Run the app in development mode: `bun run tauri dev`
- Test your changes with debug mode enabled
- Verify on multiple platforms if possible
- Test with different audio devices
- Try various transcription scenarios
**Building for Production:**
```bash
bun run tauri build
```
Test the production build to ensure it works as expected.
## 📝 Documentation Contributions
Documentation improvements are highly valued! You can contribute by:
- Improving README.md, BUILD.md, or this CONTRIBUTING.md
- Adding code comments and doc comments
- Creating tutorials or guides
- Improving error messages
- Updating the project website content
## 🤝 Community Guidelines
- **Be respectful and inclusive** - We welcome contributors of all skill levels
- **Be patient** - This is maintained by a small team, responses may take time
- **Be constructive** - Focus on solutions and improvements
- **Be collaborative** - Help others and share knowledge
- **Search first** - Check existing issues/discussions before creating new ones
## 🎯 Good First Issues
Look for issues labeled `good first issue` or `help wanted` if you're new to the project. These are typically:
- Well-defined and scoped
- Good for learning the codebase
- Mentor support available
## 📞 Getting Help
- **Discord**: Join our [Discord community](https://discord.com/invite/WVBeWsNXK4)
- **Discussions**: Ask questions in [GitHub Discussions](https://github.com/cjpais/Handy/discussions)
- **Email**: Reach out at [contact@handy.computer](mailto:contact@handy.computer)
## 📜 License
By contributing to Handy, you agree that your contributions will be licensed under the MIT License. See [LICENSE](LICENSE) for details.
---
**Thank you for contributing to Handy!** Your efforts help make speech-to-text technology more accessible, private, and extensible for everyone.
================================================
FILE: CONTRIBUTING_TRANSLATIONS.md
================================================
# Contributing Translations to Handy
Thank you for helping translate Handy! This guide explains how to add or improve translations.
## Quick Start
1. Fork the repository
2. Copy the English translation file to your language folder
3. Translate the values (not the keys!)
4. Submit a pull request
## File Structure
Translation files are located in:
```
src/i18n/locales/
├── en/
│ └── translation.json # English (source)
├── vi/
│ └── translation.json # Vietnamese
├── fr/
│ └── translation.json # French
└── [your-language]/
└── translation.json # Your contribution!
```
## Adding a New Language
### Step 1: Create the Language Folder
Create a new folder using the [ISO 639-1 language code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes):
```bash
mkdir src/i18n/locales/[language-code]
```
Examples:
- `de` for German
- `es` for Spanish
- `ja` for Japanese
- `zh` for Chinese
- `ko` for Korean
- `pt` for Portuguese
### Step 2: Copy the English File
```bash
cp src/i18n/locales/en/translation.json src/i18n/locales/[language-code]/translation.json
```
### Step 3: Translate the Values
Open the file and translate only the **values** (right side), not the keys (left side):
```json
{
"sidebar": {
"general": "General", // ← Translate this value
"advanced": "Advanced", // ← Translate this value
...
}
}
```
**Important:**
- Keep all keys exactly the same
- Preserve any `{{variables}}` in the text (e.g., `{{error}}`, `{{model}}`)
- Keep the JSON structure and formatting intact
### Step 4: Register Your Language
Edit `src/i18n/languages.ts` and add your language metadata:
```typescript
export const LANGUAGE_METADATA: Record<
string,
{ name: string; nativeName: string }
> = {
en: { name: "English", nativeName: "English" },
es: { name: "Spanish", nativeName: "Español" },
fr: { name: "French", nativeName: "Français" },
vi: { name: "Vietnamese", nativeName: "Tiếng Việt" },
de: { name: "German", nativeName: "Deutsch" }, // ← Add your language
};
```
### Step 5: Test Your Translation
1. Run the app: `bun run tauri dev`
2. Go to Settings → General → App Language
3. Select your language
4. Verify all text displays correctly
### Step 6: Submit a Pull Request
1. Commit your changes
2. Push to your fork
3. Open a pull request with:
- Language name in the title (e.g., "Add German translation")
- Any notes about the translation
## Improving Existing Translations
Found a typo or better translation?
1. Edit the relevant `translation.json` file
2. Submit a PR with a brief description of the change
## Translation Guidelines
### Do:
- Use natural, native-sounding language
- Keep translations concise (UI space is limited)
- Match the tone of the English text (friendly, clear)
- Preserve technical terms when appropriate (e.g., "API", "GPU")
### Don't:
- Translate brand names (Handy, Whisper.cpp, OpenAI)
- Change or remove `{{variables}}`
- Modify JSON keys
- Add extra spaces or formatting
### Handling Variables
Some strings contain variables like `{{error}}` or `{{model}}`. Keep these exactly as-is:
```json
// English
"downloadModel": "Failed to download model: {{error}}"
// French (correct)
"downloadModel": "Échec du téléchargement du modèle : {{error}}"
// French (incorrect - don't translate the variable!)
"downloadModel": "Échec du téléchargement du modèle : {{erreur}}"
```
### Handling Plurals
Some languages have complex plural rules. For now, use a general form that works for all cases. We may add proper plural support in the future.
## Questions?
- Open an issue on GitHub
- Join the discussion in existing translation PRs
## Currently Supported Languages
| Language | Code | Status |
| ---------- | ---- | ----------------- |
| English | `en` | Complete (source) |
| Chinese | `zh` | Complete |
| French | `fr` | Complete |
| German | `de` | Complete |
| Japanese | `ja` | Complete |
| Spanish | `es` | Complete |
| Vietnamese | `vi` | Complete |
## Requested Languages
We'd love help with:
- Korean (`ko`)
- Portuguese (`pt`)
- And more!
---
Thank you for making Handy accessible to more people around the world!
================================================
FILE: CRUSH.md
================================================
# Development Commands
**Environment Setup:**
```bash
bun install # Install dependencies
mkdir -p src-tauri/resources/models
curl -o src-tauri/resources/models/silero_vad_v4.onnx https://blob.handy.computer/silero_vad_v4.onnx
```
**Development:**
```bash
bun run tauri dev # Full app development
CMAKE_POLICY_VERSION_MINIMUM=3.5 bun run tauri dev # macOS with cmake fix
bun run dev # Frontend only (Vite)
bun run build # Build frontend
bun run tauri build # Production build
```
**Type Check & Build:**
```bash
bunx tsc --noEmit # Type checking
bun run build # Build and validate
```
# Code Style Guidelines
**Rust (Backend):**
- Use `anyhow::Error` for error handling with descriptive messages
- Prefer `Arc<Mutex<T>>` for shared state in managers
- Log with appropriate levels: `debug!`, `info!`, `eprintln!` for errors
- Builder pattern for initialization chains
- Snake_case for functions and variables, PascalCase for types
- Separate logical sections with comment blocks: `/* ─────────── */`
**TypeScript/React (Frontend):**
- Functional components with TypeScript interfaces
- Zod schemas for type validation and inference
- `useCallback` hooks for stable function references
- Destructure props with defaults: `disabled = false`
- Prefer interface aliases over type aliases for objects
- React.FC for explicit component typing
- PascalCase for components, camelCase for variables/functions
**Imports:**
- Group imports: external libs, internal modules, relative imports
- Use type imports for TypeScript: `import type { Settings }`
- Named imports preferred over default exports
**Error Handling:**
- Frontend: Try/catch with user feedback, rollback optimistic updates
- Backend: `?` operator with anyhow context messages
- Log errors appropriately for debugging level
**Component Patterns:**
- Container component pattern for layout
- Composition over inheritance
- Prop drilling minimized with context where appropriate
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2025 CJ Pais
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# Handy
[](https://discord.com/invite/WVBeWsNXK4)
**A free, open source, and extensible speech-to-text application that works completely offline.**
Handy is a cross-platform desktop application that provides simple, privacy-focused speech transcription. Press a shortcut, speak, and have your words appear in any text field. This happens on your own computer without sending any information to the cloud.
## Why Handy?
Handy was created to fill the gap for a truly open source, extensible speech-to-text tool. As stated on [handy.computer](https://handy.computer):
- **Free**: Accessibility tooling belongs in everyone's hands, not behind a paywall
- **Open Source**: Together we can build further. Extend Handy for yourself and contribute to something bigger
- **Private**: Your voice stays on your computer. Get transcriptions without sending audio to the cloud
- **Simple**: One tool, one job. Transcribe what you say and put it into a text box
Handy isn't trying to be the best speech-to-text app—it's trying to be the most forkable one.
## How It Works
1. **Press** a configurable keyboard shortcut to start/stop recording (or use push-to-talk mode)
2. **Speak** your words while the shortcut is active
3. **Release** and Handy processes your speech using Whisper
4. **Get** your transcribed text pasted directly into whatever app you're using
The process is entirely local:
- Silence is filtered using VAD (Voice Activity Detection) with Silero
- Transcription uses your choice of models:
- **Whisper models** (Small/Medium/Turbo/Large) with GPU acceleration when available
- **Parakeet V3** - CPU-optimized model with excellent performance and automatic language detection
- Works on Windows, macOS, and Linux
## Quick Start
### Installation
1. Download the latest release from the [releases page](https://github.com/cjpais/Handy/releases) or the [website](https://handy.computer)
- **macOS**: Also available via [Homebrew cask](https://formulae.brew.sh/cask/handy): `brew install --cask handy`
- **Windows**: Also available via [winget](https://github.com/microsoft/winget-pkgs): `winget install cjpais.Handy` \
**Note:** The Homebrew cask and winget package are not maintained by the Handy developers.
2. Install the application
3. Launch Handy and grant necessary system permissions (microphone, accessibility)
4. Configure your preferred keyboard shortcuts in Settings
5. Start transcribing!
### Development Setup
For detailed build instructions including platform-specific requirements, see [BUILD.md](BUILD.md).
## Integrations
<a href="https://www.raycast.com/mattiacolombomc/handy" title="Install Handy Raycast Extension"><img src="https://www.raycast.com/mattiacolombomc/handy/install_button@2x.png?v=1.1" height="64" style="height: 64px;" alt="Install handy Raycast Extension" /></a>
Control Handy from [Raycast](https://www.raycast.com) — start/stop recording, browse transcript history, manage dictionary, switch models and languages.
[Source](https://github.com/mattiacolombomc/raycast-handy) · by [@mattiacolombomc](https://github.com/mattiacolombomc)
## Architecture
Handy is built as a Tauri application combining:
- **Frontend**: React + TypeScript with Tailwind CSS for the settings UI
- **Backend**: Rust for system integration, audio processing, and ML inference
- **Core Libraries**:
- `whisper-rs`: Local speech recognition with Whisper models
- `transcription-rs`: CPU-optimized speech recognition with Parakeet models
- `cpal`: Cross-platform audio I/O
- `vad-rs`: Voice Activity Detection
- `rdev`: Global keyboard shortcuts and system events
- `rubato`: Audio resampling
### Debug Mode
Handy includes an advanced debug mode for development and troubleshooting. Access it by pressing:
- **macOS**: `Cmd+Shift+D`
- **Windows/Linux**: `Ctrl+Shift+D`
### CLI Parameters
Handy supports command-line flags for controlling a running instance and customizing startup behavior. These work on all platforms (macOS, Windows, Linux).
**Remote control flags** (sent to an already-running instance via the single-instance plugin):
```bash
handy --toggle-transcription # Toggle recording on/off
handy --toggle-post-process # Toggle recording with post-processing on/off
handy --cancel # Cancel the current operation
```
**Startup flags:**
```bash
handy --start-hidden # Start without showing the main window
handy --no-tray # Start without the system tray icon
handy --debug # Enable debug mode with verbose logging
handy --help # Show all available flags
```
Flags can be combined for autostart scenarios:
```bash
handy --start-hidden --no-tray
```
> **macOS tip:** When Handy is installed as an app bundle, invoke the binary directly:
>
> ```bash
> /Applications/Handy.app/Contents/MacOS/Handy --toggle-transcription
> ```
## Known Issues & Current Limitations
This project is actively being developed and has some [known issues](https://github.com/cjpais/Handy/issues). We believe in transparency about the current state:
### Major Issues (Help Wanted)
**Whisper Model Crashes:**
- Whisper models crash on certain system configurations (Windows and Linux)
- Does not affect all systems - issue is configuration-dependent
- If you experience crashes and are a developer, please help to fix and provide debug logs!
**Wayland Support (Linux):**
- Limited support for Wayland display server
- Requires [`wtype`](https://github.com/atx/wtype) or [`dotool`](https://sr.ht/~geb/dotool/) for text input to work correctly (see [Linux Notes](#linux-notes) below for installation)
### Linux Notes
**Text Input Tools:**
For reliable text input on Linux, install the appropriate tool for your display server:
| Display Server | Recommended Tool | Install Command |
| -------------- | ---------------- | -------------------------------------------------- |
| X11 | `xdotool` | `sudo apt install xdotool` |
| Wayland | `wtype` | `sudo apt install wtype` |
| Both | `dotool` | `sudo apt install dotool` (requires `input` group) |
- **X11**: Install `xdotool` for both direct typing and clipboard paste shortcuts
- **Wayland**: Install `wtype` (preferred) or `dotool` for text input to work correctly
- **dotool setup**: Requires adding your user to the `input` group: `sudo usermod -aG input $USER` (then log out and back in)
Without these tools, Handy falls back to enigo which may have limited compatibility, especially on Wayland.
**Other Notes:**
- **Runtime library dependency (`libgtk-layer-shell.so.0`)**:
- Handy links `gtk-layer-shell` on Linux. If startup fails with `error while loading shared libraries: libgtk-layer-shell.so.0`, install the runtime package for your distro:
| Distro | Package to install | Example command |
| ------------- | --------------------- | -------------------------------------- |
| Ubuntu/Debian | `libgtk-layer-shell0` | `sudo apt install libgtk-layer-shell0` |
| Fedora/RHEL | `gtk-layer-shell` | `sudo dnf install gtk-layer-shell` |
| Arch Linux | `gtk-layer-shell` | `sudo pacman -S gtk-layer-shell` |
- For building from source on Ubuntu/Debian, you may also need `libgtk-layer-shell-dev`.
- The recording overlay is disabled by default on Linux (`Overlay Position: None`) because certain compositors treat it as the active window. When the overlay is visible it can steal focus, which prevents Handy from pasting back into the application that triggered transcription. If you enable the overlay anyway, be aware that clipboard-based pasting might fail or end up in the wrong window.
- If you are having trouble with the app, running with the environment variable `WEBKIT_DISABLE_DMABUF_RENDERER=1` may help
- **Global keyboard shortcuts (Wayland):** On Wayland, system-level shortcuts must be configured through your desktop environment or window manager. Use the [CLI flags](#cli-parameters) as the command for your custom shortcut.
**GNOME:**
1. Open **Settings > Keyboard > Keyboard Shortcuts > Custom Shortcuts**
2. Click the **+** button to add a new shortcut
3. Set the **Name** to `Toggle Handy Transcription`
4. Set the **Command** to `handy --toggle-transcription`
5. Click **Set Shortcut** and press your desired key combination (e.g., `Super+O`)
**KDE Plasma:**
1. Open **System Settings > Shortcuts > Custom Shortcuts**
2. Click **Edit > New > Global Shortcut > Command/URL**
3. Name it `Toggle Handy Transcription`
4. In the **Trigger** tab, set your desired key combination
5. In the **Action** tab, set the command to `handy --toggle-transcription`
**Sway / i3:**
Add to your config file (`~/.config/sway/config` or `~/.config/i3/config`):
```ini
bindsym $mod+o exec handy --toggle-transcription
```
**Hyprland:**
Add to your config file (`~/.config/hypr/hyprland.conf`):
```ini
bind = $mainMod, O, exec, handy --toggle-transcription
```
- You can also manage global shortcuts outside of Handy via Unix signals, which lets Wayland window managers or other hotkey daemons keep ownership of keybindings:
| Signal | Action | Example |
| --------- | ----------------------------------------- | ---------------------- |
| `SIGUSR2` | Toggle transcription | `pkill -USR2 -n handy` |
| `SIGUSR1` | Toggle transcription with post-processing | `pkill -USR1 -n handy` |
Example Sway config:
```ini
bindsym $mod+o exec pkill -USR2 -n handy
bindsym $mod+p exec pkill -USR1 -n handy
```
`pkill` here simply delivers the signal—it does not terminate the process.
### Platform Support
- **macOS (both Intel and Apple Silicon)**
- **x64 Windows**
- **x64 Linux**
### System Requirements/Recommendations
The following are recommendations for running Handy on your own machine. If you don't meet the system requirements, the performance of the application may be degraded. We are working on improving the performance across all kinds of computers and hardware.
**For Whisper Models:**
- **macOS**: M series Mac, Intel Mac
- **Windows**: Intel, AMD, or NVIDIA GPU
- **Linux**: Intel, AMD, or NVIDIA GPU
- Ubuntu 22.04, 24.04
**For Parakeet V3 Model:**
- **CPU-only operation** - runs on a wide variety of hardware
- **Minimum**: Intel Skylake (6th gen) or equivalent AMD processors
- **Performance**: ~5x real-time speed on mid-range hardware (tested on i5)
- **Automatic language detection** - no manual language selection required
## Roadmap & Active Development
We're actively working on several features and improvements. Contributions and feedback are welcome!
### In Progress
**Debug Logging:**
- Adding debug logging to a file to help diagnose issues
**macOS Keyboard Improvements:**
- Support for Globe key as transcription trigger
- A rewrite of global shortcut handling for MacOS, and potentially other OS's too.
**Opt-in Analytics:**
- Collect anonymous usage data to help improve Handy
- Privacy-first approach with clear opt-in
**Settings Refactoring:**
- Cleanup and refactor settings system which is becoming bloated and messy
- Implement better abstractions for settings management
**Tauri Commands Cleanup:**
- Abstract and organize Tauri command patterns
- Investigate tauri-specta for improved type safety and organization
## Troubleshooting
### Manual Model Installation (For Proxy Users or Network Restrictions)
If you're behind a proxy, firewall, or in a restricted network environment where Handy cannot download models automatically, you can manually download and install them. The URLs are publicly accessible from any browser.
#### Step 1: Find Your App Data Directory
1. Open Handy settings
2. Navigate to the **About** section
3. Copy the "App Data Directory" path shown there, or use the shortcuts:
- **macOS**: `Cmd+Shift+D` to open debug menu
- **Windows/Linux**: `Ctrl+Shift+D` to open debug menu
The typical paths are:
- **macOS**: `~/Library/Application Support/com.pais.handy/`
- **Windows**: `C:\Users\{username}\AppData\Roaming\com.pais.handy\`
- **Linux**: `~/.config/com.pais.handy/`
#### Step 2: Create Models Directory
Inside your app data directory, create a `models` folder if it doesn't already exist:
```bash
# macOS/Linux
mkdir -p ~/Library/Application\ Support/com.pais.handy/models
# Windows (PowerShell)
New-Item -ItemType Directory -Force -Path "$env:APPDATA\com.pais.handy\models"
```
#### Step 3: Download Model Files
Download the models you want from below
**Whisper Models (single .bin files):**
- Small (487 MB): `https://blob.handy.computer/ggml-small.bin`
- Medium (492 MB): `https://blob.handy.computer/whisper-medium-q4_1.bin`
- Turbo (1600 MB): `https://blob.handy.computer/ggml-large-v3-turbo.bin`
- Large (1100 MB): `https://blob.handy.computer/ggml-large-v3-q5_0.bin`
**Parakeet Models (compressed archives):**
- V2 (473 MB): `https://blob.handy.computer/parakeet-v2-int8.tar.gz`
- V3 (478 MB): `https://blob.handy.computer/parakeet-v3-int8.tar.gz`
#### Step 4: Install Models
**For Whisper Models (.bin files):**
Simply place the `.bin` file directly into the `models` directory:
```
{app_data_dir}/models/
├── ggml-small.bin
├── whisper-medium-q4_1.bin
├── ggml-large-v3-turbo.bin
└── ggml-large-v3-q5_0.bin
```
**For Parakeet Models (.tar.gz archives):**
1. Extract the `.tar.gz` file
2. Place the **extracted directory** into the `models` folder
3. The directory must be named exactly as follows:
- **Parakeet V2**: `parakeet-tdt-0.6b-v2-int8`
- **Parakeet V3**: `parakeet-tdt-0.6b-v3-int8`
Final structure should look like:
```
{app_data_dir}/models/
├── parakeet-tdt-0.6b-v2-int8/ (directory with model files inside)
│ ├── (model files)
│ └── (config files)
└── parakeet-tdt-0.6b-v3-int8/ (directory with model files inside)
├── (model files)
└── (config files)
```
**Important Notes:**
- For Parakeet models, the extracted directory name **must** match exactly as shown above
- Do not rename the `.bin` files for Whisper models—use the exact filenames from the download URLs
- After placing the files, restart Handy to detect the new models
#### Step 5: Verify Installation
1. Restart Handy
2. Open Settings → Models
3. Your manually installed models should now appear as "Downloaded"
4. Select the model you want to use and test transcription
### Custom Whisper Models
Handy can auto-discover custom Whisper GGML models placed in the `models` directory. This is useful for users who want to use fine-tuned or community models not included in the default model list.
**How to use:**
1. Obtain a Whisper model in GGML `.bin` format (e.g., from [Hugging Face](https://huggingface.co/models?search=whisper%20ggml))
2. Place the `.bin` file in your `models` directory (see paths above)
3. Restart Handy to discover the new model
4. The model will appear in the "Custom Models" section of the Models settings page
**Important:**
- Community models are user-provided and may not receive troubleshooting assistance
- The model must be a valid Whisper GGML format (`.bin` file)
- Model name is derived from the filename (e.g., `my-custom-model.bin` → "My Custom Model")
### How to Contribute
1. **Check existing issues** at [github.com/cjpais/Handy/issues](https://github.com/cjpais/Handy/issues)
2. **Fork the repository** and create a feature branch
3. **Test thoroughly** on your target platform
4. **Submit a pull request** with clear description of changes
5. **Join the discussion** - reach out at [contact@handy.computer](mailto:contact@handy.computer)
The goal is to create both a useful tool and a foundation for others to build upon—a well-patterned, simple codebase that serves the community.
## Sponsors
<div align="center">
We're grateful for the support of our sponsors who help make Handy possible:
<br><br>
<a href="https://wordcab.com">
<img src="sponsor-images/wordcab.png" alt="Wordcab" width="120" height="120">
</a>
<a href="https://github.com/epicenter-so/epicenter">
<img src="sponsor-images/epicenter.png" alt="Epicenter" width="120" height="120">
</a>
<a href="https://boltai.com?utm_source=handy">
<img src="sponsor-images/boltai.jpg" alt="Bolt AI" width="120" height="120">
</a>
</div>
## Related Projects
- **[Handy CLI](https://github.com/cjpais/handy-cli)** - The original Python command-line version
- **[handy.computer](https://handy.computer)** - Project website with demos and documentation
## License
MIT License - see [LICENSE](LICENSE) file for details.
## Acknowledgments
- **Whisper** by OpenAI for the speech recognition model
- **whisper.cpp and ggml** for amazing cross-platform whisper inference/acceleration
- **Silero** for great lightweight VAD
- **Tauri** team for the excellent Rust-based app framework
- **Community contributors** helping make Handy better
================================================
FILE: eslint.config.js
================================================
import i18next from "eslint-plugin-i18next";
import tsParser from "@typescript-eslint/parser";
export default [
{
files: ["src/**/*.{ts,tsx}"],
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
},
plugins: {
i18next,
},
rules: {
// Catch text in JSX that should be translated
"i18next/no-literal-string": [
"error",
{
markupOnly: true, // Only check JSX content, not all strings
ignoreAttribute: [
"className",
"style",
"type",
"id",
"name",
"key",
"data-*",
"aria-*",
], // Ignore common non-translatable attributes
},
],
},
},
];
================================================
FILE: flake.nix
================================================
{
description = "Handy - A free, open source, and extensible speech-to-text application that works completely offline";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
# bun2nix: generates per-package Nix fetchurl expressions from bun.lock,
# replacing the old FOD approach where a single hash covered the entire
# node_modules directory (that hash would break on bun version changes).
# See: https://github.com/nix-community/bun2nix
bun2nix = {
url = "github:nix-community/bun2nix/2.0.8";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs =
{
self,
nixpkgs,
bun2nix,
}:
let
supportedSystems = [
"x86_64-linux"
"aarch64-linux"
];
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
# Read version from Cargo.toml
cargoToml = fromTOML (builtins.readFile ./src-tauri/Cargo.toml);
version = cargoToml.package.version;
# Shared native library dependencies for both package build and dev shell.
# Keep in sync: if a native dep is needed for compilation, add it here.
commonNativeDeps = pkgs: with pkgs; [
webkitgtk_4_1
gtk3
glib
libsoup_3
alsa-lib
onnxruntime
libayatana-appindicator
libevdev
libxtst
gtk-layer-shell
openssl
vulkan-loader
vulkan-headers
shaderc
];
# GStreamer plugins for WebKitGTK audio/video
gstPlugins = pkgs: with pkgs.gst_all_1; [
gstreamer
gst-plugins-base
gst-plugins-good
gst-plugins-bad
gst-plugins-ugly
];
# Shared environment variables for Rust/native builds
commonEnv = pkgs: let lib = pkgs.lib; in {
LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
BINDGEN_EXTRA_CLANG_ARGS = "-isystem ${pkgs.llvmPackages.libclang.lib}/lib/clang/${lib.getVersion pkgs.llvmPackages.libclang}/include -isystem ${pkgs.glibc.dev}/include";
ORT_LIB_LOCATION = "${pkgs.onnxruntime}/lib";
ORT_PREFER_DYNAMIC_LINK = "1";
GST_PLUGIN_SYSTEM_PATH_1_0 = "${lib.makeSearchPathOutput "lib" "lib/gstreamer-1.0" (gstPlugins pkgs)}";
};
# TODO: Remove this overlay once nixpkgs ships onnxruntime ≥ 1.24.
# Tracking PR: https://github.com/NixOS/nixpkgs/pull/499389
# ort-sys 2.0.0-rc.12 requires ONNX Runtime 1.24 (API v24);
# nixpkgs only ships 1.23.2, so use MS prebuilt binaries.
onnxruntimeOverlay = (final: prev: {
onnxruntime = let
onnxVersion = "1.24.2";
platform = {
x86_64-linux = { name = "linux-x64"; hash = "sha256-Q3JUdLpWY2QuF2hHF5Rmk4UOIAXvvXJKxy2ieP6tJeY="; };
aarch64-linux = { name = "linux-aarch64"; hash = "sha256-spla8PQ3xOAi/YAcV/tcJf0f5mDNM9JutHGUSQpbRsQ="; };
}.${final.system};
in prev.stdenv.mkDerivation {
pname = "onnxruntime";
version = onnxVersion;
src = prev.fetchurl {
url = "https://github.com/microsoft/onnxruntime/releases/download/v${onnxVersion}/onnxruntime-${platform.name}-${onnxVersion}.tgz";
hash = platform.hash;
};
sourceRoot = "onnxruntime-${platform.name}-${onnxVersion}";
nativeBuildInputs = [ prev.autoPatchelfHook ];
buildInputs = [ prev.stdenv.cc.cc.lib ];
installPhase = ''
runHook preInstall
mkdir -p $out/lib $out/include
cp -r lib/* $out/lib/
cp -r include/* $out/include/
runHook postInstall
'';
meta = prev.onnxruntime.meta // {
description = "ONNX Runtime ${onnxVersion} (prebuilt by Microsoft)";
};
};
});
in
{
packages = forAllSystems (
system:
let
pkgs = import nixpkgs {
inherit system;
overlays = [
bun2nix.overlays.default
onnxruntimeOverlay
];
};
lib = pkgs.lib;
in
{
handy = pkgs.rustPlatform.buildRustPackage {
pname = "handy";
inherit version;
src = self;
cargoRoot = "src-tauri";
cargoLock = {
lockFile = ./src-tauri/Cargo.lock;
# Automatically fetch git dependencies using builtins.fetchGit.
# This eliminates the need for manual outputHashes that had to be
# updated every time a git dependency changed in Cargo.lock.
# Safe for standalone flakes (not allowed in nixpkgs, it is needed something like crate2nix).
allowBuiltinFetchGit = true;
};
postPatch = ''
${pkgs.jq}/bin/jq 'del(.build.beforeBuildCommand) | .bundle.createUpdaterArtifacts = false' \
src-tauri/tauri.conf.json > $TMPDIR/tauri.conf.json
cp $TMPDIR/tauri.conf.json src-tauri/tauri.conf.json
# Strip postinstall hook — it runs check-nix-deps.ts which is only
# needed during local development, not inside the Nix sandbox.
${pkgs.jq}/bin/jq 'del(.scripts.postinstall)' \
package.json > $TMPDIR/package.json
cp $TMPDIR/package.json package.json
# Point libappindicator-sys to the Nix store path
substituteInPlace \
$cargoDepsCopy/libappindicator-sys-*/src/lib.rs \
--replace-fail \
"libayatana-appindicator3.so.1" \
"${pkgs.libayatana-appindicator}/lib/libayatana-appindicator3.so.1"
# Disable cbindgen in ferrous-opencc (calls cargo metadata which fails in sandbox)
# Upstream removed this call in v0.3.1+
substituteInPlace $cargoDepsCopy/ferrous-opencc-0.2.3/build.rs \
--replace-fail '.expect("Unable to generate bindings")' '.ok();'
substituteInPlace $cargoDepsCopy/ferrous-opencc-0.2.3/build.rs \
--replace-fail '.write_to_file("opencc.h");' '// skipped'
'';
# Bun dependencies: fetched per-package using hashes from .nix/bun.nix.
# This file is auto-generated by `bunx bun2nix -o .nix/bun.nix` and
# kept in sync via the postinstall hook in package.json.
# To regenerate manually: bun scripts/check-nix-deps.ts
bunDeps = pkgs.bun2nix.fetchBunDeps {
bunNix = ./.nix/bun.nix;
};
nativeBuildInputs = with pkgs; [
cargo-tauri.hook
pkg-config
wrapGAppsHook4
bun
# pkgs.bun2nix (from overlay), not the flake input — `with pkgs;`
# doesn't shadow function arguments in Nix.
pkgs.bun2nix.hook # Sets up node_modules from pre-fetched bun cache
jq
cmake
llvmPackages.libclang
shaderc
];
preBuild = ''
# bun2nix.hook has already set up node_modules from pre-fetched cache.
# Build the frontend with bun (tsc + vite).
export HOME=$TMPDIR
bun run build
'';
# Tests require runtime resources (audio devices, model files, GPU/Vulkan)
# not available in the Nix build sandbox
doCheck = false;
# The tauri hook's installPhase expects target/ in cwd, but our
# cargoRoot puts it under src-tauri/. Override to extract the DEB.
installPhase = ''
runHook preInstall
mkdir -p $out
cd src-tauri
mv target/${pkgs.stdenv.hostPlatform.rust.rustcTarget}/release/bundle/deb/*/data/usr/* $out/
runHook postInstall
'';
buildInputs = commonNativeDeps pkgs ++ (with pkgs; [
glib-networking
libx11
]) ++ gstPlugins pkgs;
env = commonEnv pkgs // {
OPENSSL_NO_VENDOR = "1";
};
preFixup = ''
gappsWrapperArgs+=(
--set WEBKIT_DISABLE_DMABUF_RENDERER 1
--set ALSA_PLUGIN_DIR "${pkgs.pipewire}/lib/alsa-lib:${pkgs.alsa-plugins}/lib/alsa-lib"
--prefix LD_LIBRARY_PATH : "${
lib.makeLibraryPath [
pkgs.vulkan-loader
pkgs.onnxruntime
]
}"
)
'';
meta = {
description = "A free, open source, and extensible speech-to-text application that works completely offline";
homepage = "https://github.com/cjpais/Handy";
license = lib.licenses.mit;
mainProgram = "handy";
platforms = supportedSystems;
};
};
default = self.packages.${system}.handy;
}
);
# NixOS module for system-level integration (udev, input group)
nixosModules.default =
{ lib, pkgs, ... }:
{
imports = [ ./nix/module.nix ];
programs.handy.package = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.handy;
};
# Home-manager module for per-user service
homeManagerModules.default =
{ lib, pkgs, ... }:
{
imports = [ ./nix/hm-module.nix ];
services.handy.package = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.handy;
};
# Development shell for building from source
devShells = forAllSystems (
system:
let
pkgs = import nixpkgs {
inherit system;
overlays = [ onnxruntimeOverlay ];
};
in
{
default = pkgs.mkShell {
buildInputs = commonNativeDeps pkgs ++ (with pkgs; [
# Rust toolchain
rustc
cargo
rust-analyzer
clippy
# Frontend
nodejs
bun
# Build tools
cargo-tauri
pkg-config
llvmPackages.libclang
cmake
]);
inherit (commonEnv pkgs)
LIBCLANG_PATH
BINDGEN_EXTRA_CLANG_ARGS
ORT_LIB_LOCATION
ORT_PREFER_DYNAMIC_LINK
GST_PLUGIN_SYSTEM_PATH_1_0;
LD_LIBRARY_PATH = "${pkgs.lib.makeLibraryPath [ pkgs.libayatana-appindicator pkgs.onnxruntime pkgs.vulkan-loader ]}";
# Same as wrapGAppsHook4
XDG_DATA_DIRS = "${pkgs.gsettings-desktop-schemas}/share/gsettings-schemas/${pkgs.gsettings-desktop-schemas.name}:${pkgs.gtk3}/share/gsettings-schemas/${pkgs.gtk3.name}:${pkgs.hicolor-icon-theme}/share";
shellHook = ''
echo "Handy development environment"
bun install
echo "Run 'bun run tauri dev' to start"
'';
};
}
);
};
}
================================================
FILE: index.html
================================================
<!doctype html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>handy</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
================================================
FILE: nix/hm-module.nix
================================================
# Home-manager module for Handy speech-to-text
#
# Provides a systemd user service for autostart.
# Usage: imports = [ handy.homeManagerModules.default ];
# services.handy.enable = true;
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.handy;
in
{
options.services.handy = {
enable = lib.mkEnableOption "Handy speech-to-text user service";
package = lib.mkOption {
type = lib.types.package;
defaultText = lib.literalExpression "handy.packages.\${system}.handy";
description = "The Handy package to use.";
};
};
config = lib.mkIf cfg.enable {
systemd.user.services.handy = {
Unit = {
Description = "Handy speech-to-text";
After = [ "graphical-session.target" ];
PartOf = [ "graphical-session.target" ];
};
Service = {
ExecStart = "${cfg.package}/bin/handy";
Restart = "on-failure";
RestartSec = 5;
};
Install.WantedBy = [ "graphical-session.target" ];
};
};
}
================================================
FILE: nix/module.nix
================================================
# NixOS module for Handy speech-to-text
#
# Handles system-level configuration that the package wrapper cannot:
# - udev rule for /dev/uinput (rdev grab() needs it for virtual input)
#
# Note: users must add themselves to the "input" group for evdev hotkey access.
#
# Usage in your flake:
#
# inputs.handy.url = "github:cjpais/Handy";
#
# nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
# modules = [
# handy.nixosModules.default
# { programs.handy.enable = true; }
# ];
# };
{
config,
lib,
pkgs,
...
}:
let
cfg = config.programs.handy;
in
{
options.programs.handy = {
enable = lib.mkEnableOption "Handy offline speech-to-text";
package = lib.mkOption {
type = lib.types.package;
defaultText = lib.literalExpression "handy.packages.\${system}.handy";
description = "The Handy package to use.";
};
};
config = lib.mkIf cfg.enable {
environment.systemPackages = [ cfg.package ];
# rdev grab() creates virtual input devices via /dev/uinput.
# Default permissions are crw------- root root — open it to the input group.
services.udev.extraRules = ''
KERNEL=="uinput", GROUP="input", MODE="0660"
'';
};
}
================================================
FILE: package.json
=========
gitextract_qhrvhh5a/ ├── .cargo/ │ └── config.toml ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ └── config.yml │ ├── PULL_REQUEST_TEMPLATE.md │ └── workflows/ │ ├── build-test.yml │ ├── build.yml │ ├── code-quality.yml │ ├── main-build.yml │ ├── nix-check.yml │ ├── playwright.yml │ ├── pr-test-build.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── .nix/ │ ├── bun-lock-hash │ └── bun.nix ├── .prettierignore ├── .prettierrc ├── .vscode/ │ └── extensions.json ├── AGENTS.md ├── BUILD.md ├── CHANGELOG.md ├── CLAUDE.md ├── CONTRIBUTING.md ├── CONTRIBUTING_TRANSLATIONS.md ├── CRUSH.md ├── LICENSE ├── README.md ├── eslint.config.js ├── flake.nix ├── index.html ├── nix/ │ ├── hm-module.nix │ └── module.nix ├── package.json ├── playwright.config.ts ├── scripts/ │ ├── check-nix-deps.ts │ └── check-translations.ts ├── src/ │ ├── App.css │ ├── App.tsx │ ├── bindings.ts │ ├── components/ │ │ ├── AccessibilityPermissions.tsx │ │ ├── Sidebar.tsx │ │ ├── footer/ │ │ │ ├── Footer.tsx │ │ │ └── index.ts │ │ ├── icons/ │ │ │ ├── CancelIcon.tsx │ │ │ ├── HandyHand.tsx │ │ │ ├── HandyTextLogo.tsx │ │ │ ├── MicrophoneIcon.tsx │ │ │ ├── ResetIcon.tsx │ │ │ ├── TranscriptionIcon.tsx │ │ │ └── index.ts │ │ ├── model-selector/ │ │ │ ├── DownloadProgressDisplay.tsx │ │ │ ├── ModelDropdown.tsx │ │ │ ├── ModelSelector.tsx │ │ │ ├── ModelStatusButton.tsx │ │ │ └── index.ts │ │ ├── onboarding/ │ │ │ ├── AccessibilityOnboarding.tsx │ │ │ ├── ModelCard.tsx │ │ │ ├── Onboarding.tsx │ │ │ └── index.ts │ │ ├── settings/ │ │ │ ├── AccelerationSelector.tsx │ │ │ ├── AlwaysOnMicrophone.tsx │ │ │ ├── AppDataDirectory.tsx │ │ │ ├── AppLanguageSelector.tsx │ │ │ ├── AppendTrailingSpace.tsx │ │ │ ├── AudioFeedback.tsx │ │ │ ├── AutoSubmit.tsx │ │ │ ├── AutostartToggle.tsx │ │ │ ├── ClamshellMicrophoneSelector.tsx │ │ │ ├── ClipboardHandling.tsx │ │ │ ├── CustomWords.tsx │ │ │ ├── ExperimentalToggle.tsx │ │ │ ├── GlobalShortcutInput.tsx │ │ │ ├── HandyKeysShortcutInput.tsx │ │ │ ├── HistoryLimit.tsx │ │ │ ├── LanguageSelector.tsx │ │ │ ├── LazyStreamClose.tsx │ │ │ ├── MicrophoneSelector.tsx │ │ │ ├── ModelUnloadTimeout.tsx │ │ │ ├── MuteWhileRecording.tsx │ │ │ ├── OutputDeviceSelector.tsx │ │ │ ├── PasteMethod.tsx │ │ │ ├── PostProcessingSettingsApi/ │ │ │ │ ├── ApiKeyField.tsx │ │ │ │ ├── BaseUrlField.tsx │ │ │ │ ├── ModelSelect.tsx │ │ │ │ ├── ProviderSelect.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── types.ts │ │ │ │ └── usePostProcessProviderState.ts │ │ │ ├── PostProcessingSettingsPrompts.tsx │ │ │ ├── PostProcessingToggle.tsx │ │ │ ├── PushToTalk.tsx │ │ │ ├── RecordingRetentionPeriod.tsx │ │ │ ├── ShortcutInput.tsx │ │ │ ├── ShowOverlay.tsx │ │ │ ├── ShowTrayIcon.tsx │ │ │ ├── SoundPicker.tsx │ │ │ ├── StartHidden.tsx │ │ │ ├── TranslateToEnglish.tsx │ │ │ ├── TypingTool.tsx │ │ │ ├── UpdateChecksToggle.tsx │ │ │ ├── VolumeSlider.tsx │ │ │ ├── about/ │ │ │ │ └── AboutSettings.tsx │ │ │ ├── advanced/ │ │ │ │ └── AdvancedSettings.tsx │ │ │ ├── debug/ │ │ │ │ ├── DebugPaths.tsx │ │ │ │ ├── DebugSettings.tsx │ │ │ │ ├── KeyboardImplementationSelector.tsx │ │ │ │ ├── LogDirectory.tsx │ │ │ │ ├── LogLevelSelector.tsx │ │ │ │ ├── PasteDelay.tsx │ │ │ │ ├── RecordingBuffer.tsx │ │ │ │ ├── WordCorrectionThreshold.tsx │ │ │ │ └── index.ts │ │ │ ├── general/ │ │ │ │ ├── GeneralSettings.tsx │ │ │ │ └── ModelSettingsCard.tsx │ │ │ ├── history/ │ │ │ │ └── HistorySettings.tsx │ │ │ ├── index.ts │ │ │ ├── models/ │ │ │ │ ├── ModelsSettings.tsx │ │ │ │ └── index.ts │ │ │ └── post-processing/ │ │ │ └── PostProcessingSettings.tsx │ │ ├── shared/ │ │ │ ├── ProgressBar.tsx │ │ │ └── index.ts │ │ ├── ui/ │ │ │ ├── Alert.tsx │ │ │ ├── AudioPlayer.tsx │ │ │ ├── Badge.tsx │ │ │ ├── Button.tsx │ │ │ ├── Dropdown.tsx │ │ │ ├── Input.tsx │ │ │ ├── PathDisplay.tsx │ │ │ ├── ResetButton.tsx │ │ │ ├── Select.tsx │ │ │ ├── SettingContainer.tsx │ │ │ ├── SettingsGroup.tsx │ │ │ ├── Slider.tsx │ │ │ ├── TextDisplay.tsx │ │ │ ├── Textarea.tsx │ │ │ ├── ToggleSwitch.tsx │ │ │ ├── Tooltip.tsx │ │ │ └── index.ts │ │ └── update-checker/ │ │ ├── UpdateChecker.tsx │ │ └── index.ts │ ├── hooks/ │ │ ├── useOsType.ts │ │ └── useSettings.ts │ ├── i18n/ │ │ ├── index.ts │ │ ├── languages.ts │ │ └── locales/ │ │ ├── ar/ │ │ │ └── translation.json │ │ ├── cs/ │ │ │ └── translation.json │ │ ├── de/ │ │ │ └── translation.json │ │ ├── en/ │ │ │ └── translation.json │ │ ├── es/ │ │ │ └── translation.json │ │ ├── fr/ │ │ │ └── translation.json │ │ ├── it/ │ │ │ └── translation.json │ │ ├── ja/ │ │ │ └── translation.json │ │ ├── ko/ │ │ │ └── translation.json │ │ ├── pl/ │ │ │ └── translation.json │ │ ├── pt/ │ │ │ └── translation.json │ │ ├── ru/ │ │ │ └── translation.json │ │ ├── tr/ │ │ │ └── translation.json │ │ ├── uk/ │ │ │ └── translation.json │ │ ├── vi/ │ │ │ └── translation.json │ │ ├── zh/ │ │ │ └── translation.json │ │ └── zh-TW/ │ │ └── translation.json │ ├── lib/ │ │ ├── constants/ │ │ │ └── languages.ts │ │ ├── types/ │ │ │ └── events.ts │ │ └── utils/ │ │ ├── format.ts │ │ ├── keyboard.ts │ │ ├── modelTranslation.ts │ │ └── rtl.ts │ ├── main.tsx │ ├── overlay/ │ │ ├── RecordingOverlay.css │ │ ├── RecordingOverlay.tsx │ │ ├── index.html │ │ └── main.tsx │ ├── stores/ │ │ ├── modelStore.ts │ │ └── settingsStore.ts │ ├── utils/ │ │ └── dateFormat.ts │ └── vite-env.d.ts ├── src-tauri/ │ ├── .gitignore │ ├── Cargo.toml │ ├── Entitlements.plist │ ├── Info.plist │ ├── build.rs │ ├── capabilities/ │ │ ├── default.json │ │ └── desktop.json │ ├── gen/ │ │ └── apple/ │ │ └── PrivacyInfo.xcprivacy │ ├── icons/ │ │ └── icon.icns │ ├── nsis/ │ │ └── installer.nsi │ ├── resources/ │ │ ├── default_settings.json │ │ └── models/ │ │ ├── gigaam_vocab.txt │ │ └── silero_vad_v4.onnx │ ├── rustfmt.toml │ ├── src/ │ │ ├── actions.rs │ │ ├── apple_intelligence.rs │ │ ├── audio_feedback.rs │ │ ├── audio_toolkit/ │ │ │ ├── audio/ │ │ │ │ ├── device.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── recorder.rs │ │ │ │ ├── resampler.rs │ │ │ │ ├── utils.rs │ │ │ │ └── visualizer.rs │ │ │ ├── bin/ │ │ │ │ └── cli.rs │ │ │ ├── constants.rs │ │ │ ├── mod.rs │ │ │ ├── text.rs │ │ │ ├── utils.rs │ │ │ └── vad/ │ │ │ ├── mod.rs │ │ │ ├── silero.rs │ │ │ └── smoothed.rs │ │ ├── cli.rs │ │ ├── clipboard.rs │ │ ├── commands/ │ │ │ ├── audio.rs │ │ │ ├── history.rs │ │ │ ├── mod.rs │ │ │ ├── models.rs │ │ │ └── transcription.rs │ │ ├── helpers/ │ │ │ ├── clamshell.rs │ │ │ └── mod.rs │ │ ├── input.rs │ │ ├── lib.rs │ │ ├── llm_client.rs │ │ ├── main.rs │ │ ├── managers/ │ │ │ ├── audio.rs │ │ │ ├── history.rs │ │ │ ├── mod.rs │ │ │ ├── model.rs │ │ │ ├── transcription.rs │ │ │ └── transcription_mock.rs │ │ ├── overlay.rs │ │ ├── portable.rs │ │ ├── settings.rs │ │ ├── shortcut/ │ │ │ ├── handler.rs │ │ │ ├── handy_keys.rs │ │ │ ├── mod.rs │ │ │ └── tauri_impl.rs │ │ ├── signal_handle.rs │ │ ├── transcription_coordinator.rs │ │ ├── tray.rs │ │ ├── tray_i18n.rs │ │ └── utils.rs │ ├── swift/ │ │ ├── apple_intelligence.swift │ │ ├── apple_intelligence_bridge.h │ │ └── apple_intelligence_stub.swift │ └── tauri.conf.json ├── tailwind.config.js ├── tests/ │ └── app.spec.ts ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts
SYMBOL INDEX (859 symbols across 138 files)
FILE: scripts/check-translations.ts
constant LOCALES_DIR (line 8) | const LOCALES_DIR = path.join(__dirname, "..", "src", "i18n", "locales");
constant REFERENCE_LANG (line 9) | const REFERENCE_LANG = "en";
type TranslationData (line 11) | type TranslationData = Record<string, unknown>;
type ValidationResult (line 13) | interface ValidationResult {
function getLanguages (line 19) | function getLanguages(): string[] {
constant LANGUAGES (line 27) | const LANGUAGES = getLanguages();
function colorize (line 38) | function colorize(text: string, color: string): string {
function getAllKeyPaths (line 42) | function getAllKeyPaths(
function hasKeyPath (line 64) | function hasKeyPath(obj: TranslationData, keyPath: string[]): boolean {
function loadTranslationFile (line 79) | function loadTranslationFile(lang: string): TranslationData | null {
function validateTranslations (line 92) | function validateTranslations(): void {
FILE: src-tauri/build.rs
function main (line 1) | fn main() {
function generate_tray_translations (line 14) | fn generate_tray_translations() {
function camel_to_snake (line 94) | fn camel_to_snake(s: &str) -> String {
function escape_string (line 106) | fn escape_string(s: &str) -> String {
function build_apple_intelligence_bridge (line 115) | fn build_apple_intelligence_bridge() {
FILE: src-tauri/src/actions.rs
type RecordingErrorEvent (line 25) | struct RecordingErrorEvent {
type FinishGuard (line 32) | struct FinishGuard(AppHandle);
method drop (line 34) | fn drop(&mut self) {
type ShortcutAction (line 42) | pub trait ShortcutAction: Send + Sync {
method start (line 43) | fn start(&self, app: &AppHandle, binding_id: &str, shortcut_str: &str);
method stop (line 44) | fn stop(&self, app: &AppHandle, binding_id: &str, shortcut_str: &str);
method start (line 319) | fn start(&self, app: &AppHandle, binding_id: &str, _shortcut_str: &str) {
method stop (line 412) | fn stop(&self, app: &AppHandle, binding_id: &str, _shortcut_str: &str) {
method start (line 569) | fn start(&self, app: &AppHandle, _binding_id: &str, _shortcut_str: &st...
method stop (line 573) | fn stop(&self, _app: &AppHandle, _binding_id: &str, _shortcut_str: &st...
method start (line 582) | fn start(&self, app: &AppHandle, binding_id: &str, shortcut_str: &str) {
method stop (line 591) | fn stop(&self, app: &AppHandle, binding_id: &str, shortcut_str: &str) {
type TranscribeAction (line 48) | struct TranscribeAction {
constant TRANSCRIPTION_FIELD (line 53) | const TRANSCRIPTION_FIELD: &str = "transcription";
function strip_invisible_chars (line 56) | fn strip_invisible_chars(s: &str) -> String {
function build_system_prompt (line 62) | fn build_system_prompt(prompt_template: &str) -> String {
function post_process_transcription (line 66) | async fn post_process_transcription(settings: &AppSettings, transcriptio...
function maybe_convert_chinese_variant (line 274) | async fn maybe_convert_chinese_variant(
type CancelAction (line 566) | struct CancelAction;
type TestAction (line 579) | struct TestAction;
FILE: src-tauri/src/apple_intelligence.rs
type AppleLLMResponse (line 6) | pub struct AppleLLMResponse {
function is_apple_intelligence_available (line 14) | pub fn is_apple_intelligence_available() -> c_int;
function free_apple_llm_response (line 15) | pub fn free_apple_llm_response(response: *mut AppleLLMResponse);
function check_apple_intelligence_availability (line 19) | pub fn check_apple_intelligence_availability() -> bool {
function process_text_with_system_prompt_apple (line 25) | pub fn process_text_with_system_prompt_apple(
function process_text_with_system_prompt (line 33) | pub fn process_text_with_system_prompt(
function test_availability (line 80) | fn test_availability() {
FILE: src-tauri/src/audio_feedback.rs
type SoundType (line 12) | pub enum SoundType {
function resolve_sound_path (line 17) | fn resolve_sound_path(
function get_sound_path (line 32) | fn get_sound_path(settings: &AppSettings, sound_type: SoundType) -> Stri...
function get_sound_base_dir (line 41) | fn get_sound_base_dir(settings: &AppSettings) -> tauri::path::BaseDirect...
function play_feedback_sound (line 48) | pub fn play_feedback_sound(app: &AppHandle, sound_type: SoundType) {
function play_feedback_sound_blocking (line 58) | pub fn play_feedback_sound_blocking(app: &AppHandle, sound_type: SoundTy...
function play_test_sound (line 68) | pub fn play_test_sound(app: &AppHandle, sound_type: SoundType) {
function play_sound_async (line 75) | fn play_sound_async(app: &AppHandle, path: PathBuf) {
function play_sound_blocking (line 84) | fn play_sound_blocking(app: &AppHandle, path: &Path) {
function play_sound_at_path (line 90) | fn play_sound_at_path(app: &AppHandle, path: &Path) -> Result<(), Box<dy...
function play_audio_file (line 97) | fn play_audio_file(
FILE: src-tauri/src/audio_toolkit/audio/device.rs
type CpalDeviceInfo (line 3) | pub struct CpalDeviceInfo {
function list_input_devices (line 10) | pub fn list_input_devices() -> Result<Vec<CpalDeviceInfo>, Box<dyn std::...
function list_output_devices (line 32) | pub fn list_output_devices() -> Result<Vec<CpalDeviceInfo>, Box<dyn std:...
FILE: src-tauri/src/audio_toolkit/audio/recorder.rs
type Cmd (line 22) | enum Cmd {
type AudioChunk (line 28) | enum AudioChunk {
type AudioRecorder (line 33) | pub struct AudioRecorder {
method new (line 42) | pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
method with_vad (line 52) | pub fn with_vad(mut self, vad: Box<dyn VoiceActivityDetector>) -> Self {
method with_level_callback (line 57) | pub fn with_level_callback<F>(mut self, cb: F) -> Self
method open (line 65) | pub fn open(&mut self, device: Option<Device>) -> Result<(), Box<dyn s...
method start (line 198) | pub fn start(&self) -> Result<(), Box<dyn std::error::Error>> {
method stop (line 205) | pub fn stop(&self) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
method close (line 213) | pub fn close(&mut self) -> Result<(), Box<dyn std::error::Error>> {
method build_stream (line 224) | fn build_stream<T>(
method get_preferred_config (line 282) | fn get_preferred_config(
function is_microphone_access_denied (line 338) | pub fn is_microphone_access_denied(error_message: &str) -> bool {
function detects_access_is_denied (line 350) | fn detects_access_is_denied() {
function detects_permission_denied (line 355) | fn detects_permission_denied() {
function detects_windows_error_code (line 360) | fn detects_windows_error_code() {
function does_not_match_unrelated_errors (line 365) | fn does_not_match_unrelated_errors() {
function run_consumer (line 370) | fn run_consumer(
FILE: src-tauri/src/audio_toolkit/audio/resampler.rs
constant RESAMPLER_CHUNK_SIZE (line 5) | const RESAMPLER_CHUNK_SIZE: usize = 1024;
type FrameResampler (line 7) | pub struct FrameResampler {
method new (line 16) | pub fn new(in_hz: usize, out_hz: usize, frame_dur: Duration) -> Self {
method push (line 37) | pub fn push(&mut self, mut src: &[f32], mut emit: impl FnMut(&[f32])) {
method finish (line 66) | pub fn finish(&mut self, mut emit: impl FnMut(&[f32])) {
method emit_frames (line 86) | fn emit_frames(&mut self, mut data: &[f32], emit: &mut impl FnMut(&[f3...
FILE: src-tauri/src/audio_toolkit/audio/utils.rs
function save_wav_file (line 7) | pub async fn save_wav_file<P: AsRef<Path>>(file_path: P, samples: &[f32]...
FILE: src-tauri/src/audio_toolkit/audio/visualizer.rs
constant DB_MIN (line 4) | const DB_MIN: f32 = -55.0;
constant DB_MAX (line 5) | const DB_MAX: f32 = -8.0;
constant GAIN (line 6) | const GAIN: f32 = 1.3;
constant CURVE_POWER (line 7) | const CURVE_POWER: f32 = 0.7;
type AudioVisualiser (line 9) | pub struct AudioVisualiser {
method new (line 21) | pub fn new(
method feed (line 80) | pub fn feed(&mut self, samples: &[f32]) -> Option<Vec<f32>> {
method reset (line 151) | pub fn reset(&mut self) {
FILE: src-tauri/src/audio_toolkit/bin/cli.rs
type RecorderMode (line 11) | enum RecorderMode {
method fmt (line 17) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type RecorderState (line 25) | struct RecorderState {
method new (line 35) | fn new(recorder: AudioRecorder) -> Self {
method switch_mode (line 46) | fn switch_mode(&mut self, new_mode: RecorderMode) -> Result<(), Box<dy...
method start_recording (line 78) | fn start_recording(
method stop_recording (line 136) | fn stop_recording(&mut self) -> Result<Vec<f32>, Box<dyn std::error::E...
method close (line 161) | fn close(&mut self) -> Result<(), Box<dyn std::error::Error>> {
function main (line 173) | fn main() -> Result<(), Box<dyn std::error::Error>> {
function print_help (line 312) | fn print_help() {
function print_devices (line 332) | fn print_devices(devices: &[CpalDeviceInfo]) {
function save_audio (line 340) | fn save_audio(samples: &[f32], filename: &str) -> Result<(), Box<dyn std...
FILE: src-tauri/src/audio_toolkit/constants.rs
constant WHISPER_SAMPLE_RATE (line 1) | pub const WHISPER_SAMPLE_RATE: u32 = 16000;
FILE: src-tauri/src/audio_toolkit/text.rs
function build_ngram (line 10) | fn build_ngram(words: &[&str]) -> String {
function find_best_match (line 34) | fn find_best_match<'a>(
function apply_custom_words (line 102) | pub fn apply_custom_words(text: &str, custom_words: &[String], threshold...
function preserve_case_pattern (line 159) | fn preserve_case_pattern(original: &str, replacement: &str) -> String {
function extract_punctuation (line 174) | fn extract_punctuation(word: &str) -> (&str, &str) {
function get_filler_words_for_language (line 202) | fn get_filler_words_for_language(lang: &str) -> &'static [&'static str] {
function collapse_stutters (line 236) | fn collapse_stutters(text: &str) -> String {
function filter_transcription_output (line 289) | pub fn filter_transcription_output(
function test_apply_custom_words_exact_match (line 328) | fn test_apply_custom_words_exact_match() {
function test_apply_custom_words_fuzzy_match (line 336) | fn test_apply_custom_words_fuzzy_match() {
function test_preserve_case_pattern (line 344) | fn test_preserve_case_pattern() {
function test_extract_punctuation (line 351) | fn test_extract_punctuation() {
function test_empty_custom_words (line 358) | fn test_empty_custom_words() {
function test_filter_filler_words (line 366) | fn test_filter_filler_words() {
function test_filter_filler_words_case_insensitive (line 373) | fn test_filter_filler_words_case_insensitive() {
function test_filter_filler_words_with_punctuation (line 380) | fn test_filter_filler_words_with_punctuation() {
function test_filter_cleans_whitespace (line 387) | fn test_filter_cleans_whitespace() {
function test_filter_trims (line 394) | fn test_filter_trims() {
function test_filter_combined (line 401) | fn test_filter_combined() {
function test_filter_preserves_valid_text (line 408) | fn test_filter_preserves_valid_text() {
function test_filter_stutter_collapse (line 415) | fn test_filter_stutter_collapse() {
function test_filter_stutter_short_words (line 422) | fn test_filter_stutter_short_words() {
function test_filter_stutter_mixed_case (line 429) | fn test_filter_stutter_mixed_case() {
function test_filter_stutter_preserves_two_repetitions (line 436) | fn test_filter_stutter_preserves_two_repetitions() {
function test_filter_english_removes_um (line 443) | fn test_filter_english_removes_um() {
function test_filter_portuguese_preserves_um (line 450) | fn test_filter_portuguese_preserves_um() {
function test_filter_spanish_preserves_ha (line 458) | fn test_filter_spanish_preserves_ha() {
function test_filter_language_code_with_region (line 466) | fn test_filter_language_code_with_region() {
function test_filter_custom_filler_words_override (line 474) | fn test_filter_custom_filler_words_override() {
function test_filter_custom_filler_words_empty_disables (line 482) | fn test_filter_custom_filler_words_empty_disables() {
function test_filter_unknown_language_uses_fallback (line 491) | fn test_filter_unknown_language_uses_fallback() {
function test_filter_fallback_does_not_remove_um (line 498) | fn test_filter_fallback_does_not_remove_um() {
function test_apply_custom_words_ngram_two_words (line 506) | fn test_apply_custom_words_ngram_two_words() {
function test_apply_custom_words_ngram_three_words (line 515) | fn test_apply_custom_words_ngram_three_words() {
function test_apply_custom_words_prefers_longer_ngram (line 523) | fn test_apply_custom_words_prefers_longer_ngram() {
function test_apply_custom_words_ngram_preserves_case (line 531) | fn test_apply_custom_words_ngram_preserves_case() {
function test_apply_custom_words_ngram_with_spaces_in_custom (line 539) | fn test_apply_custom_words_ngram_with_spaces_in_custom() {
function test_apply_custom_words_trailing_number_not_doubled (line 548) | fn test_apply_custom_words_trailing_number_not_doubled() {
FILE: src-tauri/src/audio_toolkit/utils.rs
function get_cpal_host (line 3) | pub fn get_cpal_host() -> cpal::Host {
FILE: src-tauri/src/audio_toolkit/vad/mod.rs
type VadFrame (line 3) | pub enum VadFrame<'a> {
function is_speech (line 12) | pub fn is_speech(&self) -> bool {
type VoiceActivityDetector (line 17) | pub trait VoiceActivityDetector: Send + Sync {
method push_frame (line 19) | fn push_frame<'a>(&'a mut self, frame: &'a [f32]) -> Result<VadFrame<'...
method is_voice (line 21) | fn is_voice(&mut self, frame: &[f32]) -> Result<bool> {
method reset (line 25) | fn reset(&mut self) {}
FILE: src-tauri/src/audio_toolkit/vad/silero.rs
constant SILERO_FRAME_MS (line 9) | const SILERO_FRAME_MS: u32 = 30;
constant SILERO_FRAME_SAMPLES (line 10) | const SILERO_FRAME_SAMPLES: usize =
type SileroVad (line 13) | pub struct SileroVad {
method new (line 19) | pub fn new<P: AsRef<Path>>(model_path: P, threshold: f32) -> Result<Se...
method push_frame (line 33) | fn push_frame<'a>(&'a mut self, frame: &'a [f32]) -> Result<VadFrame<'a>> {
FILE: src-tauri/src/audio_toolkit/vad/smoothed.rs
type SmoothedVad (line 5) | pub struct SmoothedVad {
method new (line 20) | pub fn new(
method push_frame (line 41) | fn push_frame<'a>(&'a mut self, frame: &'a [f32]) -> Result<VadFrame<'a>> {
method reset (line 98) | fn reset(&mut self) {
FILE: src-tauri/src/cli.rs
type CliArgs (line 5) | pub struct CliArgs {
FILE: src-tauri/src/clipboard.rs
function paste_via_clipboard (line 16) | fn paste_via_clipboard(
function try_send_key_combo_linux (line 84) | fn try_send_key_combo_linux(paste_method: &PasteMethod) -> Result<bool, ...
function try_direct_typing_linux (line 123) | fn try_direct_typing_linux(text: &str, preferred_tool: TypingTool) -> Re...
function get_available_typing_tools (line 204) | pub fn get_available_typing_tools() -> Vec<String> {
function is_wtype_available (line 226) | fn is_wtype_available() -> bool {
function is_dotool_available (line 236) | fn is_dotool_available() -> bool {
function is_ydotool_available (line 246) | fn is_ydotool_available() -> bool {
function is_xdotool_available (line 255) | fn is_xdotool_available() -> bool {
function is_kwtype_available (line 265) | fn is_kwtype_available() -> bool {
function is_wl_copy_available (line 275) | fn is_wl_copy_available() -> bool {
function type_text_via_wtype (line 285) | fn type_text_via_wtype(text: &str) -> Result<(), String> {
function type_text_via_xdotool (line 302) | fn type_text_via_xdotool(text: &str) -> Result<(), String> {
function type_text_via_dotool (line 321) | fn type_text_via_dotool(text: &str) -> Result<(), String> {
function type_text_via_ydotool (line 350) | fn type_text_via_ydotool(text: &str) -> Result<(), String> {
function type_text_via_kwtype (line 368) | fn type_text_via_kwtype(text: &str) -> Result<(), String> {
function write_clipboard_via_wl_copy (line 387) | fn write_clipboard_via_wl_copy(text: &str) -> Result<(), String> {
function send_key_combo_via_wtype (line 406) | fn send_key_combo_via_wtype(paste_method: &PasteMethod) -> Result<(), St...
function send_key_combo_via_dotool (line 429) | fn send_key_combo_via_dotool(paste_method: &PasteMethod) -> Result<(), S...
function send_key_combo_via_ydotool (line 454) | fn send_key_combo_via_ydotool(paste_method: &PasteMethod) -> Result<(), ...
function send_key_combo_via_xdotool (line 479) | fn send_key_combo_via_xdotool(paste_method: &PasteMethod) -> Result<(), ...
function paste_via_external_script (line 504) | fn paste_via_external_script(text: &str, script_path: &str) -> Result<()...
function paste_direct (line 528) | fn paste_direct(
function send_return_key (line 544) | fn send_return_key(enigo: &mut Enigo, key_type: AutoSubmitKey) -> Result...
function should_send_auto_submit (line 587) | fn should_send_auto_submit(auto_submit: bool, paste_method: PasteMethod)...
function paste (line 591) | pub fn paste(text: String, app_handle: AppHandle) -> Result<(), String> {
function auto_submit_requires_setting_enabled (line 670) | fn auto_submit_requires_setting_enabled() {
function auto_submit_skips_none_paste_method (line 676) | fn auto_submit_skips_none_paste_method() {
function auto_submit_runs_for_active_paste_methods (line 681) | fn auto_submit_runs_for_active_paste_methods() {
FILE: src-tauri/src/commands/audio.rs
type CustomSounds (line 18) | pub struct CustomSounds {
function custom_sound_exists (line 23) | fn custom_sound_exists(app: &AppHandle, sound_type: &str) -> bool {
function check_custom_sounds (line 30) | pub fn check_custom_sounds(app: AppHandle) -> CustomSounds {
type AudioDevice (line 38) | pub struct AudioDevice {
type PermissionAccess (line 46) | pub enum PermissionAccess {
type WindowsMicrophonePermissionStatus (line 53) | pub struct WindowsMicrophonePermissionStatus {
function read_registry_permission_access (line 62) | fn read_registry_permission_access(root_hkey: HKEY, path: &str) -> Permi...
function get_windows_microphone_permission_status_impl (line 80) | fn get_windows_microphone_permission_status_impl() -> WindowsMicrophoneP...
function get_windows_microphone_permission_status (line 115) | pub fn get_windows_microphone_permission_status() -> WindowsMicrophonePe...
function open_microphone_privacy_settings (line 135) | pub fn open_microphone_privacy_settings() -> Result<(), String> {
function update_microphone_mode (line 154) | pub fn update_microphone_mode(app: AppHandle, always_on: bool) -> Result...
function get_microphone_mode (line 174) | pub fn get_microphone_mode(app: AppHandle) -> Result<bool, String> {
function get_available_microphones (line 181) | pub fn get_available_microphones() -> Result<Vec<AudioDevice>, String> {
function set_selected_microphone (line 202) | pub fn set_selected_microphone(app: AppHandle, device_name: String) -> R...
function get_selected_microphone (line 221) | pub fn get_selected_microphone(app: AppHandle) -> Result<String, String> {
function get_available_output_devices (line 230) | pub fn get_available_output_devices() -> Result<Vec<AudioDevice>, String> {
function set_selected_output_device (line 251) | pub fn set_selected_output_device(app: AppHandle, device_name: String) -...
function get_selected_output_device (line 264) | pub fn get_selected_output_device(app: AppHandle) -> Result<String, Stri...
function play_test_sound (line 273) | pub async fn play_test_sound(app: AppHandle, sound_type: String) {
function set_clamshell_microphone (line 287) | pub fn set_clamshell_microphone(app: AppHandle, device_name: String) -> ...
function get_clamshell_microphone (line 300) | pub fn get_clamshell_microphone(app: AppHandle) -> Result<String, String> {
function is_recording (line 309) | pub fn is_recording(app: AppHandle) -> bool {
FILE: src-tauri/src/commands/history.rs
function get_history_entries (line 7) | pub async fn get_history_entries(
function toggle_history_entry_saved (line 19) | pub async fn toggle_history_entry_saved(
function get_audio_file_path (line 32) | pub async fn get_audio_file_path(
function delete_history_entry (line 45) | pub async fn delete_history_entry(
function update_history_limit (line 58) | pub async fn update_history_limit(
function update_recording_retention_period (line 76) | pub async fn update_recording_retention_period(
FILE: src-tauri/src/commands/mod.rs
function cancel_operation (line 13) | pub fn cancel_operation(app: AppHandle) {
function get_app_dir_path (line 19) | pub fn get_app_dir_path(app: AppHandle) -> Result<String, String> {
function get_app_settings (line 28) | pub fn get_app_settings(app: AppHandle) -> Result<AppSettings, String> {
function get_default_settings (line 34) | pub fn get_default_settings() -> Result<AppSettings, String> {
function get_log_dir_path (line 40) | pub fn get_log_dir_path(app: AppHandle) -> Result<String, String> {
function set_log_level (line 49) | pub fn set_log_level(app: AppHandle, level: LogLevel) -> Result<(), Stri...
function open_recordings_folder (line 67) | pub fn open_recordings_folder(app: AppHandle) -> Result<(), String> {
function open_log_dir (line 83) | pub fn open_log_dir(app: AppHandle) -> Result<(), String> {
function open_app_data_dir (line 97) | pub fn open_app_data_dir(app: AppHandle) -> Result<(), String> {
function check_apple_intelligence_available (line 113) | pub fn check_apple_intelligence_available() -> bool {
function initialize_enigo (line 128) | pub fn initialize_enigo(app: AppHandle) -> Result<(), String> {
type ShortcutsInitialized (line 159) | pub struct ShortcutsInitialized;
function initialize_shortcuts (line 166) | pub fn initialize_shortcuts(app: AppHandle) -> Result<(), String> {
FILE: src-tauri/src/commands/models.rs
function get_available_models (line 9) | pub async fn get_available_models(
function get_model_info (line 17) | pub async fn get_model_info(
function download_model (line 26) | pub async fn download_model(
function delete_model (line 38) | pub async fn delete_model(
function switch_active_model (line 67) | pub fn switch_active_model(app: &AppHandle, model_id: &str) -> Result<()...
function set_active_model (line 149) | pub async fn set_active_model(
function get_current_model (line 160) | pub async fn get_current_model(app_handle: AppHandle) -> Result<String, ...
function get_transcription_model_status (line 167) | pub async fn get_transcription_model_status(
function is_model_loading (line 175) | pub async fn is_model_loading(
function has_any_models_available (line 185) | pub async fn has_any_models_available(
function has_any_models_or_downloads (line 194) | pub async fn has_any_models_or_downloads(
function cancel_download (line 204) | pub async fn cancel_download(
FILE: src-tauri/src/commands/transcription.rs
type ModelLoadStatus (line 8) | pub struct ModelLoadStatus {
function set_model_unload_timeout (line 15) | pub fn set_model_unload_timeout(app: AppHandle, timeout: ModelUnloadTime...
function get_model_load_status (line 23) | pub fn get_model_load_status(
function unload_model_manually (line 34) | pub fn unload_model_manually(
FILE: src-tauri/src/helpers/clamshell.rs
function is_clamshell (line 9) | pub fn is_clamshell() -> Result<bool, String> {
function is_laptop (line 35) | pub fn is_laptop() -> Result<bool, String> {
function is_clamshell (line 51) | pub fn is_clamshell() -> Result<bool, String> {
function is_laptop (line 60) | pub fn is_laptop() -> Result<bool, String> {
function test_clamshell_check (line 70) | fn test_clamshell_check() {
function test_is_laptop (line 79) | fn test_is_laptop() {
FILE: src-tauri/src/input.rs
type EnigoState (line 7) | pub struct EnigoState(pub Mutex<Enigo>);
method new (line 10) | pub fn new() -> Result<Self, String> {
function get_cursor_position (line 19) | pub fn get_cursor_position(app_handle: &AppHandle) -> Option<(i32, i32)> {
function send_paste_ctrl_v (line 28) | pub fn send_paste_ctrl_v(enigo: &mut Enigo) -> Result<(), String> {
function send_paste_ctrl_shift_v (line 57) | pub fn send_paste_ctrl_shift_v(enigo: &mut Enigo) -> Result<(), String> {
function send_paste_shift_insert (line 92) | pub fn send_paste_shift_insert(enigo: &mut Enigo) -> Result<(), String> {
function paste_text_direct (line 117) | pub fn paste_text_direct(enigo: &mut Enigo, text: &str) -> Result<(), St...
FILE: src-tauri/src/lib.rs
function level_filter_from_u8 (line 53) | fn level_filter_from_u8(value: u8) -> log::LevelFilter {
function build_console_filter (line 65) | fn build_console_filter() -> env_filter::Filter {
function show_main_window (line 87) | fn show_main_window(app: &AppHandle) {
function should_force_show_permissions_window (line 115) | fn should_force_show_permissions_window(app: &AppHandle) -> bool {
function initialize_core_logic (line 140) | fn initialize_core_logic(app_handle: &AppHandle) {
function trigger_update_check (line 298) | fn trigger_update_check(app: AppHandle) -> Result<(), String> {
function show_main_window_command (line 310) | fn show_main_window_command(app: AppHandle) -> Result<(), String> {
function run (line 316) | pub fn run(cli_args: CliArgs) {
FILE: src-tauri/src/llm_client.rs
type ChatMessage (line 8) | struct ChatMessage {
type JsonSchema (line 14) | struct JsonSchema {
type ResponseFormat (line 21) | struct ResponseFormat {
type ChatCompletionRequest (line 28) | struct ChatCompletionRequest {
type ChatCompletionResponse (line 36) | struct ChatCompletionResponse {
type ChatChoice (line 41) | struct ChatChoice {
type ChatMessageResponse (line 46) | struct ChatMessageResponse {
function build_headers (line 51) | fn build_headers(provider: &PostProcessProvider, api_key: &str) -> Resul...
function create_client (line 88) | fn create_client(provider: &PostProcessProvider, api_key: &str) -> Resul...
function send_chat_completion (line 99) | pub async fn send_chat_completion(
function send_chat_completion_with_schema (line 111) | pub async fn send_chat_completion_with_schema(
function fetch_models (line 191) | pub async fn fetch_models(
FILE: src-tauri/src/main.rs
function main (line 7) | fn main() {
FILE: src-tauri/src/managers/audio.rs
constant STREAM_IDLE_TIMEOUT (line 11) | const STREAM_IDLE_TIMEOUT: Duration = Duration::from_secs(30);
function set_mute (line 13) | fn set_mute(mute: bool) {
constant WHISPER_SAMPLE_RATE (line 102) | const WHISPER_SAMPLE_RATE: usize = 16000;
type RecordingState (line 107) | pub enum RecordingState {
type MicrophoneMode (line 113) | pub enum MicrophoneMode {
function create_audio_recorder (line 120) | fn create_audio_recorder(
type AudioRecordingManager (line 146) | pub struct AudioRecordingManager {
method new (line 161) | pub fn new(app: &tauri::AppHandle) -> Result<Self, anyhow::Error> {
method get_effective_microphone_device (line 191) | fn get_effective_microphone_device(&self, settings: &AppSettings) -> O...
method schedule_lazy_close (line 218) | fn schedule_lazy_close(&self) {
method apply_mute (line 245) | pub fn apply_mute(&self) {
method remove_mute (line 257) | pub fn remove_mute(&self) {
method start_microphone_stream (line 266) | pub fn start_microphone_stream(&self) -> Result<(), anyhow::Error> {
method stop_microphone_stream (line 313) | pub fn stop_microphone_stream(&self) {
method update_mode (line 340) | pub fn update_mode(&self, new_mode: MicrophoneMode) -> Result<(), anyh...
method try_start_recording (line 363) | pub fn try_start_recording(&self, binding_id: &str) -> Result<(), Stri...
method update_selected_device (line 394) | pub fn update_selected_device(&self) -> Result<(), anyhow::Error> {
method stop_recording (line 404) | pub fn stop_recording(&self, binding_id: &str) -> Option<Vec<f32>> {
method is_recording (line 462) | pub fn is_recording(&self) -> bool {
method cancel_recording (line 470) | pub fn cancel_recording(&self) {
FILE: src-tauri/src/managers/history.rs
type HistoryEntry (line 37) | pub struct HistoryEntry {
type HistoryManager (line 48) | pub struct HistoryManager {
method new (line 55) | pub fn new(app_handle: &AppHandle) -> Result<Self> {
method init_database (line 79) | fn init_database(&self) -> Result<()> {
method migrate_from_tauri_plugin_sql (line 122) | fn migrate_from_tauri_plugin_sql(&self, conn: &Connection) -> Result<(...
method get_connection (line 175) | fn get_connection(&self) -> Result<Connection> {
method save_transcription (line 180) | pub async fn save_transcription(
method save_to_database (line 216) | fn save_to_database(
method cleanup_old_entries (line 235) | pub fn cleanup_old_entries(&self) -> Result<()> {
method delete_entries_and_files (line 255) | fn delete_entries_and_files(&self, entries: &[(i64, String)]) -> Resul...
method cleanup_by_count (line 285) | fn cleanup_by_count(&self, limit: usize) -> Result<()> {
method cleanup_by_time (line 314) | fn cleanup_by_time(
method get_history_entries (line 355) | pub async fn get_history_entries(&self) -> Result<Vec<HistoryEntry>> {
method get_latest_entry (line 382) | pub fn get_latest_entry(&self) -> Result<Option<HistoryEntry>> {
method get_latest_entry_with_conn (line 387) | fn get_latest_entry_with_conn(conn: &Connection) -> Result<Option<Hist...
method toggle_saved_status (line 413) | pub async fn toggle_saved_status(&self, id: i64) -> Result<()> {
method get_audio_file_path (line 440) | pub fn get_audio_file_path(&self, file_name: &str) -> PathBuf {
method get_entry_by_id (line 444) | pub async fn get_entry_by_id(&self, id: i64) -> Result<Option<HistoryE...
method delete_entry (line 469) | pub async fn delete_entry(&self, id: i64) -> Result<()> {
method format_timestamp_title (line 500) | fn format_timestamp_title(&self, timestamp: i64) -> String {
function setup_conn (line 516) | fn setup_conn() -> Connection {
function insert_entry (line 534) | fn insert_entry(conn: &Connection, timestamp: i64, text: &str, post_proc...
function get_latest_entry_returns_none_when_empty (line 552) | fn get_latest_entry_returns_none_when_empty() {
function get_latest_entry_returns_newest_entry (line 559) | fn get_latest_entry_returns_newest_entry() {
FILE: src-tauri/src/managers/model.rs
type EngineType (line 20) | pub enum EngineType {
type ModelInfo (line 31) | pub struct ModelInfo {
type DownloadProgress (line 53) | pub struct DownloadProgress {
type ModelManager (line 60) | pub struct ModelManager {
method new (line 69) | pub fn new(app_handle: &AppHandle) -> Result<Self> {
method get_available_models (line 533) | pub fn get_available_models(&self) -> Vec<ModelInfo> {
method get_model_info (line 538) | pub fn get_model_info(&self, model_id: &str) -> Option<ModelInfo> {
method migrate_bundled_models (line 543) | fn migrate_bundled_models(&self) -> Result<()> {
method migrate_gigaam_to_directory (line 573) | fn migrate_gigaam_to_directory(&self) -> Result<()> {
method update_download_status (line 614) | fn update_download_status(&self) -> Result<()> {
method auto_select_model_if_needed (line 666) | fn auto_select_model_if_needed(&self) -> Result<()> {
method discover_custom_whisper_models (line 710) | fn discover_custom_whisper_models(
method download_model (line 828) | pub async fn download_model(&self, model_id: &str) -> Result<()> {
method delete_model (line 1178) | pub fn delete_model(&self, model_id: &str) -> Result<()> {
method get_model_path (line 1248) | pub fn get_model_path(&self, model_id: &str) -> Result<PathBuf> {
method cancel_download (line 1293) | pub fn cancel_download(&self, model_id: &str) -> Result<()> {
function test_discover_custom_whisper_models (line 1333) | fn test_discover_custom_whisper_models() {
function test_discover_custom_models_empty_dir (line 1405) | fn test_discover_custom_models_empty_dir() {
function test_discover_custom_models_nonexistent_dir (line 1419) | fn test_discover_custom_models_nonexistent_dir() {
FILE: src-tauri/src/managers/transcription.rs
type ModelStateEvent (line 31) | pub struct ModelStateEvent {
type LoadedEngine (line 38) | enum LoadedEngine {
type LoadingGuard (line 50) | pub struct LoadingGuard {
method drop (line 56) | fn drop(&mut self) {
type TranscriptionManager (line 64) | pub struct TranscriptionManager {
method new (line 77) | pub fn new(app_handle: &AppHandle, model_manager: Arc<ModelManager>) -...
method lock_engine (line 165) | fn lock_engine(&self) -> MutexGuard<'_, Option<LoadedEngine>> {
method is_model_loaded (line 172) | pub fn is_model_loaded(&self) -> bool {
method try_start_loading (line 181) | pub fn try_start_loading(&self) -> Option<LoadingGuard> {
method unload_model (line 193) | pub fn unload_model(&self) -> Result<()> {
method now_ms (line 226) | fn now_ms() -> u64 {
method touch_activity (line 234) | fn touch_activity(&self) {
method maybe_unload_immediately (line 239) | pub fn maybe_unload_immediately(&self, context: &str) {
method load_model (line 251) | pub fn load_model(&self, model_id: &str) -> Result<()> {
method initiate_model_load (line 406) | pub fn initiate_model_load(&self) {
method get_current_model (line 425) | pub fn get_current_model(&self) -> Option<String> {
method transcribe (line 430) | pub fn transcribe(&self, audio: Vec<f32>) -> Result<String> {
function apply_accelerator_settings (line 702) | pub fn apply_accelerator_settings(app: &tauri::AppHandle) {
type AvailableAccelerators (line 727) | pub struct AvailableAccelerators {
function get_available_accelerators (line 733) | pub fn get_available_accelerators() -> AvailableAccelerators {
method drop (line 750) | fn drop(&mut self) {
FILE: src-tauri/src/managers/transcription_mock.rs
type ModelStateEvent (line 13) | pub struct ModelStateEvent {
type LoadingGuard (line 21) | pub struct LoadingGuard;
type TranscriptionManager (line 24) | pub struct TranscriptionManager {
method new (line 30) | pub fn new(app_handle: &AppHandle, _model_manager: Arc<ModelManager>) ...
method is_model_loaded (line 36) | pub fn is_model_loaded(&self) -> bool {
method try_start_loading (line 40) | pub fn try_start_loading(&self) -> Option<LoadingGuard> {
method unload_model (line 44) | pub fn unload_model(&self) -> Result<()> {
method maybe_unload_immediately (line 48) | pub fn maybe_unload_immediately(&self, _context: &str) {}
method load_model (line 50) | pub fn load_model(&self, _model_id: &str) -> Result<()> {
method initiate_model_load (line 54) | pub fn initiate_model_load(&self) {}
method get_current_model (line 56) | pub fn get_current_model(&self) -> Option<String> {
method transcribe (line 60) | pub fn transcribe(&self, _audio: Vec<f32>) -> Result<String> {
function apply_accelerator_settings (line 66) | pub fn apply_accelerator_settings(_app: &tauri::AppHandle) {}
type AvailableAccelerators (line 69) | pub struct AvailableAccelerators {
function get_available_accelerators (line 75) | pub fn get_available_accelerators() -> AvailableAccelerators {
FILE: src-tauri/src/overlay.rs
constant OVERLAY_WIDTH (line 33) | const OVERLAY_WIDTH: f64 = 172.0;
constant OVERLAY_HEIGHT (line 34) | const OVERLAY_HEIGHT: f64 = 36.0;
constant OVERLAY_TOP_OFFSET (line 37) | const OVERLAY_TOP_OFFSET: f64 = 46.0;
constant OVERLAY_TOP_OFFSET (line 39) | const OVERLAY_TOP_OFFSET: f64 = 4.0;
constant OVERLAY_BOTTOM_OFFSET (line 42) | const OVERLAY_BOTTOM_OFFSET: f64 = 15.0;
constant OVERLAY_BOTTOM_OFFSET (line 45) | const OVERLAY_BOTTOM_OFFSET: f64 = 40.0;
function update_gtk_layer_shell_anchors (line 48) | fn update_gtk_layer_shell_anchors(overlay_window: &tauri::webview::Webvi...
function init_gtk_layer_shell (line 71) | fn init_gtk_layer_shell(overlay_window: &tauri::webview::WebviewWindow) ...
function force_overlay_topmost (line 109) | fn force_overlay_topmost(overlay_window: &tauri::webview::WebviewWindow) {
function get_monitor_with_cursor (line 136) | fn get_monitor_with_cursor(app_handle: &AppHandle) -> Option<tauri::Moni...
function is_mouse_within_monitor (line 166) | fn is_mouse_within_monitor(
function calculate_overlay_position (line 197) | fn calculate_overlay_position(app_handle: &AppHandle) -> Option<(f64, f6...
function create_recording_overlay (line 220) | pub fn create_recording_overlay(app_handle: &AppHandle) {
function create_recording_overlay (line 281) | pub fn create_recording_overlay(app_handle: &AppHandle) {
function show_overlay_state (line 316) | fn show_overlay_state(app_handle: &AppHandle, state: &str) {
function show_recording_overlay (line 337) | pub fn show_recording_overlay(app_handle: &AppHandle) {
function show_transcribing_overlay (line 342) | pub fn show_transcribing_overlay(app_handle: &AppHandle) {
function show_processing_overlay (line 347) | pub fn show_processing_overlay(app_handle: &AppHandle) {
function update_overlay_position (line 352) | pub fn update_overlay_position(app_handle: &AppHandle) {
function hide_recording_overlay (line 367) | pub fn hide_recording_overlay(app_handle: &AppHandle) {
function emit_levels (line 382) | pub fn emit_levels(app_handle: &AppHandle, levels: &Vec<f32>) {
FILE: src-tauri/src/portable.rs
function init (line 15) | pub fn init() {
function is_portable (line 34) | pub fn is_portable() -> bool {
function data_dir (line 40) | pub fn data_dir() -> Option<&'static PathBuf> {
function app_data_dir (line 45) | pub fn app_data_dir(app: &tauri::AppHandle) -> Result<PathBuf, tauri::Er...
function app_log_dir (line 54) | pub fn app_log_dir(app: &tauri::AppHandle) -> Result<PathBuf, tauri::Err...
function resolve_app_data (line 64) | pub fn resolve_app_data(app: &tauri::AppHandle, relative: &str) -> Resul...
function store_path (line 71) | pub fn store_path(relative: &str) -> PathBuf {
FILE: src-tauri/src/settings.rs
constant APPLE_INTELLIGENCE_PROVIDER_ID (line 9) | pub const APPLE_INTELLIGENCE_PROVIDER_ID: &str = "apple_intelligence";
constant APPLE_INTELLIGENCE_DEFAULT_MODEL_ID (line 10) | pub const APPLE_INTELLIGENCE_DEFAULT_MODEL_ID: &str = "Apple Intelligence";
type LogLevel (line 14) | pub enum LogLevel {
method deserialize (line 24) | fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
function from (line 68) | fn from(level: LogLevel) -> Self {
type ShortcutBinding (line 80) | pub struct ShortcutBinding {
type LLMPrompt (line 89) | pub struct LLMPrompt {
type PostProcessProvider (line 96) | pub struct PostProcessProvider {
type OverlayPosition (line 110) | pub enum OverlayPosition {
type ModelUnloadTimeout (line 118) | pub enum ModelUnloadTimeout {
method to_minutes (line 210) | pub fn to_minutes(self) -> Option<u64> {
method to_seconds (line 223) | pub fn to_seconds(self) -> Option<u64> {
type PasteMethod (line 131) | pub enum PasteMethod {
type ClipboardHandling (line 142) | pub enum ClipboardHandling {
type AutoSubmitKey (line 149) | pub enum AutoSubmitKey {
type RecordingRetentionPeriod (line 157) | pub enum RecordingRetentionPeriod {
type KeyboardImplementation (line 167) | pub enum KeyboardImplementation {
method default (line 173) | fn default() -> Self {
method default (line 182) | fn default() -> Self {
method default (line 188) | fn default() -> Self {
method default (line 198) | fn default() -> Self {
method default (line 204) | fn default() -> Self {
type SoundTheme (line 235) | pub enum SoundTheme {
method as_str (line 242) | fn as_str(&self) -> &'static str {
method to_start_path (line 250) | pub fn to_start_path(&self) -> String {
method to_stop_path (line 254) | pub fn to_stop_path(&self) -> String {
type TypingTool (line 261) | pub enum TypingTool {
method default (line 271) | fn default() -> Self {
type WhisperAcceleratorSetting (line 278) | pub enum WhisperAcceleratorSetting {
method default (line 285) | fn default() -> Self {
type OrtAcceleratorSetting (line 292) | pub enum OrtAcceleratorSetting {
method default (line 302) | fn default() -> Self {
type AppSettings (line 309) | pub struct AppSettings {
method active_post_process_provider (line 775) | pub fn active_post_process_provider(&self) -> Option<&PostProcessProvi...
method post_process_provider (line 781) | pub fn post_process_provider(&self, provider_id: &str) -> Option<&Post...
method post_process_provider_mut (line 787) | pub fn post_process_provider_mut(
function default_model (line 404) | fn default_model() -> String {
function default_always_on_microphone (line 408) | fn default_always_on_microphone() -> bool {
function default_translate_to_english (line 412) | fn default_translate_to_english() -> bool {
function default_start_hidden (line 416) | fn default_start_hidden() -> bool {
function default_autostart_enabled (line 420) | fn default_autostart_enabled() -> bool {
function default_update_checks_enabled (line 424) | fn default_update_checks_enabled() -> bool {
function default_selected_language (line 428) | fn default_selected_language() -> String {
function default_overlay_position (line 432) | fn default_overlay_position() -> OverlayPosition {
function default_debug_mode (line 439) | fn default_debug_mode() -> bool {
function default_log_level (line 443) | fn default_log_level() -> LogLevel {
function default_word_correction_threshold (line 447) | fn default_word_correction_threshold() -> f64 {
function default_paste_delay_ms (line 451) | fn default_paste_delay_ms() -> u64 {
function default_auto_submit (line 455) | fn default_auto_submit() -> bool {
function default_history_limit (line 459) | fn default_history_limit() -> usize {
function default_recording_retention_period (line 463) | fn default_recording_retention_period() -> RecordingRetentionPeriod {
function default_audio_feedback_volume (line 467) | fn default_audio_feedback_volume() -> f32 {
function default_sound_theme (line 471) | fn default_sound_theme() -> SoundTheme {
function default_post_process_enabled (line 475) | fn default_post_process_enabled() -> bool {
function default_app_language (line 479) | fn default_app_language() -> String {
function default_show_tray_icon (line 485) | fn default_show_tray_icon() -> bool {
function default_post_process_provider_id (line 489) | fn default_post_process_provider_id() -> String {
function default_post_process_providers (line 493) | fn default_post_process_providers() -> Vec<PostProcessProvider> {
function default_post_process_api_keys (line 574) | fn default_post_process_api_keys() -> HashMap<String, String> {
function default_model_for_provider (line 582) | fn default_model_for_provider(provider_id: &str) -> String {
function default_post_process_models (line 589) | fn default_post_process_models() -> HashMap<String, String> {
function default_post_process_prompts (line 600) | fn default_post_process_prompts() -> Vec<LLMPrompt> {
function default_typing_tool (line 608) | fn default_typing_tool() -> TypingTool {
function ensure_post_process_defaults (line 612) | fn ensure_post_process_defaults(settings: &mut AppSettings) -> bool {
constant SETTINGS_STORE_PATH (line 668) | pub const SETTINGS_STORE_PATH: &str = "settings_store.json";
function get_default_settings (line 670) | pub fn get_default_settings() -> AppSettings {
function load_or_create_app_settings (line 797) | pub fn load_or_create_app_settings(app: &AppHandle) -> AppSettings {
function get_settings (line 848) | pub fn get_settings(app: &AppHandle) -> AppSettings {
function write_settings (line 872) | pub fn write_settings(app: &AppHandle, settings: AppSettings) {
function get_bindings (line 880) | pub fn get_bindings(app: &AppHandle) -> HashMap<String, ShortcutBinding> {
function get_stored_binding (line 886) | pub fn get_stored_binding(app: &AppHandle, id: &str) -> ShortcutBinding {
function get_history_limit (line 894) | pub fn get_history_limit(app: &AppHandle) -> usize {
function get_recording_retention_period (line 899) | pub fn get_recording_retention_period(app: &AppHandle) -> RecordingReten...
function default_settings_disable_auto_submit (line 909) | fn default_settings_disable_auto_submit() {
FILE: src-tauri/src/shortcut/handler.rs
function handle_shortcut_event (line 29) | pub fn handle_shortcut_event(
FILE: src-tauri/src/shortcut/handy_keys.rs
type ManagerCommand (line 46) | enum ManagerCommand {
type HandyKeysState (line 60) | pub struct HandyKeysState {
method new (line 90) | pub fn new(app: AppHandle) -> Result<Self, String> {
method manager_thread (line 110) | fn manager_thread(cmd_rx: Receiver<ManagerCommand>, app: AppHandle) {
method do_register (line 187) | fn do_register(
method do_unregister (line 213) | fn do_unregister(
method register (line 230) | pub fn register(&self, binding: &ShortcutBinding) -> Result<(), String> {
method unregister (line 247) | pub fn unregister(&self, binding: &ShortcutBinding) -> Result<(), Stri...
method start_recording (line 263) | pub fn start_recording(&self, app: &AppHandle, binding_id: String) -> ...
method recording_loop (line 302) | fn recording_loop(app: AppHandle, running: Arc<AtomicBool>) {
method stop_recording (line 338) | pub fn stop_recording(&self) -> Result<(), String> {
type FrontendKeyEvent (line 77) | pub struct FrontendKeyEvent {
method drop (line 363) | fn drop(&mut self) {
function modifiers_to_strings (line 383) | fn modifiers_to_strings(modifiers: handy_keys::Modifiers) -> Vec<String> {
function validate_shortcut (line 413) | pub fn validate_shortcut(raw: &str) -> Result<(), String> {
function init_shortcuts (line 425) | pub fn init_shortcuts(app: &AppHandle) -> Result<(), String> {
function register_cancel_shortcut (line 461) | pub fn register_cancel_shortcut(app: &AppHandle) {
function unregister_cancel_shortcut (line 485) | pub fn unregister_cancel_shortcut(app: &AppHandle) {
function register_shortcut (line 506) | pub fn register_shortcut(app: &AppHandle, binding: ShortcutBinding) -> R...
function unregister_shortcut (line 514) | pub fn unregister_shortcut(app: &AppHandle, binding: ShortcutBinding) ->...
function start_handy_keys_recording (line 524) | pub fn start_handy_keys_recording(app: AppHandle, binding_id: String) ->...
function stop_handy_keys_recording (line 539) | pub fn stop_handy_keys_recording(app: AppHandle) -> Result<(), String> {
FILE: src-tauri/src/shortcut/mod.rs
function init_shortcuts (line 34) | pub fn init_shortcuts(app: &AppHandle) {
function register_cancel_shortcut (line 60) | pub fn register_cancel_shortcut(app: &AppHandle) {
function unregister_cancel_shortcut (line 69) | pub fn unregister_cancel_shortcut(app: &AppHandle) {
function register_shortcut (line 78) | pub fn register_shortcut(app: &AppHandle, binding: ShortcutBinding) -> R...
function unregister_shortcut (line 87) | pub fn unregister_shortcut(app: &AppHandle, binding: ShortcutBinding) ->...
type BindingResponse (line 100) | pub struct BindingResponse {
function change_binding (line 108) | pub fn change_binding(
function reset_binding (line 206) | pub fn reset_binding(app: AppHandle, id: String) -> Result<BindingRespon...
function suspend_binding (line 215) | pub fn suspend_binding(app: AppHandle, id: String) -> Result<(), String> {
function resume_binding (line 228) | pub fn resume_binding(app: AppHandle, id: String) -> Result<(), String> {
type ImplementationChangeResult (line 244) | pub struct ImplementationChangeResult {
function change_keyboard_implementation_setting (line 256) | pub fn change_keyboard_implementation_setting(
function get_keyboard_implementation (line 320) | pub fn get_keyboard_implementation(app: AppHandle) -> String {
function validate_shortcut_for_implementation (line 333) | fn validate_shortcut_for_implementation(
function parse_keyboard_implementation (line 344) | fn parse_keyboard_implementation(s: &str) -> KeyboardImplementation {
function unregister_all_shortcuts (line 359) | fn unregister_all_shortcuts(app: &AppHandle, implementation: KeyboardImp...
function register_all_shortcuts_for_implementation (line 383) | fn register_all_shortcuts_for_implementation(
function initialize_handy_keys_with_rollback (line 448) | fn initialize_handy_keys_with_rollback(app: &AppHandle) -> Result<bool, ...
function change_ptt_setting (line 476) | pub fn change_ptt_setting(app: AppHandle, enabled: bool) -> Result<(), S...
function change_audio_feedback_setting (line 485) | pub fn change_audio_feedback_setting(app: AppHandle, enabled: bool) -> R...
function change_audio_feedback_volume_setting (line 494) | pub fn change_audio_feedback_volume_setting(app: AppHandle, volume: f32)...
function change_sound_theme_setting (line 503) | pub fn change_sound_theme_setting(app: AppHandle, theme: String) -> Resu...
function change_translate_to_english_setting (line 521) | pub fn change_translate_to_english_setting(app: AppHandle, enabled: bool...
function change_selected_language_setting (line 530) | pub fn change_selected_language_setting(app: AppHandle, language: String...
function change_overlay_position_setting (line 539) | pub fn change_overlay_position_setting(app: AppHandle, position: String)...
function change_debug_mode_setting (line 561) | pub fn change_debug_mode_setting(app: AppHandle, enabled: bool) -> Resul...
function change_start_hidden_setting (line 580) | pub fn change_start_hidden_setting(app: AppHandle, enabled: bool) -> Res...
function change_autostart_setting (line 599) | pub fn change_autostart_setting(app: AppHandle, enabled: bool) -> Result...
function change_update_checks_setting (line 626) | pub fn change_update_checks_setting(app: AppHandle, enabled: bool) -> Re...
function update_custom_words (line 644) | pub fn update_custom_words(app: AppHandle, words: Vec<String>) -> Result...
function change_word_correction_threshold_setting (line 653) | pub fn change_word_correction_threshold_setting(
function change_extra_recording_buffer_setting (line 665) | pub fn change_extra_recording_buffer_setting(app: AppHandle, ms: u64) ->...
function change_paste_method_setting (line 674) | pub fn change_paste_method_setting(app: AppHandle, method: String) -> Re...
function get_available_typing_tools (line 695) | pub fn get_available_typing_tools() -> Vec<String> {
function change_typing_tool_setting (line 708) | pub fn change_typing_tool_setting(app: AppHandle, tool: String) -> Resul...
function change_external_script_path_setting (line 729) | pub fn change_external_script_path_setting(
function change_clipboard_handling_setting (line 741) | pub fn change_clipboard_handling_setting(app: AppHandle, handling: Strin...
function change_auto_submit_setting (line 761) | pub fn change_auto_submit_setting(app: AppHandle, enabled: bool) -> Resu...
function change_auto_submit_key_setting (line 770) | pub fn change_auto_submit_key_setting(app: AppHandle, key: String) -> Re...
function change_post_process_enabled_setting (line 788) | pub fn change_post_process_enabled_setting(app: AppHandle, enabled: bool...
function change_experimental_enabled_setting (line 811) | pub fn change_experimental_enabled_setting(app: AppHandle, enabled: bool...
function change_post_process_base_url_setting (line 820) | pub fn change_post_process_base_url_setting(
function validate_provider_exists (line 848) | fn validate_provider_exists(
function change_post_process_api_key_setting (line 864) | pub fn change_post_process_api_key_setting(
function change_post_process_model_setting (line 878) | pub fn change_post_process_model_setting(
function set_post_process_provider (line 892) | pub fn set_post_process_provider(app: AppHandle, provider_id: String) ->...
function add_post_process_prompt (line 902) | pub fn add_post_process_prompt(
function update_post_process_prompt (line 926) | pub fn update_post_process_prompt(
function delete_post_process_prompt (line 950) | pub fn delete_post_process_prompt(app: AppHandle, id: String) -> Result<...
function fetch_post_process_models (line 978) | pub async fn fetch_post_process_models(
function set_post_process_selected_prompt (line 1023) | pub fn set_post_process_selected_prompt(app: AppHandle, id: String) -> R...
function change_mute_while_recording_setting (line 1038) | pub fn change_mute_while_recording_setting(app: AppHandle, enabled: bool...
function change_append_trailing_space_setting (line 1047) | pub fn change_append_trailing_space_setting(app: AppHandle, enabled: boo...
function change_lazy_stream_close_setting (line 1056) | pub fn change_lazy_stream_close_setting(app: AppHandle, enabled: bool) -...
function change_app_language_setting (line 1065) | pub fn change_app_language_setting(app: AppHandle, language: String) -> ...
function change_show_tray_icon_setting (line 1078) | pub fn change_show_tray_icon_setting(app: AppHandle, enabled: bool) -> R...
function apply_and_reload_accelerator (line 1091) | fn apply_and_reload_accelerator(app: &AppHandle, s: settings::AppSetting...
function change_whisper_accelerator_setting (line 1105) | pub fn change_whisper_accelerator_setting(
function change_ort_accelerator_setting (line 1117) | pub fn change_ort_accelerator_setting(
function get_available_accelerators (line 1130) | pub fn get_available_accelerators() -> crate::managers::transcription::A...
FILE: src-tauri/src/shortcut/tauri_impl.rs
function init_shortcuts (line 17) | pub fn init_shortcuts(app: &AppHandle) {
function validate_shortcut (line 44) | pub fn validate_shortcut(raw: &str) -> Result<(), String> {
function register_shortcut (line 73) | pub fn register_shortcut(app: &AppHandle, binding: ShortcutBinding) -> R...
function unregister_shortcut (line 132) | pub fn unregister_shortcut(app: &AppHandle, binding: ShortcutBinding) ->...
function register_cancel_shortcut (line 158) | pub fn register_cancel_shortcut(app: &AppHandle) {
function unregister_cancel_shortcut (line 180) | pub fn unregister_cancel_shortcut(app: &AppHandle) {
FILE: src-tauri/src/signal_handle.rs
function send_transcription_input (line 16) | pub fn send_transcription_input(app: &AppHandle, binding_id: &str, sourc...
function setup_signal_handler (line 25) | pub fn setup_signal_handler(app_handle: AppHandle, mut signals: Signals) {
FILE: src-tauri/src/transcription_coordinator.rs
constant DEBOUNCE (line 10) | const DEBOUNCE: Duration = Duration::from_millis(30);
type Command (line 13) | enum Command {
type Stage (line 27) | enum Stage {
type TranscriptionCoordinator (line 36) | pub struct TranscriptionCoordinator {
method new (line 45) | pub fn new(app: AppHandle) -> Self {
method send_input (line 121) | pub fn send_input(
method notify_cancel (line 142) | pub fn notify_cancel(&self, recording_was_active: bool) {
method notify_processing_finished (line 154) | pub fn notify_processing_finished(&self) {
function is_transcribe_binding (line 40) | pub fn is_transcribe_binding(id: &str) -> bool {
function start (line 161) | fn start(app: &AppHandle, stage: &mut Stage, binding_id: &str, hotkey_st...
function stop (line 177) | fn stop(app: &AppHandle, stage: &mut Stage, binding_id: &str, hotkey_str...
FILE: src-tauri/src/tray.rs
type TrayIconState (line 15) | pub enum TrayIconState {
type AppTheme (line 22) | pub enum AppTheme {
function get_current_theme (line 29) | pub fn get_current_theme(app: &AppHandle) -> AppTheme {
function get_icon_path (line 48) | pub fn get_icon_path(theme: AppTheme, state: TrayIconState) -> &'static ...
function change_tray_icon (line 65) | pub fn change_tray_icon(app: &AppHandle, icon: TrayIconState) {
function update_tray_menu (line 84) | pub fn update_tray_menu(app: &AppHandle, state: &TrayIconState, locale: ...
function last_transcript_text (line 217) | fn last_transcript_text(entry: &HistoryEntry) -> &str {
function set_tray_visibility (line 224) | pub fn set_tray_visibility(app: &AppHandle, visible: bool) {
function copy_last_transcript (line 233) | pub fn copy_last_transcript(app: &AppHandle) {
function build_entry (line 260) | fn build_entry(transcription: &str, post_processed: Option<&str>) -> His...
function uses_post_processed_text_when_available (line 274) | fn uses_post_processed_text_when_available() {
function falls_back_to_raw_transcription (line 280) | fn falls_back_to_raw_transcription() {
FILE: src-tauri/src/tray_i18n.rs
function get_tray_translations (line 24) | pub fn get_tray_translations(locale: Option<String>) -> TrayStrings {
FILE: src-tauri/src/utils.rs
function cancel_current_operation (line 17) | pub fn cancel_current_operation(app: &AppHandle) {
function is_wayland (line 46) | pub fn is_wayland() -> bool {
function is_kde_plasma (line 55) | pub fn is_kde_plasma() -> bool {
function is_kde_wayland (line 64) | pub fn is_kde_wayland() -> bool {
FILE: src-tauri/swift/apple_intelligence_bridge.h
type AppleLLMResponse (line 10) | typedef struct {
FILE: src/App.tsx
type OnboardingStep (line 21) | type OnboardingStep = "accessibility" | "model" | "done";
function App (line 29) | function App() {
FILE: src/bindings.ts
method changeBinding (line 8) | async changeBinding(id: string, binding: string) : Promise<Result<Bindin...
method resetBinding (line 16) | async resetBinding(id: string) : Promise<Result<BindingResponse, string>> {
method changePttSetting (line 24) | async changePttSetting(enabled: boolean) : Promise<Result<null, string>> {
method changeAudioFeedbackSetting (line 32) | async changeAudioFeedbackSetting(enabled: boolean) : Promise<Result<null...
method changeAudioFeedbackVolumeSetting (line 40) | async changeAudioFeedbackVolumeSetting(volume: number) : Promise<Result<...
method changeSoundThemeSetting (line 48) | async changeSoundThemeSetting(theme: string) : Promise<Result<null, stri...
method changeStartHiddenSetting (line 56) | async changeStartHiddenSetting(enabled: boolean) : Promise<Result<null, ...
method changeAutostartSetting (line 64) | async changeAutostartSetting(enabled: boolean) : Promise<Result<null, st...
method changeTranslateToEnglishSetting (line 72) | async changeTranslateToEnglishSetting(enabled: boolean) : Promise<Result...
method changeSelectedLanguageSetting (line 80) | async changeSelectedLanguageSetting(language: string) : Promise<Result<n...
method changeOverlayPositionSetting (line 88) | async changeOverlayPositionSetting(position: string) : Promise<Result<nu...
method changeDebugModeSetting (line 96) | async changeDebugModeSetting(enabled: boolean) : Promise<Result<null, st...
method changeWordCorrectionThresholdSetting (line 104) | async changeWordCorrectionThresholdSetting(threshold: number) : Promise<...
method changeExtraRecordingBufferSetting (line 112) | async changeExtraRecordingBufferSetting(ms: number) : Promise<Result<nul...
method changePasteMethodSetting (line 120) | async changePasteMethodSetting(method: string) : Promise<Result<null, st...
method getAvailableTypingTools (line 128) | async getAvailableTypingTools() : Promise<string[]> {
method changeTypingToolSetting (line 131) | async changeTypingToolSetting(tool: string) : Promise<Result<null, strin...
method changeExternalScriptPathSetting (line 139) | async changeExternalScriptPathSetting(path: string | null) : Promise<Res...
method changeClipboardHandlingSetting (line 147) | async changeClipboardHandlingSetting(handling: string) : Promise<Result<...
method changeAutoSubmitSetting (line 155) | async changeAutoSubmitSetting(enabled: boolean) : Promise<Result<null, s...
method changeAutoSubmitKeySetting (line 163) | async changeAutoSubmitKeySetting(key: string) : Promise<Result<null, str...
method changePostProcessEnabledSetting (line 171) | async changePostProcessEnabledSetting(enabled: boolean) : Promise<Result...
method changeExperimentalEnabledSetting (line 179) | async changeExperimentalEnabledSetting(enabled: boolean) : Promise<Resul...
method changePostProcessBaseUrlSetting (line 187) | async changePostProcessBaseUrlSetting(providerId: string, baseUrl: strin...
method changePostProcessApiKeySetting (line 195) | async changePostProcessApiKeySetting(providerId: string, apiKey: string)...
method changePostProcessModelSetting (line 203) | async changePostProcessModelSetting(providerId: string, model: string) :...
method setPostProcessProvider (line 211) | async setPostProcessProvider(providerId: string) : Promise<Result<null, ...
method fetchPostProcessModels (line 219) | async fetchPostProcessModels(providerId: string) : Promise<Result<string...
method addPostProcessPrompt (line 227) | async addPostProcessPrompt(name: string, prompt: string) : Promise<Resul...
method updatePostProcessPrompt (line 235) | async updatePostProcessPrompt(id: string, name: string, prompt: string) ...
method deletePostProcessPrompt (line 243) | async deletePostProcessPrompt(id: string) : Promise<Result<null, string>> {
method setPostProcessSelectedPrompt (line 251) | async setPostProcessSelectedPrompt(id: string) : Promise<Result<null, st...
method updateCustomWords (line 259) | async updateCustomWords(words: string[]) : Promise<Result<null, string>> {
method suspendBinding (line 271) | async suspendBinding(id: string) : Promise<Result<null, string>> {
method resumeBinding (line 282) | async resumeBinding(id: string) : Promise<Result<null, string>> {
method changeMuteWhileRecordingSetting (line 290) | async changeMuteWhileRecordingSetting(enabled: boolean) : Promise<Result...
method changeAppendTrailingSpaceSetting (line 298) | async changeAppendTrailingSpaceSetting(enabled: boolean) : Promise<Resul...
method changeLazyStreamCloseSetting (line 306) | async changeLazyStreamCloseSetting(enabled: boolean) : Promise<Result<nu...
method changeAppLanguageSetting (line 314) | async changeAppLanguageSetting(language: string) : Promise<Result<null, ...
method changeUpdateChecksSetting (line 322) | async changeUpdateChecksSetting(enabled: boolean) : Promise<Result<null,...
method changeKeyboardImplementationSetting (line 336) | async changeKeyboardImplementationSetting(implementation: string) : Prom...
method getKeyboardImplementation (line 347) | async getKeyboardImplementation() : Promise<string> {
method changeShowTrayIconSetting (line 350) | async changeShowTrayIconSetting(enabled: boolean) : Promise<Result<null,...
method changeWhisperAcceleratorSetting (line 358) | async changeWhisperAcceleratorSetting(accelerator: WhisperAcceleratorSet...
method changeOrtAcceleratorSetting (line 366) | async changeOrtAcceleratorSetting(accelerator: OrtAcceleratorSetting) : ...
method getAvailableAccelerators (line 377) | async getAvailableAccelerators() : Promise<AvailableAccelerators> {
method startHandyKeysRecording (line 383) | async startHandyKeysRecording(bindingId: string) : Promise<Result<null, ...
method stopHandyKeysRecording (line 394) | async stopHandyKeysRecording() : Promise<Result<null, string>> {
method triggerUpdateCheck (line 402) | async triggerUpdateCheck() : Promise<Result<null, string>> {
method showMainWindowCommand (line 410) | async showMainWindowCommand() : Promise<Result<null, string>> {
method cancelOperation (line 418) | async cancelOperation() : Promise<void> {
method getAppDirPath (line 421) | async getAppDirPath() : Promise<Result<string, string>> {
method getAppSettings (line 429) | async getAppSettings() : Promise<Result<AppSettings, string>> {
method getDefaultSettings (line 437) | async getDefaultSettings() : Promise<Result<AppSettings, string>> {
method getLogDirPath (line 445) | async getLogDirPath() : Promise<Result<string, string>> {
method setLogLevel (line 453) | async setLogLevel(level: LogLevel) : Promise<Result<null, string>> {
method openRecordingsFolder (line 461) | async openRecordingsFolder() : Promise<Result<null, string>> {
method openLogDir (line 469) | async openLogDir() : Promise<Result<null, string>> {
method openAppDataDir (line 477) | async openAppDataDir() : Promise<Result<null, string>> {
method checkAppleIntelligenceAvailable (line 489) | async checkAppleIntelligenceAvailable() : Promise<boolean> {
method initializeEnigo (line 496) | async initializeEnigo() : Promise<Result<null, string>> {
method initializeShortcuts (line 509) | async initializeShortcuts() : Promise<Result<null, string>> {
method getAvailableModels (line 517) | async getAvailableModels() : Promise<Result<ModelInfo[], string>> {
method getModelInfo (line 525) | async getModelInfo(modelId: string) : Promise<Result<ModelInfo | null, s...
method downloadModel (line 533) | async downloadModel(modelId: string) : Promise<Result<null, string>> {
method deleteModel (line 541) | async deleteModel(modelId: string) : Promise<Result<null, string>> {
method cancelDownload (line 549) | async cancelDownload(modelId: string) : Promise<Result<null, string>> {
method setActiveModel (line 557) | async setActiveModel(modelId: string) : Promise<Result<null, string>> {
method getCurrentModel (line 565) | async getCurrentModel() : Promise<Result<string, string>> {
method getTranscriptionModelStatus (line 573) | async getTranscriptionModelStatus() : Promise<Result<string | null, stri...
method isModelLoading (line 581) | async isModelLoading() : Promise<Result<boolean, string>> {
method hasAnyModelsAvailable (line 589) | async hasAnyModelsAvailable() : Promise<Result<boolean, string>> {
method hasAnyModelsOrDownloads (line 597) | async hasAnyModelsOrDownloads() : Promise<Result<boolean, string>> {
method updateMicrophoneMode (line 605) | async updateMicrophoneMode(alwaysOn: boolean) : Promise<Result<null, str...
method getMicrophoneMode (line 613) | async getMicrophoneMode() : Promise<Result<boolean, string>> {
method getWindowsMicrophonePermissionStatus (line 621) | async getWindowsMicrophonePermissionStatus() : Promise<WindowsMicrophone...
method openMicrophonePrivacySettings (line 624) | async openMicrophonePrivacySettings() : Promise<Result<null, string>> {
method getAvailableMicrophones (line 632) | async getAvailableMicrophones() : Promise<Result<AudioDevice[], string>> {
method setSelectedMicrophone (line 640) | async setSelectedMicrophone(deviceName: string) : Promise<Result<null, s...
method getSelectedMicrophone (line 648) | async getSelectedMicrophone() : Promise<Result<string, string>> {
method getAvailableOutputDevices (line 656) | async getAvailableOutputDevices() : Promise<Result<AudioDevice[], string...
method setSelectedOutputDevice (line 664) | async setSelectedOutputDevice(deviceName: string) : Promise<Result<null,...
method getSelectedOutputDevice (line 672) | async getSelectedOutputDevice() : Promise<Result<string, string>> {
method playTestSound (line 680) | async playTestSound(soundType: string) : Promise<void> {
method checkCustomSounds (line 683) | async checkCustomSounds() : Promise<CustomSounds> {
method setClamshellMicrophone (line 686) | async setClamshellMicrophone(deviceName: string) : Promise<Result<null, ...
method getClamshellMicrophone (line 694) | async getClamshellMicrophone() : Promise<Result<string, string>> {
method isRecording (line 702) | async isRecording() : Promise<boolean> {
method setModelUnloadTimeout (line 705) | async setModelUnloadTimeout(timeout: ModelUnloadTimeout) : Promise<void> {
method getModelLoadStatus (line 708) | async getModelLoadStatus() : Promise<Result<ModelLoadStatus, string>> {
method unloadModelManually (line 716) | async unloadModelManually() : Promise<Result<null, string>> {
method getHistoryEntries (line 724) | async getHistoryEntries() : Promise<Result<HistoryEntry[], string>> {
method toggleHistoryEntrySaved (line 732) | async toggleHistoryEntrySaved(id: number) : Promise<Result<null, string>> {
method getAudioFilePath (line 740) | async getAudioFilePath(fileName: string) : Promise<Result<string, string...
method deleteHistoryEntry (line 748) | async deleteHistoryEntry(id: number) : Promise<Result<null, string>> {
method updateHistoryLimit (line 756) | async updateHistoryLimit(limit: number) : Promise<Result<null, string>> {
method updateRecordingRetentionPeriod (line 764) | async updateRecordingRetentionPeriod(period: string) : Promise<Result<nu...
method isLaptop (line 778) | async isLaptop() : Promise<Result<boolean, string>> {
type AppSettings (line 798) | type AppSettings = { bindings: Partial<{ [key in string]: ShortcutBindin...
type AudioDevice (line 799) | type AudioDevice = { index: string; name: string; is_default: boolean }
type AutoSubmitKey (line 800) | type AutoSubmitKey = "enter" | "ctrl_enter" | "cmd_enter"
type AvailableAccelerators (line 801) | type AvailableAccelerators = { whisper: string[]; ort: string[] }
type BindingResponse (line 802) | type BindingResponse = { success: boolean; binding: ShortcutBinding | nu...
type ClipboardHandling (line 803) | type ClipboardHandling = "dont_modify" | "copy_to_clipboard"
type CustomSounds (line 804) | type CustomSounds = { start: boolean; stop: boolean }
type EngineType (line 805) | type EngineType = "Whisper" | "Parakeet" | "Moonshine" | "MoonshineStrea...
type HistoryEntry (line 806) | type HistoryEntry = { id: number; file_name: string; timestamp: number; ...
type ImplementationChangeResult (line 810) | type ImplementationChangeResult = { success: boolean;
type KeyboardImplementation (line 815) | type KeyboardImplementation = "tauri" | "handy_keys"
type LLMPrompt (line 816) | type LLMPrompt = { id: string; name: string; prompt: string }
type LogLevel (line 817) | type LogLevel = "trace" | "debug" | "info" | "warn" | "error"
type ModelInfo (line 818) | type ModelInfo = { id: string; name: string; description: string; filena...
type ModelLoadStatus (line 819) | type ModelLoadStatus = { is_loaded: boolean; current_model: string | null }
type ModelUnloadTimeout (line 820) | type ModelUnloadTimeout = "never" | "immediately" | "min_2" | "min_5" | ...
type OrtAcceleratorSetting (line 821) | type OrtAcceleratorSetting = "auto" | "cpu" | "cuda" | "directml" | "rocm"
type OverlayPosition (line 822) | type OverlayPosition = "none" | "top" | "bottom"
type PasteMethod (line 823) | type PasteMethod = "ctrl_v" | "direct" | "none" | "shift_insert" | "ctrl...
type PermissionAccess (line 824) | type PermissionAccess = "allowed" | "denied" | "unknown"
type PostProcessProvider (line 825) | type PostProcessProvider = { id: string; label: string; base_url: string...
type RecordingRetentionPeriod (line 826) | type RecordingRetentionPeriod = "never" | "preserve_limit" | "days_3" | ...
type ShortcutBinding (line 827) | type ShortcutBinding = { id: string; name: string; description: string; ...
type SoundTheme (line 828) | type SoundTheme = "marimba" | "pop" | "custom"
type TypingTool (line 829) | type TypingTool = "auto" | "wtype" | "kwtype" | "dotool" | "ydotool" | "...
type WhisperAcceleratorSetting (line 830) | type WhisperAcceleratorSetting = "auto" | "cpu" | "gpu"
type WindowsMicrophonePermissionStatus (line 831) | type WindowsMicrophonePermissionStatus = { supported: boolean; overall_a...
type __EventObj__ (line 842) | type __EventObj__<T> = {
type Result (line 854) | type Result<T, E> =
function __makeEvents__ (line 858) | function __makeEvents__<T extends Record<string, any>>(
FILE: src/components/AccessibilityPermissions.tsx
type PermissionState (line 10) | type PermissionState = "request" | "verify" | "granted";
type ButtonConfig (line 13) | interface ButtonConfig {
FILE: src/components/Sidebar.tsx
type SidebarSection (line 17) | type SidebarSection = keyof typeof SECTIONS_CONFIG;
type IconProps (line 19) | interface IconProps {
type SectionConfig (line 27) | interface SectionConfig {
constant SECTIONS_CONFIG (line 34) | const SECTIONS_CONFIG = {
type SidebarProps (line 79) | interface SidebarProps {
FILE: src/components/icons/CancelIcon.tsx
type CancelIconProps (line 3) | interface CancelIconProps {
FILE: src/components/icons/MicrophoneIcon.tsx
type MicrophoneIconProps (line 3) | interface MicrophoneIconProps {
FILE: src/components/icons/ResetIcon.tsx
type ResetIconProps (line 3) | interface ResetIconProps {
FILE: src/components/icons/TranscriptionIcon.tsx
type TranscriptionIconProps (line 3) | interface TranscriptionIconProps {
FILE: src/components/model-selector/DownloadProgressDisplay.tsx
type DownloadProgress (line 4) | interface DownloadProgress {
type DownloadStats (line 11) | interface DownloadStats {
type DownloadProgressDisplayProps (line 18) | interface DownloadProgressDisplayProps {
FILE: src/components/model-selector/ModelDropdown.tsx
type ModelDropdownProps (line 9) | interface ModelDropdownProps {
FILE: src/components/model-selector/ModelSelector.tsx
type ModelStatus (line 13) | type ModelStatus =
type ModelSelectorProps (line 22) | interface ModelSelectorProps {
FILE: src/components/model-selector/ModelStatusButton.tsx
type ModelStatus (line 3) | type ModelStatus =
type ModelStatusButtonProps (line 12) | interface ModelStatusButtonProps {
FILE: src/components/onboarding/AccessibilityOnboarding.tsx
type AccessibilityOnboardingProps (line 16) | interface AccessibilityOnboardingProps {
type PermissionStatus (line 20) | type PermissionStatus = "checking" | "needed" | "waiting" | "granted";
type PermissionPlatform (line 21) | type PermissionPlatform = "macos" | "windows" | "other";
type PermissionsState (line 23) | interface PermissionsState {
FILE: src/components/onboarding/ModelCard.tsx
type ModelCardStatus (line 35) | type ModelCardStatus =
type ModelCardProps (line 43) | interface ModelCardProps {
FILE: src/components/onboarding/Onboarding.tsx
type OnboardingProps (line 10) | interface OnboardingProps {
FILE: src/components/settings/AccelerationSelector.tsx
constant WHISPER_LABELS (line 12) | const WHISPER_LABELS: Record<WhisperAcceleratorSetting, string> = {
constant ORT_LABELS (line 18) | const ORT_LABELS: Record<OrtAcceleratorSetting, string> = {
type AccelerationSelectorProps (line 26) | interface AccelerationSelectorProps {
FILE: src/components/settings/AlwaysOnMicrophone.tsx
type AlwaysOnMicrophoneProps (line 6) | interface AlwaysOnMicrophoneProps {
FILE: src/components/settings/AppDataDirectory.tsx
type AppDataDirectoryProps (line 7) | interface AppDataDirectoryProps {
FILE: src/components/settings/AppLanguageSelector.tsx
type AppLanguageSelectorProps (line 8) | interface AppLanguageSelectorProps {
FILE: src/components/settings/AppendTrailingSpace.tsx
type AppendTrailingSpaceProps (line 6) | interface AppendTrailingSpaceProps {
FILE: src/components/settings/AudioFeedback.tsx
type AudioFeedbackProps (line 8) | interface AudioFeedbackProps {
FILE: src/components/settings/AutoSubmit.tsx
type AutoSubmitProps (line 9) | interface AutoSubmitProps {
type AutoSubmitOptionValue (line 14) | type AutoSubmitOptionValue = AutoSubmitKey | "off";
FILE: src/components/settings/AutostartToggle.tsx
type AutostartToggleProps (line 6) | interface AutostartToggleProps {
FILE: src/components/settings/ClamshellMicrophoneSelector.tsx
type ClamshellMicrophoneSelectorProps (line 9) | interface ClamshellMicrophoneSelectorProps {
FILE: src/components/settings/ClipboardHandling.tsx
type ClipboardHandlingProps (line 8) | interface ClipboardHandlingProps {
FILE: src/components/settings/CustomWords.tsx
type CustomWordsProps (line 9) | interface CustomWordsProps {
FILE: src/components/settings/ExperimentalToggle.tsx
type ExperimentalToggleProps (line 6) | interface ExperimentalToggleProps {
FILE: src/components/settings/GlobalShortcutInput.tsx
type GlobalShortcutInputProps (line 15) | interface GlobalShortcutInputProps {
FILE: src/components/settings/HandyKeysShortcutInput.tsx
type HandyKeysShortcutInputProps (line 12) | interface HandyKeysShortcutInputProps {
type HandyKeysEvent (line 19) | interface HandyKeysEvent {
FILE: src/components/settings/HistoryLimit.tsx
type HistoryLimitProps (line 7) | interface HistoryLimitProps {
FILE: src/components/settings/LanguageSelector.tsx
type LanguageSelectorProps (line 8) | interface LanguageSelectorProps {
FILE: src/components/settings/LazyStreamClose.tsx
type LazyStreamCloseProps (line 6) | interface LazyStreamCloseProps {
FILE: src/components/settings/MicrophoneSelector.tsx
type MicrophoneSelectorProps (line 8) | interface MicrophoneSelectorProps {
FILE: src/components/settings/ModelUnloadTimeout.tsx
type ModelUnloadTimeoutProps (line 8) | interface ModelUnloadTimeoutProps {
FILE: src/components/settings/MuteWhileRecording.tsx
type MuteWhileRecordingToggleProps (line 6) | interface MuteWhileRecordingToggleProps {
FILE: src/components/settings/OutputDeviceSelector.tsx
type OutputDeviceSelectorProps (line 9) | interface OutputDeviceSelectorProps {
FILE: src/components/settings/PasteMethod.tsx
type PasteMethodProps (line 10) | interface PasteMethodProps {
FILE: src/components/settings/PostProcessingSettingsApi/ApiKeyField.tsx
type ApiKeyFieldProps (line 4) | interface ApiKeyFieldProps {
FILE: src/components/settings/PostProcessingSettingsApi/BaseUrlField.tsx
type BaseUrlFieldProps (line 4) | interface BaseUrlFieldProps {
FILE: src/components/settings/PostProcessingSettingsApi/ModelSelect.tsx
type ModelSelectProps (line 5) | type ModelSelectProps = {
FILE: src/components/settings/PostProcessingSettingsApi/ProviderSelect.tsx
type ProviderSelectProps (line 4) | interface ProviderSelectProps {
FILE: src/components/settings/PostProcessingSettingsApi/types.ts
type ModelOption (line 1) | type ModelOption = {
FILE: src/components/settings/PostProcessingSettingsApi/usePostProcessProviderState.ts
type PostProcessProviderState (line 7) | type PostProcessProviderState = {
constant APPLE_PROVIDER_ID (line 31) | const APPLE_PROVIDER_ID = "apple_intelligence";
FILE: src/components/settings/PostProcessingToggle.tsx
type PostProcessingToggleProps (line 6) | interface PostProcessingToggleProps {
FILE: src/components/settings/PushToTalk.tsx
type PushToTalkProps (line 6) | interface PushToTalkProps {
FILE: src/components/settings/RecordingRetentionPeriod.tsx
type RecordingRetentionPeriodProps (line 8) | interface RecordingRetentionPeriodProps {
FILE: src/components/settings/ShortcutInput.tsx
type ShortcutInputProps (line 6) | interface ShortcutInputProps {
FILE: src/components/settings/ShowOverlay.tsx
type ShowOverlayProps (line 8) | interface ShowOverlayProps {
FILE: src/components/settings/ShowTrayIcon.tsx
type ShowTrayIconProps (line 6) | interface ShowTrayIconProps {
FILE: src/components/settings/SoundPicker.tsx
type SoundPickerProps (line 9) | interface SoundPickerProps {
FILE: src/components/settings/StartHidden.tsx
type StartHiddenProps (line 6) | interface StartHiddenProps {
FILE: src/components/settings/TranslateToEnglish.tsx
type TranslateToEnglishProps (line 6) | interface TranslateToEnglishProps {
FILE: src/components/settings/TypingTool.tsx
type TypingToolProps (line 10) | interface TypingToolProps {
FILE: src/components/settings/UpdateChecksToggle.tsx
type UpdateChecksToggleProps (line 6) | interface UpdateChecksToggleProps {
FILE: src/components/settings/debug/DebugPaths.tsx
type DebugPathsProps (line 5) | interface DebugPathsProps {
FILE: src/components/settings/debug/KeyboardImplementationSelector.tsx
constant KEYBOARD_IMPLEMENTATION_OPTIONS (line 9) | const KEYBOARD_IMPLEMENTATION_OPTIONS: DropdownOption[] = [
type KeyboardImplementationSelectorProps (line 14) | interface KeyboardImplementationSelectorProps {
FILE: src/components/settings/debug/LogDirectory.tsx
type LogDirectoryProps (line 7) | interface LogDirectoryProps {
FILE: src/components/settings/debug/LogLevelSelector.tsx
constant LOG_LEVEL_OPTIONS (line 8) | const LOG_LEVEL_OPTIONS: DropdownOption[] = [
type LogLevelSelectorProps (line 16) | interface LogLevelSelectorProps {
FILE: src/components/settings/debug/PasteDelay.tsx
type PasteDelayProps (line 6) | interface PasteDelayProps {
FILE: src/components/settings/debug/RecordingBuffer.tsx
type RecordingBufferProps (line 6) | interface RecordingBufferProps {
FILE: src/components/settings/debug/WordCorrectionThreshold.tsx
type WordCorrectionThresholdProps (line 6) | interface WordCorrectionThresholdProps {
FILE: src/components/settings/history/HistorySettings.tsx
type OpenRecordingsButtonProps (line 13) | interface OpenRecordingsButtonProps {
type HistoryEntryProps (line 218) | interface HistoryEntryProps {
FILE: src/components/shared/ProgressBar.tsx
type ProgressData (line 3) | interface ProgressData {
type ProgressBarProps (line 10) | interface ProgressBarProps {
FILE: src/components/ui/Alert.tsx
type AlertVariant (line 4) | type AlertVariant = "error" | "warning" | "info" | "success";
type AlertProps (line 6) | interface AlertProps {
FILE: src/components/ui/AudioPlayer.tsx
type AudioPlayerProps (line 4) | interface AudioPlayerProps {
FILE: src/components/ui/Badge.tsx
type BadgeProps (line 3) | interface BadgeProps {
FILE: src/components/ui/Button.tsx
type ButtonProps (line 3) | interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonEleme...
FILE: src/components/ui/Dropdown.tsx
type DropdownOption (line 4) | interface DropdownOption {
type DropdownProps (line 10) | interface DropdownProps {
FILE: src/components/ui/Input.tsx
type InputProps (line 3) | interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
FILE: src/components/ui/PathDisplay.tsx
type PathDisplayProps (line 5) | interface PathDisplayProps {
FILE: src/components/ui/ResetButton.tsx
type ResetButtonProps (line 4) | interface ResetButtonProps {
FILE: src/components/ui/Select.tsx
type SelectOption (line 11) | type SelectOption = {
type BaseProps (line 17) | type BaseProps = {
type CreatableProps (line 30) | type CreatableProps = {
type NonCreatableProps (line 35) | type NonCreatableProps = {
type SelectProps (line 40) | type SelectProps = BaseProps & (CreatableProps | NonCreatableProps);
FILE: src/components/ui/SettingContainer.tsx
type SettingContainerProps (line 4) | interface SettingContainerProps {
FILE: src/components/ui/SettingsGroup.tsx
type SettingsGroupProps (line 3) | interface SettingsGroupProps {
FILE: src/components/ui/Slider.tsx
type SliderProps (line 4) | interface SliderProps {
FILE: src/components/ui/TextDisplay.tsx
type TextDisplayProps (line 4) | interface TextDisplayProps {
FILE: src/components/ui/Textarea.tsx
type TextareaProps (line 3) | interface TextareaProps
FILE: src/components/ui/ToggleSwitch.tsx
type ToggleSwitchProps (line 4) | interface ToggleSwitchProps {
FILE: src/components/ui/Tooltip.tsx
type TooltipPosition (line 4) | type TooltipPosition = "top" | "bottom";
type TooltipCoords (line 6) | interface TooltipCoords {
type TooltipProps (line 13) | interface TooltipProps {
constant TOOLTIP_WIDTH (line 19) | const TOOLTIP_WIDTH = 200;
constant VIEWPORT_PADDING (line 20) | const VIEWPORT_PADDING = 12;
constant GAP (line 21) | const GAP = 8;
constant ARROW_MARGIN (line 22) | const ARROW_MARGIN = 12;
constant DEFAULT_HEIGHT (line 23) | const DEFAULT_HEIGHT = 60;
FILE: src/components/update-checker/UpdateChecker.tsx
type UpdateCheckerProps (line 9) | interface UpdateCheckerProps {
FILE: src/hooks/useOsType.ts
function useOsType (line 8) | function useOsType(): OSType {
FILE: src/hooks/useSettings.ts
type UseSettingsReturn (line 5) | interface UseSettingsReturn {
FILE: src/i18n/index.ts
constant SUPPORTED_LANGUAGES (line 28) | const SUPPORTED_LANGUAGES = Object.keys(resources)
type SupportedLanguageCode (line 52) | type SupportedLanguageCode = string;
FILE: src/i18n/languages.ts
constant LANGUAGE_METADATA (line 10) | const LANGUAGE_METADATA: Record<
FILE: src/lib/constants/languages.ts
type Language (line 1) | interface Language {
constant LANGUAGES (line 6) | const LANGUAGES: Language[] = [
FILE: src/lib/types/events.ts
type ModelStateEvent (line 1) | interface ModelStateEvent {
type RecordingErrorEvent (line 8) | interface RecordingErrorEvent {
FILE: src/lib/utils/keyboard.ts
type OSType (line 5) | type OSType = "macos" | "windows" | "linux" | "unknown";
FILE: src/lib/utils/modelTranslation.ts
function getTranslatedModelName (line 10) | function getTranslatedModelName(model: ModelInfo, t: TFunction): string {
function getTranslatedModelDescription (line 22) | function getTranslatedModelDescription(
FILE: src/overlay/RecordingOverlay.tsx
type OverlayState (line 14) | type OverlayState = "recording" | "transcribing" | "processing";
FILE: src/stores/modelStore.ts
type DownloadProgress (line 7) | interface DownloadProgress {
type DownloadStats (line 14) | interface DownloadStats {
type ModelsStore (line 22) | interface ModelsStore {
FILE: src/stores/settingsStore.ts
type SettingsStore (line 12) | interface SettingsStore {
constant DEFAULT_AUDIO_DEVICE (line 70) | const DEFAULT_AUDIO_DEVICE: AudioDevice = {
Condensed preview — 249 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,613K chars).
[
{
"path": ".cargo/config.toml",
"chars": 8,
"preview": "[build]\n"
},
{
"path": ".github/FUNDING.yml",
"chars": 127,
"preview": "github: cjpais\ncustom: [\"https://handy.computer/donate\", \"https://www.paypal.me/cjpais\"]\nbuy_me_a_coffee: cjpais\nko_fi: "
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 956,
"preview": "---\nname: Bug Report\nabout: Create a report to help us improve Handy\ntitle: \"[BUG] \"\nlabels: [\"bug\"]\nassignees: \"\"\n---\n\n"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 736,
"preview": "blank_issues_enabled: false\ncontact_links:\n - name: ✏️ Post-processing / Editing Transcripts\n url: https://github.co"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 2526,
"preview": "## Before Submitting This PR\n\n<!--\nHANDY IS UNDERGOING A FEATURE FREEZE. IF YOU ARE SUBMITTING A PR WHICH IS A NEW FEATU"
},
{
"path": ".github/workflows/build-test.yml",
"chars": 1591,
"preview": "name: \"Build Test\"\n\non: workflow_dispatch\n\njobs:\n build-test:\n permissions:\n contents: write\n strategy:\n "
},
{
"path": ".github/workflows/build.yml",
"chars": 22549,
"preview": "name: \"Build\"\n\non:\n workflow_call:\n inputs:\n platform:\n required: true\n type: string\n target"
},
{
"path": ".github/workflows/code-quality.yml",
"chars": 1109,
"preview": "name: \"code quality\"\non:\n workflow_dispatch:\n push:\n branches: [main]\n paths:\n - \"src/**\"\n - \"package."
},
{
"path": ".github/workflows/main-build.yml",
"chars": 1603,
"preview": "name: \"Main Branch Build\"\n\n# Runs the full cross-platform build on every push to main so breakage is\n# caught before a m"
},
{
"path": ".github/workflows/nix-check.yml",
"chars": 4797,
"preview": "# Nix CI — two tiers:\n#\n# 1. Quick checks (bun.nix sync, flake eval) run on ANY source change\n# so compilation-breaki"
},
{
"path": ".github/workflows/playwright.yml",
"chars": 929,
"preview": "name: \"Playwright\"\non:\n workflow_dispatch:\n pull_request:\n paths:\n - \"src/**\"\n - \"package.json\"\n - \""
},
{
"path": ".github/workflows/pr-test-build.yml",
"chars": 2389,
"preview": "name: \"PR Test Build\"\n\non:\n workflow_dispatch:\n inputs:\n pr_number:\n description: \"PR number to build\"\n "
},
{
"path": ".github/workflows/release.yml",
"chars": 2795,
"preview": "name: \"Release\"\n\non: workflow_dispatch\n\njobs:\n create-release:\n permissions:\n contents: write\n runs-on: ubun"
},
{
"path": ".github/workflows/test.yml",
"chars": 1086,
"preview": "name: \"test\"\non:\n workflow_dispatch:\n push:\n branches: [main]\n paths:\n - \"src-tauri/**\"\n pull_request:\n "
},
{
"path": ".gitignore",
"chars": 415,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\npackage-lock.json\n\nno"
},
{
"path": ".nix/bun-lock-hash",
"chars": 65,
"preview": "e00b12c719a762004194cec01f2ad0b78ae483c41452bcca8537179d28e704b1\n"
},
{
"path": ".nix/bun.nix",
"chars": 84344,
"preview": "# Autogenerated by `bun2nix`, editing manually is not recommended\n#\n# Set of Bun packages to install\n#\n# Consume this wi"
},
{
"path": ".prettierignore",
"chars": 194,
"preview": "# Dependencies\nnode_modules\nbun.lock\npackage-lock.json\n\n# Build outputs\ndist\ntarget\n*.bundle.*\n\n# Tauri\nsrc-tauri/target"
},
{
"path": ".prettierrc",
"chars": 24,
"preview": "{\n \"endOfLine\": \"lf\"\n}\n"
},
{
"path": ".vscode/extensions.json",
"chars": 122,
"preview": "{\n \"recommendations\": [\n \"tauri-apps.tauri-vscode\",\n \"rust-lang.rust-analyzer\",\n \"esbenp.prettier-vscode\"\n ]\n"
},
{
"path": "AGENTS.md",
"chars": 3887,
"preview": "# AGENTS.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## "
},
{
"path": "BUILD.md",
"chars": 3460,
"preview": "# Build Instructions\n\nThis guide covers how to set up the development environment and build Handy from source across dif"
},
{
"path": "CHANGELOG.md",
"chars": 4085,
"preview": "# Changelog\n\n## [0.3.0] - 2025-07-11\n\n### Added\n\n- **Translate to English** setting: Added automatic translation of spee"
},
{
"path": "CLAUDE.md",
"chars": 6061,
"preview": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## "
},
{
"path": "CONTRIBUTING.md",
"chars": 10652,
"preview": "# Contributing to Handy\n\nThank you for your interest in contributing to Handy! This guide will help you get started with"
},
{
"path": "CONTRIBUTING_TRANSLATIONS.md",
"chars": 4250,
"preview": "# Contributing Translations to Handy\n\nThank you for helping translate Handy! This guide explains how to add or improve t"
},
{
"path": "CRUSH.md",
"chars": 2062,
"preview": "# Development Commands\n\n**Environment Setup:**\n\n```bash\nbun install # Install dependencies\nmkdir -p s"
},
{
"path": "LICENSE",
"chars": 1064,
"preview": "MIT License\n\nCopyright (c) 2025 CJ Pais\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof"
},
{
"path": "README.md",
"chars": 17271,
"preview": "# Handy\n\n[. Do not edit this file man"
},
{
"path": "src/components/AccessibilityPermissions.tsx",
"chars": 3460,
"preview": "import { useEffect, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { type } from \"@taur"
},
{
"path": "src/components/Sidebar.tsx",
"chars": 3498,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Cog, FlaskConical, History, Info, Sp"
},
{
"path": "src/components/footer/Footer.tsx",
"chars": 1151,
"preview": "import React, { useState, useEffect } from \"react\";\nimport { getVersion } from \"@tauri-apps/api/app\";\n\nimport ModelSelec"
},
{
"path": "src/components/footer/index.ts",
"chars": 36,
"preview": "export { default } from \"./Footer\";\n"
},
{
"path": "src/components/icons/CancelIcon.tsx",
"chars": 1192,
"preview": "import React from \"react\";\n\ninterface CancelIconProps {\n width?: number;\n height?: number;\n color?: string;\n classNa"
},
{
"path": "src/components/icons/HandyHand.tsx",
"chars": 8633,
"preview": "const HandyHand = ({\n width,\n height,\n}: {\n width?: number | string;\n height?: number | string;\n}) => (\n <svg\n w"
},
{
"path": "src/components/icons/HandyTextLogo.tsx",
"chars": 31585,
"preview": "import React from \"react\";\n\nconst HandyTextLogo = ({\n width,\n height,\n className,\n}: {\n width?: number;\n height?: n"
},
{
"path": "src/components/icons/MicrophoneIcon.tsx",
"chars": 2977,
"preview": "import React from \"react\";\n\ninterface MicrophoneIconProps {\n width?: number;\n height?: number;\n color?: string;\n cla"
},
{
"path": "src/components/icons/ResetIcon.tsx",
"chars": 1002,
"preview": "import React from \"react\";\n\ninterface ResetIconProps {\n width?: number;\n height?: number;\n color?: string;\n classNam"
},
{
"path": "src/components/icons/TranscriptionIcon.tsx",
"chars": 5517,
"preview": "import React from \"react\";\n\ninterface TranscriptionIconProps {\n width?: number;\n height?: number;\n color?: string;\n "
},
{
"path": "src/components/icons/index.ts",
"chars": 184,
"preview": "export { default as MicrophoneIcon } from \"./MicrophoneIcon\";\nexport { default as TranscriptionIcon } from \"./Transcript"
},
{
"path": "src/components/model-selector/DownloadProgressDisplay.tsx",
"chars": 1191,
"preview": "import React from \"react\";\nimport { ProgressBar, ProgressData } from \"../shared\";\n\ninterface DownloadProgress {\n model_"
},
{
"path": "src/components/model-selector/ModelDropdown.tsx",
"chars": 2690,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport type { ModelInfo } from \"@/bindings\";\n"
},
{
"path": "src/components/model-selector/ModelSelector.tsx",
"chars": 8019,
"preview": "import React, { useState, useRef, useEffect } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { lis"
},
{
"path": "src/components/model-selector/ModelStatusButton.tsx",
"chars": 1737,
"preview": "import React from \"react\";\n\ntype ModelStatus =\n | \"ready\"\n | \"loading\"\n | \"downloading\"\n | \"extracting\"\n | \"error\"\n"
},
{
"path": "src/components/model-selector/index.ts",
"chars": 251,
"preview": "export { default } from \"./ModelSelector\";\nexport { default as ModelStatusButton } from \"./ModelStatusButton\";\nexport { "
},
{
"path": "src/components/onboarding/AccessibilityOnboarding.tsx",
"chars": 13922,
"preview": "import { useEffect, useState, useCallback, useRef } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport"
},
{
"path": "src/components/onboarding/ModelCard.tsx",
"chars": 10064,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport {\n Check,\n Download,\n Globe,\n Lang"
},
{
"path": "src/components/onboarding/Onboarding.tsx",
"chars": 4340,
"preview": "import React, { useEffect, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { toast } fro"
},
{
"path": "src/components/onboarding/index.ts",
"chars": 224,
"preview": "export { default } from \"./Onboarding\";\nexport { default as AccessibilityOnboarding } from \"./AccessibilityOnboarding\";\n"
},
{
"path": "src/components/settings/AccelerationSelector.tsx",
"chars": 3232,
"preview": "import { type FC, useEffect, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { SettingCo"
},
{
"path": "src/components/settings/AlwaysOnMicrophone.tsx",
"chars": 1025,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { ToggleSwitch } from \"../ui/ToggleSwi"
},
{
"path": "src/components/settings/AppDataDirectory.tsx",
"chars": 2233,
"preview": "import React, { useState, useEffect } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { commands } "
},
{
"path": "src/components/settings/AppLanguageSelector.tsx",
"chars": 1498,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Dropdown } from \"../ui/Dropdown\";\nim"
},
{
"path": "src/components/settings/AppendTrailingSpace.tsx",
"chars": 1020,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { ToggleSwitch } from \"../ui/ToggleSwi"
},
{
"path": "src/components/settings/AudioFeedback.tsx",
"chars": 1158,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { ToggleSwitch } from \"../ui/ToggleSwi"
},
{
"path": "src/components/settings/AutoSubmit.tsx",
"chars": 2488,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Dropdown } from \"../ui/Dropdown\";\nim"
},
{
"path": "src/components/settings/AutostartToggle.tsx",
"chars": 1003,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { ToggleSwitch } from \"../ui/ToggleSwi"
},
{
"path": "src/components/settings/ClamshellMicrophoneSelector.tsx",
"chars": 3111,
"preview": "import React, { useState, useEffect } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { commands } "
},
{
"path": "src/components/settings/ClipboardHandling.tsx",
"chars": 1630,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Dropdown } from \"../ui/Dropdown\";\nim"
},
{
"path": "src/components/settings/CustomWords.tsx",
"chars": 3989,
"preview": "import React, { useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { toast } from \"sonner\";"
},
{
"path": "src/components/settings/ExperimentalToggle.tsx",
"chars": 1021,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { ToggleSwitch } from \"../ui/ToggleSwi"
},
{
"path": "src/components/settings/GlobalShortcutInput.tsx",
"chars": 10343,
"preview": "import React, { useEffect, useState, useRef } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport {\n g"
},
{
"path": "src/components/settings/HandyKeysShortcutInput.tsx",
"chars": 9224,
"preview": "import React, { useEffect, useState, useRef, useCallback } from \"react\";\nimport { useTranslation } from \"react-i18next\";"
},
{
"path": "src/components/settings/HistoryLimit.tsx",
"chars": 1515,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { useSettings } from \"../../hooks/useS"
},
{
"path": "src/components/settings/LanguageSelector.tsx",
"chars": 6991,
"preview": "import React, { useState, useRef, useEffect, useMemo } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimp"
},
{
"path": "src/components/settings/LazyStreamClose.tsx",
"chars": 997,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { ToggleSwitch } from \"../ui/ToggleSwi"
},
{
"path": "src/components/settings/MicrophoneSelector.tsx",
"chars": 2313,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Dropdown } from \"../ui/Dropdown\";\nim"
},
{
"path": "src/components/settings/ModelUnloadTimeout.tsx",
"chars": 2806,
"preview": "import React, { useMemo } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { useSettings } from \"../"
},
{
"path": "src/components/settings/MuteWhileRecording.tsx",
"chars": 1032,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { ToggleSwitch } from \"../ui/ToggleSwi"
},
{
"path": "src/components/settings/OutputDeviceSelector.tsx",
"chars": 2683,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Dropdown } from \"../ui/Dropdown\";\nim"
},
{
"path": "src/components/settings/PasteMethod.tsx",
"chars": 3406,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Dropdown } from \"../ui/Dropdown\";\nim"
},
{
"path": "src/components/settings/PostProcessingSettingsApi/ApiKeyField.tsx",
"chars": 921,
"preview": "import React, { useState } from \"react\";\nimport { Input } from \"../../ui/Input\";\n\ninterface ApiKeyFieldProps {\n value: "
},
{
"path": "src/components/settings/PostProcessingSettingsApi/BaseUrlField.tsx",
"chars": 1067,
"preview": "import React, { useState } from \"react\";\nimport { Input } from \"../../ui/Input\";\n\ninterface BaseUrlFieldProps {\n value:"
},
{
"path": "src/components/settings/PostProcessingSettingsApi/ModelSelect.tsx",
"chars": 1289,
"preview": "import React from \"react\";\nimport type { ModelOption } from \"./types\";\nimport { Select } from \"../../ui/Select\";\n\ntype M"
},
{
"path": "src/components/settings/PostProcessingSettingsApi/ProviderSelect.tsx",
"chars": 596,
"preview": "import React from \"react\";\nimport { Dropdown, type DropdownOption } from \"../../ui/Dropdown\";\n\ninterface ProviderSelectP"
},
{
"path": "src/components/settings/PostProcessingSettingsApi/index.tsx",
"chars": 87,
"preview": "export { PostProcessingSettingsApi } from \"../post-processing/PostProcessingSettings\";\n"
},
{
"path": "src/components/settings/PostProcessingSettingsApi/types.ts",
"chars": 65,
"preview": "export type ModelOption = {\n value: string;\n label: string;\n};\n"
},
{
"path": "src/components/settings/PostProcessingSettingsApi/usePostProcessProviderState.ts",
"chars": 7442,
"preview": "import { useCallback, useMemo, useState } from \"react\";\nimport { useSettings } from \"../../../hooks/useSettings\";\nimport"
},
{
"path": "src/components/settings/PostProcessingSettingsPrompts.tsx",
"chars": 90,
"preview": "export { PostProcessingSettingsPrompts } from \"./post-processing/PostProcessingSettings\";\n"
},
{
"path": "src/components/settings/PostProcessingToggle.tsx",
"chars": 1022,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { ToggleSwitch } from \"../ui/ToggleSwi"
},
{
"path": "src/components/settings/PushToTalk.tsx",
"chars": 961,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { ToggleSwitch } from \"../ui/ToggleSwi"
},
{
"path": "src/components/settings/RecordingRetentionPeriod.tsx",
"chars": 2238,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Dropdown } from \"../ui/Dropdown\";\nim"
},
{
"path": "src/components/settings/ShortcutInput.tsx",
"chars": 1021,
"preview": "import React from \"react\";\nimport { useSettings } from \"../../hooks/useSettings\";\nimport { GlobalShortcutInput } from \"."
},
{
"path": "src/components/settings/ShowOverlay.tsx",
"chars": 1527,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Dropdown } from \"../ui/Dropdown\";\nim"
},
{
"path": "src/components/settings/ShowTrayIcon.tsx",
"chars": 1015,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { ToggleSwitch } from \"../ui/ToggleSwi"
},
{
"path": "src/components/settings/SoundPicker.tsx",
"chars": 1866,
"preview": "import React from \"react\";\nimport { Button } from \"../ui/Button\";\nimport { Dropdown, DropdownOption } from \"../ui/Dropdo"
},
{
"path": "src/components/settings/StartHidden.tsx",
"chars": 1003,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { ToggleSwitch } from \"../ui/ToggleSwi"
},
{
"path": "src/components/settings/TranslateToEnglish.tsx",
"chars": 1043,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { ToggleSwitch } from \"../ui/ToggleSwi"
},
{
"path": "src/components/settings/TypingTool.tsx",
"chars": 2436,
"preview": "import React, { useEffect, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Dropdown } "
},
{
"path": "src/components/settings/UpdateChecksToggle.tsx",
"chars": 986,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { ToggleSwitch } from \"../ui/ToggleSwi"
},
{
"path": "src/components/settings/VolumeSlider.tsx",
"chars": 863,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Slider } from \"../ui/Slider\";\nimport"
},
{
"path": "src/components/settings/about/AboutSettings.tsx",
"chars": 3163,
"preview": "import React, { useState, useEffect } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { getVersion "
},
{
"path": "src/components/settings/advanced/AdvancedSettings.tsx",
"chars": 3384,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { ShowOverlay } from \"../ShowOverlay\";"
},
{
"path": "src/components/settings/debug/DebugPaths.tsx",
"chars": 1645,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { SettingContainer } from \"../../ui/Se"
},
{
"path": "src/components/settings/debug/DebugSettings.tsx",
"chars": 2030,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { type } from \"@tauri-apps/plugin-os\";"
},
{
"path": "src/components/settings/debug/KeyboardImplementationSelector.tsx",
"chars": 2266,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { SettingContainer } from \"../../ui/Se"
},
{
"path": "src/components/settings/debug/LogDirectory.tsx",
"chars": 2130,
"preview": "import React, { useEffect, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { commands } "
},
{
"path": "src/components/settings/debug/LogLevelSelector.tsx",
"chars": 1641,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { SettingContainer } from \"../../ui/Se"
},
{
"path": "src/components/settings/debug/PasteDelay.tsx",
"chars": 950,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Slider } from \"../../ui/Slider\";\nimp"
},
{
"path": "src/components/settings/debug/RecordingBuffer.tsx",
"chars": 998,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Slider } from \"../../ui/Slider\";\nimp"
},
{
"path": "src/components/settings/debug/WordCorrectionThreshold.tsx",
"chars": 995,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Slider } from \"../../ui/Slider\";\nimp"
},
{
"path": "src/components/settings/debug/index.ts",
"chars": 171,
"preview": "export { WordCorrectionThreshold } from \"./WordCorrectionThreshold\";\nexport { LogDirectory } from \"./LogDirectory\";\nexpo"
},
{
"path": "src/components/settings/general/GeneralSettings.tsx",
"chars": 1574,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { MicrophoneSelector } from \"../Microp"
},
{
"path": "src/components/settings/general/ModelSettingsCard.tsx",
"chars": 1473,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { SettingsGroup } from \"../../ui/Setti"
},
{
"path": "src/components/settings/history/HistorySettings.tsx",
"chars": 9295,
"preview": "import React, { useState, useEffect, useCallback } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport "
},
{
"path": "src/components/settings/index.ts",
"chars": 1799,
"preview": "// Settings section components\nexport { GeneralSettings } from \"./general/GeneralSettings\";\nexport { AdvancedSettings } "
},
{
"path": "src/components/settings/models/ModelsSettings.tsx",
"chars": 13199,
"preview": "import React, { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimp"
},
{
"path": "src/components/settings/models/index.ts",
"chars": 51,
"preview": "export { ModelsSettings } from \"./ModelsSettings\";\n"
},
{
"path": "src/components/settings/post-processing/PostProcessingSettings.tsx",
"chars": 14811,
"preview": "import React, { useEffect, useState } from \"react\";\nimport { Trans, useTranslation } from \"react-i18next\";\nimport { Refr"
},
{
"path": "src/components/shared/ProgressBar.tsx",
"chars": 2573,
"preview": "import React from \"react\";\n\nexport interface ProgressData {\n id: string;\n percentage: number;\n speed?: number;\n labe"
},
{
"path": "src/components/shared/index.ts",
"chars": 107,
"preview": "export { default as ProgressBar } from \"./ProgressBar\";\nexport type { ProgressData } from \"./ProgressBar\";\n"
},
{
"path": "src/components/ui/Alert.tsx",
"chars": 1540,
"preview": "import React from \"react\";\nimport { AlertCircle, AlertTriangle, Info, CheckCircle } from \"lucide-react\";\n\ntype AlertVari"
},
{
"path": "src/components/ui/AudioPlayer.tsx",
"chars": 8339,
"preview": "import React, { useState, useRef, useEffect, useCallback } from \"react\";\nimport { Play, Pause } from \"lucide-react\";\n\nin"
},
{
"path": "src/components/ui/Badge.tsx",
"chars": 627,
"preview": "import React from \"react\";\n\ninterface BadgeProps {\n children: React.ReactNode;\n variant?: \"primary\" | \"success\" | \"sec"
},
{
"path": "src/components/ui/Button.tsx",
"chars": 1693,
"preview": "import React from \"react\";\n\ninterface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n variant?:\n "
},
{
"path": "src/components/ui/Dropdown.tsx",
"chars": 3563,
"preview": "import React, { useEffect, useRef, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\n\nexport inte"
},
{
"path": "src/components/ui/Input.tsx",
"chars": 924,
"preview": "import React from \"react\";\n\ninterface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {\n variant?: \"defa"
},
{
"path": "src/components/ui/PathDisplay.tsx",
"chars": 793,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Button } from \"./Button\";\n\ninterface"
},
{
"path": "src/components/ui/ResetButton.tsx",
"chars": 876,
"preview": "import React from \"react\";\nimport ResetIcon from \"../icons/ResetIcon\";\n\ninterface ResetButtonProps {\n onClick: () => vo"
},
{
"path": "src/components/ui/Select.tsx",
"chars": 4668,
"preview": "import React from \"react\";\nimport SelectComponent from \"react-select\";\nimport CreatableSelect from \"react-select/creatab"
},
{
"path": "src/components/ui/SettingContainer.tsx",
"chars": 6022,
"preview": "import React, { useEffect, useRef, useState } from \"react\";\nimport { Tooltip } from \"./Tooltip\";\n\ninterface SettingConta"
},
{
"path": "src/components/ui/SettingsGroup.tsx",
"chars": 783,
"preview": "import React from \"react\";\n\ninterface SettingsGroupProps {\n title?: string;\n description?: string;\n children: React.R"
},
{
"path": "src/components/ui/Slider.tsx",
"chars": 1994,
"preview": "import React from \"react\";\nimport { SettingContainer } from \"./SettingContainer\";\n\ninterface SliderProps {\n value: numb"
},
{
"path": "src/components/ui/TextDisplay.tsx",
"chars": 2705,
"preview": "import React, { useState } from \"react\";\nimport { SettingContainer } from \"./SettingContainer\";\n\ninterface TextDisplayPr"
},
{
"path": "src/components/ui/Textarea.tsx",
"chars": 819,
"preview": "import React from \"react\";\n\ninterface TextareaProps\n extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {\n vari"
},
{
"path": "src/components/ui/ToggleSwitch.tsx",
"chars": 1971,
"preview": "import React from \"react\";\nimport { SettingContainer } from \"./SettingContainer\";\n\ninterface ToggleSwitchProps {\n check"
},
{
"path": "src/components/ui/Tooltip.tsx",
"chars": 3333,
"preview": "import React, { useCallback, useEffect, useRef, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\n\ntype"
},
{
"path": "src/components/ui/index.ts",
"chars": 346,
"preview": "export { Dropdown } from \"./Dropdown\";\nexport { Slider } from \"./Slider\";\nexport { ToggleSwitch } from \"./ToggleSwitch\";"
},
{
"path": "src/components/update-checker/UpdateChecker.tsx",
"chars": 6340,
"preview": "import React, { useState, useEffect, useRef } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { che"
},
{
"path": "src/components/update-checker/index.ts",
"chars": 43,
"preview": "export { default } from \"./UpdateChecker\";\n"
},
{
"path": "src/hooks/useOsType.ts",
"chars": 525,
"preview": "import { type } from \"@tauri-apps/plugin-os\";\nimport { type OSType } from \"../lib/utils/keyboard\";\n\n/**\n * Get the curre"
},
{
"path": "src/hooks/useSettings.ts",
"chars": 2640,
"preview": "import { useEffect } from \"react\";\nimport { useSettingsStore } from \"../stores/settingsStore\";\nimport type { AppSettings"
},
{
"path": "src/i18n/index.ts",
"chars": 3905,
"preview": "import i18n from \"i18next\";\nimport { initReactI18next } from \"react-i18next\";\nimport { locale } from \"@tauri-apps/plugin"
},
{
"path": "src/i18n/languages.ts",
"chars": 1653,
"preview": "/**\n * Language metadata for supported locales.\n *\n * To add a new language:\n * 1. Create a new folder: src/i18n/locales"
},
{
"path": "src/i18n/locales/ar/translation.json",
"chars": 21867,
"preview": "{\n \"tray\": {\n \"settings\": \"...الإعدادات\",\n \"checkUpdates\": \"...التحقق من وجود تحديثات\",\n \"copyLastTranscript\":"
},
{
"path": "src/i18n/locales/cs/translation.json",
"chars": 23254,
"preview": "{\n \"tray\": {\n \"settings\": \"Nastavení...\",\n \"checkUpdates\": \"Zkontrolovat aktualizace...\",\n \"copyLastTranscript"
},
{
"path": "src/i18n/locales/de/translation.json",
"chars": 24567,
"preview": "{\n \"tray\": {\n \"settings\": \"Einstellungen...\",\n \"checkUpdates\": \"Nach Updates suchen...\",\n \"copyLastTranscript\""
},
{
"path": "src/i18n/locales/en/translation.json",
"chars": 22802,
"preview": "{\n \"tray\": {\n \"settings\": \"Settings...\",\n \"checkUpdates\": \"Check for Updates...\",\n \"copyLastTranscript\": \"Copy"
},
{
"path": "src/i18n/locales/es/translation.json",
"chars": 24881,
"preview": "{\n \"tray\": {\n \"settings\": \"Configuración...\",\n \"checkUpdates\": \"Buscar actualizaciones...\",\n \"copyLastTranscri"
},
{
"path": "src/i18n/locales/fr/translation.json",
"chars": 25387,
"preview": "{\n \"tray\": {\n \"settings\": \"Paramètres...\",\n \"checkUpdates\": \"Rechercher des mises à jour...\",\n \"copyLastTransc"
},
{
"path": "src/i18n/locales/it/translation.json",
"chars": 24625,
"preview": "{\n \"tray\": {\n \"settings\": \"Impostazioni...\",\n \"checkUpdates\": \"Verifica aggiornamenti...\",\n \"copyLastTranscrip"
},
{
"path": "src/i18n/locales/ja/translation.json",
"chars": 17947,
"preview": "{\n \"tray\": {\n \"settings\": \"設定...\",\n \"checkUpdates\": \"アップデートを確認...\",\n \"copyLastTranscript\": \"最新の文字起こしをコピー\",\n "
},
{
"path": "src/i18n/locales/ko/translation.json",
"chars": 18061,
"preview": "{\n \"tray\": {\n \"settings\": \"설정...\",\n \"checkUpdates\": \"업데이트 확인...\",\n \"copyLastTranscript\": \"마지막 녹음 내용 복사\",\n \""
},
{
"path": "src/i18n/locales/pl/translation.json",
"chars": 23676,
"preview": "{\n \"tray\": {\n \"settings\": \"Ustawienia...\",\n \"checkUpdates\": \"Sprawdź aktualizacje...\",\n \"copyLastTranscript\": "
},
{
"path": "src/i18n/locales/pt/translation.json",
"chars": 24369,
"preview": "{\n \"tray\": {\n \"settings\": \"Configurações...\",\n \"checkUpdates\": \"Verificar Atualizações...\",\n \"copyLastTranscri"
},
{
"path": "src/i18n/locales/ru/translation.json",
"chars": 24226,
"preview": "{\n \"tray\": {\n \"settings\": \"Настройки...\",\n \"checkUpdates\": \"Проверить обновления...\",\n \"copyLastTranscript\": \""
},
{
"path": "src/i18n/locales/tr/translation.json",
"chars": 23223,
"preview": "{\n \"tray\": {\n \"settings\": \"Ayarlar...\",\n \"checkUpdates\": \"Güncellemeleri Kontrol Et...\",\n \"copyLastTranscript\""
},
{
"path": "src/i18n/locales/uk/translation.json",
"chars": 23760,
"preview": "{\n \"tray\": {\n \"settings\": \"Налаштування...\",\n \"checkUpdates\": \"Перевірити оновлення...\",\n \"copyLastTranscript\""
},
{
"path": "src/i18n/locales/vi/translation.json",
"chars": 22815,
"preview": "{\n \"tray\": {\n \"settings\": \"Cài đặt...\",\n \"checkUpdates\": \"Kiểm tra cập nhật...\",\n \"copyLastTranscript\": \"Sao c"
},
{
"path": "src/i18n/locales/zh/translation.json",
"chars": 16346,
"preview": "{\n \"tray\": {\n \"settings\": \"设置...\",\n \"checkUpdates\": \"检查更新...\",\n \"copyLastTranscript\": \"复制最新转录\",\n \"unloadMod"
},
{
"path": "src/i18n/locales/zh-TW/translation.json",
"chars": 16308,
"preview": "{\n \"tray\": {\n \"settings\": \"設定...\",\n \"checkUpdates\": \"檢查更新...\",\n \"copyLastTranscript\": \"複製最新轉錄\",\n \"unloadMod"
},
{
"path": "src/lib/constants/languages.ts",
"chars": 3936,
"preview": "export interface Language {\n value: string;\n label: string;\n}\n\nexport const LANGUAGES: Language[] = [\n { value: \"auto"
},
{
"path": "src/lib/types/events.ts",
"chars": 204,
"preview": "export interface ModelStateEvent {\n event_type: string;\n model_id?: string;\n model_name?: string;\n error?: string;\n}"
},
{
"path": "src/lib/utils/format.ts",
"chars": 655,
"preview": "export const formatModelSize = (sizeMb: number | null | undefined): string => {\n if (!sizeMb || !Number.isFinite(sizeMb"
},
{
"path": "src/lib/utils/keyboard.ts",
"chars": 6088,
"preview": "/**\n * Keyboard utility functions for handling keyboard events\n */\n\nexport type OSType = \"macos\" | \"windows\" | \"linux\" |"
},
{
"path": "src/lib/utils/modelTranslation.ts",
"chars": 1253,
"preview": "import type { TFunction } from \"i18next\";\nimport type { ModelInfo } from \"@/bindings\";\n\n/**\n * Get the translated name f"
},
{
"path": "src/lib/utils/rtl.ts",
"chars": 1935,
"preview": "/**\n * RTL (Right-to-Left) utilities for handling text direction in the application.\n *\n * These utilities help manage R"
},
{
"path": "src/main.tsx",
"chars": 619,
"preview": "import React from \"react\";\nimport ReactDOM from \"react-dom/client\";\nimport { platform } from \"@tauri-apps/plugin-os\";\nim"
},
{
"path": "src/overlay/RecordingOverlay.css",
"chars": 1576,
"preview": ".recording-overlay {\n height: 36px;\n width: 172px;\n display: grid;\n grid-template-columns: auto 1fr auto;\n align-it"
},
{
"path": "src/overlay/RecordingOverlay.tsx",
"chars": 3682,
"preview": "import { listen } from \"@tauri-apps/api/event\";\nimport React, { useEffect, useRef, useState } from \"react\";\nimport { use"
},
{
"path": "src/overlay/index.html",
"chars": 535,
"preview": "<!doctype html>\n<html lang=\"en\" dir=\"ltr\">\n <head>\n <meta charset=\"utf-8\" />\n <title>Recording Overlay</title>\n "
},
{
"path": "src/overlay/main.tsx",
"chars": 285,
"preview": "import React from \"react\";\nimport ReactDOM from \"react-dom/client\";\nimport RecordingOverlay from \"./RecordingOverlay\";\ni"
},
{
"path": "src/stores/modelStore.ts",
"chars": 11467,
"preview": "import { create } from \"zustand\";\nimport { subscribeWithSelector } from \"zustand/middleware\";\nimport { produce } from \"i"
},
{
"path": "src/stores/settingsStore.ts",
"chars": 20401,
"preview": "import { create } from \"zustand\";\nimport { subscribeWithSelector } from \"zustand/middleware\";\nimport { listen } from \"@t"
},
{
"path": "src/utils/dateFormat.ts",
"chars": 3875,
"preview": "/**\n * Format a date string or timestamp to a localized date and time string\n * @param timestamp - Unix timestamp in sec"
},
{
"path": "src/vite-env.d.ts",
"chars": 38,
"preview": "/// <reference types=\"vite/client\" />\n"
},
{
"path": "src-tauri/.gitignore",
"chars": 166,
"preview": "# Generated by Cargo\n# will have compiled files and executables\n/target/\n\n# Generated by Tauri\n# will have schema files "
},
{
"path": "src-tauri/Cargo.toml",
"chars": 3637,
"preview": "[package]\nname = \"handy\"\nversion = \"0.7.12\"\ndescription = \"Handy\"\nauthors = [\"cjpais\"]\nedition = \"2021\"\nlicense = \"MIT\"\n"
},
{
"path": "src-tauri/Entitlements.plist",
"chars": 320,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "src-tauri/Info.plist",
"chars": 302,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "src-tauri/build.rs",
"chars": 7781,
"preview": "fn main() {\n #[cfg(all(target_os = \"macos\", target_arch = \"aarch64\"))]\n build_apple_intelligence_bridge();\n\n ge"
},
{
"path": "src-tauri/capabilities/default.json",
"chars": 697,
"preview": "{\n \"$schema\": \"../gen/schemas/desktop-schema.json\",\n \"identifier\": \"default\",\n \"description\": \"Capabilities for the a"
},
{
"path": "src-tauri/capabilities/desktop.json",
"chars": 262,
"preview": "{\n \"identifier\": \"desktop-capability\",\n \"platforms\": [\"macOS\", \"windows\", \"linux\"],\n \"windows\": [\"main\"],\n \"permissi"
},
{
"path": "src-tauri/gen/apple/PrivacyInfo.xcprivacy",
"chars": 514,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "src-tauri/nsis/installer.nsi",
"chars": 35564,
"preview": "; Custom NSIS template for Handy with portable mode support.\n; Based on tauri-apps/tauri@tauri-v2.9.1 crates/tauri-bundl"
},
{
"path": "src-tauri/resources/default_settings.json",
"chars": 325,
"preview": "{\n \"bindings\": {\n \"transcribe\": {\n \"id\": \"transcribe\",\n \"name\": \"Transcribe Keyboard Shortcut\",\n \"des"
},
{
"path": "src-tauri/resources/models/gigaam_vocab.txt",
"chars": 1610,
"preview": "<unk> 0\n▁ 1\n. 2\nе 3\nа 4\nс 5\nи 6\n, 7\nо 8\nт 9\nн 10\nм 11\nу 12\nй 13\nл 14\nя 15\nв 16\nд 17\nз 18\nк 19\nно 20\n▁с 21\nы 22\nг 23\n▁в 2"
},
{
"path": "src-tauri/rustfmt.toml",
"chars": 17,
"preview": "edition = \"2021\"\n"
},
{
"path": "src-tauri/src/actions.rs",
"chars": 24562,
"preview": "#[cfg(all(target_os = \"macos\", target_arch = \"aarch64\"))]\nuse crate::apple_intelligence;\nuse crate::audio_feedback::{pla"
},
{
"path": "src-tauri/src/apple_intelligence.rs",
"chars": 2441,
"preview": "use std::ffi::{CStr, CString};\nuse std::os::raw::{c_char, c_int};\n\n// Define the response structure from Swift\n#[repr(C)"
},
{
"path": "src-tauri/src/audio_feedback.rs",
"chars": 4544,
"preview": "use crate::settings::SoundTheme;\nuse crate::settings::{self, AppSettings};\nuse cpal::traits::{DeviceTrait, HostTrait};\nu"
},
{
"path": "src-tauri/src/audio_toolkit/audio/device.rs",
"chars": 1455,
"preview": "use cpal::traits::{DeviceTrait, HostTrait};\n\npub struct CpalDeviceInfo {\n pub index: String,\n pub name: String,\n "
},
{
"path": "src-tauri/src/audio_toolkit/audio/mod.rs",
"chars": 344,
"preview": "// Re-export all audio components\nmod device;\nmod recorder;\nmod resampler;\nmod utils;\nmod visualizer;\n\npub use device::{"
},
{
"path": "src-tauri/src/audio_toolkit/audio/recorder.rs",
"chars": 17199,
"preview": "use std::{\n io::Error,\n sync::{\n atomic::{AtomicBool, Ordering},\n mpsc, Arc, Mutex,\n },\n time:"
},
{
"path": "src-tauri/src/audio_toolkit/audio/resampler.rs",
"chars": 3278,
"preview": "use rubato::{FftFixedIn, Resampler};\nuse std::time::Duration;\n\n// Make this a constant you can tweak\nconst RESAMPLER_CHU"
},
{
"path": "src-tauri/src/audio_toolkit/audio/utils.rs",
"chars": 718,
"preview": "use anyhow::Result;\nuse hound::{WavSpec, WavWriter};\nuse log::debug;\nuse std::path::Path;\n\n/// Save audio samples as a W"
},
{
"path": "src-tauri/src/audio_toolkit/audio/visualizer.rs",
"chars": 5358,
"preview": "use rustfft::{num_complex::Complex32, Fft, FftPlanner};\nuse std::sync::Arc;\n\nconst DB_MIN: f32 = -55.0;\nconst DB_MAX: f3"
},
{
"path": "src-tauri/src/audio_toolkit/bin/cli.rs",
"chars": 12190,
"preview": "use hound::WavWriter;\nuse std::io::{self, Write};\n\nuse handy_app_lib::audio_toolkit::{\n audio::{list_input_devices, C"
}
]
// ... and 49 more files (download for full content)
About this extraction
This page contains the full source code of the cjpais/Handy GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 249 files (1.4 MB), approximately 417.2k tokens, and a symbol index with 859 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.