Showing preview only (3,950K chars total). Download the full file or copy to clipboard to get everything.
Repository: Zackriya-Solutions/meetily
Branch: main
Commit: 91b0c0985932
Files: 441
Total size: 3.7 MB
Directory structure:
gitextract_vm4cqimc/
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ ├── documentation.md
│ │ └── feature_request.md
│ ├── issue_template.md
│ ├── pull_request_template.md
│ └── workflows/
│ ├── ACCELERATION_GUIDE.md
│ ├── README_DEVTEST.md
│ ├── WORKFLOWS_OVERVIEW.md
│ ├── build-devtest.yml
│ ├── build-linux.yml
│ ├── build-macos.yml
│ ├── build-test.yml
│ ├── build-windows.yml
│ ├── build.yml
│ ├── pr-main-check.yml
│ └── release.yml
├── .gitignore
├── .gitmodules
├── BLUETOOTH_PLAYBACK_NOTICE.md
├── CLAUDE.md
├── CONTRIBUTING.md
├── Cargo.toml
├── LICENSE.md
├── PRIVACY_POLICY.md
├── README.md
├── backend/
│ ├── .gitignore
│ ├── API_DOCUMENTATION.md
│ ├── Dockerfile.app
│ ├── Dockerfile.server-cpu
│ ├── Dockerfile.server-gpu
│ ├── Dockerfile.server-macos
│ ├── README.md
│ ├── SCRIPTS_DOCUMENTATION.md
│ ├── app/
│ │ ├── db.py
│ │ ├── main.py
│ │ ├── schema_validator.py
│ │ └── transcript_processor.py
│ ├── build-docker.ps1
│ ├── build-docker.sh
│ ├── build_whisper.cmd
│ ├── build_whisper.sh
│ ├── clean_start_backend.cmd
│ ├── clean_start_backend.sh
│ ├── debug_cors.py
│ ├── docker/
│ │ └── entrypoint.sh
│ ├── docker-compose.yml
│ ├── download-ggml-model.cmd
│ ├── download-ggml-model.sh
│ ├── examples/
│ │ └── run_summary_workflow.py
│ ├── install_dependancies_for_windows.ps1
│ ├── requirements.txt
│ ├── run-docker.ps1
│ ├── run-docker.sh
│ ├── set_env.sh
│ ├── setup-db.ps1
│ ├── setup-db.sh
│ ├── start_python_backend.cmd
│ ├── start_whisper_server.cmd
│ ├── start_with_output.ps1
│ ├── temp.env
│ └── whisper-custom/
│ └── server/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── httplib.h
│ ├── public/
│ │ └── index.html
│ └── server.cpp
├── docs/
│ ├── BUILDING.md
│ ├── GPU_ACCELERATION.md
│ ├── architecture.md
│ └── building_in_linux.md
├── frontend/
│ ├── .gitignore
│ ├── API.md
│ ├── README.md
│ ├── build-gpu.bat
│ ├── build-gpu.ps1
│ ├── build-gpu.sh
│ ├── build.bat
│ ├── build.ps1
│ ├── build_backup.bat
│ ├── clean_build.sh
│ ├── clean_build_windows.bat
│ ├── clean_run.sh
│ ├── clean_run_windows.bat
│ ├── components.json
│ ├── dev-gpu.bat
│ ├── dev-gpu.ps1
│ ├── dev-gpu.sh
│ ├── eslint.config.mjs
│ ├── next.config.js
│ ├── package-app.sh
│ ├── package.json
│ ├── postcss.config.js
│ ├── postcss.config.mjs
│ ├── scripts/
│ │ ├── auto-detect-gpu.js
│ │ ├── load-env.ps1
│ │ └── tauri-auto.js
│ ├── src/
│ │ ├── app/
│ │ │ ├── _components/
│ │ │ │ ├── SettingsModal.tsx
│ │ │ │ ├── StatusOverlays.tsx
│ │ │ │ └── TranscriptPanel.tsx
│ │ │ ├── globals.css
│ │ │ ├── layout.tsx
│ │ │ ├── meeting-details/
│ │ │ │ ├── page-content.tsx
│ │ │ │ └── page.tsx
│ │ │ ├── metadata.ts
│ │ │ ├── metadata.tsx
│ │ │ ├── notes/
│ │ │ │ └── [id]/
│ │ │ │ └── page.tsx
│ │ │ ├── page.tsx
│ │ │ └── settings/
│ │ │ └── page.tsx
│ │ ├── components/
│ │ │ ├── AISummary/
│ │ │ │ ├── Block.tsx
│ │ │ │ ├── BlockNoteSummaryView.tsx
│ │ │ │ ├── Section.tsx
│ │ │ │ └── index.tsx
│ │ │ ├── About.tsx
│ │ │ ├── AnalyticsConsentSwitch.tsx
│ │ │ ├── AnalyticsDataModal.tsx
│ │ │ ├── AnalyticsProvider.tsx
│ │ │ ├── AudioBackendSelector.tsx
│ │ │ ├── AudioLevelMeter.tsx
│ │ │ ├── AudioPlayer.tsx
│ │ │ ├── BetaSettings.tsx
│ │ │ ├── BlockNoteEditor/
│ │ │ │ ├── BasicBlockNoteTest.tsx
│ │ │ │ └── Editor.tsx
│ │ │ ├── BluetoothPlaybackWarning.tsx
│ │ │ ├── BuiltInModelManager.tsx
│ │ │ ├── ChunkProgressDisplay.tsx
│ │ │ ├── ComplianceNotification.tsx
│ │ │ ├── ConfidenceIndicator.tsx
│ │ │ ├── ConfirmationModel/
│ │ │ │ └── confirmation-modal.tsx
│ │ │ ├── ConsoleToggle.tsx
│ │ │ ├── CustomDialog.tsx
│ │ │ ├── DatabaseImport/
│ │ │ │ ├── HomebrewDatabaseDetector.tsx
│ │ │ │ └── LegacyDatabaseImport.tsx
│ │ │ ├── DeviceSelection.tsx
│ │ │ ├── EditableTitle.tsx
│ │ │ ├── EmptyStateSummary.tsx
│ │ │ ├── ImportAudio/
│ │ │ │ ├── ImportAudioDialog.tsx
│ │ │ │ ├── ImportDropOverlay.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Info.tsx
│ │ │ ├── LanguageSelection.tsx
│ │ │ ├── Logo.tsx
│ │ │ ├── MainContent/
│ │ │ │ └── index.tsx
│ │ │ ├── MainNav/
│ │ │ │ └── index.tsx
│ │ │ ├── MeetingDetails/
│ │ │ │ ├── RetranscribeDialog.tsx
│ │ │ │ ├── SummaryGeneratorButtonGroup.tsx
│ │ │ │ ├── SummaryPanel.tsx
│ │ │ │ ├── SummaryUpdaterButtonGroup.tsx
│ │ │ │ ├── TranscriptButtonGroup.tsx
│ │ │ │ └── TranscriptPanel.tsx
│ │ │ ├── MessageToast.tsx
│ │ │ ├── ModelDownloadProgress.tsx
│ │ │ ├── ModelSettingsModal.tsx
│ │ │ ├── ParakeetModelManager.tsx
│ │ │ ├── PermissionWarning.tsx
│ │ │ ├── PreferenceSettings.tsx
│ │ │ ├── RecordingControls.tsx
│ │ │ ├── RecordingSettings.tsx
│ │ │ ├── RecordingStatusBar.tsx
│ │ │ ├── SettingTabs.tsx
│ │ │ ├── Sidebar/
│ │ │ │ ├── SidebarProvider.tsx
│ │ │ │ └── index.tsx
│ │ │ ├── SummaryModelSettings.tsx
│ │ │ ├── TranscriptRecovery/
│ │ │ │ ├── TranscriptRecovery.tsx
│ │ │ │ └── index.ts
│ │ │ ├── TranscriptSettings.tsx
│ │ │ ├── TranscriptView.tsx
│ │ │ ├── UpdateCheckProvider.tsx
│ │ │ ├── UpdateDialog.tsx
│ │ │ ├── UpdateNotification.tsx
│ │ │ ├── VirtualizedTranscriptView.tsx
│ │ │ ├── WhisperModelManager.tsx
│ │ │ ├── molecules/
│ │ │ │ └── form-components/
│ │ │ │ ├── form-input-item.tsx
│ │ │ │ ├── form-input-switch.tsx
│ │ │ │ └── form-select-item.tsx
│ │ │ ├── onboarding/
│ │ │ │ ├── OnboardingContainer.tsx
│ │ │ │ ├── OnboardingFlow.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── shared/
│ │ │ │ │ ├── PermissionRow.tsx
│ │ │ │ │ ├── ProgressIndicator.tsx
│ │ │ │ │ ├── StatusIndicator.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ └── steps/
│ │ │ │ ├── DownloadProgressStep.tsx
│ │ │ │ ├── PermissionsStep.tsx
│ │ │ │ ├── SetupOverviewStep.tsx
│ │ │ │ ├── WelcomeStep.tsx
│ │ │ │ └── index.ts
│ │ │ ├── shared/
│ │ │ │ └── DownloadProgressToast.tsx
│ │ │ └── ui/
│ │ │ ├── accordion.tsx
│ │ │ ├── alert.tsx
│ │ │ ├── button-group.tsx
│ │ │ ├── button.tsx
│ │ │ ├── command.tsx
│ │ │ ├── dialog.tsx
│ │ │ ├── dropdown-menu.tsx
│ │ │ ├── form.tsx
│ │ │ ├── input-group.tsx
│ │ │ ├── input.tsx
│ │ │ ├── label.tsx
│ │ │ ├── popover.tsx
│ │ │ ├── progress.tsx
│ │ │ ├── scroll-area.tsx
│ │ │ ├── select.tsx
│ │ │ ├── separator.tsx
│ │ │ ├── sheet.tsx
│ │ │ ├── switch.tsx
│ │ │ ├── tabs.tsx
│ │ │ ├── textarea.tsx
│ │ │ ├── tooltip.tsx
│ │ │ └── visually-hidden.tsx
│ │ ├── config/
│ │ │ └── api.ts
│ │ ├── constants/
│ │ │ ├── audioFormats.ts
│ │ │ ├── languages.ts
│ │ │ └── modelDefaults.ts
│ │ ├── contexts/
│ │ │ ├── ConfigContext.tsx
│ │ │ ├── ImportDialogContext.tsx
│ │ │ ├── OllamaDownloadContext.tsx
│ │ │ ├── OnboardingContext.tsx
│ │ │ ├── RecordingPostProcessingProvider.tsx
│ │ │ ├── RecordingStateContext.tsx
│ │ │ └── TranscriptContext.tsx
│ │ ├── hooks/
│ │ │ ├── meeting-details/
│ │ │ │ ├── useCopyOperations.ts
│ │ │ │ ├── useMeetingData.ts
│ │ │ │ ├── useMeetingOperations.ts
│ │ │ │ ├── useModelConfiguration.ts
│ │ │ │ ├── useSummaryGeneration.ts
│ │ │ │ └── useTemplates.ts
│ │ │ ├── useAudioPlayer.ts
│ │ │ ├── useAutoScroll.ts
│ │ │ ├── useImportAudio.ts
│ │ │ ├── useModalState.ts
│ │ │ ├── useNavigation.ts
│ │ │ ├── usePaginatedTranscripts.ts
│ │ │ ├── usePermissionCheck.ts
│ │ │ ├── usePlatform.ts
│ │ │ ├── useProcessingProgress.ts
│ │ │ ├── useRecordingStart.ts
│ │ │ ├── useRecordingStateSync.ts
│ │ │ ├── useRecordingStop.ts
│ │ │ ├── useTranscriptRecovery.ts
│ │ │ ├── useTranscriptStreaming.ts
│ │ │ ├── useTranscriptionModels.ts
│ │ │ └── useUpdateCheck.ts
│ │ ├── lib/
│ │ │ ├── analytics.ts
│ │ │ ├── builtin-ai.ts
│ │ │ ├── parakeet.ts
│ │ │ ├── recordingNotification.tsx
│ │ │ ├── utils.ts
│ │ │ └── whisper.ts
│ │ ├── services/
│ │ │ ├── configService.ts
│ │ │ ├── indexedDBService.ts
│ │ │ ├── recordingService.ts
│ │ │ ├── storageService.ts
│ │ │ ├── transcriptService.ts
│ │ │ └── updateService.ts
│ │ └── types/
│ │ ├── betaFeatures.ts
│ │ ├── index.ts
│ │ ├── onboarding.ts
│ │ └── summary.ts
│ ├── src-tauri/
│ │ ├── .cargo/
│ │ │ └── config.toml
│ │ ├── .gitignore
│ │ ├── CLEANUP_PLAN.md
│ │ ├── Cargo.toml
│ │ ├── Info.plist
│ │ ├── LOGGING_OPTIMIZATIONS.md
│ │ ├── NOTIFICATION_TESTING.md
│ │ ├── build/
│ │ │ └── ffmpeg.rs
│ │ ├── build.rs
│ │ ├── check_screen_permission.swift
│ │ ├── config/
│ │ │ └── backend_config.json
│ │ ├── entitlements.plist
│ │ ├── icons/
│ │ │ ├── app_icon.icns
│ │ │ └── icon.icns
│ │ ├── migrations/
│ │ │ ├── 20250916100000_initial_schema.sql
│ │ │ ├── 20250920155811_add_openrouter_api_key.sql
│ │ │ ├── 20251006000000_add_audio_sync_fields.sql
│ │ │ ├── 20251010153942_add_ollama_endpoint.sql
│ │ │ ├── 20251101000000_add_summary_backup.sql
│ │ │ ├── 20251105120000_add_pro_license_custom_openai.sql
│ │ │ ├── 20251110000000_add_grace_period_to_licensing.sql
│ │ │ ├── 20251110000001_add_speaker_field.sql
│ │ │ ├── 20251223000000_add_meeting_notes.sql
│ │ │ └── 20251229000000_add_gemini_api_key.sql
│ │ ├── scripts/
│ │ │ └── sign-windows.ps1
│ │ ├── src/
│ │ │ ├── analytics/
│ │ │ │ ├── analytics.rs
│ │ │ │ ├── commands.rs
│ │ │ │ └── mod.rs
│ │ │ ├── anthropic/
│ │ │ │ ├── anthropic.rs
│ │ │ │ └── mod.rs
│ │ │ ├── api/
│ │ │ │ ├── api.rs
│ │ │ │ ├── commands.rs
│ │ │ │ └── mod.rs
│ │ │ ├── audio/
│ │ │ │ ├── async_logger.rs
│ │ │ │ ├── audio_processing.rs
│ │ │ │ ├── batch_processor.rs
│ │ │ │ ├── buffer_pool.rs
│ │ │ │ ├── capture/
│ │ │ │ │ ├── backend_config.rs
│ │ │ │ │ ├── core_audio.rs
│ │ │ │ │ ├── microphone.rs
│ │ │ │ │ ├── mod.rs
│ │ │ │ │ └── system.rs
│ │ │ │ ├── common.rs
│ │ │ │ ├── constants.rs
│ │ │ │ ├── core-old.rs
│ │ │ │ ├── decoder.rs
│ │ │ │ ├── device_detection.rs
│ │ │ │ ├── device_monitor.rs
│ │ │ │ ├── devices/
│ │ │ │ │ ├── configuration.rs
│ │ │ │ │ ├── discovery.rs
│ │ │ │ │ ├── fallback.rs
│ │ │ │ │ ├── microphone.rs
│ │ │ │ │ ├── mod.rs
│ │ │ │ │ ├── platform/
│ │ │ │ │ │ ├── linux.rs
│ │ │ │ │ │ ├── macos.rs
│ │ │ │ │ │ ├── mod.rs
│ │ │ │ │ │ └── windows.rs
│ │ │ │ │ └── speakers.rs
│ │ │ │ ├── diagnostics.rs
│ │ │ │ ├── encode.rs
│ │ │ │ ├── ffmpeg.rs
│ │ │ │ ├── ffmpeg_mixer.rs
│ │ │ │ ├── hardware_detector.rs
│ │ │ │ ├── import.rs
│ │ │ │ ├── incremental_saver.rs
│ │ │ │ ├── level_monitor.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── permissions.rs
│ │ │ │ ├── pipeline.rs
│ │ │ │ ├── playback_monitor.rs
│ │ │ │ ├── post_processor.rs
│ │ │ │ ├── recording_commands.rs
│ │ │ │ ├── recording_commands.rs.backup
│ │ │ │ ├── recording_manager.rs
│ │ │ │ ├── recording_preferences.rs
│ │ │ │ ├── recording_saver.rs
│ │ │ │ ├── recording_saver_old.rs
│ │ │ │ ├── recording_state.rs
│ │ │ │ ├── retranscription.rs
│ │ │ │ ├── simple_level_monitor.rs
│ │ │ │ ├── stream.rs
│ │ │ │ ├── stt.rs
│ │ │ │ ├── system_audio_commands.rs
│ │ │ │ ├── system_audio_stream.rs
│ │ │ │ ├── system_audio_types.ts
│ │ │ │ ├── system_detector.rs
│ │ │ │ ├── transcription/
│ │ │ │ │ ├── engine.rs
│ │ │ │ │ ├── mod.rs
│ │ │ │ │ ├── parakeet_provider.rs
│ │ │ │ │ ├── provider.rs
│ │ │ │ │ ├── whisper_provider.rs
│ │ │ │ │ └── worker.rs
│ │ │ │ └── vad.rs
│ │ │ ├── audio_v2/
│ │ │ │ ├── compatibility.rs
│ │ │ │ ├── lib.rs
│ │ │ │ ├── limiter.rs
│ │ │ │ ├── mixer.rs
│ │ │ │ ├── normalizer.rs
│ │ │ │ ├── recorder.rs
│ │ │ │ ├── resampler.rs
│ │ │ │ ├── stream.rs
│ │ │ │ └── sync.rs
│ │ │ ├── config.rs
│ │ │ ├── console_utils/
│ │ │ │ ├── commands.rs
│ │ │ │ ├── console_utils.rs
│ │ │ │ └── mod.rs
│ │ │ ├── database/
│ │ │ │ ├── commands.rs
│ │ │ │ ├── manager.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── models.rs
│ │ │ │ ├── repositories/
│ │ │ │ │ ├── meeting.rs
│ │ │ │ │ ├── mod.rs
│ │ │ │ │ ├── setting.rs
│ │ │ │ │ ├── summary.rs
│ │ │ │ │ ├── transcript.rs
│ │ │ │ │ └── transcript_chunk.rs
│ │ │ │ └── setup.rs
│ │ │ ├── groq/
│ │ │ │ ├── groq.rs
│ │ │ │ └── mod.rs
│ │ │ ├── lib.rs
│ │ │ ├── lib_old_complex.rs
│ │ │ ├── main.rs
│ │ │ ├── notifications/
│ │ │ │ ├── commands.rs
│ │ │ │ ├── manager.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── settings.rs
│ │ │ │ ├── system.rs
│ │ │ │ └── types.rs
│ │ │ ├── ollama/
│ │ │ │ ├── commands.rs
│ │ │ │ ├── metadata.rs
│ │ │ │ ├── mod.rs
│ │ │ │ └── ollama.rs
│ │ │ ├── onboarding.rs
│ │ │ ├── openai/
│ │ │ │ ├── mod.rs
│ │ │ │ └── openai.rs
│ │ │ ├── openrouter/
│ │ │ │ ├── commands.rs
│ │ │ │ ├── mod.rs
│ │ │ │ └── openrouter.rs
│ │ │ ├── parakeet_engine/
│ │ │ │ ├── commands.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── model.rs
│ │ │ │ └── parakeet_engine.rs
│ │ │ ├── state.rs
│ │ │ ├── summary/
│ │ │ │ ├── commands.rs
│ │ │ │ ├── llm_client.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── processor.rs
│ │ │ │ ├── service.rs
│ │ │ │ ├── summary_engine/
│ │ │ │ │ ├── client.rs
│ │ │ │ │ ├── commands.rs
│ │ │ │ │ ├── mod.rs
│ │ │ │ │ ├── model_manager.rs
│ │ │ │ │ ├── models.rs
│ │ │ │ │ └── sidecar.rs
│ │ │ │ ├── template_commands.rs
│ │ │ │ └── templates/
│ │ │ │ ├── defaults.rs
│ │ │ │ ├── loader.rs
│ │ │ │ ├── mod.rs
│ │ │ │ └── types.rs
│ │ │ ├── tray.rs
│ │ │ ├── utils.rs
│ │ │ └── whisper_engine/
│ │ │ ├── _stderr_suppressor.rs
│ │ │ ├── commands.rs
│ │ │ ├── mod.rs
│ │ │ ├── parallel_commands.rs
│ │ │ ├── parallel_processor.rs
│ │ │ ├── system_monitor.rs
│ │ │ └── whisper_engine.rs
│ │ ├── tauri.conf.json
│ │ └── templates/
│ │ ├── README.md
│ │ ├── daily_standup.json
│ │ ├── project_sync.json
│ │ ├── psychatric_session.json
│ │ ├── retrospective.json
│ │ ├── sales_marketing_client_call.json
│ │ └── standard_meeting.json
│ ├── tailwind.config.js
│ ├── tailwind.config.ts
│ └── tsconfig.json
├── llama-helper/
│ ├── Cargo.toml
│ └── src/
│ └── main.rs
└── scripts/
├── generate-update-manifest-github.js
├── inject_transcript.py
└── test-update-locally.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug Report
about: Create a report to help us improve
title: '[BUG] '
labels: bug
assignees: ''
---
## Bug Description
[Provide a clear and concise description of the bug]
## Current Behavior
[Describe what is currently happening]
## Expected Behavior
[Describe what you expected to happen]
## Steps to Reproduce
1. [First step]
2. [Second step]
3. [And so on...]
## Environment
- OS: [e.g., macOS, Windows, Linux]
- Browser: [e.g., Chrome, Firefox, Safari]
- Version: [e.g., 1.0.0]
- Node Version: [e.g., 18.0.0]
- npm/pnpm Version: [e.g., 8.0.0]
## Screenshots/Videos
[If applicable, add screenshots or videos to help explain your problem]
## Error Messages
[If applicable, paste any error messages you're seeing]
## Additional Context
[Add any other context about the problem here]
## Possible Solution
[If you have suggestions on how to fix the issue, please describe them here]
## Checklist
- [ ] I have searched for similar issues
- [ ] I have provided all required information
- [ ] I have included screenshots/videos if applicable
- [ ] I have included error messages if applicable
================================================
FILE: .github/ISSUE_TEMPLATE/documentation.md
================================================
---
name: Documentation
about: Report documentation issues or suggest improvements
title: '[DOCS] '
labels: documentation
assignees: ''
---
## Documentation Issue
[Describe the documentation issue or improvement needed]
## Current Documentation
[Provide links or quotes from the current documentation that needs to be updated]
## Proposed Changes
[Describe the changes you'd like to see in the documentation]
## Reason for Change
[Explain why this documentation change is needed]
## Additional Context
[Add any other context about the documentation issue here]
## Checklist
- [ ] I have searched for similar documentation issues
- [ ] I have provided links to the current documentation
- [ ] I have clearly described the proposed changes
- [ ] I have explained the reason for the change]
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature Request
about: Suggest an idea for this project
title: '[FEATURE] '
labels: enhancement
assignees: ''
---
## Feature Description
[Provide a clear and concise description of the feature you'd like to see]
## Problem Statement
[Describe the problem this feature would solve]
## Proposed Solution
[Describe how you envision this feature working]
## User Story
As a [type of user],
I want [goal],
So that [benefit]
## Acceptance Criteria
- [ ] Criteria 1
- [ ] Criteria 2
- [ ] Criteria 3
## Technical Considerations
[Any technical details or considerations that should be taken into account]
## Alternatives Considered
[Describe any alternative solutions or features you've considered]
## Additional Context
[Add any other context, screenshots, or mockups about the feature request here]
## Checklist
- [ ] I have searched for similar feature requests
- [ ] I have provided all required information
- [ ] I have included any relevant screenshots/mockups
- [ ] I have described the problem and proposed solution clearly
================================================
FILE: .github/issue_template.md
================================================
## Description
[Provide a clear and concise description of the issue]
## Expected Behavior
[Describe what you expected to happen]
## Actual Behavior
[Describe what actually happened]
## Steps to Reproduce
1. [First step]
2. [Second step]
3. [And so on...]
## Environment
- OS: [e.g., macOS, Windows, Linux]
- Browser: [e.g., Chrome, Firefox, Safari]
- Version: [e.g., 1.0.0]
## Screenshots
[If applicable, add screenshots to help explain your problem]
## Additional Context
[Add any other context about the problem here]
## Possible Solution
[If you have suggestions on how to fix the issue, please describe them here]
================================================
FILE: .github/pull_request_template.md
================================================
## Description
[Provide a detailed description of your changes]
## Related Issue
[Link to the issue this PR addresses (e.g., "Fixes #123")]
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Documentation update
- [ ] Performance improvement
- [ ] Code refactoring
- [ ] Other (please describe)
## Testing
- [ ] Unit tests added/updated
- [ ] Manual testing performed
- [ ] All tests pass
## Documentation
- [ ] Documentation updated
- [ ] No documentation needed
## Checklist
- [ ] Code follows project style
- [ ] Self-reviewed the code
- [ ] Added comments for complex code
- [ ] Updated README if needed
- [ ] Branch is up to date with devtest
- [ ] No merge conflicts
## Screenshots (if applicable)
[Add screenshots here if your changes affect the UI]
## Additional Notes
[Add any additional information that might be helpful for reviewers]
================================================
FILE: .github/workflows/ACCELERATION_GUIDE.md
================================================
# CI/CD Hardware Acceleration Guide
This document explains the hardware acceleration configuration for all CI/CD workflows.
## Overview
All workflows now build with optimal hardware acceleration based on the platform:
| Platform | Acceleration | Technology | Performance Boost |
|----------|-------------|------------|------------------|
| **macOS** | GPU | Metal (default) | ~10-15x faster than CPU |
| **Windows** | GPU | Vulkan | ~5-10x faster than CPU |
| **Linux** | CPU Optimized | OpenBLAS | ~2-3x faster than vanilla CPU |
## Previous Configuration (REMOVED)
### ❌ What Was Wrong
**Linux/Ubuntu builds:**
```yaml
env:
WHISPER_NO_AVX: ON # Disabled AVX CPU instructions
WHISPER_NO_AVX2: ON # Disabled AVX2 CPU instructions
```
This configuration **explicitly disabled CPU optimizations**, resulting in very slow transcription performance. Even though Vulkan SDK and OpenBLAS were installed, they were not being used because the build didn't enable the required features.
**Windows builds:**
```yaml
# Vulkan SDK installed but not used
# No --features flag specified
```
The Vulkan SDK was installed but the build didn't include `--features vulkan`, so it fell back to unoptimized CPU mode.
## New Configuration (ENABLED)
### ✅ What's Fixed
**All workflows now include:**
#### 1. Windows Builds (Vulkan GPU)
```yaml
args: --target x86_64-pc-windows-msvc --features vulkan
```
**Benefits:**
- Uses Vulkan API for GPU acceleration
- Works with AMD, Intel, and NVIDIA GPUs
- 5-10x faster transcription than CPU
- Compatible with GitHub Actions Windows runners
**How it works:**
- Vulkan SDK installed via `humbletim/install-vulkan-sdk@v1.2`
- Whisper.cpp compiled with Vulkan backend
- GPU automatically used for inference
#### 2. Linux Builds (OpenBLAS CPU)
```yaml
args: --target x86_64-unknown-linux-gnu --features openblas
```
**Benefits:**
- Optimized BLAS (Basic Linear Algebra Subprograms)
- Hardware-optimized CPU operations
- 2-3x faster than vanilla CPU
- No GPU required (works on GitHub Actions runners)
**Why not Vulkan on Linux?**
- GitHub Actions runners don't have GPUs
- OpenBLAS provides best performance for CPU-only
- More reliable than trying to use virtual GPU
**How it works:**
- OpenBLAS libraries installed (`libopenblas-dev`)
- Whisper.cpp linked against OpenBLAS
- Optimized matrix operations for transcription
#### 3. macOS Builds (Metal GPU)
```yaml
# Metal enabled by default, no flags needed
# Automatically uses Apple Silicon GPU
```
**Benefits:**
- Native Apple Metal GPU acceleration
- 10-15x faster than CPU
- CoreML acceleration also available
- Built-in on macOS runners
**How it works:**
- Metal support is default on macOS
- Automatically uses M1/M2/M3 GPU
- No additional configuration needed
## Updated Workflows
### 1. `build.yml` (Reusable Workflow)
**New step added:**
```yaml
- name: Determine build features
id: build-features
shell: bash
run: |
FEATURES=""
# Windows: Use Vulkan for GPU acceleration
if [[ "${{ inputs.platform }}" == *"windows"* ]]; then
FEATURES="--features vulkan"
echo "Windows build with Vulkan GPU acceleration"
fi
# Linux: Use OpenBLAS for optimized CPU performance
if [[ "${{ inputs.platform }}" == *"ubuntu"* ]]; then
FEATURES="--features openblas"
echo "Linux build with OpenBLAS CPU optimization"
fi
# macOS: Uses Metal by default
if [[ "${{ inputs.platform }}" == *"macos"* ]]; then
echo "macOS build with Metal GPU acceleration (default)"
fi
echo "features=$FEATURES" >> "$GITHUB_OUTPUT"
```
**Build command updated:**
```yaml
args: ${{ inputs.build-args }} ${{ steps.build-features.outputs.features }}
```
**Removed:**
```yaml
# REMOVED: These were disabling CPU optimizations
WHISPER_NO_AVX: ${{ contains(inputs.platform, 'ubuntu') && 'ON' || '' }}
WHISPER_NO_AVX2: ${{ contains(inputs.platform, 'ubuntu') && 'ON' || '' }}
```
### 2. `build-devtest.yml` (DevTest Workflow)
Same changes as `build.yml`:
- ✅ Added feature detection step
- ✅ Removed `WHISPER_NO_AVX` and `WHISPER_NO_AVX2`
- ✅ Appends features to build args
### 3. `build-windows.yml` (Windows Standalone)
**Build command updated:**
```yaml
args: --target x86_64-pc-windows-msvc --features vulkan ${{ steps.build-profile.outputs.args }}
```
Now explicitly enables Vulkan acceleration.
### 4. `build-linux.yml` (Linux Standalone)
**Build command updated:**
```yaml
args: --target x86_64-unknown-linux-gnu --features openblas ${{ steps.build-profile.outputs.args }}
```
Now explicitly enables OpenBLAS optimization.
### 5. `build-macos.yml` (macOS Standalone)
**New info step added:**
```yaml
- name: Configure build acceleration
run: |
echo "✓ macOS build will use Metal GPU acceleration (enabled by default)"
echo "✓ CoreML acceleration available for Apple Silicon"
```
Documents that Metal is enabled by default.
## Performance Impact
### Transcription Speed Comparison
For a **10-minute meeting recording** (Whisper `base` model):
| Configuration | Time to Transcribe | Real-time Factor |
|--------------|-------------------|------------------|
| **Old Linux (no AVX)** | ~15 minutes | 1.5x slower than real-time ⚠️ |
| **New Linux (OpenBLAS)** | ~5 minutes | 2x faster than real-time ✅ |
| **Old Windows (CPU)** | ~10 minutes | Same as real-time ⚠️ |
| **New Windows (Vulkan)** | ~2 minutes | 5x faster than real-time ✅ |
| **macOS (Metal)** | ~1 minute | 10x faster than real-time ✅ |
### Build Time Impact
The acceleration changes **do not significantly increase build time**:
- Vulkan SDK: Already being installed
- OpenBLAS: Lightweight library
- Compilation time: ~same (30-45 minutes total)
## Verification
### How to Verify Acceleration is Working
**1. Check Build Logs**
Look for these messages in the workflow output:
```
Windows build with Vulkan GPU acceleration
✓ Windows build with Vulkan GPU acceleration
```
```
Linux build with OpenBLAS CPU optimization
✓ Linux build with OpenBLAS CPU optimization
```
```
macOS build with Metal GPU acceleration (default)
✓ macOS build will use Metal GPU acceleration (enabled by default)
```
**2. Check Build Command**
In the "Build with Tauri" step, verify the command includes:
```bash
# Windows
tauri build --target x86_64-pc-windows-msvc --features vulkan
# Linux
tauri build --target x86_64-unknown-linux-gnu --features openblas
# macOS (features implicit)
tauri build --target aarch64-apple-darwin
```
**3. Runtime Verification**
When using the built application:
- Transcription should feel snappy
- Real-time transcription should keep up with speech
- No noticeable lag when processing audio
### Checking Locally
You can verify the features locally:
```bash
# Windows (from frontend directory)
pnpm run tauri build -- --features vulkan
# Linux
pnpm run tauri build -- --features openblas
# macOS (Metal is default)
pnpm run tauri build
```
## Technical Details
### Whisper.cpp Features
The `whisper-rs` crate (which wraps whisper.cpp) supports these features:
```toml
[features]
metal = ["whisper-rs/metal"] # macOS Metal
cuda = ["whisper-rs/cuda"] # NVIDIA CUDA
vulkan = ["whisper-rs/vulkan"] # Cross-platform Vulkan
hipblas = ["whisper-rs/hipblas"] # AMD ROCm
openblas = ["whisper-rs/openblas"] # Optimized CPU BLAS
```
### Why Not CUDA?
**CUDA requires:**
- NVIDIA GPU hardware
- CUDA toolkit installation
- NVIDIA drivers
**GitHub Actions runners:**
- Don't have NVIDIA GPUs
- Can't use CUDA
**Vulkan is better for CI/CD because:**
- Software-based fallback available
- Works without dedicated GPU hardware
- Broader compatibility
### OpenBLAS vs Vulkan on Linux
We chose **OpenBLAS** over Vulkan for Linux because:
- ✅ More reliable on CI runners
- ✅ Better CPU optimization
- ✅ No GPU hardware needed
- ✅ Consistent performance
- ⚠️ Vulkan without GPU gives minimal benefit
For **local Linux development with GPU**, users can manually build with:
```bash
pnpm run tauri build -- --features vulkan
```
## Troubleshooting
### Build Fails with Vulkan Error (Windows)
**Error:**
```
error: failed to compile whisper-rs with Vulkan support
```
**Solution:**
- Ensure Vulkan SDK step runs successfully
- Check `humbletim/install-vulkan-sdk@v1.2` output
- Verify Vulkan version matches (1.4.309.0)
### Build Fails with OpenBLAS Error (Linux)
**Error:**
```
error: could not find OpenBLAS library
```
**Solution:**
- Ensure `libopenblas-dev` is in apt install list
- Check dependency installation step completed
- Verify OpenBLAS package is available for Ubuntu version
### Performance Still Slow
**Check:**
1. ✅ Build logs show correct features enabled
2. ✅ Build command includes `--features` flag
3. ✅ No error messages during Whisper compilation
4. ✅ Application binary is from new build (not cached old version)
**If still slow:**
- May be Whisper model size (try smaller model)
- May be audio file issues (check format)
- May be system resource constraints
## Future Improvements
### Potential Enhancements
1. **Add CUDA support** for users with NVIDIA GPUs
- Detect if NVIDIA GPU available
- Optionally enable CUDA feature
- Fallback to Vulkan if CUDA fails
2. **Add CoreML support** for macOS
- Enable explicit CoreML acceleration
- Test performance vs Metal alone
- Document benefits
3. **Dynamic feature detection**
- Detect available hardware at runtime
- Automatically select best backend
- Provide user override options
4. **Performance metrics**
- Log transcription performance in CI
- Compare across builds
- Alert if performance degrades
## Related Documentation
- [CLAUDE.md](../../CLAUDE.md) - Project overview with build commands
- [WORKFLOWS_OVERVIEW.md](WORKFLOWS_OVERVIEW.md) - All workflows comparison
- [README_DEVTEST.md](README_DEVTEST.md) - DevTest workflow guide
- [Whisper.cpp GitHub](https://github.com/ggerganov/whisper.cpp) - Upstream project
## Summary
✅ **All CI/CD workflows now use hardware acceleration**
- Windows: Vulkan GPU
- Linux: OpenBLAS CPU optimization
- macOS: Metal GPU (default)
✅ **Performance improvements**
- 2-10x faster transcription
- Better real-time factor
- Improved user experience
✅ **No build time increase**
- Same overall build duration
- Dependencies already installed
- Just enabling features
❌ **Removed slow configurations**
- No more `WHISPER_NO_AVX`
- No more `WHISPER_NO_AVX2`
- No more unoptimized CPU-only
---
**Last Updated:** 2025-01-15
**Version:** 1.0
**Impact:** All workflows
================================================
FILE: .github/workflows/README_DEVTEST.md
================================================
# DevTest Build Workflow
This document explains how to use the `build-devtest.yml` workflow for building and testing.
## Overview
The DevTest workflow is specifically designed for development and testing purposes. It:
- Builds for all platforms (macOS, Windows, Linux)
- Has **code signing disabled by default** to speed up builds
- Allows **optional signing** via workflow dispatch input
- Uploads artifacts for testing
## Triggering the Workflow
The workflow runs via **manual dispatch only**:
1. Go to **Actions** tab in your GitHub repository
2. Select **Build and Test - DevTest** from the left sidebar
3. Click **Run workflow** button
4. Configure options:
- **Branch**: Select the branch to build
- **Sign the build**: Check to enable code signing (default: unchecked)
- **Upload build artifacts**: Check to upload artifacts (default: checked)
5. Click **Run workflow** to start
## Workflow Options
### Sign the build
- **Unchecked (default)**: Fast builds without code signing (~25-30 minutes)
- **Checked**: Full code signing for all platforms (~35-45 minutes)
### Upload build artifacts
- **Checked (default)**: Artifacts are uploaded and available for download
- **Unchecked**: Build runs but no artifacts are saved
## Build Matrix
The workflow builds for all platforms in parallel:
| Platform | Target | Output |
|----------|--------|--------|
| macOS (Apple Silicon) | aarch64-apple-darwin | DMG + App |
| Windows (x64) | x86_64-pc-windows-msvc | MSI + NSIS |
| Linux (Ubuntu 22.04) | x86_64-unknown-linux-gnu | DEB |
| Linux (Ubuntu 24.04) | x86_64-unknown-linux-gnu | AppImage + RPM |
## Code Signing Details
When signing is enabled:
### macOS
- Uses **Apple Developer Certificate** from secrets
- Performs **notarization** with Apple ID
- Signs both DMG and .app bundle
- Verifies signatures with `codesign` and `spctl`
### Windows
- Uses **DigiCert KeyLocker** (cloud HSM)
- Signs both MSI and NSIS installers
- Verifies signatures with PowerShell
### Linux
- Uses **Tauri updater signing** (Ed25519)
- Signs update manifests for auto-updater
## Build Artifacts
Artifacts are automatically uploaded and retained for **14 days**:
- **macOS**: `*.dmg`, `*.app`, `*.app.tar.gz`, `*.app.tar.gz.sig`
- **Windows**: `*.msi`, `*.msi.sig`, `*.exe`, `*.exe.sig`
- **Linux**: `*.deb`, `*.AppImage`, `*.rpm`
### Downloading Artifacts
1. Go to **Actions** tab
2. Select the completed workflow run
3. Scroll down to **Artifacts** section
4. Click on the artifact name to download
## Examples
### Example 1: Unsigned Build (Default, Fast)
1. Go to Actions > Build and Test - DevTest
2. Click "Run workflow"
3. Leave all options at defaults
4. Click "Run workflow"
**Result:** Builds without signing in ~25-30 minutes
---
### Example 2: Signed Build
1. Go to Actions > Build and Test - DevTest
2. Click "Run workflow"
3. Check "Sign the build"
4. Click "Run workflow"
**Result:** Builds with full code signing in ~35-45 minutes
## Hardware Acceleration
Each platform uses optimal hardware acceleration:
| Platform | Acceleration | Performance |
|----------|-------------|-------------|
| macOS | Metal GPU | 10-15x faster than CPU |
| Windows | Vulkan GPU | 5-10x faster than CPU |
| Linux | OpenBLAS CPU | 2-3x faster than vanilla CPU |
## Troubleshooting
### Signing Not Working
**Problem:** Signing enabled but builds are still unsigned
**Solutions:**
1. Verify all required secrets are configured in repository settings
2. Check the workflow logs for specific error messages
3. Ensure secrets haven't expired
### Build Failures
**Problem:** Build fails during signing phase
**Solutions:**
1. Check that all required secrets are configured:
- `APPLE_CERTIFICATE`, `APPLE_ID`, `APPLE_PASSWORD`, `APPLE_TEAM_ID`
- `SM_HOST`, `SM_API_KEY`, `SM_CODE_SIGNING_CERT_SHA1_HASH`
- `TAURI_SIGNING_PRIVATE_KEY`, `TAURI_SIGNING_PRIVATE_KEY_PASSWORD`
2. Review workflow logs for specific error messages
3. Try running without signing first to isolate the issue
### Artifacts Not Available
**Problem:** Can't download build artifacts
**Solutions:**
1. Check workflow status - artifacts only available after successful build
2. Artifacts expire after 14 days
3. Ensure "Upload build artifacts" was checked when running
## Performance Comparison
| Build Type | Duration | When to Use |
|------------|----------|-------------|
| **Unsigned** (default) | ~25-30 min | Regular development, quick testing |
| **Signed** | ~35-45 min | Pre-release testing, production-like testing |
## Best Practices
1. **Use unsigned builds** for routine development and testing
2. **Enable signing** only when:
- Testing production-like scenarios
- Preparing for release
- Testing installer behavior
- Verifying code signing infrastructure
3. **Always test** locally before triggering workflow to save CI time
4. **Review** the workflow summary to confirm build status
## Workflow Configuration
Located at: `.github/workflows/build-devtest.yml`
Key configuration:
- **Default signing:** OFF
- **Artifact retention:** 14 days
- **Parallel builds:** All platforms simultaneously
- **Trigger:** Manual dispatch only
## Related Workflows
- `build-macos.yml` - macOS-specific builds with signing
- `build-windows.yml` - Windows-specific builds with signing
- `build-linux.yml` - Linux-specific builds with signing
- `build-test.yml` - All platforms with signing (pre-release)
- `release.yml` - Production release workflow
================================================
FILE: .github/workflows/WORKFLOWS_OVERVIEW.md
================================================
# GitHub Actions Workflows Overview
This document provides a quick overview of all available CI/CD workflows in this repository.
**Note:** All workflows in this repository use **manual triggers only** (`workflow_dispatch`). There are no automatic triggers from push or pull request events.
## Workflow Files
### 1. **build-devtest.yml** - DevTest Builds
**Purpose:** Fast builds for development and testing
**Key Features:**
- Signing OFF by default (faster builds)
- Optional signing via workflow dispatch input
- All platforms in parallel
- 14-day artifact retention
**Triggers:**
- Manual dispatch only
**Use When:**
- Regular development work
- Testing features
- Need fast feedback
---
### 2. **build-macos.yml** - macOS Standalone Builds
**Purpose:** Build and test specifically for Apple Silicon (M1/M2/M3)
**Key Features:**
- Apple Developer Certificate signing (optional)
- Notarization with Apple ID
- Signature verification
- macOS-focused optimizations
**Triggers:**
- Manual dispatch only
**Use When:**
- macOS-specific development
- Testing Metal GPU acceleration
- Verifying macOS-specific features
**Outputs:**
- `.dmg` installer
- `.app` bundle
---
### 3. **build-windows.yml** - Windows Standalone Builds
**Purpose:** Build and test specifically for Windows x64
**Key Features:**
- DigiCert KeyLocker signing (cloud HSM)
- Signs both MSI and NSIS installers
- Signature verification with PowerShell
- MSI installer validation
**Triggers:**
- Manual dispatch only
**Use When:**
- Windows-specific development
- Testing CUDA/Vulkan GPU acceleration
- Verifying Windows-specific features
**Outputs:**
- `.msi` installer
- `.exe` NSIS installer
---
### 4. **build-linux.yml** - Linux Standalone Builds
**Purpose:** Build and test for Linux distributions
**Key Features:**
- Support for Ubuntu 22.04 and 24.04
- Multiple bundle formats (DEB, AppImage, RPM)
- Tauri updater signing
- AppImage compatibility fixes
- Package verification
**Triggers:**
- Manual dispatch only
**Use When:**
- Linux-specific development
- Testing Vulkan GPU acceleration
- Verifying package formats
**Outputs:**
- `.deb` package (Ubuntu/Debian)
- `.AppImage` portable
- `.rpm` package (Fedora/RHEL)
---
### 5. **build-test.yml** - Multi-Platform Test Builds
**Purpose:** Test builds across all platforms with signing
**Key Features:**
- Signing ON by default
- All platforms in parallel
- Uses reusable `build.yml` workflow
- 30-day artifact retention
- Artifacts prefixed with `meetily-test-`
**Triggers:**
- Manual dispatch only
**Use When:**
- Pre-release testing
- Verifying signing infrastructure
- Testing across all platforms simultaneously
---
### 6. **build.yml** - Reusable Build Workflow
**Purpose:** Shared workflow used by other workflows
**Key Features:**
- Reusable workflow (called by others)
- Highly configurable inputs
- Used by `build-test.yml` and `release.yml`
**Not directly triggered** - used as a building block
---
### 7. **release.yml** - Production Release
**Purpose:** Create official releases with signed binaries
**Key Features:**
- Signing REQUIRED
- Creates GitHub Release (draft)
- Version tags from `tauri.conf.json`
- Uploads release assets
- **macOS and Windows only** (Linux excluded from production releases)
- Auto-generates `latest.json` for Tauri updater
- **Auto-increment versioning**: If tag exists, auto-increments (e.g., `0.1.1` -> `0.1.1.1` -> `0.1.1.2`, up to `.100`)
**Triggers:**
- Manual dispatch only
**Use When:**
- Ready to publish a new version
- Creating official release artifacts
**Outputs:**
- GitHub Release (draft)
- macOS: DMG installer, app.tar.gz (updater), .sig
- Windows: MSI installer (signed), NSIS installer (signed), .sig files
- Updater manifest: latest.json
- Release notes auto-generated
**Version Behavior:**
- If `v0.1.1` tag doesn't exist: creates `v0.1.1`
- If `v0.1.1` exists: creates `v0.1.1.1`
- If `v0.1.1.1` exists: creates `v0.1.1.2`
- Maximum: `v0.1.1.100` (then update `tauri.conf.json`)
**Note:** Linux builds are not included in releases. Use `build-linux.yml` for Linux testing.
---
### 8. **pr-main-check.yml** - Validation Check
**Purpose:** Quick validation of version and configuration
**Key Features:**
- No builds triggered
- Validates version format
- Shows current branch info
- Provides next steps guidance
**Triggers:**
- Manual dispatch only
**Use When:**
- Quick configuration check
- Before running full builds
---
## How to Run Workflows
1. **Go to Actions tab** in GitHub repository
2. **Select workflow** from left sidebar
3. **Click "Run workflow"** button
4. **Select branch** to run against
5. **Configure options** (build type, signing, etc.)
6. **Click "Run workflow"** to start
7. **Monitor progress** in the Actions tab
---
## Quick Decision Guide
### "I'm developing a new feature..."
- **Use `build-devtest.yml`** (manual dispatch)
- Fast builds, no signing by default
- Enable signing checkbox if needed
### "I need to test macOS-specific code..."
- **Use `build-macos.yml`** (manual dispatch)
- Focus on macOS
- Optional signing
### "I need to test Windows-specific code..."
- **Use `build-windows.yml`** (manual dispatch)
- Focus on Windows
- Optional signing
### "I need to test Linux packages..."
- **Use `build-linux.yml`** (manual dispatch)
- Choose Ubuntu version
- Choose bundle types
### "I need signed builds for all platforms..."
- **Use `build-test.yml`** (manual dispatch)
- All platforms
- Signing enabled
- Full verification
### "I'm ready to release..."
- **Use `release.yml`** (manual dispatch)
- Creates GitHub Release
- All platforms, fully signed
- Production-ready artifacts
---
## Workflow Dependencies
```
build.yml (reusable)
|-- build-test.yml (calls build.yml)
|-- release.yml (calls build.yml)
Standalone (don't use build.yml):
|-- build-macos.yml
|-- build-windows.yml
|-- build-linux.yml
|-- build-devtest.yml
|-- pr-main-check.yml (validation only)
```
---
## Comparison Matrix
| Workflow | Platforms | Default Signing | Speed | Retention | Use Case |
|----------|-----------|----------------|-------|-----------|----------|
| `build-devtest.yml` | All | OFF | Fast | 14 days | Development |
| `build-macos.yml` | macOS | Optional | Medium | 30 days | macOS dev |
| `build-windows.yml` | Windows | Optional | Medium | 30 days | Windows dev |
| `build-linux.yml` | Linux | Optional | Medium | 30 days | Linux dev |
| `build-test.yml` | All | ON | Slow | 30 days | Pre-release |
| `release.yml` | macOS + Windows | REQUIRED | Slow | Permanent | Release |
---
## Artifact Naming Convention
```
meetily-{workflow}-{platform}-{target}-{version}
```
**Examples:**
- `meetily-devtest-macOS-aarch64-apple-darwin-0.1.3`
- `meetily-test-windows-x86_64-pc-windows-msvc-0.1.3`
- `meetily-macos-aarch64-release-0.1.3`
---
## Required Secrets
All workflows require these secrets to be configured:
### macOS Signing
- `APPLE_CERTIFICATE` - Developer ID certificate (base64)
- `APPLE_CERTIFICATE_PASSWORD` - Certificate password
- `APPLE_ID` - Apple ID email
- `APPLE_PASSWORD` - App-specific password
- `APPLE_TEAM_ID` - Team ID
- `KEYCHAIN_PASSWORD` - Temporary keychain password
### Windows Signing (DigiCert)
- `SM_HOST` - DigiCert host URL
- `SM_API_KEY` - API key
- `SM_CLIENT_CERT_FILE_B64` - Client cert (base64)
- `SM_CLIENT_CERT_PASSWORD` - Client cert password
- `SM_CODE_SIGNING_CERT_SHA1_HASH` - Certificate hash
### Tauri Updater (All Platforms)
- `TAURI_SIGNING_PRIVATE_KEY` - Ed25519 private key
- `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` - Key password
### Application Configuration
- `MEETILY_RSA_PUBLIC_KEY` - License validation public key
- `SUPABASE_URL` - Online license verification
- `SUPABASE_ANON_KEY` - Supabase anonymous key
---
## Performance Tips
1. **Use devtest workflow** for routine development (fastest)
2. **Enable signing** only when necessary (adds 10-15 minutes)
3. **Test specific platforms** when working on platform-specific code
4. **Run full builds** (`build-test.yml`) before releases
5. **Cache is enabled** - subsequent builds are faster
---
## Troubleshooting
### Build fails with version error (Windows MSI)
- Ensure version in `tauri.conf.json` doesn't contain non-numeric pre-release identifiers
- Use `0.1.3` not `0.1.2-pro-trial`
### Signing fails
- Verify all required secrets are configured
- Check secret expiration dates
- Review workflow logs for specific errors
### Artifacts not available
- Check build succeeded completely
- Artifacts expire based on retention period
- Ensure `upload-artifacts` is enabled
### Workflow not appearing in Actions
- Verify YAML syntax is valid
- Check file is in `.github/workflows/` directory
- Ensure file extension is `.yml` or `.yaml`
---
## Support
For issues with workflows:
1. Check workflow logs in Actions tab
2. Review this documentation
3. Check `README_DEVTEST.md` for devtest-specific help
4. Check `ACCELERATION_GUIDE.md` for GPU/performance info
================================================
FILE: .github/workflows/build-devtest.yml
================================================
name: "Build and Test - DevTest"
on:
workflow_dispatch:
inputs:
sign-build:
description: 'Sign the build'
required: false
type: boolean
default: false
upload-artifacts:
description: 'Upload build artifacts'
required: false
type: boolean
default: true
# Cancel duplicate workflow runs
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
RUST_BACKTRACE: 1
CARGO_TERM_COLOR: always
jobs:
build-devtest:
name: Build ${{ matrix.platform-name }}
runs-on: ${{ matrix.platform }}
permissions:
contents: write
strategy:
fail-fast: false
matrix:
include:
- platform: macos-latest
platform-name: macOS
target: aarch64-apple-darwin
args: --target aarch64-apple-darwin
- platform: windows-latest
platform-name: Windows
target: x86_64-pc-windows-msvc
args: --target x86_64-pc-windows-msvc
- platform: ubuntu-22.04
platform-name: Linux (Ubuntu 22.04)
target: x86_64-unknown-linux-gnu
args: --target x86_64-unknown-linux-gnu --bundles deb
- platform: ubuntu-24.04
platform-name: Linux (Ubuntu 24.04)
target: x86_64-unknown-linux-gnu
args: --target x86_64-unknown-linux-gnu --bundles appimage,rpm
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get version from tauri.conf.json
id: get-version
shell: bash
run: |
VERSION=$(grep -o '"version": "[^"]*"' frontend/src-tauri/tauri.conf.json | cut -d'"' -f4)
echo "Application version: $VERSION"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 8
run_install: false
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Setup pnpm cache
uses: actions/cache@v4
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Rust cache
uses: swatinem/rust-cache@v2
with:
workspaces: ". -> target"
key: ${{ matrix.platform }}-${{ matrix.target }}
# Platform-specific dependencies
- name: Install dependencies (Ubuntu 24.04)
if: matrix.platform == 'ubuntu-24.04'
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 \
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 \
fuse libfuse2
- name: Install dependencies (Ubuntu 22.04)
if: matrix.platform == 'ubuntu-22.04'
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 fuse libfuse2
- name: Prepare Vulkan SDK (Ubuntu 24.04)
if: matrix.platform == 'ubuntu-24.04'
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 (Ubuntu 22.04)
if: matrix.platform == 'ubuntu-22.04'
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 Vulkan SDK (Windows)
if: contains(matrix.platform, 'windows')
uses: humbletim/install-vulkan-sdk@v1.2
with:
version: 1.4.309.0
cache: true
- name: Install frontend dependencies
run: |
cd frontend
pnpm install
# macOS signing setup
- name: Import Apple Developer Certificate
if: contains(matrix.platform, 'macos') && github.event.inputs.sign-build == 'true'
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 Apple certificate
if: contains(matrix.platform, 'macos') && github.event.inputs.sign-build == 'true'
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: $CERT_ID"
# Windows signing setup
- name: Setup DigiCert KeyLocker
if: contains(matrix.platform, 'windows') && github.event.inputs.sign-build == 'true'
uses: digicert/ssm-code-signing@v1.1.1
- name: Setup DigiCert Environment
if: contains(matrix.platform, 'windows') && github.event.inputs.sign-build == 'true'
shell: pwsh
run: |
# Set environment variables
"SM_HOST=${{ secrets.SM_HOST }}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"SM_API_KEY=${{ secrets.SM_API_KEY }}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"SM_CLIENT_CERT_PASSWORD=${{ secrets.SM_CLIENT_CERT_PASSWORD }}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"SM_CODE_SIGNING_CERT_SHA1_HASH=${{ secrets.SM_CODE_SIGNING_CERT_SHA1_HASH }}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
# Decode and save client certificate using PowerShell
$certPath = "D:\Certificate_pkcs12.p12"
$base64String = "${{ secrets.SM_CLIENT_CERT_FILE_B64 }}"
$certBytes = [System.Convert]::FromBase64String($base64String)
[System.IO.File]::WriteAllBytes($certPath, $certBytes)
# Verify the certificate file was created and has content
if (Test-Path $certPath) {
$certFile = Get-Item $certPath
Write-Host "Certificate file created: $certPath"
Write-Host " Size: $($certFile.Length) bytes"
Write-Host " Last Modified: $($certFile.LastWriteTime)"
# Verify it's a valid PKCS12 file by checking if we can load it
try {
$testCert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($certPath, "${{ secrets.SM_CLIENT_CERT_PASSWORD }}")
Write-Host " Certificate file is valid PKCS12 format"
Write-Host " Password is correct"
Write-Host " Certificate Subject: $($testCert.Subject)"
Write-Host " Certificate Thumbprint: $($testCert.Thumbprint)"
} catch {
Write-Warning "Certificate validation failed"
Write-Warning "Error: $($_.Exception.Message)"
Write-Warning "This may cause issues with signing, but we'll continue..."
}
} else {
Write-Error "Certificate file was not created at $certPath"
exit 1
}
# Set the environment variable with Windows-style path
"SM_CLIENT_CERT_FILE=D:\Certificate_pkcs12.p12" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
- name: Verify DigiCert Setup
if: contains(matrix.platform, 'windows') && github.event.inputs.sign-build == 'true'
shell: pwsh
run: |
smctl --version
smctl keypair ls
smctl healthcheck
- name: Sync Certificate to Windows Certificate Store
if: contains(matrix.platform, 'windows') && github.event.inputs.sign-build == 'true'
shell: pwsh
run: |
Write-Host "============================================================"
Write-Host "=== SYNCING CERTIFICATE TO WINDOWS CERTIFICATE STORE ==="
Write-Host "============================================================"
Write-Host "This step syncs the DigiCert certificate from KeyLocker HSM"
Write-Host "to the Windows Certificate Store (required for signing)"
Write-Host ""
# First, get the keypair alias from smctl keypair ls
Write-Host "Retrieving keypair alias from DigiCert KeyLocker..."
$keypairOutput = smctl keypair ls 2>&1 | Out-String
Write-Host $keypairOutput
# Extract the alias (looking for pattern like "key_XXXXXXXXXX")
$aliasMatch = [regex]::Match($keypairOutput, 'key_\d+')
if ($aliasMatch.Success) {
$keypairAlias = $aliasMatch.Value
Write-Host "Found keypair alias: $keypairAlias"
} else {
Write-Error "Could not find keypair alias in smctl output"
Write-Error "Output was: $keypairOutput"
exit 1
}
Write-Host ""
Write-Host "Syncing certificate using alias: $keypairAlias"
$certsyncOutput = smctl windows certsync --keypair-alias $keypairAlias 2>&1
Write-Host $certsyncOutput
if ($LASTEXITCODE -ne 0) {
Write-Host ""
Write-Error "Certificate sync FAILED"
Write-Error "Exit code: $LASTEXITCODE"
Write-Error "Output: $certsyncOutput"
Write-Error ""
Write-Error "Possible causes:"
Write-Error " 1. Keypair alias '$keypairAlias' not found in KeyLocker"
Write-Error " 2. Certificate is revoked or expired"
Write-Error " 3. API credentials are invalid"
exit 1
}
Write-Host ""
Write-Host "Certificate synced successfully"
Write-Host ""
# Verify certificate is now in Windows store
Write-Host "Verifying certificate in Windows Certificate Store..."
$cert = Get-ChildItem -Path Cert:\CurrentUser\My | Where-Object { $_.Thumbprint -eq $env:SM_CODE_SIGNING_CERT_SHA1_HASH } | Select-Object -First 1
if (-not $cert) {
# Try LocalMachine store
$cert = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object { $_.Thumbprint -eq $env:SM_CODE_SIGNING_CERT_SHA1_HASH } | Select-Object -First 1
}
if ($cert) {
Write-Host "Certificate found in Windows Certificate Store"
Write-Host " Subject: $($cert.Subject)"
Write-Host " Issuer: $($cert.Issuer)"
Write-Host " Thumbprint: $($cert.Thumbprint)"
Write-Host " Valid From: $($cert.NotBefore)"
Write-Host " Valid Until: $($cert.NotAfter)"
} else {
Write-Warning "Certificate not found in Windows Certificate Store after sync"
Write-Warning "Signing may fail. Continuing anyway..."
}
# Export keypair alias for Tauri signCommand
Write-Host ""
Write-Host "Exporting DIGICERT_KEYPAIR_ALIAS for Tauri signCommand..."
"DIGICERT_KEYPAIR_ALIAS=$keypairAlias" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
Write-Host "DIGICERT_KEYPAIR_ALIAS=$keypairAlias"
Write-Host ""
Write-Host "============================================================"
Write-Host ""
# Determine build features for acceleration
- name: Determine build features
id: build-features
shell: bash
run: |
FEATURES=""
# Windows: Use Vulkan for GPU acceleration
if [[ "${{ matrix.platform }}" == *"windows"* ]]; then
FEATURES="--features vulkan"
echo "Windows build with Vulkan GPU acceleration"
fi
# Linux: Use OpenBLAS for optimized CPU performance
if [[ "${{ matrix.platform }}" == *"ubuntu"* ]]; then
FEATURES="--features openblas"
echo "Linux build with OpenBLAS CPU optimization"
fi
# macOS: Uses Metal by default, no additional features needed
if [[ "${{ matrix.platform }}" == *"macos"* ]]; then
echo "macOS build with Metal GPU acceleration (default)"
fi
echo "features=$FEATURES" >> "$GITHUB_OUTPUT"
- name: Build llama-helper sidecar
shell: bash
run: |
echo "Building llama-helper sidecar..."
# Determine llama-helper features based on platform
LLAMA_FEATURES=""
if [[ "${{ matrix.platform }}" == *"macos"* ]]; then
LLAMA_FEATURES="--features metal"
echo "Using Metal GPU acceleration for macOS"
elif [[ "${{ matrix.platform }}" == *"windows"* ]]; then
LLAMA_FEATURES="--features vulkan"
echo "Using Vulkan GPU acceleration for Windows"
else
echo "Using CPU-only mode for Linux"
fi
# Build llama-helper from workspace root
cargo build --release -p llama-helper $LLAMA_FEATURES
# Determine binary extension
EXT=""
if [[ "${{ matrix.platform }}" == *"windows"* ]]; then
EXT=".exe"
fi
# Copy binary to binaries directory
mkdir -p frontend/src-tauri/binaries
cp target/release/llama-helper${EXT} frontend/src-tauri/binaries/llama-helper-${{ matrix.target }}${EXT}
echo "Copied llama-helper to frontend/src-tauri/binaries/llama-helper-${{ matrix.target }}${EXT}"
ls -la frontend/src-tauri/binaries/
# Build step (with code signing)
- name: Build Tauri app (with code signing)
if: github.event.inputs.sign-build == 'true'
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# macOS platform code signing
APPLE_ID: ${{ contains(matrix.platform, 'macos') && secrets.APPLE_ID || '' }}
APPLE_ID_PASSWORD: ${{ contains(matrix.platform, 'macos') && secrets.APPLE_ID_PASSWORD || '' }}
APPLE_PASSWORD: ${{ contains(matrix.platform, 'macos') && secrets.APPLE_PASSWORD || '' }}
APPLE_TEAM_ID: ${{ contains(matrix.platform, 'macos') && secrets.APPLE_TEAM_ID || '' }}
APPLE_CERTIFICATE: ${{ contains(matrix.platform, 'macos') && secrets.APPLE_CERTIFICATE || '' }}
APPLE_CERTIFICATE_PASSWORD: ${{ contains(matrix.platform, 'macos') && secrets.APPLE_CERTIFICATE_PASSWORD || '' }}
APPLE_SIGNING_IDENTITY: ${{ contains(matrix.platform, 'macos') && env.CERT_ID || '' }}
# Tauri updater signing (ALWAYS enabled for .sig files)
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
# License validation RSA public key (embedded at build time)
MEETILY_RSA_PUBLIC_KEY: ${{ secrets.MEETILY_RSA_PUBLIC_KEY }}
# Supabase configuration (for online license verification)
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_ANON_KEY }}
with:
projectPath: frontend
args: ${{ matrix.args }} ${{ steps.build-features.outputs.features }}
# Build step (without code signing - updater signing only)
- name: Build Tauri app (unsigned)
if: github.event.inputs.sign-build != 'true'
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Tauri updater signing (ALWAYS enabled for .sig files)
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
# License validation RSA public key (embedded at build time)
MEETILY_RSA_PUBLIC_KEY: ${{ secrets.MEETILY_RSA_PUBLIC_KEY }}
# Supabase configuration (for online license verification)
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_ANON_KEY }}
with:
projectPath: frontend
args: ${{ matrix.args }} ${{ steps.build-features.outputs.features }}
# Linux post-build processing
- name: Remove libwayland-client.so from AppImage
if: contains(matrix.platform, 'ubuntu-24.04')
run: |
APPIMAGE_PATH=$(find target/x86_64-unknown-linux-gnu/release/bundle/appimage -name "*.AppImage" | head -1)
if [ -n "$APPIMAGE_PATH" ]; then
echo "Processing AppImage: $APPIMAGE_PATH"
chmod +x "$APPIMAGE_PATH"
cd "$(dirname "$APPIMAGE_PATH")"
APPIMAGE_NAME=$(basename "$APPIMAGE_PATH")
"./$APPIMAGE_NAME" --appimage-extract
find squashfs-root -name "libwayland-client.so*" -type f -delete
wget -q https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
chmod +x appimagetool-x86_64.AppImage
ARCH=x86_64 ./appimagetool-x86_64.AppImage --no-appstream squashfs-root "$APPIMAGE_NAME"
rm -rf squashfs-root appimagetool-x86_64.AppImage
echo "AppImage processed successfully"
fi
# Verification steps
- name: Verify build artifacts
shell: bash
run: |
echo "Build artifacts:"
find target -name "*.dmg" -o -name "*.app" -o -name "*.msi" -o -name "*.exe" -o -name "*.deb" -o -name "*.AppImage" -o -name "*.rpm" 2>/dev/null || echo "Finding artifacts..."
- name: Verify code signing (macOS App)
if: contains(matrix.platform, 'macos') && github.event.inputs.sign-build == 'true'
run: |
APP_PATH=$(find target/aarch64-apple-darwin/release/bundle/macos -name "*.app" | head -1)
if [ -n "$APP_PATH" ]; then
echo "=== Verifying App Bundle: $APP_PATH ==="
codesign -dv --verbose=4 "$APP_PATH" 2>&1 | grep -i "signature\|authority"
codesign --verify --deep --strict "$APP_PATH" && echo "Code signature is valid"
spctl -a -vvv "$APP_PATH" && echo "App is notarized and accepted by Gatekeeper"
fi
DMG_PATH=$(find target/aarch64-apple-darwin/release/bundle/dmg -name "*.dmg" | head -1)
if [ -n "$DMG_PATH" ]; then
echo "=== Verifying DMG: $DMG_PATH ==="
codesign -dv --verbose=4 "$DMG_PATH" 2>&1 | grep -i "signature\|authority"
echo "DMG is signed (contains notarized .app)"
fi
- name: Verify code signing (Windows)
if: contains(matrix.platform, 'windows') && github.event.inputs.sign-build == 'true'
shell: pwsh
run: |
$msiPath = Get-ChildItem -Path "target/x86_64-pc-windows-msvc/release/bundle/msi" -Filter "*.msi" | Select-Object -First 1 -ExpandProperty FullName
if ($msiPath) {
Write-Host "Verifying MSI signature: $msiPath"
Get-AuthenticodeSignature -FilePath "$msiPath" | Format-List
}
# Upload artifacts
- name: Upload artifacts
if: github.event.inputs.upload-artifacts == 'true'
uses: actions/upload-artifact@v4
with:
name: meetily-devtest-${{ matrix.platform-name }}-${{ matrix.target }}-${{ steps.get-version.outputs.version }}
path: |
target/aarch64-apple-darwin/release/bundle/dmg/*.dmg
target/aarch64-apple-darwin/release/bundle/macos/*.app
target/aarch64-apple-darwin/release/bundle/macos/*.app.tar.gz
target/aarch64-apple-darwin/release/bundle/macos/*.app.tar.gz.sig
target/x86_64-pc-windows-msvc/release/bundle/msi/*.msi
target/x86_64-pc-windows-msvc/release/bundle/msi/*.msi.sig
target/x86_64-pc-windows-msvc/release/bundle/nsis/*.exe
target/x86_64-pc-windows-msvc/release/bundle/nsis/*.exe.sig
target/x86_64-unknown-linux-gnu/release/bundle/deb/*.deb
target/x86_64-unknown-linux-gnu/release/bundle/appimage/*.AppImage
target/x86_64-unknown-linux-gnu/release/bundle/rpm/*.rpm
retention-days: 14
# Generate summary
- name: Generate build summary
shell: bash
run: |
echo "## DevTest Build Summary - ${{ matrix.platform-name }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Version:** ${{ steps.get-version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "- **Platform:** ${{ matrix.platform-name }}" >> $GITHUB_STEP_SUMMARY
echo "- **Target:** ${{ matrix.target }}" >> $GITHUB_STEP_SUMMARY
echo "- **Signed:** ${{ github.event.inputs.sign-build == 'true' && 'Yes' || 'No (default)' }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
================================================
FILE: .github/workflows/build-linux.yml
================================================
name: "Build and Test - Linux"
on:
workflow_dispatch:
inputs:
build-type:
description: 'Build type'
required: true
type: choice
options:
- debug
- release
default: release
ubuntu-version:
description: 'Ubuntu version'
required: true
type: choice
options:
- ubuntu-22.04
- ubuntu-24.04
default: ubuntu-22.04
bundle-type:
description: 'Bundle type'
required: true
type: choice
options:
- deb
- appimage
- rpm
- all
default: all
sign-build:
description: 'Sign the build'
required: true
type: boolean
default: true
upload-artifacts:
description: 'Upload build artifacts'
required: true
type: boolean
default: true
# Cancel duplicate workflow runs
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
RUST_BACKTRACE: 1
CARGO_TERM_COLOR: always
WHISPER_NO_AVX: ON
WHISPER_NO_AVX2: ON
jobs:
build-linux:
name: Build Linux (x64)
runs-on: ${{ github.event.inputs.ubuntu-version || 'ubuntu-22.04' }}
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get version from tauri.conf.json
id: get-version
shell: bash
run: |
VERSION=$(grep -o '"version": "[^"]*"' frontend/src-tauri/tauri.conf.json | cut -d'"' -f4)
echo "Application version: $VERSION"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Determine build profile and bundle args
id: build-profile
shell: bash
run: |
# Determine build profile
if [[ "${{ github.event.inputs.build-type }}" == "debug" ]]; then
echo "profile=debug" >> "$GITHUB_OUTPUT"
echo "Build profile: debug"
else
echo "profile=release" >> "$GITHUB_OUTPUT"
echo "Build profile: release"
fi
# Determine bundle arguments
BUNDLE_TYPE="${{ github.event.inputs.bundle-type }}"
if [[ "$BUNDLE_TYPE" == "all" ]] || [[ -z "$BUNDLE_TYPE" ]]; then
# Default based on Ubuntu version
if [[ "${{ github.event.inputs.ubuntu-version }}" == "ubuntu-24.04" ]]; then
BUNDLES="appimage,rpm"
else
BUNDLES="deb"
fi
else
BUNDLES="$BUNDLE_TYPE"
fi
if [[ "${{ github.event.inputs.build-type }}" == "debug" ]]; then
echo "args=--debug --bundles $BUNDLES" >> "$GITHUB_OUTPUT"
else
echo "args=--bundles $BUNDLES" >> "$GITHUB_OUTPUT"
fi
echo "Bundle types: $BUNDLES"
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 8
run_install: false
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Setup pnpm cache
uses: actions/cache@v4
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
targets: x86_64-unknown-linux-gnu
- name: Rust cache
uses: swatinem/rust-cache@v2
with:
workspaces: ". -> target"
key: ${{ github.event.inputs.ubuntu-version || 'ubuntu-22.04' }}-x86_64-unknown-linux-gnu
- name: Install dependencies (Ubuntu 24.04)
if: github.event.inputs.ubuntu-version == 'ubuntu-24.04'
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 \
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 22.04)
if: github.event.inputs.ubuntu-version == 'ubuntu-22.04' || github.event.inputs.ubuntu-version == ''
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
- name: Prepare Vulkan SDK (Ubuntu 24.04)
if: github.event.inputs.ubuntu-version == 'ubuntu-24.04'
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 (Ubuntu 22.04)
if: github.event.inputs.ubuntu-version == 'ubuntu-22.04' || github.event.inputs.ubuntu-version == ''
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 FUSE for AppImage
run: |
sudo apt-get update
sudo apt-get install -y fuse libfuse2
- name: Install frontend dependencies
run: |
cd frontend
pnpm install
- name: Build llama-helper sidecar
run: |
echo "Building llama-helper sidecar (CPU mode)..."
cargo build --release -p llama-helper
# Copy binary to binaries directory
mkdir -p frontend/src-tauri/binaries
cp target/release/llama-helper frontend/src-tauri/binaries/llama-helper-x86_64-unknown-linux-gnu
echo "Copied llama-helper to frontend/src-tauri/binaries/"
ls -la frontend/src-tauri/binaries/
- name: Build Tauri app
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Tauri updater signing (ALWAYS enabled for .sig files)
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
# License validation RSA public key (embedded at build time)
MEETILY_RSA_PUBLIC_KEY: ${{ secrets.MEETILY_RSA_PUBLIC_KEY }}
# Supabase configuration (for online license verification)
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_ANON_KEY }}
with:
projectPath: frontend
args: --target x86_64-unknown-linux-gnu --features openblas ${{ steps.build-profile.outputs.args }}
- name: Verify build artifacts
run: |
echo "Build artifacts:"
find target/x86_64-unknown-linux-gnu/${{ steps.build-profile.outputs.profile }}/bundle -type f
- name: Remove libwayland-client.so from AppImage
if: contains(steps.build-profile.outputs.args, 'appimage')
run: |
# Find the AppImage file
APPIMAGE_PATH=$(find target/x86_64-unknown-linux-gnu/${{ 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
# Get appimagetool
wget -q https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
chmod +x appimagetool-x86_64.AppImage
# Repackage AppImage with no-appstream to avoid warnings
ARCH=x86_64 ./appimagetool-x86_64.AppImage --no-appstream squashfs-root "$APPIMAGE_NAME"
# Clean up
rm -rf squashfs-root appimagetool-x86_64.AppImage
echo "libwayland-client.so removed from AppImage successfully"
else
echo "No AppImage found to process"
fi
- name: Verify DEB package
if: contains(steps.build-profile.outputs.args, 'deb')
run: |
DEB_PATH=$(find target/x86_64-unknown-linux-gnu/${{ steps.build-profile.outputs.profile }}/bundle/deb -name "*.deb" | head -1)
if [ -n "$DEB_PATH" ]; then
echo "Verifying DEB package: $DEB_PATH"
dpkg-deb --info "$DEB_PATH"
dpkg-deb --contents "$DEB_PATH" | head -20
echo "DEB package is valid"
else
echo "No DEB package found to verify"
fi
- name: Verify RPM package
if: contains(steps.build-profile.outputs.args, 'rpm')
run: |
RPM_PATH=$(find target/x86_64-unknown-linux-gnu/${{ steps.build-profile.outputs.profile }}/bundle/rpm -name "*.rpm" | head -1)
if [ -n "$RPM_PATH" ]; then
echo "Verifying RPM package: $RPM_PATH"
sudo apt-get install -y rpm
rpm -qip "$RPM_PATH"
echo "RPM package is valid"
else
echo "No RPM package found to verify"
fi
- name: Verify AppImage
if: contains(steps.build-profile.outputs.args, 'appimage')
run: |
APPIMAGE_PATH=$(find target/x86_64-unknown-linux-gnu/${{ steps.build-profile.outputs.profile }}/bundle/appimage -name "*.AppImage" | head -1)
if [ -n "$APPIMAGE_PATH" ]; then
echo "Verifying AppImage: $APPIMAGE_PATH"
chmod +x "$APPIMAGE_PATH"
"$APPIMAGE_PATH" --appimage-help
echo "AppImage is valid"
else
echo "No AppImage found to verify"
fi
- name: Upload artifacts
if: ${{ github.event.inputs.upload-artifacts == 'true' }}
uses: actions/upload-artifact@v4
with:
name: meetily-linux-${{ github.event.inputs.ubuntu-version || 'ubuntu-22.04' }}-x64-${{ steps.build-profile.outputs.profile }}-${{ steps.get-version.outputs.version }}
path: |
target/x86_64-unknown-linux-gnu/${{ steps.build-profile.outputs.profile }}/bundle/deb/*.deb
target/x86_64-unknown-linux-gnu/${{ steps.build-profile.outputs.profile }}/bundle/appimage/*.AppImage
target/x86_64-unknown-linux-gnu/${{ steps.build-profile.outputs.profile }}/bundle/rpm/*.rpm
retention-days: 30
- name: Generate build summary
run: |
echo "## 🐧 Linux Build Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Property | Value |" >> $GITHUB_STEP_SUMMARY
echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| **Version** | ${{ steps.get-version.outputs.version }} |" >> $GITHUB_STEP_SUMMARY
echo "| **Build Profile** | ${{ steps.build-profile.outputs.profile }} |" >> $GITHUB_STEP_SUMMARY
echo "| **Target** | x86_64-unknown-linux-gnu |" >> $GITHUB_STEP_SUMMARY
echo "| **Ubuntu Version** | ${{ github.event.inputs.ubuntu-version || 'ubuntu-22.04' }} |" >> $GITHUB_STEP_SUMMARY
echo "| **Bundle Types** | ${{ steps.build-profile.outputs.args }} |" >> $GITHUB_STEP_SUMMARY
echo "| **Signed** | ${{ github.event.inputs.sign-build == 'true' && '✅ Yes' || '❌ No' }} |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 📦 Build Artifacts" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| File Type | Count |" >> $GITHUB_STEP_SUMMARY
echo "|-----------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| DEB Packages | $(find target/x86_64-unknown-linux-gnu/${{ steps.build-profile.outputs.profile }}/bundle/deb -name "*.deb" 2>/dev/null | wc -l) |" >> $GITHUB_STEP_SUMMARY
echo "| AppImages | $(find target/x86_64-unknown-linux-gnu/${{ steps.build-profile.outputs.profile }}/bundle/appimage -name "*.AppImage" 2>/dev/null | wc -l) |" >> $GITHUB_STEP_SUMMARY
echo "| RPM Packages | $(find target/x86_64-unknown-linux-gnu/${{ steps.build-profile.outputs.profile }}/bundle/rpm -name "*.rpm" 2>/dev/null | wc -l) |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "<details>" >> $GITHUB_STEP_SUMMARY
echo "<summary>📋 File List</summary>" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
find target/x86_64-unknown-linux-gnu/${{ steps.build-profile.outputs.profile }}/bundle -type f \( -name "*.deb" -o -name "*.AppImage" -o -name "*.rpm" \) 2>/dev/null >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "</details>" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
================================================
FILE: .github/workflows/build-macos.yml
================================================
name: "Build and Test - macOS"
on:
workflow_dispatch:
inputs:
build-type:
description: 'Build type'
required: true
type: choice
options:
- debug
- release
default: release
sign-build:
description: 'Sign the build'
required: true
type: boolean
default: true
upload-artifacts:
description: 'Upload build artifacts'
required: true
type: boolean
default: true
# Cancel duplicate workflow runs
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
RUST_BACKTRACE: 1
CARGO_TERM_COLOR: always
jobs:
build-macos:
name: Build macOS (Apple Silicon)
runs-on: macos-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get version from tauri.conf.json
id: get-version
shell: bash
run: |
VERSION=$(grep -o '"version": "[^"]*"' frontend/src-tauri/tauri.conf.json | cut -d'"' -f4)
echo "Application version: $VERSION"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Determine build profile
id: build-profile
shell: bash
run: |
if [[ "${{ github.event.inputs.build-type }}" == "debug" ]]; then
echo "profile=debug" >> "$GITHUB_OUTPUT"
echo "args=--debug" >> "$GITHUB_OUTPUT"
echo "Build profile: debug"
else
echo "profile=release" >> "$GITHUB_OUTPUT"
echo "args=" >> "$GITHUB_OUTPUT"
echo "Build profile: release"
fi
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 8
run_install: false
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Setup pnpm cache
uses: actions/cache@v4
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
targets: aarch64-apple-darwin
- name: Rust cache
uses: swatinem/rust-cache@v2
with:
workspaces: ". -> target"
key: macos-latest-aarch64-apple-darwin
- name: Install frontend dependencies
run: |
cd frontend
pnpm install
- name: Import Apple Developer Certificate
if: ${{ github.event.inputs.sign-build == 'true' }}
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: ${{ github.event.inputs.sign-build == 'true' }}
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: $CERT_ID"
- name: Configure build acceleration
run: |
echo "✓ macOS build will use Metal GPU acceleration (enabled by default)"
echo "✓ CoreML acceleration available for Apple Silicon"
- name: Build llama-helper sidecar
run: |
echo "Building llama-helper sidecar with Metal support..."
cargo build --release -p llama-helper --features metal
# Copy binary to binaries directory
mkdir -p frontend/src-tauri/binaries
cp target/release/llama-helper frontend/src-tauri/binaries/llama-helper-aarch64-apple-darwin
echo "Copied llama-helper to frontend/src-tauri/binaries/"
ls -la frontend/src-tauri/binaries/
- name: Cache FFmpeg binary
uses: actions/cache@v4
with:
path: frontend/src-tauri/binaries/ffmpeg-*
key: ${{ runner.os }}-ffmpeg-${{ hashFiles('frontend/src-tauri/build.rs', 'frontend/src-tauri/build/ffmpeg.rs') }}
- name: Build Tauri app (with code signing)
if: ${{ github.event.inputs.sign-build == 'true' }}
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# macOS code signing
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ env.CERT_ID }}
# Tauri updater signing (ALWAYS enabled for .sig files)
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
# License validation RSA public key (embedded at build time)
MEETILY_RSA_PUBLIC_KEY: ${{ secrets.MEETILY_RSA_PUBLIC_KEY }}
# Supabase configuration (for online license verification)
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_ANON_KEY }}
with:
projectPath: frontend
args: --target aarch64-apple-darwin ${{ steps.build-profile.outputs.args }}
- name: Build Tauri app (unsigned)
if: ${{ github.event.inputs.sign-build != 'true' }}
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Tauri updater signing (ALWAYS enabled for .sig files)
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
# License validation RSA public key (embedded at build time)
MEETILY_RSA_PUBLIC_KEY: ${{ secrets.MEETILY_RSA_PUBLIC_KEY }}
# Supabase configuration (for online license verification)
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_ANON_KEY }}
with:
projectPath: frontend
args: --target aarch64-apple-darwin ${{ steps.build-profile.outputs.args }}
- name: Verify build artifacts
run: |
echo "Build artifacts:"
find target/aarch64-apple-darwin/${{ steps.build-profile.outputs.profile }}/bundle -type f
- name: Verify code signing (App Bundle)
if: ${{ github.event.inputs.sign-build == 'true' }}
run: |
APP_PATH=$(find target/aarch64-apple-darwin/${{ steps.build-profile.outputs.profile }}/bundle/macos -name "*.app" | head -1)
if [ -n "$APP_PATH" ]; then
echo "=== Verifying App Bundle: $APP_PATH ==="
echo ""
echo "Code signature details:"
codesign -dv --verbose=4 "$APP_PATH" 2>&1 | grep -i "signature\|authority"
echo ""
echo "Verifying signature..."
codesign --verify --deep --strict "$APP_PATH" && echo "✓ Code signature is valid"
echo ""
echo "Checking notarization..."
spctl -a -vvv "$APP_PATH" && echo "✓ App is notarized and will be accepted by Gatekeeper"
else
echo "No App bundle found to verify"
fi
- name: Verify code signing (DMG)
if: ${{ github.event.inputs.sign-build == 'true' }}
run: |
DMG_PATH=$(find target/aarch64-apple-darwin/${{ steps.build-profile.outputs.profile }}/bundle/dmg -name "*.dmg" | head -1)
if [ -n "$DMG_PATH" ]; then
echo "=== Verifying DMG: $DMG_PATH ==="
echo ""
echo "Code signature details:"
codesign -dv --verbose=4 "$DMG_PATH" 2>&1 | grep -i "signature\|authority"
echo ""
echo "Verifying signature..."
codesign --verify --deep --strict "$DMG_PATH" && echo "✓ DMG signature is valid"
echo ""
echo "Note: DMG contains notarized .app bundle but DMG itself may not be notarized"
echo "This is acceptable - users will mount the DMG and run the notarized .app inside"
else
echo "No DMG found to verify"
fi
- name: Upload artifacts
if: ${{ github.event.inputs.upload-artifacts == 'true' }}
uses: actions/upload-artifact@v4
with:
name: meetily-macos-aarch64-${{ steps.build-profile.outputs.profile }}-${{ steps.get-version.outputs.version }}
path: |
target/aarch64-apple-darwin/${{ steps.build-profile.outputs.profile }}/bundle/dmg/*.dmg
target/aarch64-apple-darwin/${{ steps.build-profile.outputs.profile }}/bundle/macos/*.app
target/aarch64-apple-darwin/${{ steps.build-profile.outputs.profile }}/bundle/macos/*.app.tar.gz
target/aarch64-apple-darwin/${{ steps.build-profile.outputs.profile }}/bundle/macos/*.app.tar.gz.sig
retention-days: 30
- name: Generate build summary
run: |
echo "## 🍎 macOS Build Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Property | Value |" >> $GITHUB_STEP_SUMMARY
echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| **Version** | ${{ steps.get-version.outputs.version }} |" >> $GITHUB_STEP_SUMMARY
echo "| **Build Profile** | ${{ steps.build-profile.outputs.profile }} |" >> $GITHUB_STEP_SUMMARY
echo "| **Target** | aarch64-apple-darwin (Apple Silicon) |" >> $GITHUB_STEP_SUMMARY
echo "| **Signed** | ${{ github.event.inputs.sign-build == 'true' && '✅ Yes' || '❌ No' }} |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 📦 Build Artifacts" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| File Type | Count |" >> $GITHUB_STEP_SUMMARY
echo "|-----------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| DMG Installers | $(find target/aarch64-apple-darwin/${{ steps.build-profile.outputs.profile }}/bundle/dmg -name "*.dmg" 2>/dev/null | wc -l) |" >> $GITHUB_STEP_SUMMARY
echo "| APP Bundles | $(find target/aarch64-apple-darwin/${{ steps.build-profile.outputs.profile }}/bundle/macos -name "*.app" -type d 2>/dev/null | wc -l) |" >> $GITHUB_STEP_SUMMARY
echo "| APP Archives | $(find target/aarch64-apple-darwin/${{ steps.build-profile.outputs.profile }}/bundle/macos -name "*.app.tar.gz" 2>/dev/null | wc -l) |" >> $GITHUB_STEP_SUMMARY
echo "| APP Signatures | $(find target/aarch64-apple-darwin/${{ steps.build-profile.outputs.profile }}/bundle/macos -name "*.app.tar.gz.sig" 2>/dev/null | wc -l) |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "<details>" >> $GITHUB_STEP_SUMMARY
echo "<summary>📋 File List</summary>" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
find target/aarch64-apple-darwin/${{ steps.build-profile.outputs.profile }}/bundle -type f \( -name "*.dmg" -o -name "*.tar.gz" -o -name "*.sig" \) 2>/dev/null >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "</details>" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
================================================
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-latest" # for Apple Silicon only (M1 and above)
args: "--target aarch64-apple-darwin"
target: "aarch64-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: "windows-latest"
args: ""
target: "x86_64-pc-windows-msvc"
uses: ./.github/workflows/build.yml
with:
platform: ${{ matrix.platform }}
target: ${{ matrix.target }}
build-args: ${{ matrix.args }}
sign-binaries: true
asset-prefix: "meetily-test"
upload-artifacts: true
is-debug-build: ${{ contains(matrix.args, '--debug') }}
secrets: inherit
================================================
FILE: .github/workflows/build-windows.yml
================================================
name: "Build and Test - Windows"
on:
workflow_dispatch:
inputs:
build-type:
description: 'Build type'
required: true
type: choice
options:
- debug
- release
default: release
sign-build:
description: 'Sign the build'
required: true
type: boolean
default: true
test-signing:
description: 'Run pre-build signing test (optional)'
required: false
type: boolean
default: false
upload-artifacts:
description: 'Upload build artifacts'
required: true
type: boolean
default: true
# Cancel duplicate workflow runs
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
RUST_BACKTRACE: 1
CARGO_TERM_COLOR: always
jobs:
build-windows:
name: Build Windows (x64)
runs-on: windows-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get version from tauri.conf.json
id: get-version
shell: bash
run: |
VERSION=$(grep -o '"version": "[^"]*"' frontend/src-tauri/tauri.conf.json | cut -d'"' -f4)
echo "Application version: $VERSION"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Determine build profile
id: build-profile
shell: bash
run: |
if [[ "${{ github.event.inputs.build-type }}" == "debug" ]]; then
echo "profile=debug" >> "$GITHUB_OUTPUT"
echo "args=--debug" >> "$GITHUB_OUTPUT"
echo "Build profile: debug"
else
echo "profile=release" >> "$GITHUB_OUTPUT"
echo "args=" >> "$GITHUB_OUTPUT"
echo "Build profile: release"
fi
- name: Setup DigiCert Environment
if: ${{ github.event.inputs.sign-build == 'true' }}
shell: pwsh
run: |
# Set environment variables
"SM_HOST=${{ secrets.SM_HOST }}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"SM_API_KEY=${{ secrets.SM_API_KEY }}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"SM_CLIENT_CERT_PASSWORD=${{ secrets.SM_CLIENT_CERT_PASSWORD }}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"SM_CODE_SIGNING_CERT_SHA1_HASH=${{ secrets.SM_CODE_SIGNING_CERT_SHA1_HASH }}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
# Decode and save client certificate using PowerShell
$certPath = "D:\Certificate_pkcs12.p12"
$base64String = "${{ secrets.SM_CLIENT_CERT_FILE_B64 }}"
$certBytes = [System.Convert]::FromBase64String($base64String)
[System.IO.File]::WriteAllBytes($certPath, $certBytes)
# Verify the certificate file was created and has content
if (Test-Path $certPath) {
$certFile = Get-Item $certPath
Write-Host "Certificate file created: $certPath"
Write-Host " Size: $($certFile.Length) bytes"
Write-Host " Last Modified: $($certFile.LastWriteTime)"
# Verify it's a valid PKCS12 file by checking if we can load it
try {
$testCert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($certPath, "${{ secrets.SM_CLIENT_CERT_PASSWORD }}")
Write-Host " ✓ Certificate file is valid PKCS12 format"
Write-Host " ✓ Password is correct"
Write-Host " Certificate Subject: $($testCert.Subject)"
Write-Host " Certificate Thumbprint: $($testCert.Thumbprint)"
} catch {
Write-Warning "⚠ Certificate validation failed"
Write-Warning "Error: $($_.Exception.Message)"
Write-Warning "This may cause issues with signing, but we'll continue..."
}
} else {
Write-Error "Certificate file was not created at $certPath"
exit 1
}
# Set the environment variable with Windows-style path
"SM_CLIENT_CERT_FILE=D:\Certificate_pkcs12.p12" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
- name: Setup DigiCert KeyLocker
if: ${{ github.event.inputs.sign-build == 'true' }}
uses: digicert/ssm-code-signing@v1.1.1
- name: Sync Certificate to Windows Certificate Store
if: ${{ github.event.inputs.sign-build == 'true' }}
shell: pwsh
run: |
Write-Host "============================================================"
Write-Host "=== SYNCING CERTIFICATE TO WINDOWS CERTIFICATE STORE ==="
Write-Host "============================================================"
Write-Host "This step syncs the DigiCert certificate from KeyLocker HSM"
Write-Host "to the Windows Certificate Store (required for signing)"
Write-Host ""
# First, get the keypair alias from smctl keypair ls
Write-Host "Retrieving keypair alias from DigiCert KeyLocker..."
$keypairOutput = smctl keypair ls 2>&1 | Out-String
Write-Host $keypairOutput
# Extract the alias (looking for pattern like "key_XXXXXXXXXX")
$aliasMatch = [regex]::Match($keypairOutput, 'key_\d+')
if ($aliasMatch.Success) {
$keypairAlias = $aliasMatch.Value
Write-Host "✓ Found keypair alias: $keypairAlias"
} else {
Write-Error "✗ Could not find keypair alias in smctl output"
Write-Error "Output was: $keypairOutput"
exit 1
}
Write-Host ""
Write-Host "Syncing certificate using alias: $keypairAlias"
$certsyncOutput = smctl windows certsync --keypair-alias $keypairAlias 2>&1
Write-Host $certsyncOutput
if ($LASTEXITCODE -ne 0) {
Write-Host ""
Write-Error "✗ Certificate sync FAILED"
Write-Error "Exit code: $LASTEXITCODE"
Write-Error "Output: $certsyncOutput"
Write-Error ""
Write-Error "Possible causes:"
Write-Error " 1. Keypair alias '$keypairAlias' not found in KeyLocker"
Write-Error " 2. Certificate is revoked or expired"
Write-Error " 3. API credentials are invalid"
exit 1
}
Write-Host ""
Write-Host "✓ Certificate synced successfully"
Write-Host ""
# Verify certificate is now in Windows store
Write-Host "Verifying certificate in Windows Certificate Store..."
$cert = Get-ChildItem -Path Cert:\CurrentUser\My | Where-Object { $_.Thumbprint -eq $env:SM_CODE_SIGNING_CERT_SHA1_HASH } | Select-Object -First 1
if (-not $cert) {
# Try LocalMachine store
$cert = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object { $_.Thumbprint -eq $env:SM_CODE_SIGNING_CERT_SHA1_HASH } | Select-Object -First 1
}
if ($cert) {
Write-Host "✓ Certificate found in Windows Certificate Store"
Write-Host " Subject: $($cert.Subject)"
Write-Host " Issuer: $($cert.Issuer)"
Write-Host " Thumbprint: $($cert.Thumbprint)"
Write-Host " Valid From: $($cert.NotBefore)"
Write-Host " Valid Until: $($cert.NotAfter)"
} else {
Write-Warning "Certificate not found in Windows Certificate Store after sync"
Write-Warning "Signing may fail. Continuing anyway..."
}
# Export keypair alias for Tauri signCommand
Write-Host ""
Write-Host "Exporting DIGICERT_KEYPAIR_ALIAS for Tauri signCommand..."
"DIGICERT_KEYPAIR_ALIAS=$keypairAlias" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
Write-Host "✓ DIGICERT_KEYPAIR_ALIAS=$keypairAlias"
Write-Host ""
Write-Host "============================================================"
Write-Host ""
- name: Verify DigiCert Setup
if: ${{ github.event.inputs.sign-build == 'true' }}
shell: pwsh
run: |
Write-Host "=== DigiCert Setup Verification ==="
Write-Host ""
# Verify SMCTL is available
Write-Host "SMCTL Version:"
smctl --version
Write-Host ""
# Check environment variables
Write-Host "Environment Variables:"
Write-Host " SM_HOST: $env:SM_HOST"
Write-Host " SM_API_KEY: $($env:SM_API_KEY.Substring(0, [Math]::Min(20, $env:SM_API_KEY.Length)))..."
Write-Host " SM_CLIENT_CERT_FILE: $env:SM_CLIENT_CERT_FILE"
Write-Host " SM_CLIENT_CERT_PASSWORD: $(if ($env:SM_CLIENT_CERT_PASSWORD) { '***SET***' } else { 'NOT SET' })"
Write-Host ""
# Verify client certificate file exists and check size
if (Test-Path $env:SM_CLIENT_CERT_FILE) {
$certFile = Get-Item $env:SM_CLIENT_CERT_FILE
Write-Host "Client Certificate File:"
Write-Host " Path: $($certFile.FullName)"
Write-Host " Size: $($certFile.Length) bytes"
Write-Host " Last Modified: $($certFile.LastWriteTime)"
} else {
Write-Error "Client certificate file NOT FOUND: $env:SM_CLIENT_CERT_FILE"
}
Write-Host ""
# List available keypairs (verifies API authentication works)
Write-Host "Available Keypairs:"
smctl keypair ls
Write-Host ""
# Verify certificate (healthcheck)
Write-Host "DigiCert Healthcheck:"
smctl healthcheck
Write-Host ""
# Note: Healthcheck may show cert path/password warning but if keypair ls works,
# the signing should still work. We'll test in the pre-build signing test.
- name: Pre-Build Signing Test
if: ${{ github.event.inputs.sign-build == 'true' && github.event.inputs.test-signing == 'true' }}
shell: pwsh
run: |
Write-Host "============================================================"
Write-Host "=== PRE-BUILD SIGNING TEST ==="
Write-Host "============================================================"
Write-Host "Testing signing before Tauri build to fail fast if signing is broken"
Write-Host ""
Write-Host "Environment Configuration:"
Write-Host " SM_HOST: $($env:SM_HOST ?? 'NOT SET')"
Write-Host " SM_API_KEY: $(if ($env:SM_API_KEY) { $env:SM_API_KEY.Substring(0, [Math]::Min(20, $env:SM_API_KEY.Length)) + '...' } else { 'NOT SET' })"
Write-Host " SM_CLIENT_CERT_FILE: $($env:SM_CLIENT_CERT_FILE ?? 'NOT SET')"
Write-Host " SM_CODE_SIGNING_CERT_SHA1_HASH: $($env:SM_CODE_SIGNING_CERT_SHA1_HASH ?? 'NOT SET')"
# Verify environment is set up
if (-not $env:SM_HOST -or -not $env:SM_API_KEY) {
Write-Error "DigiCert environment not configured. Make sure 'Sign the build' is enabled."
exit 1
}
Write-Host ""
# Verify client certificate exists
if (Test-Path $env:SM_CLIENT_CERT_FILE) {
Write-Host "✓ Client certificate file exists"
$certInfo = Get-Item $env:SM_CLIENT_CERT_FILE
Write-Host " File size: $($certInfo.Length) bytes"
} else {
Write-Error "✗ Client certificate file not found: $env:SM_CLIENT_CERT_FILE"
exit 1
}
Write-Host ""
Write-Host "--- Available Keypairs in DigiCert KeyLocker ---"
$keypairOutput = smctl keypair ls 2>&1
Write-Host $keypairOutput
Write-Host ""
Write-Host "Note: Certificate was already synced to Windows Certificate Store in previous step"
Write-Host ""
# Create a minimal test executable using dotnet
Write-Host "--- Creating Test Executable ---"
Write-Host "Creating minimal C# console app..."
# Create simple C# file
$testCode = @"
using System;
class Program {
static void Main() {
Console.WriteLine("Test signing executable");
}
}
"@
Set-Content -Path "TestSigning.cs" -Value $testCode
# Create minimal project file
$projectFile = @"
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
</Project>
"@
Set-Content -Path "TestSigning.csproj" -Value $projectFile
# Build executable in one command
Write-Host "Building with dotnet publish..."
dotnet publish TestSigning.csproj -c Release -o . --self-contained false 2>&1 | Out-Null
if ($LASTEXITCODE -ne 0) {
Write-Error "Failed to build test executable"
Write-Host "Attempting to list dotnet info for debugging:"
dotnet --version
exit 1
}
if (-not (Test-Path "TestSigning.exe")) {
Write-Error "TestSigning.exe was not created"
Get-ChildItem -Filter "*.exe" | Format-Table
exit 1
}
$exeInfo = Get-Item "TestSigning.exe"
Write-Host "✓ Test executable compiled successfully"
Write-Host " File: $($exeInfo.FullName)"
Write-Host " Size: $($exeInfo.Length) bytes"
# Verify unsigned state
Write-Host ""
Write-Host "--- Checking Unsigned State ---"
$unsignedSig = Get-AuthenticodeSignature -FilePath "TestSigning.exe"
Write-Host "Pre-signing status: $($unsignedSig.Status) (expected: NotSigned)"
# Sign the test executable with signtool (certificate already in Windows store)
Write-Host ""
Write-Host "--- Signing Test Executable with SignTool ---"
Write-Host "Certificate is already synced to Windows Certificate Store"
Write-Host "Detecting certificate store location..."
Write-Host ""
# Extract keypair alias (needed for /k flag with /csp)
$aliasMatch = [regex]::Match($keypairOutput, 'key_\d+')
if ($aliasMatch.Success) {
$keypairAlias = $aliasMatch.Value
Write-Host "✓ Found keypair alias: $keypairAlias"
} else {
Write-Error "✗ Could not find keypair alias in smctl output"
exit 1
}
Write-Host ""
# Detect which store the certificate is in
$certCurrentUser = Get-ChildItem -Path Cert:\CurrentUser\My -ErrorAction SilentlyContinue | Where-Object { $_.Thumbprint -eq $env:SM_CODE_SIGNING_CERT_SHA1_HASH } | Select-Object -First 1
$certLocalMachine = Get-ChildItem -Path Cert:\LocalMachine\My -ErrorAction SilentlyContinue | Where-Object { $_.Thumbprint -eq $env:SM_CODE_SIGNING_CERT_SHA1_HASH } | Select-Object -First 1
$storeFlag = ""
$storeName = ""
if ($certCurrentUser) {
Write-Host "✓ Certificate found in CurrentUser\My store"
$storeFlag = "/s My"
$storeName = "CurrentUser\My"
} elseif ($certLocalMachine) {
Write-Host "✓ Certificate found in LocalMachine\My store"
$storeFlag = "/sm /s My"
$storeName = "LocalMachine\My"
} else {
Write-Error "✗ Certificate not found in CurrentUser\My or LocalMachine\My stores"
Write-Error "SHA1 hash: $env:SM_CODE_SIGNING_CERT_SHA1_HASH"
exit 1
}
Write-Host "Using store: $storeName"
Write-Host "SHA1 hash: $env:SM_CODE_SIGNING_CERT_SHA1_HASH"
Write-Host "Keypair alias: $keypairAlias"
Write-Host ""
# Test using the exact same command as tauri.conf.json signCommand
Write-Host "Testing sign-windows.ps1 script (same as Tauri signCommand)..."
Write-Host "Command: powershell -ExecutionPolicy Bypass -File frontend/src-tauri/scripts/sign-windows.ps1 -FilePath TestSigning.exe"
Write-Host ""
# Set DIGICERT_KEYPAIR_ALIAS for the script (already exported to GITHUB_ENV, but set locally too)
$env:DIGICERT_KEYPAIR_ALIAS = $keypairAlias
$signOutput = powershell -ExecutionPolicy Bypass -File "frontend/src-tauri/scripts/sign-windows.ps1" -FilePath "TestSigning.exe" 2>&1
Write-Host $signOutput
if ($LASTEXITCODE -ne 0) {
Write-Host ""
Write-Host "============================================================"
Write-Error "✗✗✗ SIGNING FAILED ✗✗✗"
Write-Host "============================================================"
Write-Error "Exit code: $LASTEXITCODE"
Write-Error "Script output: $signOutput"
Write-Error ""
Write-Error "Certificate was found in: $storeName"
Write-Error "DIGICERT_KEYPAIR_ALIAS: $keypairAlias"
Write-Error ""
exit 1
}
Write-Host ""
Write-Host "✓ Test executable signed successfully using sign-windows.ps1"
# Verify the signature
Write-Host ""
Write-Host "--- Verifying Signature ---"
$signature = Get-AuthenticodeSignature -FilePath "TestSigning.exe"
Write-Host "Signature Details:"
Write-Host " Status: $($signature.Status)"
Write-Host " Status Message: $($signature.StatusMessage)"
Write-Host " Signer Certificate:"
Write-Host " Subject: $($signature.SignerCertificate.Subject)"
Write-Host " Issuer: $($signature.SignerCertificate.Issuer)"
Write-Host " Thumbprint: $($signature.SignerCertificate.Thumbprint)"
Write-Host " Valid From: $($signature.SignerCertificate.NotBefore)"
Write-Host " Valid To: $($signature.SignerCertificate.NotAfter)"
if ($signature.TimeStamperCertificate) {
Write-Host " Timestamp Certificate:"
Write-Host " Subject: $($signature.TimeStamperCertificate.Subject)"
Write-Host " Issuer: $($signature.TimeStamperCertificate.Issuer)"
}
# Validate signature
if ($signature.Status -ne 'Valid') {
Write-Host ""
Write-Host "============================================================"
Write-Error "✗✗✗ SIGNATURE VERIFICATION FAILED ✗✗✗"
Write-Host "============================================================"
Write-Error "Expected Status: Valid"
Write-Error "Actual Status: $($signature.Status)"
Write-Error "Status Message: $($signature.StatusMessage)"
exit 1
}
if (-not $signature.TimeStamperCertificate) {
Write-Host ""
Write-Host "============================================================"
Write-Error "✗✗✗ MISSING TIMESTAMP ✗✗✗"
Write-Host "============================================================"
Write-Error "Timestamp certificate is missing!"
Write-Error "Without timestamp, signature will become invalid when cert expires"
exit 1
}
Write-Host ""
Write-Host "✓ Signature verified successfully"
Write-Host "✓ Timestamp present"
Write-Host ""
Write-Host "--- Cleanup ---"
Remove-Item "TestSigning.exe" -ErrorAction SilentlyContinue
Remove-Item "TestSigning.cs" -ErrorAction SilentlyContinue
Remove-Item "TestSigning.csproj" -ErrorAction SilentlyContinue
Remove-Item "TestSigning.dll" -ErrorAction SilentlyContinue
Remove-Item "TestSigning.pdb" -ErrorAction SilentlyContinue
Remove-Item "TestSigning.deps.json" -ErrorAction SilentlyContinue
Remove-Item "TestSigning.runtimeconfig.json" -ErrorAction SilentlyContinue
Write-Host "✓ Test files cleaned up"
Write-Host ""
Write-Host "============================================================"
Write-Host "=== PRE-BUILD SIGNING TEST PASSED ==="
Write-Host "============================================================"
Write-Host "Signing infrastructure is working correctly"
Write-Host "Proceeding with Tauri build..."
Write-Host ""
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 8
run_install: false
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Setup pnpm cache
uses: actions/cache@v4
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
targets: x86_64-pc-windows-msvc
- name: Rust cache
uses: swatinem/rust-cache@v2
with:
workspaces: ". -> target"
key: windows-x64-vulkan-v2
- name: Install Vulkan SDK
uses: humbletim/install-vulkan-sdk@v1.2
with:
version: 1.4.309.0
cache: true
- name: Verify and Configure Vulkan SDK
shell: pwsh
run: |
Write-Host "=== Vulkan SDK Configuration ==="
Write-Host ""
# Get VULKAN_SDK from environment
$vulkanSdk = $env:VULKAN_SDK
Write-Host "VULKAN_SDK: $vulkanSdk"
if (-not $vulkanSdk -or -not (Test-Path $vulkanSdk)) {
Write-Error "VULKAN_SDK not set or path does not exist"
exit 1
}
# Verify critical directories exist
$binDir = Join-Path $vulkanSdk "Bin"
$libDir = Join-Path $vulkanSdk "Lib"
$includeDir = Join-Path $vulkanSdk "Include"
Write-Host ""
Write-Host "Directory structure:"
Write-Host " Bin: $(if (Test-Path $binDir) { 'EXISTS' } else { 'MISSING' })"
Write-Host " Lib: $(if (Test-Path $libDir) { 'EXISTS' } else { 'MISSING' })"
Write-Host " Include: $(if (Test-Path $includeDir) { 'EXISTS' } else { 'MISSING' })"
# Check for glslc and glslangValidator
$glslc = Join-Path $binDir "glslc.exe"
$glslangValidator = Join-Path $binDir "glslangValidator.exe"
Write-Host ""
Write-Host "Shader compilers:"
Write-Host " glslc: $(if (Test-Path $glslc) { 'FOUND' } else { 'MISSING' })"
Write-Host " glslangValidator: $(if (Test-Path $glslangValidator) { 'FOUND' } else { 'MISSING' })"
# Check for vulkan-1.lib (required for linking)
$vulkanLib = Join-Path $libDir "vulkan-1.lib"
Write-Host ""
Write-Host "Libraries:"
Write-Host " vulkan-1.lib: $(if (Test-Path $vulkanLib) { 'FOUND' } else { 'MISSING' })"
# Ensure Bin is in PATH for shader compilers
$currentPath = $env:PATH
if (-not $currentPath.Contains($binDir)) {
Write-Host ""
Write-Host "Adding Vulkan SDK Bin to PATH..."
"PATH=$binDir;$currentPath" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
}
# Set additional environment variables for CMake
Write-Host ""
Write-Host "Setting CMake-related environment variables..."
"Vulkan_LIBRARY=$libDir\vulkan-1.lib" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"Vulkan_INCLUDE_DIR=$includeDir" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"VK_SDK_PATH=$vulkanSdk" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
# List SDK contents for debugging
Write-Host ""
Write-Host "Vulkan SDK Bin contents:"
Get-ChildItem $binDir -ErrorAction SilentlyContinue | Where-Object { $_.Name -like "*.exe" } | Select-Object -First 10 | ForEach-Object { Write-Host " $($_.Name)" }
Write-Host ""
Write-Host "Vulkan SDK Lib contents:"
Get-ChildItem $libDir -ErrorAction SilentlyContinue | Where-Object { $_.Name -like "*.lib" } | Select-Object -First 10 | ForEach-Object { Write-Host " $($_.Name)" }
Write-Host ""
Write-Host "=== Vulkan SDK Configuration Complete ==="
- name: Copy Vulkan runtime for bundling
shell: pwsh
run: |
# Create runtime directory
$runtimeDir = "frontend/src-tauri/vulkan-runtime"
if (!(Test-Path $runtimeDir)) {
New-Item -ItemType Directory -Force -Path $runtimeDir | Out-Null
}
# Find Vulkan SDK - check multiple possible locations
$vulkanSdk = $env:VULKAN_SDK
Write-Host "VULKAN_SDK env: $vulkanSdk"
# Common installation paths
$possiblePaths = @(
"$vulkanSdk\Bin\vulkan-1.dll",
"$vulkanSdk\runtime\x64\vulkan-1.dll",
"C:\VulkanSDK\1.4.309.0\Bin\vulkan-1.dll",
"C:\VulkanSDK\1.4.309.0\runtime\x64\vulkan-1.dll",
"$env:ProgramFiles\VulkanSDK\Bin\vulkan-1.dll",
"$env:SystemRoot\System32\vulkan-1.dll"
)
$vulkanDll = $null
foreach ($path in $possiblePaths) {
Write-Host "Checking: $path"
if (Test-Path $path) {
$vulkanDll = $path
Write-Host "Found vulkan-1.dll at: $path"
break
}
}
if ($vulkanDll) {
Copy-Item $vulkanDll -Destination $runtimeDir
Write-Host "Copied vulkan-1.dll to $runtimeDir"
} else {
Write-Host "Searching for vulkan-1.dll on system..."
$found = Get-ChildItem -Path "C:\" -Filter "vulkan-1.dll" -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1
if ($found) {
Write-Host "Found at: $($found.FullName)"
Copy-Item $found.FullName -Destination $runtimeDir
} else {
Write-Warning "vulkan-1.dll not found - Vulkan apps may not work without it"
Write-Host "System will use the user's installed Vulkan runtime"
}
}
# Verify
Write-Host "Contents of vulkan-runtime directory:"
Get-ChildItem $runtimeDir -ErrorAction SilentlyContinue
- name: Install frontend dependencies
run: |
cd frontend
pnpm install
- name: Build llama-helper sidecar
shell: pwsh
run: |
# Build llama-helper without GPU features (CPU-only)
# Note: Vulkan build has CMake race condition issues with llama-cpp-sys-2
# The Tauri app itself uses whisper-rs with Vulkan for transcription
# llama-helper is for LLM inference and works fine on CPU
Write-Host "Building llama-helper sidecar (CPU-only)..."
cargo build --release -p llama-helper
if ($LASTEXITCODE -ne 0) {
Write-Error "Failed to build llama-helper"
exit $LASTEXITCODE
}
# Copy binary to binaries directory
New-Item -ItemType Directory -Force -Path "frontend/src-tauri/binaries" | Out-Null
Copy-Item "target/release/llama-helper.exe" -Destination "frontend/src-tauri/binaries/llama-helper-x86_64-pc-windows-msvc.exe"
Write-Host "Copied llama-helper to frontend/src-tauri/binaries/"
Get-ChildItem "frontend/src-tauri/binaries/"
- name: Cache FFmpeg binary
uses: actions/cache@v4
with:
path: frontend/src-tauri/binaries/ffmpeg-*.exe
key: ${{ runner.os }}-ffmpeg-${{ hashFiles('frontend/src-tauri/build.rs', 'frontend/src-tauri/build/ffmpeg.rs') }}
- name: Build Tauri app
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Tauri updater signing (ALWAYS enabled for .sig files)
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
# License validation RSA public key (embedded at build time)
MEETILY_RSA_PUBLIC_KEY: ${{ secrets.MEETILY_RSA_PUBLIC_KEY }}
# Supabase configuration (for online license verification)
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_ANON_KEY }}
with:
projectPath: frontend
args: --target x86_64-pc-windows-msvc --features vulkan ${{ steps.build-profile.outputs.args }}
- name: Verify build artifacts
shell: bash
run: |
echo "## Build Artifacts"
find target/x86_64-pc-windows-msvc/${{ steps.build-profile.outputs.profile }}/bundle -type f \( -name "*.msi" -o -name "*.exe" \) -exec ls -lh {} \;
- name: Upload artifacts
if: ${{ github.event.inputs.upload-artifacts == 'true' }}
uses: actions/upload-artifact@v4
with:
name: meetily-windows-x64-${{ steps.build-profile.outputs.profile }}-${{ steps.get-version.outputs.version }}
path: |
target/x86_64-pc-windows-msvc/${{ steps.build-profile.outputs.profile }}/bundle/msi/*.msi
target/x86_64-pc-windows-msvc/${{ steps.build-profile.outputs.profile }}/bundle/msi/*.msi.sig
target/x86_64-pc-windows-msvc/${{ steps.build-profile.outputs.profile }}/bundle/nsis/*.exe
target/x86_64-pc-windows-msvc/${{ steps.build-profile.outputs.profile }}/bundle/nsis/*.exe.sig
retention-days: 30
- name: Generate build summary
shell: bash
run: |
echo "## 🪟 Windows Build Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Property | Value |" >> $GITHUB_STEP_SUMMARY
echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| **Version** | ${{ steps.get-version.outputs.version }} |" >> $GITHUB_STEP_SUMMARY
echo "| **Build Profile** | ${{ steps.build-profile.outputs.profile }} |" >> $GITHUB_STEP_SUMMARY
echo "| **Target** | x86_64-pc-windows-msvc |" >> $GITHUB_STEP_SUMMARY
echo "| **Signed** | ${{ (github.event.inputs.sign-build == 'true') && '✅ Yes' || '❌ No' }} |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 📦 Build Artifacts" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| File Type | Count |" >> $GITHUB_STEP_SUMMARY
echo "|-----------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| MSI Installers | $(find target/x86_64-pc-windows-msvc/${{ steps.build-profile.outputs.profile }}/bundle/msi -name "*.msi" 2>/dev/null | wc -l) |" >> $GITHUB_STEP_SUMMARY
echo "| MSI Signatures | $(find target/x86_64-pc-windows-msvc/${{ steps.build-profile.outputs.profile }}/bundle/msi -name "*.msi.sig" 2>/dev/null | wc -l) |" >> $GITHUB_STEP_SUMMARY
echo "| NSIS Installers | $(find target/x86_64-pc-windows-msvc/${{ steps.build-profile.outputs.profile }}/bundle/nsis -name "*.exe" 2>/dev/null | wc -l) |" >> $GITHUB_STEP_SUMMARY
echo "| NSIS Signatures | $(find target/x86_64-pc-windows-msvc/${{ steps.build-profile.outputs.profile }}/bundle/nsis -name "*.exe.sig" 2>/dev/null | wc -l) |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "<details>" >> $GITHUB_STEP_SUMMARY
echo "<summary>📋 File List</summary>" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
find target/x86_64-pc-windows-msvc/${{ steps.build-profile.outputs.profile }}/bundle -type f \( -name "*.msi" -o -name "*.exe" -o -name "*.sig" \) 2>/dev/null >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "</details>" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
================================================
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: "meetily"
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
jobs:
build:
permissions:
contents: write
runs-on: ${{ inputs.platform }}
steps:
- 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": "[^"]*"' frontend/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 pnpm
uses: pnpm/action-setup@v4
with:
version: 8
run_install: false
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Setup pnpm cache
uses: actions/cache@v4
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
# Only aarch64-apple-darwin for Apple Silicon (no Intel support)
targets: ${{ contains(inputs.platform, 'macos') && 'aarch64-apple-darwin' || '' }}
- name: Rust cache
uses: swatinem/rust-cache@v2
with:
workspaces: ". -> target"
key: ${{ inputs.platform }}-${{ inputs.target }}-vulkan-v2
- name: install dependencies (ubuntu 24.04)
if: contains(inputs.platform, 'ubuntu-24.04')
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 \
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 22.04)
if: contains(inputs.platform, 'ubuntu-22.04')
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
- name: Install Vulkan SDK (Windows)
if: contains(inputs.platform, 'windows')
uses: humbletim/install-vulkan-sdk@v1.2
with:
version: 1.4.309.0
cache: true
- name: Verify and Configure Vulkan SDK (Windows)
if: contains(inputs.platform, 'windows')
shell: pwsh
run: |
Write-Host "=== Vulkan SDK Configuration ==="
$vulkanSdk = $env:VULKAN_SDK
Write-Host "VULKAN_SDK: $vulkanSdk"
if (-not $vulkanSdk -or -not (Test-Path $vulkanSdk)) {
Write-Error "VULKAN_SDK not set or path does not exist"
exit 1
}
# Verify critical directories exist
$binDir = Join-Path $vulkanSdk "Bin"
$libDir = Join-Path $vulkanSdk "Lib"
$includeDir = Join-Path $vulkanSdk "Include"
Write-Host "Directory structure:"
Write-Host " Bin: $(if (Test-Path $binDir) { 'EXISTS' } else { 'MISSING' })"
Write-Host " Lib: $(if (Test-Path $libDir) { 'EXISTS' } else { 'MISSING' })"
Write-Host " Include: $(if (Test-Path $includeDir) { 'EXISTS' } else { 'MISSING' })"
# Check for glslc and glslangValidator
$glslc = Join-Path $binDir "glslc.exe"
$glslangValidator = Join-Path $binDir "glslangValidator.exe"
Write-Host "Shader compilers:"
Write-Host " glslc: $(if (Test-Path $glslc) { 'FOUND' } else { 'MISSING' })"
Write-Host " glslangValidator: $(if (Test-Path $glslangValidator) { 'FOUND' } else { 'MISSING' })"
# Ensure Bin is in PATH for shader compilers
$currentPath = $env:PATH
if (-not $currentPath.Contains($binDir)) {
Write-Host "Adding Vulkan SDK Bin to PATH..."
"PATH=$binDir;$currentPath" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
}
# Set additional environment variables for CMake
Write-Host "Setting CMake-related environment variables..."
"Vulkan_LIBRARY=$libDir\vulkan-1.lib" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"Vulkan_INCLUDE_DIR=$includeDir" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"VK_SDK_PATH=$vulkanSdk" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
Write-Host "=== Vulkan SDK Configuration Complete ==="
- name: Copy Vulkan runtime for bundling (Windows)
if: contains(inputs.platform, 'windows')
shell: pwsh
run: |
# Create runtime directory
$runtimeDir = "frontend/src-tauri/vulkan-runtime"
if (!(Test-Path $runtimeDir)) {
New-Item -ItemType Directory -Force -Path $runtimeDir | Out-Null
}
# Find Vulkan SDK - check multiple possible locations
$vulkanSdk = $env:VULKAN_SDK
Write-Host "VULKAN_SDK env: $vulkanSdk"
# Common installation paths
$possiblePaths = @(
"$vulkanSdk\Bin\vulkan-1.dll",
"$vulkanSdk\runtime\x64\vulkan-1.dll",
"C:\VulkanSDK\1.4.309.0\Bin\vulkan-1.dll",
"C:\VulkanSDK\1.4.309.0\runtime\x64\vulkan-1.dll",
"$env:ProgramFiles\VulkanSDK\Bin\vulkan-1.dll",
"$env:SystemRoot\System32\vulkan-1.dll"
)
$vulkanDll = $null
foreach ($path in $possiblePaths) {
Write-Host "Checking: $path"
if (Test-Path $path) {
$vulkanDll = $path
Write-Host "Found vulkan-1.dll at: $path"
break
}
}
if ($vulkanDll) {
Copy-Item $vulkanDll -Destination $runtimeDir
Write-Host "Copied vulkan-1.dll to $runtimeDir"
} else {
Write-Host "Searching for vulkan-1.dll on system..."
$found = Get-ChildItem -Path "C:\" -Filter "vulkan-1.dll" -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1
if ($found) {
Write-Host "Found at: $($found.FullName)"
Copy-Item $found.FullName -Destination $runtimeDir
} else {
Write-Warning "vulkan-1.dll not found - Vulkan apps may not work without it"
Write-Host "System will use the user's installed Vulkan runtime"
}
}
# Verify
Write-Host "Contents of vulkan-runtime directory:"
Get-ChildItem $runtimeDir -ErrorAction SilentlyContinue
- name: Setup DigiCert Environment
if: contains(inputs.platform, 'windows') && inputs.sign-binaries
shell: pwsh
run: |
# Set environment variables
"SM_HOST=${{ secrets.SM_HOST }}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"SM_API_KEY=${{ secrets.SM_API_KEY }}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"SM_CLIENT_CERT_PASSWORD=${{ secrets.SM_CLIENT_CERT_PASSWORD }}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"SM_CODE_SIGNING_CERT_SHA1_HASH=${{ secrets.SM_CODE_SIGNING_CERT_SHA1_HASH }}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
# Decode and save client certificate using PowerShell
$certPath = "D:\Certificate_pkcs12.p12"
$base64String = "${{ secrets.SM_CLIENT_CERT_FILE_B64 }}"
$certBytes = [System.Convert]::FromBase64String($base64String)
[System.IO.File]::WriteAllBytes($certPath, $certBytes)
# Verify the certificate file was created and has content
if (Test-Path $certPath) {
$certFile = Get-Item $certPath
Write-Host "Certificate file created: $certPath"
Write-Host " Size: $($certFile.Length) bytes"
Write-Host " Last Modified: $($certFile.LastWriteTime)"
# Verify it's a valid PKCS12 file by checking if we can load it
try {
$testCert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($certPath, "${{ secrets.SM_CLIENT_CERT_PASSWORD }}")
Write-Host " ✓ Certificate file is valid PKCS12 format"
Write-Host " ✓ Password is correct"
Write-Host " Certificate Subject: $($testCert.Subject)"
Write-Host " Certificate Thumbprint: $($testCert.Thumbprint)"
} catch {
Write-Warning "⚠ Certificate validation failed"
Write-Warning "Error: $($_.Exception.Message)"
Write-Warning "This may cause issues with signing, but we'll continue..."
}
} else {
Write-Error "Certificate file was not created at $certPath"
exit 1
}
# Set the environment variable with Windows-style path
"SM_CLIENT_CERT_FILE=D:\Certificate_pkcs12.p12" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
- name: Setup DigiCert KeyLocker
if: contains(inputs.platform, 'windows') && inputs.sign-binaries
uses: digicert/ssm-code-signing@v1.1.1
- name: Sync Certificate to Windows Certificate Store
if: contains(inputs.platform, 'windows') && inputs.sign-binaries
shell: pwsh
run: |
Write-Host "============================================================"
Write-Host "=== SYNCING CERTIFICATE TO WINDOWS CERTIFICATE STORE ==="
Write-Host "============================================================"
Write-Host "This step syncs the DigiCert certificate from KeyLocker HSM"
Write-Host "to the Windows Certificate Store (required for signing)"
Write-Host ""
# First, get the keypair alias from smctl keypair ls
Write-Host "Retrieving keypair alias from DigiCert KeyLocker..."
$keypairOutput = smctl keypair ls 2>&1 | Out-String
Write-Host $keypairOutput
# Extract the alias (looking for pattern like "key_XXXXXXXXXX")
$aliasMatch = [regex]::Match($keypairOutput, 'key_\d+')
if ($aliasMatch.Success) {
$keypairAlias = $aliasMatch.Value
Write-Host "✓ Found keypair alias: $keypairAlias"
} else {
Write-Error "✗ Could not find keypair alias in smctl output"
Write-Error "Output was: $keypairOutput"
exit 1
}
Write-Host ""
Write-Host "Syncing certificate using alias: $keypairAlias"
$certsyncOutput = smctl windows certsync --keypair-alias $keypairAlias 2>&1
Write-Host $certsyncOutput
if ($LASTEXITCODE -ne 0) {
Write-Host ""
Write-Error "✗ Certificate sync FAILED"
Write-Error "Exit code: $LASTEXITCODE"
Write-Error "Output: $certsyncOutput"
Write-Error ""
Write-Error "Possible causes:"
Write-Error " 1. Keypair alias '$keypairAlias' not found in KeyLocker"
Write-Error " 2. Certificate is revoked or expired"
Write-Error " 3. API credentials are invalid"
exit 1
}
Write-Host ""
Write-Host "✓ Certificate synced successfully"
Write-Host ""
# Verify certificate is now in Windows store
Write-Host "Verifying certificate in Windows Certificate Store..."
$cert = Get-ChildItem -Path Cert:\CurrentUser\My | Where-Object { $_.Thumbprint -eq $env:SM_CODE_SIGNING_CERT_SHA1_HASH } | Select-Object -First 1
if (-not $cert) {
# Try LocalMachine store
$cert = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object { $_.Thumbprint -eq $env:SM_CODE_SIGNING_CERT_SHA1_HASH } | Select-Object -First 1
}
if ($cert) {
Write-Host "✓ Certificate found in Windows Certificate Store"
Write-Host " Subject: $($cert.Subject)"
Write-Host " Issuer: $($cert.Issuer)"
Write-Host " Thumbprint: $($cert.Thumbprint)"
Write-Host " Valid From: $($cert.NotBefore)"
Write-Host " Valid Until: $($cert.NotAfter)"
} else {
Write-Warning "Certificate not found in Windows Certificate Store after sync"
Write-Warning "Signing may fail. Continuing anyway..."
}
# Export keypair alias for Tauri signCommand
Write-Host ""
Write-Host "Exporting DIGICERT_KEYPAIR_ALIAS for Tauri signCommand..."
"DIGICERT_KEYPAIR_ALIAS=$keypairAlias" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
Write-Host "✓ DIGICERT_KEYPAIR_ALIAS=$keypairAlias"
Write-Host ""
Write-Host "============================================================"
Write-Host ""
- name: Verify DigiCert Setup
if: contains(inputs.platform, 'windows') && inputs.sign-binaries
shell: pwsh
run: |
Write-Host "=== DigiCert Setup Verification ==="
Write-Host ""
# Verify SMCTL is available
Write-Host "SMCTL Version:"
smctl --version
Write-Host ""
# Check environment variables
Write-Host "Environment Variables:"
Write-Host " SM_HOST: $env:SM_HOST"
Write-Host " SM_API_KEY: $($env:SM_API_KEY.Substring(0, [Math]::Min(20, $env:SM_API_KEY.Length)))..."
Write-Host " SM_CLIENT_CERT_FILE: $env:SM_CLIENT_CERT_FILE"
Write-Host " SM_CLIENT_CERT_PASSWORD: $(if ($env:SM_CLIENT_CERT_PASSWORD) { '***SET***' } else { 'NOT SET' })"
Write-Host ""
# Verify client certificate file exists and check size
if (Test-Path $env:SM_CLIENT_CERT_FILE) {
$certFile = Get-Item $env:SM_CLIENT_CERT_FILE
Write-Host "Client Certificate File:"
Write-Host " Path: $($certFile.FullName)"
Write-Host " Size: $($certFile.Length) bytes"
Write-Host " Last Modified: $($certFile.LastWriteTime)"
} else {
Write-Error "Client certificate file NOT FOUND: $env:SM_CLIENT_CERT_FILE"
}
Write-Host ""
# List available keypairs (verifies API authentication works)
Write-Host "Available Keypairs:"
smctl keypair ls
Write-Host ""
# Verify certificate (healthcheck)
Write-Host "DigiCert Healthcheck:"
smctl healthcheck
Write-Host ""
# Note: Healthcheck may show cert path/password warning but if keypair ls works,
# the signing should still work. We'll test in the pre-build signing test.
- name: Prepare Vulkan SDK for Ubuntu 24.04
if: contains(inputs.platform, 'ubuntu-24.04')
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 22.04
if: contains(inputs.platform, 'ubuntu-22.04')
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
run: |
cd frontend
pnpm install
- name: rustup install target
if: ${{ inputs.target != '' && !contains(inputs.target, 'unknown-linux-gnu') && !contains(inputs.target, '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: $CERT_ID"
- name: Configure build acceleration
if: contains(inputs.platform, 'macos')
run: |
echo "✓ macOS build will use Metal GPU acceleration (enabled by default)"
echo "✓ CoreML acceleration available for Apple Silicon"
- 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
- name: Determine build features
id: build-features
shell: bash
run: |
FEATURES=""
# Windows: Use Vulkan for GPU acceleration
if [[ "${{ inputs.platform }}" == *"windows"* ]]; then
FEATURES="--features vulkan"
echo "Windows build with Vulkan GPU acceleration"
fi
# Linux: Use OpenBLAS for optimized CPU performance
if [[ "${{ inputs.platform }}" == *"ubuntu"* ]]; then
FEATURES="--features openblas"
echo "Linux build with OpenBLAS CPU optimization"
fi
# macOS: Uses Metal by default, no additional features needed
if [[ "${{ inputs.platform }}" == *"macos"* ]]; then
echo "macOS build with Metal GPU acceleration (default)"
fi
echo "features=$FEATURES" >> "$GITHUB_OUTPUT"
- name: Build llama-helper sidecar (Windows)
if: contains(inputs.platform, 'windows')
shell: pwsh
run: |
# Build llama-helper without GPU features (CPU-only)
# Note: Vulkan build has CMake race condition issues with llama-cpp-sys-2
# The Tauri app itself uses whisper-rs with Vulkan for transcription
# llama-helper is for LLM inference and works fine on CPU
Write-Host "Building llama-helper sidecar (CPU-only)..."
cargo build --release -p llama-helper
if ($LASTEXITCODE -ne 0) {
Write-Error "Failed to build llama-helper"
exit $LASTEXITCODE
}
# Copy binary to binaries directory
New-Item -ItemType Directory -Force -Path "frontend/src-tauri/binaries" | Out-Null
Copy-Item "target/release/llama-helper.exe" -Destination "frontend/src-tauri/binaries/llama-helper-x86_64-pc-windows-msvc.exe"
Write-Host "Copied llama-helper to frontend/src-tauri/binaries/"
Get-ChildItem "frontend/src-tauri/binaries/"
- name: Build llama-helper sidecar (macOS/Linux)
if: "!contains(inputs.platform, 'windows')"
shell: bash
run: |
echo "Building llama-helper sidecar..."
# Determine llama-helper features based on platform
LLAMA_FEATURES=""
if [[ "${{ inputs.platform }}" == *"macos"* ]]; then
LLAMA_FEATURES="--features metal"
echo "Using Metal GPU acceleration for macOS"
else
echo "Using CPU-only mode for Linux"
fi
# Build llama-helper from workspace root
cargo build --release -p llama-helper $LLAMA_FEATURES
# Determine target triple and binary extension
TARGET="${{ inputs.target }}"
if [[ -z "$TARGET" ]]; then
TARGET=$(rustc -vV | grep "host:" | awk '{print $2}')
fi
# Copy binary to binaries directory
mkdir -p frontend/src-tauri/binaries
cp target/release/llama-helper frontend/src-tauri/binaries/llama-helper-${TARGET}
echo "Copied llama-helper to frontend/src-tauri/binaries/llama-helper-${TARGET}"
ls -la frontend/src-tauri/binaries/
- name: Cache FFmpeg binary
uses: actions/cache@v4
with:
path: frontend/src-tauri/binaries/ffmpeg-*
key: ${{ runner.os }}-ffmpeg-${{ hashFiles('frontend/src-tauri/build.rs', 'frontend/src-tauri/build/ffmpeg.rs') }}
- name: Build with Tauri
id: tauri-build
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 || '' }}
# Tauri updater signing (ALWAYS enabled for .sig files)
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
# License validation RSA public key (embedded at build time)
MEETILY_RSA_PUBLIC_KEY: ${{ secrets.MEETILY_RSA_PUBLIC_KEY }}
# Supabase configuration (for online license verification)
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_ANON_KEY }}
with:
projectPath: frontend
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 }}
args: ${{ inputs.build-args }} ${{ steps.build-features.outputs.features }}
- 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: |
target/${{ inputs.target }}/${{ steps.build-profile.outputs.profile }}/bundle/dmg/*.dmg
target/${{ inputs.target }}/${{ steps.build-profile.outputs.profile }}/bundle/macos/*.app
target/${{ inputs.target }}/${{ steps.build-profile.outputs.profile }}/bundle/macos/*.app.tar.gz
target/${{ inputs.target }}/${{ steps.build-profile.outputs.profile }}/bundle/macos/*.app.tar.gz.sig
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 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
# Get appimagetool
wget -q https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
chmod +x appimagetool-x86_64.AppImage
# Repackage AppImage with no-appstream to avoid warnings
ARCH=x86_64 ./appimagetool-x86_64.AppImage --no-appstream squashfs-root "$APPIMAGE_NAME"
# Clean up
rm -rf squashfs-root appimagetool-x86_64.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: |
target/${{ steps.build-profile.outputs.profile }}/bundle/deb/*.deb
target/${{ steps.build-profile.outputs.profile }}/bundle/appimage/*.AppImage
target/${{ steps.build-profile.outputs.profile }}/bundle/rpm/*.rpm
retention-days: 30
- 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: |
target/${{ steps.build-profile.outputs.profile }}/bundle/msi/*.msi
target/${{ steps.build-profile.outputs.profile }}/bundle/msi/*.msi.sig
target/${{ steps.build-profile.outputs.profile }}/bundle/nsis/*.exe
target/${{ steps.build-profile.outputs.profile }}/bundle/nsis/*.exe.sig
retention-days: 30
================================================
FILE: .github/workflows/pr-main-check.yml
================================================
name: "Validation Check"
# This workflow runs basic validation checks without building
# Use this to verify version and configuration before a release
on:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
validation-check:
name: Validation Check
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Get version from tauri.conf.json
id: get-version
run: |
VERSION=$(grep -o '"version": "[^"]*"' frontend/src-tauri/tauri.conf.json | cut -d'"' -f4)
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Version: $VERSION"
- name: Check current branch
run: |
CURRENT_BRANCH="${{ github.ref_name }}"
echo "Current branch: $CURRENT_BRANCH"
if [[ "$CURRENT_BRANCH" == "main" ]]; then
echo "On main branch - ready for release"
elif [[ "$CURRENT_BRANCH" == "devtest" ]]; then
echo "On devtest branch - ready for testing"
else
echo "On feature branch: $CURRENT_BRANCH"
fi
- name: Validate version format
run: |
VERSION="${{ steps.get-version.outputs.version }}"
# Check if version matches semantic versioning pattern
if [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Version format is valid: $VERSION"
else
echo "Warning: Version '$VERSION' may not be in standard semver format"
echo "Expected format: X.Y.Z (e.g., 1.0.0)"
fi
- name: Generate validation summary
run: |
echo "## Validation Check Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Version:** ${{ steps.get-version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "**Branch:** ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
echo "**Commit:** ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Next Steps" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "1. Run **Build and Test - DevTest** for unsigned builds" >> $GITHUB_STEP_SUMMARY
echo "2. Run **Build and Test** for signed builds on all platforms" >> $GITHUB_STEP_SUMMARY
echo "3. Run **Release** to create a production release" >> $GITHUB_STEP_SUMMARY
================================================
FILE: .github/workflows/release.yml
================================================
name: "Release"
on:
workflow_dispatch:
# Prevent duplicate releases
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false # Don't cancel releases, fail instead
jobs:
# STEP 1: Create draft release first
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
with:
fetch-depth: 0 # Fetch all tags
- name: Get version from tauri.conf.json and check for existing tags
id: get-version
shell: bash
run: |
BASE_VERSION=$(grep -o '"version": "[^"]*"' frontend/src-tauri/tauri.conf.json | cut -d'"' -f4)
echo "Base version from tauri.conf.json: $BASE_VERSION"
# Fetch all tags
git fetch --tags
# Check if base version tag exists
if git tag -l "v${BASE_VERSION}" | grep -q .; then
echo "Tag v${BASE_VERSION} already exists, looking for minor updates..."
# Find all existing minor versions (e.g., 0.1.1.1, 0.1.1.2, etc.)
LATEST_MINOR=0
for i in $(seq 1 100); do
if git tag -l "v${BASE_VERSION}.${i}" | grep -q .; then
LATEST_MINOR=$i
else
break
fi
done
# Increment to next minor version
NEXT_MINOR=$((LATEST_MINOR + 1))
if [ $NEXT_MINOR -gt 100 ]; then
echo "ERROR: Maximum minor version (100) reached for ${BASE_VERSION}"
echo "Please update the version in tauri.conf.json"
exit 1
fi
VERSION="${BASE_VERSION}.${NEXT_MINOR}"
echo "Using minor update version: $VERSION"
else
VERSION="${BASE_VERSION}"
echo "Using base version: $VERSION"
fi
echo "Final version: $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: `Meetily v${{ steps.get-version.outputs.version }}`,
draft: true,
prerelease: false,
generate_release_notes: true
})
console.log(`Created draft release with ID: ${data.id}`)
return data.id
# STEP 2: Build all platforms and upload directly to release
# Note: Linux builds are excluded from release (only macOS and Windows)
build-all-platforms:
needs: create-release
permissions:
contents: write
strategy:
fail-fast: false
matrix:
include:
- platform: "macos-latest" # for Apple Silicon only (M1 and above)
args: "--target aarch64-apple-darwin"
target: "aarch64-apple-darwin"
- platform: "windows-latest"
args: ""
target: "x86_64-pc-windows-msvc"
uses: ./.github/workflows/build.yml
with:
platform: ${{ matrix.platform }}
target: ${{ matrix.target }}
build-args: ${{ matrix.args }}
sign-binaries: true
asset-prefix: "meetily"
upload-artifacts: false # tauri-action uploads directly to release
release-id: ${{ needs.create-release.outputs.release-id }}
secrets: inherit
# STEP 3: Show release summary
release-summary:
needs: [create-release, build-all-platforms]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Get release assets and latest.json
id: release-info
run: |
VERSION="${{ needs.create-release.outputs.version }}"
RELEASE_ID="${{ needs.create-release.outputs.release-id }}"
echo "## Release Assets" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# List all release assets
echo "### Files in Release" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
gh api repos/${{ github.repository }}/releases/${RELEASE_ID}/assets --jq '.[] | "\(.name) (\(.size / 1048576 | floor)MB)"' >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# Download and show latest.json
echo "### latest.json content" >> $GITHUB_STEP_SUMMARY
gh release download "v${VERSION}" --pattern "latest.json" --dir . 2>/dev/null || echo "latest.json not yet available"
if [ -f latest.json ]; then
echo "\`\`\`json" >> $GITHUB_STEP_SUMMARY
cat latest.json >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
# Download links
echo "### Download Links" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Release page:** https://github.com/${{ github.repository }}/releases/tag/v${VERSION}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Download all as ZIP:** https://github.com/${{ github.repository }}/archive/refs/tags/v${VERSION}.zip" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Manual S3 upload:** Download latest.json from release and upload to \`s3://meetily-updates/latest.json\`" >> $GITHUB_STEP_SUMMARY
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Release Summary
run: |
echo "============================================"
echo "=== Release Created Successfully ==="
echo "============================================"
echo "Version: v${{ needs.create-release.outputs.version }}"
echo "Release URL: https://github.com/${{ github.repository }}/releases/tag/v${{ needs.create-release.outputs.version }}"
echo ""
echo "Uploaded assets (via tauri-action):"
echo " - macOS: DMG installer + app.tar.gz (for updater) + .sig"
echo " - Windows: MSI + NSIS installers (SIGNED via signCommand) + .sig files"
echo " - Updater manifest: latest.json (auto-generated by tauri-action)"
echo ""
echo "Next steps:"
echo " 1. Review the draft release"
echo " 2. Edit release notes if needed"
echo " 3. Publish the release when ready"
echo "============================================"
# Add to GitHub Step Summary
echo "## Release Created Successfully" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Version:** v${{ needs.create-release.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Release URL:** [View Release](https://github.com/${{ github.repository }}/releases/tag/v${{ needs.create-release.outputs.version }})" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Uploaded Assets" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Platform | Assets |" >> $GITHUB_STEP_SUMMARY
echo "|----------|--------|" >> $GITHUB_STEP_SUMMARY
echo "| **macOS** | DMG installer, app.tar.gz (updater), .sig |" >> $GITHUB_STEP_SUMMARY
echo "| **Windows** | MSI installer (SIGNED), NSIS installer (SIGNED), .sig files |" >> $GITHUB_STEP_SUMMARY
echo "| **Updater** | latest.json manifest (auto-generated by tauri-action) |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Next Steps" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "1. [Review the draft release](https://github.com/${{ github.repository }}/releases/tag/v${{ needs.create-release.outputs.version }})" >> $GITHUB_STEP_SUMMARY
echo "2. Edit release notes if needed" >> $GITHUB_STEP_SUMMARY
echo "3. Publish the release when ready" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
================================================
FILE: .gitignore
================================================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
/experiments
# dependencies
/node_modules
**/models
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
*.mp4
# testing
/coverage
/dist
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
# Audio files
*.wav
# Large media files
docs/demo.mov
docs/demo.gif
meeting_minutes.db
# Added by Task Master AI
# Logs
logs
*.log
dev-debug.log
# Dependency directories
node_modules/
# Environment variables
.env
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
experiments/
.cursor/
target/
frontend/src-tauri/binaries/
Cargo.lock
================================================
FILE: .gitmodules
================================================
[submodule "backend/whisper.cpp"]
path = backend/whisper.cpp
url = https://github.com/Zackriya-Solutions/whisper.cpp
branch = develop
================================================
FILE: BLUETOOTH_PLAYBACK_NOTICE.md
================================================
# Bluetooth Headphone Playback Notice
## Important Information for Recording Review
When **reviewing recordings** in Meetily, we recommend using **computer speakers** or **wired headphones** rather than Bluetooth headphones for accurate playback.
---
## The Issue
Recordings may sound **distorted, sped up, or have clarity issues** when played through Bluetooth headphones, even though the recording file itself is perfectly fine.
### Symptoms
- Audio plays too fast or too slow
- Voice sounds higher/lower pitched than normal
- Quality seems degraded or "chipmunk-like"
- **Different Bluetooth devices cause different playback speeds**
### What's Actually Happening
**Your recording is fine!** The issue occurs during **playback**, not recording.
---
## Technical Explanation
### Why This Happens
1. **Meetily records at 48kHz** (professional audio standard)
2. **Bluetooth headphones use various sample rates**: 8kHz, 16kHz, 24kHz, 44.1kHz, or 48kHz
3. **macOS resamples audio** when sending 48kHz content to Bluetooth devices
4. **Resampling can fail** if macOS:
- Negotiates the wrong Bluetooth codec (SBC vs AAC vs LDAC)
- Misidentifies the device's playback capability
- Uses low-quality resampling for power efficiency
### Device-Specific Behavior
Different Bluetooth headphones report different capabilities:
| Device Type | Typical Playback Rate | Result When Playing 48kHz |
|------------|----------------------|---------------------------|
| Sony WH-1000XM4 | 16-44.1kHz (varies) | May sound 1.5-3x faster |
| AirPods Pro | 24kHz or 48kHz | Usually OK, but can vary |
| Cheap BT Headset | 8-16kHz | Often sounds very fast |
| High-end BT (LDAC) | 44.1-48kHz | Usually works correctly |
The rate depends on:
- **Bluetooth profile** (A2DP for music vs HFP for calls)
- **Active codec** (SBC, AAC, aptX, LDAC)
- **Battery mode** (power-saving modes may reduce quality)
- **macOS version** and audio driver quirks
---
## Solution: Use Computer Speakers
### For Accurate Review
✅ **Computer speakers** (built-in or external)
✅ **Wired headphones** (3.5mm jack or USB)
✅ **High-quality DAC** (digital audio converter)
❌ **Bluetooth headphones** (for reviewing recordings)
❌ **Bluetooth speakers** (same resampling issues)
### Bluetooth Headphones Are Fine For
- ✅ **Recording** (microphone input) - We handle sample rate conversion correctly
- ✅ **Live monitoring** during recording - macOS handles real-time audio
- ✅ **General computer use** - Normal audio playback
- ❌ **Reviewing Meetily recordings** - Use wired/speakers instead
---
## Verification Steps
To confirm your recording is actually fine:
1. **Play recording through computer speakers**
- If it sounds normal → Recording is good, BT playback is the issue ✅
- If it still sounds wrong → May be a different issue ❌
2. **Check file properties**
```bash
# In terminal:
ffprobe path/to/recording/audio.mp4
```
Should show:
- `sample_rate=48000` ✅
- `channels=1` ✅
- `codec_name=aac` ✅
3. **Try different playback devices**
- Computer speakers: Should sound normal
- Wired headphones: Should sound normal
- Bluetooth device A: Might sound wrong
- Bluetooth device B: Might sound differently wrong
---
## Why We Don't "Fix" This
### This is Not a Meetily Bug
The issue is in **macOS's Bluetooth audio stack**, not in Meetily's recording engine.
**Evidence:**
- Recordings play perfectly on computer speakers
- File metadata shows correct 48kHz encoding
- Other professional audio apps have the same limitation
- Issue varies by Bluetooth device (different devices = different problems)
### Industry Standard Practice
Professional audio software **always** recommends:
- Monitor through studio monitors (speakers) or wired headphones
- Avoid Bluetooth for critical listening
- Use wired connections for audio work
Examples:
- **Logic Pro X**: Warns against BT monitoring
- **Audacity**: Recommends wired headphones
- **GarageBand**: Disables BT for recording/monitoring
---
## Workarounds
### Option 1: Use Computer Speakers (Recommended)
**Best**: Most accurate, no resampling issues
### Option 2: Export at Different Sample Rate
If you **must** use Bluetooth for playback:
1. **Export recording** at lower sample rate (future feature)
2. **Transcode manually** using ffmpeg:
```bash
ffmpeg -i audio.mp4 -ar 44100 audio_44k.mp4
```
3. **Try 44.1kHz** (better BT compatibility than 48kHz)
### Option 3: Use High-Quality Bluetooth
Devices with **LDAC** or **aptX HD** codecs:
- Sony WH-1000XM5 (LDAC mode)
- Sennheiser Momentum 4
- Some high-end Bose models
These handle 48kHz better (but still not perfect).
---
## Technical Details for Developers
### Sample Rate Chain
```
Recording Pipeline:
Microphone (16kHz) → Resample to 48kHz → Pipeline (48kHz)
System Audio (48kHz) → No resampling → Pipeline (48kHz)
Mixed Audio (48kHz) → Encode → File (48kHz AAC)
Playback (Computer Speakers):
File (48kHz) → macOS CoreAudio → Speakers (48kHz) ✅
Playback (Bluetooth):
File (48kHz) → macOS CoreAudio → Bluetooth Stack → Resample → BT Device (16-48kHz) ⚠️
↑
This step can fail!
```
### Why macOS Resampling Fails
1. **Codec negotiation**: BT device claims 48kHz support but actually uses 16kHz
2. **Profile switching**: Device switches from A2DP (music) to HFP (call) mid-playback
3. **Power management**: macOS downsamples to save battery
4. **Driver bugs**: CoreAudio → Bluetooth handoff has known issues
### Apple's Documentation
From [Apple Technical Note TN2321](https://developer.apple.com/library/archive/technotes/tn2321/):
> "Bluetooth audio devices may report supported sample rates that differ from
> their actual playback rates. Applications should not rely on Bluetooth
> devices for accurate audio monitoring."
---
## FAQ
### Q: Will this be fixed in a future update?
**A**: This is a macOS/Bluetooth limitation, not a Meetily bug. We've correctly recorded at 48kHz.
### Q: Why not record at 16kHz if that's what Bluetooth uses?
**A**: Because:
1. System audio is 48kHz (can't be changed)
2. 48kHz is professional quality (16kHz is phone-call quality)
3. Most users play back on computer speakers
4. Recording at 16kHz would degrade quality for 95% of users
### Q: Can you detect my Bluetooth device and warn me?
**A**: Yes! Meetily now shows a warning when Bluetooth headphones are active during playback.
### Q: Does this affect recording quality?
**A**: **No**. Recording quality is perfect. Only **playback** through Bluetooth has issues.
### Q: What about AirPods? They're supposed to be high quality.
**A**: AirPods handle 48kHz better than most BT devices, but can still have issues depending on:
- Codec negotiation (AAC vs SBC)
- Battery level (power-saving mode)
- Connection quality (Bluetooth interference)
- macOS audio driver quirks
---
## Summary
✅ **Recordings are perfect** - 48kHz, high quality
✅ **Computer playback works** - Use speakers or wired headphones
⚠️ **Bluetooth playback may sound wrong** - macOS resampling issue
✅ **Recording through BT mic works** - We handle resampling correctly
**Bottom line**: Review your recordings through computer speakers, not Bluetooth headphones.
---
## Related Documentation
- [AIRPODS_BLUETOOTH_FIX.md](AIRPODS_BLUETOOTH_FIX.md) - Bluetooth device reconnection handling
- [BLUETOOTH_SAMPLE_RATE_FIX.md](BLUETOOTH_SAMPLE_RATE_FIX.md) - Microphone sample rate resampling
- [Apple Technical Note TN2321](https://developer.apple.com/library/archive/technotes/tn2321/) - Bluetooth Audio Best Practices
---
**Last Updated**: October 10, 2025
**Applies To**: Meetily v0.0.5+ on macOS
================================================
FILE: CLAUDE.md
================================================
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
**Meetily** is a privacy-first AI meeting assistant that captures, transcribes, and summarizes meetings entirely on local infrastructure. The project consists of two main components:
1. **Frontend**: Tauri-based desktop application (Rust + Next.js + TypeScript)
2. **Backend**: FastAPI server for meeting storage and LLM-based summarization (Python)
### Key Technology Stack
- **Desktop App**: Tauri 2.x (Rust) + Next.js 14 + React 18
- **Audio Processing**: Rust (cpal, whisper-rs, professional audio mixing)
- **Transcription**: Whisper.cpp (local, GPU-accelerated)
- **Backend API**: FastAPI + SQLite (aiosqlite)
- **LLM Integration**: Ollama (local), Claude, Groq, OpenRouter
## Essential Development Commands
### Frontend Development (Tauri Desktop App)
**Location**: `/frontend`
```bash
# macOS Development
./clean_run.sh # Clean build and run with info logging
./clean_run.sh debug # Run with debug logging
./clean_build.sh # Production build
# Windows Development
clean_run_windows.bat # Clean build and run
clean_build_windows.bat # Production build
# Manual Commands
pnpm install # Install dependencies
pnpm run dev # Next.js dev server (port 3118)
pnpm run tauri:dev # Full Tauri development mode
pnpm run tauri:build # Production build
# GPU-Specific Builds (for testing acceleration)
pnpm run tauri:dev:metal # macOS Metal GPU
pnpm run tauri:dev:cuda # NVIDIA CUDA
pnpm run tauri:dev:vulkan # AMD/Intel Vulkan
pnpm run tauri:dev:cpu # CPU-only (no GPU)
```
### Backend Development (FastAPI Server)
**Location**: `/backend`
```bash
# macOS
./build_whisper.sh small # Build Whisper with 'small' model
./clean_start_backend.sh # Start FastAPI server (port 5167)
# Windows
build_whisper.cmd small # Build Whisper with model
start_with_output.ps1 # Interactive setup and start
clean_start_backend.cmd # Start server
# Docker (Cross-Platform)
./run-docker.sh start --interactive # Interactive setup (macOS/Linux)
.\run-docker.ps1 start -Interactive # Interactive setup (Windows)
./run-docker.sh logs --service app # View logs
```
**Available Whisper Models**: `tiny`, `tiny.en`, `base`, `base.en`, `small`, `small.en`, `medium`, `medium.en`, `large-v1`, `large-v2`, `large-v3`, `large-v3-turbo`
### Service Endpoints
- **Whisper Server**: http://localhost:8178
- **Backend API**: http://localhost:5167
- **Backend Docs**: http://localhost:5167/docs
- **Frontend Dev**: http://localhost:3118
## High-Level Architecture
### Three-Tier System Architecture
```
┌─────────────────────────────────────────────────────────────────┐
│ Frontend (Tauri Desktop App) │
│ ┌──────────────────┐ ┌─────────────────┐ ┌────────────────┐ │
│ │ Next.js UI │ │ Rust Backend │ │ Whisper Engine │ │
│ │ (React/TS) │←→│ (Audio + IPC) │←→│ (Local STT) │ │
│ └──────────────────┘ └─────────────────┘ └────────────────┘ │
│ ↑ Tauri Events ↑ Audio Pipeline │
└─────────┼────────────────────────┼─────────────────────────────┘
│ HTTP/WebSocket │
↓ │
┌─────────────────────────────────┼─────────────────────────────┐
│ Backend (FastAPI) │ │
│ ┌────────────┐ ┌─────────────┴──────┐ ┌────────────────┐ │
│ │ SQLite │←→│ Meeting Manager │←→│ LLM Provider │ │
│ │ (Meetings) │ │ (CRUD + Summary) │ │ (Ollama/etc.) │ │
│ └────────────┘ └────────────────────┘ └────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
### Audio Processing Pipeline (Critical Understanding)
The audio system has **two parallel paths** with different purposes:
```
Raw Audio (Mic + System)
↓
┌────────────────────────────────────────────────────────────┐
│ Audio Pipeline Manager │
│ (frontend/src-tauri/src/audio/pipeline.rs) │
└─────────────┬──────────────────────────┬───────────────────┘
↓ ↓
┌─────────────────┐ ┌─────────────────────┐
│ Recording Path │ │ Transcription Path │
│ (Pre-mixed) │ │ (VAD-filtered) │
└─────────────────┘ └─────────────────────┘
↓ ↓
RecordingSaver.save() WhisperEngine.transcribe()
```
**Key Insight**: The pipeline performs **professional audio mixing** (RMS-based ducking, clipping prevention) for recording, while simultaneously applying **Voice Activity Detection (VAD)** to send only speech segments to Whisper for transcription.
### Audio Device Modularization (Recently Completed)
**Context**: The audio system was refactored from a monolithic 1028-line `core.rs` file into focused modules. See [AUDIO_MODULARIZATION_PLAN.md](AUDIO_MODULARIZATION_PLAN.md) for details.
```
audio/
├── devices/ # Device discovery and configuration
│ ├── discovery.rs # list_audio_devices, trigger_audio_permission
│ ├── microphone.rs # default_input_device
│ ├── speakers.rs # default_output_device
│ ├── configuration.rs # AudioDevice types, parsing
│ └── platform/ # Platform-specific implementations
│ ├── windows.rs # WASAPI logic (~200 lines)
│ ├── macos.rs # ScreenCaptureKit logic
│ └── linux.rs # ALSA/PulseAudio logic
├── capture/ # Audio stream capture
│ ├── microphone.rs # Microphone capture stream
│ ├── system.rs # System audio capture stream
│ └── core_audio.rs # macOS ScreenCaptureKit integration
├── pipeline.rs # Audio mixing and VAD processing
├── recording_manager.rs # High-level recording coordination
├── recording_commands.rs # Tauri command interface
└── recording_saver.rs # Audio file writing
```
**When working on audio features**:
- Device detection issues → `devices/discovery.rs` or `devices/platform/{windows,macos,linux}.rs`
- Microphone/speaker problems → `devices/microphone.rs` or `devices/speakers.rs`
- Audio capture issues → `capture/microphone.rs` or `capture/system.rs`
- Mixing/processing problems → `pipeline.rs`
- Recording workflow → `recording_manager.rs`
### Rust ↔ Frontend Communication (Tauri Architecture)
**Command Pattern** (Frontend → Rust):
```typescript
// Frontend: src/app/page.tsx
await invoke('start_recording', {
mic_device_name: "Built-in Microphone",
system_device_name: "BlackHole 2ch",
meeting_name: "Team Standup"
});
```
```rust
// Rust: src/lib.rs
#[tauri::command]
async fn start_recording<R: Runtime>(
app: AppHandle<R>,
mic_device_name: Option<String>,
system_device_name: Option<String>,
meeting_name: Option<String>
) -> Result<(), String> {
// Implementation delegates to audio::recording_commands
}
```
**Event Pattern** (Rust → Frontend):
```rust
// Rust: Emit transcript updates
app.emit("transcript-update", TranscriptUpdate {
text: "Hello world".to_string(),
timestamp: chrono::Utc::now(),
// ...
})?;
```
```typescript
// Frontend: Listen for events
await listen<TranscriptUpdate>('transcript-update', (event) => {
setTranscripts(prev => [...prev, event.payload]);
});
```
### Whisper Model Management
**Model Storage Locations**:
- **Development**: `frontend/models/` or `backend/whisper-server-package/models/`
- **Production (macOS)**: `~/Library/Application Support/Meetily/models/`
- **Production (Windows)**: `%APPDATA%\Meetily\models\`
**Model Loading** (frontend/src-tauri/src/whisper_engine/whisper_engine.rs):
```rust
pub async fn load_model(&self, model_name: &str) -> Result<()> {
// Automatically detects GPU capabilities (Metal/CUDA/Vulkan)
// Falls back to CPU if GPU unavailable
}
```
**GPU Acceleration**:
- **macOS**: Metal + CoreML (automatically enabled)
- **Windows/Linux**: CUDA (NVIDIA), Vulkan (AMD/Intel), or CPU
- Configure via Cargo features: `--features cuda`, `--features vulkan`
## Critical Development Patterns
### 1. Audio Buffer Management
**Ring Buffer Mixing** (pipeline.rs):
- Mic and system audio arrive asynchronously at different rates
- Ring buffer accumulates samples until both streams have aligned windows (50ms)
- Professional mixing applies RMS-based ducking to prevent system audio from drowning out microphone
- Uses `VecDeque` for efficient windowed processing
### 2. Thread Safety and Async Boundaries
**Recording State** (recording_state.rs):
```rust
pub struct RecordingState {
is_recording: Arc<AtomicBool>,
audio_sender: Arc<RwLock<Option<mpsc::UnboundedSender<AudioChunk>>>>,
// ...
}
```
**Key Pattern**: Use `Arc<RwLock<T>>` for shared state across async tasks, `Arc<AtomicBool>` for simple flags.
### 3. Error Handling and Logging
**Performance-Aware Logging** (lib.rs):
```rust
#[cfg(debug_assertions)]
macro_rules! perf_debug {
($($arg:tt)*) => { log::debug!($($arg)*) };
}
#[cfg(not(debug_assertions))]
macro_rules! perf_debug {
($($arg:tt)*) => {}; // Zero overhead in release builds
}
```
**Usage**: Use `perf_debug!()` and `perf_trace!()` for hot-path logging that should be eliminated in production.
### 4. Frontend State Management
**Sidebar Context** (components/Sidebar/SidebarProvider.tsx):
- Global state for meetings list, current meeting, recording status
- Communicates with backend API (http://localhost:5167)
- Manages WebSocket connections for real-time updates
**Pattern**: Tauri commands update Rust state → Emit events → Frontend listeners update React state → Context propagates to components
## Common Development Tasks
### Adding a New Audio Device Platform
1. Create platform file: `audio/devices/platform/{platform_name}.rs`
2. Implement device enumeration for the platform
3. Add platform-specific configuration in `audio/devices/configuration.rs`
4. Update `audio/devices/platform/mod.rs` to export new platform functions
5. Test with `cargo check` and platform-specific device tests
### Adding a New Tauri Command
1. Define command in `src/lib.rs`:
```rust
#[tauri::command]
async fn my_command(arg: String) -> Result<String, String> { /* ... */ }
```
2. Register in `tauri::Builder`:
```rust
.invoke_handler(tauri::generate_handler![
start_recording,
my_command, // Add here
])
```
3. Call from frontend:
```typescript
const result = await invoke<string>('my_command', { arg: 'value' });
```
### Modifying Audio Pipeline Behavior
**Location**: `frontend/src-tauri/src/audio/pipeline.rs`
Key components:
- `AudioMixerRingBuffer`: Manages mic + system audio synchronization
- `ProfessionalAudioMixer`: RMS-based ducking and mixing
- `AudioPipelineManager`: Orchestrates VAD, mixing, and distribution
**Testing Audio Changes**:
```bash
# Enable verbose audio logging
RUST_LOG=app_lib::audio=debug ./clean_run.sh
# Monitor audio metrics in real-time
# Check Developer Console in the app (Cmd+Shift+I on macOS)
```
### Backend API Development
**Adding New Endpoints** (backend/app/main.py):
```python
@app.post("/api/my-endpoint")
async def my_endpoint(request: MyRequest) -> MyResponse:
# Use DatabaseManager for persistence
db = DatabaseManager()
result = await db.some_operation()
return result
```
**Database Operations** (backend/app/db.py):
- All meeting data stored in SQLite
- Use `DatabaseManager` class for all DB operations
- Async operations with `aiosqlite`
## Testing and Debugging
### Frontend Debugging
**Enable Rust Logging**:
```bash
# macOS
RUST_LOG=debug ./clean_run.sh
# Windows (PowerShell)
$env:RUST_LOG="debug"; ./clean_run_windows.bat
```
**Developer Tools**:
- Open DevTools: `Cmd+Shift+I` (macOS) or `Ctrl+Shift+I` (Windows)
- Console Toggle: Built into app UI (console icon)
- View Rust logs: Check terminal output
### Backend Debugging
**View API Logs**:
```bash
# Backend logs show in terminal with detailed formatting:
# 2025-01-03 12:34:56 - INFO - [main.py:123 - endpoint_name()] - Message
```
**Test API Directly**:
- Swagger UI: http://localhost:5167/docs
- ReDoc: http://localhost:5167/redoc
### Audio Pipeline Debugging
**Key Metrics** (emitted by pipeline):
- Buffer sizes (mic/system)
- Mixing window count
- VAD detection rate
- Dropped chunk warnings
**Monitor via Developer Console**: The app includes real-time metrics display when recording.
## Platform-Specific Notes
### macOS
- **Audio Capture**: Uses ScreenCaptureKit for system audio (macOS 13+)
- **GPU**: Metal + CoreML automatically enabled
- **Permissions**: Requires microphone + screen recording permissions
- **System Audio**: Requires virtual audio device (BlackHole) for system capture
### Windows
- **Audio Capture**: Uses WASAPI (Windows Audio Session API)
- **GPU**: CUDA (NVIDIA) or Vulkan (AMD/Intel) via Cargo features
- **Build Tools**: Requires Visual Studio Build Tools with C++ workload
- **System Audio**: Uses WASAPI loopback for system capture
### Linux
- **Audio Capture**: ALSA/PulseAudio
- **GPU**: CUDA (NVIDIA) or Vulkan via Cargo features
- **Dependencies**: Requires cmake, llvm, libomp
## Performance Optimization Guidelines
### Audio Processing
- Use `perf_debug!()` / `perf_trace!()` for hot-path logging (zero cost in release)
- Batch audio metrics using `AudioMetricsBatcher` (pipeline.rs)
- Pre-allocate buffers with `AudioBufferPool` (buffer_pool.rs)
- VAD filtering reduces Whisper load by ~70% (only processes speech)
### Whisper Transcription
- **Model Selection**: Balance accuracy vs speed
- Development: `base` or `small` (fast iteration)
- Production: `medium` or `large-v3` (best quality)
- **GPU Acceleration**: 5-10x faster than CPU
- **Parallel Processing**: Available in `whisper_engine/parallel_processor.rs` for batch workloads
### Frontend Performance
- React state updates batched via Sidebar context
- Transcript rendering virtualized for large meetings
- Audio level monitoring throttled to 60fps
## Important Constraints and Gotchas
1. **Audio Chunk Size**: Pipeline expects consistent 48kHz sample rate. Resampling happens at capture time.
2. **Platform Audio Quirks**:
- macOS: ScreenCaptureKit requires macOS 13+, needs screen recording permission
- Windows: WASAPI exclusive mode can conflict with other apps
- System audio requires virtual device (BlackHole on macOS, WASAPI loopback on Windows)
3. **Whisper Model Loading**: Models are loaded once and cached. Changing models requires app restart or manual unload/reload.
4. **Backend Dependency**: Frontend can run standalone (local Whisper), but meeting persistence and LLM features require backend running.
5. **CORS Configuration**: Backend allows all origins (`"*"`) for development. Restrict for production deployment.
6. **File Paths**: Use Tauri's path APIs (`downloadDir`, etc.) for cross-platform compatibility. Never hardcode paths.
7. **Audio Permissions**: Request permissions early. macOS requires both microphone AND screen recording for system audio.
## Repository-Specific Conventions
- **Logging Format**: Backend uses detailed formatting with filename:line:function
- **Error Handling**: Rust uses `anyhow::Result`, frontend uses try-catch with user-friendly messages
- **Naming**: Audio devices use "microphone" and "system" consistently (not "input"/"output")
- **Git Branches**:
- `main`: Stable releases
- `fix/*`: Bug fixes
- `enhance/*`: Feature enhancements
- Current: `fix/audio-mixing` (working on audio pipeline improvements)
## Key Files Reference
**Core Coordination**:
- [frontend/src-tauri/src/lib.rs](frontend/src-tauri/src/lib.rs) - Main Tauri entry point, command registration
- [frontend/src-tauri/src/audio/mod.rs](frontend/src-tauri/src/audio/mod.rs) - Audio module exports
- [backend/app/main.py](backend/app/main.py) - FastAPI application, API endpoints
**Audio System**:
- [frontend/src-tauri/src/audio/recording_manager.rs](frontend/src-tauri/src/audio/recording_manager.rs) - Recording orchestration
- [frontend/src-tauri/src/audio/pipeline.rs](frontend/src-tauri/src/audio/pipeline.rs) - Audio mixing and VAD
- [frontend/src-tauri/src/audio/recording_saver.rs](frontend/src-tauri/src/audio/recording_saver.rs) - Audio file writing
**UI Components**:
- [frontend/src/app/page.tsx](frontend/src/app/page.tsx) - Main recording interface
- [frontend/src/components/Sidebar/SidebarProvider.tsx](frontend/src/components/Sidebar/SidebarProvider.tsx) - Global state management
**Whisper Integration**:
- [frontend/src-tauri/src/whisper_engine/whisper_engine.rs](frontend/src-tauri/src/whisper_engine/whisper_engine.rs) - Whisper model management and transcription
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Meeting Minutes Updates
Thank you for your interest in contributing to Meetily! This document provides guidelines and instructions for contributing to this project.
## Development Workflow
### Branch Strategy
- `main` - Production branch
- `devtest` - Development and testing branch
- Feature branches should be created from `devtest`
### Getting Started
1. Fork the repository
2. Clone your fork:
```bash
git clone https://github.com/YOUR_USERNAME/meeting-minutes.git
```
3. Add the original repository as upstream:
```bash
git remote add upstream https://github.com/Zackriya-Solutions/meeting-minutes.git
```
4. Create a new branch from `devtest`:
```bash
git checkout devtest
git pull upstream devtest
git checkout -b feature/your-feature-name
```
### Development Process
1. Always start your work from the `devtest` branch
2. Create a new branch for each feature/fix
3. Make your changes
4. Write or update tests as needed
5. Ensure all tests pass
6. Update documentation if necessary
### Issue Creation
Before starting work on a new feature or bug fix:
1. Check if an issue already exists
2. If not, create a new issue with:
- Clear title
- Detailed description
- Steps to reproduce (for bugs)
- Expected behavior
- Screenshots (if applicable)
- Labels (bug, enhancement, etc.)
### Pull Request Process
1. Create a PR from your feature branch to `devtest`
2. Link the PR to the related issue using the issue number (e.g., "Fixes #123")
3. Fill out the PR template completely
4. Ensure CI checks pass
5. Request review from at least one maintainer
6. Address any review comments
7. Once approved, the PR will be merged into `devtest`
### PR Template
```markdown
## Description
[Describe your changes here]
## Related Issue
[Link to the issue this PR addresses]
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Documentation update
- [ ] Performance improvement
- [ ] Code refactoring
- [ ] Other (please describe)
## Testing
- [ ] Unit tests added/updated
- [ ] Manual testing performed
- [ ] All tests pass
## Documentation
- [ ] Documentation updated
- [ ] No documentation needed
## Checklist
- [ ] Code follows project style
- [ ] Self-reviewed the code
- [ ] Added comments for complex code
- [ ] Updated README if needed
```
## Code Style
- Follow the existing code style
- Use meaningful variable and function names
- Add comments for complex logic
- Keep functions small and focused
- Write clear commit messages
## Commit Message Format
```
<type>(<scope>): <subject>
<body>
<footer>
```
Types:
- feat: New feature
- fix: Bug fix
- docs: Documentation changes
- style: Code style changes
- refactor: Code refactoring
- test: Adding/updating tests
- chore: Maintenance tasks
## Testing
- Write unit tests for new features
- Update existing tests when modifying code
- Ensure all tests pass before submitting PR
- Include integration tests for complex features
## Documentation
- Update documentation for new features
- Keep README up to date
- Document API changes
- Add comments for complex code
## Review Process
1. PRs require at least one review
2. Address all review comments
3. Keep the PR up to date with `devtest`
4. Squash commits if requested
## Getting Help
- Create an issue for questions
- Join our community chat
- Contact maintainers
## License
By contributing, you agree that your contributions will be licensed under the project's MIT License.
================================================
FILE: Cargo.toml
================================================
[workspace]
resolver = "2"
members = [
"frontend/src-tauri",
"llama-helper"
]
# Shared workspace settings
[workspace.package]
edition = "2021"
rust-version = "1.77"
# Shared dependencies that can be inherited by workspace members
[workspace.dependencies]
anyhow = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1.32.0", features = ["full"] }
================================================
FILE: LICENSE.md
================================================
MIT License
Copyright (c) 2024 Zackriya Solutions
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: PRIVACY_POLICY.md
================================================
# Meetily Privacy Policy
*Last updated: [Current Date]*
## Our Privacy-First Commitment
Meetily is built on the principle that your meeting data should remain private and under your control. This privacy policy explains how we handle data in our open-source meeting assistant.
## Data Processing Philosophy
### Local-First Processing
- **Meeting transcription**: Processed entirely on your device using local Whisper models
- **Audio recordings**: Never transmitted to external servers
- **Meeting content**: Remains on your infrastructure
- **AI summaries**: Generated locally or through your chosen LLM provider
### Your Data Ownership
- You own all meeting data, transcripts, and recordings
- Data is stored locally on your device
- No vendor lock-in - export your data anytime
- Complete control over data retention and deletion
## Usage Analytics
### What We Collect
To improve Meetily and ensure optimal performance, we collect minimal, anonymized usage data:
**Application Usage:**
- Feature usage patterns (which tools you use most)
- Session duration and frequency
- Performance metrics (transcription success rates, error frequencies)
- UI interaction patterns (button clicks, navigation flows)
**Technical Metrics:**
- Application version and platform information
- Error logs and crash reports (anonymized)
- Performance benchmarks (processing times, resource usage)
### What We DON'T Collect
We never collect:
- ❌ Meeting content, transcripts, or recordings
- ❌ Personal information or identifiable data
- ❌ File names, meeting titles, or metadata
- ❌ Audio data or voice patterns
- ❌ Participant names or contact information
- ❌ LLM conversations or AI-generated content
### Why We Collect This Data
This analytics collection is necessary for:
- **Product Quality**: Identifying and fixing bugs that impact user experience
- **Performance Optimization**: Understanding resource usage and system bottlenecks
- **Security**: Detecting potential security issues and vulnerabilities
- **Feature Development**: Making data-driven decisions about new features
- **Open Source Sustainability**: Ensuring the project meets user needs effectively
### Analytics Implementation
- **Provider**: PostHog (privacy-focused analytics platform)
- **Anonymization**: All data linked to generated user IDs only - no personal identification
- **Data retention**: 12 months maximum, then automatically deleted
- **Encryption**: All data encrypted in transit using industry-standard protocols
- **Location**: Data processed in accordance with PostHog's privacy policy
- **Access Control**: Strictly limited to core development team members
## Third-Party Services
### LLM Providers (Optional)
If you choose to use external LLM providers:
- **Anthropic Claude**: Subject to Anthropic's privacy policy
- **Groq**: Subject to Groq's privacy policy
- **Local Ollama**: Processed entirely on your device
### Analytics Service (Optional)
- **PostHog**: Used for usage analytics when enabled
- **Data**: Only anonymized usage patterns, no meeting content
- **Control**: Completely optional and user-controlled
## Your Privacy Rights
### Data Control
- **Access**: View all data stored locally on your device
- **Export**: Export your data in standard formats
- **Delete**: Remove all data from your device
### Analytics Transparency
- **Open source**: Full analytics implementation available for review in our source code
- **Questions**: Contact us for any analytics-related concerns
## Data Security
### Local Security
- Data encrypted at rest using your device's security features
- No transmission of sensitive meeting data
- Standard file system permissions protect your data
### Open Source Transparency
- Full source code available for security review
- Community-audited privacy implementations
- No hidden data collection or tracking
## Changes to This Policy
We will notify users of any material changes to this privacy policy through:
- Updates to this document in our GitHub repository
- Release notes for application updates
- In-app notifications for significant privacy changes
## Contact Us
For privacy-related questions or concerns:
- **GitHub Issues**: [Create an issue](https://github.com/Zackriya-Solutions/meeting-minutes/issues)
- **Email**: [Contact form](https://www.zackriya.com/service-interest-form/)
- **Community**: [Discord](https://discord.gg/crRymMQBFH)
## Open Source Commitment
As an open-source project under MIT license, you can:
- Review our complete privacy implementation
- Modify data handling to meet your requirements
- Deploy entirely on your own infrastructure
- Contribute to privacy improvements
---
*This privacy policy applies to Meetily v0.0.5 and later versions. For enterprise deployments, additional privacy controls may be available.*
================================================
FILE: README.md
================================================
<div align="center" style="border-bottom: none">
<h1>
<img src="docs/Meetily-6.png" style="border-radius: 10px;" />
<br>
Privacy-First AI Meeting Assistant
</h1>
<a href="https://trendshift.io/repositories/13272" target="_blank"><img src="https://trendshift.io/api/badge/repositories/13272" alt="Zackriya-Solutions%2Fmeeting-minutes | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
<br>
<br>
<a href="https://github.com/Zackriya-Solutions/meeting-minutes/releases/"><img src="https://img.shields.io/badge/Pre_Release-Link-brightgreen" alt="Pre-Release"></a>
<a href="https://github.com/Zackriya-Solutions/meeting-minutes/releases"><img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/zackriya-solutions/meeting-minutes?style=flat">
</a>
<a href="https://github.com/Zackriya-Solutions/meeting-minutes/releases"> <img alt="GitHub Downloads (all assets, all releases)" src="https://img.shields.io/github/downloads/zackriya-solutions/meeting-minutes/total?style=plastic"> </a>
<a href="https://github.com/Zackriya-Solutions/meeting-minutes/releases"><img src="https://img.shields.io/badge/License-MIT-blue" alt="License"></a>
<a href="https://github.com/Zackriya-Solutions/meeting-minutes/releases"><img src="https://img.shields.io/badge/Supported_OS-macOS,_Windows-white" alt="Supported OS"></a>
<a href="https://github.com/Zackriya-Solutions/meeting-minutes/releases"><img alt="GitHub Tag" src="https://img.shields.io/github/v/tag/zackriya-solutions/meeting-minutes?include_prereleases&color=yellow">
</a>
<br>
<h3>
<br>
Open Source • Privacy-First • Enterprise-Ready
</h3>
<p align="center">
Get latest <a href="https://www.zackriya.com/meetily-subscribe/"><b>Product updates</b></a> <br><br>
<a href="https://meetily.ai"><b>Website</b></a> •
<a href="https://www.linkedin.com/company/106363062/"><b>LinkedIn</b></a> •
<a href="https://discord.gg/crRymMQBFH"><b>Meetily Discord</b></a> •
<a href="https://discord.com/invite/vCFJvN4BwJ"><b>Privacy-First AI</b></a> •
<a href="https://www.reddit.com/r/meetily/"><b>Reddit</b></a>
</p>
<p align="center">
A privacy-first AI meeting assistant that captures, transcribes, and summarizes meetings entirely on your infrastructure. Built by expert AI engineers passionate about data sovereignty and open source solutions. Perfect for enterprises that need advanced meeting intelligence without compromising on privacy, compliance, or control.
</p>
<p align="center">
<img src="docs/meetily_demo.gif" width="650" alt="Meetily Demo" />
<br>
<a href="https://youtu.be/6FnhSC_eSz8">View full Demo Video</a>
</p>
</div>
---
> **🎉 New: Meetily PRO Available** - Looking for enhanced accuracy and advanced features? Check out our professional-grade solution with custom summary templates, advanced exports (PDF, DOCX), auto-meeting detection, built-in GDPR compliance, and many more. **This Community Edition remains forever free & open source**. [Learn more about PRO →](https://meetily.ai/pro/)
---
<details>
<summary>Table of Contents</summary>
- [Introduction](#introduction)
- [Why Meetily?](#why-meetily)
- [Features](#features)
- [Installation](#installation)
- [Key Features in Action](#key-features-in-action)
- [System Architecture](#system-architecture)
- [For Developers](#for-developers)
- [Meetily PRO](#meetily-pro)
- [Contributing](#contributing)
- [License](#license)
</details>
## Introduction
Meetily is a privacy-first AI meeting assistant that runs entirely on your local machine. It captures your meetings, transcribes them in real-time, and generates summaries, all without sending any data to the cloud. This makes it the perfect solution for professionals and enterprises who need to maintain complete control over their sensitive information.
## Why Meetily?
While there are many meeting transcription tools available, this solution stands out by offering:
- **Privacy First:** All processing happens locally on your device.
- **Cost-Effective:** Uses open-source AI models instead of expensive APIs.
- **Flexible:** Works offline and supports multiple meeting platforms.
- **Customizable:** Self-host and modify for your specific needs.
<details>
<summary>The Privacy Problem</summary>
Meeting AI tools create significant privacy and compliance risks across all sectors:
- **$4.4M average cost per data breach** (IBM 2024)
- **€5.88 billion in GDPR fines** issued by 2025
- **400+ unlawful recording cases** filed in California this year
Whether you're a defense consultant, enterprise executive, legal professional, or healthcare provider, your sensitive discussions shouldn't live on servers you don't control. Cloud meeting tools promise convenience but deliver privacy nightmares with unclear data storage practices and potential unauthorized access.
**Meetily solves this:** Complete data sovereignty on your infrastructure, zero vendor lock-in, and full control over your sensitive conversations.
</details>
## Features
- **Local First:** All processing is done on your machine. No data ever leaves your computer.
- **Real-time Transcription:** Get a live transcript of your meeting as it happens.
- **AI-Powered Summaries:** Generate summaries of your meetings using powerful language models.
- **Multi-Platform:** Works on macOS, Windows, and Linux.
- **Open Source:** Meetily is open source and free to use.
- **Flexible AI Provider Support:** Choose from Ollama (local), Claude, Groq, OpenRouter, or use your own OpenAI-compatible endpoint.
## Installation
### 🪟 **Windows**
1. Download the latest `x64-setup.exe` from [Releases](https://github.com/Zackriya-Solutions/meeting-minutes/releases/latest)
2. Run the installer
### 🍎 **macOS**
1. Download `meetily_0.3.0_aarch64.dmg` from [Releases](https://github.com/Zackriya-Solutions/meeting-minutes/releases/latest)
2. Open the downloaded `.dmg` file
3. Drag **Meetily** to your Applications folder
4. Open **Meetily** from Applications folder
### 🐧 **Linux**
Build from source following our detailed guides:
- [Building on Linux](docs/building_in_linux.md)
- [General Build Instructions](docs/BUILDING.md)
**Quick start:**
```bash
git clone https://github.com/Zackriya-Solutions/meeting-minutes
cd meeting-minutes/frontend
pnpm install
./build-gpu.sh
```
## Key Features in Action
### 🎯 Local Transcription
Transcribe meetings entirely on your device using **Whisper** or **Parakeet** models. No cloud required.
<p align="center">
<img src="docs/home.png" width="650" style="border-radius: 10px;" alt="Meetily Demo" />
</p>
### 📥 Import & Enhance `Beta`
Import existing audio files to generate transcripts, or enhance to re-transcribe any recorded meeting with a different model or language, all processed locally.
> Contributed by [Jeremi Joslin](https://github.com/jeremi), improved by [Vishnu P S](https://github.com/p-s-vishnu) and [Mohammed Safvan](https://github.com/mohammedsafvan)
<p align="center">
<img src="docs/meetily-export.gif" width="650" style="border-radius: 10px;" alt="Import and Enhance" />
</p>
### 🤖 AI-Powered Summaries
Generate meeting summaries with your choice of AI provider. **Ollama** (local) is recommended, with support for Claude, Groq, OpenRouter, and OpenAI.
<p align="center">
<img src="docs/summary.png" width="650" style="border-radius: 10px;" alt="Summary generation" />
</p>
<p align="center">
<img src="docs/editor1.png" width="650" style="border-radius: 10px;" alt="Editor Summary generation" />
</p>
### 🔒 Privacy-First Design
All data stays on your machine. Transcription models, recordings, and transcripts are stored locally.
<p align="center">
<img src="docs/settings.png" width="650" style="border-radius: 10px;" alt="Local Transcription and storage" />
</p>
### 🌐 Custom OpenAI Endpoint Support
Use your own OpenAI-compatible endpoint for AI summaries. Perfect for organizations with custom AI infrastructure or preferred providers.
<p align="center">
<img src="docs/custom.png" width="650" style="border-radius: 10px;" alt="Custom OpenAI Endpoint Configuration" />
</p>
### 🎙️ Professional Audio Mixing
Capture microphone and system audio simultaneously with intelligent ducking and clipping prevention.
<p align="center">
<img src="docs/audio.png" width="650" style="border-radius: 10px;" alt="Device selection" />
</p>
### ⚡ GPU Acceleration
Built-in support for hardware acceleration across platforms:
- **macOS**: Apple Silicon (M
gitextract_vm4cqimc/
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ ├── documentation.md
│ │ └── feature_request.md
│ ├── issue_template.md
│ ├── pull_request_template.md
│ └── workflows/
│ ├── ACCELERATION_GUIDE.md
│ ├── README_DEVTEST.md
│ ├── WORKFLOWS_OVERVIEW.md
│ ├── build-devtest.yml
│ ├── build-linux.yml
│ ├── build-macos.yml
│ ├── build-test.yml
│ ├── build-windows.yml
│ ├── build.yml
│ ├── pr-main-check.yml
│ └── release.yml
├── .gitignore
├── .gitmodules
├── BLUETOOTH_PLAYBACK_NOTICE.md
├── CLAUDE.md
├── CONTRIBUTING.md
├── Cargo.toml
├── LICENSE.md
├── PRIVACY_POLICY.md
├── README.md
├── backend/
│ ├── .gitignore
│ ├── API_DOCUMENTATION.md
│ ├── Dockerfile.app
│ ├── Dockerfile.server-cpu
│ ├── Dockerfile.server-gpu
│ ├── Dockerfile.server-macos
│ ├── README.md
│ ├── SCRIPTS_DOCUMENTATION.md
│ ├── app/
│ │ ├── db.py
│ │ ├── main.py
│ │ ├── schema_validator.py
│ │ └── transcript_processor.py
│ ├── build-docker.ps1
│ ├── build-docker.sh
│ ├── build_whisper.cmd
│ ├── build_whisper.sh
│ ├── clean_start_backend.cmd
│ ├── clean_start_backend.sh
│ ├── debug_cors.py
│ ├── docker/
│ │ └── entrypoint.sh
│ ├── docker-compose.yml
│ ├── download-ggml-model.cmd
│ ├── download-ggml-model.sh
│ ├── examples/
│ │ └── run_summary_workflow.py
│ ├── install_dependancies_for_windows.ps1
│ ├── requirements.txt
│ ├── run-docker.ps1
│ ├── run-docker.sh
│ ├── set_env.sh
│ ├── setup-db.ps1
│ ├── setup-db.sh
│ ├── start_python_backend.cmd
│ ├── start_whisper_server.cmd
│ ├── start_with_output.ps1
│ ├── temp.env
│ └── whisper-custom/
│ └── server/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── httplib.h
│ ├── public/
│ │ └── index.html
│ └── server.cpp
├── docs/
│ ├── BUILDING.md
│ ├── GPU_ACCELERATION.md
│ ├── architecture.md
│ └── building_in_linux.md
├── frontend/
│ ├── .gitignore
│ ├── API.md
│ ├── README.md
│ ├── build-gpu.bat
│ ├── build-gpu.ps1
│ ├── build-gpu.sh
│ ├── build.bat
│ ├── build.ps1
│ ├── build_backup.bat
│ ├── clean_build.sh
│ ├── clean_build_windows.bat
│ ├── clean_run.sh
│ ├── clean_run_windows.bat
│ ├── components.json
│ ├── dev-gpu.bat
│ ├── dev-gpu.ps1
│ ├── dev-gpu.sh
│ ├── eslint.config.mjs
│ ├── next.config.js
│ ├── package-app.sh
│ ├── package.json
│ ├── postcss.config.js
│ ├── postcss.config.mjs
│ ├── scripts/
│ │ ├── auto-detect-gpu.js
│ │ ├── load-env.ps1
│ │ └── tauri-auto.js
│ ├── src/
│ │ ├── app/
│ │ │ ├── _components/
│ │ │ │ ├── SettingsModal.tsx
│ │ │ │ ├── StatusOverlays.tsx
│ │ │ │ └── TranscriptPanel.tsx
│ │ │ ├── globals.css
│ │ │ ├── layout.tsx
│ │ │ ├── meeting-details/
│ │ │ │ ├── page-content.tsx
│ │ │ │ └── page.tsx
│ │ │ ├── metadata.ts
│ │ │ ├── metadata.tsx
│ │ │ ├── notes/
│ │ │ │ └── [id]/
│ │ │ │ └── page.tsx
│ │ │ ├── page.tsx
│ │ │ └── settings/
│ │ │ └── page.tsx
│ │ ├── components/
│ │ │ ├── AISummary/
│ │ │ │ ├── Block.tsx
│ │ │ │ ├── BlockNoteSummaryView.tsx
│ │ │ │ ├── Section.tsx
│ │ │ │ └── index.tsx
│ │ │ ├── About.tsx
│ │ │ ├── AnalyticsConsentSwitch.tsx
│ │ │ ├── AnalyticsDataModal.tsx
│ │ │ ├── AnalyticsProvider.tsx
│ │ │ ├── AudioBackendSelector.tsx
│ │ │ ├── AudioLevelMeter.tsx
│ │ │ ├── AudioPlayer.tsx
│ │ │ ├── BetaSettings.tsx
│ │ │ ├── BlockNoteEditor/
│ │ │ │ ├── BasicBlockNoteTest.tsx
│ │ │ │ └── Editor.tsx
│ │ │ ├── BluetoothPlaybackWarning.tsx
│ │ │ ├── BuiltInModelManager.tsx
│ │ │ ├── ChunkProgressDisplay.tsx
│ │ │ ├── ComplianceNotification.tsx
│ │ │ ├── ConfidenceIndicator.tsx
│ │ │ ├── ConfirmationModel/
│ │ │ │ └── confirmation-modal.tsx
│ │ │ ├── ConsoleToggle.tsx
│ │ │ ├── CustomDialog.tsx
│ │ │ ├── DatabaseImport/
│ │ │ │ ├── HomebrewDatabaseDetector.tsx
│ │ │ │ └── LegacyDatabaseImport.tsx
│ │ │ ├── DeviceSelection.tsx
│ │ │ ├── EditableTitle.tsx
│ │ │ ├── EmptyStateSummary.tsx
│ │ │ ├── ImportAudio/
│ │ │ │ ├── ImportAudioDialog.tsx
│ │ │ │ ├── ImportDropOverlay.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Info.tsx
│ │ │ ├── LanguageSelection.tsx
│ │ │ ├── Logo.tsx
│ │ │ ├── MainContent/
│ │ │ │ └── index.tsx
│ │ │ ├── MainNav/
│ │ │ │ └── index.tsx
│ │ │ ├── MeetingDetails/
│ │ │ │ ├── RetranscribeDialog.tsx
│ │ │ │ ├── SummaryGeneratorButtonGroup.tsx
│ │ │ │ ├── SummaryPanel.tsx
│ │ │ │ ├── SummaryUpdaterButtonGroup.tsx
│ │ │ │ ├── TranscriptButtonGroup.tsx
│ │ │ │ └── TranscriptPanel.tsx
│ │ │ ├── MessageToast.tsx
│ │ │ ├── ModelDownloadProgress.tsx
│ │ │ ├── ModelSettingsModal.tsx
│ │ │ ├── ParakeetModelManager.tsx
│ │ │ ├── PermissionWarning.tsx
│ │ │ ├── PreferenceSettings.tsx
│ │ │ ├── RecordingControls.tsx
│ │ │ ├── RecordingSettings.tsx
│ │ │ ├── RecordingStatusBar.tsx
│ │ │ ├── SettingTabs.tsx
│ │ │ ├── Sidebar/
│ │ │ │ ├── SidebarProvider.tsx
│ │ │ │ └── index.tsx
│ │ │ ├── SummaryModelSettings.tsx
│ │ │ ├── TranscriptRecovery/
│ │ │ │ ├── TranscriptRecovery.tsx
│ │ │ │ └── index.ts
│ │ │ ├── TranscriptSettings.tsx
│ │ │ ├── TranscriptView.tsx
│ │ │ ├── UpdateCheckProvider.tsx
│ │ │ ├── UpdateDialog.tsx
│ │ │ ├── UpdateNotification.tsx
│ │ │ ├── VirtualizedTranscriptView.tsx
│ │ │ ├── WhisperModelManager.tsx
│ │ │ ├── molecules/
│ │ │ │ └── form-components/
│ │ │ │ ├── form-input-item.tsx
│ │ │ │ ├── form-input-switch.tsx
│ │ │ │ └── form-select-item.tsx
│ │ │ ├── onboarding/
│ │ │ │ ├── OnboardingContainer.tsx
│ │ │ │ ├── OnboardingFlow.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── shared/
│ │ │ │ │ ├── PermissionRow.tsx
│ │ │ │ │ ├── ProgressIndicator.tsx
│ │ │ │ │ ├── StatusIndicator.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ └── steps/
│ │ │ │ ├── DownloadProgressStep.tsx
│ │ │ │ ├── PermissionsStep.tsx
│ │ │ │ ├── SetupOverviewStep.tsx
│ │ │ │ ├── WelcomeStep.tsx
│ │ │ │ └── index.ts
│ │ │ ├── shared/
│ │ │ │ └── DownloadProgressToast.tsx
│ │ │ └── ui/
│ │ │ ├── accordion.tsx
│ │ │ ├── alert.tsx
│ │ │ ├── button-group.tsx
│ │ │ ├── button.tsx
│ │ │ ├── command.tsx
│ │ │ ├── dialog.tsx
│ │ │ ├── dropdown-menu.tsx
│ │ │ ├── form.tsx
│ │ │ ├── input-group.tsx
│ │ │ ├── input.tsx
│ │ │ ├── label.tsx
│ │ │ ├── popover.tsx
│ │ │ ├── progress.tsx
│ │ │ ├── scroll-area.tsx
│ │ │ ├── select.tsx
│ │ │ ├── separator.tsx
│ │ │ ├── sheet.tsx
│ │ │ ├── switch.tsx
│ │ │ ├── tabs.tsx
│ │ │ ├── textarea.tsx
│ │ │ ├── tooltip.tsx
│ │ │ └── visually-hidden.tsx
│ │ ├── config/
│ │ │ └── api.ts
│ │ ├── constants/
│ │ │ ├── audioFormats.ts
│ │ │ ├── languages.ts
│ │ │ └── modelDefaults.ts
│ │ ├── contexts/
│ │ │ ├── ConfigContext.tsx
│ │ │ ├── ImportDialogContext.tsx
│ │ │ ├── OllamaDownloadContext.tsx
│ │ │ ├── OnboardingContext.tsx
│ │ │ ├── RecordingPostProcessingProvider.tsx
│ │ │ ├── RecordingStateContext.tsx
│ │ │ └── TranscriptContext.tsx
│ │ ├── hooks/
│ │ │ ├── meeting-details/
│ │ │ │ ├── useCopyOperations.ts
│ │ │ │ ├── useMeetingData.ts
│ │ │ │ ├── useMeetingOperations.ts
│ │ │ │ ├── useModelConfiguration.ts
│ │ │ │ ├── useSummaryGeneration.ts
│ │ │ │ └── useTemplates.ts
│ │ │ ├── useAudioPlayer.ts
│ │ │ ├── useAutoScroll.ts
│ │ │ ├── useImportAudio.ts
│ │ │ ├── useModalState.ts
│ │ │ ├── useNavigation.ts
│ │ │ ├── usePaginatedTranscripts.ts
│ │ │ ├── usePermissionCheck.ts
│ │ │ ├── usePlatform.ts
│ │ │ ├── useProcessingProgress.ts
│ │ │ ├── useRecordingStart.ts
│ │ │ ├── useRecordingStateSync.ts
│ │ │ ├── useRecordingStop.ts
│ │ │ ├── useTranscriptRecovery.ts
│ │ │ ├── useTranscriptStreaming.ts
│ │ │ ├── useTranscriptionModels.ts
│ │ │ └── useUpdateCheck.ts
│ │ ├── lib/
│ │ │ ├── analytics.ts
│ │ │ ├── builtin-ai.ts
│ │ │ ├── parakeet.ts
│ │ │ ├── recordingNotification.tsx
│ │ │ ├── utils.ts
│ │ │ └── whisper.ts
│ │ ├── services/
│ │ │ ├── configService.ts
│ │ │ ├── indexedDBService.ts
│ │ │ ├── recordingService.ts
│ │ │ ├── storageService.ts
│ │ │ ├── transcriptService.ts
│ │ │ └── updateService.ts
│ │ └── types/
│ │ ├── betaFeatures.ts
│ │ ├── index.ts
│ │ ├── onboarding.ts
│ │ └── summary.ts
│ ├── src-tauri/
│ │ ├── .cargo/
│ │ │ └── config.toml
│ │ ├── .gitignore
│ │ ├── CLEANUP_PLAN.md
│ │ ├── Cargo.toml
│ │ ├── Info.plist
│ │ ├── LOGGING_OPTIMIZATIONS.md
│ │ ├── NOTIFICATION_TESTING.md
│ │ ├── build/
│ │ │ └── ffmpeg.rs
│ │ ├── build.rs
│ │ ├── check_screen_permission.swift
│ │ ├── config/
│ │ │ └── backend_config.json
│ │ ├── entitlements.plist
│ │ ├── icons/
│ │ │ ├── app_icon.icns
│ │ │ └── icon.icns
│ │ ├── migrations/
│ │ │ ├── 20250916100000_initial_schema.sql
│ │ │ ├── 20250920155811_add_openrouter_api_key.sql
│ │ │ ├── 20251006000000_add_audio_sync_fields.sql
│ │ │ ├── 20251010153942_add_ollama_endpoint.sql
│ │ │ ├── 20251101000000_add_summary_backup.sql
│ │ │ ├── 20251105120000_add_pro_license_custom_openai.sql
│ │ │ ├── 20251110000000_add_grace_period_to_licensing.sql
│ │ │ ├── 20251110000001_add_speaker_field.sql
│ │ │ ├── 20251223000000_add_meeting_notes.sql
│ │ │ └── 20251229000000_add_gemini_api_key.sql
│ │ ├── scripts/
│ │ │ └── sign-windows.ps1
│ │ ├── src/
│ │ │ ├── analytics/
│ │ │ │ ├── analytics.rs
│ │ │ │ ├── commands.rs
│ │ │ │ └── mod.rs
│ │ │ ├── anthropic/
│ │ │ │ ├── anthropic.rs
│ │ │ │ └── mod.rs
│ │ │ ├── api/
│ │ │ │ ├── api.rs
│ │ │ │ ├── commands.rs
│ │ │ │ └── mod.rs
│ │ │ ├── audio/
│ │ │ │ ├── async_logger.rs
│ │ │ │ ├── audio_processing.rs
│ │ │ │ ├── batch_processor.rs
│ │ │ │ ├── buffer_pool.rs
│ │ │ │ ├── capture/
│ │ │ │ │ ├── backend_config.rs
│ │ │ │ │ ├── core_audio.rs
│ │ │ │ │ ├── microphone.rs
│ │ │ │ │ ├── mod.rs
│ │ │ │ │ └── system.rs
│ │ │ │ ├── common.rs
│ │ │ │ ├── constants.rs
│ │ │ │ ├── core-old.rs
│ │ │ │ ├── decoder.rs
│ │ │ │ ├── device_detection.rs
│ │ │ │ ├── device_monitor.rs
│ │ │ │ ├── devices/
│ │ │ │ │ ├── configuration.rs
│ │ │ │ │ ├── discovery.rs
│ │ │ │ │ ├── fallback.rs
│ │ │ │ │ ├── microphone.rs
│ │ │ │ │ ├── mod.rs
│ │ │ │ │ ├── platform/
│ │ │ │ │ │ ├── linux.rs
│ │ │ │ │ │ ├── macos.rs
│ │ │ │ │ │ ├── mod.rs
│ │ │ │ │ │ └── windows.rs
│ │ │ │ │ └── speakers.rs
│ │ │ │ ├── diagnostics.rs
│ │ │ │ ├── encode.rs
│ │ │ │ ├── ffmpeg.rs
│ │ │ │ ├── ffmpeg_mixer.rs
│ │ │ │ ├── hardware_detector.rs
│ │ │ │ ├── import.rs
│ │ │ │ ├── incremental_saver.rs
│ │ │ │ ├── level_monitor.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── permissions.rs
│ │ │ │ ├── pipeline.rs
│ │ │ │ ├── playback_monitor.rs
│ │ │ │ ├── post_processor.rs
│ │ │ │ ├── recording_commands.rs
│ │ │ │ ├── recording_commands.rs.backup
│ │ │ │ ├── recording_manager.rs
│ │ │ │ ├── recording_preferences.rs
│ │ │ │ ├── recording_saver.rs
│ │ │ │ ├── recording_saver_old.rs
│ │ │ │ ├── recording_state.rs
│ │ │ │ ├── retranscription.rs
│ │ │ │ ├── simple_level_monitor.rs
│ │ │ │ ├── stream.rs
│ │ │ │ ├── stt.rs
│ │ │ │ ├── system_audio_commands.rs
│ │ │ │ ├── system_audio_stream.rs
│ │ │ │ ├── system_audio_types.ts
│ │ │ │ ├── system_detector.rs
│ │ │ │ ├── transcription/
│ │ │ │ │ ├── engine.rs
│ │ │ │ │ ├── mod.rs
│ │ │ │ │ ├── parakeet_provider.rs
│ │ │ │ │ ├── provider.rs
│ │ │ │ │ ├── whisper_provider.rs
│ │ │ │ │ └── worker.rs
│ │ │ │ └── vad.rs
│ │ │ ├── audio_v2/
│ │ │ │ ├── compatibility.rs
│ │ │ │ ├── lib.rs
│ │ │ │ ├── limiter.rs
│ │ │ │ ├── mixer.rs
│ │ │ │ ├── normalizer.rs
│ │ │ │ ├── recorder.rs
│ │ │ │ ├── resampler.rs
│ │ │ │ ├── stream.rs
│ │ │ │ └── sync.rs
│ │ │ ├── config.rs
│ │ │ ├── console_utils/
│ │ │ │ ├── commands.rs
│ │ │ │ ├── console_utils.rs
│ │ │ │ └── mod.rs
│ │ │ ├── database/
│ │ │ │ ├── commands.rs
│ │ │ │ ├── manager.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── models.rs
│ │ │ │ ├── repositories/
│ │ │ │ │ ├── meeting.rs
│ │ │ │ │ ├── mod.rs
│ │ │ │ │ ├── setting.rs
│ │ │ │ │ ├── summary.rs
│ │ │ │ │ ├── transcript.rs
│ │ │ │ │ └── transcript_chunk.rs
│ │ │ │ └── setup.rs
│ │ │ ├── groq/
│ │ │ │ ├── groq.rs
│ │ │ │ └── mod.rs
│ │ │ ├── lib.rs
│ │ │ ├── lib_old_complex.rs
│ │ │ ├── main.rs
│ │ │ ├── notifications/
│ │ │ │ ├── commands.rs
│ │ │ │ ├── manager.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── settings.rs
│ │ │ │ ├── system.rs
│ │ │ │ └── types.rs
│ │ │ ├── ollama/
│ │ │ │ ├── commands.rs
│ │ │ │ ├── metadata.rs
│ │ │ │ ├── mod.rs
│ │ │ │ └── ollama.rs
│ │ │ ├── onboarding.rs
│ │ │ ├── openai/
│ │ │ │ ├── mod.rs
│ │ │ │ └── openai.rs
│ │ │ ├── openrouter/
│ │ │ │ ├── commands.rs
│ │ │ │ ├── mod.rs
│ │ │ │ └── openrouter.rs
│ │ │ ├── parakeet_engine/
│ │ │ │ ├── commands.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── model.rs
│ │ │ │ └── parakeet_engine.rs
│ │ │ ├── state.rs
│ │ │ ├── summary/
│ │ │ │ ├── commands.rs
│ │ │ │ ├── llm_client.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── processor.rs
│ │ │ │ ├── service.rs
│ │ │ │ ├── summary_engine/
│ │ │ │ │ ├── client.rs
│ │ │ │ │ ├── commands.rs
│ │ │ │ │ ├── mod.rs
│ │ │ │ │ ├── model_manager.rs
│ │ │ │ │ ├── models.rs
│ │ │ │ │ └── sidecar.rs
│ │ │ │ ├── template_commands.rs
│ │ │ │ └── templates/
│ │ │ │ ├── defaults.rs
│ │ │ │ ├── loader.rs
│ │ │ │ ├── mod.rs
│ │ │ │ └── types.rs
│ │ │ ├── tray.rs
│ │ │ ├── utils.rs
│ │ │ └── whisper_engine/
│ │ │ ├── _stderr_suppressor.rs
│ │ │ ├── commands.rs
│ │ │ ├── mod.rs
│ │ │ ├── parallel_commands.rs
│ │ │ ├── parallel_processor.rs
│ │ │ ├── system_monitor.rs
│ │ │ └── whisper_engine.rs
│ │ ├── tauri.conf.json
│ │ └── templates/
│ │ ├── README.md
│ │ ├── daily_standup.json
│ │ ├── project_sync.json
│ │ ├── psychatric_session.json
│ │ ├── retrospective.json
│ │ ├── sales_marketing_client_call.json
│ │ └── standard_meeting.json
│ ├── tailwind.config.js
│ ├── tailwind.config.ts
│ └── tsconfig.json
├── llama-helper/
│ ├── Cargo.toml
│ └── src/
│ └── main.rs
└── scripts/
├── generate-update-manifest-github.js
├── inject_transcript.py
└── test-update-locally.js
Showing preview only (230K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (2454 symbols across 270 files)
FILE: backend/app/db.py
class DatabaseManager (line 20) | class DatabaseManager:
method __init__ (line 21) | def __init__(self, db_path: str = None):
method _init_db (line 28) | def _init_db(self):
method _legacy_init_db (line 45) | def _legacy_init_db(self):
method _get_connection (line 162) | async def _get_connection(self):
method create_process (line 170) | async def create_process(self, meeting_id: str) -> str:
method update_process (line 211) | async def update_process(self, meeting_id: str, status: str, result: O...
method save_transcript (line 283) | async def save_transcript(self, meeting_id: str, transcript_text: str,...
method update_meeting_name (line 329) | async def update_meeting_name(self, meeting_id: str, meeting_name: str):
method get_transcript_data (line 349) | async def get_transcript_data(self, meeting_id: str):
method save_meeting (line 363) | async def save_meeting(self, meeting_id: str, title: str, folder_path:...
method save_meeting_transcript (line 389) | async def save_meeting_transcript(self, meeting_id: str, transcript: s...
method get_meeting (line 412) | async def get_meeting(self, meeting_id: str):
method update_meeting_title (line 454) | async def update_meeting_title(self, meeting_id: str, new_title: str):
method get_all_meetings (line 465) | async def get_all_meetings(self):
method delete_meeting (line 480) | async def delete_meeting(self, meeting_id: str):
method get_model_config (line 530) | async def get_model_config(self):
method save_model_config (line 537) | async def save_model_config(self, provider: str, model: str, whisperMo...
method save_api_key (line 582) | async def save_api_key(self, api_key: str, provider: str):
method get_api_key (line 627) | async def get_api_key(self, provider: str):
method get_transcript_config (line 645) | async def get_transcript_config(self):
method save_transcript_config (line 659) | async def save_transcript_config(self, provider: str, model: str):
method save_transcript_api_key (line 701) | async def save_transcript_api_key(self, api_key: str, provider: str):
method get_transcript_api_key (line 749) | async def get_transcript_api_key(self, provider: str):
method search_transcripts (line 769) | async def search_transcripts(self, query: str):
method delete_api_key (line 863) | async def delete_api_key(self, provider: str):
method update_meeting_summary (line 880) | async def update_meeting_summary(self, meeting_id: str, summary: dict):
FILE: backend/app/main.py
class Transcript (line 57) | class Transcript(BaseModel):
class MeetingResponse (line 66) | class MeetingResponse(BaseModel):
class MeetingDetailsResponse (line 70) | class MeetingDetailsResponse(BaseModel):
class MeetingTitleUpdate (line 77) | class MeetingTitleUpdate(BaseModel):
class DeleteMeetingRequest (line 81) | class DeleteMeetingRequest(BaseModel):
class SaveTranscriptRequest (line 84) | class SaveTranscriptRequest(BaseModel):
class SaveModelConfigRequest (line 89) | class SaveModelConfigRequest(BaseModel):
class SaveTranscriptConfigRequest (line 95) | class SaveTranscriptConfigRequest(BaseModel):
class TranscriptRequest (line 100) | class TranscriptRequest(BaseModel):
class SummaryProcessor (line 110) | class SummaryProcessor:
method __init__ (line 112) | def __init__(self):
method process_transcript (line 123) | async def process_transcript(self, text: str, model: str, model_name: ...
method cleanup (line 158) | def cleanup(self):
function get_meetings (line 173) | async def get_meetings():
function get_meeting (line 183) | async def get_meeting(meeting_id: str):
function save_meeting_title (line 197) | async def save_meeting_title(data: MeetingTitleUpdate):
function delete_meeting (line 207) | async def delete_meeting(data: DeleteMeetingRequest):
function process_transcript_background (line 219) | async def process_transcript_background(process_id: str, transcript: Tra...
function process_transcript_api (line 330) | async def process_transcript_api(
function get_summary (line 369) | async def get_summary(meeting_id: str):
function save_transcript (line 512) | async def save_transcript(request: SaveTranscriptRequest):
function get_model_config (line 551) | async def get_model_config():
function save_model_config (line 561) | async def save_model_config(request: SaveModelConfigRequest):
function get_transcript_config (line 569) | async def get_transcript_config():
function save_transcript_config (line 579) | async def save_transcript_config(request: SaveTranscriptConfigRequest):
class GetApiKeyRequest (line 586) | class GetApiKeyRequest(BaseModel):
function get_api_key (line 590) | async def get_api_key(request: GetApiKeyRequest):
function get_transcript_api_key (line 597) | async def get_transcript_api_key(request: GetApiKeyRequest):
class MeetingSummaryUpdate (line 603) | class MeetingSummaryUpdate(BaseModel):
function save_meeting_summary (line 608) | async def save_meeting_summary(data: MeetingSummaryUpdate):
class SearchRequest (line 620) | class SearchRequest(BaseModel):
function search_transcripts (line 624) | async def search_transcripts(request: SearchRequest):
function shutdown_event (line 634) | async def shutdown_event():
FILE: backend/app/schema_validator.py
class SchemaValidator (line 7) | class SchemaValidator:
method __init__ (line 10) | def __init__(self, db_path: str):
method validate_schema (line 13) | def validate_schema(self):
method _get_expected_schema (line 30) | def _get_expected_schema(self):
method _validate_table_schema (line 94) | def _validate_table_schema(self, cursor, table_name: str, expected_col...
FILE: backend/app/transcript_processor.py
class Block (line 34) | class Block(BaseModel):
class Section (line 52) | class Section(BaseModel):
class MeetingNotes (line 57) | class MeetingNotes(BaseModel):
class People (line 62) | class People(BaseModel):
class SummaryResponse (line 67) | class SummaryResponse(BaseModel):
class TranscriptProcessor (line 80) | class TranscriptProcessor:
method __init__ (line 82) | def __init__(self):
method process_transcript (line 87) | async def process_transcript(self, text: str, model: str, model_name: ...
method chat_ollama_model (line 235) | async def chat_ollama_model(self, model_name: str, transcript: str, cu...
method cleanup (line 289) | def cleanup(self):
FILE: backend/debug_cors.py
function test_process_transcript (line 8) | def test_process_transcript(text="This is a test transcript"):
FILE: backend/examples/run_summary_workflow.py
function process_transcript (line 24) | def process_transcript(base_url, transcript_text, provider, model_name, ...
function poll_summary_status (line 69) | def poll_summary_status(base_url, meeting_id_for_polling, interval, max_...
FILE: backend/whisper-custom/server/httplib.h
function namespace (line 292) | namespace httplib {
function scope_exit (line 330) | struct scope_exit {
function release (line 344) | void release() { this->execute_on_destruction = false; }
type Response (line 364) | struct Response
type MultipartFormData (line 367) | struct MultipartFormData {
function class (line 376) | class DataSink {
type MultipartFormDataProvider (line 416) | struct MultipartFormDataProvider {
function class (line 434) | class ContentReader {
function const (line 449) | bool operator()(ContentReceiver receiver) const {
type Request (line 460) | struct Request {
type Response (line 512) | struct Response {
function class (line 561) | class Stream {
function ContentProviderWithoutLength (line 7336) | inline ContentProviderWithoutLength ClientImpl::get_multipart_content_pr...
function process_socket (line 7378) | inline bool
function Result (line 7388) | inline Result ClientImpl::Get(const std::string &path) {
function Result (line 7392) | inline Result ClientImpl::Get(const std::string &path, Progress progress) {
function Result (line 7396) | inline Result ClientImpl::Get(const std::string &path, const Headers &he...
function Result (line 7400) | inline Result ClientImpl::Get(const std::string &path, const Headers &he...
function Result (line 7411) | inline Result ClientImpl::Get(const std::string &path,
function Result (line 7416) | inline Result ClientImpl::Get(const std::string &path,
function Result (line 7423) | inline Result ClientImpl::Get(const std::string &path, const Headers &he...
function Result (line 7428) | inline Result ClientImpl::Get(const std::string &path, const Headers &he...
function Result (line 7435) | inline Result ClientImpl::Get(const std::string &path,
function Result (line 7442) | inline Result ClientImpl::Get(const std::string &path, const Headers &he...
function Result (line 7449) | inline Result ClientImpl::Get(const std::string &path,
function Result (line 7457) | inline Result ClientImpl::Get(const std::string &path, const Headers &he...
function Result (line 7476) | inline Result ClientImpl::Get(const std::string &path, const Params &par...
function Result (line 7484) | inline Result ClientImpl::Get(const std::string &path, const Params &par...
function Result (line 7491) | inline Result ClientImpl::Get(const std::string &path, const Params &par...
function Result (line 7505) | inline Result ClientImpl::Head(const std::string &path) {
function Result (line 7509) | inline Result ClientImpl::Head(const std::string &path,
function Result (line 7519) | inline Result ClientImpl::Post(const std::string &path) {
function Result (line 7523) | inline Result ClientImpl::Post(const std::string &path,
function Result (line 7528) | inline Result ClientImpl::Post(const std::string &path, const char *body,
function Result (line 7534) | inline Result ClientImpl::Post(const std::string &path, const Headers &h...
function Result (line 7541) | inline Result ClientImpl::Post(const std::string &path, const std::strin...
function Result (line 7546) | inline Result ClientImpl::Post(const std::string &path, const Headers &h...
function Result (line 7554) | inline Result ClientImpl::Post(const std::string &path, const Params &pa...
function Result (line 7558) | inline Result ClientImpl::Post(const std::string &path, size_t content_l...
function Result (line 7565) | inline Result ClientImpl::Post(const std::string &path,
function Result (line 7571) | inline Result ClientImpl::Post(const std::string &path, const Headers &h...
function Result (line 7580) | inline Result ClientImpl::Post(const std::string &path, const Headers &h...
function Result (line 7587) | inline Result ClientImpl::Post(const std::string &path, const Headers &h...
function Result (line 7593) | inline Result ClientImpl::Post(const std::string &path,
function Result (line 7598) | inline Result ClientImpl::Post(const std::string &path, const Headers &h...
function Result (line 7607) | inline Result ClientImpl::Post(const std::string &path, const Headers &h...
function Result (line 7620) | inline Result
function Result (line 7633) | inline Result ClientImpl::Put(const std::string &path) {
function Result (line 7637) | inline Result ClientImpl::Put(const std::string &path, const char *body,
function Result (line 7643) | inline Result ClientImpl::Put(const std::string &path, const Headers &he...
function Result (line 7650) | inline Result ClientImpl::Put(const std::string &path, const std::string...
function Result (line 7655) | inline Result ClientImpl::Put(const std::string &path, const Headers &he...
function Result (line 7663) | inline Result ClientImpl::Put(const std::string &path, size_t content_le...
function Result (line 7670) | inline Result ClientImpl::Put(const std::string &path,
function Result (line 7676) | inline Result ClientImpl::Put(const std::string &path, const Headers &he...
function Result (line 7685) | inline Result ClientImpl::Put(const std::string &path, const Headers &he...
function Result (line 7692) | inline Result ClientImpl::Put(const std::string &path, const Params &par...
function Result (line 7696) | inline Result ClientImpl::Put(const std::string &path, const Headers &he...
function Result (line 7702) | inline Result ClientImpl::Put(const std::string &path,
function Result (line 7707) | inline Result ClientImpl::Put(const std::string &path, const Headers &he...
function Result (line 7716) | inline Result ClientImpl::Put(const std::string &path, const Headers &he...
function Result (line 7729) | inline Result
function Result (line 7741) | inline Result ClientImpl::Patch(const std::string &path) {
function Result (line 7745) | inline Result ClientImpl::Patch(const std::string &path, const char *body,
function Result (line 7751) | inline Result ClientImpl::Patch(const std::string &path, const Headers &...
function Result (line 7759) | inline Result ClientImpl::Patch(const std::string &path,
function Result (line 7765) | inline Result ClientImpl::Patch(const std::string &path, const Headers &...
function Result (line 7773) | inline Result ClientImpl::Patch(const std::string &path, size_t content_...
function Result (line 7780) | inline Result ClientImpl::Patch(const std::string &path,
function Result (line 7786) | inline Result ClientImpl::Patch(const std::string &path, const Headers &...
function Result (line 7795) | inline Result ClientImpl::Patch(const std::string &path, const Headers &...
function Result (line 7802) | inline Result ClientImpl::Delete(const std::string &path) {
function Result (line 7806) | inline Result ClientImpl::Delete(const std::string &path,
function Result (line 7811) | inline Result ClientImpl::Delete(const std::string &path, const char *body,
function Result (line 7817) | inline Result ClientImpl::Delete(const std::string &path,
function Result (line 7832) | inline Result ClientImpl::Delete(const std::string &path,
function Result (line 7838) | inline Result ClientImpl::Delete(const std::string &path,
function Result (line 7845) | inline Result ClientImpl::Options(const std::string &path) {
function Result (line 7849) | inline Result ClientImpl::Options(const std::string &path,
function stop (line 7859) | inline void ClientImpl::stop() {
function set_connection_timeout (line 7893) | inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) {
function set_read_timeout (line 7898) | inline void ClientImpl::set_read_timeout(time_t sec, time_t usec) {
function set_write_timeout (line 7903) | inline void ClientImpl::set_write_timeout(time_t sec, time_t usec) {
function set_basic_auth (line 7908) | inline void ClientImpl::set_basic_auth(const std::string &username,
function set_bearer_token_auth (line 7914) | inline void ClientImpl::set_bearer_token_auth(const std::string &token) {
function set_digest_auth (line 7919) | inline void ClientImpl::set_digest_auth(const std::string &username,
function set_keep_alive (line 7926) | inline void ClientImpl::set_keep_alive(bool on) { keep_alive_ = on; }
function set_follow_location (line 7928) | inline void ClientImpl::set_follow_location(bool on) { follow_location_ ...
function set_url_encode (line 7930) | inline void ClientImpl::set_url_encode(bool on) { url_encode_ = on; }
function set_hostname_addr_map (line 7932) | inline void
function set_default_headers (line 7937) | inline void ClientImpl::set_default_headers(Headers headers) {
function set_header_writer (line 7941) | inline void ClientImpl::set_header_writer(
function set_address_family (line 7946) | inline void ClientImpl::set_address_family(int family) {
function set_tcp_nodelay (line 7950) | inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
function set_socket_options (line 7952) | inline void ClientImpl::set_socket_options(SocketOptions socket_options) {
function set_compress (line 7956) | inline void ClientImpl::set_compress(bool on) { compress_ = on; }
function set_decompress (line 7958) | inline void ClientImpl::set_decompress(bool on) { decompress_ = on; }
function set_interface (line 7960) | inline void ClientImpl::set_interface(const std::string &intf) {
function set_proxy (line 7964) | inline void ClientImpl::set_proxy(const std::string &host, int port) {
function set_proxy_basic_auth (line 7969) | inline void ClientImpl::set_proxy_basic_auth(const std::string &username,
function set_proxy_bearer_token_auth (line 7975) | inline void ClientImpl::set_proxy_bearer_token_auth(const std::string &t...
function set_proxy_digest_auth (line 7980) | inline void ClientImpl::set_proxy_digest_auth(const std::string &username,
function set_ca_cert_path (line 7986) | inline void ClientImpl::set_ca_cert_path(const std::string &ca_cert_file...
function set_ca_cert_store (line 7992) | inline void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) {
function X509_STORE (line 7998) | inline X509_STORE *ClientImpl::create_ca_cert_store(const char *ca_cert,
function enable_server_certificate_verification (line 8025) | inline void ClientImpl::enable_server_certificate_verification(bool enab...
function set_logger (line 8030) | inline void ClientImpl::set_logger(Logger logger) {
function namespace (line 8038) | namespace detail {
function SSLSocketStream (line 8151) | inline SSLSocketStream::~SSLSocketStream() {}
function is_readable (line 8153) | inline bool SSLSocketStream::is_readable() const {
function write (line 8194) | inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) {
function SSLServer (line 8242) | inline SSLServer::SSLServer(const char *cert_path, const char *private_k...
function SSLServer (line 8277) | inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key,
function SSLServer (line 8301) | inline SSLServer::SSLServer(
function SSLServer (line 8312) | inline SSLServer::~SSLServer() {
function SSL_CTX (line 8318) | inline SSL_CTX *SSLServer::ssl_context() const { return ctx_; }
function process_and_close_socket (line 8320) | inline bool SSLServer::process_and_close_socket(socket_t sock) {
function SSLClient (line 8353) | inline SSLClient::SSLClient(const std::string &host)
function SSLClient (line 8356) | inline SSLClient::SSLClient(const std::string &host, int port)
function SSLClient (line 8359) | inline SSLClient::SSLClient(const std::string &host, int port,
function SSLClient (line 8381) | inline SSLClient::SSLClient(const std::string &host, int port,
function SSLClient (line 8400) | inline SSLClient::~SSLClient() {
function set_ca_cert_store (line 8410) | inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) {
function load_ca_cert_store (line 8423) | inline void SSLClient::load_ca_cert_store(const char *ca_cert,
function SSL_CTX (line 8432) | inline SSL_CTX *SSLClient::ssl_context() const { return ctx_; }
function create_and_connect_socket (line 8434) | inline bool SSLClient::create_and_connect_socket(Socket &socket, Error &...
function connect_with_proxy (line 8439) | inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
function load_certs (line 8507) | inline bool SSLClient::load_certs() {
function initialize_ssl (line 8539) | inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
function shutdown_ssl (line 8602) | inline void SSLClient::shutdown_ssl(Socket &socket, bool shutdown_gracef...
function shutdown_ssl_impl (line 8606) | inline void SSLClient::shutdown_ssl_impl(Socket &socket,
function process_socket (line 8619) | inline bool
function verify_host (line 8630) | inline bool SSLClient::verify_host(X509 *server_cert) const {
function verify_host_with_subject_alt_name (line 8656) | inline bool
function verify_host_with_common_name (line 8713) | inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) c...
function check_host_name (line 8729) | inline bool SSLClient::check_host_name(const char *pattern,
FILE: backend/whisper-custom/server/server.cpp
type server_params (line 32) | struct server_params
type whisper_params (line 46) | struct whisper_params {
function whisper_print_usage (line 97) | void whisper_print_usage(int /*argc*/, char ** argv, const whisper_param...
function whisper_params_parse (line 146) | bool whisper_params_parse(int argc, char ** argv, whisper_params & param...
type whisper_print_user_data (line 207) | struct whisper_print_user_data {
function check_ffmpeg_availibility (line 214) | void check_ffmpeg_availibility() {
function convert_to_wav (line 227) | bool convert_to_wav(const std::string & temp_filename, std::string & err...
function estimate_diarization_speaker (line 253) | std::string estimate_diarization_speaker(std::vector<std::vector<float>>...
function whisper_print_progress_callback (line 286) | void whisper_print_progress_callback(struct whisper_context * /*ctx*/, s...
function whisper_print_segment_callback (line 295) | void whisper_print_segment_callback(struct whisper_context * ctx, struct...
function output_str (line 363) | std::string output_str(struct whisper_context * ctx, const whisper_param...
function parse_str_to_bool (line 382) | bool parse_str_to_bool(const std::string & s) {
function get_req_parameters (line 389) | void get_req_parameters(const Request & req, whisper_params & params)
function main (line 495) | int main(int argc, char ** argv) {
FILE: frontend/scripts/auto-detect-gpu.js
function commandExists (line 10) | function commandExists(cmd) {
function detectGPU (line 19) | function detectGPU() {
FILE: frontend/src-tauri/build.rs
function main (line 4) | fn main() {
function detect_and_report_gpu_capabilities (line 25) | fn detect_and_report_gpu_capabilities() {
FILE: frontend/src-tauri/build/ffmpeg.rs
function ensure_ffmpeg_binary (line 8) | pub fn ensure_ffmpeg_binary() {
function download_and_extract_ffmpeg (line 63) | fn download_and_extract_ffmpeg(
function get_ffmpeg_url_for_target (line 125) | fn get_ffmpeg_url_for_target(target: &str) -> Result<String, String> {
function extract_ffmpeg_from_archive (line 154) | fn extract_ffmpeg_from_archive(
function extract_zip (line 206) | fn extract_zip(
function extract_tar_xz (line 265) | fn extract_tar_xz(
function find_ffmpeg_in_extracted_dir (line 284) | fn find_ffmpeg_in_extracted_dir(
function verify_ffmpeg_binary (line 333) | fn verify_ffmpeg_binary(path: &std::path::PathBuf) -> bool {
FILE: frontend/src-tauri/migrations/20250916100000_initial_schema.sql
type meetings (line 2) | CREATE TABLE IF NOT EXISTS meetings (
type transcripts (line 10) | CREATE TABLE IF NOT EXISTS transcripts (
type summary_processes (line 22) | CREATE TABLE IF NOT EXISTS summary_processes (
type transcript_chunks (line 38) | CREATE TABLE IF NOT EXISTS transcript_chunks (
type settings (line 51) | CREATE TABLE IF NOT EXISTS settings (
type transcript_settings (line 63) | CREATE TABLE IF NOT EXISTS transcript_settings (
FILE: frontend/src-tauri/migrations/20250920155811_add_openrouter_api_key.sql
type settings_new (line 5) | CREATE TABLE IF NOT EXISTS settings_new (
FILE: frontend/src-tauri/migrations/20251105120000_add_pro_license_custom_openai.sql
type licensing (line 9) | CREATE TABLE licensing (
FILE: frontend/src-tauri/migrations/20251223000000_add_meeting_notes.sql
type meeting_notes (line 2) | CREATE TABLE IF NOT EXISTS meeting_notes (
type idx_meeting_notes_meeting_id (line 12) | CREATE INDEX IF NOT EXISTS idx_meeting_notes_meeting_id ON meeting_notes...
FILE: frontend/src-tauri/src/analytics/analytics.rs
type AnalyticsConfig (line 10) | pub struct AnalyticsConfig {
method default (line 17) | fn default() -> Self {
type UserSession (line 27) | pub struct UserSession {
method new (line 35) | pub fn new(user_id: String) -> Self {
method duration_seconds (line 45) | pub fn duration_seconds(&self) -> i64 {
type AnalyticsClient (line 50) | pub struct AnalyticsClient {
method new (line 58) | pub async fn new(config: AnalyticsConfig) -> Self {
method identify (line 73) | pub async fn identify(&self, user_id: String, properties: Option<HashM...
method track_event (line 100) | pub async fn track_event(&self, event_name: &str, properties: Option<H...
method start_session (line 144) | pub async fn start_session(&self, user_id: String) -> Result<String, S...
method end_session (line 159) | pub async fn end_session(&self) -> Result<(), String> {
method track_daily_active_user (line 176) | pub async fn track_daily_active_user(&self) -> Result<(), String> {
method track_user_first_launch (line 193) | pub async fn track_user_first_launch(&self) -> Result<(), String> {
method get_current_session (line 201) | pub async fn get_current_session(&self) -> Option<UserSession> {
method is_session_active (line 205) | pub async fn is_session_active(&self) -> bool {
method track_meeting_started (line 210) | pub async fn track_meeting_started(&self, meeting_id: &str, meeting_ti...
method track_recording_started (line 219) | pub async fn track_recording_started(&self, meeting_id: &str) -> Resul...
method track_recording_stopped (line 227) | pub async fn track_recording_stopped(&self, meeting_id: &str, duration...
method track_meeting_deleted (line 239) | pub async fn track_meeting_deleted(&self, meeting_id: &str) -> Result<...
method track_settings_changed (line 247) | pub async fn track_settings_changed(&self, setting_type: &str, new_val...
method track_app_started (line 256) | pub async fn track_app_started(&self, version: &str) -> Result<(), Str...
method track_feature_used (line 264) | pub async fn track_feature_used(&self, feature_name: &str) -> Result<(...
method track_summary_generation_started (line 273) | pub async fn track_summary_generation_started(&self, model_provider: &...
method track_summary_generation_completed (line 283) | pub async fn track_summary_generation_completed(&self, model_provider:...
method track_summary_regenerated (line 301) | pub async fn track_summary_regenerated(&self, model_provider: &str, mo...
method track_model_changed (line 310) | pub async fn track_model_changed(&self, old_provider: &str, old_model:...
method track_custom_prompt_used (line 321) | pub async fn track_custom_prompt_used(&self, prompt_length: usize) -> ...
method track_meeting_ended (line 329) | pub async fn track_meeting_ended(
method track_analytics_enabled (line 375) | pub async fn track_analytics_enabled(&self) -> Result<(), String> {
method track_analytics_disabled (line 382) | pub async fn track_analytics_disabled(&self) -> Result<(), String> {
method track_analytics_transparency_viewed (line 389) | pub async fn track_analytics_transparency_viewed(&self) -> Result<(), ...
method is_enabled (line 396) | pub fn is_enabled(&self) -> bool {
method set_user_properties (line 400) | pub async fn set_user_properties(&self, properties: HashMap<String, St...
function create_analytics_client (line 432) | pub async fn create_analytics_client(config: AnalyticsConfig) -> Analyti...
FILE: frontend/src-tauri/src/analytics/commands.rs
function init_analytics (line 10) | pub async fn init_analytics() -> Result<(), String> {
function disable_analytics (line 26) | pub async fn disable_analytics() -> Result<(), String> {
function track_event (line 33) | pub async fn track_event(event_name: String, properties: Option<HashMap<...
function identify_user (line 47) | pub async fn identify_user(user_id: String, properties: Option<HashMap<S...
function track_meeting_started (line 61) | pub async fn track_meeting_started(meeting_id: String, meeting_title: St...
function track_recording_started (line 75) | pub async fn track_recording_started(meeting_id: String) -> Result<(), S...
function track_recording_stopped (line 89) | pub async fn track_recording_stopped(meeting_id: String, duration_second...
function track_meeting_deleted (line 103) | pub async fn track_meeting_deleted(meeting_id: String) -> Result<(), Str...
function track_settings_changed (line 117) | pub async fn track_settings_changed(setting_type: String, new_value: Str...
function track_feature_used (line 131) | pub async fn track_feature_used(feature_name: String) -> Result<(), Stri...
function is_analytics_enabled (line 145) | pub async fn is_analytics_enabled() -> bool {
function start_analytics_session (line 152) | pub async fn start_analytics_session(user_id: String) -> Result<String, ...
function end_analytics_session (line 166) | pub async fn end_analytics_session() -> Result<(), String> {
function track_daily_active_user (line 180) | pub async fn track_daily_active_user() -> Result<(), String> {
function track_user_first_launch (line 194) | pub async fn track_user_first_launch() -> Result<(), String> {
function track_summary_generation_started (line 209) | pub async fn track_summary_generation_started(model_provider: String, mo...
function track_summary_generation_completed (line 223) | pub async fn track_summary_generation_completed(model_provider: String, ...
function track_summary_regenerated (line 237) | pub async fn track_summary_regenerated(model_provider: String, model_nam...
function track_model_changed (line 251) | pub async fn track_model_changed(old_provider: String, old_model: String...
function track_custom_prompt_used (line 265) | pub async fn track_custom_prompt_used(prompt_length: usize) -> Result<()...
function track_meeting_ended (line 279) | pub async fn track_meeting_ended(
function track_analytics_enabled (line 320) | pub async fn track_analytics_enabled() -> Result<(), String> {
function track_analytics_disabled (line 334) | pub async fn track_analytics_disabled() -> Result<(), String> {
function track_analytics_transparency_viewed (line 348) | pub async fn track_analytics_transparency_viewed() -> Result<(), String> {
function is_analytics_session_active (line 362) | pub async fn is_analytics_session_active() -> bool {
FILE: frontend/src-tauri/src/anthropic/anthropic.rs
type AnthropicModel (line 8) | pub struct AnthropicModel {
type AnthropicApiModel (line 15) | struct AnthropicApiModel {
type AnthropicApiResponse (line 24) | struct AnthropicApiResponse {
type CacheEntry (line 29) | struct CacheEntry {
constant CACHE_TTL_SECS (line 38) | const CACHE_TTL_SECS: u64 = 300;
constant FALLBACK_MODELS (line 41) | const FALLBACK_MODELS: &[(&str, &str)] = &[
function get_fallback_models (line 49) | fn get_fallback_models() -> Vec<AnthropicModel> {
function is_chat_model (line 60) | fn is_chat_model(model_id: &str) -> bool {
function get_anthropic_models (line 74) | pub async fn get_anthropic_models(api_key: Option<String>) -> Result<Vec...
function clear_cache (line 166) | pub fn clear_cache() {
FILE: frontend/src-tauri/src/api/api.rs
constant APP_SERVER_URL (line 20) | const APP_SERVER_URL: &str = "http://localhost:5167";
type ApiResponse (line 23) | pub struct ApiResponse<T> {
type Meeting (line 30) | pub struct Meeting {
type SearchRequest (line 36) | pub struct SearchRequest {
type TranscriptSearchResult (line 41) | pub struct TranscriptSearchResult {
type ProfileRequest (line 50) | pub struct ProfileRequest {
type SaveProfileRequest (line 56) | pub struct SaveProfileRequest {
type UpdateProfileRequest (line 62) | pub struct UpdateProfileRequest {
type ModelConfig (line 70) | pub struct ModelConfig {
type SaveModelConfigRequest (line 82) | pub struct SaveModelConfigRequest {
type GetApiKeyRequest (line 94) | pub struct GetApiKeyRequest {
type TranscriptConfig (line 99) | pub struct TranscriptConfig {
type SaveTranscriptConfigRequest (line 107) | pub struct SaveTranscriptConfigRequest {
type DeleteMeetingRequest (line 115) | pub struct DeleteMeetingRequest {
type MeetingDetails (line 120) | pub struct MeetingDetails {
type MeetingTranscript (line 129) | pub struct MeetingTranscript {
type MeetingMetadata (line 144) | pub struct MeetingMetadata {
type PaginatedTranscriptsResponse (line 155) | pub struct PaginatedTranscriptsResponse {
type SaveMeetingTitleRequest (line 162) | pub struct SaveMeetingTitleRequest {
type SaveMeetingSummaryRequest (line 168) | pub struct SaveMeetingSummaryRequest {
type SaveTranscriptRequest (line 174) | pub struct SaveTranscriptRequest {
type TranscriptSegment (line 180) | pub struct TranscriptSegment {
type Profile (line 194) | pub struct Profile {
function get_auth_token (line 208) | async fn get_auth_token<R: Runtime>(app: &AppHandle<R>) -> Option<String> {
function get_server_address (line 233) | async fn get_server_address<R: Runtime>(_app: &AppHandle<R>) -> Result<S...
function make_api_request (line 239) | async fn make_api_request<R: Runtime, T: for<'de> Deserialize<'de>>(
function api_get_meetings (line 322) | pub async fn api_get_meetings<R: Runtime>(
function api_search_transcripts (line 356) | pub async fn api_search_transcripts<R: Runtime>(
function api_get_profile (line 386) | pub async fn api_get_profile<R: Runtime>(
function api_save_profile (line 406) | pub async fn api_save_profile<R: Runtime>(
function api_update_profile (line 433) | pub async fn api_update_profile<R: Runtime>(
function api_get_model_config (line 467) | pub async fn api_get_model_config<R: Runtime>(
function api_save_model_config (line 517) | pub async fn api_save_model_config<R: Runtime>(
function api_get_api_key (line 574) | pub async fn api_get_api_key<R: Runtime>(
function api_get_transcript_config (line 600) | pub async fn api_get_transcript_config<R: Runtime>(
function api_save_transcript_config (line 650) | pub async fn api_save_transcript_config<R: Runtime>(
function api_get_transcript_api_key (line 687) | pub async fn api_get_transcript_api_key<R: Runtime>(
function api_delete_api_key (line 717) | pub async fn api_delete_api_key<R: Runtime>(
function api_delete_meeting (line 744) | pub async fn api_delete_meeting<R: Runtime>(
function api_get_meeting (line 781) | pub async fn api_get_meeting<R: Runtime>(
function api_get_meeting_metadata (line 813) | pub async fn api_get_meeting_metadata<R: Runtime>(
function api_get_meeting_transcripts (line 846) | pub async fn api_get_meeting_transcripts<R: Runtime>(
function api_save_meeting_title (line 900) | pub async fn api_save_meeting_title<R: Runtime>(
function api_save_transcript (line 930) | pub async fn api_save_transcript<R: Runtime>(
function open_meeting_folder (line 1008) | pub async fn open_meeting_folder<R: Runtime>(
function test_backend_connection (line 1079) | pub async fn test_backend_connection<R: Runtime>(
function debug_backend_connection (line 1111) | pub async fn debug_backend_connection<R: Runtime>(app: AppHandle<R>) -> ...
function open_external_url (line 1149) | pub async fn open_external_url(url: String) -> Result<(), String> {
function api_save_custom_openai_config (line 1172) | pub async fn api_save_custom_openai_config<R: Runtime>(
function api_get_custom_openai_config (line 1246) | pub async fn api_get_custom_openai_config<R: Runtime>(
function api_test_custom_openai_connection (line 1274) | pub async fn api_test_custom_openai_connection<R: Runtime>(
FILE: frontend/src-tauri/src/audio/async_logger.rs
type AsyncLogger (line 8) | pub struct AsyncLogger {
method new (line 24) | pub fn new(buffer_size: usize) -> Self {
method log (line 56) | pub fn log(&self, level: Level, target: &str, message: String) {
method flush_messages (line 69) | fn flush_messages(messages: &mut Vec<LogMessage>) {
type LogMessage (line 14) | struct LogMessage {
function init_async_logger (line 85) | pub fn init_async_logger() {
function get_async_logger (line 94) | pub fn get_async_logger() -> Option<Arc<AsyncLogger>> {
FILE: frontend/src-tauri/src/audio/audio_processing.rs
function sanitize_filename (line 15) | pub fn sanitize_filename(name: &str) -> String {
function create_meeting_folder (line 35) | pub fn create_meeting_folder(
function normalize_v2 (line 60) | pub fn normalize_v2(audio: &[f32]) -> Vec<f32> {
type TruePeakLimiter (line 100) | struct TruePeakLimiter {
method new (line 108) | fn new(sample_rate: u32) -> Self {
method process (line 120) | fn process(&mut self, sample: f32, true_peak_limit: f32) -> f32 {
type LoudnessNormalizer (line 147) | pub struct LoudnessNormalizer {
method new (line 161) | pub fn new(channels: u32, sample_rate: u32) -> Result<Self> {
method normalize_loudness (line 186) | pub fn normalize_loudness(&mut self, samples: &[f32]) -> Vec<f32> {
type NoiseSuppressionProcessor (line 237) | pub struct NoiseSuppressionProcessor {
method new (line 248) | pub fn new(sample_rate: u32) -> Result<Self> {
method process (line 279) | pub fn process(&mut self, samples: &[f32]) -> Vec<f32> {
method buffered_samples (line 315) | pub fn buffered_samples(&self) -> usize {
method flush (line 321) | pub fn flush(&mut self) -> Vec<f32> {
type HighPassFilter (line 345) | pub struct HighPassFilter {
method new (line 362) | pub fn new(sample_rate: u32, cutoff_hz: f32) -> Self {
method process (line 381) | pub fn process(&mut self, samples: &[f32]) -> Vec<f32> {
method reset (line 399) | pub fn reset(&mut self) {
function spectral_subtraction (line 405) | pub fn spectral_subtraction(audio: &[f32], d: f32) -> Result<Vec<f32>> {
function average_noise_spectrum (line 466) | pub fn average_noise_spectrum(audio: &[f32]) -> f32 {
function audio_to_mono (line 478) | pub fn audio_to_mono(audio: &[f32], channels: u16) -> Vec<f32> {
function resample (line 510) | pub fn resample(input: &[f32], from_sample_rate: u32, to_sample_rate: u3...
function resample_audio (line 598) | pub fn resample_audio(input: &[f32], from_sample_rate: u32, to_sample_ra...
function write_audio_to_file (line 610) | pub fn write_audio_to_file(
function write_audio_to_file_with_meeting_name (line 620) | pub fn write_audio_to_file_with_meeting_name(
function write_transcript_to_file (line 665) | pub fn write_transcript_to_file(
function write_transcript_json_to_file (line 696) | pub fn write_transcript_json_to_file(
FILE: frontend/src-tauri/src/audio/batch_processor.rs
type BatchProcessor (line 8) | pub struct BatchProcessor<T, R> {
function new (line 25) | pub fn new<F>(
function add (line 92) | pub fn add(&self, item: T) -> Result<(), mpsc::error::SendError<T>> {
function get_results (line 97) | pub async fn get_results(&self) -> Vec<R> {
function clear_results (line 103) | pub async fn clear_results(&self) {
type AudioMetricsBatcher (line 109) | pub struct AudioMetricsBatcher {
method new (line 134) | pub fn new() -> Self {
method add_metric (line 180) | pub fn add_metric(&self, metric: AudioMetric) -> Result<(), mpsc::erro...
method get_summaries (line 185) | pub async fn get_summaries(&self) -> Vec<AudioMetricsSummary> {
method clear_summaries (line 190) | pub async fn clear_summaries(&self) {
type AudioMetric (line 114) | pub struct AudioMetric {
type AudioMetricsSummary (line 123) | pub struct AudioMetricsSummary {
method default (line 196) | fn default() -> Self {
FILE: frontend/src-tauri/src/audio/buffer_pool.rs
type AudioBufferPool (line 5) | pub struct AudioBufferPool {
method new (line 13) | pub fn new(max_size: usize, buffer_capacity: usize) -> Self {
method get_buffer (line 22) | pub fn get_buffer(&self) -> Vec<f32> {
method return_buffer (line 39) | pub fn return_buffer(&self, mut buffer: Vec<f32>) {
method pool_size (line 53) | pub fn pool_size(&self) -> usize {
method clear (line 58) | pub fn clear(&self) {
method clone (line 64) | fn clone(&self) -> Self {
type PooledBuffer (line 74) | pub struct PooledBuffer {
method new (line 81) | pub fn new(pool: AudioBufferPool) -> Self {
method as_mut (line 90) | pub fn as_mut(&mut self) -> &mut Vec<f32> {
method as_ref (line 95) | pub fn as_ref(&self) -> &Vec<f32> {
method into_inner (line 100) | pub fn into_inner(mut self) -> Vec<f32> {
type Target (line 114) | type Target = Vec<f32>;
method deref (line 116) | fn deref(&self) -> &Self::Target {
method deref_mut (line 122) | fn deref_mut(&mut self) -> &mut Self::Target {
method drop (line 106) | fn drop(&mut self) {
function test_buffer_pool (line 132) | fn test_buffer_pool() {
function test_pooled_buffer_raii (line 149) | fn test_pooled_buffer_raii() {
function test_pool_max_size (line 163) | fn test_pool_max_size() {
FILE: frontend/src-tauri/src/audio/capture/backend_config.rs
type AudioCaptureBackend (line 10) | pub enum AudioCaptureBackend {
method name (line 23) | pub fn name(&self) -> &'static str {
method description (line 32) | pub fn description(&self) -> &'static str {
method from_string (line 45) | pub fn from_string(s: &str) -> Option<Self> {
method to_string (line 55) | pub fn to_string(&self) -> String {
method available_backends (line 64) | pub fn available_backends() -> Vec<Self> {
method default (line 77) | pub fn default() -> Self {
method fmt (line 93) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method default (line 87) | fn default() -> Self {
type BackendConfig (line 99) | pub struct BackendConfig {
method new (line 104) | fn new() -> Self {
method get (line 111) | pub fn get(&self) -> AudioCaptureBackend {
method set (line 116) | pub fn set(&self, backend: AudioCaptureBackend) {
method available (line 122) | pub fn available(&self) -> Vec<AudioCaptureBackend> {
method reset (line 127) | pub fn reset(&self) {
function get_current_backend (line 138) | pub fn get_current_backend() -> AudioCaptureBackend {
function set_current_backend (line 143) | pub fn set_current_backend(backend: AudioCaptureBackend) {
function get_available_backends (line 148) | pub fn get_available_backends() -> Vec<AudioCaptureBackend> {
function test_backend_to_string (line 157) | fn test_backend_to_string() {
function test_backend_from_string (line 164) | fn test_backend_from_string() {
function test_available_backends (line 183) | fn test_available_backends() {
function test_default_backend (line 192) | fn test_default_backend() {
function test_backend_config (line 201) | fn test_backend_config() {
FILE: frontend/src-tauri/src/audio/capture/core_audio.rs
type WakerState (line 20) | struct WakerState {
type CoreAudioCapture (line 27) | pub struct CoreAudioCapture {
method new (line 57) | pub fn new() -> Result<Self> {
method start_device (line 154) | fn start_device(
method stream (line 242) | pub fn stream(self) -> Result<CoreAudioStream> {
method new (line 392) | pub fn new() -> Result<Self> {
method stream (line 396) | pub fn stream(self) -> Result<CoreAudioStream> {
type CoreAudioStream (line 34) | pub struct CoreAudioStream {
method sample_rate (line 337) | pub fn sample_rate(&self) -> u32 {
method sample_rate (line 403) | pub fn sample_rate(&self) -> u32 {
type AudioContext (line 45) | struct AudioContext {
function process_audio_data (line 300) | fn process_audio_data(ctx: &mut AudioContext, data: &[f32]) {
type Item (line 344) | type Item = f32;
method poll_next (line 346) | fn poll_next(
method drop (line 377) | fn drop(&mut self) {
type CoreAudioCapture (line 385) | pub struct CoreAudioCapture;
method new (line 57) | pub fn new() -> Result<Self> {
method start_device (line 154) | fn start_device(
method stream (line 242) | pub fn stream(self) -> Result<CoreAudioStream> {
method new (line 392) | pub fn new() -> Result<Self> {
method stream (line 396) | pub fn stream(self) -> Result<CoreAudioStream> {
type CoreAudioStream (line 388) | pub struct CoreAudioStream;
method sample_rate (line 337) | pub fn sample_rate(&self) -> u32 {
method sample_rate (line 403) | pub fn sample_rate(&self) -> u32 {
type Item (line 410) | type Item = f32;
method poll_next (line 412) | fn poll_next(
function test_core_audio_capture (line 427) | async fn test_core_audio_capture() {
FILE: frontend/src-tauri/src/audio/capture/system.rs
type SystemAudioCapture (line 16) | pub struct SystemAudioCapture {
method new (line 21) | pub fn new() -> Result<Self> {
method list_system_devices (line 26) | pub fn list_system_devices() -> Result<Vec<String>> {
method start_system_audio_capture (line 41) | pub fn start_system_audio_capture(&self) -> Result<SystemAudioStream> {
method check_system_audio_permissions (line 106) | pub fn check_system_audio_permissions() -> bool {
type SystemAudioStream (line 115) | pub struct SystemAudioStream {
method sample_rate (line 136) | pub fn sample_rate(&self) -> u32 {
method drop (line 122) | fn drop(&mut self) {
type Item (line 128) | type Item = f32;
method poll_next (line 130) | fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Opt...
function start_system_audio_capture (line 142) | pub async fn start_system_audio_capture() -> Result<SystemAudioStream> {
function list_system_audio_devices (line 147) | pub fn list_system_audio_devices() -> Result<Vec<String>> {
function check_system_audio_permissions (line 151) | pub fn check_system_audio_permissions() -> bool {
FILE: frontend/src-tauri/src/audio/common.rs
function unload_engine_after_batch (line 10) | pub(crate) async fn unload_engine_after_batch(use_parakeet: bool) {
function create_transcript_segments (line 39) | pub(crate) fn create_transcript_segments(transcripts: &[(String, f64, f6...
function write_transcripts_json (line 60) | pub(crate) fn write_transcripts_json(folder: &Path, segments: &[Transcri...
function split_segment_at_silence (line 98) | pub(crate) fn split_segment_at_silence(
FILE: frontend/src-tauri/src/audio/constants.rs
constant AUDIO_EXTENSIONS (line 5) | pub const AUDIO_EXTENSIONS: &[&str] = &[
FILE: frontend/src-tauri/src/audio/core-old.rs
type AudioTranscriptionEngine (line 24) | pub enum AudioTranscriptionEngine {
method fmt (line 33) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
method default (line 45) | fn default() -> Self {
type DeviceControl (line 51) | pub struct DeviceControl {
type DeviceType (line 57) | pub enum DeviceType {
type AudioDevice (line 63) | pub struct AudioDevice {
method new (line 69) | pub fn new(name: String, device_type: DeviceType) -> Self {
method from_name (line 73) | pub fn from_name(name: &str) -> Result<Self> {
method fmt (line 99) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
function parse_audio_device (line 112) | pub fn parse_audio_device(name: &str) -> Result<AudioDevice> {
function configure_windows_audio (line 118) | fn configure_windows_audio(host: &cpal::Host) -> Result<Vec<AudioDevice>> {
function configure_linux_audio (line 207) | fn configure_linux_audio(host: &cpal::Host) -> Result<Vec<AudioDevice>> {
function list_audio_devices (line 235) | pub async fn list_audio_devices() -> Result<Vec<AudioDevice>> {
function default_input_device (line 297) | pub fn default_input_device() -> Result<AudioDevice> {
function default_output_device (line 305) | pub fn default_output_device() -> Result<AudioDevice> {
function trigger_audio_permission (line 351) | pub fn trigger_audio_permission() -> Result<()> {
type AudioStream (line 382) | pub struct AudioStream {
method from_device (line 396) | pub async fn from_device(
method subscribe (line 665) | pub async fn subscribe(&self) -> broadcast::Receiver<Vec<f32>> {
method stop (line 669) | pub async fn stop(&self) -> Result<()> {
type StreamControl (line 391) | enum StreamControl {
function get_windows_device (line 699) | fn get_windows_device(audio_device: &AudioDevice) -> Result<(cpal::Devic...
function get_device_and_config (line 861) | pub async fn get_device_and_config(
FILE: frontend/src-tauri/src/audio/decoder.rs
constant FFMPEG_ONLY_EXTENSIONS (line 23) | const FFMPEG_ONLY_EXTENSIONS: &[&str] = &["mkv", "webm", "wma"];
type ProgressCallback (line 27) | pub type ProgressCallback = Box<dyn Fn(u32, &str) + Send>;
type DecodedAudio (line 31) | pub struct DecodedAudio {
method to_whisper_format (line 48) | pub fn to_whisper_format(&self) -> Vec<f32> {
method to_whisper_format_with_progress (line 53) | pub fn to_whisper_format_with_progress(&self, progress_callback: Optio...
function chunked_resample_with_progress (line 126) | fn chunked_resample_with_progress(
function normalize_audio_samples (line 232) | fn normalize_audio_samples(mut samples: Vec<f32>) -> Vec<f32> {
function needs_ffmpeg_conversion (line 265) | fn needs_ffmpeg_conversion(path: &Path) -> bool {
function convert_to_wav_with_ffmpeg (line 276) | fn convert_to_wav_with_ffmpeg(
function decode_audio_file (line 392) | pub fn decode_audio_file(path: &Path) -> Result<DecodedAudio> {
function decode_audio_file_with_progress (line 397) | pub fn decode_audio_file_with_progress(
function test_to_whisper_format_mono_16k (line 585) | fn test_to_whisper_format_mono_16k() {
function test_to_whisper_format_stereo_to_mono (line 599) | fn test_to_whisper_format_stereo_to_mono() {
function test_to_whisper_format_resamples_48k_to_16k (line 616) | fn test_to_whisper_format_resamples_48k_to_16k() {
function test_chunked_resample_same_rate (line 636) | fn test_chunked_resample_same_rate() {
function test_chunked_resample_empty_input (line 646) | fn test_chunked_resample_empty_input() {
function test_chunked_resample_downsamples_correctly (line 653) | fn test_chunked_resample_downsamples_correctly() {
function test_chunked_resample_preserves_signal_range (line 669) | fn test_chunked_resample_preserves_signal_range() {
function test_chunked_resample_matches_single_pass (line 686) | fn test_chunked_resample_matches_single_pass() {
function test_decoded_audio_duration_calculation (line 720) | fn test_decoded_audio_duration_calculation() {
function test_decoded_audio_stereo_duration (line 734) | fn test_decoded_audio_stereo_duration() {
function test_to_whisper_format_handles_large_file_threshold (line 749) | fn test_to_whisper_format_handles_large_file_threshold() {
function test_normalize_audio_samples_already_normalized (line 767) | fn test_normalize_audio_samples_already_normalized() {
function test_normalize_audio_samples_exceeds_range (line 777) | fn test_normalize_audio_samples_exceeds_range() {
function test_normalize_audio_samples_handles_nan (line 788) | fn test_normalize_audio_samples_handles_nan() {
function test_normalize_audio_samples_handles_infinity (line 797) | fn test_normalize_audio_samples_handles_infinity() {
function test_needs_ffmpeg_conversion (line 806) | fn test_needs_ffmpeg_conversion() {
FILE: frontend/src-tauri/src/audio/device_detection.rs
type InputDeviceKind (line 16) | pub enum InputDeviceKind {
method detect (line 37) | pub fn detect(device_name: &str, buffer_size: u32, sample_rate: u32) -...
method buffer_timeout (line 79) | pub fn buffer_timeout(&self) -> (Duration, Duration) {
method is_bluetooth (line 102) | pub fn is_bluetooth(&self) -> bool {
method is_wired (line 107) | pub fn is_wired(&self) -> bool {
method detect_by_name (line 116) | fn detect_by_name(device_name: &str) -> Option<Self> {
method detect_by_buffer_size (line 200) | fn detect_by_buffer_size(buffer_size: u32, sample_rate: u32) -> Option...
method detect_macos_native (line 238) | fn detect_macos_native(device_name: &str) -> Option<Self> {
method detect_windows_native (line 287) | fn detect_windows_native(device_name: &str) -> Option<Self> {
method detect_linux_native (line 334) | fn detect_linux_native(device_name: &str) -> Option<Self> {
function calculate_buffer_timeout (line 393) | pub fn calculate_buffer_timeout(
function clamp_duration (line 417) | fn clamp_duration(duration: Duration, min: Duration, max: Duration) -> D...
function test_airpods_detection (line 436) | fn test_airpods_detection() {
function test_builtin_mic_detection (line 442) | fn test_builtin_mic_detection() {
function test_bluetooth_by_buffer_size (line 449) | fn test_bluetooth_by_buffer_size() {
function test_wired_by_buffer_size (line 456) | fn test_wired_by_buffer_size() {
function test_buffer_timeout_wired (line 463) | fn test_buffer_timeout_wired() {
function test_buffer_timeout_bluetooth (line 470) | fn test_buffer_timeout_bluetooth() {
function test_calculate_buffer_timeout_bluetooth (line 477) | fn test_calculate_buffer_timeout_bluetooth() {
function test_calculate_buffer_timeout_wired (line 490) | fn test_calculate_buffer_timeout_wired() {
function test_virtual_device_detection (line 505) | fn test_virtual_device_detection() {
FILE: frontend/src-tauri/src/audio/device_monitor.rs
type DeviceEvent (line 13) | pub enum DeviceEvent {
type DeviceMonitorType (line 30) | pub enum DeviceMonitorType {
type MonitoredDevice (line 37) | struct MonitoredDevice {
method new (line 45) | fn new(name: String, device_type: DeviceMonitorType) -> Self {
method disconnect_threshold (line 60) | fn disconnect_threshold(&self) -> u32 {
method reconnect_interval (line 71) | fn reconnect_interval(&self) -> Duration {
type AudioDeviceMonitor (line 81) | pub struct AudioDeviceMonitor {
method new (line 89) | pub fn new() -> (Self, mpsc::UnboundedReceiver<DeviceEvent>) {
method start_monitoring (line 104) | pub fn start_monitoring(
method stop_monitoring (line 151) | pub async fn stop_monitoring(&mut self) {
method monitor_loop (line 163) | async fn monitor_loop(
method default (line 256) | fn default() -> Self {
method drop (line 262) | fn drop(&mut self) {
function test_bluetooth_detection (line 273) | fn test_bluetooth_detection() {
function test_monitor_creation (line 290) | async fn test_monitor_creation() {
FILE: frontend/src-tauri/src/audio/devices/configuration.rs
type AudioTranscriptionEngine (line 17) | pub enum AudioTranscriptionEngine {
method fmt (line 26) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
method default (line 38) | fn default() -> Self {
type DeviceControl (line 44) | pub struct DeviceControl {
type DeviceType (line 50) | pub enum DeviceType {
type AudioDevice (line 56) | pub struct AudioDevice {
method new (line 62) | pub fn new(name: String, device_type: DeviceType) -> Self {
method from_name (line 66) | pub fn from_name(name: &str) -> Result<Self> {
method fmt (line 92) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
function parse_audio_device (line 106) | pub fn parse_audio_device(name: &str) -> Result<AudioDevice> {
function get_device_and_config (line 111) | pub async fn get_device_and_config(
FILE: frontend/src-tauri/src/audio/devices/discovery.rs
function list_audio_devices (line 9) | pub async fn list_audio_devices() -> Result<Vec<AudioDevice>> {
function trigger_audio_permission (line 46) | pub fn trigger_audio_permission() -> Result<bool> {
FILE: frontend/src-tauri/src/audio/devices/fallback.rs
function get_safe_recording_devices_macos (line 64) | pub fn get_safe_recording_devices_macos() -> Result<(Option<AudioDevice>...
function get_safe_recording_devices (line 157) | pub fn get_safe_recording_devices() -> Result<(Option<AudioDevice>, Opti...
function test_bluetooth_override_logic (line 172) | fn test_bluetooth_override_logic() {
FILE: frontend/src-tauri/src/audio/devices/microphone.rs
function default_input_device (line 8) | pub fn default_input_device() -> Result<AudioDevice> {
function find_builtin_input_device (line 25) | pub fn find_builtin_input_device() -> Result<Option<AudioDevice>> {
FILE: frontend/src-tauri/src/audio/devices/platform/linux.rs
function configure_linux_audio (line 7) | pub fn configure_linux_audio(host: &cpal::Host) -> Result<Vec<AudioDevic...
FILE: frontend/src-tauri/src/audio/devices/platform/macos.rs
function configure_macos_audio (line 7) | pub fn configure_macos_audio(host: &cpal::Host) -> Result<Vec<AudioDevic...
FILE: frontend/src-tauri/src/audio/devices/platform/windows.rs
function configure_windows_audio (line 8) | pub fn configure_windows_audio(host: &cpal::Host) -> Result<Vec<AudioDev...
function get_windows_device (line 97) | pub fn get_windows_device(audio_device: &AudioDevice) -> Result<(cpal::D...
FILE: frontend/src-tauri/src/audio/devices/speakers.rs
function default_output_device (line 8) | pub fn default_output_device() -> Result<AudioDevice> {
function find_builtin_output_device (line 61) | pub fn find_builtin_output_device() -> Result<Option<AudioDevice>> {
FILE: frontend/src-tauri/src/audio/diagnostics.rs
function log_device_capabilities (line 24) | pub fn log_device_capabilities(
function check_for_issues (line 96) | fn check_for_issues(
function log_macos_specific_info (line 133) | fn log_macos_specific_info(_device: &AudioDevice, _config: &SupportedStr...
function log_windows_specific_info (line 164) | fn log_windows_specific_info(device: &AudioDevice, _config: &SupportedSt...
function log_linux_specific_info (line 181) | fn log_linux_specific_info(device: &AudioDevice, _config: &SupportedStre...
function log_detection_summary (line 202) | pub fn log_detection_summary(
function log_buffer_health (line 222) | pub fn log_buffer_health(
function log_mixer_status (line 248) | pub fn log_mixer_status(
function log_performance_summary (line 262) | pub fn log_performance_summary(
function test_diagnostics_dont_panic (line 294) | fn test_diagnostics_dont_panic() {
FILE: frontend/src-tauri/src/audio/encode.rs
type AudioInput (line 11) | pub struct AudioInput {
function encode_single_audio (line 18) | pub fn encode_single_audio(
FILE: frontend/src-tauri/src/audio/ffmpeg.rs
constant EXECUTABLE_NAME (line 13) | const EXECUTABLE_NAME: &str = "ffmpeg";
constant EXECUTABLE_NAME (line 16) | const EXECUTABLE_NAME: &str = "ffmpeg.exe";
function find_ffmpeg_path (line 20) | pub fn find_ffmpeg_path() -> Option<PathBuf> {
function find_ffmpeg_path_internal (line 24) | fn find_ffmpeg_path_internal() -> Option<PathBuf> {
function handle_ffmpeg_installation (line 164) | fn handle_ffmpeg_installation() -> Result<(), anyhow::Error> {
function get_ffmpeg_install_dir (line 193) | fn get_ffmpeg_install_dir() -> Result<PathBuf, anyhow::Error> {
function get_ffmpeg_install_dir (line 229) | fn get_ffmpeg_install_dir() -> Result<PathBuf, anyhow::Error> {
FILE: frontend/src-tauri/src/audio/ffmpeg_mixer.rs
constant RNNOISE_APPLY_ENABLED (line 20) | pub const RNNOISE_APPLY_ENABLED: bool = false;
type Timestamp (line 25) | struct Timestamp {
method new (line 32) | fn new() -> Self {
method advance (line 39) | fn advance(&mut self, samples: usize) {
method elapsed (line 43) | fn elapsed(&self) -> Duration {
type TimestampedChunk (line 50) | struct TimestampedChunk {
method new (line 58) | fn new(samples: Vec<f32>, sample_rate: u32) -> Self {
method duration_ms (line 68) | fn duration_ms(&self) -> f64 {
method age (line 73) | fn age(&self) -> Duration {
type SourceBuffer (line 79) | struct SourceBuffer {
method new (line 106) | fn new(device_name: String, device_kind: InputDeviceKind, sample_rate:...
method push (line 135) | fn push(&mut self, samples: Vec<f32>) {
method has_data (line 169) | fn has_data(&self) -> bool {
method pop_samples (line 179) | fn pop_samples(&mut self, sample_count: usize) -> Option<Vec<f32>> {
method buffer_size (line 220) | fn buffer_size(&self) -> usize {
method buffer_latency_ms (line 225) | fn buffer_latency_ms(&self) -> f64 {
method stats (line 230) | fn stats(&self) -> BufferStats {
type BufferStats (line 245) | pub struct BufferStats {
type AudioMixer (line 256) | struct AudioMixer {
method new (line 268) | fn new(adaptive_ducking: bool) -> Self {
method mix (line 282) | fn mix(&mut self, mic: &[f32], system: &[f32]) -> Vec<f32> {
type FFmpegAudioMixer (line 326) | pub struct FFmpegAudioMixer {
method new (line 349) | pub fn new(
method push_mic (line 378) | pub fn push_mic(&mut self, samples: Vec<f32>) {
method push_system (line 383) | pub fn push_system(&mut self, samples: Vec<f32>) {
method has_data_ready (line 388) | pub fn has_data_ready(&self) -> bool {
method pop_mixed (line 395) | pub fn pop_mixed(&mut self) -> Option<Vec<f32>> {
method get_stats (line 418) | pub fn get_stats(&self) -> (BufferStats, BufferStats) {
method log_stats (line 423) | fn log_stats(&self) {
method mic_buffer_size (line 438) | pub fn mic_buffer_size(&self) -> usize {
method system_buffer_size (line 443) | pub fn system_buffer_size(&self) -> usize {
function calculate_rms (line 449) | fn calculate_rms(samples: &[f32]) -> f32 {
function test_source_buffer_basic (line 463) | fn test_source_buffer_basic() {
function test_ffmpeg_mixer_creation (line 478) | fn test_ffmpeg_mixer_creation() {
function test_rms_calculation (line 492) | fn test_rms_calculation() {
function test_audio_mixer_clipping_prevention (line 499) | fn test_audio_mixer_clipping_prevention() {
FILE: frontend/src-tauri/src/audio/hardware_detector.rs
type HardwareProfile (line 6) | pub struct HardwareProfile {
method detect (line 52) | pub fn detect() -> &'static HardwareProfile {
method detect_hardware (line 61) | fn detect_hardware() -> HardwareProfile {
method detect_cpu_cores (line 77) | fn detect_cpu_cores() -> u8 {
method detect_gpu (line 84) | fn detect_gpu() -> (bool, GpuType) {
method detect_memory_gb (line 108) | fn detect_memory_gb() -> u8 {
method calculate_performance_tier (line 120) | fn calculate_performance_tier(cpu_cores: u8, gpu_type: &GpuType, memor...
method has_metal_support (line 154) | fn has_metal_support() -> bool {
method has_cuda_support (line 159) | fn has_cuda_support() -> bool {
method has_vulkan_support (line 166) | fn has_vulkan_support() -> bool {
method get_whisper_config (line 174) | pub fn get_whisper_config(&self) -> AdaptiveWhisperConfig {
method get_recommended_chunk_duration_ms (line 224) | pub fn get_recommended_chunk_duration_ms(&self) -> u32 {
method can_handle_realtime (line 234) | pub fn can_handle_realtime(&self, sample_rate: u32, channels: u16) -> ...
type GpuType (line 15) | pub enum GpuType {
type PerformanceTier (line 24) | pub enum PerformanceTier {
type AdaptiveWhisperConfig (line 33) | pub struct AdaptiveWhisperConfig {
type ChunkSizePreference (line 42) | pub enum ChunkSizePreference {
function test_hardware_detection (line 251) | fn test_hardware_detection() {
function test_whisper_config_generation (line 259) | fn test_whisper_config_generation() {
function test_performance_tier_logic (line 271) | fn test_performance_tier_logic() {
FILE: frontend/src-tauri/src/audio/import.rs
type ImportGuard (line 33) | struct ImportGuard;
method acquire (line 37) | fn acquire() -> Result<Self, String> {
method drop (line 49) | fn drop(&mut self) {
constant VAD_REDEMPTION_TIME_MS (line 58) | const VAD_REDEMPTION_TIME_MS: u32 = 2000;
constant MAX_FILE_SIZE_BYTES (line 61) | const MAX_FILE_SIZE_BYTES: u64 = 20 * 1024 * 1024 * 1024;
type AudioFileInfo (line 65) | pub struct AudioFileInfo {
type ImportProgress (line 75) | pub struct ImportProgress {
type ImportResult (line 83) | pub struct ImportResult {
type ImportError (line 92) | pub struct ImportError {
type ImportWarning (line 98) | pub struct ImportWarning {
type ImportStarted (line 105) | pub struct ImportStarted {
function is_import_in_progress (line 110) | pub fn is_import_in_progress() -> bool {
function cancel_import (line 115) | pub fn cancel_import() {
function validate_audio_file (line 121) | pub fn validate_audio_file(path: &Path) -> Result<AudioFileInfo> {
function extract_duration_from_metadata (line 194) | fn extract_duration_from_metadata(path: &Path) -> Result<f64> {
function start_import (line 254) | pub async fn start_import<R: Runtime>(
function run_import (line 311) | async fn run_import<R: Runtime>(
function emit_progress (line 677) | fn emit_progress<R: Runtime>(app: &AppHandle<R>, stage: &str, progress: ...
function create_meeting_with_transcripts (line 690) | async fn create_meeting_with_transcripts(
function get_or_init_whisper (line 751) | async fn get_or_init_whisper<R: Runtime>(
function get_or_init_parakeet (line 797) | async fn get_or_init_parakeet<R: Runtime>(
function get_configured_model (line 843) | async fn get_configured_model<R: Runtime>(app: &AppHandle<R>, provider_t...
function write_import_metadata (line 879) | fn write_import_metadata(
function select_and_validate_audio_command (line 918) | pub async fn select_and_validate_audio_command<R: Runtime>(
function validate_audio_file_command (line 957) | pub async fn validate_audio_file_command(path: String) -> Result<AudioFi...
function start_import_audio_command (line 964) | pub async fn start_import_audio_command<R: Runtime>(
function cancel_import_command (line 993) | pub async fn cancel_import_command() -> Result<(), String> {
function is_import_in_progress_command (line 1003) | pub async fn is_import_in_progress_command() -> bool {
function test_audio_extensions (line 1012) | fn test_audio_extensions() {
function test_create_transcript_segments_empty (line 1020) | fn test_create_transcript_segments_empty() {
function test_create_transcript_segments_single (line 1027) | fn test_create_transcript_segments_single() {
function test_cancellation_flag (line 1038) | fn test_cancellation_flag() {
function test_extract_duration_from_metadata_wav (line 1052) | fn test_extract_duration_from_metadata_wav() {
function test_extract_duration_from_metadata_mp3 (line 1065) | fn test_extract_duration_from_metadata_mp3() {
function test_validate_audio_file_with_metadata (line 1077) | fn test_validate_audio_file_with_metadata() {
function test_validate_audio_file_nonexistent (line 1091) | fn test_validate_audio_file_nonexistent() {
function test_validate_audio_file_wrong_extension (line 1098) | fn test_validate_audio_file_wrong_extension() {
function test_split_segment_at_silence_short_segment (line 1113) | fn test_split_segment_at_silence_short_segment() {
function test_split_segment_at_silence_splits_long_segment (line 1127) | fn test_split_segment_at_silence_splits_long_segment() {
function test_split_segment_at_silence_no_silence_uses_overlap (line 1156) | fn test_split_segment_at_silence_no_silence_uses_overlap() {
function test_write_transcripts_json (line 1174) | fn test_write_transcripts_json() {
function test_write_import_metadata (line 1216) | fn test_write_import_metadata() {
function test_import_pipeline_decode_vad (line 1247) | fn test_import_pipeline_decode_vad() {
FILE: frontend/src-tauri/src/audio/incremental_saver.rs
type AudioData (line 12) | struct AudioData {
type IncrementalAudioSaver (line 19) | pub struct IncrementalAudioSaver {
method new (line 34) | pub fn new(meeting_folder: PathBuf, sample_rate: u32) -> Result<Self> {
method add_chunk (line 54) | pub fn add_chunk(&mut self, chunk: AudioChunk) -> Result<()> {
method save_checkpoint (line 78) | fn save_checkpoint(&mut self) -> Result<()> {
method finalize (line 117) | pub async fn finalize(&mut self) -> Result<PathBuf> {
method merge_checkpoints (line 149) | async fn merge_checkpoints(&self, output: &PathBuf) -> Result<()> {
method get_meeting_folder (line 218) | pub fn get_meeting_folder(&self) -> &PathBuf {
method get_checkpoint_count (line 223) | pub fn get_checkpoint_count(&self) -> u32 {
type AudioRecoveryStatus (line 230) | pub struct AudioRecoveryStatus {
function recover_audio_from_checkpoints (line 241) | pub async fn recover_audio_from_checkpoints(
function cleanup_checkpoints (line 376) | pub async fn cleanup_checkpoints(meeting_folder: String) -> Result<(), S...
function has_audio_checkpoints (line 396) | pub async fn has_audio_checkpoints(meeting_folder: String) -> Result<boo...
function test_checkpoint_creation (line 423) | async fn test_checkpoint_creation() {
function test_empty_recording (line 459) | async fn test_empty_recording() {
FILE: frontend/src-tauri/src/audio/level_monitor.rs
type AudioLevelData (line 15) | pub struct AudioLevelData {
type AudioLevelUpdate (line 24) | pub struct AudioLevelUpdate {
type AudioLevelMonitor (line 29) | pub struct AudioLevelMonitor {
method new (line 35) | pub fn new() -> Self {
method start_monitoring (line 43) | pub async fn start_monitoring<R: Runtime>(
method stop_monitoring (line 118) | pub async fn stop_monitoring(&self) -> Result<()> {
method is_monitoring (line 135) | pub fn is_monitoring(&self) -> bool {
method find_device_by_name (line 140) | fn find_device_by_name(&self, host: &cpal::Host, device_name: &str) ->...
method create_level_stream (line 167) | async fn create_level_stream(
function process_audio_levels (line 285) | fn process_audio_levels(
type AudioLevelState (line 334) | struct AudioLevelState {
function is_monitoring (line 346) | pub fn is_monitoring() -> bool {
function stop_monitoring (line 351) | pub async fn stop_monitoring() -> Result<()> {
FILE: frontend/src-tauri/src/audio/permissions.rs
function check_screen_recording_permission (line 18) | pub fn check_screen_recording_permission() -> bool {
function check_screen_recording_permission (line 28) | pub fn check_screen_recording_permission() -> bool {
function request_screen_recording_permission (line 35) | pub fn request_screen_recording_permission() -> Result<()> {
function request_screen_recording_permission (line 58) | pub fn request_screen_recording_permission() -> Result<()> {
function ensure_screen_recording_permission (line 64) | pub fn ensure_screen_recording_permission() -> bool {
function check_screen_recording_permission_command (line 81) | pub async fn check_screen_recording_permission_command() -> bool {
function request_screen_recording_permission_command (line 87) | pub async fn request_screen_recording_permission_command() -> Result<(),...
function trigger_system_audio_permission (line 95) | pub fn trigger_system_audio_permission() -> Result<bool> {
function trigger_system_audio_permission (line 128) | pub fn trigger_system_audio_permission() -> Result<bool> {
function trigger_system_audio_permission_command (line 137) | pub async fn trigger_system_audio_permission_command() -> Result<bool, S...
function test_check_permission (line 152) | fn test_check_permission() {
FILE: frontend/src-tauri/src/audio/pipeline.rs
type AudioMixerRingBuffer (line 18) | struct AudioMixerRingBuffer {
method new (line 26) | fn new(sample_rate: u32) -> Self {
method add_samples (line 49) | fn add_samples(&mut self, device_type: DeviceType, samples: Vec<f32>) {
method can_mix (line 87) | fn can_mix(&self) -> bool {
method extract_window (line 92) | fn extract_window(&mut self) -> Option<(Vec<f32>, Vec<f32>)> {
type ProfessionalAudioMixer (line 147) | struct ProfessionalAudioMixer;
method new (line 150) | fn new(_sample_rate: u32) -> Self {
method mix_window (line 154) | fn mix_window(&mut self, mic_window: &[f32], sys_window: &[f32]) -> Ve...
type AudioCapture (line 194) | pub struct AudioCapture {
method new (line 217) | pub fn new(
method process_audio_data (line 386) | pub fn process_audio_data(&self, data: &[f32]) {
method handle_stream_error (line 648) | pub fn handle_stream_error(&self, error: cpal::StreamError) {
type AudioPipeline (line 680) | pub struct AudioPipeline {
method new (line 700) | pub fn new(
method run (line 767) | pub async fn run(mut self) -> Result<()> {
method flush_remaining_audio (line 900) | fn flush_remaining_audio(&mut self) -> Result<()> {
type AudioPipelineManager (line 944) | pub struct AudioPipelineManager {
method new (line 950) | pub fn new() -> Self {
method start (line 958) | pub fn start(
method stop (line 1010) | pub async fn stop(&mut self) -> Result<()> {
method force_flush_and_stop (line 1030) | pub async fn force_flush_and_stop(&mut self) -> Result<()> {
method default (line 1077) | fn default() -> Self {
FILE: frontend/src-tauri/src/audio/playback_monitor.rs
type AudioOutputInfo (line 9) | pub struct AudioOutputInfo {
function get_active_audio_output (line 17) | pub async fn get_active_audio_output() -> Result<AudioOutputInfo> {
function get_macos_output (line 35) | async fn get_macos_output() -> Result<AudioOutputInfo> {
function get_windows_output (line 82) | async fn get_windows_output() -> Result<AudioOutputInfo> {
function get_linux_output (line 121) | async fn get_linux_output() -> Result<AudioOutputInfo> {
function test_get_output_device (line 164) | async fn test_get_output_device() {
FILE: frontend/src-tauri/src/audio/post_processor.rs
type PostProcessRequest (line 8) | pub struct PostProcessRequest {
type PostProcessResponse (line 17) | pub struct PostProcessResponse {
type PostProcessor (line 27) | pub struct PostProcessor {
method new (line 35) | pub fn new() -> Self {
method process_async (line 98) | pub fn process_async(&self, request: PostProcessRequest) -> Result<()> {
method try_recv (line 105) | pub async fn try_recv(&self) -> Option<PostProcessResponse> {
method recv (line 111) | pub async fn recv(&self) -> Option<PostProcessResponse> {
method process_text (line 117) | async fn process_text(request: &PostProcessRequest) -> Result<String> {
method clean_repetitive_text (line 145) | fn clean_repetitive_text(text: &str) -> String {
method remove_artifacts (line 196) | fn remove_artifacts(text: &str) -> String {
method normalize_text (line 217) | fn normalize_text(text: &str) -> String {
method apply_contextual_improvements (line 242) | fn apply_contextual_improvements(text: &str) -> String {
method default (line 273) | fn default() -> Self {
FILE: frontend/src-tauri/src/audio/recording_commands.rs
type RecordingArgs (line 53) | pub struct RecordingArgs {
type TranscriptionStatus (line 58) | pub struct TranscriptionStatus {
function start_recording (line 69) | pub async fn start_recording<R: Runtime>(app: AppHandle<R>) -> Result<()...
function start_recording_with_meeting_name (line 74) | pub async fn start_recording_with_meeting_name<R: Runtime>(
function start_recording_with_devices (line 306) | pub async fn start_recording_with_devices<R: Runtime>(
function start_recording_with_devices_and_meeting (line 315) | pub async fn start_recording_with_devices_and_meeting<R: Runtime>(
function stop_recording (line 477) | pub async fn stop_recording<R: Runtime>(
function is_recording (line 894) | pub async fn is_recording() -> bool {
function get_transcription_status (line 899) | pub async fn get_transcription_status() -> TranscriptionStatus {
function pause_recording (line 909) | pub async fn pause_recording<R: Runtime>(app: AppHandle<R>) -> Result<()...
function resume_recording (line 943) | pub async fn resume_recording<R: Runtime>(app: AppHandle<R>) -> Result<(...
function is_recording_paused (line 977) | pub async fn is_recording_paused() -> bool {
function get_recording_state (line 988) | pub async fn get_recording_state() -> serde_json::Value {
function get_meeting_folder_path (line 1018) | pub async fn get_meeting_folder_path() -> Result<Option<String>, String> {
function get_transcript_history (line 1030) | pub async fn get_transcript_history() -> Result<Vec<crate::audio::record...
function get_recording_meeting_name (line 1043) | pub async fn get_recording_meeting_name() -> Result<Option<String>, Stri...
type DeviceEventResponse (line 1060) | pub enum DeviceEventResponse {
method from (line 1073) | fn from(event: DeviceEvent) -> Self {
type ReconnectionStatus (line 1094) | pub struct ReconnectionStatus {
type DisconnectedDeviceInfo (line 1101) | pub struct DisconnectedDeviceInfo {
function poll_audio_device_events (line 1109) | pub async fn poll_audio_device_events() -> Result<Option<DeviceEventResp...
function get_reconnection_status (line 1128) | pub async fn get_reconnection_status() -> Result<ReconnectionStatus, Str...
function get_active_audio_output (line 1156) | pub async fn get_active_audio_output() -> Result<super::playback_monitor...
function attempt_device_reconnect (line 1165) | pub async fn attempt_device_reconnect(
FILE: frontend/src-tauri/src/audio/recording_manager.rs
type StreamManagerType (line 20) | pub enum StreamManagerType {
type RecordingManager (line 25) | pub struct RecordingManager {
method new (line 39) | pub fn new() -> Self {
method start_recording (line 63) | pub async fn start_recording(
method start_recording_with_defaults_and_auto_save (line 170) | pub async fn start_recording_with_defaults_and_auto_save(&mut self, au...
method stop_streams_only (line 229) | pub async fn stop_streams_only(&mut self) -> Result<()> {
method stop_streams_and_force_flush (line 255) | pub async fn stop_streams_and_force_flush(&mut self) -> Result<()> {
method save_recording_only (line 288) | pub async fn save_recording_only<R: tauri::Runtime>(&mut self, app: &t...
method stop_recording (line 314) | pub async fn stop_recording<R: tauri::Runtime>(&mut self, app: &tauri:...
method get_recording_stats (line 353) | pub fn get_recording_stats(&self) -> (usize, u32) {
method is_recording (line 358) | pub fn is_recording(&self) -> bool {
method pause_recording (line 363) | pub fn pause_recording(&self) -> Result<()> {
method resume_recording (line 369) | pub fn resume_recording(&self) -> Result<()> {
method is_paused (line 375) | pub fn is_paused(&self) -> bool {
method is_active (line 380) | pub fn is_active(&self) -> bool {
method get_stats (line 385) | pub fn get_stats(&self) -> super::recording_state::RecordingStats {
method get_recording_duration (line 390) | pub fn get_recording_duration(&self) -> Option<f64> {
method get_active_recording_duration (line 395) | pub fn get_active_recording_duration(&self) -> Option<f64> {
method get_total_pause_duration (line 400) | pub fn get_total_pause_duration(&self) -> f64 {
method get_current_pause_duration (line 405) | pub fn get_current_pause_duration(&self) -> Option<f64> {
method get_error_info (line 410) | pub fn get_error_info(&self) -> (u32, Option<super::recording_state::A...
method active_stream_count (line 415) | pub fn active_stream_count(&self) -> usize {
method set_error_callback (line 420) | pub fn set_error_callback<F>(&self, callback: F)
method has_fatal_error (line 428) | pub fn has_fatal_error(&self) -> bool {
method set_meeting_name (line 433) | pub fn set_meeting_name(&mut self, name: Option<String>) {
method add_transcript_segment (line 438) | pub fn add_transcript_segment(&self, segment: super::recording_saver::...
method add_transcript_chunk (line 443) | pub fn add_transcript_chunk(&self, text: String) {
method get_transcript_segments (line 449) | pub fn get_transcript_segments(&self) -> Vec<super::recording_saver::T...
method get_meeting_name (line 455) | pub fn get_meeting_name(&self) -> Option<String> {
method cleanup_without_save (line 460) | pub async fn cleanup_without_save(&mut self) {
method get_meeting_folder (line 482) | pub fn get_meeting_folder(&self) -> Option<std::path::PathBuf> {
method poll_device_events (line 488) | pub fn poll_device_events(&mut self) -> Option<DeviceEvent> {
method attempt_device_reconnect (line 498) | pub async fn attempt_device_reconnect(&mut self, device_name: &str, de...
method handle_device_disconnect (line 553) | pub async fn handle_device_disconnect(&mut self, device_name: String, ...
method handle_device_reconnect (line 572) | pub async fn handle_device_reconnect(&mut self, device_name: String, d...
method is_reconnecting (line 594) | pub fn is_reconnecting(&self) -> bool {
method get_state (line 599) | pub fn get_state(&self) -> &Arc<RecordingState> {
method default (line 605) | fn default() -> Self {
method drop (line 611) | fn drop(&mut self) {
FILE: frontend/src-tauri/src/audio/recording_preferences.rs
type RecordingPreferences (line 15) | pub struct RecordingPreferences {
method default (line 29) | fn default() -> Self {
function get_default_recordings_folder (line 43) | pub fn get_default_recordings_folder() -> PathBuf {
function ensure_recordings_directory (line 80) | pub fn ensure_recordings_directory(path: &PathBuf) -> Result<()> {
function generate_recording_filename (line 89) | pub fn generate_recording_filename(format: &str) -> String {
function load_recording_preferences (line 96) | pub async fn load_recording_preferences<R: Runtime>(
function save_recording_preferences (line 138) | pub async fn save_recording_preferences<R: Runtime>(
function get_recording_preferences (line 182) | pub async fn get_recording_preferences<R: Runtime>(
function set_recording_preferences (line 191) | pub async fn set_recording_preferences<R: Runtime>(
function get_default_recordings_folder_path (line 201) | pub async fn get_default_recordings_folder_path() -> Result<String, Stri...
function open_recordings_folder (line 207) | pub async fn open_recordings_folder<R: Runtime>(app: AppHandle<R>) -> Re...
function select_recording_folder (line 247) | pub async fn select_recording_folder<R: Runtime>(
function get_available_audio_backends (line 261) | pub async fn get_available_audio_backends() -> Result<Vec<String>, Strin...
function get_current_audio_backend (line 277) | pub async fn get_current_audio_backend() -> Result<String, String> {
function set_audio_backend (line 292) | pub async fn set_audio_backend(backend: String) -> Result<(), String> {
type BackendInfo (line 349) | pub struct BackendInfo {
function get_audio_backend_info (line 356) | pub async fn get_audio_backend_info() -> Result<Vec<BackendInfo>, String> {
FILE: frontend/src-tauri/src/audio/recording_saver.rs
type TranscriptSegment (line 16) | pub struct TranscriptSegment {
type MeetingMetadata (line 29) | pub struct MeetingMetadata {
type DeviceInfo (line 44) | pub struct DeviceInfo {
type RecordingSaver (line 50) | pub struct RecordingSaver {
method new (line 61) | pub fn new() -> Self {
method set_meeting_name (line 74) | pub fn set_meeting_name(&mut self, name: Option<String>) {
method set_device_info (line 79) | pub fn set_device_info(&mut self, mic_name: Option<String>, sys_name: ...
method add_transcript_segment (line 96) | pub fn add_transcript_segment(&self, segment: TranscriptSegment) {
method add_transcript_chunk (line 122) | pub fn add_transcript_chunk(&self, text: String) {
method start_accumulation (line 140) | pub fn start_accumulation(&mut self, auto_save: bool) -> mpsc::Unbound...
method initialize_meeting_folder (line 230) | fn initialize_meeting_folder(&mut self, meeting_name: &str, create_che...
method write_metadata (line 274) | fn write_metadata(&self, folder: &PathBuf, metadata: &MeetingMetadata)...
method write_transcripts_json (line 286) | fn write_transcripts_json(&self, folder: &PathBuf) -> Result<()> {
method get_stats (line 341) | pub fn get_stats(&self) -> (usize, u32) {
method stop_and_save (line 358) | pub async fn stop_and_save<R: Runtime>(
method get_meeting_folder (line 462) | pub fn get_meeting_folder(&self) -> Option<&PathBuf> {
method get_transcript_segments (line 467) | pub fn get_transcript_segments(&self) -> Vec<TranscriptSegment> {
method get_meeting_name (line 476) | pub fn get_meeting_name(&self) -> Option<String> {
method default (line 482) | fn default() -> Self {
FILE: frontend/src-tauri/src/audio/recording_saver_old.rs
type TranscriptSegment (line 14) | pub struct TranscriptSegment {
type AudioData (line 27) | struct AudioData {
function with_mic_chunks (line 37) | fn with_mic_chunks<F, R>(f: F) -> Option<R>
function with_system_chunks (line 47) | fn with_system_chunks<F, R>(f: F) -> Option<R>
type RecordingSaver (line 58) | pub struct RecordingSaver {
method new (line 66) | pub fn new() -> Self {
method set_meeting_name (line 76) | pub fn set_meeting_name(&mut self, name: Option<String>) {
method add_transcript_segment (line 81) | pub fn add_transcript_segment(&self, segment: TranscriptSegment) {
method add_transcript_chunk (line 98) | pub fn add_transcript_chunk(&self, text: String) {
method start_accumulation (line 114) | pub fn start_accumulation(&mut self) -> mpsc::UnboundedSender<AudioChu...
method start_accumulation_with_processed (line 184) | pub fn start_accumulation_with_processed(&mut self, mut receiver: mpsc...
method get_stats (line 245) | pub fn get_stats(&self) -> (usize, u32) {
method stop_and_save (line 258) | pub async fn stop_and_save<R: Runtime>(&mut self, app: &AppHandle<R>) ...
method default (line 402) | fn default() -> Self {
FILE: frontend/src-tauri/src/audio/recording_state.rs
type DeviceType (line 12) | pub enum DeviceType {
type AudioChunk (line 19) | pub struct AudioChunk {
type ProcessedAudioChunk (line 29) | pub struct ProcessedAudioChunk {
type AudioError (line 38) | pub enum AudioError {
method is_recoverable (line 53) | pub fn is_recoverable(&self) -> bool {
method user_message (line 70) | pub fn user_message(&self) -> &'static str {
type RecordingStats (line 88) | pub struct RecordingStats {
type RecordingState (line 95) | pub struct RecordingState {
method new (line 130) | pub fn new() -> Arc<Self> {
method start_recording (line 152) | pub fn start_recording(&self) -> Result<()> {
method stop_recording (line 161) | pub fn stop_recording(&self) {
method pause_recording (line 177) | pub fn pause_recording(&self) -> Result<()> {
method resume_recording (line 191) | pub fn resume_recording(&self) -> Result<()> {
method is_recording (line 210) | pub fn is_recording(&self) -> bool {
method is_paused (line 214) | pub fn is_paused(&self) -> bool {
method is_active (line 218) | pub fn is_active(&self) -> bool {
method start_reconnecting (line 223) | pub fn start_reconnecting(&self, device: Arc<AudioDevice>, device_type...
method stop_reconnecting (line 229) | pub fn stop_reconnecting(&self) {
method is_reconnecting (line 235) | pub fn is_reconnecting(&self) -> bool {
method get_disconnected_device (line 239) | pub fn get_disconnected_device(&self) -> Option<(Arc<AudioDevice>, Dev...
method set_microphone_device (line 244) | pub fn set_microphone_device(&self, device: Arc<AudioDevice>) {
method set_system_device (line 248) | pub fn set_system_device(&self, device: Arc<AudioDevice>) {
method get_microphone_device (line 252) | pub fn get_microphone_device(&self) -> Option<Arc<AudioDevice>> {
method get_system_device (line 256) | pub fn get_system_device(&self) -> Option<Arc<AudioDevice>> {
method set_audio_sender (line 261) | pub fn set_audio_sender(&self, sender: mpsc::UnboundedSender<AudioChun...
method send_audio_chunk (line 265) | pub fn send_audio_chunk(&self, chunk: AudioChunk) -> Result<()> {
method set_error_callback (line 286) | pub fn set_error_callback<F>(&self, callback: F)
method report_error (line 293) | pub fn report_error(&self, error: AudioError) {
method get_error_count (line 326) | pub fn get_error_count(&self) -> u32 {
method get_recoverable_error_count (line 330) | pub fn get_recoverable_error_count(&self) -> u32 {
method get_last_error (line 334) | pub fn get_last_error(&self) -> Option<AudioError> {
method has_fatal_error (line 338) | pub fn has_fatal_error(&self) -> bool {
method get_stats (line 347) | pub fn get_stats(&self) -> RecordingStats {
method get_recording_duration (line 351) | pub fn get_recording_duration(&self) -> Option<f64> {
method get_active_recording_duration (line 358) | pub fn get_active_recording_duration(&self) -> Option<f64> {
method get_total_pause_duration (line 375) | pub fn get_total_pause_duration(&self) -> f64 {
method get_current_pause_duration (line 379) | pub fn get_current_pause_duration(&self) -> Option<f64> {
method get_buffer_pool (line 391) | pub fn get_buffer_pool(&self) -> AudioBufferPool {
method cleanup (line 396) | pub fn cleanup(&self) {
method default (line 418) | fn default() -> Self {
method clone (line 442) | fn clone(&self) -> Self {
FILE: frontend/src-tauri/src/audio/retranscription.rs
type RetranscriptionGuard (line 27) | struct RetranscriptionGuard;
method acquire (line 31) | fn acquire() -> Result<Self, String> {
method drop (line 43) | fn drop(&mut self) {
constant VAD_REDEMPTION_TIME_MS (line 52) | const VAD_REDEMPTION_TIME_MS: u32 = 2000;
type RetranscriptionProgress (line 56) | pub struct RetranscriptionProgress {
type RetranscriptionResult (line 65) | pub struct RetranscriptionResult {
type RetranscriptionError (line 74) | pub struct RetranscriptionError {
function is_retranscription_in_progress (line 80) | pub fn is_retranscription_in_progress() -> bool {
function cancel_retranscription (line 85) | pub fn cancel_retranscription() {
function start_retranscription (line 90) | pub async fn start_retranscription<R: Runtime>(
function find_audio_file (line 141) | fn find_audio_file(folder: &Path) -> Result<PathBuf> {
function run_retranscription (line 172) | async fn run_retranscription<R: Runtime>(
function emit_progress (line 502) | fn emit_progress<R: Runtime>(
function get_or_init_whisper (line 522) | async fn get_or_init_whisper<R: Runtime>(
function get_configured_whisper_model (line 580) | async fn get_configured_whisper_model<R: Runtime>(app: &AppHandle<R>) ->...
function get_or_init_parakeet (line 624) | async fn get_or_init_parakeet<R: Runtime>(
function get_configured_parakeet_model (line 682) | async fn get_configured_parakeet_model<R: Runtime>(app: &AppHandle<R>) -...
function write_retranscription_metadata (line 724) | fn write_retranscription_metadata(
type RetranscriptionStarted (line 771) | pub struct RetranscriptionStarted {
function start_retranscription_command (line 778) | pub async fn start_retranscription_command<R: Runtime>(
function cancel_retranscription_command (line 821) | pub async fn cancel_retranscription_command() -> Result<(), String> {
function is_retranscription_in_progress_command (line 830) | pub async fn is_retranscription_in_progress_command() -> bool {
function test_create_transcript_segments_empty (line 839) | fn test_create_transcript_segments_empty() {
function test_create_transcript_segments_single (line 846) | fn test_create_transcript_segments_single() {
function test_create_transcript_segments_multiple (line 860) | fn test_create_transcript_segments_multiple() {
function test_create_transcript_segments_trims_whitespace (line 890) | fn test_create_transcript_segments_trims_whitespace() {
function test_create_transcript_segments_generates_unique_ids (line 901) | fn test_create_transcript_segments_generates_unique_ids() {
function test_cancellation_flag (line 915) | fn test_cancellation_flag() {
function test_vad_redemption_time_constant (line 931) | fn test_vad_redemption_time_constant() {
function test_find_audio_file_common_candidates (line 937) | fn test_find_audio_file_common_candidates() {
function test_find_audio_file_non_mp4_extensions (line 950) | fn test_find_audio_file_non_mp4_extensions() {
function test_find_audio_file_fallback_scan (line 960) | fn test_find_audio_file_fallback_scan() {
function test_find_audio_file_priority_order (line 973) | fn test_find_audio_file_priority_order() {
function test_find_audio_file_empty_folder (line 984) | fn test_find_audio_file_empty_folder() {
function test_find_audio_file_nonexistent_folder (line 992) | fn test_find_audio_file_nonexistent_folder() {
function test_audio_extensions_constant (line 998) | fn test_audio_extensions_constant() {
FILE: frontend/src-tauri/src/audio/simple_level_monitor.rs
type AudioLevelData (line 8) | pub struct AudioLevelData {
type AudioLevelUpdate (line 17) | pub struct AudioLevelUpdate {
function start_monitoring (line 26) | pub async fn start_monitoring<R: Runtime>(
function stop_monitoring (line 83) | pub async fn stop_monitoring() -> Result<()> {
function is_monitoring (line 90) | pub fn is_monitoring() -> bool {
FILE: frontend/src-tauri/src/audio/stream.rs
type StreamBackend (line 17) | pub enum StreamBackend {
type AudioStream (line 32) | pub struct AudioStream {
method create (line 42) | pub async fn create(
method create_with_backend (line 54) | pub async fn create_with_backend(
method create_cpal_stream (line 106) | async fn create_cpal_stream(
method create_core_audio_stream (line 145) | async fn create_core_audio_stream(
method build_stream (line 237) | fn build_stream(
method device (line 315) | pub fn device(&self) -> &AudioDevice {
method stop (line 320) | pub fn stop(self) -> Result<()> {
type AudioStreamManager (line 356) | pub struct AudioStreamManager {
method new (line 366) | pub fn new(state: Arc<RecordingState>) -> Self {
method start_streams (line 375) | pub async fn start_streams(
method stop_streams (line 430) | pub fn stop_streams(&mut self) -> Result<()> {
method active_stream_count (line 460) | pub fn active_stream_count(&self) -> usize {
method has_active_streams (line 472) | pub fn has_active_streams(&self) -> bool {
method drop (line 478) | fn drop(&mut self) {
FILE: frontend/src-tauri/src/audio/stt.rs
function stt_sync (line 29) | pub fn stt_sync(
function stt (line 60) | pub async fn stt(
type AudioInput (line 108) | pub struct AudioInput {
type TranscriptionResult (line 116) | pub struct TranscriptionResult {
method cleanup_overlap (line 129) | pub fn cleanup_overlap(&mut self, previous_transcript: String) -> Opti...
function create_whisper_channel (line 152) | pub async fn create_whisper_channel(
function run_stt (line 305) | pub fn run_stt(
function longest_common_word_substring (line 362) | pub fn longest_common_word_substring(s1: &str, s2: &str) -> Option<(usiz...
FILE: frontend/src-tauri/src/audio/system_audio_commands.rs
type SystemAudioDetectorState (line 10) | type SystemAudioDetectorState = Arc<Mutex<Option<SystemAudioDetector>>>;
function start_system_audio_capture_command (line 14) | pub async fn start_system_audio_capture_command() -> Result<String, Stri...
function list_system_audio_devices_command (line 26) | pub async fn list_system_audio_devices_command() -> Result<Vec<String>, ...
function check_system_audio_permissions_command (line 33) | pub async fn check_system_audio_permissions_command() -> bool {
function start_system_audio_monitoring (line 39) | pub async fn start_system_audio_monitoring(
function stop_system_audio_monitoring (line 74) | pub async fn stop_system_audio_monitoring(
function get_system_audio_monitoring_status (line 90) | pub async fn get_system_audio_monitoring_status(
function init_system_audio_state (line 100) | pub fn init_system_audio_state() -> SystemAudioDetectorState {
type SystemAudioStartedPayload (line 106) | pub struct SystemAudioStartedPayload {
type SystemAudioStoppedPayload (line 111) | pub struct SystemAudioStoppedPayload;
function test_list_system_audio_devices (line 118) | async fn test_list_system_audio_devices() {
function test_check_permissions (line 133) | async fn test_check_permissions() {
FILE: frontend/src-tauri/src/audio/system_audio_stream.rs
type SystemAudioStreamManager (line 12) | pub struct SystemAudioStreamManager {
method create (line 20) | pub async fn create(
method device (line 79) | pub fn device(&self) -> &AudioDevice {
method stop (line 84) | pub fn stop(mut self) -> Result<()> {
type EnhancedAudioStreamManager (line 100) | pub struct EnhancedAudioStreamManager {
method new (line 107) | pub fn new(state: Arc<RecordingState>) -> Self {
method start_streams (line 116) | pub async fn start_streams(
method stop_streams (line 173) | pub async fn stop_streams(&mut self) -> Result<()> {
method active_stream_count (line 189) | pub fn active_stream_count(&self) -> usize {
function should_use_enhanced_system_audio (line 203) | fn should_use_enhanced_system_audio(device: &AudioDevice) -> bool {
function test_should_use_enhanced_system_audio (line 223) | fn test_should_use_enhanced_system_audio() {
FILE: frontend/src-tauri/src/audio/system_audio_types.ts
type SystemAudioCommands (line 3) | interface SystemAudioCommands {
type SystemAudioEvents (line 24) | interface SystemAudioEvents {
FILE: frontend/src-tauri/src/audio/system_detector.rs
type SystemAudioEvent (line 9) | pub enum SystemAudioEvent {
type SystemAudioCallback (line 14) | pub type SystemAudioCallback = std::sync::Arc<dyn Fn(SystemAudioEvent) +...
function new_system_audio_callback (line 16) | pub fn new_system_audio_callback<F>(f: F) -> SystemAudioCallback
type BackgroundTask (line 25) | pub struct BackgroundTask {
method start (line 31) | pub fn start<F>(&mut self, task: F)
method stop (line 56) | pub fn stop(&mut self) {
method drop (line 68) | fn drop(&mut self) {
type MacOSSystemAudioDetector (line 75) | pub struct MacOSSystemAudioDetector {
method start (line 130) | pub fn start(&mut self, callback: SystemAudioCallback) {
method stop (line 352) | pub fn stop(&mut self) {
method start (line 394) | pub fn start(&mut self, _callback: SystemAudioCallback) {
method stop (line 398) | pub fn stop(&mut self) {}
method default (line 81) | fn default() -> Self {
constant DEVICE_IS_RUNNING_SOMEWHERE (line 89) | const DEVICE_IS_RUNNING_SOMEWHERE: ca::PropAddr = ca::PropAddr {
type DetectorState (line 96) | struct DetectorState {
method new (line 104) | fn new() -> Self {
method should_trigger (line 112) | fn should_trigger(&mut self, new_state: bool) -> bool {
function list_system_audio_using_apps (line 358) | fn list_system_audio_using_apps() -> Vec<String> {
type MacOSSystemAudioDetector (line 383) | pub struct MacOSSystemAudioDetector;
method start (line 130) | pub fn start(&mut self, callback: SystemAudioCallback) {
method stop (line 352) | pub fn stop(&mut self) {
method start (line 394) | pub fn start(&mut self, _callback: SystemAudioCallback) {
method stop (line 398) | pub fn stop(&mut self) {}
method default (line 387) | fn default() -> Self {
type SystemAudioDetector (line 403) | pub struct SystemAudioDetector {
method new (line 408) | pub fn new() -> Self {
method start (line 412) | pub fn start(&mut self, callback: SystemAudioCallback) {
method stop (line 416) | pub fn stop(&mut self) {
function test_system_audio_detector (line 427) | async fn test_system_audio_detector() {
FILE: frontend/src-tauri/src/audio/transcription/engine.rs
type TranscriptionEngine (line 15) | pub enum TranscriptionEngine {
method is_model_loaded (line 23) | pub async fn is_model_loaded(&self) -> bool {
method get_current_model (line 32) | pub async fn get_current_model(&self) -> Option<String> {
method provider_name (line 41) | pub fn provider_name(&self) -> &str {
function validate_transcription_model_ready (line 55) | pub async fn validate_transcription_model_ready<R: Runtime>(app: &AppHan...
function get_or_init_transcription_engine (line 149) | pub async fn get_or_init_transcription_engine<R: Runtime>(
function get_or_init_whisper (line 225) | pub async fn get_or_init_whisper<R: Runtime>(
FILE: frontend/src-tauri/src/audio/transcription/parakeet_provider.rs
type ParakeetProvider (line 11) | pub struct ParakeetProvider {
method new (line 16) | pub fn new(engine: Arc<crate::parakeet_engine::ParakeetEngine>) -> Self {
method transcribe (line 23) | async fn transcribe(
method is_model_loaded (line 46) | async fn is_model_loaded(&self) -> bool {
method get_current_model (line 50) | async fn get_current_model(&self) -> Option<String> {
method provider_name (line 54) | fn provider_name(&self) -> &'static str {
FILE: frontend/src-tauri/src/audio/transcription/provider.rs
type TranscriptionError (line 14) | pub enum TranscriptionError {
method fmt (line 22) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type TranscriptResult (line 42) | pub struct TranscriptResult {
type TranscriptionProvider (line 50) | pub trait TranscriptionProvider: Send + Sync {
method transcribe (line 59) | async fn transcribe(
method is_model_loaded (line 66) | async fn is_model_loaded(&self) -> bool;
method get_current_model (line 69) | async fn get_current_model(&self) -> Option<String>;
method provider_name (line 72) | fn provider_name(&self) -> &'static str;
FILE: frontend/src-tauri/src/audio/transcription/whisper_provider.rs
type WhisperProvider (line 10) | pub struct WhisperProvider {
method new (line 15) | pub fn new(engine: Arc<crate::whisper_engine::WhisperEngine>) -> Self {
method transcribe (line 22) | async fn transcribe(
method is_model_loaded (line 41) | async fn is_model_loaded(&self) -> bool {
method get_current_model (line 45) | async fn get_current_model(&self) -> Option<String> {
method provider_name (line 49) | fn provider_name(&self) -> &'static str {
FILE: frontend/src-tauri/src/audio/transcription/worker.rs
function reset_speech_detected_flag (line 21) | pub fn reset_speech_detected_flag() {
type TranscriptUpdate (line 27) | pub struct TranscriptUpdate {
function start_transcription_task (line 45) | pub fn start_transcription_task<R: Runtime>(
function transcribe_chunk_with_provider (line 408) | async fn transcribe_chunk_with_provider<R: Runtime>(
function format_current_timestamp (line 576) | fn format_current_timestamp() -> String {
function format_recording_time (line 590) | fn format_recording_time(seconds: f64) -> String {
FILE: frontend/src-tauri/src/audio/vad.rs
type SpeechSegment (line 9) | pub struct SpeechSegment {
type ContinuousVadProcessor (line 17) | pub struct ContinuousVadProcessor {
method new (line 32) | pub fn new(input_sample_rate: u32, redemption_time_ms: u32) -> Result<...
method process_audio (line 87) | pub fn process_audio(&mut self, samples: &[f32]) -> Result<Vec<SpeechS...
method resample_to_16k (line 114) | fn resample_to_16k(&self, samples: &[f32]) -> Result<Vec<f32>> {
method flush (line 163) | pub fn flush(&mut self) -> Result<Vec<SpeechSegment>> {
method process_chunk (line 212) | fn process_chunk(&mut self, chunk: &[f32]) -> Result<()> {
function extract_speech_16k (line 288) | pub fn extract_speech_16k(samples_mono_16k: &[f32]) -> Result<Vec<f32>> {
function get_speech_chunks (line 329) | pub fn get_speech_chunks(samples_mono_16k: &[f32], redemption_time_ms: u...
function get_speech_chunks_with_progress (line 335) | pub fn get_speech_chunks_with_progress<F>(
function generate_test_audio_with_speech (line 416) | fn generate_test_audio_with_speech(duration_seconds: f32, sample_rate: u...
function test_vad_chunked_vs_single_processing (line 450) | fn test_vad_chunked_vs_single_processing() {
function test_vad_large_file_progress (line 475) | fn test_vad_large_file_progress() {
function test_vad_cancellation (line 501) | fn test_vad_cancellation() {
function test_vad_continuous_processor_state_across_chunks (line 516) | fn test_vad_continuous_processor_state_across_chunks() {
function test_vad_400ms_vs_2000ms_segmentation (line 543) | fn test_vad_400ms_vs_2000ms_segmentation() {
FILE: frontend/src-tauri/src/audio_v2/compatibility.rs
type LegacyBridge (line 15) | pub struct LegacyBridge {
method new (line 34) | pub fn new(mode: AudioMode) -> Self {
method initialize (line 43) | pub async fn initialize(&mut self) -> Result<()> {
method start_recording (line 69) | pub async fn start_recording<R: tauri::Runtime>(
method stop_recording (line 116) | pub async fn stop_recording<R: tauri::Runtime>(
method mode (line 162) | pub fn mode(&self) -> &AudioMode {
method switch_mode (line 167) | pub async fn switch_mode(&mut self, new_mode: AudioMode) -> Result<()> {
method get_quality_metrics (line 174) | pub fn get_quality_metrics(&self) -> Option<AudioQualityMetrics> {
type AudioMode (line 23) | pub enum AudioMode {
type AudioQualityMetrics (line 187) | pub struct AudioQualityMetrics {
method default (line 203) | fn default() -> Self {
function is_legacy_enabled (line 211) | pub fn is_legacy_enabled() -> bool {
function is_modern_enabled (line 216) | pub fn is_modern_enabled() -> bool {
function is_hybrid_enabled (line 221) | pub fn is_hybrid_enabled() -> bool {
function default_mode (line 226) | pub fn default_mode() -> super::AudioMode {
FILE: frontend/src-tauri/src/audio_v2/lib.rs
type AudioConfig (line 30) | pub struct AudioConfig {
type MixingMode (line 45) | pub enum MixingMode {
method default (line 55) | fn default() -> Self {
type ModernAudioSystem (line 67) | pub struct ModernAudioSystem {
method new (line 75) | pub fn new() -> Self {
method with_config (line 84) | pub fn with_config(config: AudioConfig) -> Self {
method initialize (line 93) | pub async fn initialize(&mut self) -> Result<()> {
method start_recording (line 100) | pub async fn start_recording(&mut self) -> Result<()> {
method stop_recording (line 107) | pub async fn stop_recording(&mut self) -> Result<Option<String>> {
method config (line 114) | pub fn config(&self) -> &AudioConfig {
method update_config (line 119) | pub fn update_config(&mut self, config: AudioConfig) {
method default (line 125) | fn default() -> Self {
FILE: frontend/src-tauri/src/audio_v2/limiter.rs
type TruePeakLimiter (line 7) | pub struct TruePeakLimiter {
method new (line 14) | pub fn new(sample_rate: u32, lookahead_ms: usize) -> Self {
method process (line 19) | pub fn process(&mut self, sample: f32, limit: f32) -> f32 {
FILE: frontend/src-tauri/src/audio_v2/mixer.rs
type AudioMixer (line 10) | pub struct AudioMixer {
method new (line 54) | pub fn new(mixing_mode: MixingMode) -> Self {
method mix (line 66) | pub fn mix(&mut self, mic: &[f32], system: &[f32]) -> Vec<f32> {
method calculate_dynamic_ratios (line 121) | fn calculate_dynamic_ratios(&self, mic_rms: f32, system_rms: f32) -> (...
method mixing_mode (line 143) | pub fn mixing_mode(&self) -> &MixingMode {
method set_mixing_mode (line 148) | pub fn set_mixing_mode(&mut self, mode: MixingMode) {
method get_level_stats (line 153) | pub fn get_level_stats(&self) -> AudioLevelStats {
type MixingMode (line 21) | pub enum MixingMode {
type RmsAnalyzer (line 31) | struct RmsAnalyzer {
method new (line 172) | fn new(window_size: usize) -> Self {
method analyze (line 180) | fn analyze(&mut self, samples: &[f32]) -> f32 {
type DuckingProcessor (line 38) | struct DuckingProcessor {
method new (line 201) | fn new(threshold: f32, attack_time: f32, release_time: f32) -> Self {
method process (line 211) | fn process(&mut self, mic_sample: f32, system_sample: f32) -> f32 {
type CrossfadeProcessor (line 47) | struct CrossfadeProcessor {
method new (line 239) | fn new(fade_length: usize) -> Self {
method process (line 246) | fn process(&mut self, mic_sample: f32, system_sample: f32) -> f32 {
type AudioLevelStats (line 261) | pub struct AudioLevelStats {
method default (line 268) | fn default() -> Self {
FILE: frontend/src-tauri/src/audio_v2/normalizer.rs
type AudioNormalizer (line 9) | pub struct AudioNormalizer {
method new (line 17) | pub fn new(target_lufs: f64) -> Self {
method normalize (line 25) | pub fn normalize(&mut self, audio: &[f32]) -> Vec<f32> {
FILE: frontend/src-tauri/src/audio_v2/recorder.rs
type ModernRecorder (line 20) | pub struct ModernRecorder {
method new (line 33) | pub fn new(sample_rate: u32) -> Self {
method start (line 47) | pub async fn start(
method process_buffers (line 111) | async fn process_buffers(
method stop (line 150) | pub async fn stop(&mut self) -> Result<Option<String>> {
method is_recording (line 169) | pub fn is_recording(&self) -> bool {
method get_level_stats (line 174) | pub fn get_level_stats(&self) -> super::mixer::AudioLevelStats {
method set_mixing_mode (line 179) | pub fn set_mixing_mode(&mut self, mode: MixingMode) {
method active_stream_count (line 184) | pub fn active_stream_count(&self) -> usize {
method default (line 190) | fn default() -> Self {
FILE: frontend/src-tauri/src/audio_v2/resampler.rs
type DynamicResampler (line 9) | pub struct DynamicResampler {
method new (line 16) | pub fn new(target_rate: u32) -> Self {
method handle_rate_change (line 21) | pub fn handle_rate_change(&mut self) {
method resample (line 26) | pub fn resample(&mut self, audio: &[f32], from_rate: u32, to_rate: u32...
FILE: frontend/src-tauri/src/audio_v2/stream.rs
type ProcessedAudio (line 21) | pub struct ProcessedAudio {
type ModernAudioStream (line 29) | pub struct ModernAudioStream {
method new (line 41) | pub async fn new(
method build_stream (line 85) | fn build_stream(
method device (line 163) | pub fn device(&self) -> &AudioDevice {
method sample_rate (line 168) | pub fn sample_rate(&self) -> u32 {
method device_type (line 173) | pub fn device_type(&self) -> &DeviceType {
method stop (line 178) | pub fn stop(self) -> Result<()> {
type Item (line 186) | type Item = ProcessedAudio;
method poll_next (line 188) | fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Opt...
type AudioProcessor (line 195) | struct AudioProcessor {
method new (line 204) | fn new(
method process_audio_data (line 219) | fn process_audio_data(&self, data: &[f32]) {
method handle_stream_error (line 237) | fn handle_stream_error(&self, error: cpal::StreamError) {
type ModernAudioStreamManager (line 243) | pub struct ModernAudioStreamManager {
method new (line 253) | pub fn new() -> Self {
method start_streams (line 263) | pub async fn start_streams(
method stop_streams (line 309) | pub fn stop_streams(&mut self) -> Result<()> {
method get_unified_stream (line 343) | pub fn get_unified_stream(&mut self) -> Option<UnifiedAudioStream> {
method active_stream_count (line 355) | pub fn active_stream_count(&self) -> usize {
method drop (line 368) | fn drop(&mut self) {
type UnifiedAudioStream (line 376) | pub struct UnifiedAudioStream<'a> {
type Item (line 382) | type Item = ProcessedAudio;
method poll_next (line 384) | fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<...
FILE: frontend/src-tauri/src/audio_v2/sync.rs
type SynchronizedChunk (line 11) | pub struct SynchronizedChunk {
type AudioSynchronizer (line 18) | pub struct AudioSynchronizer {
method new (line 25) | pub fn new(sync_tolerance_ms: u32) -> Self {
method synchronize (line 30) | pub fn synchronize(&mut self) -> Result<Vec<SynchronizedChunk>> {
FILE: frontend/src-tauri/src/config.rs
constant DEFAULT_WHISPER_MODEL (line 8) | pub const DEFAULT_WHISPER_MODEL: &str = "large-v3-turbo";
constant DEFAULT_PARAKEET_MODEL (line 12) | pub const DEFAULT_PARAKEET_MODEL: &str = "parakeet-tdt-0.6b-v3-int8";
constant WHISPER_MODEL_CATALOG (line 18) | pub const WHISPER_MODEL_CATALOG: &[(&str, &str, u32, &str, &str, &str)] ...
FILE: frontend/src-tauri/src/console_utils/console_utils.rs
function AllocConsole (line 11) | fn AllocConsole() -> i32;
function FreeConsole (line 13) | fn FreeConsole() -> i32;
function GetConsoleWindow (line 14) | fn GetConsoleWindow() -> *mut std::ffi::c_void;
function ShowWindow (line 15) | fn ShowWindow(hwnd: *mut std::ffi::c_void, n_cmd_show: i32) -> i32;
constant SW_HIDE (line 19) | const SW_HIDE: i32 = 0;
constant SW_SHOW (line 21) | const SW_SHOW: i32 = 5;
function show_console (line 24) | pub fn show_console() -> Result<String, String> {
function hide_console (line 69) | pub fn hide_console() -> Result<String, String> {
function toggle_console (line 110) | pub fn toggle_console() -> Result<String, String> {
FILE: frontend/src-tauri/src/database/commands.rs
type DatabaseCheckResult (line 10) | pub struct DatabaseCheckResult {
function check_first_launch (line 17) | pub async fn check_first_launch(app: AppHandle) -> Result<bool, String> {
function select_legacy_database_path (line 25) | pub async fn select_legacy_database_path(app: AppHandle) -> Result<Optio...
function detect_legacy_database (line 48) | pub async fn detect_legacy_database(selected_path: String) -> Result<Opt...
function check_default_legacy_database (line 87) | pub async fn check_default_legacy_database(app: AppHandle) -> Result<Opt...
function check_homebrew_database (line 109) | pub async fn check_homebrew_database(path: String) -> Result<Option<Data...
function import_and_initialize_database (line 146) | pub async fn import_and_initialize_database(
function initialize_fresh_database (line 177) | pub async fn initialize_fresh_database(app: AppHandle) -> Result<(), Str...
function get_database_directory (line 224) | pub async fn get_database_directory(app: AppHandle) -> Result<String, St...
function open_database_folder (line 235) | pub async fn open_database_folder(app: AppHandle) -> Result<(), String> {
FILE: frontend/src-tauri/src/database/manager.rs
type DatabaseManager (line 7) | pub struct DatabaseManager {
method new (line 12) | pub async fn new(tauri_db_path: &str, backend_db_path: &str) -> Result...
method new_from_app_handle (line 44) | pub async fn new_from_app_handle(app_handle: &tauri::AppHandle) -> Res...
method is_first_launch (line 121) | pub async fn is_first_launch(app_handle: &tauri::AppHandle) -> Result<...
method import_legacy_database (line 133) | pub async fn import_legacy_database(
method pool (line 160) | pub fn pool(&self) -> &SqlitePool {
method with_transaction (line 164) | pub async fn with_transaction<T, F, Fut>(&self, f: F) -> Result<T>
method cleanup (line 189) | pub async fn cleanup(&self) -> Result<()> {
FILE: frontend/src-tauri/src/database/models.rs
type MeetingModel (line 6) | pub struct MeetingModel {
type DateTimeUtc (line 16) | pub struct DateTimeUtc(pub DateTime<Utc>);
method from (line 19) | fn from(naive: NaiveDateTime) -> Self {
type Transcript (line 26) | pub struct Transcript {
type SummaryProcess (line 41) | pub struct SummaryProcess {
type TranscriptChunk (line 58) | pub struct TranscriptChunk {
type Setting (line 70) | pub struct Setting {
method get_custom_openai_config (line 103) | pub fn get_custom_openai_config(&self) -> Option<crate::summary::Custo...
type TranscriptSetting (line 111) | pub struct TranscriptSetting {
FILE: frontend/src-tauri/src/database/repositories/meeting.rs
type MeetingsRepository (line 7) | pub struct MeetingsRepository;
method get_meetings (line 10) | pub async fn get_meetings(pool: &SqlitePool) -> Result<Vec<MeetingMode...
method delete_meeting (line 18) | pub async fn delete_meeting(pool: &SqlitePool, meeting_id: &str) -> Re...
method get_meeting (line 50) | pub async fn get_meeting(
method get_meeting_metadata (line 112) | pub async fn get_meeting_metadata(
method get_meeting_transcripts_paginated (line 132) | pub async fn get_meeting_transcripts_paginated(
method update_meeting_title (line 168) | pub async fn update_meeting_title(
method update_meeting_name (line 199) | pub async fn update_meeting_name(
function delete_meeting_with_transaction (line 233) | async fn delete_meeting_with_transaction(
FILE: frontend/src-tauri/src/database/repositories/setting.rs
type SaveModelConfigRequest (line 6) | pub struct SaveModelConfigRequest {
type SaveTranscriptConfigRequest (line 18) | pub struct SaveTranscriptConfigRequest {
type SettingsRepository (line 25) | pub struct SettingsRepository;
method get_model_config (line 32) | pub async fn get_model_config(
method save_model_config (line 41) | pub async fn save_model_config(
method save_api_key (line 70) | pub async fn save_api_key(
method get_api_key (line 110) | pub async fn get_api_key(
method get_transcript_config (line 142) | pub async fn get_transcript_config(
method save_transcript_config (line 153) | pub async fn save_transcript_config(
method save_transcript_api_key (line 175) | pub async fn save_transcript_api_key(
method get_transcript_api_key (line 208) | pub async fn get_transcript_api_key(
method delete_api_key (line 234) | pub async fn delete_api_key(
method get_custom_openai_config (line 277) | pub async fn get_custom_openai_config(
method save_custom_openai_config (line 322) | pub async fn save_custom_openai_config(
FILE: frontend/src-tauri/src/database/repositories/summary.rs
type SummaryProcessesRepository (line 7) | pub struct SummaryProcessesRepository;
method get_summary_data (line 11) | pub async fn get_summary_data(
method update_meeting_summary (line 21) | pub async fn update_meeting_summary(
method get_summary_data_for_meeting (line 73) | pub async fn get_summary_data_for_meeting(
method create_or_reset_process (line 85) | pub async fn create_or_reset_process(
method update_process_completed (line 121) | pub async fn update_process_completed(
method update_process_failed (line 154) | pub async fn update_process_failed(
method update_process_cancelled (line 189) | pub async fn update_process_cancelled(
FILE: frontend/src-tauri/src/database/repositories/transcript.rs
type TranscriptsRepository (line 7) | pub struct TranscriptsRepository;
method save_transcript (line 13) | pub async fn save_transcript(
method search_transcripts (line 87) | pub async fn search_transcripts(
method get_match_context (line 124) | fn get_match_context(transcript: &str, query: &str) -> String {
FILE: frontend/src-tauri/src/database/repositories/transcript_chunk.rs
type TranscriptChunksRepository (line 6) | pub struct TranscriptChunksRepository;
method save_transcript_data (line 10) | pub async fn save_transcript_data(
FILE: frontend/src-tauri/src/database/setup.rs
function initialize_database_on_startup (line 9) | pub async fn initialize_database_on_startup(app: &AppHandle) -> Result<(...
FILE: frontend/src-tauri/src/groq/groq.rs
type GroqModel (line 8) | pub struct GroqModel {
type GroqApiModel (line 15) | struct GroqApiModel {
type GroqApiResponse (line 24) | struct GroqApiResponse {
type CacheEntry (line 29) | struct CacheEntry {
constant CACHE_TTL_SECS (line 38) | const CACHE_TTL_SECS: u64 = 300;
constant FALLBACK_MODELS (line 41) | const FALLBACK_MODELS: &[&str] = &["llama-3.3-70b-versatile"];
function get_fallback_models (line 44) | fn get_fallback_models() -> Vec<GroqModel> {
function is_chat_model (line 55) | fn is_chat_model(model_id: &str) -> bool {
function get_groq_models (line 72) | pub async fn get_groq_models(api_key: Option<String>) -> Result<Vec<Groq...
function clear_cache (line 160) | pub fn clear_cache() {
FILE: frontend/src-tauri/src/lib.rs
type RecordingArgs (line 72) | struct RecordingArgs {
type TranscriptionStatus (line 77) | struct TranscriptionStatus {
function start_recording (line 84) | async fn start_recording<R: Runtime>(
function stop_recording (line 145) | async fn stop_recording<R: Runtime>(app: AppHandle<R>, args: RecordingAr...
function is_recording (line 209) | async fn is_recording() -> bool {
function get_transcription_status (line 214) | fn get_transcription_status() -> TranscriptionStatus {
function read_audio_file (line 223) | fn read_audio_file(file_path: String) -> Result<Vec<u8>, String> {
function save_transcript (line 231) | async fn save_transcript(file_path: String, content: String) -> Result<(...
function start_audio_level_monitoring (line 252) | async fn start_audio_level_monitoring<R: Runtime>(
function stop_audio_level_monitoring (line 267) | async fn stop_audio_level_monitoring() -> Result<(), String> {
function is_audio_level_monitoring (line 276) | async fn is_audio_level_monitoring() -> bool {
function get_audio_devices (line 285) | async fn get_audio_devices() -> Result<Vec<AudioDevice>, String> {
function trigger_microphone_permission (line 292) | async fn trigger_microphone_permission() -> Result<bool, String> {
function start_recording_with_devices (line 298) | async fn start_recording_with_devices<R: Runtime>(
function start_recording_with_devices_and_meeting (line 307) | async fn start_recording_with_devices_and_meeting<R: Runtime>(
function set_language_preference (line 376) | async fn set_language_preference(language: String) -> Result<(), String> {
function get_language_preference_internal (line 386) | pub fn get_language_preference_internal() -> Option<String> {
function run (line 390) | pub fn run() {
FILE: frontend/src-tauri/src/lib_old_complex.rs
constant CHUNK_DURATION_MS (line 54) | const CHUNK_DURATION_MS: u32 = 30000;
constant WHISPER_SAMPLE_RATE (line 55) | const WHISPER_SAMPLE_RATE: u32 = 16000;
constant WAV_SAMPLE_RATE (line 56) | const WAV_SAMPLE_RATE: u32 = 44100;
constant WAV_CHANNELS (line 57) | const WAV_CHANNELS: u16 = 2;
constant WHISPER_CHANNELS (line 58) | const WHISPER_CHANNELS: u16 = 1;
constant SENTENCE_TIMEOUT_MS (line 59) | const SENTENCE_TIMEOUT_MS: u64 = 1000;
constant MIN_CHUNK_DURATION_MS (line 60) | const MIN_CHUNK_DURATION_MS: u32 = 2000;
constant MIN_RECORDING_DURATION_MS (line 61) | const MIN_RECORDING_DURATION_MS: u64 = 2000;
constant MAX_AUDIO_QUEUE_SIZE (line 62) | const MAX_AUDIO_QUEUE_SIZE: usize = 50;
constant VAD_SILENCE_THRESHOLD (line 66) | const VAD_SILENCE_THRESHOLD: f32 = 0.003;
constant VAD_RMS_SILENCE_THRESHOLD (line 67) | const VAD_RMS_SILENCE_THRESHOLD: f32 = 0.002;
constant CHUNK_SILENCE_THRESHOLD (line 68) | const CHUNK_SILENCE_THRESHOLD: f32 = 0.002;
constant CHUNK_AVG_SILENCE_THRESHOLD (line 69) | const CHUNK_AVG_SILENCE_THRESHOLD: f32 = 0.003;
constant TRANSCRIPT_SERVER_URL (line 72) | const TRANSCRIPT_SERVER_URL: &str = "http://127.0.0.1:8178";
type RecordingArgs (line 75) | struct RecordingArgs {
type TranscriptionStatus (line 80) | struct TranscriptionStatus {
type TranscriptUpdate (line 87) | struct TranscriptUpdate {
type AudioChunk (line 97) | struct AudioChunk {
type TranscriptSegment (line 106) | struct TranscriptSegment {
type TranscriptResponse (line 113) | struct TranscriptResponse {
type TranscriptAccumulator (line 120) | struct TranscriptAccumulator {
method new (line 131) | fn new() -> Self {
method set_chunk_context (line 143) | fn set_chunk_context(&mut self, chunk_id: u64, chunk_start_time: f64, ...
method add_segment (line 150) | fn add_segment(&mut self, segment: &TranscriptSegment) -> Option<Trans...
method check_timeout (line 235) | fn check_timeout(&mut self) -> Option<TranscriptUpdate> {
function audio_collection_task (line 270) | async fn audio_collection_task<R: Runtime>(
function send_audio_chunk (line 732) | async fn send_audio_chunk(chunk: Vec<f32>, client: &reqwest::Client, str...
function transcribe_audio_chunk_whisper_rs (line 789) | async fn transcribe_audio_chunk_whisper_rs(chunk: Vec<f32>) -> Result<Tr...
function transcription_worker (line 859) | async fn transcription_worker<R: Runtime>(
function whisper_rs_transcription_worker (line 1112) | async fn whisper_rs_transcription_worker<R: Runtime>(
function start_recording (line 1307) | async fn start_recording<R: Runtime>(app: AppHandle<R>) -> Result<(), St...
function stop_recording (line 1480) | async fn stop_recording<R: Runtime>(app: AppHandle<R>, args: RecordingAr...
function is_recording (line 1748) | fn is_recording() -> bool {
function get_transcription_status (line 1753) | fn get_transcription_status() -> TranscriptionStatus {
function read_audio_file (line 1787) | fn read_audio_file(file_path: String) -> Result<Vec<u8>, String> {
function save_transcript (line 1795) | async fn save_transcript(file_path: String, content: String) -> Result<(...
function init_analytics (line 1816) | async fn init_analytics() -> Result<(), String> {
function disable_analytics (line 1833) | async fn disable_analytics() -> Result<(), String> {
function track_event (line 1842) | async fn track_event(event_name: String, properties: Option<std::collect...
function identify_user (line 1853) | async fn identify_user(user_id: String, properties: Option<std::collecti...
function track_meeting_started (line 1864) | async fn track_meeting_started(meeting_id: String, meeting_title: String...
function track_recording_started (line 1875) | async fn track_recording_started(meeting_id: String) -> Result<(), Strin...
function track_recording_stopped (line 1886) | async fn track_recording_stopped(meeting_id: String, duration_seconds: O...
function track_meeting_deleted (line 1897) | async fn track_meeting_deleted(meeting_id: String) -> Result<(), String> {
function track_search_performed (line 1908) | async fn track_search_performed(query: String, results_count: usize) -> ...
function track_settings_changed (line 1919) | async fn track_settings_changed(setting_type: String, new_value: String)...
function track_feature_used (line 1930) | async fn track_feature_used(feature_name: String) -> Result<(), String> {
function is_analytics_enabled (line 1941) | async fn is_analytics_enabled() -> bool {
function start_analytics_session (line 1953) | async fn start_analytics_session(user_id: String) -> Result<String, Stri...
function end_analytics_session (line 1964) | async fn end_analytics_session() -> Result<(), String> {
function track_daily_active_user (line 1977) | async fn track_daily_active_user() -> Result<(), String> {
function track_user_first_launch (line 1988) | async fn track_user_first_launch() -> Result<(), String> {
function track_summary_generation_started (line 2000) | async fn track_summary_generation_started(model_provider: String, model_...
function track_summary_generation_completed (line 2011) | async fn track_summary_generation_completed(model_provider: String, mode...
function track_summary_regenerated (line 2022) | async fn track_summary_regenerated(model_provider: String, model_name: S...
function track_model_changed (line 2033) | async fn track_model_changed(old_provider: String, old_model: String, ne...
function track_custom_prompt_used (line 2044) | async fn track_custom_prompt_used(prompt_length: usize) -> Result<(), St...
function is_analytics_session_active (line 2055) | async fn is_analytics_session_active() -> bool {
function stereo_to_mono (line 2066) | fn stereo_to_mono(stereo: &[i16]) -> Vec<i16> {
function run (line 2077) | pub fn run() {
function resample_audio (line 2178) | fn resample_audio(samples: &[f32], from_rate: u32, to_rate: u32) -> Vec<...
function whisper_init (line 2201) | async fn whisper_init() -> Result<(), String> {
function whisper_get_available_models (line 2216) | async fn whisper_get_available_models() -> Result<Vec<ModelInfo>, String> {
function whisper_load_model (line 2228) | async fn whisper_load_model(model_name: String) -> Result<(), String> {
function whisper_get_current_model (line 2240) | async fn whisper_get_current_model() -> Result<Option<String>, String> {
function whisper_is_model_loaded (line 2251) | async fn whisper_is_model_loaded() -> Result<bool, String> {
function whisper_transcribe_audio (line 2262) | async fn whisper_transcribe_audio(audio_data: Vec<f32>) -> Result<String...
function whisper_get_models_directory (line 2274) | async fn whisper_get_models_directory() -> Result<String, String> {
function whisper_download_model (line 2286) | async fn whisper_download_model(app_handle: tauri::AppHandle, model_name...
function whisper_cancel_download (line 2337) | async fn whisper_cancel_download(model_name: String) -> Result<(), Strin...
function get_audio_devices (line 2349) | async fn get_audio_devices() -> Result<Vec<AudioDevice>, String> {
function start_recording_with_devices (line 2354) | async fn start_recording_with_devices(
function start_recording_with_custom_devices (line 2391) | async fn start_recording_with_custom_devices(
FILE: frontend/src-tauri/src/main.rs
function main (line 9) | fn main() {
FILE: frontend/src-tauri/src/notifications/commands.rs
type NotificationManagerState (line 15) | pub type NotificationManagerState<R> = Arc<RwLock<Option<NotificationMan...
function initialize_notification_manager (line 18) | pub async fn initialize_notification_manager<R: Runtime>(
function get_notification_settings (line 32) | pub async fn get_notification_settings(
function set_notification_settings (line 47) | pub async fn set_notification_settings(
function request_notification_permission (line 64) | pub async fn request_notification_permission(
function show_notification (line 80) | pub async fn show_notification(
function show_test_notification (line 97) | pub async fn show_test_notification(
function is_dnd_active (line 113) | pub async fn is_dnd_active(
function get_system_dnd_status (line 126) | pub async fn get_system_dnd_status(
function set_manual_dnd (line 139) | pub async fn set_manual_dnd(
function set_notification_consent (line 156) | pub async fn set_notification_consent(
function clear_notifications (line 173) | pub async fn clear_notifications(
function is_notification_system_ready (line 189) | pub async fn is_notification_system_ready(
function initialize_notification_manager_manual (line 202) | pub async fn initialize_notification_manager_manual(
function test_notification_with_auto_consent (line 231) | pub async fn test_notification_with_auto_consent(
function get_notification_stats (line 269) | pub async fn get_notification_stats(
function show_recording_started_notification (line 286) | pub async fn show_recording_started_notification<R: Runtime>(
function show_recording_stopped_notification (line 363) | pub async fn show_recording_stopped_notification<R: Runtime>(
function show_recording_paused_notification (line 408) | pub async fn show_recording_paused_notification(
function show_recording_resumed_notification (line 421) | pub async fn show_recording_resumed_notification(
function show_transcription_complete_notification (line 434) | pub async fn show_transcription_complete_notification(
function show_system_error_notification (line 448) | pub async fn show_system_error_notification(
FILE: frontend/src-tauri/src/notifications/manager.rs
type NotificationManager (line 13) | pub struct NotificationManager<R: Runtime> {
function new (line 24) | pub async fn new(app_handle: AppHandle<R>) -> Result<Self> {
function initialize (line 45) | pub async fn initialize(&self) -> Result<()> {
function show_notification (line 81) | pub async fn show_notification(&self, notification: Notification) -> Res...
function show_recording_started (line 101) | pub async fn show_recording_started(&self, meeting_name: Option<String>)...
function show_recording_stopped (line 116) | pub async fn show_recording_stopped(&self) -> Result<()> {
function show_recording_paused (line 127) | pub async fn show_recording_paused(&self) -> Result<()> {
function show_recording_resumed (line 138) | pub async fn show_recording_resumed(&self) -> Result<()> {
function show_transcription_complete (line 149) | pub async fn show_transcription_complete(&self, file_path: Option<String...
function show_meeting_reminder (line 160) | pub async fn show_meeting_reminder(&self, minutes_until: u64, meeting_ti...
function show_system_error (line 176) | pub async fn show_system_error(&self, error: String) -> Result<()> {
function show_test_notification (line 187) | pub async fn show_test_notification(&self) -> Result<()> {
function get_settings (line 193) | pub async fn get_settings(&self) -> NotificationSettings {
function update_settings (line 198) | pub async fn update_settings(&self, new_settings: NotificationSettings) ...
function is_dnd_active (line 219) | pub async fn is_dnd_active(&self) -> bool {
function get_system_dnd_status (line 236) | pub async fn get_system_dnd_status(&self) -> bool {
function set_manual_dnd (line 241) | pub async fn set_manual_dnd(&self, enabled: bool) -> Result<()> {
function request_permission (line 253) | pub async fn request_permission(&self) -> Result<bool> {
function set_consent (line 265) | pub async fn set_consent(&self, consent: bool) -> Result<()> {
function should_show_notification (line 277) | async fn should_show_notification(&self, notification: &Notification) ->...
function clear_notifications (line 308) | pub async fn clear_notifications(&self) -> Result<()> {
function is_ready (line 313) | pub async fn is_ready(&self) -> bool {
function get_stats (line 318) | pub async fn get_stats(&self) -> NotificationStats {
type NotificationStats (line 334) | pub struct NotificationStats {
FILE: frontend/src-tauri/src/notifications/settings.rs
type NotificationSettings (line 9) | pub struct NotificationSettings {
type NotificationPreferences (line 39) | pub struct NotificationPreferences {
method default (line 66) | fn default() -> Self {
method default (line 82) | fn default() -> Self {
type ConsentManager (line 97) | pub struct ConsentManager<R: Runtime> {
function new (line 104) | pub fn new(app_handle: AppHandle<R>) -> Result<Self> {
function get_settings_path (line 114) | fn get_settings_path() -> Result<PathBuf> {
function load_settings (line 130) | pub async fn load_settings(&self) -> Result<NotificationSettings> {
function save_settings (line 144) | pub async fn save_settings(&self, settings: &NotificationSettings) -> Re...
function has_consent (line 153) | pub async fn has_consent(&self) -> bool {
function has_system_permission (line 161) | pub async fn has_system_permission(&self) -> bool {
function set_consent (line 169) | pub async fn set_consent(&self, consent: bool) -> Result<()> {
function set_system_permission (line 179) | pub async fn set_system_permission(&self, granted: bool) -> Result<()> {
function update_preferences (line 189) | pub async fn update_preferences(&self, preferences: NotificationPreferen...
function set_dnd_mode (line 199) | pub async fn set_dnd_mode(&self, enabled: bool) -> Result<()> {
function should_show_notifications (line 209) | pub async fn should_show_notifications(&self) -> bool {
function initialize_on_first_launch (line 221) | pub async fn initialize_on_first_launch(&self) -> Result<NotificationSet...
function get_settings_with_migration (line 234) | pub async fn get_settings_with_migration(&self) -> Result<NotificationSe...
function get_default_settings (line 246) | pub fn get_default_settings() -> NotificationSettings {
function validate_settings (line 251) | pub fn validate_settings(settings: &NotificationSettings) -> Result<()> {
function merge_with_defaults (line 263) | pub fn merge_with_defaults(partial: NotificationSettings) -> Notificatio...
FILE: frontend/src-tauri/src/notifications/system.rs
type SystemNotificationHandler (line 9) | pub struct SystemNotificationHandler<R: Runtime> {
function new (line 14) | pub fn new(app_handle: AppHandle<R>) -> Self {
function show_notification (line 21) | pub async fn show_notification(&self, notification: Notification) -> Res...
function is_dnd_active (line 51) | pub async fn is_dnd_active(&self) -> bool {
function get_system_dnd_status (line 59) | pub async fn get_system_dnd_status(&self) -> bool {
function request_permission (line 66) | pub async fn request_permission(&self) -> Result<bool> {
function show_test_notification (line 77) | async fn show_test_notification(&self) -> Result<()> {
function should_respect_dnd (line 83) | fn should_respect_dnd(&self, notification: &Notification) -> bool {
function clear_notifications (line 91) | pub async fn clear_notifications(&self) -> Result<()> {
function from (line 104) | fn from(timeout: &NotificationTimeout) -> Self {
FILE: frontend/src-tauri/src/notifications/types.rs
type Notification (line 4) | pub struct Notification {
method new (line 57) | pub fn new(title: impl Into<String>, body: impl Into<String>, notifica...
method with_priority (line 71) | pub fn with_priority(mut self, priority: NotificationPriority) -> Self {
method with_timeout (line 76) | pub fn with_timeout(mut self, timeout: NotificationTimeout) -> Self {
method with_sound (line 81) | pub fn with_sound(mut self, sound: bool) -> Self {
method with_icon (line 86) | pub fn with_icon(mut self, icon: impl Into<String>) -> Self {
method with_id (line 91) | pub fn with_id(mut self, id: impl Into<String>) -> Self {
method add_action (line 96) | pub fn add_action(mut self, action: NotificationAction) -> Self {
method recording_started (line 116) | pub fn recording_started(meeting_name: Option<String>) -> Self {
method recording_stopped (line 127) | pub fn recording_stopped() -> Self {
method recording_paused (line 137) | pub fn recording_paused() -> Self {
method recording_resumed (line 147) | pub fn recording_resumed() -> Self {
method transcription_complete (line 157) | pub fn transcription_complete(file_path: Option<String>) -> Self {
method meeting_reminder (line 168) | pub fn meeting_reminder(minutes_until: u64, meeting_title: Option<Stri...
method system_error (line 179) | pub fn system_error(error: impl Into<String>) -> Self {
method test_notification (line 190) | pub fn test_notification() -> Self {
type NotificationType (line 17) | pub enum NotificationType {
type NotificationPriority (line 29) | pub enum NotificationPriority {
type NotificationTimeout (line 37) | pub enum NotificationTimeout {
type NotificationAction (line 44) | pub struct NotificationAction {
type NotificationActionType (line 51) | pub enum NotificationActionType {
method default (line 103) | fn default() -> Self {
method default (line 109) | fn default() -> Self {
FILE: frontend/src-tauri/src/ollama/metadata.rs
type ModelMetadata (line 12) | pub struct ModelMetadata {
type OllamaShowResponse (line 21) | struct OllamaShowResponse {
type ModelDetails (line 30) | struct ModelDetails {
type CacheEntry (line 38) | struct CacheEntry {
type ModelMetadataCache (line 44) | pub struct ModelMetadataCache {
method new (line 51) | pub fn new(ttl: Duration) -> Self {
method get_or_fetch (line 66) | pub async fn get_or_fetch(
method clear (line 110) | pub async fn clear(&self) {
constant DEFAULT_CONTEXT_SIZES (line 118) | const DEFAULT_CONTEXT_SIZES: &[(&str, usize)] = &[
constant ULTIMATE_FALLBACK (line 130) | const ULTIMATE_FALLBACK: usize = 4000;
function fetch_model_info (line 140) | async fn fetch_model_info(
function extract_context_from_model_info (line 225) | fn extract_context_from_model_info(
function parse_num_ctx_from_modelfile (line 257) | fn parse_num_ctx_from_modelfile(modelfile: &str) -> usize {
function get_fallback_metadata (line 282) | fn get_fallback_metadata(model_name: &str) -> ModelMetadata {
function test_parse_num_ctx_standard (line 319) | fn test_parse_num_ctx_standard() {
function test_parse_num_ctx_with_spaces (line 325) | fn test_parse_num_ctx_with_spaces() {
function test_parse_num_ctx_missing (line 331) | fn test_parse_num_ctx_missing() {
function test_parse_num_ctx_multiple_params (line 337) | fn test_parse_num_ctx_multiple_params() {
function test_fallback_metadata_llama (line 343) | fn test_fallback_metadata_llama() {
function test_fallback_metadata_mistral (line 350) | fn test_fallback_metadata_mistral() {
function test_fallback_metadata_unknown (line 356) | fn test_fallback_metadata_unknown() {
function test_fallback_metadata_phi (line 362) | fn test_fallback_metadata_phi() {
FILE: frontend/src-tauri/src/ollama/ollama.rs
type OllamaError (line 25) | pub enum OllamaError {
method fmt (line 35) | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
type OllamaModel (line 48) | pub struct OllamaModel {
type OllamaApiResponse (line 56) | struct OllamaApiResponse {
type OllamaApiModel (line 61) | struct OllamaApiModel {
function is_localhost_endpoint (line 69) | fn is_localhost_endpoint(endpoint: Option<&str>) -> bool {
function validate_endpoint_url (line 81) | fn validate_endpoint_url(url: &str) -> Result<(), OllamaError> {
function get_ollama_models (line 97) | pub async fn get_ollama_models(endpoint: Option<String>) -> Result<Vec<O...
function get_models_via_http_with_retry (line 132) | async fn get_models_via_http_with_retry(endpoint: Option<&str>) -> Resul...
function get_models_via_http_async (line 161) | async fn get_models_via_http_async(endpoint: Option<&str>) -> Result<Vec...
function get_models_via_cli (line 200) | fn get_models_via_cli() -> Result<Vec<OllamaModel>, String> {
function format_size (line 240) | fn format_size(size: i64) -> String {
type DownloadProgress (line 253) | pub struct DownloadProgress {
function pull_ollama_model (line 260) | pub async fn pull_ollama_model<R: Runtime>(
function delete_ollama_model (line 436) | pub async fn delete_ollama_model(
function get_ollama_model_context (line 489) | pub async fn get_ollama_model_context(
FILE: frontend/src-tauri/src/onboarding.rs
type OnboardingStatus (line 12) | pub struct OnboardingStatus {
type ModelStatus (line 21) | pub struct ModelStatus {
method default (line 27) | fn default() -> Self {
function load_onboarding_status (line 43) | pub async fn load_onboarding_status<R: Runtime>(
function save_onboarding_status (line 77) | pub async fn save_onboarding_status<R: Runtime>(
function reset_onboarding_status (line 108) | pub async fn reset_onboarding_status<R: Runtime>(
function get_onboarding_status (line 129) | pub async fn get_onboarding_status<R: Runtime>(
function save_onboarding_status_cmd (line 149) | pub async fn save_onboarding_status_cmd<R: Runtime>(
function reset_onboarding_status_cmd (line 159) | pub async fn reset_onboarding_status_cmd<R: Runtime>(
function complete_onboarding (line 168) | pub async fn complete_onboarding<R: Runtime>(
FILE: frontend/src-tauri/src/openai/openai.rs
type OpenAIModel (line 8) | pub struct OpenAIModel {
type OpenAIApiModel (line 14) | struct OpenAIApiModel {
type OpenAIApiResponse (line 24) | struct OpenAIApiResponse {
type CacheEntry (line 29) | struct CacheEntry {
constant CACHE_TTL_SECS (line 38) | const CACHE_TTL_SECS: u64 = 300;
constant FALLBACK_MODELS (line 41) | const FALLBACK_MODELS: &[&str] = &[
function get_fallback_models (line 68) | fn get_fallback_models() -> Vec<OpenAIModel> {
function is_chat_model (line 76) | fn is_chat_model(model_id: &str) -> bool {
function get_openai_models (line 104) | pub async fn get_openai_models(api_key: Option<String>) -> Result<Vec<Op...
function clear_cache (line 189) | pub fn clear_cache() {
FILE: frontend/src-tauri/src/openrouter/openrouter.rs
type OpenRouterModel (line 6) | pub struct OpenRouterModel {
type OpenRouterApiModel (line 15) | struct OpenRouterApiModel {
type TopProvider (line 26) | struct TopProvider {
type Pricing (line 31) | struct Pricing {
type OpenRouterResponse (line 37) | struct OpenRouterResponse {
function get_openrouter_models (line 42) | pub fn get_openrouter_models() -> Result<Vec<OpenRouterModel>, String> {
FILE: frontend/src-tauri/src/parakeet_engine/commands.rs
function set_models_directory (line 15) | pub fn set_models_directory<R: Runtime>(app: &AppHandle<R>) {
function get_models_directory (line 36) | fn get_models_directory() -> Option<PathBuf> {
function parakeet_init (line 41) | pub async fn parakeet_init() -> Result<(), String> {
function parakeet_get_available_models (line 55) | pub async fn parakeet_get_available_models() -> Result<Vec<ModelInfo>, S...
function parakeet_load_model (line 72) | pub async fn parakeet_load_model<R: Runtime>(
function parakeet_get_current_model (line 126) | pub async fn parakeet_get_current_model() -> Result<Option<String>, Stri...
function parakeet_is_model_loaded (line 140) | pub async fn parakeet_is_model_loaded() -> Result<bool, String> {
function parakeet_has_available_models (line 154) | pub async fn parakeet_has_available_models() -> Result<bool, String> {
function parakeet_validate_model_ready (line 179) | pub async fn parakeet_validate_model_ready() -> Result<String, String> {
function parakeet_validate_model_ready_with_config (line 230) | pub async fn parakeet_validate_model_ready_with_config<R: tauri::Runtime>(
function parakeet_transcribe_audio (line 347) | pub async fn parakeet_transcribe_audio(audio_data: Vec<f32>) -> Result<S...
function parakeet_get_models_directory (line 364) | pub async fn parakeet_get_models_directory() -> Result<String, String> {
function parakeet_download_model (line 379) | pub async fn parakeet_download_model<R: Runtime>(
function parakeet_cancel_download (line 467) | pub async fn parakeet_cancel_download<R: Runtime>(
function parakeet_retry_download (line 500) | pub async fn parakeet_retry_download<R: Runtime>(
function parakeet_delete_corrupted_model (line 542) | pub async fn parakeet_delete_corrupted_model(model_name: String) -> Resu...
function open_parakeet_models_folder (line 560) | pub async fn open_parakeet_models_folder() -> Result<(), String> {
FILE: frontend/src-tauri/src/parakeet_engine/model.rs
type DecoderState (line 13) | pub type DecoderState = (Array3<f32>, Array3<f32>);
constant SUBSAMPLING_FACTOR (line 15) | const SUBSAMPLING_FACTOR: usize = 8;
constant WINDOW_SIZE (line 16) | const WINDOW_SIZE: f32 = 0.01;
constant MAX_TOKENS_PER_STEP (line 17) | const MAX_TOKENS_PER_STEP: usize = 10;
type TimestampedResult (line 23) | pub struct TimestampedResult {
type ParakeetError (line 30) | pub enum ParakeetError {
type ParakeetModel (line 45) | pub struct ParakeetModel {
method new (line 61) | pub fn new<P: AsRef<Path>>(model_dir: P, quantized: bool) -> Result<Se...
method init_session (line 85) | fn init_session<P: AsRef<Path>>(
method load_vocab (line 139) | fn load_vocab<P: AsRef<Path>>(model_dir: P) -> Result<(Vec<String>, i3...
method preprocess (line 177) | pub fn preprocess(
method encode (line 201) | pub fn encode(
method create_decoder_state (line 227) | pub fn create_decoder_state(&self) -> Result<DecoderState, ParakeetErr...
method decode_step (line 264) | pub fn decode_step(
method recognize_batch (line 321) | pub fn recognize_batch(
method decode_sequence (line 343) | fn decode_sequence(
method decode_tokens (line 417) | fn decode_tokens(&self, ids: Vec<i32>, timestamps: Vec<usize>) -> Time...
method transcribe_samples (line 455) | pub fn transcribe_samples(
method drop (line 55) | fn drop(&mut self) {
FILE: frontend/src-tauri/src/parakeet_engine/parakeet_engine.rs
type QuantizationType (line 15) | pub enum QuantizationType {
method default (line 21) | fn default() -> Self {
type ModelStatus (line 28) | pub enum ModelStatus {
type DownloadProgress (line 38) | pub struct DownloadProgress {
method new (line 54) | pub fn new(downloaded: u64, total: u64, speed_mbps: f64) -> Self {
type ModelInfo (line 73) | pub struct ModelInfo {
type ParakeetEngineError (line 84) | pub enum ParakeetEngineError {
method fmt (line 94) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method from (line 109) | fn from(err: std::io::Error) -> Self {
type ParakeetEngine (line 114) | pub struct ParakeetEngine {
method new_with_models_dir (line 126) | pub fn new_with_models_dir(models_dir: Option<PathBuf>) -> Result<Self> {
method discover_models (line 167) | pub async fn discover_models(&self) -> Result<Vec<ModelInfo>> {
method validate_model_directory (line 262) | async fn validate_model_directory(&self, model_dir: &PathBuf) -> Resul...
method clean_incomplete_model_directory (line 330) | async fn clean_incomplete_model_directory(&self, model_dir: &PathBuf) ...
method load_model (line 376) | pub async fn load_model(&self, model_name: &str) -> Result<()> {
method unload_model (line 430) | pub async fn unload_model(&self) -> bool {
method get_current_model (line 444) | pub async fn get_current_model(&self) -> Option<String> {
method is_model_loaded (line 449) | pub async fn is_model_loaded(&self) -> bool {
method transcribe_audio (line 454) | pub async fn transcribe_audio(&self, audio_data: Vec<f32>) -> Result<S...
method get_models_directory (line 478) | pub async fn get_models_directory(&self) -> PathBuf {
method delete_model (line 483) | pub async fn delete_model(&self, model_name: &str) -> Result<String> {
method download_model (line 529) | pub async fn download_model(
method download_model_detailed (line 543) | pub async fn download_model_detailed(
method cancel_download (line 1051) | pub async fn cancel_download(&self, model_name: &str) -> Result<()> {
FILE: frontend/src-tauri/src/state.rs
type AppState (line 3) | pub struct AppState {
FILE: frontend/src-tauri/src/summary/commands.rs
type SummaryResponse (line 12) | pub struct SummaryResponse {
type ProcessTranscriptResponse (line 24) | pub struct ProcessTranscriptResponse {
function api_save_meeting_summary (line 33) | pub async fn api_save_meeting_summary<R: Runtime>(
function api_get_summary (line 71) | pub async fn api_get_summary<R: Runtime>(
function api_process_transcript (line 167) | pub async fn api_process_transcript<R: Runtime>(
function api_cancel_summary (line 247) | pub async fn api_cancel_summary<R: Runtime>(
FILE: frontend/src-tauri/src/summary/llm_client.rs
constant REQUEST_TIMEOUT_DURATION (line 8) | const REQUEST_TIMEOUT_DURATION: Duration = Duration::from_secs(300);
type ChatMessage (line 12) | pub struct ChatMessage {
type ChatRequest (line 19) | pub struct ChatRequest {
type ChatResponse (line 32) | pub struct ChatResponse {
type Choice (line 37) | pub struct Choice {
type MessageContent (line 42) | pub struct MessageContent {
type ClaudeRequest (line 48) | pub struct ClaudeRequest {
type ClaudeChatResponse (line 57) | pub struct ClaudeChatResponse {
type ClaudeChatContent (line 62) | pub struct ClaudeChatContent {
type LLMProvider (line 68) | pub enum LLMProvider {
method from_str (line 80) | pub fn from_str(s: &str) -> Result<Self, String> {
function generate_summary (line 113) | pub async fn generate_summary(
function provider_name (line 336) | fn provider_name(provider: &LLMProvider) -> &str {
FILE: frontend/src-tauri/src/summary/mod.rs
type CustomOpenAIConfig (line 15) | pub struct CustomOpenAIConfig {
FILE: frontend/src-tauri/src/summary/processor.rs
function rough_token_count (line 16) | pub fn rough_token_count(s: &str) -> usize {
function chunk_text (line 31) | pub fn chunk_text(text: &str, chunk_size_tokens: usize, overlap_tokens: ...
function clean_llm_markdown_output (line 102) | pub fn clean_llm_markdown_output(markdown: &str) -> String {
function extract_meeting_name_from_markdown (line 131) | pub fn extract_meeting_name_from_markdown(markdown: &str) -> Option<Stri...
function generate_meeting_summary (line 159) | pub async fn generate_meeting_summary(
FILE: frontend/src-tauri/src/summary/service.rs
type SummaryService (line 26) | pub struct SummaryService;
method register_cancellation_token (line 30) | fn register_cancellation_token(meeting_id: &str) -> CancellationToken {
method cancel_summary (line 40) | pub fn cancel_summary(meeting_id: &str) -> bool {
method cleanup_cancellation_token (line 53) | fn cleanup_cancellation_token(meeting_id: &str) {
method process_transcript_background (line 75) | pub async fn process_transcript_background<R: tauri::Runtime>(
method update_process_failed (line 344) | async fn update_process_failed(pool: &SqlitePool, meeting_id: &str, er...
FILE: frontend/src-tauri/src/summary/summary_engine/client.rs
type Request (line 25) | enum Request {
type Response (line 41) | enum Response {
function init_sidecar_manager (line 60) | pub async fn init_sidecar_manager(app_data_dir: PathBuf) -> Result<()> {
function get_sidecar_manager (line 68) | async fn get_sidecar_manager() -> Result<Arc<SidecarManager>> {
function get_cached_model_path (line 76) | fn get_cached_model_path(app_data_dir: &PathBuf, model_name: &str) -> Re...
function generate_with_builtin (line 129) | pub async fn generate_with_builtin(
function shutdown_sidecar_gracefully (line 241) | pub async fn shutdown_sidecar_gracefully() -> Result<()> {
function force_shutdown_sidecar (line 264) | pub async fn force_shutdown_sidecar() -> Result<()> {
function is_sidecar_healthy (line 280) | pub async fn is_sidecar_healthy() -> bool {
function test_request_serialization (line 297) | fn test_request_serialization() {
function test_response_deserialization (line 317) | fn test_response_deserialization() {
function test_error_response_deserialization (line 331) | fn test_error_response_deserialization() {
FILE: frontend/src-tauri/src/summary/summary_engine/commands.rs
type ModelManagerState (line 16) | pub struct ModelManagerState(pub Arc<Mutex<Option<Arc<ModelManager>>>>);
function init_model_manager (line 19) | pub async fn init_model_manager<R: Runtime>(app: &AppHandle<R>) -> anyho...
function builtin_ai_list_models (line 39) | pub async fn builtin_ai_list_models<R: Runtime>(
function builtin_ai_get_model_info (line 68) | pub async fn builtin_ai_get_model_info<R: Runtime>(
function builtin_ai_download_model (line 98) | pub async fn builtin_ai_download_model<R: Runtime>(
function builtin_ai_cancel_download (line 185) | pub async fn builtin_ai_cancel_download<R: Runtime>(
function builtin_ai_delete_model (line 217) | pub async fn builtin_ai_delete_model(
function builtin_ai_is_model_ready (line 237) | pub async fn builtin_ai_is_model_ready<R: Runtime>(
function builtin_ai_get_available_summary_model (line 278) | pub async fn builtin_ai_get_available_summary_model<R: Runtime>(
function init_model_manager_at_startup (line 331) | pub async fn init_model_manager_at_startup<R: Runtime>(
function builtin_ai_get_recommended_model (line 362) | pub async fn builtin_ai_get_recommended_model() -> Result<String, String> {
function get_system_ram_gb (line 383) | fn get_system_ram_gb() -> Result<u64, String> {
FILE: frontend/src-tauri/src/summary/summary_engine/model_manager.rs
type DownloadProgress (line 25) | pub struct DownloadProgress {
method new (line 41) | pub fn new(downloaded: u64, total: u64, speed_mbps: f64) -> Self {
type ModelStatus (line 61) | pub enum ModelStatus {
type ModelInfo (line 80) | pub struct ModelInfo {
type ModelManager (line 110) | pub struct ModelManager {
method new (line 126) | pub fn new() -> Result<Self> {
method new_with_models_dir (line 131) | pub fn new_with_models_dir(models_dir: Option<PathBuf>) -> Result<Self> {
method init (line 168) | pub async fn init(&self) -> Result<()> {
method scan_models (line 182) | pub async fn scan_models(&self) -> Result<()> {
method list_models (line 301) | pub async fn list_models(&self) -> Vec<ModelInfo> {
method get_model_info (line 311) | pub async fn get_model_info(&self, model_name: &str) -> Option<ModelIn...
method is_model_ready (line 321) | pub async fn is_model_ready(&self, model_name: &str, refresh: bool) ->...
method download_model (line 337) | pub async fn download_model(
method download_model_detailed (line 351) | pub async fn download_model_detailed(
method validate_gguf_file (line 773) | async fn validate_gguf_file(&self, path: &PathBuf) -> Result<()> {
method cancel_download (line 796) | pub async fn cancel_download(&self, model_name: &str) -> Result<()> {
method delete_model (line 823) | pub async fn delete_model(&self, model_name: &str) -> Result<()> {
method get_models_directory (line 848) | pub fn get_models_directory(&self) -> PathBuf {
FILE: frontend/src-tauri/src/summary/summary_engine/models.rs
type SamplingParams (line 14) | pub struct SamplingParams {
type ModelDef (line 30) | pub struct ModelDef {
function get_available_models (line 66) | pub fn get_available_models() -> Vec<ModelDef> {
function get_model_by_name (line 107) | pub fn get_model_by_name(name: &str) -> Option<ModelDef> {
function get_default_model (line 112) | pub fn get_default_model() -> ModelDef {
function get_model_path (line 120) | pub fn get_model_path(app_data_dir: &PathBuf, model_name: &str) -> Resul...
function get_models_directory (line 131) | pub fn get_models_directory(app_data_dir: &PathBuf) -> PathBuf {
constant GEMMA3_TEMPLATE (line 140) | pub const GEMMA3_TEMPLATE: &str = "\
function format_prompt (line 157) | pub fn format_prompt(
constant DEFAULT_MAX_TOKENS (line 179) | pub const DEFAULT_MAX_TOKENS: i32 = 4096;
constant DEFAULT_IDLE_TIMEOUT_SECS (line 182) | pub const DEFAULT_IDLE_TIMEOUT_SECS: u64 = 300;
constant GENERATION_TIMEOUT_SECS (line 185) | pub const GENERATION_TIMEOUT_SECS: u64 = 900;
FILE: frontend/src-tauri/src/summary/summary_engine/sidecar.rs
type SidecarManager (line 25) | pub struct SidecarManager {
method new (line 78) | pub fn new(_app_data_dir: PathBuf) -> Result<Self> {
method resolve_helper_binary (line 108) | fn resolve_helper_binary() -> Result<PathBuf> {
method ensure_running (line 263) | pub async fn ensure_running(&self, model_path: PathBuf) -> Result<()> {
method spawn (line 279) | async fn spawn(&self, model_path: PathBuf) -> Result<()> {
method send_request (line 352) | pub async fn send_request(&self, request_json: String, timeout: Durati...
method read_response (line 393) | async fn read_response(&self) -> Result<String> {
method send_ping (line 413) | async fn send_ping(&self) -> Result<()> {
method shutdown_gracefully (line 445) | pub async fn shutdown_gracefully(&self) -> Result<()> {
method shutdown (line 476) | pub async fn shutdown(&self) -> Result<()> {
method is_healthy (line 540) | pub fn is_healthy(&self) -> bool {
method update_activity (line 545) | async fn update_activity(&self) {
method seconds_since_activity (line 551) | async fn seconds_since_activity(&self) -> u64 {
method start_health_check_loop (line 557) | fn start_health_check_loop(&self) {
method start_idle_check_loop (line 605) | fn start_idle_check_loop(&self) {
type RequestGuard (line 59) | struct RequestGuard {
method new (line 64) | fn new(counter: Arc<AtomicUsize>) -> Self {
method drop (line 71) | fn drop(&mut self) {
method drop (line 662) | fn drop(&mut self) {
FILE: frontend/src-tauri/src/summary/template_commands.rs
type TemplateInfo (line 8) | pub struct TemplateInfo {
type TemplateDetails (line 21) | pub struct TemplateDetails {
function api_list_templates (line 43) | pub async fn api_list_templates<R: Runtime>(
function api_get_template_details (line 72) | pub async fn api_get_template_details<R: Runtime>(
function api_validate_template (line 108) | pub async fn api_validate_template<R: Runtime>(
function test_list_templates (line 131) | async fn test_list_templates() {
function test_validate_template_valid (line 140) | async fn test_validate_template_valid() {
function test_validate_template_invalid (line 161) | async fn test_validate_template_invalid() {
FILE: frontend/src-tauri/src/summary/templates/defaults.rs
constant DAILY_STANDUP (line 7) | pub const DAILY_STANDUP: &str = include_str!("../../../templates/daily_s...
constant STANDARD_MEETING (line 10) | pub const STANDARD_MEETING: &str = include_str!("../../../templates/stan...
function get_builtin_templates (line 15) | pub fn get_builtin_templates() -> Vec<(&'static str, &'static str)> {
function get_builtin_template (line 29) | pub fn get_builtin_template(id: &str) -> Option<&'static str> {
function list_builtin_template_ids (line 38) | pub fn list_builtin_template_ids() -> Vec<&'static str> {
function test_builtin_templates_valid_json (line 47) | fn test_builtin_templates_valid_json() {
function test_get_builtin_template (line 60) | fn test_get_builtin_template() {
FILE: frontend/src-tauri/src/summary/templates/loader.rs
function set_bundled_templates_dir (line 12) | pub fn set_bundled_templates_dir(path: PathBuf) {
function get_custom_templates_dir (line 25) | fn get_custom_templates_dir() -> Option<PathBuf> {
function load_bundled_template (line 39) | fn load_bundled_template(template_id: &str) -> Option<String> {
function load_custom_template (line 64) | fn load_custom_template(template_id: &str) -> Option<String> {
function get_template (line 95) | pub fn get_template(template_id: &str) -> Result<Template, String> {
function validate_and_parse_template (line 127) | pub fn validate_and_parse_template(json_content: &str) -> Result<Templat...
function list_template_ids (line 142) | pub fn list_template_ids() -> Vec<String> {
function list_templates (line 203) | pub fn list_templates() -> Vec<(String, String, String)> {
function test_get_builtin_template (line 225) | fn test_get_builtin_template() {
function test_get_nonexistent_template (line 235) | fn test_get_nonexistent_template() {
function test_list_template_ids (line 241) | fn test_list_template_ids() {
function test_validate_invalid_json (line 248) | fn test_validate_invalid_json() {
FILE: frontend/src-tauri/src/summary/templates/mod.rs
function test_module_integration (line 56) | fn test_module_integration() {
function test_template_metadata (line 73) | fn test_template_metadata() {
FILE: frontend/src-tauri/src/summary/templates/types.rs
type TemplateSection (line 5) | pub struct TemplateSection {
type Template (line 26) | pub struct Template {
method validate (line 39) | pub fn validate(&self) -> Result<(), String> {
method to_markdown_structure (line 74) | pub fn to_markdown_structure(&self) -> String {
method to_section_instructions (line 85) | pub fn to_section_instructions(&self) -> String {
function test_validate_valid_template (line 117) | fn test_validate_valid_template() {
function test_validate_empty_name (line 136) | fn test_validate_empty_name() {
function test_validate_invalid_format (line 147) | fn test_validate_invalid_format() {
FILE: frontend/src-tauri/src/tray.rs
type RecordingState (line 9) | pub enum RecordingState {
function create_tray (line 19) | pub fn create_tray<R: Runtime>(app: &AppHandle<R>) -> tauri::Result<()> {
function handle_menu_event (line 37) | fn handle_menu_event<R: Runtime>(app: &AppHandle<R>, item_id: &str) {
function toggle_recording_handler (line 55) | fn toggle_recording_handler<R: Runtime>(app: &AppHandle<R>) {
function pause_recording_handler (line 117) | fn pause_recording_handler<R: Runtime>(app: &AppHandle<R>) {
function resume_recording_handler (line 134) | fn resume_recording_handler<R: Runtime>(app: &AppHandle<R>) {
function stop_recording_handler (line 152) | fn stop_recording_handler<R: Runtime>(app: &AppHandle<R>) {
function check_updates_handler (line 203) | fn check_updates_handler<R: Runtime>(app: &AppHandle<R>) {
function update_tray_menu (line 212) | pub fn update_tray_menu<R: Runtime>(app: &AppHandle<R>) {
function set_tray_state (line 222) | pub fn set_tray_state<R: Runtime>(app: &AppHandle<R>, state: RecordingSt...
function get_current_recording_state (line 237) | async fn get_current_recording_state() -> RecordingState {
function check_can_record (line 267) | async fn check_can_record<R: Runtime>(app: &AppHandle<R>) -> bool {
function update_tray_menu_async (line 293) | pub async fn update_tray_menu_async<R: Runtime>(app: &AppHandle<R>) {
function build_menu (line 316) | fn build_menu<R: Runtime>(
function focus_main_window (line 394) | fn focus_main_window<R: Runtime>(app: &AppHandle<R>) {
FILE: frontend/src-tauri/src/utils.rs
function format_timestamp (line 1) | pub fn format_timestamp(seconds: f64) -> String {
function open_system_settings (line 12) | pub async fn open_system_settings(preference_pane: String) -> Result<(),...
FILE: frontend/src-tauri/src/whisper_engine/commands.rs
function set_models_directory (line 15) | pub fn set_models_directory<R: Runtime>(app: &AppHandle<R>) {
function get_models_directory (line 36) | fn get_models_directory() -> Option<PathBuf> {
function whisper_init (line 41) | pub async fn whisper_init() -> Result<(), String> {
function whisper_get_available_models (line 55) | pub async fn whisper_get_available_models() -> Result<Vec<ModelInfo>, St...
function discover_models_standalone (line 75) | fn discover_models_standalone() -> Result<Vec<ModelInfo>, String> {
function whisper_load_model (line 127) | pub async fn whisper_load_model(
function whisper_get_current_model (line 181) | pub async fn whisper_get_current_model() -> Result<Option<String>, Strin...
function whisper_is_model_loaded (line 195) | pub async fn whisper_is_model_loaded() -> Result<bool, String> {
function whisper_has_available_models (line 209) | pub async fn whisper_has_available_models() -> Result<bool, String> {
function whisper_validate_model_ready (line 234) | pub async fn whisper_validate_model_ready() -> Result<String, String> {
function whisper_validate_model_ready_with_config (line 280) | pub async fn whisper_validate_model_ready_with_config<R: tauri::Runtime>(
function whisper_transcribe_audio (line 388) | pub async fn whisper_transcribe_audio(audio_data: Vec<f32>) -> Result<St...
function whisper_get_models_directory (line 407) | pub async fn whisper_get_models_directory() -> Result<String, String> {
function whisper_download_model (line 422) | pub async fn whisper_download_model(
function whisper_cancel_download (line 488) | pub async fn whisper_cancel_download(model_name: String) -> Result<(), S...
function whisper_delete_corrupted_model (line 505) | pub async fn whisper_delete_corrupted_model(model_name: String) -> Resul...
function open_models_folder (line 523) | pub async fn open_models_folder() -> Result<(), String> {
FILE: frontend/src-tauri/src/whisper_engine/parallel_commands.rs
type ParallelProcessorState (line 12) | pub struct ParallelProcessorState {
method new (line 18) | pub fn new() -> Self {
function initialize_parallel_processor (line 27) | pub async fn initialize_parallel_processor(
function start_parallel_processing (line 62) | pub async fn start_parallel_processing(
function pause_parallel_processing (line 86) | pub async fn pause_parallel_processing(
function resume_parallel_processing (line 98) | pub async fn resume_parallel_processing(
function stop_parallel_processing (line 110) | pub async fn stop_parallel_processing(
function get_parallel_processing_status (line 122) | pub async fn get_parallel_processing_status(
function get_system_resources (line 134) | pub async fn get_system_resources(
function check_resource_constraints (line 150) | pub async fn check_resource_constraints(
function calculate_optimal_workers (line 162) | pub async fn calculate_optimal_workers(
function prepare_audio_chunks (line 172) | pub async fn prepare_audio_chunks(
function test_parallel_processing_setup (line 204) | pub async fn test_parallel_processing_setup(
FILE: frontend/src-tauri/src/whisper_engine/parallel_processor.rs
type AudioChunk (line 12) | pub struct AudioChunk {
type TranscriptionResult (line 21) | pub struct TranscriptionResult {
type ProcessingError (line 31) | pub struct ProcessingError {
type ProcessingEvent (line 39) | pub enum ProcessingEvent {
type ParallelConfig (line 52) | pub struct ParallelConfig {
method default (line 62) | fn default() -> Self {
type ParallelProcessor (line 75) | pub struct ParallelProcessor {
method new (line 102) | pub fn new(
method calculate_safe_worker_count (line 134) | pub async fn calculate_safe_worker_count(&self) -> Result<usize> {
method start_processing (line 145) | pub async fn start_processing(
method spawn_workers (line 188) | async fn spawn_workers(&mut self, worker_count: usize, model_name: Str...
method create_worker (line 199) | async fn create_worker(&self, worker_id: u32, model_name: String) -> R...
method process_chunk_safely (line 328) | async fn process_chunk_safely(
method start_resource_monitoring (line 372) | async fn start_resource_monitoring(&self) {
method pause_processing (line 415) | pub async fn pause_processing(&self) {
method resume_processing (line 421) | pub async fn resume_processing(&self) {
method stop_processing (line 427) | pub async fn stop_processing(&mut self) {
method get_processing_status (line 444) | pub async fn get_processing_status(&self) -> ProcessingStatus {
type Worker (line 86) | struct Worker {
type ChunkQueue (line 93) | struct ChunkQueue {
method new (line 460) | fn new() -> Self {
type ProcessingStatus (line 472) | pub struct ProcessingStatus {
FILE: frontend/src-tauri/src/whisper_engine/system_monitor.rs
type SystemResources (line 9) | pub struct SystemResources {
type ResourceLimits (line 19) | pub struct ResourceLimits {
method default (line 27) | fn default() -> Self {
type SystemMonitor (line 37) | pub struct SystemMonitor {
method new (line 44) | pub fn new() -> Self {
method with_limits (line 56) | pub fn with_limits(limits: ResourceLimits) -> Self {
method refresh_system_info (line 62) | pub async fn refresh_system_info(&self) -> Result<()> {
method get_current_resources (line 77) | pub async fn get_current_resources(&self) -> Result<SystemResources> {
method get_cpu_temperature (line 109) | async fn get_cpu_temperature(&self, _system: &System) -> Option<f32> {
method check_resource_constraints (line 117) | pub async fn check_resource_constraints(&self) -> Result<ResourceStatu...
method calculate_safe_worker_count (line 169) | pub async fn calculate_safe_worker_count(&self) -> Result<usize> {
method set_monitoring_enabled (line 191) | pub fn set_monitoring_enabled(&mut self, enabled: bool) {
method get_limits (line 200) | pub fn get_limits(&self) -> &ResourceLimits {
method update_limits (line 204) | pub fn update_limits(&mut self, limits: ResourceLimits) {
type ResourceStatus (line 212) | pub struct ResourceStatus {
method is_healthy (line 221) | pub fn is_healthy(&self) -> bool {
method get_primary_constraint (line 225) | pub fn get_primary_constraint(&self) -> Option<String> {
function create_system_monitor (line 239) | pub fn create_system_monitor() -> SystemMonitor {
function create_system_monitor_with_limits (line 244) | pub fn create_system_monitor_with_limits(
function test_system_monitor_creation (line 263) | async fn test_system_monitor_creation() {
function test_get_current_resources (line 269) | async fn test_get_current_resources() {
function test_safe_worker_count (line 281) | async fn test_safe_worker_count() {
FILE: frontend/src-tauri/src/whisper_engine/whisper_engine.rs
type ModelStatus (line 16) | pub enum ModelStatus {
type ModelInfo (line 25) | pub struct ModelInfo {
type WhisperEngine (line 35) | pub struct WhisperEngine {
method detect_gpu_acceleration (line 53) | fn detect_gpu_acceleration() -> bool {
method new (line 77) | pub fn new() -> Result<Self> {
method new_with_models_dir (line 83) | pub fn new_with_models_dir(models_dir: Option<PathBuf>) -> Result<Self> {
method discover_models (line 169) | pub async fn discover_models(&self) -> Result<Vec<ModelInfo>> {
method load_model (line 259) | pub async fn load_model(&self, model_name: &str) -> Result<()> {
method unload_model (line 345) | pub async fn unload_model(&self) -> bool {
method get_current_model (line 358) | pub async fn get_current_model(&self) -> Option<String> {
method is_model_loaded (line 362) | pub async fn is_model_loaded(&self) -> bool {
method clean_repetitive_text (line 367) | fn clean_repetitive_text(text: &str) -> String {
method is_meaningless_output (line 402) | fn is_meaningless_output(text: &str) -> bool {
method remove_word_repetitions (line 434) | fn remove_word_repetitions<'a>(words: &'a [&'a str]) -> Vec<&'a str> {
method remove_phrase_repetitions (line 461) | fn remove_phrase_repetitions<'a>(words: &'a [&'a str]) -> Vec<&'a str> {
method calculate_repetition_ratio (line 498) | fn calculate_repetition_ratio(text: &str) -> f32 {
method transcribe_audio_with_confidence (line 516) | pub async fn transcribe_audio_with_confidence(&self, audio_data: Vec<f...
method transcribe_audio (line 633) | pub async fn transcribe_audio(&self, audio_data: Vec<f32>, language: O...
method get_models_directory (line 806) | pub async fn get_models_directory(&self) -> PathBuf {
method validate_model_file (line 811) | async fn validate_model_file(&self, model_path: &PathBuf) -> Result<()> {
method delete_model (line 832) | pub async fn delete_model(&self, model_name: &str) -> Result<String> {
method download_model (line 897) | pub async fn download_model(&self, model_name: &str, progress_callback...
method cancel_download (line 1099) | pub async fn cancel_download(&self, model_name: &str) -> Result<()> {
FILE: frontend/src/app/_components/SettingsModal.tsx
type modalType (line 11) | type modalType = "modelSettings" | "deviceSettings" | "languageSettings"...
type SettingsModalsProps (line 20) | interface SettingsModalsProps {
function SettingsModals (line 37) | function SettingsModals({
FILE: frontend/src/app/_components/StatusOverlays.tsx
type StatusOverlaysProps (line 1) | interface StatusOverlaysProps {
type StatusOverlayProps (line 11) | interface StatusOverlayProps {
function StatusOverlay (line 17) | function StatusOverlay({ show, message, sidebarCollapsed }: StatusOverla...
function StatusOverlays (line 40) | function StatusOverlays({
FILE: frontend/src/app/_components/TranscriptPanel.tsx
type TranscriptPanelProps (line 21) | interface TranscriptPanelProps {
function TranscriptPanel (line 28) | function TranscriptPanel({
FILE: frontend/src/app/layout.tsx
function ConditionalImportDialog (line 39) | function ConditionalImportDialog({
function RootLayout (line 66) | function RootLayout({
FILE: frontend/src/app/meeting-details/page-content.tsx
function PageContent (line 21) | function PageContent({
FILE: frontend/src/app/meeting-details/page.tsx
type MeetingDetailsResponse (line 13) | interface MeetingDetailsResponse {
function MeetingDetailsContent (line 22) | function MeetingDetailsContent() {
function MeetingDetails (line 383) | function MeetingDetails() {
FILE: frontend/src/app/notes/[id]/page.tsx
type PageProps (line 4) | interface PageProps {
type Note (line 10) | interface Note {
function generateStaticParams (line 19) | function generateStaticParams() {
FILE: frontend/src/app/page.tsx
function Home (line 25) | function Home() {
FILE: frontend/src/app/settings/page.tsx
constant TABS (line 17) | const TABS = [
function SettingsPage (line 25) | function SettingsPage() {
FILE: frontend/src/components/AISummary/Block.tsx
type BlockProps (line 6) | interface BlockProps {
type CommandOption (line 21) | interface CommandOption {
constant COMMANDS (line 29) | const COMMANDS: CommandOption[] = [
FILE: frontend/src/components/AISummary/BlockNoteSummaryView.tsx
type BlockNoteSummaryViewProps (line 15) | interface BlockNoteSummaryViewProps {
type BlockNoteSummaryViewRef (line 30) | interface BlockNoteSummaryViewRef {
function detectSummaryFormat (line 37) | function detectSummaryFormat(data: any): { format: SummaryFormat; data: ...
FILE: frontend/src/components/AISummary/Section.tsx
type SectionProps (line 9) | interface SectionProps {
FILE: frontend/src/components/AISummary/index.tsx
type Props (line 9) | interface Props {
FILE: frontend/src/components/About.tsx
function About (line 13) | function About() {
FILE: frontend/src/components/AnalyticsConsentSwitch.tsx
function AnalyticsConsentSwitch (line 12) | function AnalyticsConsentSwitch() {
FILE: frontend/src/components/AnalyticsDataModal.tsx
type AnalyticsDataModalProps (line 6) | interface AnalyticsDataModalProps {
function AnalyticsDataModal (line 12) | function AnalyticsDataModal({ isOpen, onClose, onConfirmDisable }: Analy...
FILE: frontend/src/components/AnalyticsProvider.tsx
type AnalyticsProviderProps (line 8) | interface AnalyticsProviderProps {
type AnalyticsContextType (line 12) | interface AnalyticsContextType {
function AnalyticsProvider (line 22) | function AnalyticsProvider({ children }: AnalyticsProviderProps) {
FILE: frontend/src/components/AudioBackendSelector.tsx
type BackendInfo (line 5) | interface BackendInfo {
type AudioBackendSelectorProps (line 11) | interface AudioBackendSelectorProps {
function AudioBackendSelector (line 17) | function AudioBackendSelector({
FILE: frontend/src/components/AudioLevelMeter.tsx
type AudioLevelMeterProps (line 3) | interface AudioLevelMeterProps {
function AudioLevelMeter (line 12) | function AudioLevelMeter({
type CompactAudioLevelMeterProps (line 108) | interface CompactAudioLevelMeterProps {
function CompactAudioLevelMeter (line 116) | function CompactAudioLevelMeter({
FILE: frontend/src/components/BetaSettings.tsx
function BetaSettings (line 12) | function BetaSettings() {
FILE: frontend/src/components/BlockNoteEditor/BasicBlockNoteTest.tsx
function BasicBlockNoteTest (line 9) | function BasicBlockNoteTest() {
FILE: frontend/src/components/BlockNoteEditor/Editor.tsx
type EditorProps (line 8) | interface EditorProps {
function Editor (line 14) | function Editor({ initialContent, onChange, editable = true }: EditorPro...
FILE: frontend/src/components/BluetoothPlaybackWarning.tsx
type AudioOutputInfo (line 8) | interface AudioOutputInfo {
type BluetoothPlaybackWarningProps (line 15) | interface BluetoothPlaybackWarningProps {
function BluetoothPlaybackWarning (line 22) | function BluetoothPlaybackWarning({
FILE: frontend/src/components/BuiltInModelManager.tsx
type ModelInfo (line 12) | interface ModelInfo {
type DownloadProgressInfo (line 25) | interface DownloadProgressInfo {
type BuiltInModelManagerProps (line 31) | interface BuiltInModelManagerProps {
function BuiltInModelManager (line 36) | function BuiltInModelManager({ selectedModel, onModelSelect }: BuiltInMo...
FILE: frontend/src/components/ChunkProgressDisplay.tsx
type ChunkStatus (line 3) | interface ChunkStatus {
type ProcessingProgress (line 13) | interface ProcessingProgress {
type ChunkProgressDisplayProps (line 22) | interface ChunkProgressDisplayProps {
function ChunkProgressDisplay (line 31) | function ChunkProgressDisplay({
function ChunkProgressMini (line 261) | function ChunkProgressMini({ progress, className = '' }: { progress: Pro...
FILE: frontend/src/components/ComplianceNotification.tsx
type ComplianceNotificationProps (line 7) | interface ComplianceNotificationProps {
FILE: frontend/src/components/ConfidenceIndicator.tsx
type ConfidenceIndicatorProps (line 3) | interface ConfidenceIndicatorProps {
FILE: frontend/src/components/ConfirmationModel/confirmation-modal.tsx
type ConfirmationModalProps (line 3) | interface ConfirmationModalProps {
function ConfirmationModal (line 10) | function ConfirmationModal({ onConfirm, onCancel, text, isOpen }: Confir...
FILE: frontend/src/components/ConsoleToggle.tsx
function ConsoleToggle (line 7) | function ConsoleToggle() {
FILE: frontend/src/components/CustomDialog.tsx
type DialogProps (line 7) | interface DialogProps {
function CustomDialog (line 13) | function CustomDialog({ triggerComponent, dialogContent, dialogTitle = "...
FILE: frontend/src/components/DatabaseImport/HomebrewDatabaseDetector.tsx
type HomebrewDatabaseDetectorProps (line 8) | interface HomebrewDatabaseDetectorProps {
constant HOMEBREW_PATHS (line 14) | const HOMEBREW_PATHS = [
function HomebrewDatabaseDetector (line 19) | function HomebrewDatabaseDetector({ onImportSuccess, onDecline }: Homebr...
FILE: frontend/src/components/DatabaseImport/LegacyDatabaseImport.tsx
type LegacyDatabaseImportProps (line 10) | interface LegacyDatabaseImportProps {
type ImportState (line 15) | type ImportState = 'idle' | 'selecting' | 'detecting' | 'importing' | 's...
function LegacyDatabaseImport (line 17) | function LegacyDatabaseImport({ isOpen, onComplete }: LegacyDatabaseImpo...
FILE: frontend/src/components/DeviceSelection.tsx
type AudioDevice (line 11) | interface AudioDevice {
type SelectedDevices (line 16) | interface SelectedDevices {
type AudioLevelData (line 21) | interface AudioLevelData {
type AudioLevelUpdate (line 29) | interface AudioLevelUpdate {
type DeviceSelectionProps (line 34) | interface DeviceSelectionProps {
function DeviceSelection (line 40) | function DeviceSelection({ selectedDevices, onDeviceChange, disabled = f...
FILE: frontend/src/components/EditableTitle.tsx
type EditableTitleProps (line 5) | interface EditableTitleProps {
FILE: frontend/src/components/EmptyStateSummary.tsx
type EmptyStateSummaryProps (line 13) | interface EmptyStateSummaryProps {
function EmptyStateSummary (line 19) | function EmptyStateSummary({ onGenerate, hasModel, isGenerating = false ...
FILE: frontend/src/components/ImportAudio/ImportAudioDialog.tsx
type ImportAudioDialogProps (line 42) | interface ImportAudioDialogProps {
function formatDuration (line 49) | function formatDuration(seconds: number): string {
function formatFileSize (line 60) | function formatFileSize(bytes: number): string {
function ImportAudioDialog (line 67) | function ImportAudioDialog({
FILE: frontend/src/components/ImportAudio/ImportDropOverlay.tsx
type ImportDropOverlayProps (line 5) | interface ImportDropOverlayProps {
function ImportDropOverlay (line 9) | function ImportDropOverlay({ visible }: ImportDropOverlayProps) {
FILE: frontend/src/components/Info.tsx
type InfoProps (line 7) | interface InfoProps {
FILE: frontend/src/components/LanguageSelection.tsx
type Language (line 7) | interface Language {
constant LANGUAGES (line 13) | const LANGUAGES: Language[] = [
type LanguageSelectionProps (line 117) | interface LanguageSelectionProps {
function LanguageSelection (line 124) | function LanguageSelection({
FILE: frontend/src/components/Logo.tsx
type LogoProps (line 7) | interface LogoProps {
FILE: frontend/src/components/MainContent/index.tsx
type MainContentProps (line 6) | interface MainContentProps {
FILE: frontend/src/components/MainNav/index.tsx
type MainNavProps (line 5) | interface MainNavProps {
FILE: frontend/src/components/MeetingDetails/RetranscribeDialog.tsx
type RetranscribeDialogProps (line 27) | interface RetranscribeDialogProps {
type RetranscriptionProgress (line 35) | interface RetranscriptionProgress {
type RetranscriptionResult (line 42) | interface RetranscriptionResult {
type RetranscriptionError (line 49) | interface RetranscriptionError {
function RetranscribeDialog (line 54) | function RetranscribeDialog({
FILE: frontend/src/components/MeetingDetails/SummaryGeneratorButtonGroup.tsx
type SummaryGeneratorButtonGroupProps (line 27) | interface SummaryGeneratorButtonGroupProps {
function SummaryGeneratorButtonGroup (line 43) | function SummaryGeneratorButtonGroup({
FILE: frontend/src/components/MeetingDetails/SummaryPanel.tsx
type SummaryPanelProps (line 13) | interface SummaryPanelProps {
function SummaryPanel (line 53) | function SummaryPanel({
FILE: frontend/src/components/MeetingDetails/SummaryUpdaterButtonGroup.tsx
type SummaryUpdaterButtonGroupProps (line 8) | interface SummaryUpdaterButtonGroupProps {
function SummaryUpdaterButtonGroup (line 18) | function SummaryUpdaterButtonGroup({
FILE: frontend/src/components/MeetingDetails/TranscriptButtonGroup.tsx
type TranscriptButtonGroupProps (line 12) | interface TranscriptButtonGroupProps {
function TranscriptButtonGroup (line 22) | function TranscriptButtonGroup({
FILE: frontend/src/components/MeetingDetails/TranscriptPanel.tsx
type TranscriptPanelProps (line 9) | interface TranscriptPanelProps {
function TranscriptPanel (line 33) | function TranscriptPanel({
FILE: frontend/src/components/MessageToast.tsx
type MessageToastProps (line 3) | interface MessageToastProps {
function MessageToast (line 10) | function MessageToast({ message, type, show, setShow }: MessageToastProp...
FILE: frontend/src/components/ModelDownloadProgress.tsx
type ModelDownloadProgressProps (line 5) | interface ModelDownloadProgressProps {
function ModelDownloadProgress (line 11) | function ModelDownloadProgress({ status, modelName, onCancel }: ModelDow...
type ProgressRingProps (line 54) | interface ProgressRingProps {
function ProgressRing (line 60) | function ProgressRing({ progress, size = 40, strokeWidth = 3 }: Progress...
type DownloadSummaryProps (line 101) | interface DownloadSummaryProps {
function DownloadSummary (line 107) | function DownloadSummary({ totalModels, downloadedModels, totalSizeMb }:...
FILE: frontend/src/components/ModelSettingsModal.tsx
type ModelConfig (line 33) | interface ModelConfig {
type OllamaModel (line 48) | interface OllamaModel {
type OpenRouterModel (line 55) | interface OpenRouterModel {
type OpenAIModel (line 63) | interface OpenAIModel {
type AnthropicModel (line 67) | interface AnthropicModel {
type GroqModel (line 72) | interface GroqModel {
constant OPENAI_FALLBACK_MODELS (line 78) | const OPENAI_FALLBACK_MODELS = [
constant CLAUDE_FALLBACK_MODELS (line 90) | const CLAUDE_FALLBACK_MODELS = [
constant GROQ_FALLBACK_MODELS (line 97) | const GROQ_FALLBACK_MODELS = [
type ModelSettingsModalProps (line 104) | interface ModelSettingsModalProps {
function ModelSettingsModal (line 111) | function ModelSettingsModal({
FILE: frontend/src/components/ParakeetModelManager.tsx
type ParakeetModelManagerProps (line 15) | interface ParakeetModelManagerProps {
function ParakeetModelManager (line 22) | function ParakeetModelManager({
type ModelCardProps (line 416) | interface ModelCardProps {
function ModelCard (line 427) | function ModelCard({
FILE: frontend/src/components/PermissionWarning.tsx
type PermissionWarningProps (line 7) | interface PermissionWarningProps {
function PermissionWarning (line 14) | function PermissionWarning({
FILE: frontend/src/components/PreferenceSettings.tsx
function PreferenceSettings (line 11) | function PreferenceSettings() {
FILE: frontend/src/components/RecordingControls.tsx
type RecordingControlsProps (line 14) | interface RecordingControlsProps {
FILE: frontend/src/components/RecordingSettings.tsx
type RecordingPreferences (line 9) | interface RecordingPreferences {
type RecordingSettingsProps (line 17) | interface RecordingSettingsProps {
function RecordingSettings (line 21) | function RecordingSettings({ onSave }: RecordingSettingsProps) {
FILE: frontend/src/components/RecordingStatusBar.tsx
type RecordingStatusBarProps (line 7) | interface RecordingStatusBarProps {
FILE: frontend/src/components/SettingTabs.tsx
type SettingTabsProps (line 7) | interface SettingTabsProps {
function SettingTabs (line 18) | function SettingTabs({
FILE: frontend/src/components/Sidebar/SidebarProvider.tsx
type SidebarItem (line 10) | interface SidebarItem {
type CurrentMeeting (line 17) | interface CurrentMeeting {
type TranscriptSearchResult (line 23) | interface TranscriptSearchResult {
type SidebarContextType (line 30) | interface SidebarContextType {
function SidebarProvider (line 67) | function SidebarProvider({ children }: { children: React.ReactNode }) {
FILE: frontend/src/components/Sidebar/index.tsx
type SidebarItem (line 35) | interface SidebarItem {
FILE: frontend/src/components/SummaryModelSettings.tsx
type SummaryModelSettingsProps (line 10) | interface SummaryModelSettingsProps {
function SummaryModelSettings (line 14) | function SummaryModelSettings({ refetchTrigger }: SummaryModelSettingsPr...
FILE: frontend/src/components/TranscriptRecovery/TranscriptRecovery.tsx
type TranscriptRecoveryProps (line 25) | interface TranscriptRecoveryProps {
function TranscriptRecovery (line 34) | function TranscriptRecovery({
FILE: frontend/src/components/TranscriptSettings.tsx
type TranscriptModelProps (line 12) | interface TranscriptModelProps {
type TranscriptSettingsProps (line 18) | interface TranscriptSettingsProps {
function TranscriptSettings (line 24) | function TranscriptSettings({ transcriptModelConfig, setTranscriptModelC...
FILE: frontend/src/components/TranscriptView.tsx
type TranscriptViewProps (line 10) | interface TranscriptViewProps {
type SpeechDetectedEvent (line 19) | interface SpeechDetectedEvent {
function formatRecordingTime (line 24) | function formatRecordingTime(seconds: number | undefined): string {
function cleanRepetitions (line 35) | function cleanRepetitions(text: string): string {
function cleanStopWords (line 82) | function cleanStopWords(text: string): string {
FILE: frontend/src/components/UpdateCheckProvider.tsx
type UpdateCheckContextType (line 9) | interface UpdateCheckContextType {
function UpdateCheckProvider (line 18) | function UpdateCheckProvider({ children }: { children: React.ReactNode }) {
function useUpdateCheckContext (line 72) | function useUpdateCheckContext() {
FILE: frontend/src/components/UpdateDialog.tsx
type UpdateDialogProps (line 17) | interface UpdateDialogProps {
function UpdateDialog (line 23) | function UpdateDialog({ open, onOpenChange, updateInfo }: UpdateDialogPr...
function formatBytes (line 300) | function formatBytes(bytes: number): string {
FILE: frontend/src/components/UpdateNotification.tsx
function setUpdateDialogCallback (line 8) | function setUpdateDialogCallback(callback: () => void) {
function showUpdateNotification (line 12) | function showUpdateNotification(updateInfo: UpdateInfo, onUpdateClick?: ...
FILE: frontend/src/components/VirtualizedTranscriptView.tsx
type VirtualizedTranscriptViewProps (line 13) | interface VirtualizedTranscriptViewProps {
constant VIRTUALIZATION_THRESHOLD (line 40) | const VIRTUALIZATION_THRESHOLD = 10;
function formatRecordingTime (line 43) | function formatRecordingTime(seconds: number | undefined): string {
function cleanStopWords (line 54) | function cleanStopWords(text: string): string {
FILE: frontend/src/components/WhisperModelManager.tsx
type ModelManagerProps (line 18) | interface ModelManagerProps {
function ModelManager (line 25) | function ModelManager({
type ModelCardProps (line 493) | interface ModelCardProps {
function ModelCard (line 505) | function ModelCard({
FILE: frontend/src/components/molecules/form-components/form-input-item.tsx
type IInpuItemProps (line 13) | type IInpuItemProps = {
FILE: frontend/src/components/molecules/form-components/form-input-switch.tsx
type IInpuItemProps (line 11) | type IInpuItemProps = {
FILE: frontend/src/components/molecules/form-components/form-select-item.tsx
type ISelectItemProps (line 18) | type ISelectItemProps = {
FILE: frontend/src/components/onboarding/OnboardingContainer.tsx
function OnboardingContainer (line 8) | function OnboardingContainer({
FILE: frontend/src/components/onboarding/OnboardingFlow.tsx
type OnboardingFlowProps (line 10) | interface OnboardingFlowProps {
function OnboardingFlow (line 14) | function OnboardingFlow({ onComplete }: OnboardingFlowProps) {
FILE: frontend/src/components/onboarding/shared/PermissionRow.tsx
function PermissionRow (line 7) | function PermissionRow({ icon, title, description, status, isPending = f...
FILE: frontend/src/components/onboarding/shared/ProgressIndicator.tsx
type ProgressIndicatorProps (line 4) | interface ProgressIndicatorProps {
function ProgressIndicator (line 17) | function ProgressIndicator({ current, total, onStepClick }: ProgressIndi...
FILE: frontend/src/components/onboarding/shared/StatusIndicator.tsx
function StatusIndicator (line 5) | function StatusIndicator({ status, size = 'md' }: StatusIndicatorProps) {
FILE: frontend/src/components/onboarding/steps/DownloadProgressStep.tsx
constant PARAKEET_MODEL (line 11) | const PARAKEET_MODEL = 'parakeet-tdt-0.6b-v3-int8';
type DownloadStatus (line 13) | type DownloadStatus = 'waiting' | 'downloading' | 'completed' | 'error';
type DownloadState (line 15) | interface DownloadState {
function DownloadProgressStep (line 24) | function DownloadProgressStep() {
FILE: frontend/src/components/onboarding/steps/PermissionsStep.tsx
function PermissionsStep (line 9) | function PermissionsStep() {
FILE: frontend/src/components/onboarding/steps/SetupOverviewStep.tsx
function SetupOverviewStep (line 14) | function SetupOverviewStep() {
FILE: frontend/src/components/onboarding/steps/WelcomeStep.tsx
function WelcomeStep (line 7) | function WelcomeStep() {
FILE: frontend/src/components/shared/DownloadProgressToast.tsx
type DownloadProgress (line 8) | interface DownloadProgress {
function categorizeError (line 20) | function categorizeError(error: string): string {
function DownloadToastContent (line 49) | function DownloadToastContent({
function useDownloadProgressToast (line 123) | function useDownloadProgressToast() {
function DownloadProgressToastProvider (line 351) | function DownloadProgressToastProvider() {
FILE: frontend/src/components/ui/accordion.tsx
function Accordion (line 9) | function Accordion({
function AccordionItem (line 15) | function AccordionItem({
function AccordionTrigger (line 28) | function AccordionTrigger({
function AccordionContent (line 54) | function AccordionContent({
FILE: frontend/src/components/ui/button-group.tsx
function ButtonGroup (line 24) | function ButtonGroup({
function ButtonGroupText (line 40) | function ButtonGroupText({
function ButtonGroupSeparator (line 60) | function ButtonGroupSeparator({
FILE: frontend/src/components/ui/button.tsx
type ButtonProps (line 41) | interface ButtonProps
FILE: frontend/src/components/ui/form.tsx
type FormFieldContextValue (line 20) | type FormFieldContextValue<
type FormItemContextValue (line 67) | type FormItemContextValue = {
FILE: frontend/src/components/ui/input-group.tsx
function InputGroup (line 11) | function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
function InputGroupAddon (line 60) | function InputGroupAddon({
function InputGroupButton (line 100) | function InputGroupButton({
function InputGroupText (line 119) | function InputGroupText({ className, ...props }: React.ComponentProps<"s...
function InputGroupInput (line 131) | function InputGroupInput({
function InputGroupTextarea (line 147) | function InputGroupTextarea({
FILE: frontend/src/components/ui/sheet.tsx
type SheetContentProps (line 52) | interface SheetContentProps
FILE: frontend/src/constants/audioFormats.ts
constant AUDIO_EXTENSIONS (line 9) | const AUDIO_EXTENSIONS = [
type AudioExtension (line 13) | type AudioExtension = typeof AUDIO_EXTENSIONS[number];
constant AUDIO_FORMAT_DISPLAY_NAMES (line 22) | const AUDIO_FORMAT_DISPLAY_NAMES: Record<AudioExtension, string> = {
function getAudioFormatsDisplayList (line 39) | function getAudioFormatsDisplayList(): string {
FILE: frontend/src/constants/languages.ts
constant LANGUAGES (line 2) | const LANGUAGES = [
FILE: frontend/src/constants/modelDefaults.ts
constant DEFAULT_WHISPER_MODEL (line 10) | const DEFAULT_WHISPER_MODEL = 'large-v3-turbo';
constant DEFAULT_PARAKEET_MODEL (line 16) | const DEFAULT_PARAKEET_MODEL = 'parakeet-tdt-0.6b-v3-int8';
constant MODEL_DEFAULTS (line 21) | const MODEL_DEFAULTS = {
FILE: frontend/src/contexts/ConfigContext.tsx
type OllamaModel (line 11) | interface OllamaModel {
type StorageLocations (line 18) | interface StorageLocations {
type NotificationSettings (line 24) | interface NotificationSettings {
type ConfigContextType (line 45) | interface ConfigContextType {
function ConfigProvider (line 99) | function ConfigProvider({ children }: { children: ReactNode }) {
function useConfig (line 541) | function useConfig() {
FILE: frontend/src/contexts/ImportDialogContext.tsx
type ImportDialogContextType (line 7) | interface ImportDialogContextType {
type ImportDialogProviderProps (line 19) | interface ImportDialogProviderProps {
function ImportDialogProvider (line 24) | function ImportDialogProvider({ children, onOpen }: ImportDialogProvider...
FILE: frontend/src/contexts/OllamaDownloadContext.tsx
type OllamaDownloadState (line 16) | interface OllamaDownloadState {
type OllamaDownloadContextType (line 21) | interface OllamaDownloadContextType extends OllamaDownloadState {
function OllamaDownloadProvider (line 36) | function OllamaDownloadProvider({ children }: { children: React.ReactNod...
FILE: frontend/src/contexts/OnboardingContext.tsx
constant PARAKEET_MODEL (line 8) | const PARAKEET_MODEL = 'parakeet-tdt-0.6b-v3-int8';
type OnboardingStatus (line 10) | interface OnboardingStatus {
type SummaryModelProgressInfo (line 21) | interface SummaryModelProgressInfo {
type ParakeetProgressInfo (line 28) | interface ParakeetProgressInfo {
type OnboardingContextType (line 35) | interface OnboardingContextType {
function OnboardingProvider (line 67) | function OnboardingProvider({ children }: { children: React.ReactNode }) {
function useOnboarding (line 541) | function useOnboarding() {
FILE: frontend/src/contexts/RecordingPostProcessingProvider.tsx
function RecordingPostProcessingProvider (line 20) | function RecordingPostProcessingProvider({ children }: { children: React...
FILE: frontend/src/contexts/RecordingStateContext.tsx
type RecordingStatus (line 16) | enum RecordingStatus {
type RecordingState (line 27) | interface RecordingState {
type RecordingStateContextType (line 39) | interface RecordingStateContextType extends RecordingState {
function RecordingStateProvider (line 59) | function RecordingStateProvider({ children }: { children: React.ReactNod...
FILE: frontend/src/contexts/TranscriptContext.tsx
type TranscriptContextType (line 11) | interface TranscriptContextType {
function TranscriptProvider (line 27) | function TranscriptProvider({ children }: { children: ReactNode }) {
function useTranscripts (line 533) | function useTranscripts() {
FILE: frontend/src/hooks/meeting-details/useCopyOperations.ts
type UseCopyOperationsProps (line 8) | interface UseCopyOperationsProps {
function useCopyOperations (line 16) | function useCopyOperations({
FILE: frontend/src/hooks/meeting-details/useMeetingData.ts
type UseMeetingDataProps (line 8) | interface UseMeetingDataProps {
function useMeetingData (line 14) | function useMeetingData({ meeting, summaryData, onMeetingUpdated }: UseM...
FILE: frontend/src/hooks/meeting-details/useMeetingOperations.ts
type UseMeetingOperationsProps (line 5) | interfac
Condensed preview — 441 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (4,044K chars).
[
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 1109,
"preview": "---\nname: Bug Report\nabout: Create a report to help us improve\ntitle: '[BUG] '\nlabels: bug\nassignees: ''\n---\n\n## Bug Des"
},
{
"path": ".github/ISSUE_TEMPLATE/documentation.md",
"chars": 794,
"preview": "---\nname: Documentation\nabout: Report documentation issues or suggest improvements\ntitle: '[DOCS] '\nlabels: documentatio"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 1043,
"preview": "---\nname: Feature Request\nabout: Suggest an idea for this project\ntitle: '[FEATURE] '\nlabels: enhancement\nassignees: ''\n"
},
{
"path": ".github/issue_template.md",
"chars": 626,
"preview": "## Description\n[Provide a clear and concise description of the issue]\n\n## Expected Behavior\n[Describe what you expected "
},
{
"path": ".github/pull_request_template.md",
"chars": 858,
"preview": "## Description\n[Provide a detailed description of your changes]\n\n## Related Issue\n[Link to the issue this PR addresses ("
},
{
"path": ".github/workflows/ACCELERATION_GUIDE.md",
"chars": 10526,
"preview": "# CI/CD Hardware Acceleration Guide\n\nThis document explains the hardware acceleration configuration for all CI/CD workfl"
},
{
"path": ".github/workflows/README_DEVTEST.md",
"chars": 5469,
"preview": "# DevTest Build Workflow\n\nThis document explains how to use the `build-devtest.yml` workflow for building and testing.\n\n"
},
{
"path": ".github/workflows/WORKFLOWS_OVERVIEW.md",
"chars": 8985,
"preview": "# GitHub Actions Workflows Overview\n\nThis document provides a quick overview of all available CI/CD workflows in this re"
},
{
"path": ".github/workflows/build-devtest.yml",
"chars": 22645,
"preview": "name: \"Build and Test - DevTest\"\n\non:\n workflow_dispatch:\n inputs:\n sign-build:\n description: 'Sign the "
},
{
"path": ".github/workflows/build-linux.yml",
"chars": 14385,
"preview": "name: \"Build and Test - Linux\"\n\non:\n workflow_dispatch:\n inputs:\n build-type:\n description: 'Build type'"
},
{
"path": ".github/workflows/build-macos.yml",
"chars": 12519,
"preview": "name: \"Build and Test - macOS\"\n\non:\n workflow_dispatch:\n inputs:\n build-type:\n description: 'Build type'"
},
{
"path": ".github/workflows/build-test.yml",
"chars": 1081,
"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-windows.yml",
"chars": 32531,
"preview": "name: \"Build and Test - Windows\"\n\non:\n workflow_dispatch:\n inputs:\n build-type:\n description: 'Build typ"
},
{
"path": ".github/workflows/build.yml",
"chars": 29187,
"preview": "name: \"Build\"\n\non:\n workflow_call:\n inputs:\n platform:\n required: true\n type: string\n target"
},
{
"path": ".github/workflows/pr-main-check.yml",
"chars": 2468,
"preview": "name: \"Validation Check\"\n\n# This workflow runs basic validation checks without building\n# Use this to verify version and"
},
{
"path": ".github/workflows/release.yml",
"chars": 8353,
"preview": "name: \"Release\"\n\non:\n workflow_dispatch:\n\n# Prevent duplicate releases\nconcurrency:\n group: release-${{ github.ref }}\n"
},
{
"path": ".gitignore",
"chars": 885,
"preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n/experiments\n\n# dependencies\n/nod"
},
{
"path": ".gitmodules",
"chars": 136,
"preview": "[submodule \"backend/whisper.cpp\"]\n\tpath = backend/whisper.cpp\n\turl = https://github.com/Zackriya-Solutions/whisper.cpp\n\t"
},
{
"path": "BLUETOOTH_PLAYBACK_NOTICE.md",
"chars": 7747,
"preview": "# Bluetooth Headphone Playback Notice\n\n## Important Information for Recording Review\n\nWhen **reviewing recordings** in M"
},
{
"path": "CLAUDE.md",
"chars": 16984,
"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": 3486,
"preview": "# Contributing to Meeting Minutes Updates\n\nThank you for your interest in contributing to Meetily! This document provide"
},
{
"path": "Cargo.toml",
"chars": 402,
"preview": "[workspace]\nresolver = \"2\"\nmembers = [\n \"frontend/src-tauri\",\n \"llama-helper\"\n]\n\n# Shared workspace settings\n[work"
},
{
"path": "LICENSE.md",
"chars": 1074,
"preview": "MIT License\n\nCopyright (c) 2024 Zackriya Solutions\n\nPermission is hereby granted, free of charge, to any person obtainin"
},
{
"path": "PRIVACY_POLICY.md",
"chars": 4803,
"preview": "# Meetily Privacy Policy\n\n*Last updated: [Current Date]*\n\n## Our Privacy-First Commitment\n\nMeetily is built on the princ"
},
{
"path": "README.md",
"chars": 12343,
"preview": "<div align=\"center\" style=\"border-bottom: none\">\n <h1>\n <img src=\"docs/Meetily-6.png\" style=\"border-radius: 10"
},
{
"path": "backend/.gitignore",
"chars": 6150,
"preview": "# Created by https://www.toptal.com/developers/gitignore/api/python,flask,venv\n# Edit at https://www.toptal.com/develope"
},
{
"path": "backend/API_DOCUMENTATION.md",
"chars": 7573,
"preview": "# Meetily API Documentation\n\n## Prerequisites\n\n### System Requirements\n- Python 3.8 or higher\n- pip (Python package inst"
},
{
"path": "backend/Dockerfile.app",
"chars": 1454,
"preview": "# Use Python 3.11 slim image as base\nFROM python:3.11-slim\n\n# Set working directory\nWORKDIR /app\n\n# Install system depen"
},
{
"path": "backend/Dockerfile.server-cpu",
"chars": 2327,
"preview": "# Multi-stage build for Whisper Server (CPU-only)\n# This version provides maximum compatibility across all systems\n\nFROM"
},
{
"path": "backend/Dockerfile.server-gpu",
"chars": 2608,
"preview": "# Multi-stage build for Whisper Server (GPU-enabled)\n# This version includes CUDA support for NVIDIA GPUs with CPU fallb"
},
{
"path": "backend/Dockerfile.server-macos",
"chars": 2392,
"preview": "# Multi-stage build for Whisper Server (macOS Apple Silicon optimized)\n# This version provides CPU-only compatibility fo"
},
{
"path": "backend/README.md",
"chars": 16847,
"preview": "# Meetily Backend\n\nFastAPI backend for meeting transcription and analysis with **Docker distribution system** for easy d"
},
{
"path": "backend/SCRIPTS_DOCUMENTATION.md",
"chars": 21013,
"preview": "# Backend Scripts Documentation\n\nThis comprehensive document details all the `.cmd`, `.ps1`, and `.sh` scripts in the ba"
},
{
"path": "backend/app/db.py",
"chars": 40935,
"preview": "import aiosqlite\nimport json\nimport os\nfrom datetime import datetime\nfrom typing import Optional, Dict\nimport logging\nfr"
},
{
"path": "backend/app/main.py",
"chars": 27567,
"preview": "from fastapi import FastAPI, HTTPException, BackgroundTasks\nfrom fastapi.middleware.cors import CORSMiddleware\nfrom fast"
},
{
"path": "backend/app/schema_validator.py",
"chars": 5386,
"preview": "import sqlite3\nimport logging\nfrom typing import Dict, List, Tuple\n\nlogger = logging.getLogger(__name__)\n\nclass SchemaVa"
},
{
"path": "backend/app/transcript_processor.py",
"chars": 14110,
"preview": "from pydantic import BaseModel\nfrom typing import List, Tuple, Literal\nfrom pydantic_ai import Agent\nfrom pydantic_ai.mo"
},
{
"path": "backend/build-docker.ps1",
"chars": 24012,
"preview": "# Multi-platform Docker build script for Whisper Server and Meeting App\n# Supports both CPU-only and GPU-enabled builds "
},
{
"path": "backend/build-docker.sh",
"chars": 14483,
"preview": "#!/bin/bash\n\n# Multi-platform Docker build script for Whisper Server and Meeting App\n# Supports both CPU-only and GPU-en"
},
{
"path": "backend/build_whisper.cmd",
"chars": 7793,
"preview": "@echo off\nsetlocal enabledelayedexpansion\n\necho === Starting Whisper.cpp Build Process ===\necho.\n\necho Updating git subm"
},
{
"path": "backend/build_whisper.sh",
"chars": 8868,
"preview": "#!/bin/bash\n\n# Color codes\nGREEN='\\033[0;32m'\nBLUE='\\033[0;34m'\nYELLOW='\\033[1;33m'\nRED='\\033[0;31m'\nNC='\\033[0m' # No C"
},
{
"path": "backend/clean_start_backend.cmd",
"chars": 8071,
"preview": "@echo off\nsetlocal enabledelayedexpansion\n\nREM Configuration\nset \"PACKAGE_NAME=whisper-server-package\"\nset \"MODEL_DIR=%P"
},
{
"path": "backend/clean_start_backend.sh",
"chars": 8354,
"preview": "#!/bin/bash\n\n# Exit on error\nset -e\n\n# Color codes and emojis\nGREEN='\\033[0;32m'\nBLUE='\\033[0;34m'\nYELLOW='\\033[1;33m'\nR"
},
{
"path": "backend/debug_cors.py",
"chars": 1338,
"preview": "\"\"\"\nDebug script to test the /process-transcript endpoint\n\"\"\"\nimport requests\nimport json\nimport sys\n\ndef test_process_t"
},
{
"path": "backend/docker/entrypoint.sh",
"chars": 17380,
"preview": "#!/bin/bash\n\n# Whisper Server Docker Entrypoint Script\n# Handles GPU detection, model management, and server startup\n\nse"
},
{
"path": "backend/docker-compose.yml",
"chars": 9174,
"preview": "version: '3.8'\n\n# ⚠️ AUDIO PROCESSING WARNING:\n# Docker containers with insufficient resources will drop audio chunks w"
},
{
"path": "backend/download-ggml-model.cmd",
"chars": 5000,
"preview": "@echo off\n\npushd %~dp0\nset models_path=%CD%\nfor %%d in (%~dp0..) do set root_path=%%~fd\npopd\n\nset models=tiny.en tiny ba"
},
{
"path": "backend/download-ggml-model.sh",
"chars": 3167,
"preview": "#!/bin/sh\n\n# This script downloads Whisper model files that have already been converted to ggml format.\n# This way you d"
},
{
"path": "backend/examples/run_summary_workflow.py",
"chars": 10406,
"preview": "import requests\nimport time\nimport argparse\nimport json\nimport sys\nimport uuid # Import uuid to generate unique IDs\nimpo"
},
{
"path": "backend/install_dependancies_for_windows.ps1",
"chars": 7305,
"preview": "Write-Host \"Installing dependencies...\"\n\ntry {\n\n # Install Chocolatey if not already installed\n if (!(Test-Path \"$"
},
{
"path": "backend/requirements.txt",
"chars": 178,
"preview": "pydantic-ai==0.2.15\npydantic==2.11.5\npandas==2.2.3\ndevtools==0.12.2\npython-dotenv==1.1.0\nfastapi==0.115.9\nuvicorn==0.34."
},
{
"path": "backend/run-docker.ps1",
"chars": 62270,
"preview": "# Easy deployment script for Whisper Server and Meeting App Docker containers\n# Handles model downloads, GPU detection, "
},
{
"path": "backend/run-docker.sh",
"chars": 68147,
"preview": "#!/bin/bash\n\n# Easy deployment script for Whisper Server and Meeting App Docker containers\n# Handles model downloads, GP"
},
{
"path": "backend/set_env.sh",
"chars": 1190,
"preview": "#!/bin/bash\n\n# This script sets up environment variables for API keys\n\n# Copy template environment file\necho \"Setting up"
},
{
"path": "backend/setup-db.ps1",
"chars": 10418,
"preview": "# Database Setup Script for Meeting App\n# Handles existing database discovery and migration\n\nparam(\n [string]$DbPath,"
},
{
"path": "backend/setup-db.sh",
"chars": 10799,
"preview": "#!/bin/bash\n\n# Database Setup Script for Meeting App\n# Handles existing database discovery and migration\n\nset -e\n\n# Conf"
},
{
"path": "backend/start_python_backend.cmd",
"chars": 1281,
"preview": "@echo off\nsetlocal enabledelayedexpansion\n\nset \"PORT=5167\"\nif \"%~1\" neq \"\" (\n set \"PORT=%~1\"\n)\n\necho Starting Python "
},
{
"path": "backend/start_whisper_server.cmd",
"chars": 1340,
"preview": "@echo off\nsetlocal enabledelayedexpansion\n\nset \"PACKAGE_NAME=whisper-server-package\"\nset \"MODEL_NAME=ggml-small.bin\"\n\nif"
},
{
"path": "backend/start_with_output.ps1",
"chars": 38212,
"preview": "# PowerShell script to start both Whisper server and Python backend with visible output\n# This script uses PowerShell's "
},
{
"path": "backend/temp.env",
"chars": 85,
"preview": "ANTHROPIC_API_KEY=api_key_here\nGROQ_API_KEY=gapi_key_here\nOPENAI_API_KEY=api_key_here"
},
{
"path": "backend/whisper-custom/server/CMakeLists.txt",
"chars": 305,
"preview": "set(TARGET whisper-server)\nadd_executable(${TARGET} server.cpp httplib.h)\n\ninclude(DefaultTargetOptions)\n\ntarget_link_li"
},
{
"path": "backend/whisper-custom/server/README.md",
"chars": 3450,
"preview": "# whisper.cpp/examples/server\n\nSimple http server. WAV Files are passed to the inference model via http requests.\n\nhttps"
},
{
"path": "backend/whisper-custom/server/httplib.h",
"chars": 302116,
"preview": "//\n// httplib.h\n//\n// Copyright (c) 2023 Yuji Hirose. All rights reserved.\n// MIT License\n//\n\n#ifndef CPPHTTPLIB_HTTP"
},
{
"path": "backend/whisper-custom/server/public/index.html",
"chars": 8717,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n <title>Whisper.cpp Live Transcription</title>\n <style>\n body {\n f"
},
{
"path": "backend/whisper-custom/server/server.cpp",
"chars": 50512,
"preview": "#include \"common.h\"\n\n#include \"whisper.h\"\n#include \"httplib.h\"\n#include \"json.hpp\"\n\n#include <cmath>\n#include <fstream>\n"
},
{
"path": "docs/BUILDING.md",
"chars": 8975,
"preview": "# Building Meetily from Source\n\nThis guide provides detailed instructions for building Meetily from source on different "
},
{
"path": "docs/GPU_ACCELERATION.md",
"chars": 2148,
"preview": "# GPU Acceleration Guide\n\nMeetily supports GPU acceleration for transcription, which can significantly improve performan"
},
{
"path": "docs/architecture.md",
"chars": 1546,
"preview": "# System Architecture\n\nMeetily is a self-contained desktop application built with [Tauri](https://tauri.app/). It combin"
},
{
"path": "docs/building_in_linux.md",
"chars": 10604,
"preview": "## 🐧 Building on Linux\n\nThis guide helps you build Meetily on Linux with **automatic GPU acceleration**. The build syste"
},
{
"path": "frontend/.gitignore",
"chars": 628,
"preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/gen"
},
{
"path": "frontend/API.md",
"chars": 8551,
"preview": "# Whisper.cpp Live Transcription API Documentation\n\n## Overview\n\nThe Whisper.cpp Live Transcription API provides real-ti"
},
{
"path": "frontend/README.md",
"chars": 4822,
"preview": "# Meetily - Frontend\n\nA modern desktop application for recording, transcribing, and analyzing meetings with AI assistanc"
},
{
"path": "frontend/build-gpu.bat",
"chars": 8653,
"preview": "@echo off\nREM Meetily GPU-Accelerated Build Script for Windows\nREM Automatically detects and builds with optimal GPU fea"
},
{
"path": "frontend/build-gpu.ps1",
"chars": 2212,
"preview": "# GPU-accelerated build script for Meetily (Windows PowerShell)\n# Automatically detects and builds with optimal GPU feat"
},
{
"path": "frontend/build-gpu.sh",
"chars": 5167,
"preview": "#!/bin/bash\n# GPU-accelerated build script for Meetily\n# Automatically detects and builds with optimal GPU features\n\nset"
},
{
"path": "frontend/build.bat",
"chars": 8308,
"preview": "@echo off\nREM Meetily Build Script for Windows\nREM This script sets up environment variables and builds the Tauri applic"
},
{
"path": "frontend/build.ps1",
"chars": 2973,
"preview": "# Meetily Build Script with Code Signing\n# Loads signing credentials from .env file or environment variables\n# Then call"
},
{
"path": "frontend/build_backup.bat",
"chars": 8270,
"preview": "@echo off\nREM Meetily Build Script for Windows\nREM This script sets up environment variables and builds the Tauri applic"
},
{
"path": "frontend/clean_build.sh",
"chars": 1228,
"preview": "#!/bin/bash\n\n# Exit on error\nset -e\n\n# Add log level selector with default to INFO\nLOG_LEVEL=${1:-info}\n\ncase $LOG_LEVEL"
},
{
"path": "frontend/clean_build_windows.bat",
"chars": 196,
"preview": "@echo off\n\necho Cleaning npm dependencies...\nrd /s /q node_modules\ndel /f /q package-lock.json\n\necho Installing npm depe"
},
{
"path": "frontend/clean_run.sh",
"chars": 848,
"preview": "#!/bin/bash\n\n# Exit on error\nset -e\n\n# Add log level selector with default to INFO\nLOG_LEVEL=${1:-info}\n\ncase $LOG_LEVEL"
},
{
"path": "frontend/clean_run_windows.bat",
"chars": 194,
"preview": "@echo off\n\necho Cleaning npm dependencies...\nrd /s /q node_modules\ndel /f /q package-lock.json\n\necho Installing npm depe"
},
{
"path": "frontend/components.json",
"chars": 448,
"preview": "{\n \"$schema\": \"https://ui.shadcn.com/schema.json\",\n \"style\": \"new-york\",\n \"rsc\": true,\n \"tsx\": true,\n \"tailwind\": {"
},
{
"path": "frontend/dev-gpu.bat",
"chars": 8314,
"preview": "@echo off\nREM Meetily GPU-Accelerated Development Script for Windows\nREM Automatically detects and runs in development m"
},
{
"path": "frontend/dev-gpu.ps1",
"chars": 1925,
"preview": "# GPU-accelerated development script for Meetily (Windows PowerShell)\n# Automatically detects and runs in development mo"
},
{
"path": "frontend/dev-gpu.sh",
"chars": 5091,
"preview": "#!/bin/bash\n# GPU-accelerated development script for Meetily\n# Automatically detects and runs in development mode with o"
},
{
"path": "frontend/eslint.config.mjs",
"chars": 393,
"preview": "import { dirname } from \"path\";\nimport { fileURLToPath } from \"url\";\nimport { FlatCompat } from \"@eslint/eslintrc\";\n\ncon"
},
{
"path": "frontend/next.config.js",
"chars": 557,
"preview": "/** @type {import('next').NextConfig} */\nconst nextConfig = {\n reactStrictMode: false, // Disabled for BlockNote compat"
},
{
"path": "frontend/package-app.sh",
"chars": 360,
"preview": "#!/bin/bash\n\n# Exit on error\nset -e\n\necho \"Cleaning up previous builds...\"\nrm -rf .next\nrm -rf out\nrm -rf src-tauri/targ"
},
{
"path": "frontend/package.json",
"chars": 4042,
"preview": "{\n \"name\": \"meetily\",\n \"version\": \"0.3.0\",\n \"private\": true,\n \"main\": \"electron/main.js\",\n \"scripts\": {\n "
},
{
"path": "frontend/postcss.config.js",
"chars": 91,
"preview": "module.exports = {\n plugins: {\n tailwindcss: {},\n autoprefixer: {},\n },\n }"
},
{
"path": "frontend/postcss.config.mjs",
"chars": 135,
"preview": "/** @type {import('postcss-load-config').Config} */\nconst config = {\n plugins: {\n tailwindcss: {},\n },\n};\n\nexport d"
},
{
"path": "frontend/scripts/auto-detect-gpu.js",
"chars": 3244,
"preview": "#!/usr/bin/env node\n/**\n * Auto-detect GPU capabilities and set appropriate features\n * Used by npm scripts to automatic"
},
{
"path": "frontend/scripts/load-env.ps1",
"chars": 2323,
"preview": "# Load Environment Variables from .env file\n# This script parses a .env file and loads variables into the current PowerS"
},
{
"path": "frontend/scripts/tauri-auto.js",
"chars": 1760,
"preview": "#!/usr/bin/env node\n/**\n * Auto-detect GPU and run Tauri with appropriate features\n */\n\nconst { execSync } = require('ch"
},
{
"path": "frontend/src/app/_components/SettingsModal.tsx",
"chars": 14680,
"preview": "import { ModelConfig } from \"@/components/ModelSettingsModal\";\nimport { PreferenceSettings } from \"@/components/Preferen"
},
{
"path": "frontend/src/app/_components/StatusOverlays.tsx",
"chars": 1857,
"preview": "interface StatusOverlaysProps {\n // Status flags\n isProcessing: boolean; // Processing transcription after record"
},
{
"path": "frontend/src/app/_components/TranscriptPanel.tsx",
"chars": 4266,
"preview": "import { VirtualizedTranscriptView } from '@/components/VirtualizedTranscriptView';\nimport { PermissionWarning } from '@"
},
{
"path": "frontend/src/app/globals.css",
"chars": 4002,
"preview": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n@keyframes vibrate {\n 0% {\n transform: translate(0);\n }"
},
{
"path": "frontend/src/app/layout.tsx",
"chars": 10317,
"preview": "'use client'\n\nimport './globals.css'\nimport { Source_Sans_3 } from 'next/font/google'\nimport Sidebar from '@/components/"
},
{
"path": "frontend/src/app/meeting-details/page-content.tsx",
"chars": 8518,
"preview": "\"use client\";\nimport { useState, useEffect, useRef } from 'react';\nimport { motion } from 'framer-motion';\nimport { Summ"
},
{
"path": "frontend/src/app/meeting-details/page.tsx",
"chars": 13348,
"preview": "\"use client\"\nimport { useSidebar } from \"@/components/Sidebar/SidebarProvider\";\nimport { useState, useEffect, useCallbac"
},
{
"path": "frontend/src/app/metadata.ts",
"chars": 138,
"preview": "import { Metadata } from 'next'\n\nexport const metadata: Metadata = {\n title: 'Meetily',\n description: 'AI-powered meet"
},
{
"path": "frontend/src/app/metadata.tsx",
"chars": 140,
"preview": "import { Metadata } from 'next';\n\nexport const metadata: Metadata = {\n title: 'Meetily',\n description: 'AI-powered mee"
},
{
"path": "frontend/src/app/notes/[id]/page.tsx",
"chars": 4780,
"preview": "import React from 'react';\nimport { Clock, Users, Calendar, Tag } from 'lucide-react';\n\ninterface PageProps {\n params: "
},
{
"path": "frontend/src/app/page.tsx",
"chars": 10199,
"preview": "'use client';\n\nimport { useState, useEffect } from 'react';\nimport { motion } from 'framer-motion';\nimport { RecordingCo"
},
{
"path": "frontend/src/app/settings/page.tsx",
"chars": 5421,
"preview": "'use client';\n\nimport React, { useState, useEffect, useLayoutEffect, useRef } from 'react';\nimport { ArrowLeft, Settings"
},
{
"path": "frontend/src/components/AISummary/Block.tsx",
"chars": 9575,
"preview": "'use client';\n\nimport { Block } from '@/types';\nimport { useRef, useState, useEffect } from 'react';\n\ninterface BlockPro"
},
{
"path": "frontend/src/components/AISummary/BlockNoteSummaryView.tsx",
"chars": 8325,
"preview": "\"use client\";\n\nimport { useState, useEffect, useCallback, useRef, forwardRef, useImperativeHandle } from 'react';\nimport"
},
{
"path": "frontend/src/components/AISummary/Section.tsx",
"chars": 4448,
"preview": "'use client';\n\nimport { Section as SectionType, Block } from '@/types';\nimport { BlockComponent } from './Block';\nimport"
},
{
"path": "frontend/src/components/AISummary/index.tsx",
"chars": 27432,
"preview": "'use client';\n\nimport { useState, useEffect, useCallback, useRef, useMemo } from 'react';\nimport { Summary, Block } from"
},
{
"path": "frontend/src/components/About.tsx",
"chars": 7245,
"preview": "import React, { useState, useEffect } from \"react\";\nimport { invoke } from '@tauri-apps/api/core';\nimport { getVersion }"
},
{
"path": "frontend/src/components/AnalyticsConsentSwitch.tsx",
"chars": 8329,
"preview": "import React, { useContext, useState, useEffect } from 'react';\nimport { Switch } from '@/components/ui/switch';\nimport "
},
{
"path": "frontend/src/components/AnalyticsDataModal.tsx",
"chars": 7102,
"preview": "'use client';\n\nimport React from 'react';\nimport { X, Info, Shield } from 'lucide-react';\n\ninterface AnalyticsDataModalP"
},
{
"path": "frontend/src/components/AnalyticsProvider.tsx",
"chars": 4320,
"preview": "'use client';\n\nimport React, { useEffect, ReactNode, useRef, useState, createContext } from 'react';\nimport Analytics fr"
},
{
"path": "frontend/src/components/AudioBackendSelector.tsx",
"chars": 6178,
"preview": "import React, { useState, useEffect } from 'react';\nimport { invoke } from '@tauri-apps/api/core';\nimport { Info } from "
},
{
"path": "frontend/src/components/AudioLevelMeter.tsx",
"chars": 4544,
"preview": "import React from 'react';\n\ninterface AudioLevelMeterProps {\n rmsLevel: number; // 0.0 to 1.0\n peakLevel: number; "
},
{
"path": "frontend/src/components/AudioPlayer.tsx",
"chars": 0,
"preview": ""
},
{
"path": "frontend/src/components/BetaSettings.tsx",
"chars": 2563,
"preview": "\"use client\"\n\nimport { Switch } from \"./ui/switch\"\nimport { FlaskConical, AlertCircle } from \"lucide-react\"\nimport { use"
},
{
"path": "frontend/src/components/BlockNoteEditor/BasicBlockNoteTest.tsx",
"chars": 1811,
"preview": "import \"@blocknote/core/fonts/inter.css\";\nimport { useCreateBlockNote } from \"@blocknote/react\";\nimport { BlockNoteView "
},
{
"path": "frontend/src/components/BlockNoteEditor/Editor.tsx",
"chars": 1813,
"preview": "\"use client\";\n\nimport { useEffect } from \"react\";\nimport { PartialBlock, Block } from \"@blocknote/core\";\nimport \"@blockn"
},
{
"path": "frontend/src/components/BluetoothPlaybackWarning.tsx",
"chars": 3543,
"preview": "\"use client\";\nimport { useState, useEffect } from 'react';\nimport { invoke } from '@tauri-apps/api/core';\nimport { Alert"
},
{
"path": "frontend/src/components/BuiltInModelManager.tsx",
"chars": 17528,
"preview": "'use client';\n\nimport { useState, useEffect } from 'react';\nimport { invoke } from '@tauri-apps/api/core';\nimport { list"
},
{
"path": "frontend/src/components/ChunkProgressDisplay.tsx",
"chars": 9739,
"preview": "import React from 'react';\n\nexport interface ChunkStatus {\n chunk_id: number;\n status: 'pending' | 'processing' | 'com"
},
{
"path": "frontend/src/components/ComplianceNotification.tsx",
"chars": 3812,
"preview": "'use client';\n\nimport React, { useState, useEffect, useRef } from 'react';\nimport { Button } from './ui/button';\nimport "
},
{
"path": "frontend/src/components/ConfidenceIndicator.tsx",
"chars": 1477,
"preview": "'use client';\n\ninterface ConfidenceIndicatorProps {\n confidence: number;\n showIndicator?: boolean;\n}\n\nexport const Con"
},
{
"path": "frontend/src/components/ConfirmationModel/confirmation-modal.tsx",
"chars": 1076,
"preview": "import React from 'react';\n\ninterface ConfirmationModalProps {\n onConfirm: () => void;\n onCancel: () => void;\n text: "
},
{
"path": "frontend/src/components/ConsoleToggle.tsx",
"chars": 2634,
"preview": "import { useState } from 'react';\nimport { invoke } from '@tauri-apps/api/core';\nimport { Button } from '@/components/ui"
},
{
"path": "frontend/src/components/CustomDialog.tsx",
"chars": 1173,
"preview": "import React from \"react\";\nimport { Settings } from \"lucide-react\";\nimport { Dialog, DialogContent, DialogTitle, DialogT"
},
{
"path": "frontend/src/components/DatabaseImport/HomebrewDatabaseDetector.tsx",
"chars": 5114,
"preview": "'use client';\n\nimport { useEffect, useState } from 'react';\nimport { invoke } from '@tauri-apps/api/core';\nimport { toas"
},
{
"path": "frontend/src/components/DatabaseImport/LegacyDatabaseImport.tsx",
"chars": 8349,
"preview": "'use client';\n\nimport { useState } from 'react';\nimport { invoke } from '@tauri-apps/api/core';\nimport { toast } from 's"
},
{
"path": "frontend/src/components/DeviceSelection.tsx",
"chars": 13678,
"preview": "import React, { useState, useEffect } from 'react';\nimport { invoke } from '@tauri-apps/api/core';\nimport { listen } fro"
},
{
"path": "frontend/src/components/EditableTitle.tsx",
"chars": 3374,
"preview": "'use client';\n\nimport { useRef, useEffect } from 'react';\n\ninterface EditableTitleProps {\n title: string;\n isEditing: "
},
{
"path": "frontend/src/components/EmptyStateSummary.tsx",
"chars": 1922,
"preview": "'use client';\n\nimport { motion } from 'framer-motion';\nimport { FileQuestion, Sparkles } from 'lucide-react';\nimport { B"
},
{
"path": "frontend/src/components/ImportAudio/ImportAudioDialog.tsx",
"chars": 16948,
"preview": "import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';\nimport {\n Upload,\n Globe,\n Loader2,"
},
{
"path": "frontend/src/components/ImportAudio/ImportDropOverlay.tsx",
"chars": 981,
"preview": "import React from 'react';\nimport { Upload } from 'lucide-react';\nimport { getAudioFormatsDisplayList } from '@/constant"
},
{
"path": "frontend/src/components/ImportAudio/index.ts",
"chars": 114,
"preview": "export { ImportAudioDialog } from './ImportAudioDialog';\nexport { ImportDropOverlay } from './ImportDropOverlay';\n"
},
{
"path": "frontend/src/components/Info.tsx",
"chars": 1371,
"preview": "import React from \"react\";\nimport { Info as InfoIcon } from \"lucide-react\";\nimport { Dialog, DialogContent, DialogTitle,"
},
{
"path": "frontend/src/components/LanguageSelection.tsx",
"chars": 8944,
"preview": "import React, { useState, useEffect } from 'react';\nimport { Globe } from 'lucide-react';\nimport Analytics from '@/lib/a"
},
{
"path": "frontend/src/components/Logo.tsx",
"chars": 1325,
"preview": "import React from \"react\";\nimport Image from \"next/image\";\nimport { Dialog, DialogContent, DialogTitle, DialogTrigger } "
},
{
"path": "frontend/src/components/MainContent/index.tsx",
"chars": 524,
"preview": "'use client';\n\nimport React from 'react';\nimport { useSidebar } from '@/components/Sidebar/SidebarProvider';\n\ninterface "
},
{
"path": "frontend/src/components/MainNav/index.tsx",
"chars": 380,
"preview": "'use client';\n\nimport React from 'react';\n\ninterface MainNavProps {\n title: string;\n}\n\nconst MainNav: React.FC<MainNavP"
},
{
"path": "frontend/src/components/MeetingDetails/RetranscribeDialog.tsx",
"chars": 14254,
"preview": "import React, { useState, useEffect, useRef, useMemo } from 'react';\nimport { RefreshCw, Globe, Loader2, AlertCircle, Ch"
},
{
"path": "frontend/src/components/MeetingDetails/SummaryGeneratorButtonGroup.tsx",
"chars": 11595,
"preview": "\"use client\";\n\nimport { ModelConfig, ModelSettingsModal } from '@/components/ModelSettingsModal';\nimport {\n Dialog,\n D"
},
{
"path": "frontend/src/components/MeetingDetails/SummaryPanel.tsx",
"chars": 11261,
"preview": "\"use client\";\n\nimport { Summary, SummaryResponse, Transcript } from '@/types';\nimport { EditableTitle } from '@/componen"
},
{
"path": "frontend/src/components/MeetingDetails/SummaryUpdaterButtonGroup.tsx",
"chars": 2237,
"preview": "\"use client\";\n\nimport { Button } from '@/components/ui/button';\nimport { ButtonGroup } from '@/components/ui/button-grou"
},
{
"path": "frontend/src/components/MeetingDetails/TranscriptButtonGroup.tsx",
"chars": 3232,
"preview": "\"use client\";\n\nimport { useState, useCallback } from 'react';\nimport { Button } from '@/components/ui/button';\nimport { "
},
{
"path": "frontend/src/components/MeetingDetails/TranscriptPanel.tsx",
"chars": 3736,
"preview": "\"use client\";\n\nimport { Transcript, TranscriptSegmentData } from '@/types';\nimport { TranscriptView } from '@/components"
},
{
"path": "frontend/src/components/MessageToast.tsx",
"chars": 609,
"preview": "import {useEffect, useState} from 'react';\n\ninterface MessageToastProps {\n message: string;\n type: 'success' | 'er"
},
{
"path": "frontend/src/components/ModelDownloadProgress.tsx",
"chars": 3893,
"preview": "import React from 'react';\nimport { ModelStatus } from '../lib/whisper';\nimport { Button } from './ui/button';\n\ninterfac"
},
{
"path": "frontend/src/components/ModelSettingsModal.tsx",
"chars": 56134,
"preview": "import { useState, useEffect, useRef } from 'react';\nimport { useSidebar } from './Sidebar/SidebarProvider';\nimport { in"
},
{
"path": "frontend/src/components/ParakeetModelManager.tsx",
"chars": 20687,
"preview": "import React, { useState, useEffect, useRef, useCallback } from 'react';\nimport { listen } from '@tauri-apps/api/event';"
},
{
"path": "frontend/src/components/PermissionWarning.tsx",
"chars": 5469,
"preview": "import React from 'react';\nimport { AlertTriangle, Mic, Speaker, RefreshCw } from 'lucide-react';\nimport { Alert, AlertD"
},
{
"path": "frontend/src/components/PreferenceSettings.tsx",
"chars": 9171,
"preview": "\"use client\"\n\nimport { useEffect, useState, useRef } from \"react\"\nimport { Switch } from \"./ui/switch\"\nimport { FolderOp"
},
{
"path": "frontend/src/components/RecordingControls.tsx",
"chars": 21956,
"preview": "'use client';\n\nimport { invoke } from '@tauri-apps/api/core';\nimport { appDataDir } from '@tauri-apps/api/path';\nimport "
},
{
"path": "frontend/src/components/RecordingSettings.tsx",
"chars": 8981,
"preview": "import React, { useState, useEffect } from 'react';\nimport { Switch } from '@/components/ui/switch';\nimport { FolderOpen"
},
{
"path": "frontend/src/components/RecordingStatusBar.tsx",
"chars": 1686,
"preview": "'use client';\n\nimport { motion } from 'framer-motion';\nimport { useRecordingState } from '@/contexts/RecordingStateConte"
},
{
"path": "frontend/src/components/SettingTabs.tsx",
"chars": 2131,
"preview": "import { Tabs, TabsContent, TabsList, TabsTrigger } from \"@/components/ui/tabs\"\nimport { ModelConfig, ModelSettingsModal"
},
{
"path": "frontend/src/components/Sidebar/SidebarProvider.tsx",
"chars": 10387,
"preview": "'use client';\n\nimport React, { createContext, useContext, useState, useEffect } from 'react';\nimport { usePathname, useR"
},
{
"path": "frontend/src/components/Sidebar/index.tsx",
"chars": 31951,
"preview": "'use client';\n\nimport React, { useState, useMemo, useEffect, useCallback } from 'react';\nimport { ChevronDown, ChevronRi"
},
{
"path": "frontend/src/components/SummaryModelSettings.tsx",
"chars": 5307,
"preview": "'use client';\n\nimport { useState, useEffect, useCallback } from 'react';\nimport { invoke } from '@tauri-apps/api/core';\n"
},
{
"path": "frontend/src/components/TranscriptRecovery/TranscriptRecovery.tsx",
"chars": 12415,
"preview": "/**\n * TranscriptRecovery Component\n *\n * Modal dialog for recovering interrupted meetings from IndexedDB.\n * Displays r"
},
{
"path": "frontend/src/components/TranscriptRecovery/index.ts",
"chars": 59,
"preview": "export { TranscriptRecovery } from './TranscriptRecovery';\n"
},
{
"path": "frontend/src/components/TranscriptSettings.tsx",
"chars": 11342,
"preview": "import { useState, useEffect } from 'react';\nimport { invoke } from '@tauri-apps/api/core';\nimport { Select, SelectConte"
},
{
"path": "frontend/src/components/TranscriptView.tsx",
"chars": 13940,
"preview": "'use client';\n\nimport { Transcript } from '@/types';\nimport { useEffect, useRef, useState } from 'react';\nimport { Confi"
},
{
"path": "frontend/src/components/UpdateCheckProvider.tsx",
"chars": 2353,
"preview": "'use client'\n\nimport React, { createContext, useContext, useState, useCallback, useEffect } from 'react';\nimport { useUp"
},
{
"path": "frontend/src/components/UpdateDialog.tsx",
"chars": 10081,
"preview": "import React, { useState, useEffect } from 'react';\nimport { Download, X, CheckCircle2, AlertCircle, Loader2 } from 'luc"
},
{
"path": "frontend/src/components/UpdateNotification.tsx",
"chars": 1298,
"preview": "import React from 'react';\nimport { Download } from 'lucide-react';\nimport { toast } from 'sonner';\nimport { UpdateInfo "
},
{
"path": "frontend/src/components/VirtualizedTranscriptView.tsx",
"chars": 16578,
"preview": "'use client';\n\nimport { useCallback, useRef, useReducer, startTransition, useEffect, useState, memo } from \"react\";\nimpo"
},
{
"path": "frontend/src/components/WhisperModelManager.tsx",
"chars": 24742,
"preview": "import React, { useState, useEffect, useRef } from 'react';\nimport { listen } from '@tauri-apps/api/event';\nimport { inv"
},
{
"path": "frontend/src/components/molecules/form-components/form-input-item.tsx",
"chars": 3462,
"preview": "import {\n FormControl,\n FormField,\n FormItem,\n FormMessage,\n FormLabel,\n} from '@/components/ui/form';\nimport { Inp"
},
{
"path": "frontend/src/components/molecules/form-components/form-input-switch.tsx",
"chars": 1434,
"preview": "import {\n FormControl,\n FormField,\n FormItem,\n FormLabel,\n FormDescription,\n} from '@/components/ui/form';\nimport {"
},
{
"path": "frontend/src/components/molecules/form-components/form-select-item.tsx",
"chars": 2426,
"preview": "import {\n FormField,\n FormItem,\n FormLabel,\n FormControl,\n FormMessage,\n} from '@/components/ui/form';\nimport {\n S"
},
{
"path": "frontend/src/components/onboarding/OnboardingContainer.tsx",
"chars": 3583,
"preview": "import React from 'react';\nimport { ChevronLeft, ChevronRight } from 'lucide-react';\nimport { cn } from '@/lib/utils';\ni"
},
{
"path": "frontend/src/components/onboarding/OnboardingFlow.tsx",
"chars": 1524,
"preview": "import React, { useEffect } from 'react';\nimport { useOnboarding } from '@/contexts/OnboardingContext';\nimport {\n Welco"
},
{
"path": "frontend/src/components/onboarding/index.ts",
"chars": 137,
"preview": "export { OnboardingFlow } from './OnboardingFlow';\nexport { OnboardingContainer } from './OnboardingContainer';\nexport *"
},
{
"path": "frontend/src/components/onboarding/shared/PermissionRow.tsx",
"chars": 2931,
"preview": "import React from 'react';\nimport { CheckCircle2, Loader2, XCircle } from 'lucide-react';\nimport { cn } from '@/lib/util"
},
{
"path": "frontend/src/components/onboarding/shared/ProgressIndicator.tsx",
"chars": 2395,
"preview": "import React from 'react';\nimport { Check, Lock, Download, CheckCircle2, BrainCircuit } from 'lucide-react';\n\ninterface "
},
{
"path": "frontend/src/components/onboarding/shared/StatusIndicator.tsx",
"chars": 558,
"preview": "import React from 'react';\nimport { cn } from '@/lib/utils';\nimport type { StatusIndicatorProps } from '@/types/onboardi"
},
{
"path": "frontend/src/components/onboarding/shared/index.ts",
"chars": 159,
"preview": "export { ProgressIndicator } from './ProgressIndicator';\nexport { PermissionRow } from './PermissionRow';\nexport { Statu"
},
{
"path": "frontend/src/components/onboarding/steps/DownloadProgressStep.tsx",
"chars": 16927,
"preview": "import React, { useEffect, useState, useRef } from 'react';\nimport { invoke } from '@tauri-apps/api/core';\nimport { list"
},
{
"path": "frontend/src/components/onboarding/steps/PermissionsStep.tsx",
"chars": 6088,
"preview": "import React, { useEffect, useState, useCallback } from 'react';\nimport { invoke } from '@tauri-apps/api/core';\nimport {"
},
{
"path": "frontend/src/components/onboarding/steps/SetupOverviewStep.tsx",
"chars": 4311,
"preview": "import React, { useEffect, useState } from 'react';\nimport { invoke } from '@tauri-apps/api/core';\nimport { Download, In"
},
{
"path": "frontend/src/components/onboarding/steps/WelcomeStep.tsx",
"chars": 2066,
"preview": "import React from 'react';\nimport { Lock, Sparkles, Cpu } from 'lucide-react';\nimport { Button } from '@/components/ui/b"
},
{
"path": "frontend/src/components/onboarding/steps/index.ts",
"chars": 217,
"preview": "export { WelcomeStep } from './WelcomeStep';\nexport { PermissionsStep } from './PermissionsStep';\nexport { DownloadProgr"
},
{
"path": "frontend/src/components/shared/DownloadProgressToast.tsx",
"chars": 11577,
"preview": "'use client';\n\nimport React, { useEffect, useState, useCallback } from 'react';\nimport { listen } from '@tauri-apps/api/"
},
{
"path": "frontend/src/components/ui/accordion.tsx",
"chars": 2084,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { ChevronDownIcon } from \"lucide-react\"\nimport { Accordion as Accord"
},
{
"path": "frontend/src/components/ui/alert.tsx",
"chars": 1598,
"preview": "import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/"
},
{
"path": "frontend/src/components/ui/button-group.tsx",
"chars": 2209,
"preview": "import { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { "
},
{
"path": "frontend/src/components/ui/button.tsx",
"chars": 2182,
"preview": "import * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class"
},
{
"path": "frontend/src/components/ui/command.tsx",
"chars": 4887,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { type DialogProps } from \"@radix-ui/react-dialog\"\nimport { Command "
},
{
"path": "frontend/src/components/ui/dialog.tsx",
"chars": 3849,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\"\nimport { X } from"
},
{
"path": "frontend/src/components/ui/dropdown-menu.tsx",
"chars": 7606,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\"\nimpo"
},
{
"path": "frontend/src/components/ui/form.tsx",
"chars": 4132,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as LabelPrimitive from \"@radix-ui/react-label\"\nimport { Slot } fro"
},
{
"path": "frontend/src/components/ui/input-group.tsx",
"chars": 4985,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport {"
},
{
"path": "frontend/src/components/ui/input.tsx",
"chars": 768,
"preview": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Input = React.forwardRef<HTMLInputElement, React"
},
{
"path": "frontend/src/components/ui/label.tsx",
"chars": 724,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as LabelPrimitive from \"@radix-ui/react-label\"\nimport { cva, type "
},
{
"path": "frontend/src/components/ui/popover.tsx",
"chars": 1356,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as PopoverPrimitive from \"@radix-ui/react-popover\"\n\nimport { cn } "
},
{
"path": "frontend/src/components/ui/progress.tsx",
"chars": 792,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as ProgressPrimitive from \"@radix-ui/react-progress\"\n\nimport { cn "
},
{
"path": "frontend/src/components/ui/scroll-area.tsx",
"chars": 1656,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as ScrollAreaPrimitive from \"@radix-ui/react-scroll-area\"\n\nimport "
}
]
// ... and 241 more files (download for full content)
About this extraction
This page contains the full source code of the Zackriya-Solutions/meetily GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 441 files (3.7 MB), approximately 990.0k tokens, and a symbol index with 2454 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.