feat(native): record encoding (#14188)

fix #13784 

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Start/stop system or meeting recordings with Ogg/Opus artifacts and
native start/stop APIs; workspace backup recovery.

* **Refactor**
* Simplified recording lifecycle and UI flows; native runtime now
orchestrates recording/processing and reporting.

* **Bug Fixes**
* Stronger path validation, safer import/export dialogs, consistent
error handling/logging, and retry-safe recording processing.

* **Chores**
* Added cross-platform native audio capture and Ogg/Opus encoding
support.

* **Tests**
* New unit, integration, and e2e tests for recording, path guards,
dialogs, and workspace recovery.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
DarkSky
2026-03-22 02:50:14 +08:00
committed by GitHub
parent 6a93566422
commit bcf2a51d41
44 changed files with 2921 additions and 1143 deletions
@@ -160,11 +160,7 @@ class TrayState implements Disposable {
const recordingStatus = recordingStatus$.value;
if (
!recordingStatus ||
(recordingStatus?.status !== 'paused' &&
recordingStatus?.status !== 'recording')
) {
if (!recordingStatus || recordingStatus.status !== 'recording') {
const appMenuItems = runningAppGroups.map(appGroup => ({
label: appGroup.name,
icon: appGroup.icon || undefined,
@@ -172,7 +168,9 @@ class TrayState implements Disposable {
logger.info(
`User action: Start Recording Meeting (${appGroup.name})`
);
startRecording(appGroup);
startRecording(appGroup).catch(err => {
logger.error('Failed to start recording:', err);
});
},
}));
@@ -188,7 +186,9 @@ class TrayState implements Disposable {
logger.info(
'User action: Start Recording Meeting (System audio)'
);
startRecording();
startRecording().catch(err => {
logger.error('Failed to start recording:', err);
});
},
},
...appMenuItems,
@@ -201,7 +201,7 @@ class TrayState implements Disposable {
? `Recording (${recordingStatus.appGroup?.name})`
: 'Recording';
// recording is either started or paused
// recording is active
items.push(
{
label: recordingLabel,