@agent-native/core 0.157.0 → 0.157.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +19 -4
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +7 -2
- package/corpus/templates/assets/app/global.css +4 -0
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +1 -2
- package/corpus/templates/brain/app/routes/sources.tsx +1 -2
- package/corpus/templates/calendar/app/hooks/use-bookings.ts +12 -2
- package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +64 -2
- package/corpus/templates/calendar/server/handlers/bookings.ts +224 -93
- package/corpus/templates/calendar/server/plugins/auth.ts +0 -2
- package/corpus/templates/clips/actions/create-folder.ts +7 -1
- package/corpus/templates/clips/actions/create-meeting.ts +6 -2
- package/corpus/templates/clips/actions/create-recording.ts +5 -3
- package/corpus/templates/clips/actions/finalize-recording.ts +97 -65
- package/corpus/templates/clips/actions/import-loom-recording.ts +5 -3
- package/corpus/templates/clips/actions/request-recording-access.ts +305 -0
- package/corpus/templates/clips/actions/stitch-recordings.ts +5 -3
- package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +38 -63
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +75 -75
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +3 -21
- package/corpus/templates/clips/app/components/sharing/share-ui.tsx +13 -30
- package/corpus/templates/clips/app/i18n/en-US.ts +10 -0
- package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +5 -5
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +9 -16
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +135 -18
- package/corpus/templates/clips/changelog/2026-08-14-long-resumable-uploads-now-abort-cleanly-and-show-errors.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-14-standardized-share-popovers.md +6 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +11 -3
- package/corpus/templates/clips/chrome-extension/src/github-preview-content.ts +37 -8
- package/corpus/templates/clips/chrome-extension/src/github-preview.ts +3 -3
- package/corpus/templates/clips/chrome-extension/src/offscreen.ts +8 -25
- package/corpus/templates/clips/desktop/src/app.tsx +24 -0
- package/corpus/templates/clips/desktop/src/lib/media-capture-constraints.ts +42 -13
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +11 -18
- package/corpus/templates/clips/desktop/src/overlays/onboarding.tsx +1 -0
- package/corpus/templates/clips/desktop/src/shared/config.ts +1 -0
- package/corpus/templates/clips/desktop/src/styles.css +24 -9
- package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +3 -0
- package/corpus/templates/clips/desktop/src-tauri/src/config.rs +8 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +154 -176
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +115 -31
- package/corpus/templates/clips/desktop/src-tauri/src/rewind_clip.rs +7 -0
- package/corpus/templates/clips/server/db/index.ts +1 -0
- package/corpus/templates/clips/server/db/schema.ts +1 -0
- package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +6 -1
- package/corpus/templates/clips/server/lib/emails.ts +23 -0
- package/corpus/templates/clips/server/lib/public-agent-context.ts +55 -0
- package/corpus/templates/clips/server/lib/recordings.ts +2 -1
- package/corpus/templates/clips/server/lib/resumable-upload-cleanup.ts +49 -0
- package/corpus/templates/clips/server/lib/s3-upload-provider.ts +34 -7
- package/corpus/templates/clips/server/lib/upload-interruption.ts +15 -1
- package/corpus/templates/clips/server/lib/upload-lease.ts +94 -9
- package/corpus/templates/clips/server/routes/api/agent-context.json.get.ts +82 -7
- package/corpus/templates/clips/server/routes/api/public-recording.get.ts +1 -0
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +71 -12
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/interrupt.post.ts +4 -3
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/reset-chunks.post.ts +133 -4
- package/corpus/templates/clips/server/routes/api/uploads/reap.post.ts +8 -1
- package/corpus/templates/clips/shared/recording-link.ts +4 -0
- package/corpus/templates/clips/shared/share-loader-response.ts +2 -1
- package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +7 -16
- package/corpus/templates/factory/AGENTS.md +5 -1
- package/corpus/templates/factory/README.md +6 -2
- package/corpus/templates/factory/actions/view-screen.ts +15 -0
- package/corpus/templates/factory/app/components/factory/FactoryAgentsView.tsx +58 -189
- package/corpus/templates/factory/app/components/layout/Header.tsx +1 -0
- package/corpus/templates/factory/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/factory/app/components/layout/Sidebar.tsx +7 -0
- package/corpus/templates/factory/app/hooks/use-navigation-state.ts +3 -0
- package/corpus/templates/factory/app/i18n/en-US.ts +3 -1
- package/corpus/templates/factory/app/routes/agents.tsx +9 -0
- package/corpus/templates/mail/server/plugins/auth.ts +0 -2
- package/corpus/templates/plan/app/components/plan/LocalCodebasePicker.tsx +50 -14
- package/corpus/templates/plan/app/pages/PlansPage.tsx +9 -19
- package/corpus/templates/slides/app/components/editor/HistoryPanel.tsx +7 -1
- package/corpus/templates/slides/app/context/DeckContext.tsx +71 -22
- package/corpus/templates/slides/app/lib/editor-shortcuts.ts +46 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +13 -0
- package/dist/client/AgentPanel.d.ts +13 -1
- package/dist/client/AgentPanel.js +12 -10
- package/dist/client/AssistantChat.js +25 -5
- package/dist/client/HighlightedCodeBlock.js +12 -2
- package/dist/client/agent-chat/index.d.ts +1 -0
- package/dist/client/agent-chat/index.js +1 -0
- package/dist/client/chat/markdown-renderer.js +16 -3
- package/dist/client/chat-first/apps-rail.js +1 -1
- package/dist/client/components/ui/message-scroller.js +1 -1
- package/dist/client/settings/AccountSettingsCard.js +48 -19
- package/dist/client/sharing/ShareButton.d.ts +1 -2
- package/dist/client/sharing/ShareButton.js +5 -41
- package/dist/client/sharing/ShareDialog.js +5 -4
- package/dist/client/use-chat-threads.js +10 -14
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/localization/default-messages.d.ts +1 -0
- package/dist/localization/default-messages.js +1 -0
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
- package/dist/provider-api/actions/provider-api.d.ts +15 -15
- package/dist/server/auth-marketing.d.ts +1 -0
- package/dist/server/auth-marketing.js +0 -2
- package/dist/server/auth.d.ts +1 -0
- package/dist/server/auth.js +18 -0
- package/dist/server/onboarding-html.d.ts +1 -0
- package/dist/server/onboarding-html.js +2 -88
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/shared/markdown-block-split.d.ts +16 -0
- package/dist/shared/markdown-block-split.js +81 -5
- package/dist/styles/agent-native.css +13 -0
- package/dist/voice/voice-cleanup-prompt.js +1 -0
- package/docs/content/cloneable-saas.mdx +24 -26
- package/package.json +3 -3
- package/corpus/templates/clips/shared/mic-audio-cleanup.ts +0 -350
|
@@ -1,350 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Conservative browser-side mic cleanup for Clips recorders.
|
|
3
|
-
*
|
|
4
|
-
* The goal is not to "fix" bad audio with a heavy denoiser. Instead we trim
|
|
5
|
-
* obvious AC rumble, notch the mains hum that sits under cheap mics, and only
|
|
6
|
-
* apply a gentle downward expander when the signal is already quiet.
|
|
7
|
-
*
|
|
8
|
-
* Safe failure rule: if the browser does not support the needed Web Audio
|
|
9
|
-
* pieces, or if graph setup fails for any reason, callers get the original
|
|
10
|
-
* stream back with a no-op cleanup handle rather than a thrown error.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
export const MIC_AUDIO_HIGH_PASS_HZ = 70;
|
|
14
|
-
export const MIC_AUDIO_HUM_NOTCH_FREQUENCIES = [50, 60, 100, 120] as const;
|
|
15
|
-
export const MIC_AUDIO_HUM_NOTCH_Q = 35;
|
|
16
|
-
export const MIC_AUDIO_EXPANDER_INTERVAL_MS = 40;
|
|
17
|
-
export const MIC_AUDIO_EXPANDER_FLOOR_GAIN = 0.24;
|
|
18
|
-
export const MIC_AUDIO_EXPANDER_OPEN_DB = -42;
|
|
19
|
-
export const MIC_AUDIO_EXPANDER_CLOSE_DB = -58;
|
|
20
|
-
export const MIC_AUDIO_EXPANDER_SMOOTHING_SECONDS = 0.09;
|
|
21
|
-
|
|
22
|
-
export type MicAudioCleanupPreset = "balanced" | "strong";
|
|
23
|
-
|
|
24
|
-
export interface MicAudioCleanupOptions {
|
|
25
|
-
audioContext?: AudioContext | null;
|
|
26
|
-
preset?: MicAudioCleanupPreset;
|
|
27
|
-
intervalMs?: number;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface MicAudioCleanupHandle {
|
|
31
|
-
readonly inputStream: MediaStream;
|
|
32
|
-
readonly stream: MediaStream;
|
|
33
|
-
readonly active: boolean;
|
|
34
|
-
stop(): void;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
type AudioContextLike = Pick<
|
|
38
|
-
AudioContext,
|
|
39
|
-
| "state"
|
|
40
|
-
| "currentTime"
|
|
41
|
-
| "resume"
|
|
42
|
-
| "close"
|
|
43
|
-
| "createAnalyser"
|
|
44
|
-
| "createBiquadFilter"
|
|
45
|
-
| "createGain"
|
|
46
|
-
| "createMediaStreamDestination"
|
|
47
|
-
| "createMediaStreamSource"
|
|
48
|
-
>;
|
|
49
|
-
|
|
50
|
-
type AudioNodeLike = {
|
|
51
|
-
connect(destination: unknown): unknown;
|
|
52
|
-
disconnect(): void;
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
type GainNodeLike = AudioNodeLike & {
|
|
56
|
-
gain: AudioParam & {
|
|
57
|
-
setTargetAtTime?: (
|
|
58
|
-
value: number,
|
|
59
|
-
startTime: number,
|
|
60
|
-
timeConstant: number,
|
|
61
|
-
) => void;
|
|
62
|
-
};
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
type BiquadFilterNodeLike = AudioNodeLike & {
|
|
66
|
-
type: BiquadFilterType;
|
|
67
|
-
frequency: AudioParam;
|
|
68
|
-
Q: AudioParam;
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
type AnalyserNodeLike = AudioNodeLike & {
|
|
72
|
-
fftSize: number;
|
|
73
|
-
getFloatTimeDomainData: (dataArray: Float32Array<ArrayBuffer>) => void;
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
type MediaStreamDestinationNodeLike = AudioNodeLike & {
|
|
77
|
-
stream: MediaStream;
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
type MediaStreamSourceNodeLike = AudioNodeLike;
|
|
81
|
-
|
|
82
|
-
interface MicAudioCleanupRuntime {
|
|
83
|
-
ctx: AudioContextLike;
|
|
84
|
-
ownsContext: boolean;
|
|
85
|
-
source: MediaStreamSourceNodeLike;
|
|
86
|
-
highPass: BiquadFilterNodeLike;
|
|
87
|
-
notches: BiquadFilterNodeLike[];
|
|
88
|
-
gain: GainNodeLike;
|
|
89
|
-
analyser: AnalyserNodeLike;
|
|
90
|
-
destination: MediaStreamDestinationNodeLike;
|
|
91
|
-
intervalId: ReturnType<typeof setInterval>;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function getAudioContextCtor(): typeof AudioContext | null {
|
|
95
|
-
const global = globalThis as typeof globalThis & {
|
|
96
|
-
AudioContext?: typeof AudioContext;
|
|
97
|
-
webkitAudioContext?: typeof AudioContext;
|
|
98
|
-
};
|
|
99
|
-
return global.AudioContext ?? global.webkitAudioContext ?? null;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function normalizePreset(preset: MicAudioCleanupPreset | undefined) {
|
|
103
|
-
return preset === "strong"
|
|
104
|
-
? {
|
|
105
|
-
floorGain: 0.16,
|
|
106
|
-
openDb: -38,
|
|
107
|
-
closeDb: -56,
|
|
108
|
-
smoothingSeconds: 0.07,
|
|
109
|
-
intervalMs: 32,
|
|
110
|
-
}
|
|
111
|
-
: {
|
|
112
|
-
floorGain: MIC_AUDIO_EXPANDER_FLOOR_GAIN,
|
|
113
|
-
openDb: MIC_AUDIO_EXPANDER_OPEN_DB,
|
|
114
|
-
closeDb: MIC_AUDIO_EXPANDER_CLOSE_DB,
|
|
115
|
-
smoothingSeconds: MIC_AUDIO_EXPANDER_SMOOTHING_SECONDS,
|
|
116
|
-
intervalMs: MIC_AUDIO_EXPANDER_INTERVAL_MS,
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function hasAudioTrack(stream: MediaStream): boolean {
|
|
121
|
-
try {
|
|
122
|
-
return stream.getAudioTracks().length > 0;
|
|
123
|
-
// coercion-ok: an unreadable track is a typed inactive cleanup fallback.
|
|
124
|
-
} catch {
|
|
125
|
-
return false;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function buildTargetGain(
|
|
130
|
-
levelDb: number,
|
|
131
|
-
preset: ReturnType<typeof normalizePreset>,
|
|
132
|
-
) {
|
|
133
|
-
if (!Number.isFinite(levelDb)) return preset.floorGain;
|
|
134
|
-
if (levelDb >= preset.openDb) return 1;
|
|
135
|
-
if (levelDb <= preset.closeDb) return preset.floorGain;
|
|
136
|
-
|
|
137
|
-
const span = preset.openDb - preset.closeDb;
|
|
138
|
-
const t = (levelDb - preset.closeDb) / span;
|
|
139
|
-
// Ease in so the expander stays out of the way until the signal is
|
|
140
|
-
// genuinely quiet, then falls away a little more decisively.
|
|
141
|
-
return preset.floorGain + (1 - preset.floorGain) * t * t;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function measureRmsDb(samples: Float32Array): number {
|
|
145
|
-
let sumSquares = 0;
|
|
146
|
-
for (let i = 0; i < samples.length; i++) {
|
|
147
|
-
const sample = samples[i] ?? 0;
|
|
148
|
-
sumSquares += sample * sample;
|
|
149
|
-
}
|
|
150
|
-
if (sumSquares <= 0) return Number.NEGATIVE_INFINITY;
|
|
151
|
-
const rms = Math.sqrt(sumSquares / samples.length);
|
|
152
|
-
if (rms <= 0) return Number.NEGATIVE_INFINITY;
|
|
153
|
-
return 20 * Math.log10(rms);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function disconnectNode(node: AudioNodeLike | null | undefined): void {
|
|
157
|
-
if (!node) return;
|
|
158
|
-
try {
|
|
159
|
-
node.disconnect();
|
|
160
|
-
// coercion-ok: disconnect is teardown-only and may race an ended track.
|
|
161
|
-
} catch {
|
|
162
|
-
// ignore — cleanup must be best-effort.
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function stopDestinationStream(stream: MediaStream): void {
|
|
167
|
-
try {
|
|
168
|
-
for (const track of stream.getTracks()) {
|
|
169
|
-
try {
|
|
170
|
-
track.stop();
|
|
171
|
-
// coercion-ok: stopping an already-ended destination track is cleanup-only.
|
|
172
|
-
} catch {
|
|
173
|
-
// ignore
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
// coercion-ok: destination teardown is best-effort after capture has ended.
|
|
177
|
-
} catch {
|
|
178
|
-
// ignore
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
function createInactiveHandle(stream: MediaStream): MicAudioCleanupHandle {
|
|
183
|
-
return {
|
|
184
|
-
inputStream: stream,
|
|
185
|
-
stream,
|
|
186
|
-
active: false,
|
|
187
|
-
stop() {
|
|
188
|
-
// no-op
|
|
189
|
-
},
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function tryBuildMicAudioCleanup(
|
|
194
|
-
stream: MediaStream,
|
|
195
|
-
options: MicAudioCleanupOptions,
|
|
196
|
-
): MicAudioCleanupHandle {
|
|
197
|
-
// The browser path can hand us the actual `AudioContext` instance, while
|
|
198
|
-
// tests can inject a fake object. If neither exists, there is nothing to do.
|
|
199
|
-
const audioContextCtor = getAudioContextCtor();
|
|
200
|
-
const providedContext = options.audioContext ?? null;
|
|
201
|
-
const audioContext: AudioContextLike | null =
|
|
202
|
-
providedContext ?? (audioContextCtor ? new audioContextCtor() : null);
|
|
203
|
-
const ownsContext = !providedContext;
|
|
204
|
-
|
|
205
|
-
if (!audioContext || !hasAudioTrack(stream)) {
|
|
206
|
-
return createInactiveHandle(stream);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const preset = normalizePreset(options.preset);
|
|
210
|
-
const intervalMs = Math.max(
|
|
211
|
-
16,
|
|
212
|
-
Math.round(options.intervalMs ?? preset.intervalMs),
|
|
213
|
-
);
|
|
214
|
-
const supportsNodes =
|
|
215
|
-
typeof audioContext.createAnalyser === "function" &&
|
|
216
|
-
typeof audioContext.createBiquadFilter === "function" &&
|
|
217
|
-
typeof audioContext.createGain === "function" &&
|
|
218
|
-
typeof audioContext.createMediaStreamDestination === "function" &&
|
|
219
|
-
typeof audioContext.createMediaStreamSource === "function";
|
|
220
|
-
if (!supportsNodes) {
|
|
221
|
-
return createInactiveHandle(stream);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
let runtime: MicAudioCleanupRuntime | null = null;
|
|
225
|
-
try {
|
|
226
|
-
const source = audioContext.createMediaStreamSource(stream);
|
|
227
|
-
const highPass = audioContext.createBiquadFilter();
|
|
228
|
-
highPass.type = "highpass";
|
|
229
|
-
highPass.frequency.value = MIC_AUDIO_HIGH_PASS_HZ;
|
|
230
|
-
highPass.Q.value = 0.707;
|
|
231
|
-
|
|
232
|
-
const notches = MIC_AUDIO_HUM_NOTCH_FREQUENCIES.map((frequency) => {
|
|
233
|
-
const filter = audioContext.createBiquadFilter();
|
|
234
|
-
filter.type = "notch";
|
|
235
|
-
filter.frequency.value = frequency;
|
|
236
|
-
filter.Q.value = MIC_AUDIO_HUM_NOTCH_Q;
|
|
237
|
-
return filter;
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
const gain = audioContext.createGain();
|
|
241
|
-
gain.gain.value = 1;
|
|
242
|
-
const analyser = audioContext.createAnalyser();
|
|
243
|
-
analyser.fftSize = 1024;
|
|
244
|
-
const destination = audioContext.createMediaStreamDestination();
|
|
245
|
-
|
|
246
|
-
source.connect(highPass);
|
|
247
|
-
let node: AudioNodeLike = highPass;
|
|
248
|
-
for (const notch of notches) {
|
|
249
|
-
node.connect(notch);
|
|
250
|
-
node = notch;
|
|
251
|
-
}
|
|
252
|
-
node.connect(gain);
|
|
253
|
-
node.connect(analyser);
|
|
254
|
-
gain.connect(destination);
|
|
255
|
-
|
|
256
|
-
if (
|
|
257
|
-
audioContext.state === "suspended" &&
|
|
258
|
-
typeof audioContext.resume === "function"
|
|
259
|
-
) {
|
|
260
|
-
void audioContext.resume().catch(() => {});
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
const samples = new Float32Array(analyser.fftSize);
|
|
264
|
-
const tick = () => {
|
|
265
|
-
if (!runtime) return;
|
|
266
|
-
try {
|
|
267
|
-
runtime.analyser.getFloatTimeDomainData(samples);
|
|
268
|
-
const nextGain = buildTargetGain(measureRmsDb(samples), preset);
|
|
269
|
-
const now = runtime.ctx.currentTime;
|
|
270
|
-
if (typeof runtime.gain.gain.setTargetAtTime === "function") {
|
|
271
|
-
runtime.gain.gain.setTargetAtTime(
|
|
272
|
-
nextGain,
|
|
273
|
-
now,
|
|
274
|
-
preset.smoothingSeconds,
|
|
275
|
-
);
|
|
276
|
-
} else {
|
|
277
|
-
runtime.gain.gain.value = nextGain;
|
|
278
|
-
}
|
|
279
|
-
// coercion-ok: a transient analyser/meter failure must not stop recording.
|
|
280
|
-
} catch {
|
|
281
|
-
// Keep the recording alive even if the meter path glitches.
|
|
282
|
-
}
|
|
283
|
-
};
|
|
284
|
-
|
|
285
|
-
const intervalId = globalThis.setInterval(tick, intervalMs);
|
|
286
|
-
runtime = {
|
|
287
|
-
ctx: audioContext,
|
|
288
|
-
ownsContext,
|
|
289
|
-
source,
|
|
290
|
-
highPass,
|
|
291
|
-
notches,
|
|
292
|
-
gain,
|
|
293
|
-
analyser,
|
|
294
|
-
destination,
|
|
295
|
-
intervalId,
|
|
296
|
-
};
|
|
297
|
-
|
|
298
|
-
return {
|
|
299
|
-
inputStream: stream,
|
|
300
|
-
stream: destination.stream,
|
|
301
|
-
active: true,
|
|
302
|
-
stop() {
|
|
303
|
-
if (!runtime) return;
|
|
304
|
-
const current = runtime;
|
|
305
|
-
runtime = null;
|
|
306
|
-
try {
|
|
307
|
-
clearInterval(current.intervalId);
|
|
308
|
-
// coercion-ok: timer cleanup is best-effort after the interval ends.
|
|
309
|
-
} catch {
|
|
310
|
-
// ignore
|
|
311
|
-
}
|
|
312
|
-
disconnectNode(current.source);
|
|
313
|
-
disconnectNode(current.highPass);
|
|
314
|
-
for (const notch of current.notches) disconnectNode(notch);
|
|
315
|
-
disconnectNode(current.gain);
|
|
316
|
-
disconnectNode(current.analyser);
|
|
317
|
-
stopDestinationStream(current.destination.stream);
|
|
318
|
-
if (current.ownsContext && typeof current.ctx.close === "function") {
|
|
319
|
-
void current.ctx.close().catch(() => {});
|
|
320
|
-
}
|
|
321
|
-
},
|
|
322
|
-
};
|
|
323
|
-
} catch {
|
|
324
|
-
if (runtime) {
|
|
325
|
-
try {
|
|
326
|
-
clearInterval(runtime.intervalId);
|
|
327
|
-
} catch {
|
|
328
|
-
// ignore
|
|
329
|
-
}
|
|
330
|
-
disconnectNode(runtime.source);
|
|
331
|
-
disconnectNode(runtime.highPass);
|
|
332
|
-
for (const notch of runtime.notches) disconnectNode(notch);
|
|
333
|
-
disconnectNode(runtime.gain);
|
|
334
|
-
disconnectNode(runtime.analyser);
|
|
335
|
-
stopDestinationStream(runtime.destination.stream);
|
|
336
|
-
}
|
|
337
|
-
return createInactiveHandle(stream);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
export function createMicAudioCleanup(
|
|
342
|
-
stream: MediaStream,
|
|
343
|
-
options: MicAudioCleanupOptions = {},
|
|
344
|
-
): MicAudioCleanupHandle {
|
|
345
|
-
try {
|
|
346
|
-
return tryBuildMicAudioCleanup(stream, options);
|
|
347
|
-
} catch {
|
|
348
|
-
return createInactiveHandle(stream);
|
|
349
|
-
}
|
|
350
|
-
}
|