From 3ca2026f202d0b4eb77b511585a4b4c895dade75 Mon Sep 17 00:00:00 2001 From: Tadas Petra <60107328+tadaspetra@users.noreply.github.com> Date: Sun, 29 Mar 2026 10:57:33 -0500 Subject: [PATCH 1/5] Improve export parity and idle media cleanup Match ffmpeg easing and keyframe cadence to the editor so exports stay sharper, and lazily release idle capture resources to reduce stale stream issues without changing active recording finalization. Made-with: Cursor --- src/main/services/render-filter-service.ts | 21 +- src/main/services/render-service.ts | 2 + src/renderer/app.ts | 74 ++++++- src/renderer/features/media-cleanup.ts | 120 +++++++++++ tests/unit/media-cleanup.test.ts | 231 +++++++++++++++++++++ tests/unit/render-filter-service.test.ts | 6 +- tests/unit/render-service.test.ts | 7 +- 7 files changed, 451 insertions(+), 10 deletions(-) create mode 100644 src/renderer/features/media-cleanup.ts create mode 100644 tests/unit/media-cleanup.test.ts diff --git a/src/main/services/render-filter-service.ts b/src/main/services/render-filter-service.ts index ce25ed6..a8e42a2 100644 --- a/src/main/services/render-filter-service.ts +++ b/src/main/services/render-filter-service.ts @@ -4,6 +4,11 @@ export const TRANSITION_DURATION = 0.3; const AUTHORING_CANVAS_W = 1920; const AUTHORING_CANVAS_H = 1080; +function easeExpr(timeVar: string, start: number): string { + const progress = `(${timeVar}-${start.toFixed(3)})/${TRANSITION_DURATION.toFixed(3)}`; + return `if(lt(${progress},0.5),2*${progress}*${progress},1-pow(-2*${progress}+2,2)/2)`; +} + interface KeyframeProp extends Keyframe { backgroundFocusX?: number; backgroundFocusY?: number; @@ -76,7 +81,8 @@ export function buildNumericExpr( const diff = currVal - prevVal; if (Math.abs(diff) > 0.0001) { - expr = `if(gte(${timeVar},${time.toFixed(3)}),${currVal.toFixed(precision)},if(gte(${timeVar},${start.toFixed(3)}),${prevVal.toFixed(precision)}+${diff.toFixed(precision)}*(${timeVar}-${start.toFixed(3)})/${TRANSITION_DURATION.toFixed(3)},${expr}))`; + const eased = easeExpr(timeVar, start); + expr = `if(gte(${timeVar},${time.toFixed(3)}),${currVal.toFixed(precision)},if(gte(${timeVar},${start.toFixed(3)}),${prevVal.toFixed(precision)}+${diff.toFixed(precision)}*${eased},${expr}))`; } else { expr = `if(gte(${timeVar},${time.toFixed(3)}),${currVal.toFixed(precision)},${expr})`; } @@ -136,7 +142,8 @@ export function buildPosExpr(keyframes: Keyframe[], prop: 'pipX' | 'pipY'): stri } else if (prevVal !== currVal && !prevFull && !currFull) { const start = time - TRANSITION_DURATION; const diff = currVal - prevVal; - expr = `if(gte(t,${time.toFixed(3)}),${currVal},if(gte(t,${start.toFixed(3)}),${prevVal}+${diff}*(t-${start.toFixed(3)})/${TRANSITION_DURATION.toFixed(3)},${expr}))`; + const eased = easeExpr('t', start); + expr = `if(gte(t,${time.toFixed(3)}),${currVal},if(gte(t,${start.toFixed(3)}),${prevVal}+${diff}*${eased},${expr}))`; } else { expr = `if(gte(t,${time.toFixed(3)}),${currVal},${expr})`; } @@ -159,10 +166,11 @@ export function buildAlphaExpr(keyframes: Keyframe[]): string { if (prev.pipVisible !== curr.pipVisible) { const start = time - TRANSITION_DURATION; + const eased = easeExpr('T', start); if (curr.pipVisible) { - expr = `if(gte(T,${time.toFixed(3)}),1,if(gte(T,${start.toFixed(3)}),(T-${start.toFixed(3)})/${TRANSITION_DURATION.toFixed(3)},${expr}))`; + expr = `if(gte(T,${time.toFixed(3)}),1,if(gte(T,${start.toFixed(3)}),${eased},${expr}))`; } else { - expr = `if(gte(T,${time.toFixed(3)}),0,if(gte(T,${start.toFixed(3)}),(${time.toFixed(3)}-T)/${TRANSITION_DURATION.toFixed(3)},${expr}))`; + expr = `if(gte(T,${time.toFixed(3)}),0,if(gte(T,${start.toFixed(3)}),1-${eased},${expr}))`; } } else { expr = `if(gte(T,${time.toFixed(3)}),${curr.pipVisible ? '1' : '0'},${expr})`; @@ -191,10 +199,11 @@ export function buildCamFullAlphaExpr(keyframes: Keyframe[]): string { const currFull = isFullVisible(curr); if (prevFull !== currFull) { + const eased = easeExpr('T', start); if (currFull) { - expr = `if(gte(T,${time.toFixed(3)}),1,if(gte(T,${start.toFixed(3)}),(T-${start.toFixed(3)})/${TRANSITION_DURATION.toFixed(3)},${expr}))`; + expr = `if(gte(T,${time.toFixed(3)}),1,if(gte(T,${start.toFixed(3)}),${eased},${expr}))`; } else { - expr = `if(gte(T,${time.toFixed(3)}),0,if(gte(T,${start.toFixed(3)}),(${time.toFixed(3)}-T)/${TRANSITION_DURATION.toFixed(3)},${expr}))`; + expr = `if(gte(T,${time.toFixed(3)}),0,if(gte(T,${start.toFixed(3)}),1-${eased},${expr}))`; } } else { expr = `if(gte(T,${time.toFixed(3)}),${currFull ? '1' : '0'},${expr})`; diff --git a/src/main/services/render-service.ts b/src/main/services/render-service.ts index d68ee7d..a145740 100644 --- a/src/main/services/render-service.ts +++ b/src/main/services/render-service.ts @@ -308,6 +308,8 @@ function buildOutputArgs( config.crf, '-preset', config.preset, + '-g', + String(targetFps * 2), '-pix_fmt', config.pixelFormat, '-c:a', diff --git a/src/renderer/app.ts b/src/renderer/app.ts index d442975..7beba5b 100644 --- a/src/renderer/app.ts +++ b/src/renderer/app.ts @@ -37,6 +37,7 @@ import { shouldRenderPreviewFrame, createCameraRecordingStream } from './features/recording/recorder-utils'; +import { cleanupAllMedia } from './features/media-cleanup'; const projectHomeView = document.getElementById('projectHomeView'); const workspaceHeader = document.getElementById('workspaceHeader'); @@ -136,6 +137,7 @@ import { let saveDebounceTimer = null; let persistQueue = Promise.resolve(); let mediaInitialized = false; + let mediaIdleTimer = null; let scribeWs = null; let scribeWorkletNode = null; let speechSegments = []; @@ -144,6 +146,61 @@ import { let scribeLastFailureReason = null; let scribeManualClose = false; let micSourceNode = null; + const MEDIA_IDLE_TIMEOUT_MS = 30000; + + function clearMediaIdleTimer() { + if (!mediaIdleTimer) return; + clearTimeout(mediaIdleTimer); + mediaIdleTimer = null; + } + + function resetMediaRefsAfterCleanup() { + recording = false; + screenStream = null; + cameraStream = null; + audioStream = null; + recorders = []; + screenRecInterval = null; + timerInterval = null; + audioContext = null; + analyser = null; + meterRAF = null; + drawRAF = null; + lastCompositeDrawAt = 0; + scribeWs = null; + scribeWorkletNode = null; + audioChunkBuffer = []; + audioSendInterval = null; + micSourceNode = null; + mediaInitialized = false; + screenVideo.srcObject = null; + cameraVideo.srcObject = null; + } + + function cleanupRendererMediaResources() { + cleanupAllMedia({ + recording, + screenStream, + cameraStream, + audioStream, + recorders, + screenRecInterval, + audioSendInterval, + timerInterval, + audioContext, + scribeWorkletNode, + scribeWs, + drawRAF, + meterRAF, + cancelEditorDrawLoop, + stopAudioMeter + }); + resetMediaRefsAfterCleanup(); + } + + function hasActiveRecorders() { + return recorders.some((recorder) => recorder?.state && recorder.state !== 'inactive'); + } function handleRenderProgress(update) { if (!editorState || !editorState.rendering) return; @@ -568,6 +625,8 @@ import { processingView.classList.toggle('hidden', !showProcessing); if (showRecording) { + clearMediaIdleTimer(); + if (!mediaInitialized) void ensureMediaInitialized(); updatePreview(); } else if (drawRAF) { cancelAnimationFrame(drawRAF); @@ -575,6 +634,14 @@ import { lastCompositeDrawAt = 0; } + if (!showRecording && mediaInitialized && !recording && !mediaIdleTimer) { + mediaIdleTimer = setTimeout(() => { + mediaIdleTimer = null; + if (hasActiveRecorders()) return; + cleanupRendererMediaResources(); + }, MEDIA_IDLE_TIMEOUT_MS); + } + if (showTimeline && editorState && !hasPendingEditorDraw()) { editorDrawLoop(); } else if (!showTimeline && hasPendingEditorDraw()) { @@ -881,8 +948,6 @@ import { setWorkspaceView('recording'); } - await ensureMediaInitialized(); - if (activeWorkspaceView === 'recording') updatePreview(); await window.electronAPI.projectSetLast(projectPath); updateWorkspaceHeader(); @@ -912,6 +977,7 @@ import { try { await updateScreenStream(); } catch (error) { console.warn('Screen source init failed:', error); } try { await updateCameraStream(); } catch (error) { console.warn('Camera source init failed:', error); } try { await updateAudioStream(); } catch (error) { console.warn('Audio source init failed:', error); } + if (activeWorkspaceView === 'recording') updatePreview(); } async function syncContentProtection() { @@ -4195,6 +4261,10 @@ import { refreshRecentProjects(); window.addEventListener('beforeunload', () => { + clearMediaIdleTimer(); + if (!recording && !hasActiveRecorders()) { + cleanupRendererMediaResources(); + } flushScheduledProjectSave().catch((error) => { console.warn('Failed to flush project save on exit:', error); }); diff --git a/src/renderer/features/media-cleanup.ts b/src/renderer/features/media-cleanup.ts new file mode 100644 index 0000000..7c4b766 --- /dev/null +++ b/src/renderer/features/media-cleanup.ts @@ -0,0 +1,120 @@ +export interface MediaRefs { + recording?: boolean; + screenStream?: MediaStream | null; + cameraStream?: MediaStream | null; + audioStream?: MediaStream | null; + recorders?: MediaRecorder[]; + screenRecInterval?: ReturnType | null; + audioSendInterval?: ReturnType | null; + timerInterval?: ReturnType | null; + audioContext?: AudioContext | null; + scribeWorkletNode?: AudioWorkletNode | null; + scribeWs?: WebSocket | null; + drawRAF?: number | null; + meterRAF?: number | null; + cancelEditorDrawLoop?: (() => void) | null; + stopAudioMeter?: (() => void) | null; +} + +export function stopStream(stream: MediaStream | null | undefined): void { + if (!stream) return; + + try { + stream.getTracks().forEach((track) => track.stop()); + } catch { + // Best-effort cleanup for already-stopped or torn-down streams. + } +} + +export function cleanupAllMedia(refs: MediaRefs | null | undefined): void { + if (!refs) return; + + if (refs.recording) { + refs.recording = false; + } + + if (refs.screenRecInterval) { + clearInterval(refs.screenRecInterval); + refs.screenRecInterval = null; + } + if (refs.audioSendInterval) { + clearInterval(refs.audioSendInterval); + refs.audioSendInterval = null; + } + if (refs.timerInterval) { + clearInterval(refs.timerInterval); + refs.timerInterval = null; + } + + if (refs.recorders && refs.recorders.length > 0) { + refs.recorders.forEach((recorder) => { + try { + if (recorder.state !== 'inactive') recorder.stop(); + } catch { + // Already stopped. + } + }); + refs.recorders = []; + } + + if (refs.scribeWorkletNode) { + try { + refs.scribeWorkletNode.disconnect(); + } catch { + // Already disconnected. + } + refs.scribeWorkletNode = null; + } + + if (refs.scribeWs) { + try { + refs.scribeWs.close(); + } catch { + // Already closed. + } + refs.scribeWs = null; + } + + if (typeof refs.stopAudioMeter === 'function') { + try { + refs.stopAudioMeter(); + } catch { + // Best-effort cleanup. + } + } + + if (refs.audioContext) { + try { + refs.audioContext.close(); + } catch { + // Already closed. + } + refs.audioContext = null; + } + + if (refs.drawRAF) { + cancelAnimationFrame(refs.drawRAF); + refs.drawRAF = null; + } + if (refs.meterRAF) { + cancelAnimationFrame(refs.meterRAF); + refs.meterRAF = null; + } + + if (typeof refs.cancelEditorDrawLoop === 'function') { + try { + refs.cancelEditorDrawLoop(); + } catch { + // Best-effort cleanup. + } + } + + stopStream(refs.screenStream); + refs.screenStream = null; + + stopStream(refs.cameraStream); + refs.cameraStream = null; + + stopStream(refs.audioStream); + refs.audioStream = null; +} diff --git a/tests/unit/media-cleanup.test.ts b/tests/unit/media-cleanup.test.ts new file mode 100644 index 0000000..c64e5b8 --- /dev/null +++ b/tests/unit/media-cleanup.test.ts @@ -0,0 +1,231 @@ +import { beforeEach, describe, expect, test, vi } from 'vitest'; + +import { + cleanupAllMedia, + stopStream, + type MediaRefs, +} from '../../src/renderer/features/media-cleanup'; + +function makeFakeStream(trackCount = 1) { + const tracks = Array.from({ length: trackCount }, () => ({ stop: vi.fn() })); + return { getTracks: () => tracks, _tracks: tracks }; +} + +function makeFakeRecorder(state = 'recording') { + return { state, stop: vi.fn() }; +} + +function makeFakeAudioContext() { + return { close: vi.fn() }; +} + +function makeFakeWorkletNode() { + return { disconnect: vi.fn() }; +} + +function makeFakeWebSocket() { + return { close: vi.fn() }; +} + +type FakeMediaRefs = MediaRefs & { + screenStream: ReturnType | null; + cameraStream: ReturnType | null; + audioStream: ReturnType | null; + recorders: ReturnType[]; + audioContext: ReturnType | null; + scribeWorkletNode: ReturnType | null; + scribeWs: ReturnType | null; + cancelEditorDrawLoop: ReturnType | null; + stopAudioMeter: ReturnType | null; +}; + +function makeFullRefs(): FakeMediaRefs { + return { + recording: true, + screenStream: makeFakeStream(2), + cameraStream: makeFakeStream(1), + audioStream: makeFakeStream(1), + recorders: [makeFakeRecorder('recording'), makeFakeRecorder('recording')], + screenRecInterval: setInterval(() => {}, 100000), + audioSendInterval: setInterval(() => {}, 100000), + timerInterval: setInterval(() => {}, 100000), + audioContext: makeFakeAudioContext(), + scribeWorkletNode: makeFakeWorkletNode(), + scribeWs: makeFakeWebSocket(), + drawRAF: 1, + meterRAF: 2, + cancelEditorDrawLoop: vi.fn(), + stopAudioMeter: vi.fn(), + } as FakeMediaRefs; +} + +beforeEach(() => { + globalThis.cancelAnimationFrame = vi.fn() as unknown as typeof globalThis.cancelAnimationFrame; +}); + +describe('stopStream', () => { + test('no-ops for null', () => { + expect(() => stopStream(null)).not.toThrow(); + }); + + test('no-ops for undefined', () => { + expect(() => stopStream(undefined)).not.toThrow(); + }); + + test('stops all tracks on a stream', () => { + const stream = makeFakeStream(3); + stopStream(stream as unknown as MediaStream); + stream._tracks.forEach((track) => expect(track.stop).toHaveBeenCalledOnce()); + }); + + test('does not throw when track.stop() throws', () => { + const stream = { + getTracks: () => [{ stop: () => { throw new Error('already stopped'); } }], + }; + expect(() => stopStream(stream as unknown as MediaStream)).not.toThrow(); + }); +}); + +describe('cleanupAllMedia', () => { + test('called with null refs does not throw', () => { + expect(() => cleanupAllMedia(null)).not.toThrow(); + }); + + test('called with undefined refs does not throw', () => { + expect(() => cleanupAllMedia(undefined)).not.toThrow(); + }); + + test('called with all-null resource refs does not throw', () => { + const refs = { + recording: false, + screenStream: null, + cameraStream: null, + audioStream: null, + recorders: [] as ReturnType[], + screenRecInterval: null, + audioSendInterval: null, + timerInterval: null, + audioContext: null, + scribeWorkletNode: null, + scribeWs: null, + drawRAF: null, + meterRAF: null, + cancelEditorDrawLoop: null, + stopAudioMeter: null, + } as FakeMediaRefs; + expect(() => cleanupAllMedia(refs)).not.toThrow(); + }); + + test('called with empty object does not throw', () => { + expect(() => cleanupAllMedia({} as MediaRefs)).not.toThrow(); + }); + + test('double call does not throw', () => { + const refs = makeFullRefs(); + cleanupAllMedia(refs); + expect(() => cleanupAllMedia(refs)).not.toThrow(); + }); + + test('stops all media streams', () => { + const refs = makeFullRefs(); + const screenTracks = refs.screenStream!._tracks; + const cameraTracks = refs.cameraStream!._tracks; + const audioTracks = refs.audioStream!._tracks; + + cleanupAllMedia(refs); + + screenTracks.forEach((track) => expect(track.stop).toHaveBeenCalledOnce()); + cameraTracks.forEach((track) => expect(track.stop).toHaveBeenCalledOnce()); + audioTracks.forEach((track) => expect(track.stop).toHaveBeenCalledOnce()); + expect(refs.screenStream).toBeNull(); + expect(refs.cameraStream).toBeNull(); + expect(refs.audioStream).toBeNull(); + }); + + test('stops active media recorders and empties the array', () => { + const refs = makeFullRefs(); + const recorders = refs.recorders; + + cleanupAllMedia(refs); + + recorders.forEach((recorder) => expect(recorder.stop).toHaveBeenCalledOnce()); + expect(refs.recorders).toEqual([]); + }); + + test('skips already inactive recorders', () => { + const refs = makeFullRefs(); + refs.recorders = [makeFakeRecorder('inactive')] as FakeMediaRefs['recorders']; + + cleanupAllMedia(refs); + + expect(refs.recorders).toEqual([]); + }); + + test('clears intervals and nulls refs', () => { + const refs = makeFullRefs(); + + cleanupAllMedia(refs); + + expect(refs.screenRecInterval).toBeNull(); + expect(refs.audioSendInterval).toBeNull(); + expect(refs.timerInterval).toBeNull(); + }); + + test('disconnects scribe worklet node', () => { + const refs = makeFullRefs(); + const node = refs.scribeWorkletNode!; + + cleanupAllMedia(refs); + + expect(node.disconnect).toHaveBeenCalledOnce(); + expect(refs.scribeWorkletNode).toBeNull(); + }); + + test('closes scribe websocket', () => { + const refs = makeFullRefs(); + const socket = refs.scribeWs!; + + cleanupAllMedia(refs); + + expect(socket.close).toHaveBeenCalledOnce(); + expect(refs.scribeWs).toBeNull(); + }); + + test('calls stopAudioMeter and closes the audio context', () => { + const refs = makeFullRefs(); + const audioContext = refs.audioContext!; + + cleanupAllMedia(refs); + + expect(refs.stopAudioMeter).toHaveBeenCalledOnce(); + expect(audioContext.close).toHaveBeenCalledOnce(); + expect(refs.audioContext).toBeNull(); + }); + + test('cancels animation frames', () => { + const refs = makeFullRefs(); + + cleanupAllMedia(refs); + + expect(globalThis.cancelAnimationFrame).toHaveBeenCalledWith(1); + expect(globalThis.cancelAnimationFrame).toHaveBeenCalledWith(2); + expect(refs.drawRAF).toBeNull(); + expect(refs.meterRAF).toBeNull(); + }); + + test('calls cancelEditorDrawLoop', () => { + const refs = makeFullRefs(); + + cleanupAllMedia(refs); + + expect(refs.cancelEditorDrawLoop).toHaveBeenCalledOnce(); + }); + + test('sets recording to false', () => { + const refs = makeFullRefs(); + + cleanupAllMedia(refs); + + expect(refs.recording).toBe(false); + }); +}); diff --git a/tests/unit/render-filter-service.test.ts b/tests/unit/render-filter-service.test.ts index f984b38..721d1ae 100644 --- a/tests/unit/render-filter-service.test.ts +++ b/tests/unit/render-filter-service.test.ts @@ -23,6 +23,7 @@ describe('main/services/render-filter-service', () => { expect(expr).toContain('if(gte(t,1.000)'); expect(expr).toContain('if(gte(t,0.700)'); expect(expr).toContain('200'); + expect(expr).toContain('pow('); }); test('buildAlphaExpr transitions before the keyframe time', () => { @@ -35,6 +36,7 @@ describe('main/services/render-filter-service', () => { // At T=2.0 fully invisible, transition starts at T=1.7 expect(expr).toContain('if(gte(T,2.000),0'); expect(expr).toContain('if(gte(T,1.700)'); + expect(expr).toContain('pow('); }); test('buildCamFullAlphaExpr transitions before the keyframe time', () => { @@ -47,6 +49,7 @@ describe('main/services/render-filter-service', () => { // At T=1.0 fully visible fullscreen, transition starts at T=0.7 expect(expr).toContain('if(gte(T,1.000),1'); expect(expr).toContain('if(gte(T,0.700)'); + expect(expr).toContain('pow('); }); test('buildPosExpr snaps position at transition start for fullscreen→pip', () => { @@ -85,7 +88,8 @@ describe('main/services/render-filter-service', () => { 'it' ); expect(expr).toContain('if(gte(it,2.000),2.000'); - expect(expr).toContain('if(gte(it,1.700),1.000+1.000*(it-1.700)/0.300'); + expect(expr).toContain('if(gte(it,1.700),1.000+1.000*'); + expect(expr).toContain('pow('); }); test('panToFocusCoord converts section pan into focus position', () => { diff --git a/tests/unit/render-service.test.ts b/tests/unit/render-service.test.ts index 97e8bd2..4c456d6 100644 --- a/tests/unit/render-service.test.ts +++ b/tests/unit/render-service.test.ts @@ -143,6 +143,8 @@ describe('main/services/render-service', () => { '8', '-preset', 'slow', + '-g', + '60', '-pix_fmt', 'yuv420p', '-c:a', @@ -192,6 +194,8 @@ describe('main/services/render-service', () => { '24', '-preset', 'veryfast', + '-g', + '60', '-pix_fmt', 'yuv420p', '-b:a', @@ -493,7 +497,8 @@ describe('main/services/render-service', () => { const argString = execCalls[0].args.join(' '); expect(argString).toContain('if(gte(it,1.000),2.000'); - expect(argString).toContain('if(gte(it,0.700),1.000+1.000*(it-0.700)/0.300'); + expect(argString).toContain('if(gte(it,0.700),1.000+1.000*if(lt('); + expect(argString).toContain('pow('); }); test('renderComposite reuses ffmpeg inputs for repeated sections from the same take', async () => { From 0f8c00eca90e9c91059e63c40b675b551e2bf8ba Mon Sep 17 00:00:00 2001 From: Tadas Petra <60107328+tadaspetra@users.noreply.github.com> Date: Sun, 29 Mar 2026 20:11:55 -0500 Subject: [PATCH 2/5] prettier --- .agents/skills/agents/SKILL.md | 64 +- .../agents/references/agent-configuration.md | 223 +- .../skills/agents/references/client-tools.md | 58 +- .../skills/agents/references/installation.md | 15 +- .../agents/references/outbound-calls.md | 66 +- .../agents/references/widget-embedding.md | 94 +- .agents/skills/music/SKILL.md | 24 +- .../skills/music/references/api_reference.md | 56 +- .../skills/music/references/installation.md | 4 +- .agents/skills/sound-effects/SKILL.md | 39 +- .../sound-effects/references/installation.md | 4 +- .agents/skills/speech-to-text/SKILL.md | 52 +- .../speech-to-text/references/installation.md | 15 +- .../references/realtime-client-side.md | 36 +- .../references/realtime-commit-strategies.md | 49 +- .../references/realtime-events.md | 100 +- .../references/realtime-server-side.md | 30 +- .../references/transcription-options.md | 109 +- .agents/skills/text-to-speech/SKILL.md | 64 +- .../text-to-speech/references/installation.md | 13 +- .../text-to-speech/references/streaming.md | 69 +- .../references/voice-settings.md | 28 +- docs/production/feature-inventory.md | 36 + docs/production/runbook.md | 5 + docs/production/target-architecture.md | 1 + src/index.html | 667 +- src/main.ts | 4 +- src/main/app/create-window.ts | 27 +- src/main/infra/file-system.ts | 13 +- src/main/ipc/register-handlers.ts | 167 +- src/main/services/ffmpeg-runner.ts | 14 +- src/main/services/fps-service.ts | 17 +- src/main/services/project-service.ts | 100 +- src/main/services/proxy-service.ts | 61 +- src/main/services/render-filter-service.ts | 145 +- src/main/services/render-service.ts | 138 +- src/main/services/sections-service.ts | 13 +- src/preload.ts | 8 +- src/renderer/app.ts | 8496 +++++++++-------- .../features/recording/recorder-utils.ts | 29 +- src/renderer/features/timeline/camera-sync.ts | 16 +- .../features/timeline/keyframe-ops.ts | 20 +- .../features/timeline/section-utils.ts | 57 +- .../features/transcript/scribe-status.ts | 10 +- .../features/transcript/transcript-utils.ts | 11 +- src/renderer/styles/main.css | 1055 +- src/shared/domain/project.ts | 120 +- src/shared/electron-api.ts | 11 +- tests/integration/project-service.test.ts | 26 +- tests/unit/create-window.test.ts | 5 +- tests/unit/ffmpeg-runner.test.ts | 5 +- tests/unit/fps-service.test.ts | 6 +- tests/unit/keyframe-ops.test.ts | 90 +- tests/unit/media-cleanup.test.ts | 14 +- tests/unit/preload.test.ts | 10 +- tests/unit/project-domain.test.ts | 36 +- tests/unit/proxy-service.test.ts | 47 +- tests/unit/register-handlers.test.ts | 39 +- tests/unit/render-filter-service.test.ts | 64 +- tests/unit/render-service.test.ts | 195 +- tests/unit/scribe-status.test.ts | 24 +- tsconfig.main.tsbuildinfo | 1 + tsconfig.preload.tsbuildinfo | 1 + tsconfig.renderer.tsbuildinfo | 1 + tsconfig.shared.tsbuildinfo | 1 + 65 files changed, 7345 insertions(+), 5673 deletions(-) create mode 100644 tsconfig.main.tsbuildinfo create mode 100644 tsconfig.preload.tsbuildinfo create mode 100644 tsconfig.renderer.tsbuildinfo create mode 100644 tsconfig.shared.tsbuildinfo diff --git a/.agents/skills/agents/SKILL.md b/.agents/skills/agents/SKILL.md index 352e7b0..61c2b4f 100644 --- a/.agents/skills/agents/SKILL.md +++ b/.agents/skills/agents/SKILL.md @@ -3,7 +3,11 @@ name: agents description: Build voice AI agents with ElevenLabs. Use when creating voice assistants, customer service bots, interactive voice characters, or any real-time voice conversation experience. license: MIT compatibility: Requires internet access and an ElevenLabs API key (ELEVENLABS_API_KEY). -metadata: {"openclaw": {"requires": {"env": ["ELEVENLABS_API_KEY"]}, "primaryEnv": "ELEVENLABS_API_KEY"}} +metadata: + { + 'openclaw': + { 'requires': { 'env': ['ELEVENLABS_API_KEY'] }, 'primaryEnv': 'ELEVENLABS_API_KEY' } + } --- # ElevenLabs Agents Platform @@ -58,22 +62,22 @@ agent = client.conversational_ai.agents.create( ### JavaScript ```javascript -import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js"; +import { ElevenLabsClient } from '@elevenlabs/elevenlabs-js'; const client = new ElevenLabsClient(); const agent = await client.conversationalAi.agents.create({ - name: "My Assistant", + name: 'My Assistant', conversationConfig: { agent: { - firstMessage: "Hello! How can I help?", - language: "en", + firstMessage: 'Hello! How can I help?', + language: 'en', prompt: { - prompt: "You are a helpful assistant.", - llm: "gemini-2.0-flash", + prompt: 'You are a helpful assistant.', + llm: 'gemini-2.0-flash', temperature: 0.7 } }, - tts: { voiceId: "JBFqnCBsd6RMkjVDRZzb" } + tts: { voiceId: 'JBFqnCBsd6RMkjVDRZzb' } } }); ``` @@ -89,25 +93,28 @@ curl -X POST "https://api.elevenlabs.io/v1/convai/agents/create" \ ## Starting Conversations **Server-side (Python):** Get signed URL for client connection: + ```python signed_url = client.conversational_ai.conversations.get_signed_url(agent_id="your-agent-id") ``` **Client-side (JavaScript):** + ```javascript -import { Conversation } from "@elevenlabs/client"; +import { Conversation } from '@elevenlabs/client'; const conversation = await Conversation.startSession({ - agentId: "your-agent-id", - onMessage: (msg) => console.log("Agent:", msg.message), - onUserTranscript: (t) => console.log("User:", t.message), + agentId: 'your-agent-id', + onMessage: (msg) => console.log('Agent:', msg.message), + onUserTranscript: (t) => console.log('User:', t.message), onError: (e) => console.error(e) }); ``` **React Hook:** + ```typescript -import { useConversation } from "@elevenlabs/react"; +import { useConversation } from '@elevenlabs/react'; const conversation = useConversation({ onMessage: (msg) => console.log(msg) }); // Get signed URL from backend, then: @@ -116,13 +123,13 @@ await conversation.startSession({ signedUrl: token }); ## Configuration -| Provider | Models | -|----------|--------| -| OpenAI | `gpt-5`, `gpt-5-mini`, `gpt-5-nano`, `gpt-4.1`, `gpt-4.1-mini`, `gpt-4.1-nano`, `gpt-4o`, `gpt-4o-mini`, `gpt-4-turbo` | -| Anthropic | `claude-sonnet-4-5`, `claude-sonnet-4`, `claude-haiku-4-5`, `claude-3-7-sonnet`, `claude-3-5-sonnet`, `claude-3-haiku` | -| Google | `gemini-3-pro-preview`, `gemini-3-flash-preview`, `gemini-2.5-flash`, `gemini-2.5-flash-lite`, `gemini-2.0-flash`, `gemini-2.0-flash-lite` | -| ElevenLabs | `glm-45-air-fp8`, `qwen3-30b-a3b`, `gpt-oss-120b` | -| Custom | `custom-llm` (bring your own endpoint) | +| Provider | Models | +| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------ | +| OpenAI | `gpt-5`, `gpt-5-mini`, `gpt-5-nano`, `gpt-4.1`, `gpt-4.1-mini`, `gpt-4.1-nano`, `gpt-4o`, `gpt-4o-mini`, `gpt-4-turbo` | +| Anthropic | `claude-sonnet-4-5`, `claude-sonnet-4`, `claude-haiku-4-5`, `claude-3-7-sonnet`, `claude-3-5-sonnet`, `claude-3-haiku` | +| Google | `gemini-3-pro-preview`, `gemini-3-flash-preview`, `gemini-2.5-flash`, `gemini-2.5-flash-lite`, `gemini-2.0-flash`, `gemini-2.0-flash-lite` | +| ElevenLabs | `glm-45-air-fp8`, `qwen3-30b-a3b`, `gpt-oss-120b` | +| Custom | `custom-llm` (bring your own endpoint) | **Popular voices:** `JBFqnCBsd6RMkjVDRZzb` (George), `EXAVITQu4vr4xnSDxMaL` (Sarah), `onwK4e9ZLuTAKqWW03F9` (Daniel), `XB0fDUnXU5powFXDhCwa` (Charlotte) @@ -155,12 +162,13 @@ Extend agents with webhook, client, or built-in system tools. Tools are defined ``` **Client tools** run in browser: + ```javascript clientTools: { show_product: async ({ productId }) => { - document.getElementById("product").src = `/products/${productId}`; + document.getElementById('product').src = `/products/${productId}`; return { success: true }; - } + }; } ``` @@ -170,7 +178,11 @@ See [Client Tools Reference](references/client-tools.md) for complete documentat ```html - + ``` Customize with attributes: `avatar-image-url`, `action-text`, `start-call-text`, `end-call-text`. @@ -196,9 +208,9 @@ print(f"Call initiated: {response.conversation_id}") ```javascript const response = await client.conversationalAi.twilio.outboundCall({ - agentId: "your-agent-id", - agentPhoneNumberId: "your-phone-number-id", - toNumber: "+1234567890", + agentId: 'your-agent-id', + agentPhoneNumberId: 'your-phone-number-id', + toNumber: '+1234567890' }); ``` diff --git a/.agents/skills/agents/references/agent-configuration.md b/.agents/skills/agents/references/agent-configuration.md index 03d6b98..6405e03 100644 --- a/.agents/skills/agents/references/agent-configuration.md +++ b/.agents/skills/agents/references/agent-configuration.md @@ -50,14 +50,14 @@ conversation_config={ } ``` -| Field | Type | Default | Description | -|-------|------|---------|-------------| -| `first_message` | string | `""` | What the agent says when conversation starts | -| `language` | string | `"en"` | ISO 639-1 language code (en, es, fr, etc.) | -| `disable_first_message_interruptions` | bool | `false` | Prevent user from interrupting the first message | -| `hinglish_mode` | bool | `false` | When enabled and language is Hindi, agent responds in Hinglish | -| `dynamic_variables` | object | - | Config with `dynamic_variable_placeholders` containing key-value pairs | -| `prompt` | object | - | LLM configuration (see prompt section below) | +| Field | Type | Default | Description | +| ------------------------------------- | ------ | ------- | ---------------------------------------------------------------------- | +| `first_message` | string | `""` | What the agent says when conversation starts | +| `language` | string | `"en"` | ISO 639-1 language code (en, es, fr, etc.) | +| `disable_first_message_interruptions` | bool | `false` | Prevent user from interrupting the first message | +| `hinglish_mode` | bool | `false` | When enabled and language is Hindi, agent responds in Hinglish | +| `dynamic_variables` | object | - | Config with `dynamic_variable_placeholders` containing key-value pairs | +| `prompt` | object | - | LLM configuration (see prompt section below) | ### tts (Text-to-Speech) @@ -75,28 +75,28 @@ conversation_config={ } ``` -| Field | Type | Default | Description | -|-------|------|---------|-------------| -| `voice_id` | string | `"cjVigY5qzO86Huf0OWal"` | Voice to use | -| `model_id` | string | - | TTS model (see below) | -| `stability` | float | `0.5` | 0-1, lower = more expressive | -| `similarity_boost` | float | `0.8` | 0-1, higher = closer to original voice | -| `speed` | float | `1.0` | 0.7-1.2, speech speed multiplier | -| `optimize_streaming_latency` | int | - | 0-4, higher = faster but lower quality | -| `expressive_mode` | bool | `true` | Enable expressive voice generation | -| `agent_output_audio_format` | string | - | Output audio codec format | -| `pronunciation_dictionary_locators` | array | - | Pronunciation overrides | +| Field | Type | Default | Description | +| ----------------------------------- | ------ | ------------------------ | -------------------------------------- | +| `voice_id` | string | `"cjVigY5qzO86Huf0OWal"` | Voice to use | +| `model_id` | string | - | TTS model (see below) | +| `stability` | float | `0.5` | 0-1, lower = more expressive | +| `similarity_boost` | float | `0.8` | 0-1, higher = closer to original voice | +| `speed` | float | `1.0` | 0.7-1.2, speech speed multiplier | +| `optimize_streaming_latency` | int | - | 0-4, higher = faster but lower quality | +| `expressive_mode` | bool | `true` | Enable expressive voice generation | +| `agent_output_audio_format` | string | - | Output audio codec format | +| `pronunciation_dictionary_locators` | array | - | Pronunciation overrides | **Available TTS models for agents:** -| Model ID | Languages | Latency | -|----------|-----------|---------| -| `eleven_flash_v2_5` | 32 | ~75ms (recommended) | -| `eleven_flash_v2` | English | ~75ms | -| `eleven_turbo_v2_5` | 32 | ~250-300ms | -| `eleven_turbo_v2` | English | ~250-300ms | -| `eleven_multilingual_v2` | 29 | Standard | -| `eleven_v3_conversational` | 70+ | Standard | +| Model ID | Languages | Latency | +| -------------------------- | --------- | ------------------- | +| `eleven_flash_v2_5` | 32 | ~75ms (recommended) | +| `eleven_flash_v2` | English | ~75ms | +| `eleven_turbo_v2_5` | 32 | ~250-300ms | +| `eleven_turbo_v2` | English | ~250-300ms | +| `eleven_multilingual_v2` | 29 | Standard | +| `eleven_v3_conversational` | 70+ | Standard | ### asr (Automatic Speech Recognition) @@ -110,12 +110,12 @@ conversation_config={ } ``` -| Field | Type | Default | Description | -|-------|------|---------|-------------| -| `quality` | string | `"high"` | Transcription quality level | -| `provider` | string | `"elevenlabs"` | ASR provider (`elevenlabs` or `scribe_realtime`) | -| `keywords` | array | - | Words to boost recognition accuracy | -| `user_input_audio_format` | string | - | Input audio format (e.g., `pcm_16000`, `ulaw_8000`) | +| Field | Type | Default | Description | +| ------------------------- | ------ | -------------- | --------------------------------------------------- | +| `quality` | string | `"high"` | Transcription quality level | +| `provider` | string | `"elevenlabs"` | ASR provider (`elevenlabs` or `scribe_realtime`) | +| `keywords` | array | - | Words to boost recognition accuracy | +| `user_input_audio_format` | string | - | Input audio format (e.g., `pcm_16000`, `ulaw_8000`) | ### turn (Turn-Taking) @@ -129,23 +129,23 @@ conversation_config={ } ``` -| Field | Type | Default | Description | -|-------|------|---------|-------------| -| `turn_timeout` | number | `7` | Seconds to wait before re-engaging the user | -| `turn_eagerness` | string | `"normal"` | How quickly agent responds: `patient`, `normal`, or `eager` | -| `silence_end_call_timeout` | number | `-1` | Seconds of silence before ending call (-1 = disabled) | -| `initial_wait_time` | number | - | Seconds to wait for user to start speaking | -| `spelling_patience` | string | `"auto"` | Entity detection patience: `auto` or `off` | -| `speculative_turn` | bool | `false` | Enable speculative turn detection | -| `soft_timeout_config` | object | - | Configures a message if user is silent (see below) | +| Field | Type | Default | Description | +| -------------------------- | ------ | ---------- | ----------------------------------------------------------- | +| `turn_timeout` | number | `7` | Seconds to wait before re-engaging the user | +| `turn_eagerness` | string | `"normal"` | How quickly agent responds: `patient`, `normal`, or `eager` | +| `silence_end_call_timeout` | number | `-1` | Seconds of silence before ending call (-1 = disabled) | +| `initial_wait_time` | number | - | Seconds to wait for user to start speaking | +| `spelling_patience` | string | `"auto"` | Entity detection patience: `auto` or `off` | +| `speculative_turn` | bool | `false` | Enable speculative turn detection | +| `soft_timeout_config` | object | - | Configures a message if user is silent (see below) | **soft_timeout_config:** -| Field | Type | Default | Description | -|-------|------|---------|-------------| -| `timeout_seconds` | number | `-1` | Seconds before soft timeout (-1 = disabled) | -| `message` | string | `"Hhmmmm...yeah."` | What agent says on timeout | -| `use_llm_generated_message` | bool | `false` | Let LLM generate the timeout message | +| Field | Type | Default | Description | +| --------------------------- | ------ | ------------------ | ------------------------------------------- | +| `timeout_seconds` | number | `-1` | Seconds before soft timeout (-1 = disabled) | +| `message` | string | `"Hhmmmm...yeah."` | What agent says on timeout | +| `use_llm_generated_message` | bool | `false` | Let LLM generate the timeout message | ## prompt (nested in conversation_config.agent) @@ -167,35 +167,35 @@ conversation_config={ } ``` -| Field | Type | Default | Description | -|-------|------|---------|-------------| -| `prompt` | string | `""` | System prompt defining agent behavior | -| `llm` | string | - | Model ID (see LLM providers below) | -| `temperature` | float | `0` | 0-1, higher = more creative | -| `max_tokens` | int | `-1` | Max tokens for LLM response (-1 = unlimited) | -| `reasoning_effort` | string | - | Reasoning depth: `none`, `minimal`, `low`, `medium`, `high` (model-dependent) | -| `thinking_budget` | int | - | Max thinking tokens for reasoning models | -| `tools` | array | - | Webhook and client tool definitions | -| `built_in_tools` | object | - | System tools (end_call, transfer, etc.) | -| `tool_ids` | array | - | References to pre-configured tools | -| `knowledge_base` | array | - | Documents for RAG | -| `custom_llm` | object | - | Custom LLM endpoint config | -| `timezone` | string | - | IANA timezone (e.g., `America/New_York`) | -| `backup_llm_config` | object | - | Fallback LLM configuration | -| `cascade_timeout_seconds` | number | `8` | Seconds before cascading to backup LLM (2-15) | -| `mcp_server_ids` | array | - | MCP server IDs to connect | -| `native_mcp_server_ids` | array | - | Native MCP server IDs | -| `ignore_default_personality` | bool | - | Skip default personality instructions | +| Field | Type | Default | Description | +| ---------------------------- | ------ | ------- | ----------------------------------------------------------------------------- | +| `prompt` | string | `""` | System prompt defining agent behavior | +| `llm` | string | - | Model ID (see LLM providers below) | +| `temperature` | float | `0` | 0-1, higher = more creative | +| `max_tokens` | int | `-1` | Max tokens for LLM response (-1 = unlimited) | +| `reasoning_effort` | string | - | Reasoning depth: `none`, `minimal`, `low`, `medium`, `high` (model-dependent) | +| `thinking_budget` | int | - | Max thinking tokens for reasoning models | +| `tools` | array | - | Webhook and client tool definitions | +| `built_in_tools` | object | - | System tools (end_call, transfer, etc.) | +| `tool_ids` | array | - | References to pre-configured tools | +| `knowledge_base` | array | - | Documents for RAG | +| `custom_llm` | object | - | Custom LLM endpoint config | +| `timezone` | string | - | IANA timezone (e.g., `America/New_York`) | +| `backup_llm_config` | object | - | Fallback LLM configuration | +| `cascade_timeout_seconds` | number | `8` | Seconds before cascading to backup LLM (2-15) | +| `mcp_server_ids` | array | - | MCP server IDs to connect | +| `native_mcp_server_ids` | array | - | Native MCP server IDs | +| `ignore_default_personality` | bool | - | Skip default personality instructions | ### LLM Providers -| Provider | Model IDs | -|----------|-----------| -| OpenAI | `gpt-5`, `gpt-5-mini`, `gpt-5-nano`, `gpt-4.1`, `gpt-4.1-mini`, `gpt-4.1-nano`, `gpt-4o`, `gpt-4o-mini`, `gpt-4-turbo` | -| Anthropic | `claude-sonnet-4-5`, `claude-sonnet-4`, `claude-haiku-4-5`, `claude-3-7-sonnet`, `claude-3-5-sonnet`, `claude-3-haiku` | -| Google | `gemini-3-pro-preview`, `gemini-3-flash-preview`, `gemini-2.5-flash`, `gemini-2.5-flash-lite`, `gemini-2.0-flash`, `gemini-2.0-flash-lite` | -| ElevenLabs | `glm-45-air-fp8`, `qwen3-30b-a3b`, `gpt-oss-120b` (hosted, ultra-low latency) | -| Custom | `custom-llm` (requires custom_llm config) | +| Provider | Model IDs | +| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------ | +| OpenAI | `gpt-5`, `gpt-5-mini`, `gpt-5-nano`, `gpt-4.1`, `gpt-4.1-mini`, `gpt-4.1-nano`, `gpt-4o`, `gpt-4o-mini`, `gpt-4-turbo` | +| Anthropic | `claude-sonnet-4-5`, `claude-sonnet-4`, `claude-haiku-4-5`, `claude-3-7-sonnet`, `claude-3-5-sonnet`, `claude-3-haiku` | +| Google | `gemini-3-pro-preview`, `gemini-3-flash-preview`, `gemini-2.5-flash`, `gemini-2.5-flash-lite`, `gemini-2.0-flash`, `gemini-2.0-flash-lite` | +| ElevenLabs | `glm-45-air-fp8`, `qwen3-30b-a3b`, `gpt-oss-120b` (hosted, ultra-low latency) | +| Custom | `custom-llm` (requires custom_llm config) | ### Custom LLM @@ -237,35 +237,35 @@ platform_settings={ ### auth -| Field | Type | Description | -|-------|------|-------------| -| `enable_auth` | bool | Require signed URLs/tokens for connections | -| `allowlist` | array | Allowed origins for CORS | -| `shareable_token` | string | Public conversation token | +| Field | Type | Description | +| ----------------- | ------ | ------------------------------------------ | +| `enable_auth` | bool | Require signed URLs/tokens for connections | +| `allowlist` | array | Allowed origins for CORS | +| `shareable_token` | string | Public conversation token | ### call_limits -| Field | Type | Description | -|-------|------|-------------| -| `agent_concurrency_limit` | int | Max simultaneous conversations (default: -1, unlimited) | -| `daily_limit` | int | Max conversations per day (default: 100000) | -| `bursting_enabled` | bool | Allow exceeding limits at 2x cost (default: true) | +| Field | Type | Description | +| ------------------------- | ---- | ------------------------------------------------------- | +| `agent_concurrency_limit` | int | Max simultaneous conversations (default: -1, unlimited) | +| `daily_limit` | int | Max conversations per day (default: 100000) | +| `bursting_enabled` | bool | Allow exceeding limits at 2x cost (default: true) | ### conversation (inside conversation_config) -| Field | Type | Default | Description | -|-------|------|---------|-------------| -| `max_duration_seconds` | int | `600` | Max conversation duration | -| `text_only` | bool | `false` | Text-only mode (avoids audio pricing) | -| `monitoring_enabled` | bool | `false` | Enable real-time WebSocket monitoring | +| Field | Type | Default | Description | +| ---------------------- | ---- | ------- | ------------------------------------- | +| `max_duration_seconds` | int | `600` | Max conversation duration | +| `text_only` | bool | `false` | Text-only mode (avoids audio pricing) | +| `monitoring_enabled` | bool | `false` | Enable real-time WebSocket monitoring | ## Additional Top-Level Fields -| Field | Type | Description | -|-------|------|-------------| -| `tags` | array | Classification labels for filtering (e.g., `["production"]`, `["test"]`) | -| `coaching_settings` | object | Configuration for agent coaching and evaluation | -| `workflow` | object | Conversation flow definition and tool interaction sequences | +| Field | Type | Description | +| ------------------- | ------ | ------------------------------------------------------------------------ | +| `tags` | array | Classification labels for filtering (e.g., `["production"]`, `["test"]`) | +| `coaching_settings` | object | Configuration for agent coaching and evaluation | +| `workflow` | object | Conversation flow definition and tool interaction sequences | ## Knowledge Base / RAG @@ -356,7 +356,7 @@ agent = client.conversational_ai.agents.get(agent_id="your-agent-id") ``` ```javascript -const agent = await client.conversationalAi.agents.get("your-agent-id"); +const agent = await client.conversationalAi.agents.get('your-agent-id'); ``` ```bash @@ -368,6 +368,7 @@ curl -X GET "https://api.elevenlabs.io/v1/convai/agents/your-agent-id" -H "xi-ap Only include fields you want to change. All other settings remain unchanged. **Python:** + ```python # Update name client.conversational_ai.agents.update(agent_id="id", name="New Name") @@ -394,17 +395,19 @@ client.conversational_ai.agents.update(agent_id="id", platform_settings={ ``` **JavaScript:** + ```javascript -await client.conversationalAi.agents.update("id", { name: "New Name" }); -await client.conversationalAi.agents.update("id", { - conversationConfig: { tts: { voiceId: "EXAVITQu4vr4xnSDxMaL" } } +await client.conversationalAi.agents.update('id', { name: 'New Name' }); +await client.conversationalAi.agents.update('id', { + conversationConfig: { tts: { voiceId: 'EXAVITQu4vr4xnSDxMaL' } } }); -await client.conversationalAi.agents.update("id", { - conversationConfig: { agent: { prompt: { prompt: "New instructions.", llm: "claude-sonnet-4" } } } +await client.conversationalAi.agents.update('id', { + conversationConfig: { agent: { prompt: { prompt: 'New instructions.', llm: 'claude-sonnet-4' } } } }); ``` **cURL:** + ```bash curl -X PATCH "https://api.elevenlabs.io/v1/convai/agents/your-agent-id" \ -H "xi-api-key: $ELEVENLABS_API_KEY" -H "Content-Type: application/json" \ @@ -413,17 +416,17 @@ curl -X PATCH "https://api.elevenlabs.io/v1/convai/agents/your-agent-id" \ #### Updatable Fields -| Section | Fields | -|---------|--------| -| Root | `name`, `tags` | -| `conversation_config.agent` | `first_message`, `language`, `disable_first_message_interruptions`, `dynamic_variables` | +| Section | Fields | +| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | +| Root | `name`, `tags` | +| `conversation_config.agent` | `first_message`, `language`, `disable_first_message_interruptions`, `dynamic_variables` | | `conversation_config.agent.prompt` | `prompt`, `llm`, `temperature`, `max_tokens`, `reasoning_effort`, `tools`, `built_in_tools`, `knowledge_base`, `custom_llm`, `timezone` | -| `conversation_config.tts` | `voice_id`, `model_id`, `stability`, `similarity_boost`, `speed`, `optimize_streaming_latency`, `expressive_mode` | -| `conversation_config.asr` | `quality`, `provider`, `keywords`, `user_input_audio_format` | -| `conversation_config.turn` | `turn_timeout`, `turn_eagerness`, `silence_end_call_timeout`, `soft_timeout_config` | -| `conversation_config.conversation` | `max_duration_seconds`, `text_only`, `monitoring_enabled` | -| `platform_settings.auth` | `enable_auth`, `allowlist` | -| `platform_settings.call_limits` | `agent_concurrency_limit`, `daily_limit`, `bursting_enabled` | +| `conversation_config.tts` | `voice_id`, `model_id`, `stability`, `similarity_boost`, `speed`, `optimize_streaming_latency`, `expressive_mode` | +| `conversation_config.asr` | `quality`, `provider`, `keywords`, `user_input_audio_format` | +| `conversation_config.turn` | `turn_timeout`, `turn_eagerness`, `silence_end_call_timeout`, `soft_timeout_config` | +| `conversation_config.conversation` | `max_duration_seconds`, `text_only`, `monitoring_enabled` | +| `platform_settings.auth` | `enable_auth`, `allowlist` | +| `platform_settings.call_limits` | `agent_concurrency_limit`, `daily_limit`, `bursting_enabled` | ### SDK: Delete Agent @@ -432,7 +435,7 @@ client.conversational_ai.agents.delete(agent_id="your-agent-id") ``` ```javascript -await client.conversationalAi.agents.delete("your-agent-id"); +await client.conversationalAi.agents.delete('your-agent-id'); ``` ```bash diff --git a/.agents/skills/agents/references/client-tools.md b/.agents/skills/agents/references/client-tools.md index 35b19b5..9b85d97 100644 --- a/.agents/skills/agents/references/client-tools.md +++ b/.agents/skills/agents/references/client-tools.md @@ -4,11 +4,11 @@ Extend your agent with custom capabilities. Tools let the agent take actions bey ## Tool Types -| Type | Execution | Use Case | -|------|-----------|----------| -| **Webhook** | Server-side via HTTP | Database queries, API calls, secure operations | -| **Client** | Browser-side JavaScript | UI updates, local storage, navigation | -| **System** | Built-in ElevenLabs | End call, transfer, standard actions | +| Type | Execution | Use Case | +| ----------- | ----------------------- | ---------------------------------------------- | +| **Webhook** | Server-side via HTTP | Database queries, API calls, secure operations | +| **Client** | Browser-side JavaScript | UI updates, local storage, navigation | +| **System** | Built-in ElevenLabs | End call, transfer, standard actions | ## Where Tools Live @@ -145,29 +145,29 @@ Or for structured data: ### Webhook Tool Options -| Field | Type | Default | Description | -|-------|------|---------|-------------| -| `response_timeout_secs` | int | `20` | Timeout in seconds (5-120) | -| `disable_interruptions` | bool | `false` | Prevent user interruptions during tool execution | -| `execution_mode` | string | `"immediate"` | `immediate`, `post_tool_speech`, or `async` | -| `tool_call_sound` | string | - | Sound during execution: `typing`, `elevator1`-`elevator4` | -| `force_pre_tool_speech` | bool | `false` | Force agent to speak before executing tool | -| `tool_error_handling_mode` | string | `"auto"` | `auto`, `summarized`, `passthrough`, or `hide` | +| Field | Type | Default | Description | +| -------------------------- | ------ | ------------- | --------------------------------------------------------- | +| `response_timeout_secs` | int | `20` | Timeout in seconds (5-120) | +| `disable_interruptions` | bool | `false` | Prevent user interruptions during tool execution | +| `execution_mode` | string | `"immediate"` | `immediate`, `post_tool_speech`, or `async` | +| `tool_call_sound` | string | - | Sound during execution: `typing`, `elevator1`-`elevator4` | +| `force_pre_tool_speech` | bool | `false` | Force agent to speak before executing tool | +| `tool_error_handling_mode` | string | `"auto"` | `auto`, `summarized`, `passthrough`, or `hide` | **Note:** The default `api_schema.method` is `GET`. Always set `"method": "POST"` explicitly for webhook tools that send request bodies. ### Server Implementation (Node.js) ```javascript -app.post("/webhook/get_weather", async (req, res) => { +app.post('/webhook/get_weather', async (req, res) => { const { parameters, conversation_id } = req.body; - const { city, units = "fahrenheit" } = parameters; + const { city, units = 'fahrenheit' } = parameters; // Fetch weather from your data source const weather = await weatherService.get(city, units); res.json({ - result: `It's ${weather.temp}°${units === "celsius" ? "C" : "F"} and ${weather.condition} in ${city}.`, + result: `It's ${weather.temp}°${units === 'celsius' ? 'C' : 'F'} and ${weather.condition} in ${city}.` }); }); ``` @@ -198,17 +198,17 @@ Execute JavaScript in the user's browser. Useful for UI updates, navigation, or Client tools are registered when starting a conversation: ```javascript -import { Conversation } from "@elevenlabs/client"; +import { Conversation } from '@elevenlabs/client'; const conversation = await Conversation.startSession({ - agentId: "your-agent-id", + agentId: 'your-agent-id', clientTools: { show_product: async ({ productId }) => { // Update UI to show product - const modal = document.getElementById("product-modal"); + const modal = document.getElementById('product-modal'); modal.innerHTML = await fetchProductCard(productId); modal.showModal(); - return { success: true, message: "Showing product" }; + return { success: true, message: 'Showing product' }; }, navigate_to: async ({ page }) => { @@ -221,8 +221,8 @@ const conversation = await Conversation.startSession({ // Store in localStorage localStorage.setItem(key, value); return { saved: true }; - }, - }, + } + } }); ``` @@ -282,8 +282,8 @@ When users want to go somewhere, use navigate_to.""", ### Client Tool Options -| Field | Type | Default | Description | -|-------|------|---------|-------------| +| Field | Type | Default | Description | +| ------------------ | ---- | ------- | ------------------------------------------ | | `expects_response` | bool | `false` | Whether the tool returns data to the agent | ### Client Tool Return Values @@ -293,11 +293,11 @@ Return data that the agent can use in conversation: ```javascript clientTools: { check_cart: async () => { - const cart = JSON.parse(localStorage.getItem("cart") || "[]"); + const cart = JSON.parse(localStorage.getItem('cart') || '[]'); return { itemCount: cart.length, total: cart.reduce((sum, item) => sum + item.price, 0), - items: cart.map((item) => item.name), + items: cart.map((item) => item.name) }; }; } @@ -404,7 +404,7 @@ Return helpful error messages: ```javascript // Server webhook -app.post("/webhook/lookup_order", async (req, res) => { +app.post('/webhook/lookup_order', async (req, res) => { const { order_id } = req.body.parameters; const order = await db.orders.find(order_id); @@ -413,8 +413,8 @@ app.post("/webhook/lookup_order", async (req, res) => { return res.json({ result: { error: true, - message: `Order ${order_id} not found. Please verify the order ID.`, - }, + message: `Order ${order_id} not found. Please verify the order ID.` + } }); } diff --git a/.agents/skills/agents/references/installation.md b/.agents/skills/agents/references/installation.md index aa62004..e94860d 100644 --- a/.agents/skills/agents/references/installation.md +++ b/.agents/skills/agents/references/installation.md @@ -48,14 +48,14 @@ npm install @elevenlabs/elevenlabs-js > **Important:** Always use `@elevenlabs/elevenlabs-js`. The old `elevenlabs` npm package (v1.x) is deprecated and should not be used. ```javascript -import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js"; +import { ElevenLabsClient } from '@elevenlabs/elevenlabs-js'; // Option 1: Environment variable (recommended) // Set ELEVENLABS_API_KEY in your environment const client = new ElevenLabsClient(); // Option 2: Pass directly -const client = new ElevenLabsClient({ apiKey: "your-api-key" }); +const client = new ElevenLabsClient({ apiKey: 'your-api-key' }); ``` ### Migrating from deprecated packages @@ -75,10 +75,11 @@ npm install @elevenlabs/react # React hooks ``` **Import changes:** + ```javascript -import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js"; -import { Conversation } from "@elevenlabs/client"; -import { useConversation } from "@elevenlabs/react"; +import { ElevenLabsClient } from '@elevenlabs/elevenlabs-js'; +import { Conversation } from '@elevenlabs/client'; +import { useConversation } from '@elevenlabs/react'; ``` ## Python @@ -126,6 +127,6 @@ Or use the `setup-api-key` skill for guided setup. ## Environment Variables -| Variable | Description | -|----------|-------------| +| Variable | Description | +| -------------------- | ---------------------------------- | | `ELEVENLABS_API_KEY` | Your ElevenLabs API key (required) | diff --git a/.agents/skills/agents/references/outbound-calls.md b/.agents/skills/agents/references/outbound-calls.md index 48a9e4c..1a50c06 100644 --- a/.agents/skills/agents/references/outbound-calls.md +++ b/.agents/skills/agents/references/outbound-calls.md @@ -14,12 +14,12 @@ See the [main agents skill](../SKILL.md#outbound-calls) for basic Python, JavaSc ## Request Parameters -| Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `agent_id` | string | Yes | The ID of your ElevenLabs agent | -| `agent_phone_number_id` | string | Yes | The ID of the Twilio phone number linked to your agent | -| `to_number` | string | Yes | The destination phone number (E.164 format) | -| `conversation_initiation_client_data` | object | No | Override conversation settings for this call | +| Parameter | Type | Required | Description | +| ------------------------------------- | ------ | -------- | ------------------------------------------------------ | +| `agent_id` | string | Yes | The ID of your ElevenLabs agent | +| `agent_phone_number_id` | string | Yes | The ID of the Twilio phone number linked to your agent | +| `to_number` | string | Yes | The destination phone number (E.164 format) | +| `conversation_initiation_client_data` | object | No | Override conversation settings for this call | ## Response @@ -32,12 +32,12 @@ See the [main agents skill](../SKILL.md#outbound-calls) for basic Python, JavaSc } ``` -| Field | Type | Description | -|-------|------|-------------| -| `success` | boolean | Whether the call was initiated successfully | -| `message` | string | Status message | -| `conversation_id` | string | ElevenLabs conversation ID for tracking | -| `callSid` | string | Twilio Call SID for reference | +| Field | Type | Description | +| ----------------- | ------- | ------------------------------------------- | +| `success` | boolean | Whether the call was initiated successfully | +| `message` | string | Status message | +| `conversation_id` | string | ElevenLabs conversation ID for tracking | +| `callSid` | string | Twilio Call SID for reference | ## Customizing the Call @@ -72,24 +72,24 @@ response = client.conversational_ai.twilio.outbound_call( ```javascript const response = await client.conversationalAi.twilio.outboundCall({ - agentId: "your-agent-id", - agentPhoneNumberId: "your-phone-number-id", - toNumber: "+1234567890", + agentId: 'your-agent-id', + agentPhoneNumberId: 'your-phone-number-id', + toNumber: '+1234567890', conversationInitiationClientData: { conversationConfigOverride: { agent: { - firstMessage: "Hello! This is a reminder about your appointment tomorrow.", - language: "en", + firstMessage: 'Hello! This is a reminder about your appointment tomorrow.', + language: 'en' }, tts: { - voiceId: "JBFqnCBsd6RMkjVDRZzb", - }, + voiceId: 'JBFqnCBsd6RMkjVDRZzb' + } }, dynamicVariables: { - customer_name: "John", - appointment_time: "2:00 PM", - }, - }, + customer_name: 'John', + appointment_time: '2:00 PM' + } + } }); ``` @@ -97,20 +97,20 @@ const response = await client.conversationalAi.twilio.outboundCall({ ### Agent Settings -| Option | Type | Description | -|--------|------|-------------| -| `first_message` | string | Custom greeting for this call | -| `language` | string | Language code (e.g., "en", "es", "fr") | -| `prompt` | object | Override agent prompt and LLM settings | +| Option | Type | Description | +| --------------- | ------ | -------------------------------------- | +| `first_message` | string | Custom greeting for this call | +| `language` | string | Language code (e.g., "en", "es", "fr") | +| `prompt` | object | Override agent prompt and LLM settings | ### TTS Settings -| Option | Type | Description | -|--------|------|-------------| -| `voice_id` | string | Voice ID to use for this call | -| `stability` | number | Voice stability (0.0-1.0) | +| Option | Type | Description | +| ------------------ | ------ | -------------------------------- | +| `voice_id` | string | Voice ID to use for this call | +| `stability` | number | Voice stability (0.0-1.0) | | `similarity_boost` | number | Voice similarity boost (0.0-1.0) | -| `speed` | number | Speech speed multiplier | +| `speed` | number | Speech speed multiplier | ### Dynamic Variables diff --git a/.agents/skills/agents/references/widget-embedding.md b/.agents/skills/agents/references/widget-embedding.md index 2d491aa..f8d3ddb 100644 --- a/.agents/skills/agents/references/widget-embedding.md +++ b/.agents/skills/agents/references/widget-embedding.md @@ -6,7 +6,11 @@ Add a voice AI agent to any website with the ElevenLabs conversation widget. ```html - + ``` This creates a floating button that users can click to start a voice conversation. @@ -17,39 +21,39 @@ This creates a floating button that users can click to start a voice conversatio ### Required -| Attribute | Description | -|-----------|-------------| -| `agent-id` | Your ElevenLabs agent ID | +| Attribute | Description | +| ------------ | ------------------------------------------------ | +| `agent-id` | Your ElevenLabs agent ID | | `signed-url` | Alternative to `agent-id` when using signed URLs | ### Appearance -| Attribute | Description | Default | -|-----------|-------------|---------| -| `avatar-image-url` | URL for agent avatar image | ElevenLabs logo | -| `avatar-orb-color-1` | Primary orb gradient color | `#2792dc` | -| `avatar-orb-color-2` | Secondary orb gradient color | `#9ce6e6` | +| Attribute | Description | Default | +| -------------------- | ---------------------------- | --------------- | +| `avatar-image-url` | URL for agent avatar image | ElevenLabs logo | +| `avatar-orb-color-1` | Primary orb gradient color | `#2792dc` | +| `avatar-orb-color-2` | Secondary orb gradient color | `#9ce6e6` | ### Text Labels -| Attribute | Description | Default | -|-----------|-------------|---------| -| `action-text` | Tooltip when hovering | "Talk to AI" | -| `start-call-text` | Button to start call | "Start call" | -| `end-call-text` | Button to end call | "End call" | -| `expand-text` | Expand chat button | "Open" | -| `collapse-text` | Collapse chat button | "Close" | -| `listening-text` | Listening state label | "Listening..." | -| `speaking-text` | Speaking state label | "Assistant speaking" | +| Attribute | Description | Default | +| ----------------- | --------------------- | -------------------- | +| `action-text` | Tooltip when hovering | "Talk to AI" | +| `start-call-text` | Button to start call | "Start call" | +| `end-call-text` | Button to end call | "End call" | +| `expand-text` | Expand chat button | "Open" | +| `collapse-text` | Collapse chat button | "Close" | +| `listening-text` | Listening state label | "Listening..." | +| `speaking-text` | Speaking state label | "Assistant speaking" | ### Behavior -| Attribute | Description | Default | -|-----------|-------------|---------| -| `variant` | Widget style: `compact` or `expanded` | `compact` | -| `server-location` | Server region (`us`, `eu-residency`, `in-residency`, `global`) | `us` | -| `dismissible` | Allow the user to minimize the widget | `false` | -| `disable-banner` | Hide "Powered by ElevenLabs" | `false` | +| Attribute | Description | Default | +| ----------------- | -------------------------------------------------------------- | --------- | +| `variant` | Widget style: `compact` or `expanded` | `compact` | +| `server-location` | Server region (`us`, `eu-residency`, `in-residency`, `global`) | `us` | +| `dismissible` | Allow the user to minimize the widget | `false` | +| `disable-banner` | Hide "Powered by ElevenLabs" | `false` | ## Examples @@ -86,10 +90,7 @@ This creates a floating button that users can click to start a voice conversatio ### Expanded Variant ```html - + ``` ### Full Customization @@ -150,7 +151,7 @@ Access the widget element to control it programmatically: ``` @@ -180,9 +181,7 @@ Hide the default widget and use your own button: } - + ``` @@ -197,11 +196,11 @@ For agents with authentication enabled, pass a signed URL: @@ -281,8 +280,8 @@ You can have multiple widgets for different agents: function App() { useEffect(() => { // Load widget script - const script = document.createElement("script"); - script.src = "https://unpkg.com/@elevenlabs/convai-widget-embed"; + const script = document.createElement('script'); + script.src = 'https://unpkg.com/@elevenlabs/convai-widget-embed'; script.async = true; document.body.appendChild(script); @@ -307,11 +306,11 @@ function App() { - + + diff --git a/src/main.ts b/src/main.ts index d58e3b8..8fd6863 100644 --- a/src/main.ts +++ b/src/main.ts @@ -25,7 +25,7 @@ registerIpcHandlers({ renderComposite, computeSections, getScribeToken, - proxyService, + proxyService }); function createMainWindow(): void { @@ -33,7 +33,7 @@ function createMainWindow(): void { BrowserWindow, onConsoleMessage: ({ level, message, line, sourceId }) => { console.log(`[renderer:${level}] ${message} (${sourceId}:${line})`); - }, + } }); win.on('closed', () => { win = null; diff --git a/src/main/app/create-window.ts b/src/main/app/create-window.ts index 668ba2b..851f336 100644 --- a/src/main/app/create-window.ts +++ b/src/main/app/create-window.ts @@ -3,7 +3,7 @@ import path from 'node:path'; import type { BrowserWindow as ElectronBrowserWindow, BrowserWindowConstructorOptions, - Event, + Event } from 'electron'; interface ConsoleMessagePayload { @@ -15,13 +15,13 @@ interface ConsoleMessagePayload { } export type BrowserWindowConstructor = new ( - options: BrowserWindowConstructorOptions, + options: BrowserWindowConstructorOptions ) => ElectronBrowserWindow; export function createWindow({ BrowserWindow, onConsoleMessage, - appRootDir = path.join(__dirname, '..', '..'), + appRootDir = path.join(__dirname, '..', '..') }: { BrowserWindow: BrowserWindowConstructor; onConsoleMessage?: (payload: ConsoleMessagePayload) => void; @@ -31,21 +31,18 @@ export function createWindow({ width: 960, height: 800, webPreferences: { - preload: path.join(appRootDir, 'preload.js'), - }, + preload: path.join(appRootDir, 'preload.js') + } }); win.setContentProtection(true); - win.webContents.on( - 'console-message', - (event, level, message, line, sourceId) => { - if (typeof onConsoleMessage === 'function') { - onConsoleMessage({ event, level, message, line, sourceId }); - return; - } - console.log(`[renderer:${level}] ${message} (${sourceId}:${line})`); - }, - ); + win.webContents.on('console-message', (event, level, message, line, sourceId) => { + if (typeof onConsoleMessage === 'function') { + onConsoleMessage({ event, level, message, line, sourceId }); + return; + } + console.log(`[renderer:${level}] ${message} (${sourceId}:${line})`); + }); win.loadFile(path.join(appRootDir, 'index.html')); return win; diff --git a/src/main/infra/file-system.ts b/src/main/infra/file-system.ts index e6dfecd..27ad8d2 100644 --- a/src/main/infra/file-system.ts +++ b/src/main/infra/file-system.ts @@ -38,11 +38,14 @@ export function writeJsonFile(filePath: string, data: unknown): void { export function atomicWriteFileSync( filePath: string, data: string | Buffer, - encoding?: BufferEncoding, + encoding?: BufferEncoding ): void { const dir = path.dirname(filePath); ensureDirectory(dir); - const tmpPath = path.join(dir, `.tmp-${crypto.randomBytes(6).toString('hex')}${path.extname(filePath)}`); + const tmpPath = path.join( + dir, + `.tmp-${crypto.randomBytes(6).toString('hex')}${path.extname(filePath)}` + ); try { if (encoding) { fs.writeFileSync(tmpPath, data, encoding); @@ -52,7 +55,11 @@ export function atomicWriteFileSync( fs.renameSync(tmpPath, filePath); } catch (error) { // Clean up temp file on failure - try { fs.unlinkSync(tmpPath); } catch { /* ignore */ } + try { + fs.unlinkSync(tmpPath); + } catch { + /* ignore */ + } throw error; } } diff --git a/src/main/ipc/register-handlers.ts b/src/main/ipc/register-handlers.ts index e896ba8..a72c27f 100644 --- a/src/main/ipc/register-handlers.ts +++ b/src/main/ipc/register-handlers.ts @@ -9,7 +9,7 @@ import type { IpcMain, IpcMainInvokeEvent, OpenDialogOptions, - Shell, + Shell } from 'electron'; import type { createProjectService } from '../services/project-service'; @@ -40,7 +40,7 @@ export function registerIpcHandlers({ renderComposite, computeSections, getScribeToken, - proxyService, + proxyService }: { ipcMain: IpcMain; app: App; @@ -70,7 +70,7 @@ export function registerIpcHandlers({ try { const sources = await desktopCapturer.getSources({ types: ['screen', 'window'], - thumbnailSize: { width: 0, height: 0 }, + thumbnailSize: { width: 0, height: 0 } }); return sources.map((source) => ({ id: source.id, name: source.name })); } catch (error) { @@ -83,45 +83,38 @@ export function registerIpcHandlers({ const { canceled, filePaths } = await showOpenDialog({ title: typeof opts.title === 'string' && opts.title ? opts.title : 'Choose Folder', buttonLabel: - typeof opts.buttonLabel === 'string' && opts.buttonLabel - ? opts.buttonLabel - : 'Use Folder', + typeof opts.buttonLabel === 'string' && opts.buttonLabel ? opts.buttonLabel : 'Use Folder', defaultPath: app.getPath('documents') || app.getPath('home'), - properties: ['openDirectory', 'createDirectory'], + properties: ['openDirectory', 'createDirectory'] }); if (canceled || !filePaths.length) return null; return filePaths[0]; }); - ipcMain.handle( - 'pick-project-location', - async (_event, opts: PickFolderOptions = {}) => { - const projectName = projectService.sanitizeProjectName( - opts.name || 'Untitled Project', - ); - const defaultBasePath = app.getPath('documents') || app.getPath('home'); - - if (process.platform === 'win32') { - const { canceled, filePaths } = await showOpenDialog({ - title: `Choose where to create "${projectName}"`, - buttonLabel: 'Create Project Here', - defaultPath: defaultBasePath, - properties: ['openDirectory'], - }); - if (canceled || !filePaths.length) return null; - return path.join(filePaths[0], projectName); - } + ipcMain.handle('pick-project-location', async (_event, opts: PickFolderOptions = {}) => { + const projectName = projectService.sanitizeProjectName(opts.name || 'Untitled Project'); + const defaultBasePath = app.getPath('documents') || app.getPath('home'); + if (process.platform === 'win32') { const { canceled, filePaths } = await showOpenDialog({ title: `Choose where to create "${projectName}"`, buttonLabel: 'Create Project Here', defaultPath: defaultBasePath, - properties: ['openDirectory', 'createDirectory'], + properties: ['openDirectory'] }); if (canceled || !filePaths.length) return null; return path.join(filePaths[0], projectName); - }, - ); + } + + const { canceled, filePaths } = await showOpenDialog({ + title: `Choose where to create "${projectName}"`, + buttonLabel: 'Create Project Here', + defaultPath: defaultBasePath, + properties: ['openDirectory', 'createDirectory'] + }); + if (canceled || !filePaths.length) return null; + return path.join(filePaths[0], projectName); + }); ipcMain.handle('open-folder', async (_event, folder: string) => { shell.openPath(folder); @@ -143,19 +136,13 @@ export function registerIpcHandlers({ return projectService.setRecoveryTake(payload); }); - ipcMain.handle( - 'project-clear-recovery-take', - async (_event, projectFolder: string) => { - return projectService.clearRecoveryByProject(projectFolder); - }, - ); + ipcMain.handle('project-clear-recovery-take', async (_event, projectFolder: string) => { + return projectService.clearRecoveryByProject(projectFolder); + }); - ipcMain.handle( - 'project-complete-recovery-take', - async (_event, projectFolder: string) => { - return projectService.completeRecoveryByProject(projectFolder); - }, - ); + ipcMain.handle('project-complete-recovery-take', async (_event, projectFolder: string) => { + return projectService.completeRecoveryByProject(projectFolder); + }); ipcMain.handle('project-list-recent', async (_event, limit = 10) => { return projectService.listRecentProjects(limit); @@ -173,35 +160,27 @@ export function registerIpcHandlers({ 'save-video', async (_event, buffer: ArrayBuffer, folder: string, suffix?: string) => { return projectService.saveVideo(buffer, folder, suffix); - }, + } ); - ipcMain.handle( - 'render-composite', - async (event: IpcMainInvokeEvent, opts: unknown) => { - return renderComposite(opts as Parameters[0], { - onProgress: (progress) => { - event.sender.send('render-composite-progress', progress); - }, - }); - }, - ); + ipcMain.handle('render-composite', async (event: IpcMainInvokeEvent, opts: unknown) => { + return renderComposite(opts as Parameters[0], { + onProgress: (progress) => { + event.sender.send('render-composite-progress', progress); + } + }); + }); - ipcMain.handle( - 'import-file', - async (_event, sourcePath: string, projectFolder: string) => { - if (!sourcePath || !projectFolder) throw new Error('Missing source path or project folder'); - return copyFile(path.resolve(sourcePath), path.resolve(projectFolder), 'image'); - }, - ); + ipcMain.handle('import-file', async (_event, sourcePath: string, projectFolder: string) => { + if (!sourcePath || !projectFolder) throw new Error('Missing source path or project folder'); + return copyFile(path.resolve(sourcePath), path.resolve(projectFolder), 'image'); + }); ipcMain.handle('pick-image-file', async () => { const { canceled, filePaths } = await showOpenDialog({ title: 'Select Image', - filters: [ - { name: 'Images', extensions: ['png', 'jpg', 'jpeg', 'webp', 'gif', 'bmp'] }, - ], - properties: ['openFile'], + filters: [{ name: 'Images', extensions: ['png', 'jpg', 'jpeg', 'webp', 'gif', 'bmp'] }], + properties: ['openFile'] }); if (canceled || !filePaths.length) return null; return filePaths[0]; @@ -222,38 +201,54 @@ export function registerIpcHandlers({ ipcMain.handle( 'proxy:generate', - (event: IpcMainInvokeEvent, opts: { takeId: string; screenPath: string; projectFolder: string; durationSec?: number }) => { + ( + event: IpcMainInvokeEvent, + opts: { takeId: string; screenPath: string; projectFolder: string; durationSec?: number } + ) => { if (!proxyService || !opts.screenPath || !opts.projectFolder) return null; const proxyPath = proxyService.deriveProxyPath(opts.screenPath); - const totalDuration = Number.isFinite(opts.durationSec) && (opts.durationSec as number) > 0 - ? (opts.durationSec as number) - : 0; + const totalDuration = + Number.isFinite(opts.durationSec) && (opts.durationSec as number) > 0 + ? (opts.durationSec as number) + : 0; event.sender.send('proxy:progress', { takeId: opts.takeId, status: 'started', percent: 0 }); - const onProgress = totalDuration > 0 - ? (progress: { outTimeSec: number | null }) => { - if (event.sender.isDestroyed()) return; - const outSec = progress.outTimeSec; - if (Number.isFinite(outSec) && outSec !== null && outSec >= 0) { - const percent = Math.max(0, Math.min(1, outSec / totalDuration)); - event.sender.send('proxy:progress', { takeId: opts.takeId, status: 'progress', percent }); + const onProgress = + totalDuration > 0 + ? (progress: { outTimeSec: number | null }) => { + if (event.sender.isDestroyed()) return; + const outSec = progress.outTimeSec; + if (Number.isFinite(outSec) && outSec !== null && outSec >= 0) { + const percent = Math.max(0, Math.min(1, outSec / totalDuration)); + event.sender.send('proxy:progress', { + takeId: opts.takeId, + status: 'progress', + percent + }); + } } + : undefined; + + proxyService + .generateProxy({ screenPath: opts.screenPath, proxyPath, onProgress }) + .then(() => { + if (!event.sender.isDestroyed()) { + event.sender.send('proxy:progress', { takeId: opts.takeId, status: 'done', proxyPath }); } - : undefined; - - proxyService.generateProxy({ screenPath: opts.screenPath, proxyPath, onProgress }).then(() => { - if (!event.sender.isDestroyed()) { - event.sender.send('proxy:progress', { takeId: opts.takeId, status: 'done', proxyPath }); - } - }).catch((err: unknown) => { - if (!event.sender.isDestroyed()) { - const message = err instanceof Error ? err.message : String(err); - event.sender.send('proxy:progress', { takeId: opts.takeId, status: 'error', error: message }); - } - }); + }) + .catch((err: unknown) => { + if (!event.sender.isDestroyed()) { + const message = err instanceof Error ? err.message : String(err); + event.sender.send('proxy:progress', { + takeId: opts.takeId, + status: 'error', + error: message + }); + } + }); return proxyPath; - }, + } ); } diff --git a/src/main/services/ffmpeg-runner.ts b/src/main/services/ffmpeg-runner.ts index 0c855bc..afa2ccc 100644 --- a/src/main/services/ffmpeg-runner.ts +++ b/src/main/services/ffmpeg-runner.ts @@ -22,11 +22,7 @@ export function parseTimeToSeconds(value: unknown): number | null { const hours = Number(parts[0]); const minutes = Number(parts[1]); const seconds = Number(parts[2]); - if ( - !Number.isFinite(hours) || - !Number.isFinite(minutes) || - !Number.isFinite(seconds) - ) { + if (!Number.isFinite(hours) || !Number.isFinite(minutes) || !Number.isFinite(seconds)) { return null; } @@ -44,7 +40,7 @@ function parseSpeedField(value: unknown): number | null { } export function parseFfmpegProgress( - fields: FfmpegProgressFields | null | undefined, + fields: FfmpegProgressFields | null | undefined ): FfmpegProgress | null { if (!fields || typeof fields !== 'object') return null; const status = typeof fields.progress === 'string' ? fields.progress : null; @@ -57,7 +53,7 @@ export function parseFfmpegProgress( fps: parseNumericField(fields.fps), speed: parseSpeedField(fields.speed), outTimeSec, - raw: { ...fields }, + raw: { ...fields } }; } @@ -65,7 +61,7 @@ export function runFfmpeg({ ffmpegPath, args, spawnImpl = spawn, - onProgress, + onProgress }: { ffmpegPath?: string; args?: string[]; @@ -74,7 +70,7 @@ export function runFfmpeg({ } = {}): Promise<{ stderr: string }> { return new Promise((resolve, reject) => { const child = spawnImpl(ffmpegPath as string, Array.isArray(args) ? args : [], { - stdio: ['ignore', 'pipe', 'pipe'], + stdio: ['ignore', 'pipe', 'pipe'] }); let stdoutBuffer = ''; diff --git a/src/main/services/fps-service.ts b/src/main/services/fps-service.ts index 8e56deb..ff48985 100644 --- a/src/main/services/fps-service.ts +++ b/src/main/services/fps-service.ts @@ -11,11 +11,7 @@ export function parseFpsToken(token: unknown): number | null { const [numRaw, denRaw] = value.split('/'); const numerator = Number(numRaw); const denominator = Number(denRaw); - if ( - !Number.isFinite(numerator) || - !Number.isFinite(denominator) || - denominator === 0 - ) { + if (!Number.isFinite(numerator) || !Number.isFinite(denominator) || denominator === 0) { return null; } @@ -31,7 +27,7 @@ export function parseVideoFpsFromProbeOutput(output: unknown): number | null { const patterns = [ /,\s*([0-9]+(?:\.[0-9]+)?(?:\/[0-9]+(?:\.[0-9]+)?)?)\s*fps\b/i, - /,\s*([0-9]+(?:\.[0-9]+)?(?:\/[0-9]+(?:\.[0-9]+)?)?)\s*tbr\b/i, + /,\s*([0-9]+(?:\.[0-9]+)?(?:\/[0-9]+(?:\.[0-9]+)?)?)\s*tbr\b/i ]; for (const pattern of patterns) { @@ -47,7 +43,7 @@ export function parseVideoFpsFromProbeOutput(output: unknown): number | null { export function probeVideoFpsWithFfmpeg( ffmpegPath: string, - filePath: string, + filePath: string ): Promise { return new Promise((resolve) => { execFile( @@ -58,13 +54,10 @@ export function probeVideoFpsWithFfmpeg( const output = `${stdout || ''}\n${stderr || ''}`; const fps = parseVideoFpsFromProbeOutput(output); if (error && !fps) { - console.warn( - `[render-composite] FPS probe failed for ${filePath}:`, - error.message, - ); + console.warn(`[render-composite] FPS probe failed for ${filePath}:`, error.message); } resolve(fps); - }, + } ); }); } diff --git a/src/main/services/project-service.ts b/src/main/services/project-service.ts index cc19e89..a775b8f 100644 --- a/src/main/services/project-service.ts +++ b/src/main/services/project-service.ts @@ -9,7 +9,7 @@ import { isDirectoryEmpty, readJsonFile, safeUnlink, - writeJsonFile, + writeJsonFile } from '../infra/file-system'; import { createDefaultProject, @@ -20,7 +20,7 @@ import { toProjectRelativePath, type ProjectData, type RecoveryTake, - type RecoveryTrimSegment, + type RecoveryTrimSegment } from '../../shared/domain/project'; export const PROJECT_FILE_NAME = 'project.json'; @@ -70,10 +70,7 @@ export function createProjectService({ app }: { app: Pick }) { return path.join(app.getPath('userData'), PROJECT_META_FILE_NAME); } - function normalizeRecoveryTake( - rawTake: unknown, - projectFolder: string, - ): RecoveryTake | null { + function normalizeRecoveryTake(rawTake: unknown, projectFolder: string): RecoveryTake | null { if (!isRecord(rawTake)) return null; const take = rawTake as RecoveryTakeInput; @@ -102,10 +99,7 @@ export function createProjectService({ app }: { app: Pick }) { return { start, end, - text: - typeof rawSegment.text === 'string' - ? rawSegment.text.trim() - : '', + text: typeof rawSegment.text === 'string' ? rawSegment.text.trim() : '' }; }) .filter((segment): segment is RecoveryTrimSegment => Boolean(segment)) @@ -115,19 +109,13 @@ export function createProjectService({ app }: { app: Pick }) { if (cameraPath && !fs.existsSync(cameraPath)) return null; return { - id: - typeof take.id === 'string' && take.id - ? take.id - : `recovery-${Date.now()}`, - createdAt: - typeof take.createdAt === 'string' - ? take.createdAt - : new Date().toISOString(), + id: typeof take.id === 'string' && take.id ? take.id : `recovery-${Date.now()}`, + createdAt: typeof take.createdAt === 'string' ? take.createdAt : new Date().toISOString(), screenPath, cameraPath, recordedDuration: Number.isFinite(recordedDuration) ? recordedDuration : 0, sections, - trimSegments, + trimSegments }; } @@ -141,17 +129,14 @@ export function createProjectService({ app }: { app: Pick }) { return null; } - function writeRecoveryTake( - projectFolder: string, - rawTake: unknown, - ): RecoveryTake { + function writeRecoveryTake(projectFolder: string, rawTake: unknown): RecoveryTake { const normalized = normalizeRecoveryTake(rawTake, projectFolder); if (!normalized) throw new Error('Invalid recovery recording'); const serializable = { ...normalized, screenPath: toProjectRelativePath(projectFolder, normalized.screenPath), - cameraPath: toProjectRelativePath(projectFolder, normalized.cameraPath), + cameraPath: toProjectRelativePath(projectFolder, normalized.cameraPath) }; writeJsonFile(getProjectRecoveryFilePath(projectFolder), serializable); return normalized; @@ -173,10 +158,7 @@ export function createProjectService({ app }: { app: Pick }) { throw new Error('Project location must be a folder'); } - if ( - isDirectoryEmpty(resolvedTarget) && - !fs.existsSync(getProjectFilePath(resolvedTarget)) - ) { + if (isDirectoryEmpty(resolvedTarget) && !fs.existsSync(getProjectFilePath(resolvedTarget))) { return resolvedTarget; } @@ -191,10 +173,7 @@ export function createProjectService({ app }: { app: Pick }) { return candidate; } - function saveProjectToDisk( - projectFolder: string, - rawProject: unknown, - ): ProjectData { + function saveProjectToDisk(projectFolder: string, rawProject: unknown): ProjectData { const normalized = normalizeProjectData(rawProject, projectFolder); normalized.updatedAt = new Date().toISOString(); @@ -203,11 +182,11 @@ export function createProjectService({ app }: { app: Pick }) { ...take, screenPath: toProjectRelativePath(projectFolder, take.screenPath), cameraPath: toProjectRelativePath(projectFolder, take.cameraPath), - proxyPath: toProjectRelativePath(projectFolder, take.proxyPath), + proxyPath: toProjectRelativePath(projectFolder, take.proxyPath) })); serializable.timeline.sections = serializable.timeline.sections.map((section) => ({ ...section, - imagePath: toProjectRelativePath(projectFolder, section.imagePath), + imagePath: toProjectRelativePath(projectFolder, section.imagePath) })); writeJsonFile(getProjectFilePath(projectFolder), serializable); @@ -216,10 +195,7 @@ export function createProjectService({ app }: { app: Pick }) { function loadProjectFromDisk(projectFolder: string): ProjectData { const resolvedFolder = path.resolve(projectFolder); - const rawProject = readJsonFile( - getProjectFilePath(resolvedFolder), - null, - ); + const rawProject = readJsonFile(getProjectFilePath(resolvedFolder), null); if (!rawProject) { throw new Error(`Project file missing at ${getProjectFilePath(resolvedFolder)}`); } @@ -232,17 +208,13 @@ export function createProjectService({ app }: { app: Pick }) { const recentProjectPaths = Array.isArray(raw?.recentProjectPaths) ? raw.recentProjectPaths.filter( (projectPath): projectPath is string => - typeof projectPath === 'string' && projectPath.trim().length > 0, + typeof projectPath === 'string' && projectPath.trim().length > 0 ) : []; return { - lastProjectPath: - typeof raw?.lastProjectPath === 'string' ? raw.lastProjectPath : null, - recentProjectPaths: [...new Set(recentProjectPaths)].slice( - 0, - MAX_RECENT_PROJECTS, - ), + lastProjectPath: typeof raw?.lastProjectPath === 'string' ? raw.lastProjectPath : null, + recentProjectPaths: [...new Set(recentProjectPaths)].slice(0, MAX_RECENT_PROJECTS) }; } @@ -255,13 +227,9 @@ export function createProjectService({ app }: { app: Pick }) { const meta = readProjectMeta(); const remaining = meta.recentProjectPaths.filter( (savedProjectPath) => - savedProjectPath !== resolvedFolder && - fs.existsSync(getProjectFilePath(savedProjectPath)), - ); - meta.recentProjectPaths = [resolvedFolder, ...remaining].slice( - 0, - MAX_RECENT_PROJECTS, + savedProjectPath !== resolvedFolder && fs.existsSync(getProjectFilePath(savedProjectPath)) ); + meta.recentProjectPaths = [resolvedFolder, ...remaining].slice(0, MAX_RECENT_PROJECTS); meta.lastProjectPath = resolvedFolder; writeProjectMeta(meta); } @@ -286,7 +254,7 @@ export function createProjectService({ app }: { app: Pick }) { id: project.id, name: project.name, createdAt: project.createdAt, - updatedAt: project.updatedAt, + updatedAt: project.updatedAt }); if (projects.length >= maxItems) break; } catch (error) { @@ -305,8 +273,7 @@ export function createProjectService({ app }: { app: Pick }) { function createProject(opts: CreateProjectOptions = {}) { const baseName = sanitizeProjectName(opts.name || 'Untitled Project'); - const explicitProjectPath = - typeof opts.projectPath === 'string' ? opts.projectPath.trim() : ''; + const explicitProjectPath = typeof opts.projectPath === 'string' ? opts.projectPath.trim() : ''; let targetFolder: string; if (explicitProjectPath) { @@ -315,14 +282,11 @@ export function createProjectService({ app }: { app: Pick }) { ensureDirectory(parentFolder); targetFolder = resolveAvailableProjectFolder(targetFolder); } else { - const parentFolder = - typeof opts.parentFolder === 'string' ? opts.parentFolder : ''; + const parentFolder = typeof opts.parentFolder === 'string' ? opts.parentFolder : ''; if (!parentFolder) throw new Error('Missing parent folder'); const resolvedParent = path.resolve(parentFolder); ensureDirectory(resolvedParent); - targetFolder = resolveAvailableProjectFolder( - path.join(resolvedParent, baseName), - ); + targetFolder = resolveAvailableProjectFolder(path.join(resolvedParent, baseName)); } if (fs.existsSync(targetFolder) && !fs.statSync(targetFolder).isDirectory()) { @@ -332,7 +296,7 @@ export function createProjectService({ app }: { app: Pick }) { ensureDirectory(targetFolder); const project = saveProjectToDisk( targetFolder, - createDefaultProject(path.basename(targetFolder)), + createDefaultProject(path.basename(targetFolder)) ); touchRecentProject(targetFolder); return { projectPath: targetFolder, project }; @@ -351,8 +315,7 @@ export function createProjectService({ app }: { app: Pick }) { } function saveProject(payload: ProjectPayload = {}) { - const projectPath = - typeof payload.projectPath === 'string' ? payload.projectPath : ''; + const projectPath = typeof payload.projectPath === 'string' ? payload.projectPath : ''; if (!projectPath) throw new Error('Missing project path'); const resolvedFolder = path.resolve(projectPath); @@ -363,8 +326,7 @@ export function createProjectService({ app }: { app: Pick }) { } function setRecoveryTake(payload: RecoveryPayload = {}) { - const projectPath = - typeof payload.projectPath === 'string' ? payload.projectPath : ''; + const projectPath = typeof payload.projectPath === 'string' ? payload.projectPath : ''; if (!projectPath) throw new Error('Missing project path'); const resolvedFolder = path.resolve(projectPath); @@ -410,11 +372,7 @@ export function createProjectService({ app }: { app: Pick }) { return true; } - function saveVideo( - buffer: ArrayBuffer | Uint8Array, - folder: string, - suffix?: string, - ): string { + function saveVideo(buffer: ArrayBuffer | Uint8Array, folder: string, suffix?: string): string { const filename = `recording-${Date.now()}${suffix ? `-${suffix}` : ''}.webm`; ensureDirectory(folder); const filePath = path.join(folder, filename); @@ -428,7 +386,7 @@ export function createProjectService({ app }: { app: Pick }) { const stat = fs.statSync(filePath); if (stat.size !== data.length) { throw new Error( - `Recording file verification failed: expected ${data.length} bytes, got ${stat.size}`, + `Recording file verification failed: expected ${data.length} bytes, got ${stat.size}` ); } @@ -460,6 +418,6 @@ export function createProjectService({ app }: { app: Pick }) { completeRecoveryByProject, loadLastProject, setLastProject, - saveVideo, + saveVideo }; } diff --git a/src/main/services/proxy-service.ts b/src/main/services/proxy-service.ts index 82499fb..b846bc7 100644 --- a/src/main/services/proxy-service.ts +++ b/src/main/services/proxy-service.ts @@ -19,10 +19,12 @@ function drainQueue(): void { function enqueue(fn: () => Promise): Promise { return new Promise((resolve, reject) => { queue.push(() => { - fn().then(resolve, reject).finally(() => { - activeCount -= 1; - drainQueue(); - }); + fn() + .then(resolve, reject) + .finally(() => { + activeCount -= 1; + drainQueue(); + }); }); drainQueue(); }); @@ -49,7 +51,7 @@ export interface GenerateProxyDeps { export function generateProxy( opts: GenerateProxyOpts, - deps: GenerateProxyDeps = {}, + deps: GenerateProxyDeps = {} ): Promise { const runFfmpegImpl = deps.runFfmpeg ?? runFfmpeg; const fsImpl = deps.fs ?? fs; @@ -59,24 +61,41 @@ export function generateProxy( const tmpPath = `${opts.proxyPath}.tmp`; if (fsImpl.existsSync(tmpPath)) { - try { fsImpl.unlinkSync(tmpPath); } catch { /* ignore */ } + try { + fsImpl.unlinkSync(tmpPath); + } catch { + /* ignore */ + } } const args = [ - '-progress', 'pipe:1', '-nostats', - '-i', opts.screenPath, - '-vf', 'scale=960:540', - '-c:v', 'libx264', - '-crf', '23', - '-preset', 'ultrafast', - '-threads', '2', - '-g', '15', - '-c:a', 'aac', - '-b:a', '64k', - '-movflags', '+faststart', - '-f', 'mp4', + '-progress', + 'pipe:1', + '-nostats', + '-i', + opts.screenPath, + '-vf', + 'scale=960:540', + '-c:v', + 'libx264', + '-crf', + '23', + '-preset', + 'ultrafast', + '-threads', + '2', + '-g', + '15', + '-c:a', + 'aac', + '-b:a', + '64k', + '-movflags', + '+faststart', + '-f', + 'mp4', '-y', - tmpPath, + tmpPath ]; try { @@ -85,7 +104,9 @@ export function generateProxy( } catch (err) { try { if (fsImpl.existsSync(tmpPath)) fsImpl.unlinkSync(tmpPath); - } catch { /* ignore */ } + } catch { + /* ignore */ + } throw err; } }); diff --git a/src/main/services/render-filter-service.ts b/src/main/services/render-filter-service.ts index a8e42a2..16a3161 100644 --- a/src/main/services/render-filter-service.ts +++ b/src/main/services/render-filter-service.ts @@ -16,7 +16,7 @@ interface KeyframeProp extends Keyframe { function collapseConsecutiveKeyframes( keyframes: T[], - isSame: (prev: T, curr: T) => boolean, + isSame: (prev: T, curr: T) => boolean ): T[] { if (!Array.isArray(keyframes) || keyframes.length <= 1) return keyframes; @@ -46,20 +46,13 @@ export function buildNumericExpr( prop: keyof KeyframeProp, precision = 3, defaultValue = 0, - timeVar = 't', + timeVar = 't' ): string { - const relevantKeyframes = collapseConsecutiveKeyframes( - keyframes, - (prev, curr) => { - const prevVal = Number.isFinite(Number(prev?.[prop])) - ? Number(prev[prop]) - : defaultValue; - const currVal = Number.isFinite(Number(curr?.[prop])) - ? Number(curr[prop]) - : defaultValue; - return Math.abs(prevVal - currVal) <= 0.0001; - }, - ); + const relevantKeyframes = collapseConsecutiveKeyframes(keyframes, (prev, curr) => { + const prevVal = Number.isFinite(Number(prev?.[prop])) ? Number(prev[prop]) : defaultValue; + const currVal = Number.isFinite(Number(curr?.[prop])) ? Number(curr[prop]) : defaultValue; + return Math.abs(prevVal - currVal) <= 0.0001; + }); const firstValue = Number.isFinite(Number(relevantKeyframes[0]?.[prop])) ? Number(relevantKeyframes[0][prop]) @@ -70,12 +63,8 @@ export function buildNumericExpr( for (let index = 1; index < relevantKeyframes.length; index += 1) { const prev = relevantKeyframes[index - 1]; const curr = relevantKeyframes[index]; - const prevVal = Number.isFinite(Number(prev?.[prop])) - ? Number(prev[prop]) - : defaultValue; - const currVal = Number.isFinite(Number(curr?.[prop])) - ? Number(curr[prop]) - : defaultValue; + const prevVal = Number.isFinite(Number(prev?.[prop])) ? Number(prev[prop]) : defaultValue; + const currVal = Number.isFinite(Number(curr?.[prop])) ? Number(curr[prop]) : defaultValue; const time = curr.time; const start = time - TRANSITION_DURATION; const diff = currVal - prevVal; @@ -90,11 +79,7 @@ export function buildNumericExpr( return expr; } -export function panToFocusCoord( - zoom: unknown, - pan: unknown, - defaultCoord = 0.5, -): number { +export function panToFocusCoord(zoom: unknown, pan: unknown, defaultCoord = 0.5): number { const normalizedZoom = Number.isFinite(Number(zoom)) ? Number(zoom) : 1; const normalizedPan = Number.isFinite(Number(pan)) ? Number(pan) : 0; if (normalizedZoom <= 1.0001) return defaultCoord; @@ -103,20 +88,15 @@ export function panToFocusCoord( } export function buildPosExpr(keyframes: Keyframe[], prop: 'pipX' | 'pipY'): string { - const relevantKeyframes = collapseConsecutiveKeyframes( - keyframes, - (prev, curr) => { - const prevVisible = - prev.pipVisible !== undefined ? prev.pipVisible : true; - const currVisible = - curr.pipVisible !== undefined ? curr.pipVisible : true; - return ( - Math.round(prev[prop]) === Math.round(curr[prop]) && - (prev.cameraFullscreen || false) === (curr.cameraFullscreen || false) && - prevVisible === currVisible - ); - }, - ); + const relevantKeyframes = collapseConsecutiveKeyframes(keyframes, (prev, curr) => { + const prevVisible = prev.pipVisible !== undefined ? prev.pipVisible : true; + const currVisible = curr.pipVisible !== undefined ? curr.pipVisible : true; + return ( + Math.round(prev[prop]) === Math.round(curr[prop]) && + (prev.cameraFullscreen || false) === (curr.cameraFullscreen || false) && + prevVisible === currVisible + ); + }); if (relevantKeyframes.length === 1) { return String(Math.round(relevantKeyframes[0][prop])); @@ -131,10 +111,8 @@ export function buildPosExpr(keyframes: Keyframe[], prop: 'pipX' | 'pipY'): stri const time = curr.time; const prevFull = prev.cameraFullscreen || false; const currFull = curr.cameraFullscreen || false; - const prevVisible = - prev.pipVisible !== undefined ? prev.pipVisible : true; - const currVisible = - curr.pipVisible !== undefined ? curr.pipVisible : true; + const prevVisible = prev.pipVisible !== undefined ? prev.pipVisible : true; + const currVisible = curr.pipVisible !== undefined ? curr.pipVisible : true; if ((prevFull && !currFull) || (!prevVisible && currVisible)) { const start = time - TRANSITION_DURATION; @@ -154,7 +132,7 @@ export function buildPosExpr(keyframes: Keyframe[], prop: 'pipX' | 'pipY'): stri export function buildAlphaExpr(keyframes: Keyframe[]): string { const relevantKeyframes = collapseConsecutiveKeyframes( keyframes, - (prev, curr) => prev.pipVisible === curr.pipVisible, + (prev, curr) => prev.pipVisible === curr.pipVisible ); if (relevantKeyframes.length === 1) return relevantKeyframes[0].pipVisible ? '1' : '0'; @@ -185,7 +163,7 @@ export function buildCamFullAlphaExpr(keyframes: Keyframe[]): string { const relevantKeyframes = collapseConsecutiveKeyframes( keyframes, - (prev, curr) => isFullVisible(prev) === isFullVisible(curr), + (prev, curr) => isFullVisible(prev) === isFullVisible(curr) ); if (relevantKeyframes.length === 1) return isFullVisible(relevantKeyframes[0]) ? '1' : '0'; @@ -220,37 +198,28 @@ export function buildScreenFilter( canvasW: number, canvasH: number, outputLabel = '[screen]', - targetFps = 30, + targetFps = 30 ): string { const { outW, outH } = resolveOutputSize(canvasW, canvasH); - const normalizedKeyframes = (Array.isArray(keyframes) ? keyframes : []).map( - (keyframe) => ({ - ...keyframe, - backgroundZoom: Number.isFinite(Number(keyframe?.backgroundZoom)) - ? Number(keyframe.backgroundZoom) - : 1, - backgroundPanX: Number.isFinite(Number(keyframe?.backgroundPanX)) - ? Number(keyframe.backgroundPanX) - : 0, - backgroundPanY: Number.isFinite(Number(keyframe?.backgroundPanY)) - ? Number(keyframe.backgroundPanY) - : 0, - backgroundFocusX: panToFocusCoord( - keyframe?.backgroundZoom, - keyframe?.backgroundPanX, - 0.5, - ), - backgroundFocusY: panToFocusCoord( - keyframe?.backgroundZoom, - keyframe?.backgroundPanY, - 0.5, - ), - }), - ); + const normalizedKeyframes = (Array.isArray(keyframes) ? keyframes : []).map((keyframe) => ({ + ...keyframe, + backgroundZoom: Number.isFinite(Number(keyframe?.backgroundZoom)) + ? Number(keyframe.backgroundZoom) + : 1, + backgroundPanX: Number.isFinite(Number(keyframe?.backgroundPanX)) + ? Number(keyframe.backgroundPanX) + : 0, + backgroundPanY: Number.isFinite(Number(keyframe?.backgroundPanY)) + ? Number(keyframe.backgroundPanY) + : 0, + backgroundFocusX: panToFocusCoord(keyframe?.backgroundZoom, keyframe?.backgroundPanX, 0.5), + backgroundFocusY: panToFocusCoord(keyframe?.backgroundZoom, keyframe?.backgroundPanY, 0.5) + })); - const baseFilter = screenFitMode === 'fill' - ? `[0:v]scale=${outW}:${outH}:flags=lanczos:force_original_aspect_ratio=increase,crop=${outW}:${outH}[screen_base]` - : `[0:v]scale=${outW}:${outH}:flags=lanczos:force_original_aspect_ratio=decrease,pad=${outW}:${outH}:'(ow-iw)/2':'(oh-ih)/2':color=black[screen_base]`; + const baseFilter = + screenFitMode === 'fill' + ? `[0:v]scale=${outW}:${outH}:flags=lanczos:force_original_aspect_ratio=increase,crop=${outW}:${outH}[screen_base]` + : `[0:v]scale=${outW}:${outH}:flags=lanczos:force_original_aspect_ratio=decrease,pad=${outW}:${outH}:'(ow-iw)/2':'(oh-ih)/2':color=black[screen_base]`; const hasBackgroundAnimation = normalizedKeyframes.some((keyframe) => { return ( @@ -264,27 +233,9 @@ export function buildScreenFilter( return baseFilter.replace('[screen_base]', outputLabel); } - const zoomExpr = buildNumericExpr( - normalizedKeyframes, - 'backgroundZoom', - 3, - 1, - 'it', - ); - const focusXExpr = buildNumericExpr( - normalizedKeyframes, - 'backgroundFocusX', - 6, - 0.5, - 'it', - ); - const focusYExpr = buildNumericExpr( - normalizedKeyframes, - 'backgroundFocusY', - 6, - 0.5, - 'it', - ); + const zoomExpr = buildNumericExpr(normalizedKeyframes, 'backgroundZoom', 3, 1, 'it'); + const focusXExpr = buildNumericExpr(normalizedKeyframes, 'backgroundFocusX', 6, 0.5, 'it'); + const focusYExpr = buildNumericExpr(normalizedKeyframes, 'backgroundFocusY', 6, 0.5, 'it'); const animatedFilter = `[screen_base]zoompan=z='${zoomExpr}':x='max(0,min(iw-iw/zoom,iw*(${focusXExpr})-iw/zoom/2))':y='max(0,min(ih-ih/zoom,ih*(${focusYExpr})-ih/zoom/2))':d=1:s=${outW}x${outH}:fps=${targetFps},setsar=1${outputLabel}`; return `${baseFilter};${animatedFilter}`; } @@ -297,7 +248,7 @@ export function buildFilterComplex( sourceHeight: number, canvasW: number, canvasH: number, - targetFps = 30, + targetFps = 30 ): string { const { outW, outH } = resolveOutputSize(canvasW, canvasH); const scaleX = outW / AUTHORING_CANVAS_W; @@ -314,7 +265,7 @@ export function buildFilterComplex( const scaledKeyframes = keyframes.map((keyframe) => ({ ...keyframe, pipX: Math.round(keyframe.pipX * scaleX), - pipY: Math.round(keyframe.pipY * scaleY), + pipY: Math.round(keyframe.pipY * scaleY) })); const screenFilter = buildScreenFilter( @@ -325,7 +276,7 @@ export function buildFilterComplex( canvasW, canvasH, '[screen]', - targetFps, + targetFps ); const hasPip = keyframes.some((keyframe) => keyframe.pipVisible); diff --git a/src/main/services/render-service.ts b/src/main/services/render-service.ts index a145740..055e4de 100644 --- a/src/main/services/render-service.ts +++ b/src/main/services/render-service.ts @@ -13,15 +13,12 @@ import { type ExportAudioPreset, type ExportVideoPreset, type Keyframe, - type ScreenFitMode, + type ScreenFitMode } from '../../shared/domain/project'; import type { RenderProgressUpdate } from '../../shared/electron-api'; import { chooseRenderFps, probeVideoFpsWithFfmpeg } from './fps-service'; import { runFfmpeg, type FfmpegProgress } from './ffmpeg-runner'; -import { - buildFilterComplex, - buildScreenFilter, -} from './render-filter-service'; +import { buildFilterComplex, buildScreenFilter } from './render-filter-service'; export interface RenderSectionInput { takeId: string | null; @@ -82,7 +79,7 @@ const QUALITY_RENDER_CONFIG: RenderVideoConfig = { crf: '8', preset: 'slow', pixelFormat: 'yuv420p', - audioBitrate: '192k', + audioBitrate: '192k' }; const FAST_RENDER_CONFIG: RenderVideoConfig = { @@ -93,7 +90,7 @@ const FAST_RENDER_CONFIG: RenderVideoConfig = { crf: '24', preset: 'veryfast', pixelFormat: 'yuv420p', - audioBitrate: '128k', + audioBitrate: '128k' }; function isRecord(value: unknown): value is Record { @@ -107,15 +104,13 @@ function roundDownToEven(value: number): number { } function getRenderVideoConfig(exportVideoPreset: ExportVideoPreset): RenderVideoConfig { - return exportVideoPreset === 'fast' - ? FAST_RENDER_CONFIG - : QUALITY_RENDER_CONFIG; + return exportVideoPreset === 'fast' ? FAST_RENDER_CONFIG : QUALITY_RENDER_CONFIG; } export function deriveRenderCanvasSize( sourceWidth: number, sourceHeight: number, - exportVideoPreset: ExportVideoPreset = 'quality', + exportVideoPreset: ExportVideoPreset = 'quality' ) { const config = getRenderVideoConfig(exportVideoPreset); const normalizedWidth = Number(sourceWidth); @@ -139,17 +134,15 @@ export function deriveRenderCanvasSize( const fitWidth = roundDownToEven((boundedHeight * 16) / 9); const candidates = [ fitHeight <= boundedHeight ? { canvasW: boundedWidth, canvasH: fitHeight } : null, - fitWidth <= boundedWidth ? { canvasW: fitWidth, canvasH: boundedHeight } : null, + fitWidth <= boundedWidth ? { canvasW: fitWidth, canvasH: boundedHeight } : null ].filter((candidate): candidate is { canvasW: number; canvasH: number } => Boolean(candidate)); const bestFit = candidates.reduce<{ canvasW: number; canvasH: number } | null>( (best, candidate) => { if (!best) return candidate; - return candidate.canvasW * candidate.canvasH > best.canvasW * best.canvasH - ? candidate - : best; + return candidate.canvasW * candidate.canvasH > best.canvasW * best.canvasH ? candidate : best; }, - null, + null ); if (!bestFit || bestFit.canvasW < config.minWidth || bestFit.canvasH < config.minHeight) { @@ -166,12 +159,15 @@ export function normalizeSectionInput(rawSections: unknown): RenderSectionInput[ if (!isRecord(rawSection)) return null; const sourceStart = Number(rawSection.sourceStart); const sourceEnd = Number(rawSection.sourceEnd); - if (!Number.isFinite(sourceStart) || !Number.isFinite(sourceEnd) || sourceEnd <= sourceStart) { + if ( + !Number.isFinite(sourceStart) || + !Number.isFinite(sourceEnd) || + sourceEnd <= sourceStart + ) { return null; } return { - takeId: - typeof rawSection.takeId === 'string' ? rawSection.takeId : null, + takeId: typeof rawSection.takeId === 'string' ? rawSection.takeId : null, sourceStart, sourceEnd, backgroundZoom: normalizeBackgroundZoom(rawSection.backgroundZoom), @@ -180,16 +176,13 @@ export function normalizeSectionInput(rawSections: unknown): RenderSectionInput[ imagePath: typeof rawSection.imagePath === 'string' && rawSection.imagePath ? rawSection.imagePath - : null, + : null }; }) .filter((section): section is RenderSectionInput => Boolean(section)); } -export function assertFilePath( - filePath: string | null, - label: string, -): asserts filePath is string { +export function assertFilePath(filePath: string | null, label: string): asserts filePath is string { if (typeof filePath !== 'string' || !filePath.trim()) { throw new Error(`Missing ${label} path`); } @@ -212,7 +205,7 @@ export function buildCameraTrimFilter( section: RenderSectionInput, targetFps: number, index: number, - cameraSyncOffsetMs: number, + cameraSyncOffsetMs: number ): string { const start = Number(section.sourceStart); const end = Number(section.sourceEnd); @@ -236,7 +229,7 @@ export function buildCameraTrimFilter( function buildInputPlan( sections: RenderSectionInput[], takeMap: Map, - hasCamera: boolean, + hasCamera: boolean ) { const fpsProbePaths = new Set(); const sectionInputs: Array<{ screenIdx: number; cameraIdx: number; imageIdx: number }> = []; @@ -287,14 +280,14 @@ function buildInputPlan( return { args, fpsProbePaths, - sectionInputs, + sectionInputs }; } function buildOutputArgs( targetFps: number, outputPath: string, - exportVideoPreset: ExportVideoPreset, + exportVideoPreset: ExportVideoPreset ): string[] { const config = getRenderVideoConfig(exportVideoPreset); return [ @@ -317,27 +310,27 @@ function buildOutputArgs( '-b:a', config.audioBitrate, '-y', - outputPath, + outputPath ]; } function getTotalDurationSec(sections: RenderSectionInput[]): number { return sections.reduce( (total, section) => total + Math.max(0, section.sourceEnd - section.sourceStart), - 0, + 0 ); } function assertOverlayFilterSize(filter: string): void { if (filter.length <= MAX_OVERLAY_FILTER_LENGTH) return; throw new Error( - 'Render filter is too complex for ffmpeg. Reduce camera layout changes and try again.', + 'Render filter is too complex for ffmpeg. Reduce camera layout changes and try again.' ); } function buildRenderProgressUpdate( progress: FfmpegProgress | null, - totalDurationSec: number, + totalDurationSec: number ): RenderProgressUpdate | null { if (!progress || typeof progress !== 'object') return null; @@ -356,7 +349,7 @@ function buildRenderProgressUpdate( outTimeSec: hasOutTime ? outTimeSec : totalDurationSec, durationSec: totalDurationSec, frame: progress.frame ?? null, - speed: progress.speed ?? null, + speed: progress.speed ?? null }; } @@ -364,19 +357,17 @@ function buildRenderProgressUpdate( phase: 'rendering', percent: clampedPercent, status: - clampedPercent === null - ? 'Rendering...' - : `Rendering ${Math.round(clampedPercent * 100)}%`, + clampedPercent === null ? 'Rendering...' : `Rendering ${Math.round(clampedPercent * 100)}%`, outTimeSec: hasOutTime ? outTimeSec : null, durationSec: totalDurationSec, frame: progress.frame ?? null, - speed: progress.speed ?? null, + speed: progress.speed ?? null }; } export async function renderComposite( opts: RenderCompositeOptions = {}, - deps: RenderCompositeDeps = {}, + deps: RenderCompositeDeps = {} ): Promise { const takes = Array.isArray(opts.takes) ? opts.takes : []; const sections = normalizeSectionInput(opts.sections); @@ -387,7 +378,9 @@ export async function renderComposite( const exportVideoPreset = normalizeExportVideoPreset(opts.exportVideoPreset); const cameraSyncOffsetMs = normalizeCameraSyncOffsetMs(opts.cameraSyncOffsetMs); const sourceWidth = Number.isFinite(Number(opts.sourceWidth)) ? Number(opts.sourceWidth) : 1920; - const sourceHeight = Number.isFinite(Number(opts.sourceHeight)) ? Number(opts.sourceHeight) : 1080; + const sourceHeight = Number.isFinite(Number(opts.sourceHeight)) + ? Number(opts.sourceHeight) + : 1080; const outputFolder = typeof opts.outputFolder === 'string' ? opts.outputFolder : ''; const probeFps = deps.probeVideoFpsWithFfmpeg || probeVideoFpsWithFfmpeg; @@ -404,51 +397,41 @@ export async function renderComposite( if (!ffmpegPath) throw new Error('ffmpeg-static is unavailable on this platform'); const outputPath = path.join(outputFolder, `recording-${now()}-edited.mp4`); - const { canvasW, canvasH } = deriveRenderCanvasSize( - sourceWidth, - sourceHeight, - exportVideoPreset, - ); + const { canvasW, canvasH } = deriveRenderCanvasSize(sourceWidth, sourceHeight, exportVideoPreset); const takeMap = new Map(); for (const take of takes) { if (!take || typeof take.id !== 'string' || !take.id) continue; takeMap.set(take.id, { screenPath: take.screenPath, - cameraPath: take.cameraPath, + cameraPath: take.cameraPath }); } - const hasCamera = keyframes.some( - (keyframe) => keyframe.pipVisible || keyframe.cameraFullscreen, - ); - const { args, fpsProbePaths, sectionInputs } = buildInputPlan( - sections, - takeMap, - hasCamera, - ); + const hasCamera = keyframes.some((keyframe) => keyframe.pipVisible || keyframe.cameraFullscreen); + const { args, fpsProbePaths, sectionInputs } = buildInputPlan(sections, takeMap, hasCamera); const totalDurationSec = getTotalDurationSec(sections); const fpsProbeResults = await Promise.all( Array.from(fpsProbePaths).map(async (filePath) => ({ filePath, - fps: await probeFps(ffmpegPath, filePath), - })), + fps: await probeFps(ffmpegPath, filePath) + })) ); const targetFps = chooseRenderFps( fpsProbeResults.map((result) => result.fps), - hasCamera, + hasCamera ); console.log( '[render-composite] FPS selection:', fpsProbeResults.map((result) => ({ file: path.basename(result.filePath), - fps: result.fps ? Number(result.fps.toFixed(3)) : null, + fps: result.fps ? Number(result.fps.toFixed(3)) : null })), 'targetFps=', - targetFps, + targetFps ); const hasImageSections = sections.some((s) => s.imagePath); @@ -460,35 +443,34 @@ export async function renderComposite( const end = section.sourceEnd.toFixed(3); if (imageIdx >= 0) { - const imageScale = screenFitMode === 'fill' - ? `scale=${canvasW}:${canvasH}:flags=lanczos:force_original_aspect_ratio=increase,crop=${canvasW}:${canvasH}` - : `scale=${canvasW}:${canvasH}:flags=lanczos:force_original_aspect_ratio=decrease,pad=${canvasW}:${canvasH}:(ow-iw)/2:(oh-ih)/2:black`; + const imageScale = + screenFitMode === 'fill' + ? `scale=${canvasW}:${canvasH}:flags=lanczos:force_original_aspect_ratio=increase,crop=${canvasW}:${canvasH}` + : `scale=${canvasW}:${canvasH}:flags=lanczos:force_original_aspect_ratio=decrease,pad=${canvasW}:${canvasH}:(ow-iw)/2:(oh-ih)/2:black`; filterParts.push( - `[${imageIdx}:v]${imageScale},format=yuv420p,setpts=PTS-STARTPTS,setsar=1[sv${index}]`, + `[${imageIdx}:v]${imageScale},format=yuv420p,setpts=PTS-STARTPTS,setsar=1[sv${index}]` ); } else if (hasImageSections) { // Pre-scale video to canvas size so concat inputs match image sections filterParts.push( - `[${screenIdx}:v]trim=start=${start}:end=${end},setpts=PTS-STARTPTS,scale=${canvasW}:${canvasH}:flags=lanczos:force_original_aspect_ratio=increase,crop=${canvasW}:${canvasH},setsar=1[sv${index}]`, + `[${screenIdx}:v]trim=start=${start}:end=${end},setpts=PTS-STARTPTS,scale=${canvasW}:${canvasH}:flags=lanczos:force_original_aspect_ratio=increase,crop=${canvasW}:${canvasH},setsar=1[sv${index}]` ); } else { filterParts.push( - `[${screenIdx}:v]trim=start=${start}:end=${end},setpts=PTS-STARTPTS,setsar=1[sv${index}]`, + `[${screenIdx}:v]trim=start=${start}:end=${end},setpts=PTS-STARTPTS,setsar=1[sv${index}]` ); } filterParts.push( - `[${screenIdx}:a]atrim=start=${start}:end=${end},asetpts=PTS-STARTPTS[sa${index}]`, + `[${screenIdx}:a]atrim=start=${start}:end=${end},asetpts=PTS-STARTPTS[sa${index}]` ); } const screenLabels = sections.map((_, index) => `[sv${index}][sa${index}]`).join(''); - filterParts.push( - `${screenLabels}concat=n=${sections.length}:v=1:a=1[screen_raw][audio_out]`, - ); + filterParts.push(`${screenLabels}concat=n=${sections.length}:v=1:a=1[screen_raw][audio_out]`); const exportAudioLabel = buildExportAudioLabel(exportAudioPreset); if (exportAudioLabel === 'audio_final') { filterParts.push( - '[audio_out]acompressor=threshold=0.125:ratio=3:attack=20:release=250:makeup=1.5[audio_final]', + '[audio_out]acompressor=threshold=0.125:ratio=3:attack=20:release=250:makeup=1.5[audio_final]' ); } @@ -499,13 +481,7 @@ export async function renderComposite( const duration = (section.sourceEnd - section.sourceStart).toFixed(3); if (cameraIdx >= 0) { filterParts.push( - buildCameraTrimFilter( - cameraIdx, - section, - targetFps, - index, - cameraSyncOffsetMs, - ), + buildCameraTrimFilter(cameraIdx, section, targetFps, index, cameraSyncOffsetMs) ); } else { filterParts.push(`color=black:s=${canvasW}x${canvasH}:d=${duration}[cv${index}]`); @@ -522,7 +498,7 @@ export async function renderComposite( sourceHeight, canvasW, canvasH, - targetFps, + targetFps ); assertOverlayFilterSize(overlayFilter); const adaptedOverlay = overlayFilter @@ -538,7 +514,7 @@ export async function renderComposite( canvasW, canvasH, '[out]', - targetFps, + targetFps ).replace(/\[0:v\]/g, '[screen_raw]'); filterParts.push(screenOnlyFilter); } @@ -550,7 +526,7 @@ export async function renderComposite( '-map', '[out_cfr]', '-map', - `[${exportAudioLabel}]`, + `[${exportAudioLabel}]` ); args.push(...buildOutputArgs(targetFps, outputPath, exportVideoPreset)); @@ -561,7 +537,7 @@ export async function renderComposite( phase: 'starting', percent: 0, status: 'Preparing render...', - durationSec: totalDurationSec, + durationSec: totalDurationSec }); } @@ -573,7 +549,7 @@ export async function renderComposite( if (!onProgress) return; const update = buildRenderProgressUpdate(progress, totalDurationSec); if (update) onProgress(update); - }, + } }); return outputPath; } catch (error) { diff --git a/src/main/services/sections-service.ts b/src/main/services/sections-service.ts index b5d0eb5..34303b2 100644 --- a/src/main/services/sections-service.ts +++ b/src/main/services/sections-service.ts @@ -4,7 +4,7 @@ export function computeSections( opts: { segments?: Array<{ start: number; end: number }>; paddingSeconds?: number; - } = {}, + } = {} ): { sections: Section[]; trimmedDuration: number } { const segments = Array.isArray(opts.segments) ? opts.segments : []; const paddingSeconds = Number.isFinite(Number(opts.paddingSeconds)) @@ -18,12 +18,9 @@ export function computeSections( const padded = segments .map((segment) => ({ start: Math.max(0, Number(segment.start) - paddingSeconds), - end: Number(segment.end) + paddingSeconds, + end: Number(segment.end) + paddingSeconds })) - .filter( - (segment) => - Number.isFinite(segment.start) && Number.isFinite(segment.end), - ) + .filter((segment) => Number.isFinite(segment.start) && Number.isFinite(segment.end)) .filter((segment) => segment.end > segment.start) .sort((left, right) => left.start - right.start); @@ -61,13 +58,13 @@ export function computeSections( label: `Section ${index + 1}`, transcript: '', takeId: null, - imagePath: null, + imagePath: null }); timelineCursor += sectionDuration; } return { sections: remapped, - trimmedDuration: remapped.length > 0 ? remapped[remapped.length - 1].end : 0, + trimmedDuration: remapped.length > 0 ? remapped[remapped.length - 1].end : 0 }; } diff --git a/src/preload.ts b/src/preload.ts index f1a98e4..288bd28 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -17,8 +17,7 @@ function toFileUrl(filePath: string | null | undefined): string { } const electronApi: ElectronApi = { - saveVideo: (buffer, folder, suffix) => - ipcRenderer.invoke('save-video', buffer, folder, suffix), + saveVideo: (buffer, folder, suffix) => ipcRenderer.invoke('save-video', buffer, folder, suffix), pickFolder: (opts) => ipcRenderer.invoke('pick-folder', opts), pickProjectLocation: (opts) => ipcRenderer.invoke('pick-project-location', opts), pathToFileUrl: (filePath) => toFileUrl(filePath), @@ -26,8 +25,7 @@ const electronApi: ElectronApi = { projectCreate: (opts) => ipcRenderer.invoke('project-create', opts), projectOpen: (projectFolder) => ipcRenderer.invoke('project-open', projectFolder), projectSave: (payload) => ipcRenderer.invoke('project-save', payload), - projectSetRecoveryTake: (payload) => - ipcRenderer.invoke('project-set-recovery-take', payload), + projectSetRecoveryTake: (payload) => ipcRenderer.invoke('project-set-recovery-take', payload), projectClearRecoveryTake: (projectFolder) => ipcRenderer.invoke('project-clear-recovery-take', projectFolder), projectCompleteRecoveryTake: (projectFolder) => @@ -55,7 +53,7 @@ const electronApi: ElectronApi = { const handler = (_event: unknown, payload: ProxyProgressUpdate) => listener(payload); ipcRenderer.on('proxy:progress', handler); return () => ipcRenderer.removeListener('proxy:progress', handler); - }, + } }; contextBridge.exposeInMainWorld('electronAPI', electronApi); diff --git a/src/renderer/app.ts b/src/renderer/app.ts index 7beba5b..dd7b0f4 100644 --- a/src/renderer/app.ts +++ b/src/renderer/app.ts @@ -39,4233 +39,4469 @@ import { } from './features/recording/recorder-utils'; import { cleanupAllMedia } from './features/media-cleanup'; - const projectHomeView = document.getElementById('projectHomeView'); - const workspaceHeader = document.getElementById('workspaceHeader'); - const newProjectNameInput = document.getElementById('newProjectName'); - const createProjectBtn = document.getElementById('createProjectBtn'); - const openProjectBtn = document.getElementById('openProjectBtn'); - const projectHomeMessage = document.getElementById('projectHomeMessage'); - const lastProjectRow = document.getElementById('lastProjectRow'); - const lastProjectName = document.getElementById('lastProjectName'); - const lastProjectPath = document.getElementById('lastProjectPath'); - const resumeLastBtn = document.getElementById('resumeLastBtn'); - const recentProjectsList = document.getElementById('recentProjectsList'); - const activeProjectNameEl = document.getElementById('activeProjectName'); - const activeProjectPathEl = document.getElementById('activeProjectPath'); - const goRecordingBtn = document.getElementById('goRecordingBtn'); - const goTimelineBtn = document.getElementById('goTimelineBtn'); - const switchProjectBtn = document.getElementById('switchProjectBtn'); - const exportAudioPresetControl = document.getElementById('exportAudioPresetControl'); - const exportAudioPresetSelect = document.getElementById('exportAudioPreset'); - const exportVideoPresetControl = document.getElementById('exportVideoPresetControl'); - const exportVideoPresetSelect = document.getElementById('exportVideoPreset'); - const cameraSyncOffsetControl = document.getElementById('cameraSyncOffsetControl'); - const cameraSyncOffsetInput = document.getElementById('cameraSyncOffsetMs'); - - const screenSelect = document.getElementById('screenSource'); - const screenFitSelect = document.getElementById('screenFit'); - const cameraSelect = document.getElementById('cameraSource'); - const audioSelect = document.getElementById('audioSource'); - const canvas = document.getElementById('compositeCanvas'); - const ctx = canvas.getContext('2d'); - const screenVideo = document.getElementById('screenVideo'); - const cameraVideo = document.getElementById('cameraVideo'); - const noPreview = document.getElementById('noPreview'); - const audioMeter = document.getElementById('audioMeter'); - const recordBtn = document.getElementById('recordBtn'); - const timerEl = document.getElementById('timer'); - const folderPathEl = document.getElementById('folderPath'); - const openFolderBtn = document.getElementById('openFolderBtn'); - const pickFolderBtn = document.getElementById('pickFolderBtn'); - const contentProtectionToggle = document.getElementById('contentProtectionToggle'); - const recordingView = document.getElementById('recordingView'); - const transcriptPanel = document.getElementById('transcriptPanel'); - const transcriptContent = document.getElementById('transcriptContent'); - const transcriptStatus = document.getElementById('transcriptStatus'); - const segmentBadge = document.getElementById('segmentBadge'); - const processingView = document.getElementById('processingView'); - const processingTitle = document.getElementById('processingTitle'); - const processingStatus = document.getElementById('processingStatus'); - const processingBar = document.getElementById('processingBar'); - - // Editor DOM refs - const editorView = document.getElementById('editorView'); - const editorCanvas = document.getElementById('editorCanvas'); - const editorCtx = editorCanvas.getContext('2d'); - const editorRenderBtn = document.getElementById('editorRenderBtn'); - const editorUndoBtn = document.getElementById('editorUndoBtn'); - const editorRedoBtn = document.getElementById('editorRedoBtn'); - const editorPlayBtn = document.getElementById('editorPlayBtn'); - const editorSplitBtn = document.getElementById('editorSplitBtn'); - const editorToggleCamBtn = document.getElementById('editorToggleCamBtn'); - const editorCamFullBtn = document.getElementById('editorCamFullBtn'); - const editorBgZoomInput = document.getElementById('editorBgZoomInput'); - const editorBgZoomValue = document.getElementById('editorBgZoomValue'); - const editorApplyFutureBtn = document.getElementById('editorApplyFutureBtn'); - const editorTimeEl = document.getElementById('editorTime'); - const editorTimelineWrapper = document.getElementById('editorTimelineWrapper'); - const editorTimeline = document.getElementById('editorTimeline'); - const editorSectionMarkers = document.getElementById('editorSectionMarkers'); - const editorScrubber = document.getElementById('editorScrubber'); - const editorCameraTrack = document.getElementById('editorCameraTrack'); - const editorCameraMarkers = document.getElementById('editorCameraMarkers'); - const cameraTrackLabel = document.getElementById('cameraTrackLabel'); - const editorSectionTranscriptList = document.getElementById('editorSectionTranscriptList'); - let editorRenderTimeout = null; - const editorWaveformCanvas = document.getElementById('editorWaveformCanvas'); - - let screenStream = null; - let cameraStream = null; - let audioStream = null; - let recorders = []; - let recording = false; - let screenRecInterval = null; - let trackEndedCleanups = []; - let timerInterval = null; - let startTime = 0; - let audioContext = null; - let analyser = null; - let meterRAF = null; - let drawRAF = null; - let lastCompositeDrawAt = 0; - let saveFolder = ''; - let hideFromRecording = 'true'; - let activeProjectPath = ''; - let activeProject = null; - let activeProjectSession = 0; - let activeWorkspaceView = 'home'; - let saveDebounceTimer = null; - let persistQueue = Promise.resolve(); - let mediaInitialized = false; - let mediaIdleTimer = null; - let scribeWs = null; - let scribeWorkletNode = null; - let speechSegments = []; - let audioChunkBuffer = []; - let audioSendInterval = null; - let scribeLastFailureReason = null; - let scribeManualClose = false; - let micSourceNode = null; - const MEDIA_IDLE_TIMEOUT_MS = 30000; - - function clearMediaIdleTimer() { - if (!mediaIdleTimer) return; - clearTimeout(mediaIdleTimer); - mediaIdleTimer = null; - } - - function resetMediaRefsAfterCleanup() { - recording = false; - screenStream = null; - cameraStream = null; - audioStream = null; - recorders = []; - screenRecInterval = null; - timerInterval = null; - audioContext = null; - analyser = null; - meterRAF = null; - drawRAF = null; - lastCompositeDrawAt = 0; - scribeWs = null; - scribeWorkletNode = null; - audioChunkBuffer = []; - audioSendInterval = null; - micSourceNode = null; - mediaInitialized = false; - screenVideo.srcObject = null; - cameraVideo.srcObject = null; - } - - function cleanupRendererMediaResources() { - cleanupAllMedia({ - recording, - screenStream, - cameraStream, - audioStream, - recorders, - screenRecInterval, - audioSendInterval, - timerInterval, - audioContext, - scribeWorkletNode, - scribeWs, - drawRAF, - meterRAF, - cancelEditorDrawLoop, - stopAudioMeter - }); - resetMediaRefsAfterCleanup(); - } - - function hasActiveRecorders() { - return recorders.some((recorder) => recorder?.state && recorder.state !== 'inactive'); - } - - function handleRenderProgress(update) { - if (!editorState || !editorState.rendering) return; - - const percent = Number.isFinite(Number(update?.percent)) - ? Math.max(0, Math.min(1, Number(update.percent))) - : null; - editorState.renderProgress = percent ?? 0; - - processingTitle.textContent = 'Rendering export...'; - processingStatus.textContent = typeof update?.status === 'string' && update.status ? update.status : 'Rendering...'; - setProcessingProgress(percent); - - if (percent === null) { - setRenderBtnState(processingStatus.textContent, 'busy'); - return; - } - - setRenderBtnState(`Rendering ${Math.round(percent * 100)}%`, 'busy'); - } - - if (typeof window.electronAPI.onRenderProgress === 'function') { - window.electronAPI.onRenderProgress((update) => { - handleRenderProgress(update); - }); - } - - if (typeof window.electronAPI.onProxyProgress === 'function') { - window.electronAPI.onProxyProgress((payload) => { - if (!payload || !payload.takeId) return; - if (payload.status === 'progress') { - const current = proxyStatus.get(payload.takeId); - if (current && current.status === 'pending') { - current.percent = payload.percent || 0; - updateProxyProgressBars(payload.takeId, current.percent); - } - } else if (payload.status === 'done' && payload.proxyPath) { - proxyStatus.set(payload.takeId, { status: 'done', percent: 1 }); - const take = activeProject?.takes?.find(t => t.id === payload.takeId); - if (take) { - take.proxyPath = payload.proxyPath; - persistProjectNow().catch(err => console.warn('[Proxy] Failed to persist proxyPath:', err)); - } - // Hot-swap the cached video element to use the proxy - const cached = takeVideoPool.get(payload.takeId); - if (cached) { - const wasPlaying = !cached.screen.paused; - const currentTime = cached.screen.currentTime; - const rate = cached.screen.playbackRate; - cached.screen.src = pathToFileUrl(payload.proxyPath); - cached.screen.addEventListener('loadedmetadata', () => { - cached.screen.currentTime = currentTime; - if (wasPlaying) { - cached.screen.playbackRate = rate; - cached.screen.play().catch(() => {}); - if (!hasPendingEditorDraw()) scheduleEditorDrawLoop(); - } - }, { once: true }); - } - renderSectionMarkers(); - } else if (payload.status === 'error') { - proxyStatus.set(payload.takeId, { status: 'error', percent: 0 }); - console.warn('[Proxy] Generation failed for take', payload.takeId, payload.error); - renderSectionMarkers(); - } - }); - } - let scribeAudioOffset = 0; // seconds between recording start and first audio sent to Scribe - let workletRegistered = null; // tracks which AudioContext has the worklet registered - - const CANVAS_W = 1920; - const CANVAS_H = 1080; - const PIP_FRACTION = 0.22; - const PIP_MARGIN = 20; - const PIP_SIZE = Math.round(CANVAS_W * PIP_FRACTION); - const MIN_SECTION_ZOOM = 1; - const MAX_SECTION_ZOOM = 3; - const DEFAULT_SECTION_ZOOM = 1; - const MIN_SECTION_PAN = -1; - const MAX_SECTION_PAN = 1; - const EXPORT_AUDIO_PRESET_OFF = 'off'; - const EXPORT_AUDIO_PRESET_COMPRESSED = 'compressed'; - const EXPORT_VIDEO_PRESET_FAST = 'fast'; - const EXPORT_VIDEO_PRESET_QUALITY = 'quality'; - - function snapToNearestCorner(cursorX, cursorY) { - const midX = CANVAS_W / 2; - const midY = CANVAS_H / 2; - return { - x: cursorX < midX ? PIP_MARGIN : CANVAS_W - PIP_SIZE - PIP_MARGIN, - y: cursorY < midY ? PIP_MARGIN : CANVAS_H - PIP_SIZE - PIP_MARGIN - }; - } - - function clampSectionZoom(value) { - const zoom = Number(value); - if (!Number.isFinite(zoom)) return DEFAULT_SECTION_ZOOM; - return Math.max(MIN_SECTION_ZOOM, Math.min(MAX_SECTION_ZOOM, zoom)); - } - - function formatSectionZoom(value) { - return `${clampSectionZoom(value).toFixed(2)}x`; - } - - function clampSectionPan(value) { - const pan = Number(value); - if (!Number.isFinite(pan)) return 0; - return Math.max(MIN_SECTION_PAN, Math.min(MAX_SECTION_PAN, pan)); - } - - function normalizeExportAudioPreset(value) { - return value === EXPORT_AUDIO_PRESET_OFF - ? EXPORT_AUDIO_PRESET_OFF - : EXPORT_AUDIO_PRESET_COMPRESSED; - } - - function normalizeExportVideoPreset(value) { - return value === EXPORT_VIDEO_PRESET_FAST - ? EXPORT_VIDEO_PRESET_FAST - : EXPORT_VIDEO_PRESET_QUALITY; - } - - function getZoomCropBounds(zoom) { - const clampedZoom = clampSectionZoom(zoom); - const sourceW = CANVAS_W / clampedZoom; - const sourceH = CANVAS_H / clampedZoom; - return { - sourceW, - sourceH, - maxOffsetX: Math.max(0, (CANVAS_W - sourceW) / 2), - maxOffsetY: Math.max(0, (CANVAS_H - sourceH) / 2) - }; - } - - function resolveZoomCrop(zoom, panX = 0, panY = 0) { - const { sourceW, sourceH, maxOffsetX, maxOffsetY } = getZoomCropBounds(zoom); - return { - sourceW, - sourceH, - sourceX: maxOffsetX + clampSectionPan(panX) * maxOffsetX, - sourceY: maxOffsetY + clampSectionPan(panY) * maxOffsetY, - maxOffsetX, - maxOffsetY - }; - } - - function panToFocusCoord(zoom, pan, defaultCoord = 0.5) { - const normalizedZoom = clampSectionZoom(zoom); - if (normalizedZoom <= 1.0001) return defaultCoord; - const cropFraction = 1 / normalizedZoom; - return cropFraction / 2 + ((clampSectionPan(pan) + 1) / 2) * (1 - cropFraction); - } - - function focusToPanCoord(zoom, focus, defaultPan = 0) { - const normalizedZoom = clampSectionZoom(zoom); - if (normalizedZoom <= 1.0001) return defaultPan; - const cropFraction = 1 / normalizedZoom; - const availableFraction = 1 - cropFraction; - if (availableFraction <= 0.000001) return defaultPan; - return clampSectionPan((((focus - cropFraction / 2) / availableFraction) * 2) - 1); - } - - const TRANSITION_DURATION = 0.3; - const CAMERA_DRIFT_SOFT_THRESHOLD = 0.015; - const CAMERA_DRIFT_HARD_THRESHOLD = 0.18; - const CAMERA_DRIFT_LOG_THRESHOLD = 0.08; - const CAMERA_DRIFT_LOG_INTERVAL_MS = 1000; - const CAMERA_RESYNC_COOLDOWN_MS = 500; - - canvas.width = CANVAS_W; - canvas.height = CANVAS_H; - editorCanvas.width = CANVAS_W; - editorCanvas.height = CANVAS_H; - - // ===== Editor State ===== - const undoStack = []; - const redoStack = []; - const MAX_UNDO = 50; - let editorState = null; - let editorDrawRAF = null; - let editorPausedDrawTimer = null; - let editorVideoFrameCallbackId = null; - let editorVideoFrameHost = null; - let editorVideoFrameSafetyTimer = null; - let draggingPip = false; - let pipDragMoved = false; - let waveformPeaks = null; - let timelineZoom = 1; - let trimDragState = null; - let sectionDragState = null; - let sectionZoomDragActive = false; - let draggingBackground = false; - let backgroundDragMoved = false; - let backgroundDragState = null; - let takeAudioBufferCache = new Map(); // takeId -> AudioBuffer - let takeVideoPool = new Map(); // takeId -> { screen: HTMLVideoElement, camera: HTMLVideoElement|null } - const proxyStatus = new Map(); // takeId -> { status: 'pending'|'done'|'error', percent: number } - let activeTakeId = null; - let activePlaybackSection = null; - let cameraResyncCooldownUntil = 0; - let lastCameraDriftLogAt = 0; - const editorZoomBuffer = document.createElement('canvas'); - editorZoomBuffer.width = CANVAS_W; - editorZoomBuffer.height = CANVAS_H; - const editorZoomBufferCtx = editorZoomBuffer.getContext('2d'); - - const sectionImageCache = new Map(); - - function loadSectionImage(imagePath) { - if (!imagePath) return Promise.resolve(null); - if (sectionImageCache.has(imagePath)) return Promise.resolve(sectionImageCache.get(imagePath)); - return new Promise((resolve) => { - const img = new Image(); - img.onload = () => { sectionImageCache.set(imagePath, img); resolve(img); }; - img.onerror = () => { console.warn('Failed to load section image:', imagePath); resolve(null); }; - img.src = pathToFileUrl(imagePath); - }); - } - - function preloadSectionImages() { - if (!editorState?.sections) return; - for (const section of editorState.sections) { - if (section.imagePath) loadSectionImage(section.imagePath); - } - } - - function getOrCreateTakeVideos(takeId) { - if (takeVideoPool.has(takeId)) return takeVideoPool.get(takeId); - const take = activeProject?.takes?.find(t => t.id === takeId); - if (!take) return null; - const screen = document.createElement('video'); - screen.playsInline = true; - screen.preload = 'auto'; - screen.src = pathToFileUrl(take.proxyPath || take.screenPath); - let camera = null; - if (take.cameraPath) { - camera = document.createElement('video'); - camera.playsInline = true; - camera.muted = true; - camera.preload = 'auto'; - camera.src = pathToFileUrl(take.cameraPath); - } - const entry = { screen, camera }; - takeVideoPool.set(takeId, entry); - return entry; - } - - function cleanupVideoPool() { - for (const [, videos] of takeVideoPool) { - videos.screen.pause(); - videos.screen.src = ''; - if (videos.camera) { - videos.camera.pause(); - videos.camera.src = ''; - } - } - takeVideoPool.clear(); - takeAudioBufferCache.clear(); - activeTakeId = null; - activePlaybackSection = null; - } - - function resolveTimeToSource(timelineTime) { - const section = findSectionForTime(timelineTime); - if (!section) return null; - const sourceTime = section.sourceStart + (timelineTime - section.start); - return { takeId: section.takeId, sourceTime, section }; - } - - function recalculateTimelinePositions() { - if (!editorState || !editorState.sections) return; - let cursor = 0; - for (const section of editorState.sections) { - const duration = section.sourceEnd - section.sourceStart; - section.start = roundMs(cursor); - section.end = roundMs(cursor + duration); - section.duration = roundMs(duration); - cursor += duration; - } - editorState.duration = cursor; - } - - function easeInOut(t) { - return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2; - } - - function getActiveProjectSession() { - return { - id: activeProjectSession, - projectPath: activeProjectPath - }; - } - - function matchesActiveProjectSession(session) { - return !!session - && session.id === activeProjectSession - && session.projectPath === activeProjectPath; - } - - function pathToFileUrl(filePath) { - if (!filePath) return ''; - return window.electronAPI.pathToFileUrl(filePath); - } - - function formatProjectDate(value) { - if (!value) return ''; - const dt = new Date(value); - if (Number.isNaN(dt.getTime())) return ''; - return dt.toLocaleString(); - } - - function setToggleButtonState(button, active, disabled) { - button.disabled = !!disabled; - button.className = `px-3 py-1.5 rounded-md text-sm transition-colors ${active ? 'bg-white text-neutral-950 font-medium' : 'text-neutral-400 hover:text-neutral-200 hover:bg-neutral-800'} disabled:opacity-40 disabled:cursor-not-allowed`; - } - - function updateWorkspaceHeader() { - const hasProject = !!activeProjectPath; - const showTimelineTools = hasProject && activeWorkspaceView === 'timeline'; - activeProjectNameEl.textContent = activeProject?.name || 'Project'; - activeProjectPathEl.textContent = activeProjectPath || ''; - workspaceHeader.classList.toggle('hidden', !hasProject || activeWorkspaceView === 'home'); - setToggleButtonState(goRecordingBtn, activeWorkspaceView === 'recording', !hasProject || activeWorkspaceView === 'processing'); - setToggleButtonState(goTimelineBtn, activeWorkspaceView === 'timeline', !hasProject || !editorState || activeWorkspaceView === 'processing' || recording); - recordBtn.classList.toggle('hidden', activeWorkspaceView !== 'recording'); - timerEl.classList.toggle('hidden', activeWorkspaceView !== 'recording'); - cameraSyncOffsetControl.classList.toggle('hidden', !showTimelineTools); - cameraSyncOffsetControl.classList.toggle('flex', showTimelineTools); - exportAudioPresetControl.classList.toggle('hidden', !showTimelineTools); - exportAudioPresetControl.classList.toggle('flex', showTimelineTools); - exportVideoPresetControl.classList.toggle('hidden', !showTimelineTools); - exportVideoPresetControl.classList.toggle('flex', showTimelineTools); - editorRenderBtn.classList.toggle('hidden', !showTimelineTools); - } - - function hasPendingEditorDraw() { - return !!editorDrawRAF || !!editorPausedDrawTimer || editorVideoFrameCallbackId !== null; - } - - function cancelEditorDrawLoop() { - if (editorDrawRAF) { - cancelAnimationFrame(editorDrawRAF); - editorDrawRAF = null; - } - if (editorPausedDrawTimer) { - clearTimeout(editorPausedDrawTimer); - editorPausedDrawTimer = null; - } - if (editorVideoFrameHost && editorVideoFrameCallbackId !== null - && typeof editorVideoFrameHost.cancelVideoFrameCallback === 'function') { - try { - editorVideoFrameHost.cancelVideoFrameCallback(editorVideoFrameCallbackId); - } catch (_error) { - // Ignore cancellation races while switching active takes. - } - } - editorVideoFrameCallbackId = null; - editorVideoFrameHost = null; - if (editorVideoFrameSafetyTimer) { - clearTimeout(editorVideoFrameSafetyTimer); - editorVideoFrameSafetyTimer = null; - } - } - - function scheduleEditorDrawLoop() { - if (!editorState || activeWorkspaceView !== 'timeline') return; - - if (editorState.playing && activeTakeId) { - const videos = getOrCreateTakeVideos(activeTakeId); - const screen = videos?.screen; - if (screen && typeof screen.requestVideoFrameCallback === 'function') { - editorVideoFrameHost = screen; - editorVideoFrameCallbackId = screen.requestVideoFrameCallback(() => { - if (editorVideoFrameSafetyTimer) { clearTimeout(editorVideoFrameSafetyTimer); editorVideoFrameSafetyTimer = null; } - editorVideoFrameCallbackId = null; - editorVideoFrameHost = null; - editorDrawLoop(); - }); - // Safety fallback: if the video frame callback doesn't fire within - // 200 ms (e.g. src was swapped or video stalled), fall back to rAF - editorVideoFrameSafetyTimer = setTimeout(() => { - editorVideoFrameSafetyTimer = null; - if (editorVideoFrameCallbackId !== null && editorState?.playing) { - cancelEditorDrawLoop(); - editorDrawRAF = requestAnimationFrame(() => { - editorDrawRAF = null; - editorDrawLoop(); - }); - } - }, 200); - return; - } - } - - if (editorState.playing) { - editorDrawRAF = requestAnimationFrame(() => { - editorDrawRAF = null; - editorDrawLoop(); - }); - return; - } - - editorPausedDrawTimer = setTimeout(() => { - editorPausedDrawTimer = null; - editorDrawRAF = requestAnimationFrame(() => { - editorDrawRAF = null; - editorDrawLoop(); - }); - }, Math.round(1000 / 24)); - } - - function setWorkspaceView(nextView) { - activeWorkspaceView = nextView; - const showHome = nextView === 'home'; - const showRecording = nextView === 'recording'; - const showTimeline = nextView === 'timeline' && !!editorState; - const showProcessing = nextView === 'processing'; - - projectHomeView.classList.toggle('hidden', !showHome); - recordingView.classList.toggle('hidden', !showRecording); - editorView.classList.toggle('hidden', !showTimeline); - processingView.classList.toggle('hidden', !showProcessing); - - if (showRecording) { - clearMediaIdleTimer(); - if (!mediaInitialized) void ensureMediaInitialized(); - updatePreview(); - } else if (drawRAF) { - cancelAnimationFrame(drawRAF); - drawRAF = null; - lastCompositeDrawAt = 0; - } - - if (!showRecording && mediaInitialized && !recording && !mediaIdleTimer) { - mediaIdleTimer = setTimeout(() => { - mediaIdleTimer = null; - if (hasActiveRecorders()) return; - cleanupRendererMediaResources(); - }, MEDIA_IDLE_TIMEOUT_MS); - } - - if (showTimeline && editorState && !hasPendingEditorDraw()) { - editorDrawLoop(); - } else if (!showTimeline && hasPendingEditorDraw()) { - cancelEditorDrawLoop(); - if (editorState?.playing) editorPause(); - } - - updateWorkspaceHeader(); - } - - function getProjectTimelineSnapshot() { - if (!editorState) { - return activeProject?.timeline || { - duration: 0, - sections: [], - keyframes: [], - selectedSectionId: null, - hasCamera: false, - sourceWidth: null, - sourceHeight: null - }; - } - - return { - duration: Number(editorState.duration) || 0, - sections: Array.isArray(editorState.sections) ? editorState.sections.map(section => ({ ...section })) : [], - keyframes: Array.isArray(editorState.keyframes) - ? editorState.keyframes.map(kf => ({ - ...kf, - backgroundZoom: clampSectionZoom(kf.backgroundZoom), - backgroundPanX: clampSectionPan(kf.backgroundPanX), - backgroundPanY: clampSectionPan(kf.backgroundPanY) - })) - : [], - selectedSectionId: editorState.selectedSectionId || null, - hasCamera: !!editorState.hasCamera, - sourceWidth: editorState.sourceWidth || null, - sourceHeight: editorState.sourceHeight || null - }; - } - - function buildProjectSavePayload() { - if (!activeProject) return null; - return { - ...activeProject, - settings: { - screenFitMode: screenFitSelect.value || 'fill', - hideFromRecording: hideFromRecording === 'true', - exportAudioPreset: normalizeExportAudioPreset(exportAudioPresetSelect.value), - exportVideoPreset: normalizeExportVideoPreset(exportVideoPresetSelect.value), - cameraSyncOffsetMs: normalizeCameraSyncOffsetMs(cameraSyncOffsetInput.value) - }, - timeline: getProjectTimelineSnapshot() - }; - } - - async function persistProjectNow() { - if (!activeProjectPath || !activeProject) return; - const expectedProjectPath = activeProjectPath; - const payload = buildProjectSavePayload(); - if (!payload) return; - - persistQueue = persistQueue.then(async () => { - const result = await window.electronAPI.projectSave({ - projectPath: expectedProjectPath, - project: payload - }); - if (result?.projectPath && result?.project && activeProjectPath === expectedProjectPath) { - activeProjectPath = result.projectPath; - activeProject = result.project; - saveFolder = activeProjectPath; - folderPathEl.textContent = activeProjectPath; - openFolderBtn.classList.toggle('hidden', !activeProjectPath); - updateWorkspaceHeader(); - await window.electronAPI.projectSetLast(expectedProjectPath); - } - }).catch((error) => { - console.error('Failed to persist project:', error); - }); - - await persistQueue; - } - - async function saveRecoveryTake(take) { - if (!activeProjectPath || !take?.screenPath) return; - try { - await window.electronAPI.projectSetRecoveryTake({ - projectPath: activeProjectPath, - take - }); - } catch (error) { - console.error('Failed to save recovery take:', error); - } - } - - async function _clearRecoveryTake(projectPath = activeProjectPath) { - if (!projectPath) return; - try { - await window.electronAPI.projectClearRecoveryTake(projectPath); - } catch (error) { - console.error('Failed to clear recovery take:', error); - } - } - - async function completeRecoveryTake(projectPath = activeProjectPath) { - if (!projectPath) return; - try { - await window.electronAPI.projectCompleteRecoveryTake(projectPath); - } catch (error) { - console.error('Failed to finalize recovery take:', error); - } - } - - function scheduleProjectSave() { - if (!activeProjectPath || !activeProject) return; - if (saveDebounceTimer) clearTimeout(saveDebounceTimer); - saveDebounceTimer = setTimeout(() => { - persistProjectNow().catch((error) => { - console.error('Failed to save project:', error); - }); - }, 250); - } - - async function flushScheduledProjectSave() { - if (saveDebounceTimer) { - clearTimeout(saveDebounceTimer); - saveDebounceTimer = null; - } - await persistProjectNow(); - } - - function clearEditorState() { - editorPause(); - cancelEditorDrawLoop(); - - cleanupVideoPool(); - editorState = null; - proxyStatus.clear(); - undoStack.length = 0; - redoStack.length = 0; - waveformPeaks = null; - renderWaveform(); - renderSectionMarkers(); - updateSectionZoomControls(); - updateWorkspaceHeader(); - updateUndoRedoButtons(); - } - - function snapshotTimeline() { - return { - sections: editorState.sections.map(s => ({ ...s })), - keyframes: editorState.keyframes.map(kf => ({ ...kf })), - selectedSectionId: editorState.selectedSectionId, - selectedSectionIds: new Set(editorState.selectedSectionIds || []), - duration: editorState.duration - }; - } - - function restoreSnapshot(snapshot) { - editorState.sections = snapshot.sections; - editorState.keyframes = snapshot.keyframes; - editorState.selectedSectionId = snapshot.selectedSectionId; - editorState.selectedSectionIds = snapshot.selectedSectionIds || new Set([snapshot.selectedSectionId].filter(Boolean)); - editorState.duration = snapshot.duration; - recalculateTimelinePositions(); - syncSectionAnchorKeyframes(); - renderSectionMarkers(); - updateSectionZoomControls(); - refreshWaveform(); - editorSeek(Math.min(editorState.currentTime, editorState.duration)); - updateUndoRedoButtons(); - scheduleProjectSave(); - } - - function pushUndo() { - if (!editorState) return; - undoStack.push(snapshotTimeline()); - if (undoStack.length > MAX_UNDO) undoStack.shift(); - redoStack.length = 0; - updateUndoRedoButtons(); - } - - function editorUndo() { - if (!editorState || editorState.rendering || undoStack.length === 0) return; - redoStack.push(snapshotTimeline()); - restoreSnapshot(undoStack.pop()); - } - - function editorRedo() { - if (!editorState || editorState.rendering || redoStack.length === 0) return; - undoStack.push(snapshotTimeline()); - restoreSnapshot(redoStack.pop()); - } - - function updateUndoRedoButtons() { - editorUndoBtn.disabled = undoStack.length === 0; - editorRedoBtn.disabled = redoStack.length === 0; - } - - function renderRecentProjects(meta) { - const projects = Array.isArray(meta?.projects) ? meta.projects : []; - const lastPath = meta?.lastProjectPath || ''; - - recentProjectsList.innerHTML = ''; - if (projects.length === 0) { - const empty = document.createElement('div'); - empty.className = 'text-sm text-neutral-600 py-2'; - empty.textContent = 'No recent projects yet.'; - recentProjectsList.appendChild(empty); - } else { - for (const project of projects) { - const btn = document.createElement('button'); - btn.className = 'w-full text-left bg-neutral-900 border border-neutral-800 rounded-lg px-3.5 py-2.5 hover:bg-neutral-800 hover:border-neutral-700 transition-all'; - btn.type = 'button'; - btn.dataset.projectPath = project.projectPath; - - const title = document.createElement('div'); - title.className = 'text-sm text-neutral-100 truncate font-medium'; - title.textContent = project.name || 'Untitled Project'; - const subtitle = document.createElement('div'); - subtitle.className = 'text-xs text-neutral-500 truncate mt-0.5'; - subtitle.textContent = `${project.projectPath} • ${formatProjectDate(project.updatedAt)}`; - btn.appendChild(title); - btn.appendChild(subtitle); - recentProjectsList.appendChild(btn); - } - } - - const last = projects.find(project => project.projectPath === lastPath) || projects[0]; - if (last) { - lastProjectName.textContent = last.name || 'Untitled Project'; - lastProjectPath.textContent = last.projectPath; - resumeLastBtn.dataset.projectPath = last.projectPath; - lastProjectRow.classList.remove('hidden'); - } else { - lastProjectRow.classList.add('hidden'); - resumeLastBtn.dataset.projectPath = ''; - } - } - - function clearProjectHomeMessage() { - projectHomeMessage.textContent = ''; - projectHomeMessage.className = 'hidden rounded border px-3 py-2 text-sm'; - } - - function showProjectHomeMessage(message, tone = 'error') { - if (!message) { - clearProjectHomeMessage(); - return; - } - - const toneClass = tone === 'info' - ? 'border-blue-500/40 bg-blue-500/10 text-blue-200' - : 'border-red-500/40 bg-red-500/10 text-red-200'; - - projectHomeMessage.textContent = message; - projectHomeMessage.className = `rounded border px-3 py-2 text-sm ${toneClass}`; - } - - async function refreshRecentProjects() { - try { - const recent = await window.electronAPI.projectListRecent(8); - renderRecentProjects(recent || {}); - } catch (error) { - console.error('Failed to list recent projects:', error); - renderRecentProjects({ projects: [], lastProjectPath: null }); - } - } - - async function activateProject(projectPath, project, preferredView = 'recording') { - if (!projectPath || !project) return; - - await flushScheduledProjectSave(); - clearEditorState(); - activeProjectSession += 1; - - activeProjectPath = projectPath; - activeProject = project; - saveFolder = projectPath; - folderPathEl.textContent = projectPath; - openFolderBtn.classList.remove('hidden'); - screenFitSelect.value = project.settings?.screenFitMode === 'fit' ? 'fit' : 'fill'; - hideFromRecording = project.settings?.hideFromRecording === false ? 'false' : 'true'; - exportAudioPresetSelect.value = normalizeExportAudioPreset(project.settings?.exportAudioPreset); - exportVideoPresetSelect.value = normalizeExportVideoPreset(project.settings?.exportVideoPreset); - cameraSyncOffsetInput.value = String(normalizeCameraSyncOffsetMs(project.settings?.cameraSyncOffsetMs)); - await syncContentProtection(); - - if (project.timeline && Array.isArray(project.timeline.sections) && project.timeline.sections.length > 0) { - enterEditor( - project.timeline.sections, - { - duration: project.timeline.duration || 0, - keyframes: project.timeline.keyframes || [], - selectedSectionId: project.timeline.selectedSectionId || null, - hasCamera: !!project.timeline.hasCamera, - sourceWidth: project.timeline.sourceWidth || null, - sourceHeight: project.timeline.sourceHeight || null, - cameraSyncOffsetMs: project.settings?.cameraSyncOffsetMs, - initialView: preferredView === 'recording' ? 'recording' : 'timeline' - } - ); - } else { - setWorkspaceView('recording'); - } - - await window.electronAPI.projectSetLast(projectPath); - updateWorkspaceHeader(); - - // Queue background proxy generation for any takes missing a proxy - if (Array.isArray(project.takes)) { - let needsMarkerUpdate = false; - for (const take of project.takes) { - if (!take.proxyPath && take.screenPath) { - proxyStatus.set(take.id, { status: 'pending', percent: 0 }); - needsMarkerUpdate = true; - window.electronAPI.generateProxy({ - takeId: take.id, - screenPath: take.screenPath, - projectFolder: projectPath, - durationSec: take.duration || 0, - }).catch(err => console.warn('[Proxy] Failed to start proxy generation:', err)); - } - } - if (needsMarkerUpdate) renderSectionMarkers(); - } - } - - async function ensureMediaInitialized() { - if (mediaInitialized) return; - mediaInitialized = true; - await enumerateDevices(); - try { await updateScreenStream(); } catch (error) { console.warn('Screen source init failed:', error); } - try { await updateCameraStream(); } catch (error) { console.warn('Camera source init failed:', error); } - try { await updateAudioStream(); } catch (error) { console.warn('Audio source init failed:', error); } - if (activeWorkspaceView === 'recording') updatePreview(); - } - - async function syncContentProtection() { - const enabled = hideFromRecording === 'true'; - contentProtectionToggle.checked = enabled; - - try { - await window.electronAPI.setContentProtection(enabled); - } catch (error) { - console.error('Failed to update content protection:', error); - } - } - - function findSectionForTime(time) { - if (!editorState || !editorState.sections || editorState.sections.length === 0) return null; - const sections = editorState.sections; - for (let i = 0; i < sections.length; i++) { - const section = sections[i]; - const isLast = i === sections.length - 1; - if (time >= section.start && (time < section.end || (isLast && time <= section.end + 0.001))) { - return section; - } - } - if (time < sections[0].start) return sections[0]; - return sections[sections.length - 1]; - } - - function getSelectedSection() { - if (!editorState || !editorState.sections || editorState.sections.length === 0) return null; - return editorState.sections.find(section => section.id === editorState.selectedSectionId) || editorState.sections[0]; - } - - function getSectionBackgroundZoom(sectionId) { - if (!editorState || !sectionId) return DEFAULT_SECTION_ZOOM; - const anchor = editorState.keyframes.find(kf => kf.sectionId === sectionId); - return clampSectionZoom(anchor?.backgroundZoom); - } - - function getSectionBackgroundPan(sectionId) { - if (!editorState || !sectionId) return { x: 0, y: 0 }; - const anchor = editorState.keyframes.find(kf => kf.sectionId === sectionId); - return { - x: clampSectionPan(anchor?.backgroundPanX), - y: clampSectionPan(anchor?.backgroundPanY) - }; - } - - function updateSectionZoomControls() { - if (!editorBgZoomInput || !editorBgZoomValue) return; - const selectedSection = getSelectedSection(); - const disabled = !editorState || editorState.rendering || !selectedSection; - const zoom = selectedSection ? getSectionBackgroundZoom(selectedSection.id) : DEFAULT_SECTION_ZOOM; - editorBgZoomInput.disabled = disabled; - editorBgZoomInput.value = String(zoom); - editorBgZoomValue.textContent = formatSectionZoom(zoom); - } - - function getSectionAnchorKeyframe(sectionId, createIfMissing) { - if (!editorState || !sectionId) return null; - - let anchor = editorState.keyframes.find(kf => kf.sectionId === sectionId); - if (anchor || !createIfMissing) return anchor || null; - - const section = editorState.sections.find(s => s.id === sectionId); - if (!section) return null; - - const fallback = getStateAtTime(section.start); - anchor = { - time: section.start, - pipX: fallback.pipX, - pipY: fallback.pipY, - pipVisible: fallback.pipVisible, - cameraFullscreen: fallback.cameraFullscreen || false, - backgroundZoom: clampSectionZoom(fallback.backgroundZoom), - backgroundPanX: clampSectionPan(fallback.backgroundPanX), - backgroundPanY: clampSectionPan(fallback.backgroundPanY), - sectionId: section.id, - autoSection: true - }; - editorState.keyframes.push(anchor); - editorState.keyframes.sort((a, b) => a.time - b.time); - return anchor; - } - - function syncSectionAnchorKeyframes() { - if (!editorState || !editorState.sections || editorState.sections.length === 0) return; - - const manual = editorState.keyframes - .filter(kf => !kf.sectionId) - .map(kf => ({ - ...kf, - backgroundZoom: clampSectionZoom(kf.backgroundZoom), - backgroundPanX: clampSectionPan(kf.backgroundPanX), - backgroundPanY: clampSectionPan(kf.backgroundPanY) - })); - const sectionAnchors = editorState.sections.map((section) => { - const existing = editorState.keyframes.find(kf => kf.sectionId === section.id); - return { - time: section.start, - pipX: existing ? existing.pipX : editorState.defaultPipX, - pipY: existing ? existing.pipY : editorState.defaultPipY, - pipVisible: existing ? existing.pipVisible : true, - cameraFullscreen: existing ? !!existing.cameraFullscreen : false, - backgroundZoom: existing ? clampSectionZoom(existing.backgroundZoom) : DEFAULT_SECTION_ZOOM, - backgroundPanX: existing ? clampSectionPan(existing.backgroundPanX) : 0, - backgroundPanY: existing ? clampSectionPan(existing.backgroundPanY) : 0, - sectionId: section.id, - autoSection: true - }; - }); - - editorState.keyframes = [...sectionAnchors, ...manual].sort((a, b) => a.time - b.time); - - if (!editorState.sections.some(section => section.id === editorState.selectedSectionId)) { - editorState.selectedSectionId = editorState.sections[0].id; - } - } - - function selectEditorSection(sectionId, shiftKey) { - if (!editorState || !editorState.sections || editorState.sections.length === 0) return; - if (!editorState.sections.some(section => section.id === sectionId)) return; - commitSectionZoomChange(); - - if (shiftKey && editorState.selectedSectionId) { - const anchorIndex = editorState.sections.findIndex(s => s.id === editorState.selectedSectionId); - const targetIndex = editorState.sections.findIndex(s => s.id === sectionId); - if (anchorIndex >= 0 && targetIndex >= 0) { - const lo = Math.min(anchorIndex, targetIndex); - const hi = Math.max(anchorIndex, targetIndex); - editorState.selectedSectionIds = new Set( - editorState.sections.slice(lo, hi + 1).map(s => s.id) - ); - } - } else { - editorState.selectedSectionId = sectionId; - editorState.selectedSectionIds = new Set([sectionId]); - } - - renderSectionMarkers(); - updateSectionZoomControls(); - updateEditorTimeDisplay(); - scheduleProjectSave(); - } - - function applyStyleToFutureSections() { - if (!editorState || !editorState.sections || editorState.sections.length === 0) return; - - const currentSection = getSelectedSection(); - if (!currentSection) return; - - const currentAnchor = getSectionAnchorKeyframe(currentSection.id, true); - if (!currentAnchor) return; - - const currentIndex = editorState.sections.findIndex(s => s.id === currentSection.id); - const futureSections = editorState.sections.slice(currentIndex + 1); - if (futureSections.length === 0) return; - - pushUndo(); - - for (const section of futureSections) { - const anchor = getSectionAnchorKeyframe(section.id, true); - if (!anchor) continue; - anchor.pipX = currentAnchor.pipX; - anchor.pipY = currentAnchor.pipY; - anchor.pipVisible = currentAnchor.pipVisible; - anchor.cameraFullscreen = currentAnchor.cameraFullscreen; - anchor.backgroundZoom = clampSectionZoom(currentAnchor.backgroundZoom); - anchor.backgroundPanX = clampSectionPan(currentAnchor.backgroundPanX); - anchor.backgroundPanY = clampSectionPan(currentAnchor.backgroundPanY); - } - - renderSectionMarkers(); - updateSectionZoomControls(); - editorSeek(editorState.currentTime); - updateEditorTimeDisplay(); - scheduleProjectSave(); - } - - function _deleteNearestKeyframe() { - if (!editorState || !Array.isArray(editorState.keyframes)) return; - - const manualKeyframes = editorState.keyframes.filter(kf => !kf.sectionId); - if (manualKeyframes.length === 0) return; - - const currentTime = Number(editorState.currentTime) || 0; - let nearest = manualKeyframes[0]; - let nearestDistance = Math.abs((Number(nearest.time) || 0) - currentTime); - - for (let i = 1; i < manualKeyframes.length; i++) { - const candidate = manualKeyframes[i]; - const distance = Math.abs((Number(candidate.time) || 0) - currentTime); - if (distance < nearestDistance) { - nearest = candidate; - nearestDistance = distance; - } - } - - const nearestTime = Number(nearest.time) || 0; - editorState.keyframes = editorState.keyframes.filter((kf) => { - if (kf.sectionId) return true; - const time = Number(kf.time) || 0; - return time !== nearestTime; - }); - - renderSectionMarkers(); - editorSeek(currentTime); - updateEditorTimeDisplay(); - scheduleProjectSave(); - } - - function renderSectionTranscriptList() { - if (!editorState || !editorState.sections || editorState.sections.length === 0) { - editorSectionTranscriptList.innerHTML = '
No sections available.
'; - return; - } - - editorSectionTranscriptList.innerHTML = ''; - for (const section of editorState.sections) { - const inSelection = editorState.selectedSectionIds?.has(section.id); - const selected = inSelection || section.id === editorState.selectedSectionId; - const transcript = normalizeTranscriptText(section.transcript); - - const row = document.createElement('button'); - row.type = 'button'; - row.dataset.sectionId = section.id; - row.className = `w-full text-left rounded-lg px-3 py-2 transition-all ${selected ? 'bg-neutral-800' : 'hover:bg-neutral-900'}`; - - const meta = document.createElement('div'); - meta.className = 'text-xs text-neutral-500 font-mono tabular-nums'; - meta.textContent = `${section.label} (${formatTime(section.start)} - ${formatTime(section.end)})`; - - const text = document.createElement('div'); - text.className = `mt-1 text-sm leading-snug ${transcript ? 'text-neutral-300' : 'text-neutral-600 italic'}`; - text.textContent = transcript || 'No transcript captured for this section.'; - - row.appendChild(meta); - row.appendChild(text); - row.addEventListener('click', () => { - selectEditorSection(section.id); - }); - - editorSectionTranscriptList.appendChild(row); - } - } - - function computeWaveformPeaksFromCache(numBuckets = 800) { - if (!editorState || !editorState.sections || editorState.sections.length === 0) return null; - const totalDuration = editorState.duration; - if (totalDuration <= 0) return null; - - const peaks = new Float32Array(numBuckets); - for (let bucket = 0; bucket < numBuckets; bucket++) { - const bucketStart = (bucket / numBuckets) * totalDuration; - const bucketEnd = ((bucket + 1) / numBuckets) * totalDuration; - let maxPeak = 0; - - for (const section of editorState.sections) { - if (bucketEnd <= section.start || bucketStart >= section.end) continue; - const overlapStart = Math.max(bucketStart, section.start); - const overlapEnd = Math.min(bucketEnd, section.end); - const sourceStart = section.sourceStart + (overlapStart - section.start); - const sourceEnd = section.sourceStart + (overlapEnd - section.start); - - const audioBuffer = takeAudioBufferCache.get(section.takeId); - if (!audioBuffer) continue; - const channelData = audioBuffer.getChannelData(0); - const sampleRate = audioBuffer.sampleRate; - const startSample = Math.floor(sourceStart * sampleRate); - const endSample = Math.min(Math.ceil(sourceEnd * sampleRate), channelData.length); - - for (let j = startSample; j < endSample; j++) { - const abs = Math.abs(channelData[j]); - if (abs > maxPeak) maxPeak = abs; - } - } - peaks[bucket] = maxPeak; - } - return peaks; - } - - function refreshWaveform() { - if (!editorState) return; - waveformPeaks = computeWaveformPeaksFromCache(Math.round(800 * timelineZoom)); - renderWaveform(); - } - - async function extractWaveformPeaks(numBuckets = 800) { - if (!editorState || !editorState.sections || editorState.sections.length === 0) return null; - - try { - // Decode and cache audio for each referenced take - for (const section of editorState.sections) { - if (!section.takeId || takeAudioBufferCache.has(section.takeId)) continue; - const take = activeProject?.takes?.find(t => t.id === section.takeId); - if (!take) continue; - try { - const url = pathToFileUrl(take.screenPath); - const response = await fetch(url); - const arrayBuffer = await response.arrayBuffer(); - const offlineCtx = new OfflineAudioContext(1, 1, 44100); - const audioBuffer = await offlineCtx.decodeAudioData(arrayBuffer); - takeAudioBufferCache.set(section.takeId, audioBuffer); - } catch (err) { - console.warn(`Failed to decode audio for take ${section.takeId}:`, err); - } - } - - return computeWaveformPeaksFromCache(numBuckets); - } catch (err) { - console.warn('Failed to extract waveform:', err); - return null; - } - } - - function renderWaveform() { - const canvas = editorWaveformCanvas; - const rect = canvas.parentElement.getBoundingClientRect(); - canvas.width = Math.round(rect.width * devicePixelRatio); - canvas.height = Math.round(rect.height * devicePixelRatio); - const wCtx = canvas.getContext('2d'); - wCtx.clearRect(0, 0, canvas.width, canvas.height); - if (!waveformPeaks || waveformPeaks.length === 0) return; - - const w = canvas.width; - const h = canvas.height; - const midY = h / 2; - const barWidth = w / waveformPeaks.length; - - wCtx.fillStyle = 'rgba(163, 163, 163, 0.5)'; - for (let i = 0; i < waveformPeaks.length; i++) { - const barHeight = waveformPeaks[i] * midY * 0.9; - const x = i * barWidth; - wCtx.fillRect(x, midY - barHeight, Math.max(1, barWidth - 0.5), barHeight * 2); - } - } - - function renderSectionMarkers() { - if (!editorState || !editorState.duration || !editorState.sections || editorState.sections.length === 0) { - editorSectionMarkers.innerHTML = ''; - renderSectionTranscriptList(); - return; - } - - editorSectionMarkers.innerHTML = ''; - for (const section of editorState.sections) { - const sectionStart = (section.start / editorState.duration) * 100; - const sectionWidth = Math.max(0.35, ((section.end - section.start) / editorState.duration) * 100); - const inSelection = editorState.selectedSectionIds?.has(section.id); - const selected = inSelection || section.id === editorState.selectedSectionId; - const hasImage = !!section.imagePath; - const baseColor = hasImage - ? (section.index % 2 === 0 ? 'rgba(76,29,149,0.45)' : 'rgba(88,28,135,0.4)') - : (section.index % 2 === 0 ? 'rgba(23,23,23,0.72)' : 'rgba(38,38,38,0.68)'); - - const band = document.createElement('div'); - band.className = 'absolute top-0 bottom-0'; - band.dataset.sectionId = section.id; - band.style.left = sectionStart + '%'; - band.style.width = sectionWidth + '%'; - band.style.backgroundColor = selected ? (hasImage ? 'rgba(139,92,246,0.25)' : 'rgba(255,255,255,0.12)') : baseColor; - band.style.borderLeft = section.index === 0 ? 'none' : '1px solid rgba(10,10,10,0.9)'; - if (selected) { - band.style.boxShadow = 'inset 0 0 0 2px rgba(255,255,255,0.3)'; - } - const transcriptPreview = normalizeTranscriptText(section.transcript); - band.title = transcriptPreview - ? `${section.label}: ${formatTime(section.start)} - ${formatTime(section.end)}\n${transcriptPreview}` - : `${section.label}: ${formatTime(section.start)} - ${formatTime(section.end)}`; - const label = document.createElement('div'); - label.className = 'absolute text-[10px] font-medium pointer-events-none'; - label.style.left = '6px'; - label.style.top = '50%'; - label.style.transform = 'translateY(-50%)'; - label.style.color = selected ? 'rgba(255,255,255,0.9)' : 'rgba(163,163,163,0.8)'; - label.textContent = hasImage ? `${section.index + 1} IMG` : String(section.index + 1); - band.appendChild(label); - if (selected) { - const leftHandle = document.createElement('div'); - leftHandle.dataset.trimEdge = 'left'; - leftHandle.dataset.sectionId = section.id; - leftHandle.style.cssText = 'position:absolute;top:0;bottom:0;left:0;width:6px;cursor:col-resize;z-index:30;border-left:3px solid rgba(255,255,255,0.5);'; - band.appendChild(leftHandle); - const rightHandle = document.createElement('div'); - rightHandle.dataset.trimEdge = 'right'; - rightHandle.dataset.sectionId = section.id; - rightHandle.style.cssText = 'position:absolute;top:0;bottom:0;right:0;width:6px;cursor:col-resize;z-index:30;border-right:3px solid rgba(255,255,255,0.5);'; - band.appendChild(rightHandle); - } - const takeProxy = proxyStatus.get(section.takeId); - if (takeProxy && takeProxy.status === 'pending') { - const pct = Math.round((takeProxy.percent || 0) * 100); - const proxyBar = document.createElement('div'); - proxyBar.className = 'absolute bottom-0 left-0 pointer-events-none'; - proxyBar.dataset.proxyBar = section.takeId; - proxyBar.style.cssText = `height:3px;width:${pct}%;background:rgba(251,191,36,0.85);z-index:10;transition:width 0.3s ease;`; - proxyBar.title = `Optimizing for editing\u2026 ${pct}%`; - band.appendChild(proxyBar); - } - - editorSectionMarkers.appendChild(band); - - if (section.index < editorState.sections.length - 1) { - const cut = document.createElement('div'); - cut.className = 'absolute top-0 bottom-0 pointer-events-none'; - cut.style.left = `${(section.end / editorState.duration) * 100}%`; - cut.style.width = '3px'; - cut.style.transform = 'translateX(-1.5px)'; - cut.style.backgroundColor = 'rgba(255,255,255,0.25)'; - editorSectionMarkers.appendChild(cut); - } - } - renderSectionTranscriptList(); - renderCameraMarkers(); - } - - function updateProxyProgressBars(takeId, percent) { - const pct = Math.round(percent * 100); - const bars = editorSectionMarkers.querySelectorAll(`[data-proxy-bar="${takeId}"]`); - for (const bar of bars) { - bar.style.width = `${pct}%`; - bar.title = `Optimizing for editing\u2026 ${pct}%`; - } - } - - function renderCameraMarkers() { - if (!editorCameraMarkers) return; - editorCameraMarkers.innerHTML = ''; - - const showCameraTrack = editorState && editorState.hasCamera; - if (editorCameraTrack) editorCameraTrack.style.display = showCameraTrack ? '' : 'none'; - if (cameraTrackLabel) cameraTrackLabel.style.display = showCameraTrack ? '' : 'none'; - - if (!showCameraTrack || !editorState.duration || !editorState.sections || editorState.sections.length === 0) return; - - for (const section of editorState.sections) { - const anchor = editorState.keyframes.find(kf => kf.sectionId === section.id); - const pipVisible = anchor ? anchor.pipVisible : true; - const cameraFullscreen = anchor ? !!anchor.cameraFullscreen : false; - - const sectionStart = (section.start / editorState.duration) * 100; - const sectionWidth = Math.max(0.35, ((section.end - section.start) / editorState.duration) * 100); - const inSelection = editorState.selectedSectionIds?.has(section.id); - const selected = inSelection || section.id === editorState.selectedSectionId; - - const band = document.createElement('div'); - band.className = 'absolute top-0 bottom-0'; - band.dataset.sectionId = section.id; - band.style.left = sectionStart + '%'; - band.style.width = sectionWidth + '%'; - - if (cameraFullscreen) { - band.style.backgroundColor = selected ? 'rgba(59,130,246,0.35)' : 'rgba(59,130,246,0.2)'; - } else if (pipVisible) { - band.style.backgroundColor = selected ? 'rgba(59,130,246,0.25)' : 'rgba(59,130,246,0.12)'; - } else { - band.style.backgroundColor = selected ? 'rgba(255,255,255,0.08)' : 'rgba(23,23,23,0.5)'; - } - - band.style.borderLeft = section.index === 0 ? 'none' : '1px solid rgba(10,10,10,0.7)'; - - const label = document.createElement('div'); - label.className = 'absolute text-[8px] font-medium pointer-events-none truncate'; - label.style.cssText = 'left:4px;top:50%;transform:translateY(-50%);right:4px;'; - label.style.color = pipVisible ? 'rgba(147,197,253,0.7)' : 'rgba(100,100,100,0.5)'; - label.textContent = cameraFullscreen ? 'Full' : pipVisible ? 'PiP' : 'Off'; - band.appendChild(label); - - editorCameraMarkers.appendChild(band); - } - } - - function startTrimDrag(e, sectionId, edge) { - const section = editorState.sections.find(s => s.id === sectionId); - if (!section) return; - pushUndo(); - editorPause(); - e.preventDefault(); - const rect = editorTimeline.getBoundingClientRect(); - trimDragState = { - sectionId, - edge, - originalSourceStart: section.sourceStart, - originalSourceEnd: section.sourceEnd, - originalStart: section.start, - originalEnd: section.end, - startMouseX: e.clientX, - pixelsPerSecond: rect.width / editorState.duration - }; - document.body.style.cursor = 'col-resize'; - const onMove = (e2) => { e2.preventDefault(); updateTrimDrag(e2); }; - const onUp = () => { - document.body.style.cursor = ''; - window.removeEventListener('mousemove', onMove); - window.removeEventListener('mouseup', onUp); - finishTrimDrag(); - }; - window.addEventListener('mousemove', onMove); - window.addEventListener('mouseup', onUp); - } - - function updateTrimDrag(e) { - if (!trimDragState || !editorState) return; - const section = editorState.sections.find(s => s.id === trimDragState.sectionId); - if (!section) return; - const MIN_DURATION = 0.1; - const deltaPixels = e.clientX - trimDragState.startMouseX; - const deltaTime = deltaPixels / trimDragState.pixelsPerSecond; - - if (trimDragState.edge === 'left') { - section.sourceStart = roundMs(Math.max(0, Math.min( - trimDragState.originalSourceEnd - MIN_DURATION, - trimDragState.originalSourceStart + deltaTime - ))); - // Keep right edge fixed, move left edge only - const newDuration = section.sourceEnd - section.sourceStart; - section.end = trimDragState.originalEnd; - section.start = roundMs(section.end - newDuration); - section.duration = roundMs(newDuration); - } else { - section.sourceEnd = roundMs(Math.max( - section.sourceStart + MIN_DURATION, - trimDragState.originalSourceEnd + deltaTime - )); - // Keep left edge fixed, move right edge only - const newDuration = section.sourceEnd - section.sourceStart; - section.start = trimDragState.originalStart; - section.end = roundMs(section.start + newDuration); - section.duration = roundMs(newDuration); - } - - renderSectionMarkers(); - } - - function finishTrimDrag() { - if (!trimDragState || !editorState) { trimDragState = null; return; } - const section = editorState.sections.find(s => s.id === trimDragState.sectionId); - if (!section) { trimDragState = null; return; } - const sourceStartChanged = Math.abs(section.sourceStart - trimDragState.originalSourceStart) > 0.01; - const sourceEndChanged = Math.abs(section.sourceEnd - trimDragState.originalSourceEnd) > 0.01; - trimDragState = null; - if (!sourceStartChanged && !sourceEndChanged) { undoStack.pop(); updateUndoRedoButtons(); return; } - // Now reflow the full timeline - recalculateTimelinePositions(); - syncSectionAnchorKeyframes(); - renderSectionMarkers(); - refreshWaveform(); - editorSeek(section.start); - scheduleProjectSave(); - } - - function getRenderKeyframes() { - if (!editorState) return []; - - if (editorState.sections && editorState.sections.length > 0) { - syncSectionAnchorKeyframes(); - } - - const sorted = [...editorState.keyframes].sort((a, b) => a.time - b.time); - const minimal = sorted.map(kf => ({ - time: kf.time, - pipX: kf.pipX, - pipY: kf.pipY, - pipVisible: kf.pipVisible, - cameraFullscreen: !!kf.cameraFullscreen, - backgroundZoom: clampSectionZoom(kf.backgroundZoom), - backgroundPanX: clampSectionPan(kf.backgroundPanX), - backgroundPanY: clampSectionPan(kf.backgroundPanY) - })); - - if (minimal.length === 0 || minimal[0].time > 0.0001) { - minimal.unshift({ - time: 0, - pipX: editorState.defaultPipX, - pipY: editorState.defaultPipY, - pipVisible: true, - cameraFullscreen: false, - backgroundZoom: DEFAULT_SECTION_ZOOM, - backgroundPanX: 0, - backgroundPanY: 0 - }); - } - - return minimal; - } - - function getRenderSections() { - if (!editorState) return []; - if (editorState.sections && editorState.sections.length > 0) { - syncSectionAnchorKeyframes(); - } - return editorState.sections.map((section) => { - const anchor = getSectionAnchorKeyframe(section.id, true); - return { - takeId: section.takeId, - sourceStart: section.sourceStart, - sourceEnd: section.sourceEnd, - backgroundZoom: clampSectionZoom(anchor?.backgroundZoom), - backgroundPanX: clampSectionPan(anchor?.backgroundPanX), - backgroundPanY: clampSectionPan(anchor?.backgroundPanY), - imagePath: section.imagePath || null - }; - }); - } - - // ===== Shared drawPip function ===== - function drawPip(targetCtx, video, pipX, pipY, pipW, pipH) { - const r = 12; - targetCtx.save(); - targetCtx.beginPath(); - targetCtx.moveTo(pipX + r, pipY); - targetCtx.lineTo(pipX + pipW - r, pipY); - targetCtx.quadraticCurveTo(pipX + pipW, pipY, pipX + pipW, pipY + r); - targetCtx.lineTo(pipX + pipW, pipY + pipH - r); - targetCtx.quadraticCurveTo(pipX + pipW, pipY + pipH, pipX + pipW - r, pipY + pipH); - targetCtx.lineTo(pipX + r, pipY + pipH); - targetCtx.quadraticCurveTo(pipX, pipY + pipH, pipX, pipY + pipH - r); - targetCtx.lineTo(pipX, pipY + r); - targetCtx.quadraticCurveTo(pipX, pipY, pipX + r, pipY); - targetCtx.closePath(); - targetCtx.clip(); - const camW = video.videoWidth; - const camH = video.videoHeight; - const cropSize = Math.min(camW, camH); - const sx = (camW - cropSize) / 2; - const sy = (camH - cropSize) / 2; - targetCtx.drawImage(video, sx, sy, cropSize, cropSize, pipX, pipY, pipW, pipH); - targetCtx.restore(); - } - - function drawCameraRect(targetCtx, video, x, y, w, h, r) { - const vw = video.videoWidth; - const vh = video.videoHeight; - if (!vw || !vh) return; - targetCtx.save(); - targetCtx.beginPath(); - if (r > 0.5) { - targetCtx.moveTo(x + r, y); - targetCtx.lineTo(x + w - r, y); - targetCtx.quadraticCurveTo(x + w, y, x + w, y + r); - targetCtx.lineTo(x + w, y + h - r); - targetCtx.quadraticCurveTo(x + w, y + h, x + w - r, y + h); - targetCtx.lineTo(x + r, y + h); - targetCtx.quadraticCurveTo(x, y + h, x, y + h - r); - targetCtx.lineTo(x, y + r); - targetCtx.quadraticCurveTo(x, y, x + r, y); - } else { - targetCtx.rect(x, y, w, h); - } - targetCtx.closePath(); - targetCtx.clip(); - const scale = Math.max(w / vw, h / vh); - const dw = vw * scale; - const dh = vh * scale; - const dx = x + (w - dw) / 2; - const dy = y + (h - dh) / 2; - targetCtx.drawImage(video, dx, dy, dw, dh); - targetCtx.restore(); - } - - // Populate device lists - async function enumerateDevices() { - try { - const tempStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true }); - tempStream.getTracks().forEach(t => t.stop()); - } catch (_e) { - // Intentionally ignore - we only need to stop tracks to refresh device list - } - - const devices = await navigator.mediaDevices.enumerateDevices(); - const sources = await window.electronAPI.getSources(); - - screenSelect.innerHTML = ''; - cameraSelect.innerHTML = ''; - audioSelect.innerHTML = ''; - - sources.forEach(s => { - const opt = document.createElement('option'); - opt.value = s.id; - opt.textContent = s.name; - screenSelect.appendChild(opt); - }); - - const videoInputs = devices.filter(d => d.kind === 'videoinput'); - - if (videoInputs.length > 0) { - const sep = document.createElement('option'); - sep.disabled = true; - sep.textContent = '── Capture Devices ──'; - screenSelect.appendChild(sep); - videoInputs.forEach((d, i) => { - const opt = document.createElement('option'); - opt.value = 'device:' + d.deviceId; - opt.textContent = d.label || `Camera ${i + 1}`; - screenSelect.appendChild(opt); - }); - } - - videoInputs.forEach((d, i) => { - const opt = document.createElement('option'); - opt.value = d.deviceId; - opt.textContent = d.label || `Camera ${i + 1}`; - cameraSelect.appendChild(opt); - }); - - devices.filter(d => d.kind === 'audioinput').forEach((d, i) => { - const opt = document.createElement('option'); - opt.value = d.deviceId; - opt.textContent = d.label || `Microphone ${i + 1}`; - audioSelect.appendChild(opt); - }); - - // Default to first screen source (Entire Screen) - const screenIdx = sources.findIndex(s => s.id.startsWith('screen:')); - if (screenIdx !== -1) screenSelect.selectedIndex = screenIdx + 1; // +1 for "None" - else if (screenSelect.options.length > 1) screenSelect.selectedIndex = 1; - if (cameraSelect.options.length > 1) cameraSelect.selectedIndex = 1; - if (audioSelect.options.length > 1) audioSelect.selectedIndex = 1; - } - - async function updateScreenStream() { - if (screenStream) { - screenStream.getTracks().forEach(t => t.stop()); - screenStream = null; - screenVideo.srcObject = null; - } - - const sourceId = screenSelect.value; - if (!sourceId) return; - - if (sourceId.startsWith('device:')) { - const deviceId = sourceId.slice('device:'.length); - screenStream = await navigator.mediaDevices.getUserMedia({ - audio: false, - video: { deviceId: { exact: deviceId }, width: { ideal: 1920 }, height: { ideal: 1080 } } - }); - } else { - screenStream = await navigator.mediaDevices.getUserMedia({ - audio: false, - video: { - mandatory: { - chromeMediaSource: 'desktop', - chromeMediaSourceId: sourceId, - maxFrameRate: 30 - } - } - }); - } - screenVideo.srcObject = screenStream; - } - - async function updateCameraStream() { - if (cameraStream) { - cameraStream.getTracks().forEach(t => t.stop()); - cameraStream = null; - cameraVideo.srcObject = null; - } - - const deviceId = cameraSelect.value; - if (!deviceId) return; - - cameraStream = await navigator.mediaDevices.getUserMedia({ - video: { - deviceId: { exact: deviceId }, - width: { ideal: 1920, max: 1920 }, - height: { ideal: 1080, max: 1080 }, - frameRate: { ideal: 30, max: 30 }, - aspectRatio: { ideal: 16 / 9 } - }, - audio: false - }); - const [cameraTrack] = cameraStream.getVideoTracks(); - if (cameraTrack && 'contentHint' in cameraTrack) { - cameraTrack.contentHint = 'detail'; - console.log(`Camera track settings: ${JSON.stringify(cameraTrack.getSettings?.() || {})}`); - } - cameraVideo.srcObject = cameraStream; - } - - async function updateAudioStream() { - stopAudioMeter(); - if (audioStream) { - audioStream.getTracks().forEach(t => t.stop()); - audioStream = null; - } - - const deviceId = audioSelect.value; - if (!deviceId) return; - - audioStream = await navigator.mediaDevices.getUserMedia({ - audio: { deviceId: { exact: deviceId } }, - video: false - }); - startAudioMeter(audioStream); - } - - function updatePreview() { - const hasAny = screenStream || cameraStream; - noPreview.classList.toggle('hidden', !!hasAny); - recordBtn.disabled = !hasAny || !saveFolder; - - if (drawRAF) cancelAnimationFrame(drawRAF); - lastCompositeDrawAt = 0; - if (hasAny) drawComposite(); - } - - function drawComposite(now = performance.now()) { - if (!shouldRenderPreviewFrame(now, lastCompositeDrawAt, recording)) { - drawRAF = requestAnimationFrame(drawComposite); - return; - } - - lastCompositeDrawAt = now; - ctx.fillStyle = '#000'; - ctx.fillRect(0, 0, CANVAS_W, CANVAS_H); - - const hasScreen = screenStream && screenVideo.videoWidth; - const hasCamera = cameraStream && cameraVideo.videoWidth; - - const drawScreen = screenFitSelect.value === 'fill' ? drawFill : drawFit; - - if (hasScreen && hasCamera) { - drawScreen(ctx, screenVideo, 0, 0, CANVAS_W, CANVAS_H); - const pipW = PIP_SIZE; - const pipH = pipW; - const pipX = CANVAS_W - pipW - PIP_MARGIN; - const pipY = CANVAS_H - pipH - PIP_MARGIN; - drawPip(ctx, cameraVideo, pipX, pipY, pipW, pipH); - } else if (hasScreen) { - drawScreen(ctx, screenVideo, 0, 0, CANVAS_W, CANVAS_H); - } else if (hasCamera) { - drawFit(ctx, cameraVideo, 0, 0, CANVAS_W, CANVAS_H); - } - - drawRAF = requestAnimationFrame(drawComposite); - } - - function getSourceWidth(source) { - return source.videoWidth || source.naturalWidth || source.width || 0; - } - - function getSourceHeight(source) { - return source.videoHeight || source.naturalHeight || source.height || 0; - } - - function drawFit(targetCtx, video, x, y, w, h) { - const vw = getSourceWidth(video); - const vh = getSourceHeight(video); - if (!vw || !vh) return; - const scale = Math.min(w / vw, h / vh); - const dw = vw * scale; - const dh = vh * scale; - const dx = x + (w - dw) / 2; - const dy = y + (h - dh) / 2; - targetCtx.drawImage(video, dx, dy, dw, dh); - } - - function drawFill(targetCtx, video, x, y, w, h) { - const vw = getSourceWidth(video); - const vh = getSourceHeight(video); - if (!vw || !vh) return; - const scale = Math.max(w / vw, h / vh); - const dw = vw * scale; - const dh = vh * scale; - const dx = x + (w - dw) / 2; - const dy = y + (h - dh) / 2; - targetCtx.save(); - targetCtx.beginPath(); - targetCtx.rect(x, y, w, h); - targetCtx.clip(); - targetCtx.drawImage(video, dx, dy, dw, dh); - targetCtx.restore(); - } - - function drawEditorScreenWithZoom(targetCtx, video, fitMode, backgroundZoom, backgroundPanX = 0, backgroundPanY = 0, backgroundFocusX = null, backgroundFocusY = null) { - if (!editorZoomBufferCtx) return; - const zoom = clampSectionZoom(backgroundZoom); - const drawBase = fitMode === 'fill' ? drawFill : drawFit; - - if (zoom <= 1.0001) { - drawBase(targetCtx, video, 0, 0, CANVAS_W, CANVAS_H); - return; - } - - editorZoomBufferCtx.fillStyle = '#000'; - editorZoomBufferCtx.fillRect(0, 0, CANVAS_W, CANVAS_H); - drawBase(editorZoomBufferCtx, video, 0, 0, CANVAS_W, CANVAS_H); - - const { sourceW, sourceH } = resolveZoomCrop(zoom, backgroundPanX, backgroundPanY); - const focusX = backgroundFocusX ?? panToFocusCoord(zoom, backgroundPanX, 0.5); - const focusY = backgroundFocusY ?? panToFocusCoord(zoom, backgroundPanY, 0.5); - const sourceX = Math.max(0, Math.min(CANVAS_W - sourceW, focusX * CANVAS_W - sourceW / 2)); - const sourceY = Math.max(0, Math.min(CANVAS_H - sourceH, focusY * CANVAS_H - sourceH / 2)); - targetCtx.drawImage( - editorZoomBuffer, - sourceX, - sourceY, - sourceW, - sourceH, - 0, - 0, - CANVAS_W, - CANVAS_H - ); - } - - // Audio level meter - function startAudioMeter(stream) { - audioContext = new AudioContext(); - analyser = audioContext.createAnalyser(); - analyser.fftSize = 256; - micSourceNode = audioContext.createMediaStreamSource(stream); - micSourceNode.connect(analyser); - - const data = new Uint8Array(analyser.frequencyBinCount); - - function updateMeter() { - analyser.getByteFrequencyData(data); - const avg = data.reduce((a, b) => a + b, 0) / data.length; - const pct = Math.min(100, (avg / 128) * 100); - audioMeter.style.width = pct + '%'; - audioMeter.className = `h-full rounded-full transition-all duration-75 ${pct > 70 ? 'bg-red-500' : pct > 40 ? 'bg-amber-500' : 'bg-emerald-500'}`; - meterRAF = requestAnimationFrame(updateMeter); - } - updateMeter(); - } - - function stopAudioMeter() { - if (meterRAF) cancelAnimationFrame(meterRAF); - meterRAF = null; - micSourceNode = null; - if (audioContext) { - if (workletRegistered === audioContext) workletRegistered = null; - audioContext.close(); - audioContext = null; - } - audioMeter.style.width = '0%'; - } - - // Recording - function toggleRecording() { - if (!recording) startRecording(); - else stopRecording(); - } - - function createRecorder(stream, suffix) { - const chunks = []; - let recorderError = null; - const recorderOptions = getRecorderOptions({ - suffix, - hasAudio: typeof stream?.getAudioTracks === 'function' && stream.getAudioTracks().length > 0 - }); - const recorder = new MediaRecorder(stream, recorderOptions); - console.log(`[Recorder] ${suffix} configured`, { - mimeType: recorder.mimeType || recorderOptions.mimeType || 'default', - videoTracks: typeof stream?.getVideoTracks === 'function' ? stream.getVideoTracks().length : 0, - audioTracks: typeof stream?.getAudioTracks === 'function' ? stream.getAudioTracks().length : 0, - videoBitsPerSecond: recorderOptions.videoBitsPerSecond || null, - audioBitsPerSecond: recorderOptions.audioBitsPerSecond || null - }); - - recorder.ondataavailable = (e) => { - if (e.data.size > 0) chunks.push(e.data); - }; - - recorder.onerror = (event) => { - recorderError = event?.error?.message || `${suffix} recorder failed`; - console.error(`[Recorder] ${suffix} error`, event?.error || event); - }; - - // blobPromise resolves with { blob, path } when recording stops - recorder.blobPromise = new Promise((resolve) => { - let settled = false; - const settle = (result) => { - if (settled) return; - settled = true; - resolve(result); - }; - - recorder.onstop = async () => { - const result = await finalizeRecordingChunks({ - chunks, - saveFolder, - saveVideo: window.electronAPI.saveVideo, - suffix - }); - - const error = result.path ? null : (result.error || recorderError || `${suffix} recording failed`); - if (result.path) { - console.log('Saved:', result.path); - } else { - console.error(`[Recorder] ${suffix} finalize failed`, error); - } - - settle({ - ...result, - error - }); - }; - }); - - recorder.suffix = suffix; - return recorder; - } - - function addAudioToStream(stream) { - if (!audioStream) return stream; - const combined = new MediaStream([ - ...stream.getVideoTracks(), - ...audioStream.getAudioTracks() - ]); - return combined; - } - - function mergeInt16Arrays(arrays) { - let totalLength = 0; - for (const arr of arrays) totalLength += arr.length; - const merged = new Int16Array(totalLength); - let offset = 0; - for (const arr of arrays) { - merged.set(arr, offset); - offset += arr.length; - } - return merged; - } - - function setTranscriptStatus(text, tone = 'neutral') { - if (!transcriptStatus) return; - - const toneClasses = { - neutral: 'text-neutral-500', - success: 'text-emerald-400', - warning: 'text-amber-400', - error: 'text-red-400' - }; - const resolvedTone = toneClasses[tone] || toneClasses.neutral; - const hasText = typeof text === 'string' && text.trim().length > 0; - - transcriptStatus.className = `px-1 pb-1 text-[11px] ${resolvedTone}`; - transcriptStatus.classList.toggle('hidden', !hasText); - transcriptStatus.textContent = hasText ? text : ''; - } - - function applyScribeStatus(status) { - if (!status) return; - - if (status.failureReason) { - scribeLastFailureReason = status.failureReason; - } else if (status.tone === 'success') { - scribeLastFailureReason = null; - } - - setTranscriptStatus(status.text, status.tone); - } - - async function startRecording() { - if (!activeProjectPath) return; - recorders = []; - speechSegments = []; - audioChunkBuffer = []; - scribeLastFailureReason = null; - scribeManualClose = false; - - // Individual screen (with audio) - // Route through a canvas at constant 30fps to prevent keyframe flicker - // from variable frame rate desktop capture input - if (screenStream) { - const srcTrack = screenStream.getVideoTracks()[0]; - const settings = srcTrack.getSettings(); - const recCanvas = document.createElement('canvas'); - recCanvas.width = settings.width || 1920; - recCanvas.height = settings.height || 1080; - const recCtx = recCanvas.getContext('2d', { alpha: false }); - recCtx.drawImage(screenVideo, 0, 0, recCanvas.width, recCanvas.height); - screenRecInterval = setInterval(() => { - recCtx.drawImage(screenVideo, 0, 0, recCanvas.width, recCanvas.height); - }, 1000 / 30); - const screenOnly = addAudioToStream(recCanvas.captureStream(30)); - recorders.push(createRecorder(screenOnly, 'screen')); - } - - // Individual camera (video only; export uses screen audio) - if (cameraStream) { - const cameraOnly = createCameraRecordingStream(cameraStream); - if (cameraOnly) { - const [cameraTrack] = cameraOnly.getVideoTracks(); - console.log('[Recorder] camera recording track settings:', cameraTrack?.getSettings?.() || {}); - recorders.push(createRecorder(cameraOnly, 'camera')); - } - } - - // Monitor source tracks for unexpected disconnection during recording. - // If a critical track (screen, audio) ends, auto-stop to preserve what we have. - trackEndedCleanups = []; - const monitorTrack = (track, label, critical) => { - const handler = () => { - if (!recording) return; - console.warn(`[Recorder] ${label} track ended unexpectedly`); - if (critical) { - console.error(`[Recorder] Critical track lost (${label}), auto-stopping to save recording`); - stopRecording(); - } - }; - track.addEventListener('ended', handler); - trackEndedCleanups.push(() => track.removeEventListener('ended', handler)); - }; - if (screenStream) { - for (const t of screenStream.getTracks()) monitorTrack(t, 'screen', true); - } - if (cameraStream) { - for (const t of cameraStream.getTracks()) monitorTrack(t, 'camera', false); - } - if (audioStream) { - for (const t of audioStream.getTracks()) monitorTrack(t, 'audio', true); - } - - const recorderTimesliceMs = getRecorderTimesliceMs(); - recorders.forEach(r => r.start(recorderTimesliceMs)); - recording = true; - updateWorkspaceHeader(); - recordBtn.textContent = 'Stop'; - recordBtn.classList.replace('bg-red-600', 'bg-neutral-700'); - recordBtn.classList.replace('hover:bg-red-700', 'hover:bg-neutral-600'); - screenSelect.disabled = true; - cameraSelect.disabled = true; - audioSelect.disabled = true; - - startTime = Date.now(); - timerInterval = setInterval(updateTimer, 200); - - // Show transcript panel and clear previous content - transcriptPanel.classList.remove('hidden'); - transcriptContent.innerHTML = ''; - segmentBadge.textContent = '0 segments'; - setTranscriptStatus('Transcription connecting...', 'neutral'); - - // Set up Scribe via direct WebSocket - if (audioContext && audioStream && micSourceNode) { - try { - const token = await window.electronAPI.getScribeToken(); - const sampleRate = audioContext.sampleRate; - - // Map sample rate to ElevenLabs audio_format parameter - const formatMap = { 8000: 'pcm_8000', 16000: 'pcm_16000', 22050: 'pcm_22050', 24000: 'pcm_24000', 44100: 'pcm_44100', 48000: 'pcm_48000' }; - const audioFormat = formatMap[sampleRate] || 'pcm_16000'; - - const wsUrl = `wss://api.elevenlabs.io/v1/speech-to-text/realtime` - + `?model_id=scribe_v2_realtime` - + `&token=${token}` - + `&audio_format=${audioFormat}` - + `&commit_strategy=vad` - + `&include_timestamps=true` - + `&vad_silence_threshold_secs=1.5` - + `&vad_threshold=0.8` - + `&min_speech_duration_ms=200` - + `&language_code=eng`; - - scribeWs = new WebSocket(wsUrl); - - scribeWs.onopen = () => { - setTranscriptStatus('Transcription connecting...', 'neutral'); - }; - - scribeWs.onmessage = (event) => { - let msg; - try { - msg = JSON.parse(event.data); - } catch (error) { - console.warn('Failed to parse Scribe message:', error); - return; - } - - applyScribeStatus(getScribeStatusFromMessage(msg)); - - if (msg.message_type === 'partial_transcript') { - updatePartialTranscript(msg.text || ''); - } else if (msg.message_type === 'committed_transcript_with_timestamps') { - commitTranscript(msg); - } - }; - - scribeWs.onerror = (err) => { - console.error('Scribe WebSocket error:', err); - if (!scribeManualClose) { - setTranscriptStatus('Transcription connection error', 'error'); - } - }; - - scribeWs.onclose = (event) => { - console.warn('Scribe WebSocket closed:', { - code: event.code, - reason: event.reason, - wasClean: event.wasClean, - lastFailureReason: scribeLastFailureReason, - manualClose: scribeManualClose - }); - if (!scribeManualClose) { - applyScribeStatus( - getScribeStatusFromCloseEvent(event, scribeLastFailureReason) - ); - } - }; - - // Set up AudioWorklet for PCM capture (only register module once per AudioContext) - if (workletRegistered !== audioContext) { - await audioContext.audioWorklet.addModule(new URL('./audio-processor.js', window.location.href).toString()); - workletRegistered = audioContext; - } - scribeWorkletNode = new AudioWorkletNode(audioContext, 'audio-capture'); - micSourceNode.connect(scribeWorkletNode); - - scribeWorkletNode.port.onmessage = (e) => { - if (e.data.pcm) { - audioChunkBuffer.push(new Int16Array(e.data.pcm)); - } - }; - - // Record the offset: time between recording start and first audio send - scribeAudioOffset = (Date.now() - startTime) / 1000; - - // Send accumulated audio every ~100ms - audioSendInterval = setInterval(() => { - if (audioChunkBuffer.length === 0 || !scribeWs || scribeWs.readyState !== WebSocket.OPEN) return; - const merged = mergeInt16Arrays(audioChunkBuffer); - audioChunkBuffer = []; - const bytes = new Uint8Array(merged.buffer); - const CHUNK = 8192; - let binary = ''; - for (let i = 0; i < bytes.length; i += CHUNK) { - binary += String.fromCharCode.apply(null, bytes.subarray(i, i + CHUNK)); - } - const base64 = btoa(binary); - scribeWs.send(JSON.stringify({ - message_type: 'input_audio_chunk', - audio_base_64: base64, - sample_rate: sampleRate, - commit: false - })); - }, 100); - - } catch (err) { - console.warn('Scribe setup failed:', err); - const reason = err instanceof Error ? err.message : 'setup failed'; - setTranscriptStatus(`Transcription unavailable: ${reason}`, 'error'); - } - } else { - setTranscriptStatus('Transcription unavailable: microphone not ready', 'warning'); - } - } - - function updatePartialTranscript(text) { - let partial = document.getElementById('partialText'); - if (!partial) { - partial = document.createElement('div'); - partial.id = 'partialText'; - partial.className = 'text-neutral-600 italic'; - transcriptContent.prepend(partial); - } - partial.textContent = stripNonSpeechAnnotations(text); - transcriptContent.scrollTop = 0; - } - - function commitTranscript(data) { - // Remove partial display - const partial = document.getElementById('partialText'); - if (partial) partial.remove(); - - const spokenWords = extractSpokenWordTokens(data.words); - if (spokenWords.length === 0) return; - - // Build clean text from spoken words only, excluding non-speech annotations. - const cleanText = stripNonSpeechAnnotations(spokenWords.map(w => w.text).join(' ')); - if (!cleanText) return; - - // Shift timestamps by the offset between recording start and first audio sent - speechSegments.push({ - start: spokenWords[0].start + scribeAudioOffset, - end: spokenWords[spokenWords.length - 1].end + scribeAudioOffset, - text: cleanText - }); - - // Add committed text - const div = document.createElement('div'); - div.className = 'mb-2 text-neutral-300 cursor-pointer rounded-md px-1.5 py-0.5 -mx-1 hover:bg-neutral-800/60 transition-colors'; - div.dataset.segmentIndex = speechSegments.length - 1; - div.textContent = cleanText; - div.addEventListener('click', () => { - const idx = parseInt(div.dataset.segmentIndex, 10); - selectSegment(selectedSegmentIndex === idx ? -1 : idx); - }); - transcriptContent.prepend(div); - transcriptContent.scrollTop = 0; - - updateSegmentBadge(); - } - - async function recoverPendingTake(recoveryTake) { - if (!recoveryTake?.screenPath) return; - - const projectSession = getActiveProjectSession(); - const existingTake = Array.isArray(activeProject?.takes) - ? activeProject.takes.find((take) => take.id === recoveryTake.id) - : null; - if (existingTake) { - await completeRecoveryTake(projectSession.projectPath); - return; - } - - const takeId = recoveryTake.id || `take-${Date.now()}`; - const screenPath = recoveryTake.screenPath; - const cameraPath = recoveryTake.cameraPath || null; - let recoverySections = normalizeTakeSections(recoveryTake.sections, recoveryTake.recordedDuration); - const recoverySegments = Array.isArray(recoveryTake.trimSegments) ? recoveryTake.trimSegments : []; - const fallbackSections = buildRemappedSectionsFromSegments(recoverySegments); - - try { - if (recoverySegments.length > 0) { - const computed = await window.electronAPI.computeSections({ - segments: recoverySegments - }); - if (!matchesActiveProjectSession(projectSession)) return; - recoverySections = Array.isArray(computed?.sections) && computed.sections.length > 0 - ? attachSectionTranscripts(computed.sections, fallbackSections) - : (fallbackSections.length > 0 ? fallbackSections : recoverySections); - } - - // Set takeId on all sections - recoverySections = recoverySections.map(s => ({ ...s, takeId })); - - // Add take to project before entering editor (video pool needs it) - if (activeProject) { - if (!Array.isArray(activeProject.takes)) activeProject.takes = []; - activeProject.takes.push({ - id: takeId, - createdAt: recoveryTake.createdAt || new Date().toISOString(), - duration: recoveryTake.recordedDuration, - screenPath, - cameraPath, - sections: recoverySections - }); - } - - const appendResult = appendTakeToTimeline({ - takeId, - screenPath, - cameraPath, - recordedDuration: recoveryTake.recordedDuration, - trimSections: recoverySections, - projectSession - }); - if (!appendResult || !matchesActiveProjectSession(projectSession)) return; - - if (activeProject && appendResult) { - // Update the take's sections with the final result - const take = activeProject.takes.find(t => t.id === takeId); - if (take) { - take.duration = appendResult.takeDuration; - take.sections = appendResult.takeSections; - } - await persistProjectNow(); - } - - await completeRecoveryTake(projectSession.projectPath); - } catch (error) { - console.error('Failed to recover pending take:', error); - if (matchesActiveProjectSession(projectSession)) { - setWorkspaceView(editorState ? 'timeline' : 'recording'); - } - } - } - - function setProcessingProgress(progress = null) { - if (!processingBar) return; - const isDeterminate = Number.isFinite(Number(progress)); - if (!isDeterminate) { - processingBar.classList.add('animate-pulse'); - processingBar.style.width = '100%'; - return; - } - - const clamped = Math.max(0, Math.min(1, Number(progress))); - processingBar.classList.remove('animate-pulse'); - processingBar.style.width = `${Math.max(2, Math.round(clamped * 100))}%`; - } - - function _showProcessingState(title, status, progress = null) { - processingTitle.textContent = title || 'Processing...'; - processingStatus.textContent = status || ''; - setProcessingProgress(progress); - setWorkspaceView('processing'); - } - - function _buildSectionAnchorSnapshot(keyframes) { - const anchors = new Map(); - for (const keyframe of Array.isArray(keyframes) ? keyframes : []) { - if (!keyframe.sectionId) continue; - anchors.set(keyframe.sectionId, { - pipX: keyframe.pipX, - pipY: keyframe.pipY, - pipVisible: keyframe.pipVisible !== false, - cameraFullscreen: !!keyframe.cameraFullscreen, - backgroundZoom: clampSectionZoom(keyframe.backgroundZoom), - backgroundPanX: clampSectionPan(keyframe.backgroundPanX), - backgroundPanY: clampSectionPan(keyframe.backgroundPanY) - }); +const projectHomeView = document.getElementById('projectHomeView'); +const workspaceHeader = document.getElementById('workspaceHeader'); +const newProjectNameInput = document.getElementById('newProjectName'); +const createProjectBtn = document.getElementById('createProjectBtn'); +const openProjectBtn = document.getElementById('openProjectBtn'); +const projectHomeMessage = document.getElementById('projectHomeMessage'); +const lastProjectRow = document.getElementById('lastProjectRow'); +const lastProjectName = document.getElementById('lastProjectName'); +const lastProjectPath = document.getElementById('lastProjectPath'); +const resumeLastBtn = document.getElementById('resumeLastBtn'); +const recentProjectsList = document.getElementById('recentProjectsList'); +const activeProjectNameEl = document.getElementById('activeProjectName'); +const activeProjectPathEl = document.getElementById('activeProjectPath'); +const goRecordingBtn = document.getElementById('goRecordingBtn'); +const goTimelineBtn = document.getElementById('goTimelineBtn'); +const switchProjectBtn = document.getElementById('switchProjectBtn'); +const exportAudioPresetControl = document.getElementById('exportAudioPresetControl'); +const exportAudioPresetSelect = document.getElementById('exportAudioPreset'); +const exportVideoPresetControl = document.getElementById('exportVideoPresetControl'); +const exportVideoPresetSelect = document.getElementById('exportVideoPreset'); +const cameraSyncOffsetControl = document.getElementById('cameraSyncOffsetControl'); +const cameraSyncOffsetInput = document.getElementById('cameraSyncOffsetMs'); + +const screenSelect = document.getElementById('screenSource'); +const screenFitSelect = document.getElementById('screenFit'); +const cameraSelect = document.getElementById('cameraSource'); +const audioSelect = document.getElementById('audioSource'); +const canvas = document.getElementById('compositeCanvas'); +const ctx = canvas.getContext('2d'); +const screenVideo = document.getElementById('screenVideo'); +const cameraVideo = document.getElementById('cameraVideo'); +const noPreview = document.getElementById('noPreview'); +const audioMeter = document.getElementById('audioMeter'); +const recordBtn = document.getElementById('recordBtn'); +const timerEl = document.getElementById('timer'); +const folderPathEl = document.getElementById('folderPath'); +const openFolderBtn = document.getElementById('openFolderBtn'); +const pickFolderBtn = document.getElementById('pickFolderBtn'); +const contentProtectionToggle = document.getElementById('contentProtectionToggle'); +const recordingView = document.getElementById('recordingView'); +const transcriptPanel = document.getElementById('transcriptPanel'); +const transcriptContent = document.getElementById('transcriptContent'); +const transcriptStatus = document.getElementById('transcriptStatus'); +const segmentBadge = document.getElementById('segmentBadge'); +const processingView = document.getElementById('processingView'); +const processingTitle = document.getElementById('processingTitle'); +const processingStatus = document.getElementById('processingStatus'); +const processingBar = document.getElementById('processingBar'); + +// Editor DOM refs +const editorView = document.getElementById('editorView'); +const editorCanvas = document.getElementById('editorCanvas'); +const editorCtx = editorCanvas.getContext('2d'); +const editorRenderBtn = document.getElementById('editorRenderBtn'); +const editorUndoBtn = document.getElementById('editorUndoBtn'); +const editorRedoBtn = document.getElementById('editorRedoBtn'); +const editorPlayBtn = document.getElementById('editorPlayBtn'); +const editorSplitBtn = document.getElementById('editorSplitBtn'); +const editorToggleCamBtn = document.getElementById('editorToggleCamBtn'); +const editorCamFullBtn = document.getElementById('editorCamFullBtn'); +const editorBgZoomInput = document.getElementById('editorBgZoomInput'); +const editorBgZoomValue = document.getElementById('editorBgZoomValue'); +const editorApplyFutureBtn = document.getElementById('editorApplyFutureBtn'); +const editorTimeEl = document.getElementById('editorTime'); +const editorTimelineWrapper = document.getElementById('editorTimelineWrapper'); +const editorTimeline = document.getElementById('editorTimeline'); +const editorSectionMarkers = document.getElementById('editorSectionMarkers'); +const editorScrubber = document.getElementById('editorScrubber'); +const editorCameraTrack = document.getElementById('editorCameraTrack'); +const editorCameraMarkers = document.getElementById('editorCameraMarkers'); +const cameraTrackLabel = document.getElementById('cameraTrackLabel'); +const editorSectionTranscriptList = document.getElementById('editorSectionTranscriptList'); +let editorRenderTimeout = null; +const editorWaveformCanvas = document.getElementById('editorWaveformCanvas'); + +let screenStream = null; +let cameraStream = null; +let audioStream = null; +let recorders = []; +let recording = false; +let screenRecInterval = null; +let trackEndedCleanups = []; +let timerInterval = null; +let startTime = 0; +let audioContext = null; +let analyser = null; +let meterRAF = null; +let drawRAF = null; +let lastCompositeDrawAt = 0; +let saveFolder = ''; +let hideFromRecording = 'true'; +let activeProjectPath = ''; +let activeProject = null; +let activeProjectSession = 0; +let activeWorkspaceView = 'home'; +let saveDebounceTimer = null; +let persistQueue = Promise.resolve(); +let mediaInitialized = false; +let mediaIdleTimer = null; +let scribeWs = null; +let scribeWorkletNode = null; +let speechSegments = []; +let audioChunkBuffer = []; +let audioSendInterval = null; +let scribeLastFailureReason = null; +let scribeManualClose = false; +let micSourceNode = null; +const MEDIA_IDLE_TIMEOUT_MS = 30000; + +function clearMediaIdleTimer() { + if (!mediaIdleTimer) return; + clearTimeout(mediaIdleTimer); + mediaIdleTimer = null; +} + +function resetMediaRefsAfterCleanup() { + recording = false; + screenStream = null; + cameraStream = null; + audioStream = null; + recorders = []; + screenRecInterval = null; + timerInterval = null; + audioContext = null; + analyser = null; + meterRAF = null; + drawRAF = null; + lastCompositeDrawAt = 0; + scribeWs = null; + scribeWorkletNode = null; + audioChunkBuffer = []; + audioSendInterval = null; + micSourceNode = null; + mediaInitialized = false; + screenVideo.srcObject = null; + cameraVideo.srcObject = null; +} + +function cleanupRendererMediaResources() { + cleanupAllMedia({ + recording, + screenStream, + cameraStream, + audioStream, + recorders, + screenRecInterval, + audioSendInterval, + timerInterval, + audioContext, + scribeWorkletNode, + scribeWs, + drawRAF, + meterRAF, + cancelEditorDrawLoop, + stopAudioMeter + }); + resetMediaRefsAfterCleanup(); +} + +function hasActiveRecorders() { + return recorders.some((recorder) => recorder?.state && recorder.state !== 'inactive'); +} + +function handleRenderProgress(update) { + if (!editorState || !editorState.rendering) return; + + const percent = Number.isFinite(Number(update?.percent)) + ? Math.max(0, Math.min(1, Number(update.percent))) + : null; + editorState.renderProgress = percent ?? 0; + + processingTitle.textContent = 'Rendering export...'; + processingStatus.textContent = + typeof update?.status === 'string' && update.status ? update.status : 'Rendering...'; + setProcessingProgress(percent); + + if (percent === null) { + setRenderBtnState(processingStatus.textContent, 'busy'); + return; + } + + setRenderBtnState(`Rendering ${Math.round(percent * 100)}%`, 'busy'); +} + +if (typeof window.electronAPI.onRenderProgress === 'function') { + window.electronAPI.onRenderProgress((update) => { + handleRenderProgress(update); + }); +} + +if (typeof window.electronAPI.onProxyProgress === 'function') { + window.electronAPI.onProxyProgress((payload) => { + if (!payload || !payload.takeId) return; + if (payload.status === 'progress') { + const current = proxyStatus.get(payload.takeId); + if (current && current.status === 'pending') { + current.percent = payload.percent || 0; + updateProxyProgressBars(payload.takeId, current.percent); + } + } else if (payload.status === 'done' && payload.proxyPath) { + proxyStatus.set(payload.takeId, { status: 'done', percent: 1 }); + const take = activeProject?.takes?.find((t) => t.id === payload.takeId); + if (take) { + take.proxyPath = payload.proxyPath; + persistProjectNow().catch((err) => + console.warn('[Proxy] Failed to persist proxyPath:', err) + ); } - return anchors; - } - - function remapManualKeyframesAfterSectionDelete(keyframes, removedSection) { - const epsilon = 0.001; - const removedDuration = Math.max(0, Number(removedSection?.end) - Number(removedSection?.start)); - - return (Array.isArray(keyframes) ? keyframes : []) - .filter(keyframe => !keyframe.sectionId) - .map((keyframe) => { - const time = Number(keyframe.time) || 0; - if (time >= removedSection.start - epsilon && time < removedSection.end - epsilon) { - return null; - } - - const nextTime = time >= removedSection.end - epsilon - ? roundMs(time - removedDuration) - : roundMs(time); - - return { - ...keyframe, - time: Math.max(0, nextTime) - }; - }) - .filter(Boolean) - .sort((a, b) => a.time - b.time); - } - - function deleteSelectedSection() { - if (!editorState || editorState.rendering) return; - const selectedSection = getSelectedSection(); - if (!selectedSection) return; - - const selectedIndex = editorState.sections.findIndex(section => section.id === selectedSection.id); - if (selectedIndex < 0) return; - - pushUndo(); - - // Remove the section - editorState.sections = editorState.sections.filter(section => section.id !== selectedSection.id); - - if (editorState.sections.length === 0) { - const savedSourceWidth = editorState.sourceWidth || null; - const savedSourceHeight = editorState.sourceHeight || null; - clearEditorState(); - if (activeProject) { - activeProject = { - ...activeProject, - timeline: { - duration: 0, - sections: [], - keyframes: [], - selectedSectionId: null, - hasCamera: false, - sourceWidth: savedSourceWidth, - sourceHeight: savedSourceHeight + // Hot-swap the cached video element to use the proxy + const cached = takeVideoPool.get(payload.takeId); + if (cached) { + const wasPlaying = !cached.screen.paused; + const currentTime = cached.screen.currentTime; + const rate = cached.screen.playbackRate; + cached.screen.src = pathToFileUrl(payload.proxyPath); + cached.screen.addEventListener( + 'loadedmetadata', + () => { + cached.screen.currentTime = currentTime; + if (wasPlaying) { + cached.screen.playbackRate = rate; + cached.screen.play().catch(() => {}); + if (!hasPendingEditorDraw()) scheduleEditorDrawLoop(); } - }; - } - persistProjectNow(); - setWorkspaceView('recording'); - return; + }, + { once: true } + ); } - - // Remove deleted section's anchor, keep remaining anchors, remap manual keyframes - const remainingAnchors = editorState.keyframes.filter( - kf => kf.sectionId && kf.sectionId !== selectedSection.id - ); - const remappedManual = remapManualKeyframesAfterSectionDelete(editorState.keyframes, selectedSection); - editorState.keyframes = [...remainingAnchors, ...remappedManual]; - - // Update indices and labels only — keep existing IDs stable - reindexSections(editorState.sections); - - recalculateTimelinePositions(); - syncSectionAnchorKeyframes(); - - const nextSelected = editorState.sections[Math.min(selectedIndex, editorState.sections.length - 1)] || editorState.sections[0]; - editorState.selectedSectionId = nextSelected?.id || null; - renderSectionMarkers(); - refreshWaveform(); - editorSeek(nextSelected?.start || 0); - scheduleProjectSave(); - } - - function splitSectionAtPlayhead() { - if (!editorState || editorState.rendering) return; - - const time = editorState.currentTime; - const section = findSectionForTime(time); - if (!section) return; - - const MIN_DURATION = 0.1; - const offsetInSection = time - section.start; - const sourceTime = roundMs(section.sourceStart + offsetInSection); - - if (sourceTime - section.sourceStart < MIN_DURATION || section.sourceEnd - sourceTime < MIN_DURATION) return; - - pushUndo(); - - const sectionIndex = editorState.sections.findIndex(s => s.id === section.id); - if (sectionIndex < 0) return; - - // Split: modify original in place (becomes left half), insert new right half - const newSectionId = generateSectionId(); - const rightSection = { - id: newSectionId, index: 0, label: 'temp', - start: 0, end: 0, duration: 0, - sourceStart: sourceTime, - sourceEnd: section.sourceEnd, - takeId: section.takeId, - transcript: '' - }; - - section.sourceEnd = sourceTime; - editorState.sections.splice(sectionIndex + 1, 0, rightSection); - - // Update indices and labels only — keep existing IDs stable - reindexSections(editorState.sections); - - recalculateTimelinePositions(); - - // Add one anchor keyframe for the new right-half section (inherits from parent) - const newAnchor = buildSplitAnchorKeyframe( - editorState.keyframes, section.id, newSectionId, - rightSection.start, { pipX: editorState.defaultPipX, pipY: editorState.defaultPipY } - ); - editorState.keyframes.push(newAnchor); - editorState.keyframes.sort((a, b) => a.time - b.time); - - editorState.selectedSectionId = newSectionId; - + } else if (payload.status === 'error') { + proxyStatus.set(payload.takeId, { status: 'error', percent: 0 }); + console.warn('[Proxy] Generation failed for take', payload.takeId, payload.error); renderSectionMarkers(); - refreshWaveform(); - editorSeek(editorState.currentTime); - scheduleProjectSave(); - } - - function appendTakeToTimeline({ takeId, screenPath: _screenPath, cameraPath, recordedDuration, trimSections, projectSession }) { - const takeSections = normalizeTakeSections(trimSections, recordedDuration); - // Ensure all sections carry the takeId - for (const section of takeSections) { - section.takeId = takeId; - } - const takeDuration = takeSections.length > 0 - ? takeSections[takeSections.length - 1].end - : Math.max(0, Number(recordedDuration) || 0); - - if (!matchesActiveProjectSession(projectSession)) return null; - - const hasCamera = !!cameraPath; - - if (!editorState) { - enterEditor( - takeSections, - { - hasCamera, - initialView: 'timeline' - } - ); - - return { - takeSections, - takeDuration, - appendedSections: takeSections - }; - } - - pushUndo(); - - const baseDuration = Math.max(0, Number(editorState.duration) || 0); - const existingSections = editorState.sections.map(s => ({ ...s })); - const existingKeyframes = Array.isArray(editorState.keyframes) - ? editorState.keyframes.map(kf => ({ ...kf })) - : []; - - const hadCameraBefore = !!editorState.hasCamera; - const keepCamera = hadCameraBefore || hasCamera; - - const startIndex = existingSections.length; - const appendedSections = takeSections.map((section, idx) => { - const sectionNumber = startIndex + idx + 1; - return { - ...section, - id: `section-${sectionNumber}`, - index: sectionNumber - 1, - label: `Section ${sectionNumber}`, - start: roundMs(section.start + baseDuration), - end: roundMs(section.end + baseDuration), - duration: roundMs(section.end - section.start), - takeId - }; - }); - - const timelineSections = [...existingSections, ...appendedSections]; - - const carryState = getStateAtTime(Math.max(0, baseDuration - 0.001)); - const newAnchors = appendedSections.map((section) => ({ - time: section.start, - pipX: carryState.pipX, - pipY: carryState.pipY, - pipVisible: carryState.pipVisible, - cameraFullscreen: !!carryState.cameraFullscreen, - backgroundZoom: clampSectionZoom(carryState.backgroundZoom), - backgroundPanX: clampSectionPan(carryState.backgroundPanX), - backgroundPanY: clampSectionPan(carryState.backgroundPanY), - sectionId: section.id, - autoSection: true - })); - - const withoutConflictingAnchors = existingKeyframes.filter( - kf => !kf.sectionId || !newAnchors.some(anchor => anchor.sectionId === kf.sectionId) - ); - - enterEditor( - timelineSections, - { - keyframes: [...withoutConflictingAnchors, ...newAnchors].sort((a, b) => a.time - b.time), - selectedSectionId: appendedSections[0]?.id || editorState?.selectedSectionId, - hasCamera: keepCamera, - screenFitMode: editorState?.screenFitMode, - sourceWidth: editorState?.sourceWidth, - sourceHeight: editorState?.sourceHeight, - initialView: 'timeline' - } - ); - - return { - takeSections, - takeDuration, - appendedSections - }; } - - async function stopRecording() { - const projectSession = getActiveProjectSession(); - const recordedDuration = (Date.now() - startTime) / 1000; - clearInterval(timerInterval); - - // Remove track-ended listeners (no longer needed once we're stopping) - for (const cleanup of trackEndedCleanups) cleanup(); - trackEndedCleanups = []; - - // Stop audio send interval - if (audioSendInterval) { - clearInterval(audioSendInterval); - audioSendInterval = null; - } - - // Disconnect worklet (must sever micSourceNode→worklet input, not just worklet outputs) - if (scribeWorkletNode) { - scribeWorkletNode.port.onmessage = null; - if (micSourceNode) micSourceNode.disconnect(scribeWorkletNode); - scribeWorkletNode.disconnect(); - scribeWorkletNode = null; - } - - // Send final commit and close WebSocket. - // Detach onmessage first so late-arriving transcripts cannot mutate - // speechSegments after we snapshot them for section computation. - const hadScribe = !!scribeWs; - scribeManualClose = true; - if (scribeWs) { - scribeWs.onmessage = null; - if (scribeWs.readyState === WebSocket.OPEN) { - scribeWs.send(JSON.stringify({ message_type: 'commit' })); - await new Promise(r => setTimeout(r, 1000)); - scribeWs.close(); - } - } - scribeWs = null; - scribeLastFailureReason = null; - audioChunkBuffer = []; - - if (screenRecInterval) { - clearInterval(screenRecInterval); - screenRecInterval = null; - } - - const recorderFinalizeTimeoutMs = getRecorderFinalizeTimeoutMs(); - recorders.forEach(r => { - if (r.state === 'inactive') return; - if (typeof r.requestData === 'function') { - try { - r.requestData(); - } catch (error) { - console.warn(`[Recorder] ${r.suffix} requestData failed`, error); - } + }); +} +let scribeAudioOffset = 0; // seconds between recording start and first audio sent to Scribe +let workletRegistered = null; // tracks which AudioContext has the worklet registered + +const CANVAS_W = 1920; +const CANVAS_H = 1080; +const PIP_FRACTION = 0.22; +const PIP_MARGIN = 20; +const PIP_SIZE = Math.round(CANVAS_W * PIP_FRACTION); +const MIN_SECTION_ZOOM = 1; +const MAX_SECTION_ZOOM = 3; +const DEFAULT_SECTION_ZOOM = 1; +const MIN_SECTION_PAN = -1; +const MAX_SECTION_PAN = 1; +const EXPORT_AUDIO_PRESET_OFF = 'off'; +const EXPORT_AUDIO_PRESET_COMPRESSED = 'compressed'; +const EXPORT_VIDEO_PRESET_FAST = 'fast'; +const EXPORT_VIDEO_PRESET_QUALITY = 'quality'; + +function snapToNearestCorner(cursorX, cursorY) { + const midX = CANVAS_W / 2; + const midY = CANVAS_H / 2; + return { + x: cursorX < midX ? PIP_MARGIN : CANVAS_W - PIP_SIZE - PIP_MARGIN, + y: cursorY < midY ? PIP_MARGIN : CANVAS_H - PIP_SIZE - PIP_MARGIN + }; +} + +function clampSectionZoom(value) { + const zoom = Number(value); + if (!Number.isFinite(zoom)) return DEFAULT_SECTION_ZOOM; + return Math.max(MIN_SECTION_ZOOM, Math.min(MAX_SECTION_ZOOM, zoom)); +} + +function formatSectionZoom(value) { + return `${clampSectionZoom(value).toFixed(2)}x`; +} + +function clampSectionPan(value) { + const pan = Number(value); + if (!Number.isFinite(pan)) return 0; + return Math.max(MIN_SECTION_PAN, Math.min(MAX_SECTION_PAN, pan)); +} + +function normalizeExportAudioPreset(value) { + return value === EXPORT_AUDIO_PRESET_OFF + ? EXPORT_AUDIO_PRESET_OFF + : EXPORT_AUDIO_PRESET_COMPRESSED; +} + +function normalizeExportVideoPreset(value) { + return value === EXPORT_VIDEO_PRESET_FAST + ? EXPORT_VIDEO_PRESET_FAST + : EXPORT_VIDEO_PRESET_QUALITY; +} + +function getZoomCropBounds(zoom) { + const clampedZoom = clampSectionZoom(zoom); + const sourceW = CANVAS_W / clampedZoom; + const sourceH = CANVAS_H / clampedZoom; + return { + sourceW, + sourceH, + maxOffsetX: Math.max(0, (CANVAS_W - sourceW) / 2), + maxOffsetY: Math.max(0, (CANVAS_H - sourceH) / 2) + }; +} + +function resolveZoomCrop(zoom, panX = 0, panY = 0) { + const { sourceW, sourceH, maxOffsetX, maxOffsetY } = getZoomCropBounds(zoom); + return { + sourceW, + sourceH, + sourceX: maxOffsetX + clampSectionPan(panX) * maxOffsetX, + sourceY: maxOffsetY + clampSectionPan(panY) * maxOffsetY, + maxOffsetX, + maxOffsetY + }; +} + +function panToFocusCoord(zoom, pan, defaultCoord = 0.5) { + const normalizedZoom = clampSectionZoom(zoom); + if (normalizedZoom <= 1.0001) return defaultCoord; + const cropFraction = 1 / normalizedZoom; + return cropFraction / 2 + ((clampSectionPan(pan) + 1) / 2) * (1 - cropFraction); +} + +function focusToPanCoord(zoom, focus, defaultPan = 0) { + const normalizedZoom = clampSectionZoom(zoom); + if (normalizedZoom <= 1.0001) return defaultPan; + const cropFraction = 1 / normalizedZoom; + const availableFraction = 1 - cropFraction; + if (availableFraction <= 0.000001) return defaultPan; + return clampSectionPan(((focus - cropFraction / 2) / availableFraction) * 2 - 1); +} + +const TRANSITION_DURATION = 0.3; +const CAMERA_DRIFT_SOFT_THRESHOLD = 0.015; +const CAMERA_DRIFT_HARD_THRESHOLD = 0.18; +const CAMERA_DRIFT_LOG_THRESHOLD = 0.08; +const CAMERA_DRIFT_LOG_INTERVAL_MS = 1000; +const CAMERA_RESYNC_COOLDOWN_MS = 500; + +canvas.width = CANVAS_W; +canvas.height = CANVAS_H; +editorCanvas.width = CANVAS_W; +editorCanvas.height = CANVAS_H; + +// ===== Editor State ===== +const undoStack = []; +const redoStack = []; +const MAX_UNDO = 50; +let editorState = null; +let editorDrawRAF = null; +let editorPausedDrawTimer = null; +let editorVideoFrameCallbackId = null; +let editorVideoFrameHost = null; +let editorVideoFrameSafetyTimer = null; +let draggingPip = false; +let pipDragMoved = false; +let waveformPeaks = null; +let timelineZoom = 1; +let trimDragState = null; +let sectionDragState = null; +let sectionZoomDragActive = false; +let draggingBackground = false; +let backgroundDragMoved = false; +let backgroundDragState = null; +let takeAudioBufferCache = new Map(); // takeId -> AudioBuffer +let takeVideoPool = new Map(); // takeId -> { screen: HTMLVideoElement, camera: HTMLVideoElement|null } +const proxyStatus = new Map(); // takeId -> { status: 'pending'|'done'|'error', percent: number } +let activeTakeId = null; +let activePlaybackSection = null; +let cameraResyncCooldownUntil = 0; +let lastCameraDriftLogAt = 0; +const editorZoomBuffer = document.createElement('canvas'); +editorZoomBuffer.width = CANVAS_W; +editorZoomBuffer.height = CANVAS_H; +const editorZoomBufferCtx = editorZoomBuffer.getContext('2d'); + +const sectionImageCache = new Map(); + +function loadSectionImage(imagePath) { + if (!imagePath) return Promise.resolve(null); + if (sectionImageCache.has(imagePath)) return Promise.resolve(sectionImageCache.get(imagePath)); + return new Promise((resolve) => { + const img = new Image(); + img.onload = () => { + sectionImageCache.set(imagePath, img); + resolve(img); + }; + img.onerror = () => { + console.warn('Failed to load section image:', imagePath); + resolve(null); + }; + img.src = pathToFileUrl(imagePath); + }); +} + +function preloadSectionImages() { + if (!editorState?.sections) return; + for (const section of editorState.sections) { + if (section.imagePath) loadSectionImage(section.imagePath); + } +} + +function getOrCreateTakeVideos(takeId) { + if (takeVideoPool.has(takeId)) return takeVideoPool.get(takeId); + const take = activeProject?.takes?.find((t) => t.id === takeId); + if (!take) return null; + const screen = document.createElement('video'); + screen.playsInline = true; + screen.preload = 'auto'; + screen.src = pathToFileUrl(take.proxyPath || take.screenPath); + let camera = null; + if (take.cameraPath) { + camera = document.createElement('video'); + camera.playsInline = true; + camera.muted = true; + camera.preload = 'auto'; + camera.src = pathToFileUrl(take.cameraPath); + } + const entry = { screen, camera }; + takeVideoPool.set(takeId, entry); + return entry; +} + +function cleanupVideoPool() { + for (const [, videos] of takeVideoPool) { + videos.screen.pause(); + videos.screen.src = ''; + if (videos.camera) { + videos.camera.pause(); + videos.camera.src = ''; + } + } + takeVideoPool.clear(); + takeAudioBufferCache.clear(); + activeTakeId = null; + activePlaybackSection = null; +} + +function resolveTimeToSource(timelineTime) { + const section = findSectionForTime(timelineTime); + if (!section) return null; + const sourceTime = section.sourceStart + (timelineTime - section.start); + return { takeId: section.takeId, sourceTime, section }; +} + +function recalculateTimelinePositions() { + if (!editorState || !editorState.sections) return; + let cursor = 0; + for (const section of editorState.sections) { + const duration = section.sourceEnd - section.sourceStart; + section.start = roundMs(cursor); + section.end = roundMs(cursor + duration); + section.duration = roundMs(duration); + cursor += duration; + } + editorState.duration = cursor; +} + +function easeInOut(t) { + return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2; +} + +function getActiveProjectSession() { + return { + id: activeProjectSession, + projectPath: activeProjectPath + }; +} + +function matchesActiveProjectSession(session) { + return ( + !!session && session.id === activeProjectSession && session.projectPath === activeProjectPath + ); +} + +function pathToFileUrl(filePath) { + if (!filePath) return ''; + return window.electronAPI.pathToFileUrl(filePath); +} + +function formatProjectDate(value) { + if (!value) return ''; + const dt = new Date(value); + if (Number.isNaN(dt.getTime())) return ''; + return dt.toLocaleString(); +} + +function setToggleButtonState(button, active, disabled) { + button.disabled = !!disabled; + button.className = `px-3 py-1.5 rounded-md text-sm transition-colors ${active ? 'bg-white text-neutral-950 font-medium' : 'text-neutral-400 hover:text-neutral-200 hover:bg-neutral-800'} disabled:opacity-40 disabled:cursor-not-allowed`; +} + +function updateWorkspaceHeader() { + const hasProject = !!activeProjectPath; + const showTimelineTools = hasProject && activeWorkspaceView === 'timeline'; + activeProjectNameEl.textContent = activeProject?.name || 'Project'; + activeProjectPathEl.textContent = activeProjectPath || ''; + workspaceHeader.classList.toggle('hidden', !hasProject || activeWorkspaceView === 'home'); + setToggleButtonState( + goRecordingBtn, + activeWorkspaceView === 'recording', + !hasProject || activeWorkspaceView === 'processing' + ); + setToggleButtonState( + goTimelineBtn, + activeWorkspaceView === 'timeline', + !hasProject || !editorState || activeWorkspaceView === 'processing' || recording + ); + recordBtn.classList.toggle('hidden', activeWorkspaceView !== 'recording'); + timerEl.classList.toggle('hidden', activeWorkspaceView !== 'recording'); + cameraSyncOffsetControl.classList.toggle('hidden', !showTimelineTools); + cameraSyncOffsetControl.classList.toggle('flex', showTimelineTools); + exportAudioPresetControl.classList.toggle('hidden', !showTimelineTools); + exportAudioPresetControl.classList.toggle('flex', showTimelineTools); + exportVideoPresetControl.classList.toggle('hidden', !showTimelineTools); + exportVideoPresetControl.classList.toggle('flex', showTimelineTools); + editorRenderBtn.classList.toggle('hidden', !showTimelineTools); +} + +function hasPendingEditorDraw() { + return !!editorDrawRAF || !!editorPausedDrawTimer || editorVideoFrameCallbackId !== null; +} + +function cancelEditorDrawLoop() { + if (editorDrawRAF) { + cancelAnimationFrame(editorDrawRAF); + editorDrawRAF = null; + } + if (editorPausedDrawTimer) { + clearTimeout(editorPausedDrawTimer); + editorPausedDrawTimer = null; + } + if ( + editorVideoFrameHost && + editorVideoFrameCallbackId !== null && + typeof editorVideoFrameHost.cancelVideoFrameCallback === 'function' + ) { + try { + editorVideoFrameHost.cancelVideoFrameCallback(editorVideoFrameCallbackId); + } catch (_error) { + // Ignore cancellation races while switching active takes. + } + } + editorVideoFrameCallbackId = null; + editorVideoFrameHost = null; + if (editorVideoFrameSafetyTimer) { + clearTimeout(editorVideoFrameSafetyTimer); + editorVideoFrameSafetyTimer = null; + } +} + +function scheduleEditorDrawLoop() { + if (!editorState || activeWorkspaceView !== 'timeline') return; + + if (editorState.playing && activeTakeId) { + const videos = getOrCreateTakeVideos(activeTakeId); + const screen = videos?.screen; + if (screen && typeof screen.requestVideoFrameCallback === 'function') { + editorVideoFrameHost = screen; + editorVideoFrameCallbackId = screen.requestVideoFrameCallback(() => { + if (editorVideoFrameSafetyTimer) { + clearTimeout(editorVideoFrameSafetyTimer); + editorVideoFrameSafetyTimer = null; } - r.stop(); + editorVideoFrameCallbackId = null; + editorVideoFrameHost = null; + editorDrawLoop(); }); - - // Await each recorder independently so one finalize failure cannot wedge the whole stop flow. - const results = {}; - const finalizeErrors = []; - for (const r of recorders) { - const result = await Promise.race([ - r.blobPromise, - new Promise((resolve) => { - setTimeout(() => { - resolve({ - blob: new Blob([], { type: 'video/webm' }), - error: `${r.suffix} recording did not finish saving in time`, - path: null, - suffix: r.suffix - }); - }, recorderFinalizeTimeoutMs); - }) - ]); - results[r.suffix] = result; - if (result?.error) finalizeErrors.push(result.error); - } - - recorders = []; - recording = false; - updateWorkspaceHeader(); - recordBtn.textContent = 'Record'; - recordBtn.classList.replace('bg-neutral-700', 'bg-red-600'); - recordBtn.classList.replace('hover:bg-neutral-600', 'hover:bg-red-700'); - screenSelect.disabled = false; - cameraSelect.disabled = false; - audioSelect.disabled = false; - timerEl.textContent = '00:00'; - - // Hide transcript panel - transcriptPanel.classList.add('hidden'); - setTranscriptStatus('', 'neutral'); - - // Enter editor if we have at least a screen recording - if (results.screen?.path) { - if (finalizeErrors.length > 0) { - console.warn('Recording finalized with partial failures:', finalizeErrors); - } - const takeId = `take-${Date.now()}`; - const takeCreatedAt = new Date().toISOString(); - const screenPath = results.screen.path; - const cameraPath = results.camera?.path || null; - let sectionsForTimeline = buildDefaultSectionsForDuration(recordedDuration); - - // Compute sections from speech segments (instant, no FFmpeg) - const activeSegments = speechSegments.filter(s => !s.deleted); - const fallbackSections = buildRemappedSectionsFromSegments(activeSegments); - await saveRecoveryTake({ - id: takeId, - createdAt: takeCreatedAt, - screenPath, - cameraPath, - recordedDuration, - sections: sectionsForTimeline, - trimSegments: activeSegments - }); - if (activeSegments.length > 0) { - try { - const computed = await window.electronAPI.computeSections({ - segments: activeSegments - }); - sectionsForTimeline = Array.isArray(computed?.sections) && computed.sections.length > 0 - ? attachSectionTranscripts(computed.sections, fallbackSections) - : (fallbackSections.length > 0 ? fallbackSections : sectionsForTimeline); - if (!matchesActiveProjectSession(projectSession)) return; - } catch (err) { - console.warn('Section computation failed, using fallback sections:', err); - if (fallbackSections.length > 0) sectionsForTimeline = fallbackSections; - } - } else if (hadScribe) { - console.warn('No speech detected, using full recording'); - } - - // Set takeId on all sections - sectionsForTimeline = sectionsForTimeline.map(s => ({ ...s, takeId })); - - // Add take to project before entering editor (video pool needs it) - if (activeProject) { - if (!Array.isArray(activeProject.takes)) activeProject.takes = []; - activeProject.takes.push({ - id: takeId, - createdAt: takeCreatedAt, - duration: recordedDuration, - screenPath, - cameraPath, - proxyPath: null, - sections: sectionsForTimeline - }); - } - - try { - const appendResult = appendTakeToTimeline({ - takeId, - screenPath, - cameraPath, - recordedDuration, - trimSections: sectionsForTimeline, - projectSession + // Safety fallback: if the video frame callback doesn't fire within + // 200 ms (e.g. src was swapped or video stalled), fall back to rAF + editorVideoFrameSafetyTimer = setTimeout(() => { + editorVideoFrameSafetyTimer = null; + if (editorVideoFrameCallbackId !== null && editorState?.playing) { + cancelEditorDrawLoop(); + editorDrawRAF = requestAnimationFrame(() => { + editorDrawRAF = null; + editorDrawLoop(); }); - if (!appendResult || !matchesActiveProjectSession(projectSession)) return; - - if (activeProject && appendResult) { - const take = activeProject.takes.find(t => t.id === takeId); - if (take) { - take.duration = appendResult.takeDuration; - take.sections = appendResult.takeSections; - } - await persistProjectNow(); - // Trigger background proxy generation for the new take - if (activeProjectPath && screenPath) { - proxyStatus.set(takeId, { status: 'pending', percent: 0 }); - renderSectionMarkers(); - window.electronAPI.generateProxy({ takeId, screenPath, projectFolder: activeProjectPath, durationSec: recordedDuration }) - .catch(err => console.warn('[Proxy] Failed to start proxy generation:', err)); - } - } - await completeRecoveryTake(); - } catch (error) { - console.error('Failed to append recording to project timeline:', error); - setWorkspaceView('recording'); } - } else if (finalizeErrors.length > 0) { - console.error('Recording finalize failed:', finalizeErrors); - showProjectHomeMessage(finalizeErrors.join(' ')); - } - updateWorkspaceHeader(); + }, 200); + return; } + } - function updateTimer() { - const elapsed = Math.floor((Date.now() - startTime) / 1000); - const m = String(Math.floor(elapsed / 60)).padStart(2, '0'); - const s = String(elapsed % 60).padStart(2, '0'); - timerEl.textContent = `${m}:${s}`; - } + if (editorState.playing) { + editorDrawRAF = requestAnimationFrame(() => { + editorDrawRAF = null; + editorDrawLoop(); + }); + return; + } - // ===== Editor ===== - - function enterEditor(rawSections, opts = {}) { - // Stop live preview while timeline is active. - if (drawRAF) { cancelAnimationFrame(drawRAF); drawRAF = null; } - lastCompositeDrawAt = 0; - cancelEditorDrawLoop(); - - // Reset timeline zoom - timelineZoom = 1; - editorTimeline.style.minWidth = '100%'; - editorTimelineWrapper.scrollLeft = 0; - - // Clean up previous video pool - cleanupVideoPool(); - - const defaultPipX = CANVAS_W - PIP_SIZE - PIP_MARGIN; - const defaultPipY = CANVAS_H - PIP_SIZE - PIP_MARGIN; - const sections = normalizeSections(rawSections, opts.duration || 0); - - // Calculate duration from sections - const duration = sections.length > 0 - ? sections[sections.length - 1].end - : (opts.duration || 0); - - const sectionKeyframes = sections.map(section => ({ - time: section.start, - pipX: defaultPipX, - pipY: defaultPipY, - pipVisible: true, - cameraFullscreen: false, - backgroundZoom: DEFAULT_SECTION_ZOOM, - backgroundPanX: 0, - backgroundPanY: 0, - sectionId: section.id, - autoSection: true - })); - - const providedKeyframes = Array.isArray(opts.keyframes) && opts.keyframes.length > 0 - ? opts.keyframes.map(kf => ({ + editorPausedDrawTimer = setTimeout( + () => { + editorPausedDrawTimer = null; + editorDrawRAF = requestAnimationFrame(() => { + editorDrawRAF = null; + editorDrawLoop(); + }); + }, + Math.round(1000 / 24) + ); +} + +function setWorkspaceView(nextView) { + activeWorkspaceView = nextView; + const showHome = nextView === 'home'; + const showRecording = nextView === 'recording'; + const showTimeline = nextView === 'timeline' && !!editorState; + const showProcessing = nextView === 'processing'; + + projectHomeView.classList.toggle('hidden', !showHome); + recordingView.classList.toggle('hidden', !showRecording); + editorView.classList.toggle('hidden', !showTimeline); + processingView.classList.toggle('hidden', !showProcessing); + + if (showRecording) { + clearMediaIdleTimer(); + if (!mediaInitialized) void ensureMediaInitialized(); + updatePreview(); + } else if (drawRAF) { + cancelAnimationFrame(drawRAF); + drawRAF = null; + lastCompositeDrawAt = 0; + } + + if (!showRecording && mediaInitialized && !recording && !mediaIdleTimer) { + mediaIdleTimer = setTimeout(() => { + mediaIdleTimer = null; + if (hasActiveRecorders()) return; + cleanupRendererMediaResources(); + }, MEDIA_IDLE_TIMEOUT_MS); + } + + if (showTimeline && editorState && !hasPendingEditorDraw()) { + editorDrawLoop(); + } else if (!showTimeline && hasPendingEditorDraw()) { + cancelEditorDrawLoop(); + if (editorState?.playing) editorPause(); + } + + updateWorkspaceHeader(); +} + +function getProjectTimelineSnapshot() { + if (!editorState) { + return ( + activeProject?.timeline || { + duration: 0, + sections: [], + keyframes: [], + selectedSectionId: null, + hasCamera: false, + sourceWidth: null, + sourceHeight: null + } + ); + } + + return { + duration: Number(editorState.duration) || 0, + sections: Array.isArray(editorState.sections) + ? editorState.sections.map((section) => ({ ...section })) + : [], + keyframes: Array.isArray(editorState.keyframes) + ? editorState.keyframes.map((kf) => ({ ...kf, backgroundZoom: clampSectionZoom(kf.backgroundZoom), backgroundPanX: clampSectionPan(kf.backgroundPanX), backgroundPanY: clampSectionPan(kf.backgroundPanY) })) - : null; - const keyframes = (providedKeyframes || sectionKeyframes).sort((a, b) => a.time - b.time); - - editorState = { - duration, - currentTime: 0, - playing: false, - pipSize: PIP_SIZE, - defaultPipX, - defaultPipY, - keyframes, - sections, - selectedSectionId: opts.selectedSectionId || sections[0]?.id || null, - selectedSectionIds: new Set([opts.selectedSectionId || sections[0]?.id || null].filter(Boolean)), - screenFitMode: opts.screenFitMode || screenFitSelect.value, - rendering: false, - renderProgress: 0, - playbackSpeed: 1, - cameraSyncOffsetMs: normalizeCameraSyncOffsetMs(opts.cameraSyncOffsetMs), - hasCamera: typeof opts.hasCamera === 'boolean' ? opts.hasCamera : false, - sourceWidth: opts.sourceWidth || null, - sourceHeight: opts.sourceHeight || null - }; - screenFitSelect.value = editorState.screenFitMode === 'fit' ? 'fit' : 'fill'; - cameraSyncOffsetInput.value = String(editorState.cameraSyncOffsetMs); - updateSectionZoomControls(); - - // Pre-create video elements for all referenced takes - const referencedTakeIds = new Set(sections.map(s => s.takeId).filter(Boolean)); - for (const takeId of referencedTakeIds) { - getOrCreateTakeVideos(takeId); - } - - // Set up initial active take from first section - if (sections.length > 0) { - const firstSection = sections[0]; - activeTakeId = firstSection.takeId; - activePlaybackSection = firstSection; - const videos = getOrCreateTakeVideos(firstSection.takeId); - if (videos) { - videos.screen.currentTime = firstSection.sourceStart; - if (videos.camera) { - videos.camera.currentTime = resolveCameraPlaybackTargetTime( - firstSection.sourceStart, - editorState.cameraSyncOffsetMs - ); - } - } - } - - // Wait for metadata to get source resolution. - // When a proxy is used for playback, probe the ORIGINAL source for true - // dimensions so exports render at full resolution (not the 960x540 proxy). - if (referencedTakeIds.size > 0) { - const firstTakeId = sections[0]?.takeId; - const firstTake = firstTakeId ? activeProject?.takes?.find(t => t.id === firstTakeId) : null; - const videos = firstTakeId ? getOrCreateTakeVideos(firstTakeId) : null; - if (videos) { - const applySourceResolution = (w, h) => { - if (!editorState) return; - if (w && h) { - editorState.sourceWidth = w; - editorState.sourceHeight = h; - } - syncSectionAnchorKeyframes(); - renderSectionMarkers(); - updateEditorTimeDisplay(); - scheduleProjectSave(); - extractWaveformPeaks().then(peaks => { - if (!editorState) return; - waveformPeaks = peaks; - renderWaveform(); - }); - }; - - if (firstTake?.proxyPath && firstTake?.screenPath) { - // Proxy is used for playback — probe original for true dimensions - const sourceProbe = document.createElement('video'); - sourceProbe.preload = 'metadata'; - sourceProbe.src = pathToFileUrl(firstTake.screenPath); - sourceProbe.addEventListener('loadedmetadata', () => { - applySourceResolution(sourceProbe.videoWidth, sourceProbe.videoHeight); - sourceProbe.src = ''; - }, { once: true }); - } else { - // No proxy — read dimensions from the pool video directly - const onMeta = () => applySourceResolution(videos.screen.videoWidth, videos.screen.videoHeight); - if (videos.screen.readyState >= 1) onMeta(); - else videos.screen.addEventListener('loadedmetadata', onMeta, { once: true }); - } - } - } - - updateEditorTimeDisplay(); - renderSectionMarkers(); - preloadSectionImages(); - const initialView = opts.initialView === 'recording' ? 'recording' : 'timeline'; - setWorkspaceView(initialView); - } - - function _exitEditor() { - setWorkspaceView('recording'); - } - - function getStateAtTime(time) { - const defaultKf = { - time: 0, - pipX: editorState.defaultPipX, - pipY: editorState.defaultPipY, - pipVisible: true, - cameraFullscreen: false, - backgroundZoom: DEFAULT_SECTION_ZOOM, - backgroundPanX: 0, - backgroundPanY: 0 - }; - const userKfs = editorState.keyframes; - const kfs = userKfs.length > 0 && userKfs[0].time === 0 ? userKfs : [defaultKf, ...userKfs]; - - // Find active keyframe (last one at or before time) - let activeIdx = 0; - for (let i = 1; i < kfs.length; i++) { - if (kfs[i].time <= time) activeIdx = i; - else break; - } - - const active = kfs[activeIdx]; - const next = activeIdx < kfs.length - 1 ? kfs[activeIdx + 1] : null; - - let pipX = active.pipX; - let pipY = active.pipY; - let opacity = active.pipVisible ? 1 : 0; - let cameraFullscreen = active.cameraFullscreen || false; - let camTransition = cameraFullscreen ? 1 : 0; - let backgroundZoom = clampSectionZoom(active.backgroundZoom); - let backgroundPanX = clampSectionPan(active.backgroundPanX); - let backgroundPanY = clampSectionPan(active.backgroundPanY); - let backgroundFocusX = panToFocusCoord(backgroundZoom, backgroundPanX, 0.5); - let backgroundFocusY = panToFocusCoord(backgroundZoom, backgroundPanY, 0.5); - - // Transition toward next keyframe at end of current section - if (next) { - const remaining = next.time - time; - if (remaining > 0 && remaining < TRANSITION_DURATION) { - const t = 1 - remaining / TRANSITION_DURATION; - const nextVisible = next.pipVisible !== undefined ? next.pipVisible : true; - const nextFullscreen = next.cameraFullscreen || false; - - if (active.pipVisible !== nextVisible) { - // Visibility transition: fade in/out toward next state - if (nextVisible) { - // Fading in: use next position (where camera will appear) - opacity = t; - pipX = next.pipX; - pipY = next.pipY; - cameraFullscreen = nextFullscreen; - camTransition = nextFullscreen ? 1 : 0; - } else { - // Fading out: keep current position - opacity = 1 - t; - camTransition = cameraFullscreen ? 1 : 0; - } - } else { - // No visibility change - handle position and fullscreen transitions - if (cameraFullscreen !== nextFullscreen) { - camTransition = nextFullscreen ? t : 1 - t; - if (!nextFullscreen) { - // Shrinking from fullscreen: use next pip position as destination - pipX = next.pipX; - pipY = next.pipY; - } - } - - if (!cameraFullscreen && !nextFullscreen - && (active.pipX !== next.pipX || active.pipY !== next.pipY)) { - pipX = active.pipX + (next.pipX - active.pipX) * t; - pipY = active.pipY + (next.pipY - active.pipY) * t; - } - } - - if (Math.abs(backgroundZoom - clampSectionZoom(next.backgroundZoom)) > 0.0001) { - backgroundZoom = backgroundZoom + (clampSectionZoom(next.backgroundZoom) - backgroundZoom) * t; - } - const nextFocusX = panToFocusCoord(next.backgroundZoom, next.backgroundPanX, 0.5); - const nextFocusY = panToFocusCoord(next.backgroundZoom, next.backgroundPanY, 0.5); - backgroundFocusX = backgroundFocusX + (nextFocusX - backgroundFocusX) * t; - backgroundFocusY = backgroundFocusY + (nextFocusY - backgroundFocusY) * t; - backgroundPanX = focusToPanCoord(backgroundZoom, backgroundFocusX, backgroundPanX); - backgroundPanY = focusToPanCoord(backgroundZoom, backgroundFocusY, backgroundPanY); - } - } - - return { - pipX, - pipY, - pipVisible: opacity > 0, - opacity, - cameraFullscreen, - camTransition, - backgroundZoom, - backgroundPanX, - backgroundPanY, - backgroundFocusX, - backgroundFocusY - }; - } - - function formatTime(seconds) { - const m = String(Math.floor(seconds / 60)).padStart(2, '0'); - const s = String(Math.floor(seconds % 60)).padStart(2, '0'); - return `${m}:${s}`; - } - - function updateEditorTimeDisplay() { - if (!editorState) return; - const selectedSection = getSelectedSection(); - const sectionText = selectedSection ? ` | ${selectedSection.label}` : ''; - const speedText = editorState.playbackSpeed !== 1 ? ` [${editorState.playbackSpeed}x]` : ''; - editorTimeEl.textContent = `${formatTime(editorState.currentTime)} / ${formatTime(editorState.duration)}${speedText}${sectionText}`; - } - - function switchPlaybackSection(nextSection, opts = {}) { - if (!editorState || !nextSection) return false; - const previousTakeId = activeTakeId; - const sameTake = previousTakeId === nextSection.takeId; - const nextVideos = getOrCreateTakeVideos(nextSection.takeId); - if (!nextVideos) return false; - - const targetSourceTime = Number.isFinite(Number(opts.sourceTime)) - ? Number(opts.sourceTime) - : nextSection.sourceStart; - const seekPlan = computePlaybackSeekPlan( - nextVideos.screen.currentTime, - nextVideos.camera?.currentTime, - targetSourceTime, - editorState.cameraSyncOffsetMs - ); - - if (!sameTake && previousTakeId) { - const previousVideos = getOrCreateTakeVideos(previousTakeId); - if (previousVideos) { - previousVideos.screen.pause(); - if (previousVideos.camera) { - previousVideos.camera.pause(); - previousVideos.camera.playbackRate = 1; - } - } - } - - if (seekPlan.screenNeedsSeek) nextVideos.screen.currentTime = seekPlan.targetSourceTime; - if (nextVideos.camera && seekPlan.cameraNeedsSeek) nextVideos.camera.currentTime = seekPlan.targetCameraTime; - - activeTakeId = nextSection.takeId; - activePlaybackSection = nextSection; - - if (opts.logSwitch) { - console.debug('[Editor] Section switch', { - from: opts.fromSectionId || null, - to: nextSection.id, - sameTake, - seek: seekPlan.needsSeek, - reason: opts.reason || 'unknown' - }); - } - - if (opts.resumePlayback) { - const speed = editorState.playbackSpeed || 1; - nextVideos.screen.playbackRate = speed; - if (nextVideos.screen.paused) nextVideos.screen.play().catch(() => {}); - if (editorState.hasCamera && nextVideos.camera && nextVideos.camera.paused) { - nextVideos.camera.playbackRate = speed; - nextVideos.camera.play().catch(() => {}); - } - } - - return true; - } - - function syncCameraPlayback(videos) { - if (!editorState?.hasCamera || !videos?.camera) return; - - const baseRate = editorState.playbackSpeed || 1; - const drift = computeCameraPlaybackDrift( - videos.screen.currentTime, - videos.camera.currentTime, - editorState.cameraSyncOffsetMs - ); - const absDrift = Math.abs(drift); - const now = performance.now(); - - if (absDrift >= CAMERA_DRIFT_HARD_THRESHOLD && now >= cameraResyncCooldownUntil) { - videos.camera.currentTime = resolveCameraPlaybackTargetTime( - videos.screen.currentTime, - editorState.cameraSyncOffsetMs - ); - videos.camera.playbackRate = baseRate; - cameraResyncCooldownUntil = now + CAMERA_RESYNC_COOLDOWN_MS; - console.debug('[Editor] Camera hard resync', { - drift: Number(drift.toFixed(3)), - threshold: CAMERA_DRIFT_HARD_THRESHOLD - }); - return; - } - - if (absDrift >= CAMERA_DRIFT_SOFT_THRESHOLD) { - const correction = Math.min(0.06, absDrift * 0.5); - const targetRate = drift > 0 ? baseRate + correction : baseRate - correction; - const clampedRate = Math.max(baseRate - 0.08, Math.min(baseRate + 0.08, targetRate)); - if (Math.abs(videos.camera.playbackRate - clampedRate) > 0.004) { - videos.camera.playbackRate = clampedRate; - } - if (absDrift >= CAMERA_DRIFT_LOG_THRESHOLD && now - lastCameraDriftLogAt >= CAMERA_DRIFT_LOG_INTERVAL_MS) { - console.debug('[Editor] Camera drift', { - drift: Number(drift.toFixed(3)), - playbackRate: Number(clampedRate.toFixed(3)) - }); - lastCameraDriftLogAt = now; - } - } else if (Math.abs(videos.camera.playbackRate - baseRate) > 0.001) { - videos.camera.playbackRate = baseRate; - } - } - - function editorPlay() { - if (!editorState || editorState.rendering) return; - editorState.playing = true; - const speed = editorState.playbackSpeed || 1; - if (activeTakeId) { - const videos = getOrCreateTakeVideos(activeTakeId); - if (videos) { - videos.screen.playbackRate = speed; - videos.screen.play().catch(() => {}); - if (editorState.hasCamera && videos.camera) { - videos.camera.playbackRate = speed; - videos.camera.play().catch(() => {}); - } - } - } - editorPlayBtn.textContent = 'Pause'; - } - - function editorPause() { - if (!editorState) return; - editorState.playing = false; - for (const [, videos] of takeVideoPool) { - videos.screen.pause(); - videos.screen.playbackRate = 1; - if (videos.camera) { - videos.camera.pause(); - videos.camera.playbackRate = 1; - } - } - editorPlayBtn.textContent = 'Play'; - } - - function editorTogglePlay() { - if (!editorState) return; - if (editorState.playing) editorPause(); - else editorPlay(); - } - - function cyclePlaybackSpeed() { - if (!editorState) return; - const speeds = [1, 1.5, 2]; - const idx = speeds.indexOf(editorState.playbackSpeed); - editorState.playbackSpeed = speeds[(idx + 1) % speeds.length]; - if (editorState.playing && activeTakeId) { - const videos = getOrCreateTakeVideos(activeTakeId); - if (videos) { - videos.screen.playbackRate = editorState.playbackSpeed; - if (editorState.hasCamera && videos.camera) { - videos.camera.playbackRate = editorState.playbackSpeed; - } - } - } - updateEditorTimeDisplay(); - } - - function editorSeek(time) { - if (!editorState) return; - time = Math.max(0, Math.min(time, editorState.duration)); - editorState.currentTime = time; - - const resolved = resolveTimeToSource(time); - if (resolved) { - switchPlaybackSection( - resolved.section, - { - sourceTime: resolved.sourceTime, - resumePlayback: editorState.playing, - reason: 'seek', - fromSectionId: activePlaybackSection?.id - } - ); - } - updateEditorTimeDisplay(); - updateScrubberPosition(); - } - - function updateScrubberPosition() { - if (!editorState || editorState.duration <= 0) return; - const pct = (editorState.currentTime / editorState.duration) * 100; - editorScrubber.style.left = pct + '%'; - if (editorState.playing) scrollTimelineToPlayhead(); - } - - function editorDrawLoop() { - if (!editorState) return; - - if (editorState.playing && activeTakeId && activePlaybackSection) { - const videos = getOrCreateTakeVideos(activeTakeId); - if (videos) { - const sourceTime = videos.screen.currentTime; - const timelineTime = activePlaybackSection.start + (sourceTime - activePlaybackSection.sourceStart); - editorState.currentTime = timelineTime; - - // Check if we've passed the current section's end - if (sourceTime >= activePlaybackSection.sourceEnd - 0.01) { - const currentIdx = editorState.sections.indexOf(activePlaybackSection); - const nextSection = editorState.sections[currentIdx + 1]; - - if (nextSection) { - const fromSectionId = activePlaybackSection?.id; - const sameTake = activeTakeId === nextSection.takeId; - const contiguousSource = sameTake - && Math.abs(sourceTime - nextSection.sourceStart) <= 0.05; - switchPlaybackSection( - nextSection, - { - sourceTime: contiguousSource ? sourceTime : nextSection.sourceStart, - resumePlayback: true, - logSwitch: true, - reason: 'boundary', - fromSectionId - } - ); - } else { - // End of timeline - editorPause(); - } - } - - syncCameraPlayback(videos); - } - - updateEditorTimeDisplay(); - updateScrubberPosition(); - } - - // Draw composite frame - editorCtx.fillStyle = '#000'; - editorCtx.fillRect(0, 0, CANVAS_W, CANVAS_H); - - const activeVideos = activeTakeId ? getOrCreateTakeVideos(activeTakeId) : null; - const hasScreen = activeVideos && activeVideos.screen.videoWidth > 0; - const hasCamera = editorState.hasCamera && activeVideos?.camera && activeVideos.camera.videoWidth > 0; - const state = getStateAtTime(editorState.currentTime); - - const currentSection = findSectionForTime(editorState.currentTime); - const sectionImage = currentSection?.imagePath ? sectionImageCache.get(currentSection.imagePath) : null; - - if (sectionImage) { - drawEditorScreenWithZoom( - editorCtx, - sectionImage, - editorState.screenFitMode, - state.backgroundZoom, - state.backgroundPanX, - state.backgroundPanY, - state.backgroundFocusX, - state.backgroundFocusY - ); - } else if (hasScreen) { - drawEditorScreenWithZoom( - editorCtx, - activeVideos.screen, - editorState.screenFitMode, - state.backgroundZoom, - state.backgroundPanX, - state.backgroundPanY, - state.backgroundFocusX, - state.backgroundFocusY - ); - } - - if (hasCamera) { - if (state.camTransition > 0 && state.opacity > 0) { - editorCtx.save(); - if (state.opacity < 1) editorCtx.globalAlpha = state.opacity; - const t = easeInOut(state.camTransition); - const camX = state.pipX * (1 - t); - const camY = state.pipY * (1 - t); - const camW = editorState.pipSize + (CANVAS_W - editorState.pipSize) * t; - const camH = editorState.pipSize + (CANVAS_H - editorState.pipSize) * t; - const camR = 12 * (1 - t); - drawCameraRect(editorCtx, activeVideos.camera, camX, camY, camW, camH, camR); - editorCtx.restore(); - } else if (state.opacity > 0) { - editorCtx.save(); - editorCtx.globalAlpha = state.opacity; - drawPip(editorCtx, activeVideos.camera, state.pipX, state.pipY, editorState.pipSize, editorState.pipSize); - editorCtx.restore(); - } - } - - scheduleEditorDrawLoop(); - } - - // ===== Keyframe management ===== - - function getMutableCameraKeyframe() { - if (!editorState) return null; - - const selectedSection = getSelectedSection(); - if (selectedSection) { - return getSectionAnchorKeyframe(selectedSection.id, true); - } - - const section = findSectionForTime(editorState.currentTime); - if (section) { - return getSectionAnchorKeyframe(section.id, true); - } - - return null; - } - - function toggleCameraVisibility() { - if (!editorState || editorState.rendering) return; - const target = getMutableCameraKeyframe(); - if (!target) return; - pushUndo(); - target.pipVisible = !target.pipVisible; - scheduleProjectSave(); - } - - function toggleCameraFullscreen() { - if (!editorState || editorState.rendering) return; - const target = getMutableCameraKeyframe(); - if (!target) return; - pushUndo(); - target.cameraFullscreen = !(target.cameraFullscreen || false); - scheduleProjectSave(); - } - - function setSelectedSectionBackgroundZoom(nextZoom, opts = {}) { - if (!editorState || editorState.rendering) return false; - const pushHistory = opts.pushHistory === true; - const selectedSection = getSelectedSection(); - if (!selectedSection) return false; - const anchor = getSectionAnchorKeyframe(selectedSection.id, true); - if (!anchor) return false; - const normalizedZoom = clampSectionZoom(nextZoom); - const currentZoom = clampSectionZoom(anchor.backgroundZoom); - if (Math.abs(normalizedZoom - currentZoom) < 0.0001) { - updateSectionZoomControls(); - return false; - } - if (pushHistory) pushUndo(); - anchor.backgroundZoom = normalizedZoom; - updateSectionZoomControls(); - return true; - } - - function setSectionBackgroundPan(sectionId, nextPanX, nextPanY) { - if (!editorState || editorState.rendering || !sectionId) return false; - const anchor = getSectionAnchorKeyframe(sectionId, true); - if (!anchor) return false; - const normalizedPanX = clampSectionPan(nextPanX); - const normalizedPanY = clampSectionPan(nextPanY); - const currentPanX = clampSectionPan(anchor.backgroundPanX); - const currentPanY = clampSectionPan(anchor.backgroundPanY); - if ( - Math.abs(normalizedPanX - currentPanX) < 0.0001 - && Math.abs(normalizedPanY - currentPanY) < 0.0001 - ) { - return false; - } - anchor.backgroundPanX = normalizedPanX; - anchor.backgroundPanY = normalizedPanY; - return true; - } - - function commitSectionZoomChange() { - if (!sectionZoomDragActive) return; - sectionZoomDragActive = false; - scheduleProjectSave(); - } - - // ===== PiP drag-to-reposition ===== - - function canvasToEditorCoords(clientX, clientY) { - const rect = editorCanvas.getBoundingClientRect(); - const scaleX = CANVAS_W / rect.width; - const scaleY = CANVAS_H / rect.height; - return { - x: (clientX - rect.left) * scaleX, - y: (clientY - rect.top) * scaleY - }; - } - - editorCanvas.addEventListener('mousedown', (e) => { - if (!editorState || editorState.rendering) return; - const activeSection = findSectionForTime(editorState.currentTime); - if (activeSection) selectEditorSection(activeSection.id); - const { x, y } = canvasToEditorCoords(e.clientX, e.clientY); - const kf = getStateAtTime(editorState.currentTime); - if (editorState.hasCamera && kf.pipVisible && kf.camTransition <= 0) { - const pipW = editorState.pipSize; - const pipH = editorState.pipSize; - if (x >= kf.pipX && x <= kf.pipX + pipW && y >= kf.pipY && y <= kf.pipY + pipH) { - pipDragMoved = false; - pushUndo(); - draggingPip = true; - e.preventDefault(); - return; - } - } - - if (!activeSection || kf.backgroundZoom <= 1.0001 || (kf.cameraFullscreen && kf.opacity > 0)) return; - const initialPan = getSectionBackgroundPan(activeSection.id); - pushUndo(); - backgroundDragMoved = false; - draggingBackground = true; - backgroundDragState = { - sectionId: activeSection.id, - startMouseX: x, - startMouseY: y, - startPanX: initialPan.x, - startPanY: initialPan.y, - zoom: kf.backgroundZoom - }; - e.preventDefault(); + : [], + selectedSectionId: editorState.selectedSectionId || null, + hasCamera: !!editorState.hasCamera, + sourceWidth: editorState.sourceWidth || null, + sourceHeight: editorState.sourceHeight || null + }; +} + +function buildProjectSavePayload() { + if (!activeProject) return null; + return { + ...activeProject, + settings: { + screenFitMode: screenFitSelect.value || 'fill', + hideFromRecording: hideFromRecording === 'true', + exportAudioPreset: normalizeExportAudioPreset(exportAudioPresetSelect.value), + exportVideoPreset: normalizeExportVideoPreset(exportVideoPresetSelect.value), + cameraSyncOffsetMs: normalizeCameraSyncOffsetMs(cameraSyncOffsetInput.value) + }, + timeline: getProjectTimelineSnapshot() + }; +} + +async function persistProjectNow() { + if (!activeProjectPath || !activeProject) return; + const expectedProjectPath = activeProjectPath; + const payload = buildProjectSavePayload(); + if (!payload) return; + + persistQueue = persistQueue + .then(async () => { + const result = await window.electronAPI.projectSave({ + projectPath: expectedProjectPath, + project: payload + }); + if (result?.projectPath && result?.project && activeProjectPath === expectedProjectPath) { + activeProjectPath = result.projectPath; + activeProject = result.project; + saveFolder = activeProjectPath; + folderPathEl.textContent = activeProjectPath; + openFolderBtn.classList.toggle('hidden', !activeProjectPath); + updateWorkspaceHeader(); + await window.electronAPI.projectSetLast(expectedProjectPath); + } + }) + .catch((error) => { + console.error('Failed to persist project:', error); }); - window.addEventListener('mousemove', (e) => { - if (draggingBackground && editorState && backgroundDragState) { - const { x, y } = canvasToEditorCoords(e.clientX, e.clientY); - const deltaX = x - backgroundDragState.startMouseX; - const deltaY = y - backgroundDragState.startMouseY; - const { maxOffsetX, maxOffsetY } = getZoomCropBounds(backgroundDragState.zoom); - const nextPanX = maxOffsetX > 0 - ? backgroundDragState.startPanX - (deltaX / maxOffsetX) - : 0; - const nextPanY = maxOffsetY > 0 - ? backgroundDragState.startPanY - (deltaY / maxOffsetY) - : 0; - backgroundDragMoved = setSectionBackgroundPan(backgroundDragState.sectionId, nextPanX, nextPanY) || backgroundDragMoved; - return; - } + await persistQueue; +} - if (!draggingPip || !editorState) return; - pipDragMoved = true; - const { x, y } = canvasToEditorCoords(e.clientX, e.clientY); - const snapped = snapToNearestCorner(x, y); - - const selectedSection = getSelectedSection(); - const section = selectedSection || findSectionForTime(editorState.currentTime); - if (section) { - const anchor = getSectionAnchorKeyframe(section.id, true); - if (anchor) { - anchor.pipX = snapped.x; - anchor.pipY = snapped.y; - } - } +async function saveRecoveryTake(take) { + if (!activeProjectPath || !take?.screenPath) return; + try { + await window.electronAPI.projectSetRecoveryTake({ + projectPath: activeProjectPath, + take + }); + } catch (error) { + console.error('Failed to save recovery take:', error); + } +} + +async function _clearRecoveryTake(projectPath = activeProjectPath) { + if (!projectPath) return; + try { + await window.electronAPI.projectClearRecoveryTake(projectPath); + } catch (error) { + console.error('Failed to clear recovery take:', error); + } +} + +async function completeRecoveryTake(projectPath = activeProjectPath) { + if (!projectPath) return; + try { + await window.electronAPI.projectCompleteRecoveryTake(projectPath); + } catch (error) { + console.error('Failed to finalize recovery take:', error); + } +} + +function scheduleProjectSave() { + if (!activeProjectPath || !activeProject) return; + if (saveDebounceTimer) clearTimeout(saveDebounceTimer); + saveDebounceTimer = setTimeout(() => { + persistProjectNow().catch((error) => { + console.error('Failed to save project:', error); + }); + }, 250); +} + +async function flushScheduledProjectSave() { + if (saveDebounceTimer) { + clearTimeout(saveDebounceTimer); + saveDebounceTimer = null; + } + await persistProjectNow(); +} + +function clearEditorState() { + editorPause(); + cancelEditorDrawLoop(); + + cleanupVideoPool(); + editorState = null; + proxyStatus.clear(); + undoStack.length = 0; + redoStack.length = 0; + waveformPeaks = null; + renderWaveform(); + renderSectionMarkers(); + updateSectionZoomControls(); + updateWorkspaceHeader(); + updateUndoRedoButtons(); +} + +function snapshotTimeline() { + return { + sections: editorState.sections.map((s) => ({ ...s })), + keyframes: editorState.keyframes.map((kf) => ({ ...kf })), + selectedSectionId: editorState.selectedSectionId, + selectedSectionIds: new Set(editorState.selectedSectionIds || []), + duration: editorState.duration + }; +} + +function restoreSnapshot(snapshot) { + editorState.sections = snapshot.sections; + editorState.keyframes = snapshot.keyframes; + editorState.selectedSectionId = snapshot.selectedSectionId; + editorState.selectedSectionIds = + snapshot.selectedSectionIds || new Set([snapshot.selectedSectionId].filter(Boolean)); + editorState.duration = snapshot.duration; + recalculateTimelinePositions(); + syncSectionAnchorKeyframes(); + renderSectionMarkers(); + updateSectionZoomControls(); + refreshWaveform(); + editorSeek(Math.min(editorState.currentTime, editorState.duration)); + updateUndoRedoButtons(); + scheduleProjectSave(); +} + +function pushUndo() { + if (!editorState) return; + undoStack.push(snapshotTimeline()); + if (undoStack.length > MAX_UNDO) undoStack.shift(); + redoStack.length = 0; + updateUndoRedoButtons(); +} + +function editorUndo() { + if (!editorState || editorState.rendering || undoStack.length === 0) return; + redoStack.push(snapshotTimeline()); + restoreSnapshot(undoStack.pop()); +} + +function editorRedo() { + if (!editorState || editorState.rendering || redoStack.length === 0) return; + undoStack.push(snapshotTimeline()); + restoreSnapshot(redoStack.pop()); +} + +function updateUndoRedoButtons() { + editorUndoBtn.disabled = undoStack.length === 0; + editorRedoBtn.disabled = redoStack.length === 0; +} + +function renderRecentProjects(meta) { + const projects = Array.isArray(meta?.projects) ? meta.projects : []; + const lastPath = meta?.lastProjectPath || ''; + + recentProjectsList.innerHTML = ''; + if (projects.length === 0) { + const empty = document.createElement('div'); + empty.className = 'text-sm text-neutral-600 py-2'; + empty.textContent = 'No recent projects yet.'; + recentProjectsList.appendChild(empty); + } else { + for (const project of projects) { + const btn = document.createElement('button'); + btn.className = + 'w-full text-left bg-neutral-900 border border-neutral-800 rounded-lg px-3.5 py-2.5 hover:bg-neutral-800 hover:border-neutral-700 transition-all'; + btn.type = 'button'; + btn.dataset.projectPath = project.projectPath; + + const title = document.createElement('div'); + title.className = 'text-sm text-neutral-100 truncate font-medium'; + title.textContent = project.name || 'Untitled Project'; + const subtitle = document.createElement('div'); + subtitle.className = 'text-xs text-neutral-500 truncate mt-0.5'; + subtitle.textContent = `${project.projectPath} • ${formatProjectDate(project.updatedAt)}`; + btn.appendChild(title); + btn.appendChild(subtitle); + recentProjectsList.appendChild(btn); + } + } + + const last = projects.find((project) => project.projectPath === lastPath) || projects[0]; + if (last) { + lastProjectName.textContent = last.name || 'Untitled Project'; + lastProjectPath.textContent = last.projectPath; + resumeLastBtn.dataset.projectPath = last.projectPath; + lastProjectRow.classList.remove('hidden'); + } else { + lastProjectRow.classList.add('hidden'); + resumeLastBtn.dataset.projectPath = ''; + } +} + +function clearProjectHomeMessage() { + projectHomeMessage.textContent = ''; + projectHomeMessage.className = 'hidden rounded border px-3 py-2 text-sm'; +} + +function showProjectHomeMessage(message, tone = 'error') { + if (!message) { + clearProjectHomeMessage(); + return; + } + + const toneClass = + tone === 'info' + ? 'border-blue-500/40 bg-blue-500/10 text-blue-200' + : 'border-red-500/40 bg-red-500/10 text-red-200'; + + projectHomeMessage.textContent = message; + projectHomeMessage.className = `rounded border px-3 py-2 text-sm ${toneClass}`; +} + +async function refreshRecentProjects() { + try { + const recent = await window.electronAPI.projectListRecent(8); + renderRecentProjects(recent || {}); + } catch (error) { + console.error('Failed to list recent projects:', error); + renderRecentProjects({ projects: [], lastProjectPath: null }); + } +} + +async function activateProject(projectPath, project, preferredView = 'recording') { + if (!projectPath || !project) return; + + await flushScheduledProjectSave(); + clearEditorState(); + activeProjectSession += 1; + + activeProjectPath = projectPath; + activeProject = project; + saveFolder = projectPath; + folderPathEl.textContent = projectPath; + openFolderBtn.classList.remove('hidden'); + screenFitSelect.value = project.settings?.screenFitMode === 'fit' ? 'fit' : 'fill'; + hideFromRecording = project.settings?.hideFromRecording === false ? 'false' : 'true'; + exportAudioPresetSelect.value = normalizeExportAudioPreset(project.settings?.exportAudioPreset); + exportVideoPresetSelect.value = normalizeExportVideoPreset(project.settings?.exportVideoPreset); + cameraSyncOffsetInput.value = String( + normalizeCameraSyncOffsetMs(project.settings?.cameraSyncOffsetMs) + ); + await syncContentProtection(); + + if ( + project.timeline && + Array.isArray(project.timeline.sections) && + project.timeline.sections.length > 0 + ) { + enterEditor(project.timeline.sections, { + duration: project.timeline.duration || 0, + keyframes: project.timeline.keyframes || [], + selectedSectionId: project.timeline.selectedSectionId || null, + hasCamera: !!project.timeline.hasCamera, + sourceWidth: project.timeline.sourceWidth || null, + sourceHeight: project.timeline.sourceHeight || null, + cameraSyncOffsetMs: project.settings?.cameraSyncOffsetMs, + initialView: preferredView === 'recording' ? 'recording' : 'timeline' + }); + } else { + setWorkspaceView('recording'); + } + + await window.electronAPI.projectSetLast(projectPath); + updateWorkspaceHeader(); + + // Queue background proxy generation for any takes missing a proxy + if (Array.isArray(project.takes)) { + let needsMarkerUpdate = false; + for (const take of project.takes) { + if (!take.proxyPath && take.screenPath) { + proxyStatus.set(take.id, { status: 'pending', percent: 0 }); + needsMarkerUpdate = true; + window.electronAPI + .generateProxy({ + takeId: take.id, + screenPath: take.screenPath, + projectFolder: projectPath, + durationSec: take.duration || 0 + }) + .catch((err) => console.warn('[Proxy] Failed to start proxy generation:', err)); + } + } + if (needsMarkerUpdate) renderSectionMarkers(); + } +} + +async function ensureMediaInitialized() { + if (mediaInitialized) return; + mediaInitialized = true; + await enumerateDevices(); + try { + await updateScreenStream(); + } catch (error) { + console.warn('Screen source init failed:', error); + } + try { + await updateCameraStream(); + } catch (error) { + console.warn('Camera source init failed:', error); + } + try { + await updateAudioStream(); + } catch (error) { + console.warn('Audio source init failed:', error); + } + if (activeWorkspaceView === 'recording') updatePreview(); +} + +async function syncContentProtection() { + const enabled = hideFromRecording === 'true'; + contentProtectionToggle.checked = enabled; + + try { + await window.electronAPI.setContentProtection(enabled); + } catch (error) { + console.error('Failed to update content protection:', error); + } +} + +function findSectionForTime(time) { + if (!editorState || !editorState.sections || editorState.sections.length === 0) return null; + const sections = editorState.sections; + for (let i = 0; i < sections.length; i++) { + const section = sections[i]; + const isLast = i === sections.length - 1; + if (time >= section.start && (time < section.end || (isLast && time <= section.end + 0.001))) { + return section; + } + } + if (time < sections[0].start) return sections[0]; + return sections[sections.length - 1]; +} + +function getSelectedSection() { + if (!editorState || !editorState.sections || editorState.sections.length === 0) return null; + return ( + editorState.sections.find((section) => section.id === editorState.selectedSectionId) || + editorState.sections[0] + ); +} + +function getSectionBackgroundZoom(sectionId) { + if (!editorState || !sectionId) return DEFAULT_SECTION_ZOOM; + const anchor = editorState.keyframes.find((kf) => kf.sectionId === sectionId); + return clampSectionZoom(anchor?.backgroundZoom); +} + +function getSectionBackgroundPan(sectionId) { + if (!editorState || !sectionId) return { x: 0, y: 0 }; + const anchor = editorState.keyframes.find((kf) => kf.sectionId === sectionId); + return { + x: clampSectionPan(anchor?.backgroundPanX), + y: clampSectionPan(anchor?.backgroundPanY) + }; +} + +function updateSectionZoomControls() { + if (!editorBgZoomInput || !editorBgZoomValue) return; + const selectedSection = getSelectedSection(); + const disabled = !editorState || editorState.rendering || !selectedSection; + const zoom = selectedSection + ? getSectionBackgroundZoom(selectedSection.id) + : DEFAULT_SECTION_ZOOM; + editorBgZoomInput.disabled = disabled; + editorBgZoomInput.value = String(zoom); + editorBgZoomValue.textContent = formatSectionZoom(zoom); +} + +function getSectionAnchorKeyframe(sectionId, createIfMissing) { + if (!editorState || !sectionId) return null; + + let anchor = editorState.keyframes.find((kf) => kf.sectionId === sectionId); + if (anchor || !createIfMissing) return anchor || null; + + const section = editorState.sections.find((s) => s.id === sectionId); + if (!section) return null; + + const fallback = getStateAtTime(section.start); + anchor = { + time: section.start, + pipX: fallback.pipX, + pipY: fallback.pipY, + pipVisible: fallback.pipVisible, + cameraFullscreen: fallback.cameraFullscreen || false, + backgroundZoom: clampSectionZoom(fallback.backgroundZoom), + backgroundPanX: clampSectionPan(fallback.backgroundPanX), + backgroundPanY: clampSectionPan(fallback.backgroundPanY), + sectionId: section.id, + autoSection: true + }; + editorState.keyframes.push(anchor); + editorState.keyframes.sort((a, b) => a.time - b.time); + return anchor; +} + +function syncSectionAnchorKeyframes() { + if (!editorState || !editorState.sections || editorState.sections.length === 0) return; + + const manual = editorState.keyframes + .filter((kf) => !kf.sectionId) + .map((kf) => ({ + ...kf, + backgroundZoom: clampSectionZoom(kf.backgroundZoom), + backgroundPanX: clampSectionPan(kf.backgroundPanX), + backgroundPanY: clampSectionPan(kf.backgroundPanY) + })); + const sectionAnchors = editorState.sections.map((section) => { + const existing = editorState.keyframes.find((kf) => kf.sectionId === section.id); + return { + time: section.start, + pipX: existing ? existing.pipX : editorState.defaultPipX, + pipY: existing ? existing.pipY : editorState.defaultPipY, + pipVisible: existing ? existing.pipVisible : true, + cameraFullscreen: existing ? !!existing.cameraFullscreen : false, + backgroundZoom: existing ? clampSectionZoom(existing.backgroundZoom) : DEFAULT_SECTION_ZOOM, + backgroundPanX: existing ? clampSectionPan(existing.backgroundPanX) : 0, + backgroundPanY: existing ? clampSectionPan(existing.backgroundPanY) : 0, + sectionId: section.id, + autoSection: true + }; + }); + + editorState.keyframes = [...sectionAnchors, ...manual].sort((a, b) => a.time - b.time); + + if (!editorState.sections.some((section) => section.id === editorState.selectedSectionId)) { + editorState.selectedSectionId = editorState.sections[0].id; + } +} + +function selectEditorSection(sectionId, shiftKey) { + if (!editorState || !editorState.sections || editorState.sections.length === 0) return; + if (!editorState.sections.some((section) => section.id === sectionId)) return; + commitSectionZoomChange(); + + if (shiftKey && editorState.selectedSectionId) { + const anchorIndex = editorState.sections.findIndex( + (s) => s.id === editorState.selectedSectionId + ); + const targetIndex = editorState.sections.findIndex((s) => s.id === sectionId); + if (anchorIndex >= 0 && targetIndex >= 0) { + const lo = Math.min(anchorIndex, targetIndex); + const hi = Math.max(anchorIndex, targetIndex); + editorState.selectedSectionIds = new Set( + editorState.sections.slice(lo, hi + 1).map((s) => s.id) + ); + } + } else { + editorState.selectedSectionId = sectionId; + editorState.selectedSectionIds = new Set([sectionId]); + } + + renderSectionMarkers(); + updateSectionZoomControls(); + updateEditorTimeDisplay(); + scheduleProjectSave(); +} + +function applyStyleToFutureSections() { + if (!editorState || !editorState.sections || editorState.sections.length === 0) return; + + const currentSection = getSelectedSection(); + if (!currentSection) return; + + const currentAnchor = getSectionAnchorKeyframe(currentSection.id, true); + if (!currentAnchor) return; + + const currentIndex = editorState.sections.findIndex((s) => s.id === currentSection.id); + const futureSections = editorState.sections.slice(currentIndex + 1); + if (futureSections.length === 0) return; + + pushUndo(); + + for (const section of futureSections) { + const anchor = getSectionAnchorKeyframe(section.id, true); + if (!anchor) continue; + anchor.pipX = currentAnchor.pipX; + anchor.pipY = currentAnchor.pipY; + anchor.pipVisible = currentAnchor.pipVisible; + anchor.cameraFullscreen = currentAnchor.cameraFullscreen; + anchor.backgroundZoom = clampSectionZoom(currentAnchor.backgroundZoom); + anchor.backgroundPanX = clampSectionPan(currentAnchor.backgroundPanX); + anchor.backgroundPanY = clampSectionPan(currentAnchor.backgroundPanY); + } + + renderSectionMarkers(); + updateSectionZoomControls(); + editorSeek(editorState.currentTime); + updateEditorTimeDisplay(); + scheduleProjectSave(); +} + +function _deleteNearestKeyframe() { + if (!editorState || !Array.isArray(editorState.keyframes)) return; + + const manualKeyframes = editorState.keyframes.filter((kf) => !kf.sectionId); + if (manualKeyframes.length === 0) return; + + const currentTime = Number(editorState.currentTime) || 0; + let nearest = manualKeyframes[0]; + let nearestDistance = Math.abs((Number(nearest.time) || 0) - currentTime); + + for (let i = 1; i < manualKeyframes.length; i++) { + const candidate = manualKeyframes[i]; + const distance = Math.abs((Number(candidate.time) || 0) - currentTime); + if (distance < nearestDistance) { + nearest = candidate; + nearestDistance = distance; + } + } + + const nearestTime = Number(nearest.time) || 0; + editorState.keyframes = editorState.keyframes.filter((kf) => { + if (kf.sectionId) return true; + const time = Number(kf.time) || 0; + return time !== nearestTime; + }); + + renderSectionMarkers(); + editorSeek(currentTime); + updateEditorTimeDisplay(); + scheduleProjectSave(); +} + +function renderSectionTranscriptList() { + if (!editorState || !editorState.sections || editorState.sections.length === 0) { + editorSectionTranscriptList.innerHTML = + '
No sections available.
'; + return; + } + + editorSectionTranscriptList.innerHTML = ''; + for (const section of editorState.sections) { + const inSelection = editorState.selectedSectionIds?.has(section.id); + const selected = inSelection || section.id === editorState.selectedSectionId; + const transcript = normalizeTranscriptText(section.transcript); + + const row = document.createElement('button'); + row.type = 'button'; + row.dataset.sectionId = section.id; + row.className = `w-full text-left rounded-lg px-3 py-2 transition-all ${selected ? 'bg-neutral-800' : 'hover:bg-neutral-900'}`; + + const meta = document.createElement('div'); + meta.className = 'text-xs text-neutral-500 font-mono tabular-nums'; + meta.textContent = `${section.label} (${formatTime(section.start)} - ${formatTime(section.end)})`; + + const text = document.createElement('div'); + text.className = `mt-1 text-sm leading-snug ${transcript ? 'text-neutral-300' : 'text-neutral-600 italic'}`; + text.textContent = transcript || 'No transcript captured for this section.'; + + row.appendChild(meta); + row.appendChild(text); + row.addEventListener('click', () => { + selectEditorSection(section.id); }); - window.addEventListener('mouseup', () => { - const wasDraggingBackground = draggingBackground; - draggingBackground = false; - backgroundDragState = null; - if (wasDraggingBackground) { - if (backgroundDragMoved) { - scheduleProjectSave(); - } else { - undoStack.pop(); - updateUndoRedoButtons(); - } - backgroundDragMoved = false; - } + editorSectionTranscriptList.appendChild(row); + } +} + +function computeWaveformPeaksFromCache(numBuckets = 800) { + if (!editorState || !editorState.sections || editorState.sections.length === 0) return null; + const totalDuration = editorState.duration; + if (totalDuration <= 0) return null; + + const peaks = new Float32Array(numBuckets); + for (let bucket = 0; bucket < numBuckets; bucket++) { + const bucketStart = (bucket / numBuckets) * totalDuration; + const bucketEnd = ((bucket + 1) / numBuckets) * totalDuration; + let maxPeak = 0; + + for (const section of editorState.sections) { + if (bucketEnd <= section.start || bucketStart >= section.end) continue; + const overlapStart = Math.max(bucketStart, section.start); + const overlapEnd = Math.min(bucketEnd, section.end); + const sourceStart = section.sourceStart + (overlapStart - section.start); + const sourceEnd = section.sourceStart + (overlapEnd - section.start); + + const audioBuffer = takeAudioBufferCache.get(section.takeId); + if (!audioBuffer) continue; + const channelData = audioBuffer.getChannelData(0); + const sampleRate = audioBuffer.sampleRate; + const startSample = Math.floor(sourceStart * sampleRate); + const endSample = Math.min(Math.ceil(sourceEnd * sampleRate), channelData.length); + + for (let j = startSample; j < endSample; j++) { + const abs = Math.abs(channelData[j]); + if (abs > maxPeak) maxPeak = abs; + } + } + peaks[bucket] = maxPeak; + } + return peaks; +} + +function refreshWaveform() { + if (!editorState) return; + waveformPeaks = computeWaveformPeaksFromCache(Math.round(800 * timelineZoom)); + renderWaveform(); +} + +async function extractWaveformPeaks(numBuckets = 800) { + if (!editorState || !editorState.sections || editorState.sections.length === 0) return null; + + try { + // Decode and cache audio for each referenced take + for (const section of editorState.sections) { + if (!section.takeId || takeAudioBufferCache.has(section.takeId)) continue; + const take = activeProject?.takes?.find((t) => t.id === section.takeId); + if (!take) continue; + try { + const url = pathToFileUrl(take.screenPath); + const response = await fetch(url); + const arrayBuffer = await response.arrayBuffer(); + const offlineCtx = new OfflineAudioContext(1, 1, 44100); + const audioBuffer = await offlineCtx.decodeAudioData(arrayBuffer); + takeAudioBufferCache.set(section.takeId, audioBuffer); + } catch (err) { + console.warn(`Failed to decode audio for take ${section.takeId}:`, err); + } + } + + return computeWaveformPeaksFromCache(numBuckets); + } catch (err) { + console.warn('Failed to extract waveform:', err); + return null; + } +} + +function renderWaveform() { + const canvas = editorWaveformCanvas; + const rect = canvas.parentElement.getBoundingClientRect(); + canvas.width = Math.round(rect.width * devicePixelRatio); + canvas.height = Math.round(rect.height * devicePixelRatio); + const wCtx = canvas.getContext('2d'); + wCtx.clearRect(0, 0, canvas.width, canvas.height); + if (!waveformPeaks || waveformPeaks.length === 0) return; + + const w = canvas.width; + const h = canvas.height; + const midY = h / 2; + const barWidth = w / waveformPeaks.length; + + wCtx.fillStyle = 'rgba(163, 163, 163, 0.5)'; + for (let i = 0; i < waveformPeaks.length; i++) { + const barHeight = waveformPeaks[i] * midY * 0.9; + const x = i * barWidth; + wCtx.fillRect(x, midY - barHeight, Math.max(1, barWidth - 0.5), barHeight * 2); + } +} + +function renderSectionMarkers() { + if ( + !editorState || + !editorState.duration || + !editorState.sections || + editorState.sections.length === 0 + ) { + editorSectionMarkers.innerHTML = ''; + renderSectionTranscriptList(); + return; + } + + editorSectionMarkers.innerHTML = ''; + for (const section of editorState.sections) { + const sectionStart = (section.start / editorState.duration) * 100; + const sectionWidth = Math.max( + 0.35, + ((section.end - section.start) / editorState.duration) * 100 + ); + const inSelection = editorState.selectedSectionIds?.has(section.id); + const selected = inSelection || section.id === editorState.selectedSectionId; + const hasImage = !!section.imagePath; + const baseColor = hasImage + ? section.index % 2 === 0 + ? 'rgba(76,29,149,0.45)' + : 'rgba(88,28,135,0.4)' + : section.index % 2 === 0 + ? 'rgba(23,23,23,0.72)' + : 'rgba(38,38,38,0.68)'; + + const band = document.createElement('div'); + band.className = 'absolute top-0 bottom-0'; + band.dataset.sectionId = section.id; + band.style.left = sectionStart + '%'; + band.style.width = sectionWidth + '%'; + band.style.backgroundColor = selected + ? hasImage + ? 'rgba(139,92,246,0.25)' + : 'rgba(255,255,255,0.12)' + : baseColor; + band.style.borderLeft = section.index === 0 ? 'none' : '1px solid rgba(10,10,10,0.9)'; + if (selected) { + band.style.boxShadow = 'inset 0 0 0 2px rgba(255,255,255,0.3)'; + } + const transcriptPreview = normalizeTranscriptText(section.transcript); + band.title = transcriptPreview + ? `${section.label}: ${formatTime(section.start)} - ${formatTime(section.end)}\n${transcriptPreview}` + : `${section.label}: ${formatTime(section.start)} - ${formatTime(section.end)}`; + const label = document.createElement('div'); + label.className = 'absolute text-[10px] font-medium pointer-events-none'; + label.style.left = '6px'; + label.style.top = '50%'; + label.style.transform = 'translateY(-50%)'; + label.style.color = selected ? 'rgba(255,255,255,0.9)' : 'rgba(163,163,163,0.8)'; + label.textContent = hasImage ? `${section.index + 1} IMG` : String(section.index + 1); + band.appendChild(label); + if (selected) { + const leftHandle = document.createElement('div'); + leftHandle.dataset.trimEdge = 'left'; + leftHandle.dataset.sectionId = section.id; + leftHandle.style.cssText = + 'position:absolute;top:0;bottom:0;left:0;width:6px;cursor:col-resize;z-index:30;border-left:3px solid rgba(255,255,255,0.5);'; + band.appendChild(leftHandle); + const rightHandle = document.createElement('div'); + rightHandle.dataset.trimEdge = 'right'; + rightHandle.dataset.sectionId = section.id; + rightHandle.style.cssText = + 'position:absolute;top:0;bottom:0;right:0;width:6px;cursor:col-resize;z-index:30;border-right:3px solid rgba(255,255,255,0.5);'; + band.appendChild(rightHandle); + } + const takeProxy = proxyStatus.get(section.takeId); + if (takeProxy && takeProxy.status === 'pending') { + const pct = Math.round((takeProxy.percent || 0) * 100); + const proxyBar = document.createElement('div'); + proxyBar.className = 'absolute bottom-0 left-0 pointer-events-none'; + proxyBar.dataset.proxyBar = section.takeId; + proxyBar.style.cssText = `height:3px;width:${pct}%;background:rgba(251,191,36,0.85);z-index:10;transition:width 0.3s ease;`; + proxyBar.title = `Optimizing for editing\u2026 ${pct}%`; + band.appendChild(proxyBar); + } + + editorSectionMarkers.appendChild(band); + + if (section.index < editorState.sections.length - 1) { + const cut = document.createElement('div'); + cut.className = 'absolute top-0 bottom-0 pointer-events-none'; + cut.style.left = `${(section.end / editorState.duration) * 100}%`; + cut.style.width = '3px'; + cut.style.transform = 'translateX(-1.5px)'; + cut.style.backgroundColor = 'rgba(255,255,255,0.25)'; + editorSectionMarkers.appendChild(cut); + } + } + renderSectionTranscriptList(); + renderCameraMarkers(); +} + +function updateProxyProgressBars(takeId, percent) { + const pct = Math.round(percent * 100); + const bars = editorSectionMarkers.querySelectorAll(`[data-proxy-bar="${takeId}"]`); + for (const bar of bars) { + bar.style.width = `${pct}%`; + bar.title = `Optimizing for editing\u2026 ${pct}%`; + } +} + +function renderCameraMarkers() { + if (!editorCameraMarkers) return; + editorCameraMarkers.innerHTML = ''; + + const showCameraTrack = editorState && editorState.hasCamera; + if (editorCameraTrack) editorCameraTrack.style.display = showCameraTrack ? '' : 'none'; + if (cameraTrackLabel) cameraTrackLabel.style.display = showCameraTrack ? '' : 'none'; + + if ( + !showCameraTrack || + !editorState.duration || + !editorState.sections || + editorState.sections.length === 0 + ) + return; + + for (const section of editorState.sections) { + const anchor = editorState.keyframes.find((kf) => kf.sectionId === section.id); + const pipVisible = anchor ? anchor.pipVisible : true; + const cameraFullscreen = anchor ? !!anchor.cameraFullscreen : false; + + const sectionStart = (section.start / editorState.duration) * 100; + const sectionWidth = Math.max( + 0.35, + ((section.end - section.start) / editorState.duration) * 100 + ); + const inSelection = editorState.selectedSectionIds?.has(section.id); + const selected = inSelection || section.id === editorState.selectedSectionId; + + const band = document.createElement('div'); + band.className = 'absolute top-0 bottom-0'; + band.dataset.sectionId = section.id; + band.style.left = sectionStart + '%'; + band.style.width = sectionWidth + '%'; + + if (cameraFullscreen) { + band.style.backgroundColor = selected ? 'rgba(59,130,246,0.35)' : 'rgba(59,130,246,0.2)'; + } else if (pipVisible) { + band.style.backgroundColor = selected ? 'rgba(59,130,246,0.25)' : 'rgba(59,130,246,0.12)'; + } else { + band.style.backgroundColor = selected ? 'rgba(255,255,255,0.08)' : 'rgba(23,23,23,0.5)'; + } + + band.style.borderLeft = section.index === 0 ? 'none' : '1px solid rgba(10,10,10,0.7)'; + + const label = document.createElement('div'); + label.className = 'absolute text-[8px] font-medium pointer-events-none truncate'; + label.style.cssText = 'left:4px;top:50%;transform:translateY(-50%);right:4px;'; + label.style.color = pipVisible ? 'rgba(147,197,253,0.7)' : 'rgba(100,100,100,0.5)'; + label.textContent = cameraFullscreen ? 'Full' : pipVisible ? 'PiP' : 'Off'; + band.appendChild(label); + + editorCameraMarkers.appendChild(band); + } +} + +function startTrimDrag(e, sectionId, edge) { + const section = editorState.sections.find((s) => s.id === sectionId); + if (!section) return; + pushUndo(); + editorPause(); + e.preventDefault(); + const rect = editorTimeline.getBoundingClientRect(); + trimDragState = { + sectionId, + edge, + originalSourceStart: section.sourceStart, + originalSourceEnd: section.sourceEnd, + originalStart: section.start, + originalEnd: section.end, + startMouseX: e.clientX, + pixelsPerSecond: rect.width / editorState.duration + }; + document.body.style.cursor = 'col-resize'; + const onMove = (e2) => { + e2.preventDefault(); + updateTrimDrag(e2); + }; + const onUp = () => { + document.body.style.cursor = ''; + window.removeEventListener('mousemove', onMove); + window.removeEventListener('mouseup', onUp); + finishTrimDrag(); + }; + window.addEventListener('mousemove', onMove); + window.addEventListener('mouseup', onUp); +} + +function updateTrimDrag(e) { + if (!trimDragState || !editorState) return; + const section = editorState.sections.find((s) => s.id === trimDragState.sectionId); + if (!section) return; + const MIN_DURATION = 0.1; + const deltaPixels = e.clientX - trimDragState.startMouseX; + const deltaTime = deltaPixels / trimDragState.pixelsPerSecond; + + if (trimDragState.edge === 'left') { + section.sourceStart = roundMs( + Math.max( + 0, + Math.min( + trimDragState.originalSourceEnd - MIN_DURATION, + trimDragState.originalSourceStart + deltaTime + ) + ) + ); + // Keep right edge fixed, move left edge only + const newDuration = section.sourceEnd - section.sourceStart; + section.end = trimDragState.originalEnd; + section.start = roundMs(section.end - newDuration); + section.duration = roundMs(newDuration); + } else { + section.sourceEnd = roundMs( + Math.max(section.sourceStart + MIN_DURATION, trimDragState.originalSourceEnd + deltaTime) + ); + // Keep left edge fixed, move right edge only + const newDuration = section.sourceEnd - section.sourceStart; + section.start = trimDragState.originalStart; + section.end = roundMs(section.start + newDuration); + section.duration = roundMs(newDuration); + } + + renderSectionMarkers(); +} + +function finishTrimDrag() { + if (!trimDragState || !editorState) { + trimDragState = null; + return; + } + const section = editorState.sections.find((s) => s.id === trimDragState.sectionId); + if (!section) { + trimDragState = null; + return; + } + const sourceStartChanged = + Math.abs(section.sourceStart - trimDragState.originalSourceStart) > 0.01; + const sourceEndChanged = Math.abs(section.sourceEnd - trimDragState.originalSourceEnd) > 0.01; + trimDragState = null; + if (!sourceStartChanged && !sourceEndChanged) { + undoStack.pop(); + updateUndoRedoButtons(); + return; + } + // Now reflow the full timeline + recalculateTimelinePositions(); + syncSectionAnchorKeyframes(); + renderSectionMarkers(); + refreshWaveform(); + editorSeek(section.start); + scheduleProjectSave(); +} + +function getRenderKeyframes() { + if (!editorState) return []; + + if (editorState.sections && editorState.sections.length > 0) { + syncSectionAnchorKeyframes(); + } + + const sorted = [...editorState.keyframes].sort((a, b) => a.time - b.time); + const minimal = sorted.map((kf) => ({ + time: kf.time, + pipX: kf.pipX, + pipY: kf.pipY, + pipVisible: kf.pipVisible, + cameraFullscreen: !!kf.cameraFullscreen, + backgroundZoom: clampSectionZoom(kf.backgroundZoom), + backgroundPanX: clampSectionPan(kf.backgroundPanX), + backgroundPanY: clampSectionPan(kf.backgroundPanY) + })); + + if (minimal.length === 0 || minimal[0].time > 0.0001) { + minimal.unshift({ + time: 0, + pipX: editorState.defaultPipX, + pipY: editorState.defaultPipY, + pipVisible: true, + cameraFullscreen: false, + backgroundZoom: DEFAULT_SECTION_ZOOM, + backgroundPanX: 0, + backgroundPanY: 0 + }); + } + + return minimal; +} + +function getRenderSections() { + if (!editorState) return []; + if (editorState.sections && editorState.sections.length > 0) { + syncSectionAnchorKeyframes(); + } + return editorState.sections.map((section) => { + const anchor = getSectionAnchorKeyframe(section.id, true); + return { + takeId: section.takeId, + sourceStart: section.sourceStart, + sourceEnd: section.sourceEnd, + backgroundZoom: clampSectionZoom(anchor?.backgroundZoom), + backgroundPanX: clampSectionPan(anchor?.backgroundPanX), + backgroundPanY: clampSectionPan(anchor?.backgroundPanY), + imagePath: section.imagePath || null + }; + }); +} + +// ===== Shared drawPip function ===== +function drawPip(targetCtx, video, pipX, pipY, pipW, pipH) { + const r = 12; + targetCtx.save(); + targetCtx.beginPath(); + targetCtx.moveTo(pipX + r, pipY); + targetCtx.lineTo(pipX + pipW - r, pipY); + targetCtx.quadraticCurveTo(pipX + pipW, pipY, pipX + pipW, pipY + r); + targetCtx.lineTo(pipX + pipW, pipY + pipH - r); + targetCtx.quadraticCurveTo(pipX + pipW, pipY + pipH, pipX + pipW - r, pipY + pipH); + targetCtx.lineTo(pipX + r, pipY + pipH); + targetCtx.quadraticCurveTo(pipX, pipY + pipH, pipX, pipY + pipH - r); + targetCtx.lineTo(pipX, pipY + r); + targetCtx.quadraticCurveTo(pipX, pipY, pipX + r, pipY); + targetCtx.closePath(); + targetCtx.clip(); + const camW = video.videoWidth; + const camH = video.videoHeight; + const cropSize = Math.min(camW, camH); + const sx = (camW - cropSize) / 2; + const sy = (camH - cropSize) / 2; + targetCtx.drawImage(video, sx, sy, cropSize, cropSize, pipX, pipY, pipW, pipH); + targetCtx.restore(); +} + +function drawCameraRect(targetCtx, video, x, y, w, h, r) { + const vw = video.videoWidth; + const vh = video.videoHeight; + if (!vw || !vh) return; + targetCtx.save(); + targetCtx.beginPath(); + if (r > 0.5) { + targetCtx.moveTo(x + r, y); + targetCtx.lineTo(x + w - r, y); + targetCtx.quadraticCurveTo(x + w, y, x + w, y + r); + targetCtx.lineTo(x + w, y + h - r); + targetCtx.quadraticCurveTo(x + w, y + h, x + w - r, y + h); + targetCtx.lineTo(x + r, y + h); + targetCtx.quadraticCurveTo(x, y + h, x, y + h - r); + targetCtx.lineTo(x, y + r); + targetCtx.quadraticCurveTo(x, y, x + r, y); + } else { + targetCtx.rect(x, y, w, h); + } + targetCtx.closePath(); + targetCtx.clip(); + const scale = Math.max(w / vw, h / vh); + const dw = vw * scale; + const dh = vh * scale; + const dx = x + (w - dw) / 2; + const dy = y + (h - dh) / 2; + targetCtx.drawImage(video, dx, dy, dw, dh); + targetCtx.restore(); +} + +// Populate device lists +async function enumerateDevices() { + try { + const tempStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true }); + tempStream.getTracks().forEach((t) => t.stop()); + } catch (_e) { + // Intentionally ignore - we only need to stop tracks to refresh device list + } + + const devices = await navigator.mediaDevices.enumerateDevices(); + const sources = await window.electronAPI.getSources(); + + screenSelect.innerHTML = ''; + cameraSelect.innerHTML = ''; + audioSelect.innerHTML = ''; + + sources.forEach((s) => { + const opt = document.createElement('option'); + opt.value = s.id; + opt.textContent = s.name; + screenSelect.appendChild(opt); + }); + + const videoInputs = devices.filter((d) => d.kind === 'videoinput'); + + if (videoInputs.length > 0) { + const sep = document.createElement('option'); + sep.disabled = true; + sep.textContent = '── Capture Devices ──'; + screenSelect.appendChild(sep); + videoInputs.forEach((d, i) => { + const opt = document.createElement('option'); + opt.value = 'device:' + d.deviceId; + opt.textContent = d.label || `Camera ${i + 1}`; + screenSelect.appendChild(opt); + }); + } + + videoInputs.forEach((d, i) => { + const opt = document.createElement('option'); + opt.value = d.deviceId; + opt.textContent = d.label || `Camera ${i + 1}`; + cameraSelect.appendChild(opt); + }); + + devices + .filter((d) => d.kind === 'audioinput') + .forEach((d, i) => { + const opt = document.createElement('option'); + opt.value = d.deviceId; + opt.textContent = d.label || `Microphone ${i + 1}`; + audioSelect.appendChild(opt); + }); - const wasDragging = draggingPip; - draggingPip = false; - if (wasDragging) { - if (pipDragMoved) { - scheduleProjectSave(); - } else { - undoStack.pop(); - updateUndoRedoButtons(); + // Default to first screen source (Entire Screen) + const screenIdx = sources.findIndex((s) => s.id.startsWith('screen:')); + if (screenIdx !== -1) + screenSelect.selectedIndex = screenIdx + 1; // +1 for "None" + else if (screenSelect.options.length > 1) screenSelect.selectedIndex = 1; + if (cameraSelect.options.length > 1) cameraSelect.selectedIndex = 1; + if (audioSelect.options.length > 1) audioSelect.selectedIndex = 1; +} + +async function updateScreenStream() { + if (screenStream) { + screenStream.getTracks().forEach((t) => t.stop()); + screenStream = null; + screenVideo.srcObject = null; + } + + const sourceId = screenSelect.value; + if (!sourceId) return; + + if (sourceId.startsWith('device:')) { + const deviceId = sourceId.slice('device:'.length); + screenStream = await navigator.mediaDevices.getUserMedia({ + audio: false, + video: { deviceId: { exact: deviceId }, width: { ideal: 1920 }, height: { ideal: 1080 } } + }); + } else { + screenStream = await navigator.mediaDevices.getUserMedia({ + audio: false, + video: { + mandatory: { + chromeMediaSource: 'desktop', + chromeMediaSourceId: sourceId, + maxFrameRate: 30 } - pipDragMoved = false; } }); + } + screenVideo.srcObject = screenStream; +} + +async function updateCameraStream() { + if (cameraStream) { + cameraStream.getTracks().forEach((t) => t.stop()); + cameraStream = null; + cameraVideo.srcObject = null; + } + + const deviceId = cameraSelect.value; + if (!deviceId) return; + + cameraStream = await navigator.mediaDevices.getUserMedia({ + video: { + deviceId: { exact: deviceId }, + width: { ideal: 1920, max: 1920 }, + height: { ideal: 1080, max: 1080 }, + frameRate: { ideal: 30, max: 30 }, + aspectRatio: { ideal: 16 / 9 } + }, + audio: false + }); + const [cameraTrack] = cameraStream.getVideoTracks(); + if (cameraTrack && 'contentHint' in cameraTrack) { + cameraTrack.contentHint = 'detail'; + console.log(`Camera track settings: ${JSON.stringify(cameraTrack.getSettings?.() || {})}`); + } + cameraVideo.srcObject = cameraStream; +} + +async function updateAudioStream() { + stopAudioMeter(); + if (audioStream) { + audioStream.getTracks().forEach((t) => t.stop()); + audioStream = null; + } + + const deviceId = audioSelect.value; + if (!deviceId) return; + + audioStream = await navigator.mediaDevices.getUserMedia({ + audio: { deviceId: { exact: deviceId } }, + video: false + }); + startAudioMeter(audioStream); +} + +function updatePreview() { + const hasAny = screenStream || cameraStream; + noPreview.classList.toggle('hidden', !!hasAny); + recordBtn.disabled = !hasAny || !saveFolder; + + if (drawRAF) cancelAnimationFrame(drawRAF); + lastCompositeDrawAt = 0; + if (hasAny) drawComposite(); +} + +function drawComposite(now = performance.now()) { + if (!shouldRenderPreviewFrame(now, lastCompositeDrawAt, recording)) { + drawRAF = requestAnimationFrame(drawComposite); + return; + } + + lastCompositeDrawAt = now; + ctx.fillStyle = '#000'; + ctx.fillRect(0, 0, CANVAS_W, CANVAS_H); + + const hasScreen = screenStream && screenVideo.videoWidth; + const hasCamera = cameraStream && cameraVideo.videoWidth; + + const drawScreen = screenFitSelect.value === 'fill' ? drawFill : drawFit; + + if (hasScreen && hasCamera) { + drawScreen(ctx, screenVideo, 0, 0, CANVAS_W, CANVAS_H); + const pipW = PIP_SIZE; + const pipH = pipW; + const pipX = CANVAS_W - pipW - PIP_MARGIN; + const pipY = CANVAS_H - pipH - PIP_MARGIN; + drawPip(ctx, cameraVideo, pipX, pipY, pipW, pipH); + } else if (hasScreen) { + drawScreen(ctx, screenVideo, 0, 0, CANVAS_W, CANVAS_H); + } else if (hasCamera) { + drawFit(ctx, cameraVideo, 0, 0, CANVAS_W, CANVAS_H); + } + + drawRAF = requestAnimationFrame(drawComposite); +} + +function getSourceWidth(source) { + return source.videoWidth || source.naturalWidth || source.width || 0; +} + +function getSourceHeight(source) { + return source.videoHeight || source.naturalHeight || source.height || 0; +} + +function drawFit(targetCtx, video, x, y, w, h) { + const vw = getSourceWidth(video); + const vh = getSourceHeight(video); + if (!vw || !vh) return; + const scale = Math.min(w / vw, h / vh); + const dw = vw * scale; + const dh = vh * scale; + const dx = x + (w - dw) / 2; + const dy = y + (h - dh) / 2; + targetCtx.drawImage(video, dx, dy, dw, dh); +} + +function drawFill(targetCtx, video, x, y, w, h) { + const vw = getSourceWidth(video); + const vh = getSourceHeight(video); + if (!vw || !vh) return; + const scale = Math.max(w / vw, h / vh); + const dw = vw * scale; + const dh = vh * scale; + const dx = x + (w - dw) / 2; + const dy = y + (h - dh) / 2; + targetCtx.save(); + targetCtx.beginPath(); + targetCtx.rect(x, y, w, h); + targetCtx.clip(); + targetCtx.drawImage(video, dx, dy, dw, dh); + targetCtx.restore(); +} + +function drawEditorScreenWithZoom( + targetCtx, + video, + fitMode, + backgroundZoom, + backgroundPanX = 0, + backgroundPanY = 0, + backgroundFocusX = null, + backgroundFocusY = null +) { + if (!editorZoomBufferCtx) return; + const zoom = clampSectionZoom(backgroundZoom); + const drawBase = fitMode === 'fill' ? drawFill : drawFit; + + if (zoom <= 1.0001) { + drawBase(targetCtx, video, 0, 0, CANVAS_W, CANVAS_H); + return; + } + + editorZoomBufferCtx.fillStyle = '#000'; + editorZoomBufferCtx.fillRect(0, 0, CANVAS_W, CANVAS_H); + drawBase(editorZoomBufferCtx, video, 0, 0, CANVAS_W, CANVAS_H); + + const { sourceW, sourceH } = resolveZoomCrop(zoom, backgroundPanX, backgroundPanY); + const focusX = backgroundFocusX ?? panToFocusCoord(zoom, backgroundPanX, 0.5); + const focusY = backgroundFocusY ?? panToFocusCoord(zoom, backgroundPanY, 0.5); + const sourceX = Math.max(0, Math.min(CANVAS_W - sourceW, focusX * CANVAS_W - sourceW / 2)); + const sourceY = Math.max(0, Math.min(CANVAS_H - sourceH, focusY * CANVAS_H - sourceH / 2)); + targetCtx.drawImage( + editorZoomBuffer, + sourceX, + sourceY, + sourceW, + sourceH, + 0, + 0, + CANVAS_W, + CANVAS_H + ); +} + +// Audio level meter +function startAudioMeter(stream) { + audioContext = new AudioContext(); + analyser = audioContext.createAnalyser(); + analyser.fftSize = 256; + micSourceNode = audioContext.createMediaStreamSource(stream); + micSourceNode.connect(analyser); + + const data = new Uint8Array(analyser.frequencyBinCount); + + function updateMeter() { + analyser.getByteFrequencyData(data); + const avg = data.reduce((a, b) => a + b, 0) / data.length; + const pct = Math.min(100, (avg / 128) * 100); + audioMeter.style.width = pct + '%'; + audioMeter.className = `h-full rounded-full transition-all duration-75 ${pct > 70 ? 'bg-red-500' : pct > 40 ? 'bg-amber-500' : 'bg-emerald-500'}`; + meterRAF = requestAnimationFrame(updateMeter); + } + updateMeter(); +} + +function stopAudioMeter() { + if (meterRAF) cancelAnimationFrame(meterRAF); + meterRAF = null; + micSourceNode = null; + if (audioContext) { + if (workletRegistered === audioContext) workletRegistered = null; + audioContext.close(); + audioContext = null; + } + audioMeter.style.width = '0%'; +} + +// Recording +function toggleRecording() { + if (!recording) startRecording(); + else stopRecording(); +} + +function createRecorder(stream, suffix) { + const chunks = []; + let recorderError = null; + const recorderOptions = getRecorderOptions({ + suffix, + hasAudio: typeof stream?.getAudioTracks === 'function' && stream.getAudioTracks().length > 0 + }); + const recorder = new MediaRecorder(stream, recorderOptions); + console.log(`[Recorder] ${suffix} configured`, { + mimeType: recorder.mimeType || recorderOptions.mimeType || 'default', + videoTracks: typeof stream?.getVideoTracks === 'function' ? stream.getVideoTracks().length : 0, + audioTracks: typeof stream?.getAudioTracks === 'function' ? stream.getAudioTracks().length : 0, + videoBitsPerSecond: recorderOptions.videoBitsPerSecond || null, + audioBitsPerSecond: recorderOptions.audioBitsPerSecond || null + }); + + recorder.ondataavailable = (e) => { + if (e.data.size > 0) chunks.push(e.data); + }; + + recorder.onerror = (event) => { + recorderError = event?.error?.message || `${suffix} recorder failed`; + console.error(`[Recorder] ${suffix} error`, event?.error || event); + }; + + // blobPromise resolves with { blob, path } when recording stops + recorder.blobPromise = new Promise((resolve) => { + let settled = false; + const settle = (result) => { + if (settled) return; + settled = true; + resolve(result); + }; + + recorder.onstop = async () => { + const result = await finalizeRecordingChunks({ + chunks, + saveFolder, + saveVideo: window.electronAPI.saveVideo, + suffix + }); - // ===== Timeline scrubber ===== - - editorTimeline.addEventListener('mousedown', (e) => { - if (!editorState || editorState.rendering) return; - - const isSelected = (id) => editorState.selectedSectionIds?.has(id) || id === editorState.selectedSectionId; - - // Trim handles only work on already-selected sections - const trimEdge = e.target?.dataset?.trimEdge; - if (trimEdge) { - const trimSectionId = e.target.dataset.sectionId; - if (trimSectionId && isSelected(trimSectionId)) { - startTrimDrag(e, trimSectionId, trimEdge); - return; - } - // Clicked trim handle on unselected section — just select it - if (trimSectionId) { - selectEditorSection(trimSectionId); - return; - } + const error = result.path + ? null + : result.error || recorderError || `${suffix} recording failed`; + if (result.path) { + console.log('Saved:', result.path); + } else { + console.error(`[Recorder] ${suffix} finalize failed`, error); } - const bandEl = e.target?.closest?.('[data-section-id]'); - const sectionId = bandEl?.dataset?.sectionId || null; + settle({ + ...result, + error + }); + }; + }); + + recorder.suffix = suffix; + return recorder; +} + +function addAudioToStream(stream) { + if (!audioStream) return stream; + const combined = new MediaStream([...stream.getVideoTracks(), ...audioStream.getAudioTracks()]); + return combined; +} + +function mergeInt16Arrays(arrays) { + let totalLength = 0; + for (const arr of arrays) totalLength += arr.length; + const merged = new Int16Array(totalLength); + let offset = 0; + for (const arr of arrays) { + merged.set(arr, offset); + offset += arr.length; + } + return merged; +} + +function setTranscriptStatus(text, tone = 'neutral') { + if (!transcriptStatus) return; + + const toneClasses = { + neutral: 'text-neutral-500', + success: 'text-emerald-400', + warning: 'text-amber-400', + error: 'text-red-400' + }; + const resolvedTone = toneClasses[tone] || toneClasses.neutral; + const hasText = typeof text === 'string' && text.trim().length > 0; + + transcriptStatus.className = `px-1 pb-1 text-[11px] ${resolvedTone}`; + transcriptStatus.classList.toggle('hidden', !hasText); + transcriptStatus.textContent = hasText ? text : ''; +} + +function applyScribeStatus(status) { + if (!status) return; + + if (status.failureReason) { + scribeLastFailureReason = status.failureReason; + } else if (status.tone === 'success') { + scribeLastFailureReason = null; + } + + setTranscriptStatus(status.text, status.tone); +} + +async function startRecording() { + if (!activeProjectPath) return; + recorders = []; + speechSegments = []; + audioChunkBuffer = []; + scribeLastFailureReason = null; + scribeManualClose = false; + + // Individual screen (with audio) + // Route through a canvas at constant 30fps to prevent keyframe flicker + // from variable frame rate desktop capture input + if (screenStream) { + const srcTrack = screenStream.getVideoTracks()[0]; + const settings = srcTrack.getSettings(); + const recCanvas = document.createElement('canvas'); + recCanvas.width = settings.width || 1920; + recCanvas.height = settings.height || 1080; + const recCtx = recCanvas.getContext('2d', { alpha: false }); + recCtx.drawImage(screenVideo, 0, 0, recCanvas.width, recCanvas.height); + screenRecInterval = setInterval(() => { + recCtx.drawImage(screenVideo, 0, 0, recCanvas.width, recCanvas.height); + }, 1000 / 30); + const screenOnly = addAudioToStream(recCanvas.captureStream(30)); + recorders.push(createRecorder(screenOnly, 'screen')); + } + + // Individual camera (video only; export uses screen audio) + if (cameraStream) { + const cameraOnly = createCameraRecordingStream(cameraStream); + if (cameraOnly) { + const [cameraTrack] = cameraOnly.getVideoTracks(); + console.log( + '[Recorder] camera recording track settings:', + cameraTrack?.getSettings?.() || {} + ); + recorders.push(createRecorder(cameraOnly, 'camera')); + } + } + + // Monitor source tracks for unexpected disconnection during recording. + // If a critical track (screen, audio) ends, auto-stop to preserve what we have. + trackEndedCleanups = []; + const monitorTrack = (track, label, critical) => { + const handler = () => { + if (!recording) return; + console.warn(`[Recorder] ${label} track ended unexpectedly`); + if (critical) { + console.error(`[Recorder] Critical track lost (${label}), auto-stopping to save recording`); + stopRecording(); + } + }; + track.addEventListener('ended', handler); + trackEndedCleanups.push(() => track.removeEventListener('ended', handler)); + }; + if (screenStream) { + for (const t of screenStream.getTracks()) monitorTrack(t, 'screen', true); + } + if (cameraStream) { + for (const t of cameraStream.getTracks()) monitorTrack(t, 'camera', false); + } + if (audioStream) { + for (const t of audioStream.getTracks()) monitorTrack(t, 'audio', true); + } + + const recorderTimesliceMs = getRecorderTimesliceMs(); + recorders.forEach((r) => r.start(recorderTimesliceMs)); + recording = true; + updateWorkspaceHeader(); + recordBtn.textContent = 'Stop'; + recordBtn.classList.replace('bg-red-600', 'bg-neutral-700'); + recordBtn.classList.replace('hover:bg-red-700', 'hover:bg-neutral-600'); + screenSelect.disabled = true; + cameraSelect.disabled = true; + audioSelect.disabled = true; + + startTime = Date.now(); + timerInterval = setInterval(updateTimer, 200); + + // Show transcript panel and clear previous content + transcriptPanel.classList.remove('hidden'); + transcriptContent.innerHTML = ''; + segmentBadge.textContent = '0 segments'; + setTranscriptStatus('Transcription connecting...', 'neutral'); + + // Set up Scribe via direct WebSocket + if (audioContext && audioStream && micSourceNode) { + try { + const token = await window.electronAPI.getScribeToken(); + const sampleRate = audioContext.sampleRate; + + // Map sample rate to ElevenLabs audio_format parameter + const formatMap = { + 8000: 'pcm_8000', + 16000: 'pcm_16000', + 22050: 'pcm_22050', + 24000: 'pcm_24000', + 44100: 'pcm_44100', + 48000: 'pcm_48000' + }; + const audioFormat = formatMap[sampleRate] || 'pcm_16000'; + + const wsUrl = + `wss://api.elevenlabs.io/v1/speech-to-text/realtime` + + `?model_id=scribe_v2_realtime` + + `&token=${token}` + + `&audio_format=${audioFormat}` + + `&commit_strategy=vad` + + `&include_timestamps=true` + + `&vad_silence_threshold_secs=1.5` + + `&vad_threshold=0.8` + + `&min_speech_duration_ms=200` + + `&language_code=eng`; + + scribeWs = new WebSocket(wsUrl); + + scribeWs.onopen = () => { + setTranscriptStatus('Transcription connecting...', 'neutral'); + }; - if (sectionId) { - // Shift-click: range select, no drag - if (e.shiftKey) { - selectEditorSection(sectionId, true); + scribeWs.onmessage = (event) => { + let msg; + try { + msg = JSON.parse(event.data); + } catch (error) { + console.warn('Failed to parse Scribe message:', error); return; } - // Click on section: select if needed, then set up potential drag - if (!isSelected(sectionId)) { - selectEditorSection(sectionId); - } + applyScribeStatus(getScribeStatusFromMessage(msg)); - if (editorState.sections.length > 1) { - sectionDragState = { - sectionId, - startX: e.clientX, - started: false, - dropIndicator: null - }; - const onMove = (e2) => updateSectionDrag(e2); - const onUp = (e2) => { - window.removeEventListener('mousemove', onMove); - window.removeEventListener('mouseup', onUp); - finishSectionDrag(e2); - }; - window.addEventListener('mousemove', onMove); - window.addEventListener('mouseup', onUp); + if (msg.message_type === 'partial_transcript') { + updatePartialTranscript(msg.text || ''); + } else if (msg.message_type === 'committed_transcript_with_timestamps') { + commitTranscript(msg); } - return; - } - - // Background click: seek - seekFromTimeline(e); - const onMove = (e2) => seekFromTimeline(e2); - const onUp = () => { - window.removeEventListener('mousemove', onMove); - window.removeEventListener('mouseup', onUp); }; - window.addEventListener('mousemove', onMove); - window.addEventListener('mouseup', onUp); - }); - - function seekFromTimeline(e) { - const rect = editorTimeline.getBoundingClientRect(); - const pct = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width)); - editorSeek(pct * editorState.duration); - } - const SECTION_DRAG_THRESHOLD = 5; - - function getDragSelectedIds() { - if (!editorState) return new Set(); - const ids = editorState.selectedSectionIds; - if (ids && ids.size > 0) return ids; - if (editorState.selectedSectionId) return new Set([editorState.selectedSectionId]); - return new Set(); - } - - function computeDropTarget(clientX) { - const dragIds = getDragSelectedIds(); - const remaining = editorState.sections.filter(s => !dragIds.has(s.id)); - const bands = Array.from(editorSectionMarkers.querySelectorAll('[data-section-id]')); - - let slotIndex = 0; - for (const section of remaining) { - const band = bands.find(b => b.dataset.sectionId === section.id); - if (!band) { slotIndex++; continue; } - const rect = band.getBoundingClientRect(); - if (clientX < rect.left + rect.width / 2) { - return { insertIndex: slotIndex, section }; + scribeWs.onerror = (err) => { + console.error('Scribe WebSocket error:', err); + if (!scribeManualClose) { + setTranscriptStatus('Transcription connection error', 'error'); } - slotIndex++; - } - return { insertIndex: remaining.length, section: null }; - } - - function showDropIndicator(target) { - removeDropIndicator(); - if (!editorState || editorState.sections.length === 0) return; - const indicator = document.createElement('div'); - indicator.className = 'section-drop-indicator'; - indicator.style.cssText = 'position:absolute;top:0;bottom:0;width:3px;background:rgba(59,130,246,0.9);z-index:40;pointer-events:none;border-radius:1px;box-shadow:0 0 6px rgba(59,130,246,0.5);'; - - if (target.section) { - const pct = (target.section.start / editorState.duration) * 100; - indicator.style.left = pct + '%'; - } else { - // After all remaining sections — show at the end - indicator.style.right = '0'; - } - indicator.style.transform = 'translateX(-1.5px)'; - editorSectionMarkers.appendChild(indicator); - if (sectionDragState) sectionDragState.dropIndicator = indicator; - } - - function removeDropIndicator() { - if (sectionDragState?.dropIndicator) { - sectionDragState.dropIndicator.remove(); - sectionDragState.dropIndicator = null; - } - editorSectionMarkers.querySelectorAll('.section-drop-indicator').forEach(el => el.remove()); - } + }; - function updateSectionDrag(e) { - if (!sectionDragState || !editorState) return; - const dx = Math.abs(e.clientX - sectionDragState.startX); - if (!sectionDragState.started && dx < SECTION_DRAG_THRESHOLD) return; - if (!sectionDragState.started) { - sectionDragState.started = true; - document.body.style.cursor = 'grabbing'; - } - e.preventDefault(); - showDropIndicator(computeDropTarget(e.clientX)); - } + scribeWs.onclose = (event) => { + console.warn('Scribe WebSocket closed:', { + code: event.code, + reason: event.reason, + wasClean: event.wasClean, + lastFailureReason: scribeLastFailureReason, + manualClose: scribeManualClose + }); + if (!scribeManualClose) { + applyScribeStatus(getScribeStatusFromCloseEvent(event, scribeLastFailureReason)); + } + }; - function finishSectionDrag(e) { - if (!sectionDragState || !editorState) { sectionDragState = null; return; } - const wasDrag = sectionDragState.started; - removeDropIndicator(); - document.body.style.cursor = ''; - if (!wasDrag) { - seekFromTimeline(e); - sectionDragState = null; - return; + // Set up AudioWorklet for PCM capture (only register module once per AudioContext) + if (workletRegistered !== audioContext) { + await audioContext.audioWorklet.addModule( + new URL('./audio-processor.js', window.location.href).toString() + ); + workletRegistered = audioContext; } - const { insertIndex } = computeDropTarget(e.clientX); - const dragIds = getDragSelectedIds(); - sectionDragState = null; - - pushUndo(); - const moved = dragIds.size > 1 - ? moveSectionsToIndex(editorState.sections, dragIds, insertIndex) - : moveSectionToIndex( - editorState.sections, - editorState.sections.findIndex(s => dragIds.has(s.id)), - insertIndex >= editorState.sections.length ? editorState.sections.length - 1 : insertIndex - ); - if (!moved) { undoStack.pop(); updateUndoRedoButtons(); return; } - recalculateTimelinePositions(); - syncSectionAnchorKeyframes(); - renderSectionMarkers(); - refreshWaveform(); - // Seek to the start of the first moved section - const firstMoved = editorState.sections.find(s => dragIds.has(s.id)); - editorSeek(firstMoved?.start || 0); - scheduleProjectSave(); - } - - function applyTimelineZoom(newZoom, pivotClientX) { - const wrapper = editorTimelineWrapper; - const oldZoom = timelineZoom; - newZoom = Math.max(1, Math.min(50, newZoom)); - if (newZoom === oldZoom) return; - - // Compute pivot position as fraction of content - const rect = wrapper.getBoundingClientRect(); - const pivotX = (pivotClientX !== undefined ? pivotClientX : rect.left + rect.width / 2); - const pivotFraction = (pivotX - rect.left + wrapper.scrollLeft) / (rect.width * oldZoom); + scribeWorkletNode = new AudioWorkletNode(audioContext, 'audio-capture'); + micSourceNode.connect(scribeWorkletNode); - timelineZoom = newZoom; - editorTimeline.style.minWidth = (newZoom * 100) + '%'; - - // Recompute waveform with more detail - waveformPeaks = computeWaveformPeaksFromCache(Math.round(800 * newZoom)); - renderWaveform(); + scribeWorkletNode.port.onmessage = (e) => { + if (e.data.pcm) { + audioChunkBuffer.push(new Int16Array(e.data.pcm)); + } + }; - // Adjust scroll so the pivot point stays under the cursor - const newContentWidth = rect.width * newZoom; - wrapper.scrollLeft = pivotFraction * newContentWidth - (pivotX - rect.left); - } + // Record the offset: time between recording start and first audio send + scribeAudioOffset = (Date.now() - startTime) / 1000; - function scrollTimelineToPlayhead() { - if (!editorState || editorState.duration <= 0 || timelineZoom <= 1) return; - const wrapper = editorTimelineWrapper; - const wrapperWidth = wrapper.clientWidth; - const contentWidth = wrapperWidth * timelineZoom; - const playheadX = (editorState.currentTime / editorState.duration) * contentWidth; - const margin = wrapperWidth * 0.2; - if (playheadX < wrapper.scrollLeft + margin) { - wrapper.scrollLeft = playheadX - margin; - } else if (playheadX > wrapper.scrollLeft + wrapperWidth - margin) { - wrapper.scrollLeft = playheadX - wrapperWidth + margin; - } + // Send accumulated audio every ~100ms + audioSendInterval = setInterval(() => { + if (audioChunkBuffer.length === 0 || !scribeWs || scribeWs.readyState !== WebSocket.OPEN) + return; + const merged = mergeInt16Arrays(audioChunkBuffer); + audioChunkBuffer = []; + const bytes = new Uint8Array(merged.buffer); + const CHUNK = 8192; + let binary = ''; + for (let i = 0; i < bytes.length; i += CHUNK) { + binary += String.fromCharCode.apply(null, bytes.subarray(i, i + CHUNK)); + } + const base64 = btoa(binary); + scribeWs.send( + JSON.stringify({ + message_type: 'input_audio_chunk', + audio_base_64: base64, + sample_rate: sampleRate, + commit: false + }) + ); + }, 100); + } catch (err) { + console.warn('Scribe setup failed:', err); + const reason = err instanceof Error ? err.message : 'setup failed'; + setTranscriptStatus(`Transcription unavailable: ${reason}`, 'error'); + } + } else { + setTranscriptStatus('Transcription unavailable: microphone not ready', 'warning'); + } +} + +function updatePartialTranscript(text) { + let partial = document.getElementById('partialText'); + if (!partial) { + partial = document.createElement('div'); + partial.id = 'partialText'; + partial.className = 'text-neutral-600 italic'; + transcriptContent.prepend(partial); + } + partial.textContent = stripNonSpeechAnnotations(text); + transcriptContent.scrollTop = 0; +} + +function commitTranscript(data) { + // Remove partial display + const partial = document.getElementById('partialText'); + if (partial) partial.remove(); + + const spokenWords = extractSpokenWordTokens(data.words); + if (spokenWords.length === 0) return; + + // Build clean text from spoken words only, excluding non-speech annotations. + const cleanText = stripNonSpeechAnnotations(spokenWords.map((w) => w.text).join(' ')); + if (!cleanText) return; + + // Shift timestamps by the offset between recording start and first audio sent + speechSegments.push({ + start: spokenWords[0].start + scribeAudioOffset, + end: spokenWords[spokenWords.length - 1].end + scribeAudioOffset, + text: cleanText + }); + + // Add committed text + const div = document.createElement('div'); + div.className = + 'mb-2 text-neutral-300 cursor-pointer rounded-md px-1.5 py-0.5 -mx-1 hover:bg-neutral-800/60 transition-colors'; + div.dataset.segmentIndex = speechSegments.length - 1; + div.textContent = cleanText; + div.addEventListener('click', () => { + const idx = parseInt(div.dataset.segmentIndex, 10); + selectSegment(selectedSegmentIndex === idx ? -1 : idx); + }); + transcriptContent.prepend(div); + transcriptContent.scrollTop = 0; + + updateSegmentBadge(); +} + +async function recoverPendingTake(recoveryTake) { + if (!recoveryTake?.screenPath) return; + + const projectSession = getActiveProjectSession(); + const existingTake = Array.isArray(activeProject?.takes) + ? activeProject.takes.find((take) => take.id === recoveryTake.id) + : null; + if (existingTake) { + await completeRecoveryTake(projectSession.projectPath); + return; + } + + const takeId = recoveryTake.id || `take-${Date.now()}`; + const screenPath = recoveryTake.screenPath; + const cameraPath = recoveryTake.cameraPath || null; + let recoverySections = normalizeTakeSections( + recoveryTake.sections, + recoveryTake.recordedDuration + ); + const recoverySegments = Array.isArray(recoveryTake.trimSegments) + ? recoveryTake.trimSegments + : []; + const fallbackSections = buildRemappedSectionsFromSegments(recoverySegments); + + try { + if (recoverySegments.length > 0) { + const computed = await window.electronAPI.computeSections({ + segments: recoverySegments + }); + if (!matchesActiveProjectSession(projectSession)) return; + recoverySections = + Array.isArray(computed?.sections) && computed.sections.length > 0 + ? attachSectionTranscripts(computed.sections, fallbackSections) + : fallbackSections.length > 0 + ? fallbackSections + : recoverySections; + } + + // Set takeId on all sections + recoverySections = recoverySections.map((s) => ({ ...s, takeId })); + + // Add take to project before entering editor (video pool needs it) + if (activeProject) { + if (!Array.isArray(activeProject.takes)) activeProject.takes = []; + activeProject.takes.push({ + id: takeId, + createdAt: recoveryTake.createdAt || new Date().toISOString(), + duration: recoveryTake.recordedDuration, + screenPath, + cameraPath, + sections: recoverySections + }); } - // ===== Screen track drag-and-drop for images ===== - - const IMAGE_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.webp', '.gif', '.bmp']; - const editorScreenTrack = document.getElementById('editorScreenTrack'); - - function isImageFile(fileName) { - const ext = '.' + fileName.split('.').pop().toLowerCase(); - return IMAGE_EXTENSIONS.includes(ext); - } + const appendResult = appendTakeToTimeline({ + takeId, + screenPath, + cameraPath, + recordedDuration: recoveryTake.recordedDuration, + trimSections: recoverySections, + projectSession + }); + if (!appendResult || !matchesActiveProjectSession(projectSession)) return; - async function importImageToSection(sourcePath, section) { - if (!section || !activeProjectPath) return; - try { - const copiedPath = await window.electronAPI.importFile(sourcePath, activeProjectPath); - pushUndo(); - section.imagePath = copiedPath; - await loadSectionImage(copiedPath); - renderSectionMarkers(); - scheduleProjectSave(); - } catch (err) { - console.error('Failed to import image:', err); + if (activeProject && appendResult) { + // Update the take's sections with the final result + const take = activeProject.takes.find((t) => t.id === takeId); + if (take) { + take.duration = appendResult.takeDuration; + take.sections = appendResult.takeSections; } + await persistProjectNow(); } - editorScreenTrack.addEventListener('dragover', (e) => { - if (!editorState || editorState.rendering) return; - e.preventDefault(); - e.dataTransfer.dropEffect = 'copy'; - const bandEl = e.target.closest?.('[data-section-id]'); - // Clear previous highlights and highlight current target - editorScreenTrack.querySelectorAll('[data-section-id]').forEach(el => el.style.outline = ''); - if (bandEl) bandEl.style.outline = '2px solid rgba(59,130,246,0.6)'; + await completeRecoveryTake(projectSession.projectPath); + } catch (error) { + console.error('Failed to recover pending take:', error); + if (matchesActiveProjectSession(projectSession)) { + setWorkspaceView(editorState ? 'timeline' : 'recording'); + } + } +} + +function setProcessingProgress(progress = null) { + if (!processingBar) return; + const isDeterminate = Number.isFinite(Number(progress)); + if (!isDeterminate) { + processingBar.classList.add('animate-pulse'); + processingBar.style.width = '100%'; + return; + } + + const clamped = Math.max(0, Math.min(1, Number(progress))); + processingBar.classList.remove('animate-pulse'); + processingBar.style.width = `${Math.max(2, Math.round(clamped * 100))}%`; +} + +function _showProcessingState(title, status, progress = null) { + processingTitle.textContent = title || 'Processing...'; + processingStatus.textContent = status || ''; + setProcessingProgress(progress); + setWorkspaceView('processing'); +} + +function _buildSectionAnchorSnapshot(keyframes) { + const anchors = new Map(); + for (const keyframe of Array.isArray(keyframes) ? keyframes : []) { + if (!keyframe.sectionId) continue; + anchors.set(keyframe.sectionId, { + pipX: keyframe.pipX, + pipY: keyframe.pipY, + pipVisible: keyframe.pipVisible !== false, + cameraFullscreen: !!keyframe.cameraFullscreen, + backgroundZoom: clampSectionZoom(keyframe.backgroundZoom), + backgroundPanX: clampSectionPan(keyframe.backgroundPanX), + backgroundPanY: clampSectionPan(keyframe.backgroundPanY) }); - - editorScreenTrack.addEventListener('dragleave', (e) => { - // Only clear if leaving the track entirely - if (!editorScreenTrack.contains(e.relatedTarget)) { - editorScreenTrack.querySelectorAll('[data-section-id]').forEach(el => el.style.outline = ''); + } + return anchors; +} + +function remapManualKeyframesAfterSectionDelete(keyframes, removedSection) { + const epsilon = 0.001; + const removedDuration = Math.max(0, Number(removedSection?.end) - Number(removedSection?.start)); + + return (Array.isArray(keyframes) ? keyframes : []) + .filter((keyframe) => !keyframe.sectionId) + .map((keyframe) => { + const time = Number(keyframe.time) || 0; + if (time >= removedSection.start - epsilon && time < removedSection.end - epsilon) { + return null; } - }); - editorScreenTrack.addEventListener('drop', async (e) => { - e.preventDefault(); - editorScreenTrack.querySelectorAll('[data-section-id]').forEach(el => el.style.outline = ''); - - if (!editorState || editorState.rendering || !activeProjectPath) return; - const file = e.dataTransfer?.files?.[0]; - if (!file || !file.path || !isImageFile(file.name)) return; + const nextTime = + time >= removedSection.end - epsilon ? roundMs(time - removedDuration) : roundMs(time); - const bandEl = e.target.closest?.('[data-section-id]'); - const sectionId = bandEl?.dataset?.sectionId; - const section = sectionId ? editorState.sections.find(s => s.id === sectionId) : null; - if (!section) return; - - await importImageToSection(file.path, section); + return { + ...keyframe, + time: Math.max(0, nextTime) + }; + }) + .filter(Boolean) + .sort((a, b) => a.time - b.time); +} + +function deleteSelectedSection() { + if (!editorState || editorState.rendering) return; + const selectedSection = getSelectedSection(); + if (!selectedSection) return; + + const selectedIndex = editorState.sections.findIndex( + (section) => section.id === selectedSection.id + ); + if (selectedIndex < 0) return; + + pushUndo(); + + // Remove the section + editorState.sections = editorState.sections.filter( + (section) => section.id !== selectedSection.id + ); + + if (editorState.sections.length === 0) { + const savedSourceWidth = editorState.sourceWidth || null; + const savedSourceHeight = editorState.sourceHeight || null; + clearEditorState(); + if (activeProject) { + activeProject = { + ...activeProject, + timeline: { + duration: 0, + sections: [], + keyframes: [], + selectedSectionId: null, + hasCamera: false, + sourceWidth: savedSourceWidth, + sourceHeight: savedSourceHeight + } + }; + } + persistProjectNow(); + setWorkspaceView('recording'); + return; + } + + // Remove deleted section's anchor, keep remaining anchors, remap manual keyframes + const remainingAnchors = editorState.keyframes.filter( + (kf) => kf.sectionId && kf.sectionId !== selectedSection.id + ); + const remappedManual = remapManualKeyframesAfterSectionDelete( + editorState.keyframes, + selectedSection + ); + editorState.keyframes = [...remainingAnchors, ...remappedManual]; + + // Update indices and labels only — keep existing IDs stable + reindexSections(editorState.sections); + + recalculateTimelinePositions(); + syncSectionAnchorKeyframes(); + + const nextSelected = + editorState.sections[Math.min(selectedIndex, editorState.sections.length - 1)] || + editorState.sections[0]; + editorState.selectedSectionId = nextSelected?.id || null; + + renderSectionMarkers(); + refreshWaveform(); + editorSeek(nextSelected?.start || 0); + scheduleProjectSave(); +} + +function splitSectionAtPlayhead() { + if (!editorState || editorState.rendering) return; + + const time = editorState.currentTime; + const section = findSectionForTime(time); + if (!section) return; + + const MIN_DURATION = 0.1; + const offsetInSection = time - section.start; + const sourceTime = roundMs(section.sourceStart + offsetInSection); + + if ( + sourceTime - section.sourceStart < MIN_DURATION || + section.sourceEnd - sourceTime < MIN_DURATION + ) + return; + + pushUndo(); + + const sectionIndex = editorState.sections.findIndex((s) => s.id === section.id); + if (sectionIndex < 0) return; + + // Split: modify original in place (becomes left half), insert new right half + const newSectionId = generateSectionId(); + const rightSection = { + id: newSectionId, + index: 0, + label: 'temp', + start: 0, + end: 0, + duration: 0, + sourceStart: sourceTime, + sourceEnd: section.sourceEnd, + takeId: section.takeId, + transcript: '' + }; + + section.sourceEnd = sourceTime; + editorState.sections.splice(sectionIndex + 1, 0, rightSection); + + // Update indices and labels only — keep existing IDs stable + reindexSections(editorState.sections); + + recalculateTimelinePositions(); + + // Add one anchor keyframe for the new right-half section (inherits from parent) + const newAnchor = buildSplitAnchorKeyframe( + editorState.keyframes, + section.id, + newSectionId, + rightSection.start, + { pipX: editorState.defaultPipX, pipY: editorState.defaultPipY } + ); + editorState.keyframes.push(newAnchor); + editorState.keyframes.sort((a, b) => a.time - b.time); + + editorState.selectedSectionId = newSectionId; + + renderSectionMarkers(); + refreshWaveform(); + editorSeek(editorState.currentTime); + scheduleProjectSave(); +} + +function appendTakeToTimeline({ + takeId, + screenPath: _screenPath, + cameraPath, + recordedDuration, + trimSections, + projectSession +}) { + const takeSections = normalizeTakeSections(trimSections, recordedDuration); + // Ensure all sections carry the takeId + for (const section of takeSections) { + section.takeId = takeId; + } + const takeDuration = + takeSections.length > 0 + ? takeSections[takeSections.length - 1].end + : Math.max(0, Number(recordedDuration) || 0); + + if (!matchesActiveProjectSession(projectSession)) return null; + + const hasCamera = !!cameraPath; + + if (!editorState) { + enterEditor(takeSections, { + hasCamera, + initialView: 'timeline' }); - // ===== Image picker button ===== - const editorImageBtn = document.getElementById('editorImageBtn'); - editorImageBtn.addEventListener('click', async () => { - if (!editorState || editorState.rendering || !activeProjectPath) return; - const section = getSelectedSection(); - if (!section) return; - - const filePath = await window.electronAPI.pickImageFile(); - if (!filePath) return; - - await importImageToSection(filePath, section); + return { + takeSections, + takeDuration, + appendedSections: takeSections + }; + } + + pushUndo(); + + const baseDuration = Math.max(0, Number(editorState.duration) || 0); + const existingSections = editorState.sections.map((s) => ({ ...s })); + const existingKeyframes = Array.isArray(editorState.keyframes) + ? editorState.keyframes.map((kf) => ({ ...kf })) + : []; + + const hadCameraBefore = !!editorState.hasCamera; + const keepCamera = hadCameraBefore || hasCamera; + + const startIndex = existingSections.length; + const appendedSections = takeSections.map((section, idx) => { + const sectionNumber = startIndex + idx + 1; + return { + ...section, + id: `section-${sectionNumber}`, + index: sectionNumber - 1, + label: `Section ${sectionNumber}`, + start: roundMs(section.start + baseDuration), + end: roundMs(section.end + baseDuration), + duration: roundMs(section.end - section.start), + takeId + }; + }); + + const timelineSections = [...existingSections, ...appendedSections]; + + const carryState = getStateAtTime(Math.max(0, baseDuration - 0.001)); + const newAnchors = appendedSections.map((section) => ({ + time: section.start, + pipX: carryState.pipX, + pipY: carryState.pipY, + pipVisible: carryState.pipVisible, + cameraFullscreen: !!carryState.cameraFullscreen, + backgroundZoom: clampSectionZoom(carryState.backgroundZoom), + backgroundPanX: clampSectionPan(carryState.backgroundPanX), + backgroundPanY: clampSectionPan(carryState.backgroundPanY), + sectionId: section.id, + autoSection: true + })); + + const withoutConflictingAnchors = existingKeyframes.filter( + (kf) => !kf.sectionId || !newAnchors.some((anchor) => anchor.sectionId === kf.sectionId) + ); + + enterEditor(timelineSections, { + keyframes: [...withoutConflictingAnchors, ...newAnchors].sort((a, b) => a.time - b.time), + selectedSectionId: appendedSections[0]?.id || editorState?.selectedSectionId, + hasCamera: keepCamera, + screenFitMode: editorState?.screenFitMode, + sourceWidth: editorState?.sourceWidth, + sourceHeight: editorState?.sourceHeight, + initialView: 'timeline' + }); + + return { + takeSections, + takeDuration, + appendedSections + }; +} + +async function stopRecording() { + const projectSession = getActiveProjectSession(); + const recordedDuration = (Date.now() - startTime) / 1000; + clearInterval(timerInterval); + + // Remove track-ended listeners (no longer needed once we're stopping) + for (const cleanup of trackEndedCleanups) cleanup(); + trackEndedCleanups = []; + + // Stop audio send interval + if (audioSendInterval) { + clearInterval(audioSendInterval); + audioSendInterval = null; + } + + // Disconnect worklet (must sever micSourceNode→worklet input, not just worklet outputs) + if (scribeWorkletNode) { + scribeWorkletNode.port.onmessage = null; + if (micSourceNode) micSourceNode.disconnect(scribeWorkletNode); + scribeWorkletNode.disconnect(); + scribeWorkletNode = null; + } + + // Send final commit and close WebSocket. + // Detach onmessage first so late-arriving transcripts cannot mutate + // speechSegments after we snapshot them for section computation. + const hadScribe = !!scribeWs; + scribeManualClose = true; + if (scribeWs) { + scribeWs.onmessage = null; + if (scribeWs.readyState === WebSocket.OPEN) { + scribeWs.send(JSON.stringify({ message_type: 'commit' })); + await new Promise((r) => setTimeout(r, 1000)); + scribeWs.close(); + } + } + scribeWs = null; + scribeLastFailureReason = null; + audioChunkBuffer = []; + + if (screenRecInterval) { + clearInterval(screenRecInterval); + screenRecInterval = null; + } + + const recorderFinalizeTimeoutMs = getRecorderFinalizeTimeoutMs(); + recorders.forEach((r) => { + if (r.state === 'inactive') return; + if (typeof r.requestData === 'function') { + try { + r.requestData(); + } catch (error) { + console.warn(`[Recorder] ${r.suffix} requestData failed`, error); + } + } + r.stop(); + }); + + // Await each recorder independently so one finalize failure cannot wedge the whole stop flow. + const results = {}; + const finalizeErrors = []; + for (const r of recorders) { + const result = await Promise.race([ + r.blobPromise, + new Promise((resolve) => { + setTimeout(() => { + resolve({ + blob: new Blob([], { type: 'video/webm' }), + error: `${r.suffix} recording did not finish saving in time`, + path: null, + suffix: r.suffix + }); + }, recorderFinalizeTimeoutMs); + }) + ]); + results[r.suffix] = result; + if (result?.error) finalizeErrors.push(result.error); + } + + recorders = []; + recording = false; + updateWorkspaceHeader(); + recordBtn.textContent = 'Record'; + recordBtn.classList.replace('bg-neutral-700', 'bg-red-600'); + recordBtn.classList.replace('hover:bg-neutral-600', 'hover:bg-red-700'); + screenSelect.disabled = false; + cameraSelect.disabled = false; + audioSelect.disabled = false; + timerEl.textContent = '00:00'; + + // Hide transcript panel + transcriptPanel.classList.add('hidden'); + setTranscriptStatus('', 'neutral'); + + // Enter editor if we have at least a screen recording + if (results.screen?.path) { + if (finalizeErrors.length > 0) { + console.warn('Recording finalized with partial failures:', finalizeErrors); + } + const takeId = `take-${Date.now()}`; + const takeCreatedAt = new Date().toISOString(); + const screenPath = results.screen.path; + const cameraPath = results.camera?.path || null; + let sectionsForTimeline = buildDefaultSectionsForDuration(recordedDuration); + + // Compute sections from speech segments (instant, no FFmpeg) + const activeSegments = speechSegments.filter((s) => !s.deleted); + const fallbackSections = buildRemappedSectionsFromSegments(activeSegments); + await saveRecoveryTake({ + id: takeId, + createdAt: takeCreatedAt, + screenPath, + cameraPath, + recordedDuration, + sections: sectionsForTimeline, + trimSegments: activeSegments }); - - // ===== Editor button handlers ===== - - new ResizeObserver(() => renderWaveform()).observe(editorTimelineWrapper); - - editorTimelineWrapper.addEventListener('wheel', (e) => { - if (!editorState) return; - // Pinch-to-zoom (trackpad) sends ctrlKey with deltaY - // Also support Ctrl+scroll wheel - if (e.ctrlKey || e.metaKey) { - e.preventDefault(); - const factor = 1 - e.deltaY * 0.01; - applyTimelineZoom(timelineZoom * factor, e.clientX); - } - }, { passive: false }); - - // Back button removed; user navigates via header tabs - editorUndoBtn.addEventListener('click', editorUndo); - editorRedoBtn.addEventListener('click', editorRedo); - editorPlayBtn.addEventListener('click', editorTogglePlay); - editorSplitBtn.addEventListener('click', splitSectionAtPlayhead); - editorToggleCamBtn.addEventListener('click', toggleCameraVisibility); - editorCamFullBtn.addEventListener('click', toggleCameraFullscreen); - editorApplyFutureBtn.addEventListener('click', applyStyleToFutureSections); - editorBgZoomInput.addEventListener('input', () => { - if (!editorState || editorState.rendering) return; - const changed = setSelectedSectionBackgroundZoom(editorBgZoomInput.value, { - pushHistory: !sectionZoomDragActive + if (activeSegments.length > 0) { + try { + const computed = await window.electronAPI.computeSections({ + segments: activeSegments + }); + sectionsForTimeline = + Array.isArray(computed?.sections) && computed.sections.length > 0 + ? attachSectionTranscripts(computed.sections, fallbackSections) + : fallbackSections.length > 0 + ? fallbackSections + : sectionsForTimeline; + if (!matchesActiveProjectSession(projectSession)) return; + } catch (err) { + console.warn('Section computation failed, using fallback sections:', err); + if (fallbackSections.length > 0) sectionsForTimeline = fallbackSections; + } + } else if (hadScribe) { + console.warn('No speech detected, using full recording'); + } + + // Set takeId on all sections + sectionsForTimeline = sectionsForTimeline.map((s) => ({ ...s, takeId })); + + // Add take to project before entering editor (video pool needs it) + if (activeProject) { + if (!Array.isArray(activeProject.takes)) activeProject.takes = []; + activeProject.takes.push({ + id: takeId, + createdAt: takeCreatedAt, + duration: recordedDuration, + screenPath, + cameraPath, + proxyPath: null, + sections: sectionsForTimeline }); - if (changed) sectionZoomDragActive = true; - }); - editorBgZoomInput.addEventListener('change', commitSectionZoomChange); - editorBgZoomInput.addEventListener('pointerup', commitSectionZoomChange); - editorBgZoomInput.addEventListener('blur', commitSectionZoomChange); - updateSectionZoomControls(); - - // ===== Render pipeline ===== - - editorRenderBtn.addEventListener('click', async () => { - if (!editorState || editorState.rendering) return; - await renderVideo(); - }); - - function setRenderBtnState(text, style = 'idle') { - clearTimeout(editorRenderTimeout); - editorRenderBtn.textContent = text; - if (style === 'busy') { - editorRenderBtn.className = 'px-4 py-1.5 bg-neutral-700 text-neutral-300 rounded-lg text-sm font-medium transition-colors min-w-[80px] text-center cursor-wait'; - } else if (style === 'done') { - editorRenderBtn.className = 'px-4 py-1.5 bg-emerald-600 text-white rounded-lg text-sm font-medium transition-colors min-w-[80px] text-center'; - } else if (style === 'error') { - editorRenderBtn.className = 'px-4 py-1.5 bg-red-600 text-white rounded-lg text-sm font-medium transition-colors min-w-[80px] text-center'; - } else { - editorRenderBtn.className = 'px-4 py-1.5 bg-white text-neutral-950 hover:bg-neutral-200 rounded-lg text-sm font-medium transition-colors min-w-[80px] text-center'; - } } - async function renderVideo() { - commitSectionZoomChange(); - editorState.rendering = true; - editorState.renderProgress = 0; - setRenderBtnState('Rendering...', 'busy'); - processingTitle.textContent = 'Rendering export...'; - processingStatus.textContent = 'Preparing render...'; - setProcessingProgress(0); - editorPause(); - - // Disable controls - editorUndoBtn.disabled = true; - editorRedoBtn.disabled = true; - editorPlayBtn.disabled = true; - editorSplitBtn.disabled = true; - editorToggleCamBtn.disabled = true; - editorCamFullBtn.disabled = true; - editorRenderBtn.disabled = true; - updateSectionZoomControls(); + try { + const appendResult = appendTakeToTimeline({ + takeId, + screenPath, + cameraPath, + recordedDuration, + trimSections: sectionsForTimeline, + projectSession + }); + if (!appendResult || !matchesActiveProjectSession(projectSession)) return; - try { - const renderKeyframes = getRenderKeyframes(); - const renderSections = getRenderSections(); - - // Collect takes referenced by sections - const referencedTakeIds = new Set(editorState.sections.map(s => s.takeId).filter(Boolean)); - const takes = []; - for (const takeId of referencedTakeIds) { - const take = activeProject?.takes?.find(t => t.id === takeId); - if (take) { - takes.push({ id: take.id, screenPath: take.screenPath, cameraPath: take.cameraPath }); - } + if (activeProject && appendResult) { + const take = activeProject.takes.find((t) => t.id === takeId); + if (take) { + take.duration = appendResult.takeDuration; + take.sections = appendResult.takeSections; } - - const mp4Path = await window.electronAPI.renderComposite({ - takes, - sections: renderSections, - keyframes: renderKeyframes, - pipSize: editorState.pipSize, - screenFitMode: editorState.screenFitMode, - exportAudioPreset: normalizeExportAudioPreset(exportAudioPresetSelect.value), - exportVideoPreset: normalizeExportVideoPreset(exportVideoPresetSelect.value), - cameraSyncOffsetMs: editorState.cameraSyncOffsetMs, - sourceWidth: editorState.sourceWidth || CANVAS_W, - sourceHeight: editorState.sourceHeight || CANVAS_H, - outputFolder: saveFolder - }); - - editorState.rendering = false; - editorState.renderProgress = 1; - setProcessingProgress(1); - setRenderBtnState('Done!', 'done'); - console.log('Rendered:', mp4Path); await persistProjectNow(); - } catch (err) { - editorState.rendering = false; - editorState.renderProgress = 0; - setProcessingProgress(null); - console.error('Render error:', err); - setRenderBtnState('Failed', 'error'); + // Trigger background proxy generation for the new take + if (activeProjectPath && screenPath) { + proxyStatus.set(takeId, { status: 'pending', percent: 0 }); + renderSectionMarkers(); + window.electronAPI + .generateProxy({ + takeId, + screenPath, + projectFolder: activeProjectPath, + durationSec: recordedDuration + }) + .catch((err) => console.warn('[Proxy] Failed to start proxy generation:', err)); + } } - - // Re-enable controls - updateUndoRedoButtons(); - editorPlayBtn.disabled = false; - editorSplitBtn.disabled = false; - editorToggleCamBtn.disabled = false; - editorCamFullBtn.disabled = false; - editorRenderBtn.disabled = false; - updateSectionZoomControls(); - - editorRenderTimeout = setTimeout(() => setRenderBtnState('Render', 'idle'), 3000); - editorSeek(0); + await completeRecoveryTake(); + } catch (error) { + console.error('Failed to append recording to project timeline:', error); + setWorkspaceView('recording'); } - - // ===== Segment selection ===== - - transcriptContent.addEventListener('click', (e) => { - if (!e.target.closest('[data-segment-index]')) selectSegment(-1); - }); - - let selectedSegmentIndex = -1; - const recordingUndoStack = []; - - function selectSegment(index) { - // Deselect previous - if (selectedSegmentIndex >= 0) { - const prev = transcriptContent.querySelector(`[data-segment-index="${selectedSegmentIndex}"]`); - if (prev) prev.style.outline = ''; - } - selectedSegmentIndex = index; - if (index >= 0) { - const el = transcriptContent.querySelector(`[data-segment-index="${index}"]`); - if (el) el.style.outline = '2px solid rgba(255, 255, 255, 0.3)'; + } else if (finalizeErrors.length > 0) { + console.error('Recording finalize failed:', finalizeErrors); + showProjectHomeMessage(finalizeErrors.join(' ')); + } + updateWorkspaceHeader(); +} + +function updateTimer() { + const elapsed = Math.floor((Date.now() - startTime) / 1000); + const m = String(Math.floor(elapsed / 60)).padStart(2, '0'); + const s = String(elapsed % 60).padStart(2, '0'); + timerEl.textContent = `${m}:${s}`; +} + +// ===== Editor ===== + +function enterEditor(rawSections, opts = {}) { + // Stop live preview while timeline is active. + if (drawRAF) { + cancelAnimationFrame(drawRAF); + drawRAF = null; + } + lastCompositeDrawAt = 0; + cancelEditorDrawLoop(); + + // Reset timeline zoom + timelineZoom = 1; + editorTimeline.style.minWidth = '100%'; + editorTimelineWrapper.scrollLeft = 0; + + // Clean up previous video pool + cleanupVideoPool(); + + const defaultPipX = CANVAS_W - PIP_SIZE - PIP_MARGIN; + const defaultPipY = CANVAS_H - PIP_SIZE - PIP_MARGIN; + const sections = normalizeSections(rawSections, opts.duration || 0); + + // Calculate duration from sections + const duration = sections.length > 0 ? sections[sections.length - 1].end : opts.duration || 0; + + const sectionKeyframes = sections.map((section) => ({ + time: section.start, + pipX: defaultPipX, + pipY: defaultPipY, + pipVisible: true, + cameraFullscreen: false, + backgroundZoom: DEFAULT_SECTION_ZOOM, + backgroundPanX: 0, + backgroundPanY: 0, + sectionId: section.id, + autoSection: true + })); + + const providedKeyframes = + Array.isArray(opts.keyframes) && opts.keyframes.length > 0 + ? opts.keyframes.map((kf) => ({ + ...kf, + backgroundZoom: clampSectionZoom(kf.backgroundZoom), + backgroundPanX: clampSectionPan(kf.backgroundPanX), + backgroundPanY: clampSectionPan(kf.backgroundPanY) + })) + : null; + const keyframes = (providedKeyframes || sectionKeyframes).sort((a, b) => a.time - b.time); + + editorState = { + duration, + currentTime: 0, + playing: false, + pipSize: PIP_SIZE, + defaultPipX, + defaultPipY, + keyframes, + sections, + selectedSectionId: opts.selectedSectionId || sections[0]?.id || null, + selectedSectionIds: new Set( + [opts.selectedSectionId || sections[0]?.id || null].filter(Boolean) + ), + screenFitMode: opts.screenFitMode || screenFitSelect.value, + rendering: false, + renderProgress: 0, + playbackSpeed: 1, + cameraSyncOffsetMs: normalizeCameraSyncOffsetMs(opts.cameraSyncOffsetMs), + hasCamera: typeof opts.hasCamera === 'boolean' ? opts.hasCamera : false, + sourceWidth: opts.sourceWidth || null, + sourceHeight: opts.sourceHeight || null + }; + screenFitSelect.value = editorState.screenFitMode === 'fit' ? 'fit' : 'fill'; + cameraSyncOffsetInput.value = String(editorState.cameraSyncOffsetMs); + updateSectionZoomControls(); + + // Pre-create video elements for all referenced takes + const referencedTakeIds = new Set(sections.map((s) => s.takeId).filter(Boolean)); + for (const takeId of referencedTakeIds) { + getOrCreateTakeVideos(takeId); + } + + // Set up initial active take from first section + if (sections.length > 0) { + const firstSection = sections[0]; + activeTakeId = firstSection.takeId; + activePlaybackSection = firstSection; + const videos = getOrCreateTakeVideos(firstSection.takeId); + if (videos) { + videos.screen.currentTime = firstSection.sourceStart; + if (videos.camera) { + videos.camera.currentTime = resolveCameraPlaybackTargetTime( + firstSection.sourceStart, + editorState.cameraSyncOffsetMs + ); } } + } + + // Wait for metadata to get source resolution. + // When a proxy is used for playback, probe the ORIGINAL source for true + // dimensions so exports render at full resolution (not the 960x540 proxy). + if (referencedTakeIds.size > 0) { + const firstTakeId = sections[0]?.takeId; + const firstTake = firstTakeId ? activeProject?.takes?.find((t) => t.id === firstTakeId) : null; + const videos = firstTakeId ? getOrCreateTakeVideos(firstTakeId) : null; + if (videos) { + const applySourceResolution = (w, h) => { + if (!editorState) return; + if (w && h) { + editorState.sourceWidth = w; + editorState.sourceHeight = h; + } + syncSectionAnchorKeyframes(); + renderSectionMarkers(); + updateEditorTimeDisplay(); + scheduleProjectSave(); + extractWaveformPeaks().then((peaks) => { + if (!editorState) return; + waveformPeaks = peaks; + renderWaveform(); + }); + }; - function applySegmentDeletedStyle(el, deleted) { - el.style.textDecoration = deleted ? 'line-through' : ''; - el.style.opacity = deleted ? '0.4' : ''; - } - - // ===== Keyboard shortcuts ===== - - function updateSegmentBadge() { - const total = speechSegments.length; - const removed = speechSegments.filter(s => s.deleted).length; - const active = total - removed; - if (removed > 0) { - segmentBadge.textContent = `${active} segment${active !== 1 ? 's' : ''} (${removed} removed)`; + if (firstTake?.proxyPath && firstTake?.screenPath) { + // Proxy is used for playback — probe original for true dimensions + const sourceProbe = document.createElement('video'); + sourceProbe.preload = 'metadata'; + sourceProbe.src = pathToFileUrl(firstTake.screenPath); + sourceProbe.addEventListener( + 'loadedmetadata', + () => { + applySourceResolution(sourceProbe.videoWidth, sourceProbe.videoHeight); + sourceProbe.src = ''; + }, + { once: true } + ); } else { - segmentBadge.textContent = `${total} segment${total !== 1 ? 's' : ''}`; - } - } - - document.addEventListener('keydown', (e) => { - // Backspace during recording: toggle delete on selected segment, or remove last non-deleted - if (recording && e.code === 'Backspace') { - e.preventDefault(); - if (selectedSegmentIndex >= 0 && selectedSegmentIndex < speechSegments.length) { - // Toggle delete on selected segment - const seg = speechSegments[selectedSegmentIndex]; - recordingUndoStack.push({ segmentIndex: selectedSegmentIndex, wasDeleted: seg.deleted }); - seg.deleted = !seg.deleted; - const el = transcriptContent.querySelector(`[data-segment-index="${selectedSegmentIndex}"]`); - if (el) applySegmentDeletedStyle(el, seg.deleted); - updateSegmentBadge(); + // No proxy — read dimensions from the pool video directly + const onMeta = () => + applySourceResolution(videos.screen.videoWidth, videos.screen.videoHeight); + if (videos.screen.readyState >= 1) onMeta(); + else videos.screen.addEventListener('loadedmetadata', onMeta, { once: true }); + } + } + } + + updateEditorTimeDisplay(); + renderSectionMarkers(); + preloadSectionImages(); + const initialView = opts.initialView === 'recording' ? 'recording' : 'timeline'; + setWorkspaceView(initialView); +} + +function _exitEditor() { + setWorkspaceView('recording'); +} + +function getStateAtTime(time) { + const defaultKf = { + time: 0, + pipX: editorState.defaultPipX, + pipY: editorState.defaultPipY, + pipVisible: true, + cameraFullscreen: false, + backgroundZoom: DEFAULT_SECTION_ZOOM, + backgroundPanX: 0, + backgroundPanY: 0 + }; + const userKfs = editorState.keyframes; + const kfs = userKfs.length > 0 && userKfs[0].time === 0 ? userKfs : [defaultKf, ...userKfs]; + + // Find active keyframe (last one at or before time) + let activeIdx = 0; + for (let i = 1; i < kfs.length; i++) { + if (kfs[i].time <= time) activeIdx = i; + else break; + } + + const active = kfs[activeIdx]; + const next = activeIdx < kfs.length - 1 ? kfs[activeIdx + 1] : null; + + let pipX = active.pipX; + let pipY = active.pipY; + let opacity = active.pipVisible ? 1 : 0; + let cameraFullscreen = active.cameraFullscreen || false; + let camTransition = cameraFullscreen ? 1 : 0; + let backgroundZoom = clampSectionZoom(active.backgroundZoom); + let backgroundPanX = clampSectionPan(active.backgroundPanX); + let backgroundPanY = clampSectionPan(active.backgroundPanY); + let backgroundFocusX = panToFocusCoord(backgroundZoom, backgroundPanX, 0.5); + let backgroundFocusY = panToFocusCoord(backgroundZoom, backgroundPanY, 0.5); + + // Transition toward next keyframe at end of current section + if (next) { + const remaining = next.time - time; + if (remaining > 0 && remaining < TRANSITION_DURATION) { + const t = 1 - remaining / TRANSITION_DURATION; + const nextVisible = next.pipVisible !== undefined ? next.pipVisible : true; + const nextFullscreen = next.cameraFullscreen || false; + + if (active.pipVisible !== nextVisible) { + // Visibility transition: fade in/out toward next state + if (nextVisible) { + // Fading in: use next position (where camera will appear) + opacity = t; + pipX = next.pipX; + pipY = next.pipY; + cameraFullscreen = nextFullscreen; + camTransition = nextFullscreen ? 1 : 0; } else { - // No selection: delete last non-deleted segment - for (let i = speechSegments.length - 1; i >= 0; i--) { - if (!speechSegments[i].deleted) { - recordingUndoStack.push({ segmentIndex: i, wasDeleted: false }); - speechSegments[i].deleted = true; - const el = transcriptContent.querySelector(`[data-segment-index="${i}"]`); - if (el) applySegmentDeletedStyle(el, true); - updateSegmentBadge(); - break; - } + // Fading out: keep current position + opacity = 1 - t; + camTransition = cameraFullscreen ? 1 : 0; + } + } else { + // No visibility change - handle position and fullscreen transitions + if (cameraFullscreen !== nextFullscreen) { + camTransition = nextFullscreen ? t : 1 - t; + if (!nextFullscreen) { + // Shrinking from fullscreen: use next pip position as destination + pipX = next.pipX; + pipY = next.pipY; } } - return; - } - // Ctrl+Z during recording: undo last delete/undelete action - if (recording && e.code === 'KeyZ' && (e.metaKey || e.ctrlKey) && !e.shiftKey) { - e.preventDefault(); - const action = recordingUndoStack.pop(); - if (action) { - speechSegments[action.segmentIndex].deleted = action.wasDeleted; - const el = transcriptContent.querySelector(`[data-segment-index="${action.segmentIndex}"]`); - if (el) applySegmentDeletedStyle(el, action.wasDeleted); - updateSegmentBadge(); + if ( + !cameraFullscreen && + !nextFullscreen && + (active.pipX !== next.pipX || active.pipY !== next.pipY) + ) { + pipX = active.pipX + (next.pipX - active.pipX) * t; + pipY = active.pipY + (next.pipY - active.pipY) * t; } - return; - } - - // Escape during recording: deselect segment - if (recording && e.code === 'Escape') { - selectSegment(-1); - return; - } - - if (!editorState || editorState.rendering || activeWorkspaceView !== 'timeline') return; - // Don't capture if focus is in an input - if (e.target.tagName === 'SELECT' || e.target.tagName === 'INPUT') return; - - if (e.code === 'KeyZ' && (e.metaKey || e.ctrlKey)) { - e.preventDefault(); - if (e.shiftKey) { editorRedo(); } else { editorUndo(); } - return; - } - - if (e.code === 'Space') { - e.preventDefault(); - editorTogglePlay(); - } else if (e.code === 'ArrowLeft') { - e.preventDefault(); - editorSeek(editorState.currentTime - 1 / 30); - } else if (e.code === 'ArrowRight') { - e.preventDefault(); - editorSeek(editorState.currentTime + 1 / 30); - } else if (e.code === 'Backspace' || e.code === 'Delete') { - e.preventDefault(); - deleteSelectedSection(); - } else if (e.code === 'KeyS') { - e.preventDefault(); - splitSectionAtPlayhead(); - } else if (e.code === 'KeyC') { - e.preventDefault(); - toggleCameraVisibility(); - } else if (e.code === 'KeyF') { - e.preventDefault(); - toggleCameraFullscreen(); - } else if (e.code === 'KeyL') { - e.preventDefault(); - cyclePlaybackSpeed(); - } else if (e.code === 'KeyI') { - e.preventDefault(); - editorImageBtn.click(); } - }); - - // Settings - // Settings panel is always visible in the left sidebar - - openFolderBtn.addEventListener('click', () => { - if (activeProjectPath) window.electronAPI.openFolder(activeProjectPath); - }); - activeProjectPathEl.addEventListener('click', () => { - if (activeProjectPath) window.electronAPI.openFolder(activeProjectPath); + if (Math.abs(backgroundZoom - clampSectionZoom(next.backgroundZoom)) > 0.0001) { + backgroundZoom = + backgroundZoom + (clampSectionZoom(next.backgroundZoom) - backgroundZoom) * t; + } + const nextFocusX = panToFocusCoord(next.backgroundZoom, next.backgroundPanX, 0.5); + const nextFocusY = panToFocusCoord(next.backgroundZoom, next.backgroundPanY, 0.5); + backgroundFocusX = backgroundFocusX + (nextFocusX - backgroundFocusX) * t; + backgroundFocusY = backgroundFocusY + (nextFocusY - backgroundFocusY) * t; + backgroundPanX = focusToPanCoord(backgroundZoom, backgroundFocusX, backgroundPanX); + backgroundPanY = focusToPanCoord(backgroundZoom, backgroundFocusY, backgroundPanY); + } + } + + return { + pipX, + pipY, + pipVisible: opacity > 0, + opacity, + cameraFullscreen, + camTransition, + backgroundZoom, + backgroundPanX, + backgroundPanY, + backgroundFocusX, + backgroundFocusY + }; +} + +function formatTime(seconds) { + const m = String(Math.floor(seconds / 60)).padStart(2, '0'); + const s = String(Math.floor(seconds % 60)).padStart(2, '0'); + return `${m}:${s}`; +} + +function updateEditorTimeDisplay() { + if (!editorState) return; + const selectedSection = getSelectedSection(); + const sectionText = selectedSection ? ` | ${selectedSection.label}` : ''; + const speedText = editorState.playbackSpeed !== 1 ? ` [${editorState.playbackSpeed}x]` : ''; + editorTimeEl.textContent = `${formatTime(editorState.currentTime)} / ${formatTime(editorState.duration)}${speedText}${sectionText}`; +} + +function switchPlaybackSection(nextSection, opts = {}) { + if (!editorState || !nextSection) return false; + const previousTakeId = activeTakeId; + const sameTake = previousTakeId === nextSection.takeId; + const nextVideos = getOrCreateTakeVideos(nextSection.takeId); + if (!nextVideos) return false; + + const targetSourceTime = Number.isFinite(Number(opts.sourceTime)) + ? Number(opts.sourceTime) + : nextSection.sourceStart; + const seekPlan = computePlaybackSeekPlan( + nextVideos.screen.currentTime, + nextVideos.camera?.currentTime, + targetSourceTime, + editorState.cameraSyncOffsetMs + ); + + if (!sameTake && previousTakeId) { + const previousVideos = getOrCreateTakeVideos(previousTakeId); + if (previousVideos) { + previousVideos.screen.pause(); + if (previousVideos.camera) { + previousVideos.camera.pause(); + previousVideos.camera.playbackRate = 1; + } + } + } + + if (seekPlan.screenNeedsSeek) nextVideos.screen.currentTime = seekPlan.targetSourceTime; + if (nextVideos.camera && seekPlan.cameraNeedsSeek) + nextVideos.camera.currentTime = seekPlan.targetCameraTime; + + activeTakeId = nextSection.takeId; + activePlaybackSection = nextSection; + + if (opts.logSwitch) { + console.debug('[Editor] Section switch', { + from: opts.fromSectionId || null, + to: nextSection.id, + sameTake, + seek: seekPlan.needsSeek, + reason: opts.reason || 'unknown' }); - - pickFolderBtn.addEventListener('click', () => { - setWorkspaceView('home'); + } + + if (opts.resumePlayback) { + const speed = editorState.playbackSpeed || 1; + nextVideos.screen.playbackRate = speed; + if (nextVideos.screen.paused) nextVideos.screen.play().catch(() => {}); + if (editorState.hasCamera && nextVideos.camera && nextVideos.camera.paused) { + nextVideos.camera.playbackRate = speed; + nextVideos.camera.play().catch(() => {}); + } + } + + return true; +} + +function syncCameraPlayback(videos) { + if (!editorState?.hasCamera || !videos?.camera) return; + + const baseRate = editorState.playbackSpeed || 1; + const drift = computeCameraPlaybackDrift( + videos.screen.currentTime, + videos.camera.currentTime, + editorState.cameraSyncOffsetMs + ); + const absDrift = Math.abs(drift); + const now = performance.now(); + + if (absDrift >= CAMERA_DRIFT_HARD_THRESHOLD && now >= cameraResyncCooldownUntil) { + videos.camera.currentTime = resolveCameraPlaybackTargetTime( + videos.screen.currentTime, + editorState.cameraSyncOffsetMs + ); + videos.camera.playbackRate = baseRate; + cameraResyncCooldownUntil = now + CAMERA_RESYNC_COOLDOWN_MS; + console.debug('[Editor] Camera hard resync', { + drift: Number(drift.toFixed(3)), + threshold: CAMERA_DRIFT_HARD_THRESHOLD }); - - contentProtectionToggle.addEventListener('change', async () => { - hideFromRecording = contentProtectionToggle.checked ? 'true' : 'false'; - await syncContentProtection(); - scheduleProjectSave(); + return; + } + + if (absDrift >= CAMERA_DRIFT_SOFT_THRESHOLD) { + const correction = Math.min(0.06, absDrift * 0.5); + const targetRate = drift > 0 ? baseRate + correction : baseRate - correction; + const clampedRate = Math.max(baseRate - 0.08, Math.min(baseRate + 0.08, targetRate)); + if (Math.abs(videos.camera.playbackRate - clampedRate) > 0.004) { + videos.camera.playbackRate = clampedRate; + } + if ( + absDrift >= CAMERA_DRIFT_LOG_THRESHOLD && + now - lastCameraDriftLogAt >= CAMERA_DRIFT_LOG_INTERVAL_MS + ) { + console.debug('[Editor] Camera drift', { + drift: Number(drift.toFixed(3)), + playbackRate: Number(clampedRate.toFixed(3)) + }); + lastCameraDriftLogAt = now; + } + } else if (Math.abs(videos.camera.playbackRate - baseRate) > 0.001) { + videos.camera.playbackRate = baseRate; + } +} + +function editorPlay() { + if (!editorState || editorState.rendering) return; + editorState.playing = true; + const speed = editorState.playbackSpeed || 1; + if (activeTakeId) { + const videos = getOrCreateTakeVideos(activeTakeId); + if (videos) { + videos.screen.playbackRate = speed; + videos.screen.play().catch(() => {}); + if (editorState.hasCamera && videos.camera) { + videos.camera.playbackRate = speed; + videos.camera.play().catch(() => {}); + } + } + } + editorPlayBtn.textContent = 'Pause'; +} + +function editorPause() { + if (!editorState) return; + editorState.playing = false; + for (const [, videos] of takeVideoPool) { + videos.screen.pause(); + videos.screen.playbackRate = 1; + if (videos.camera) { + videos.camera.pause(); + videos.camera.playbackRate = 1; + } + } + editorPlayBtn.textContent = 'Play'; +} + +function editorTogglePlay() { + if (!editorState) return; + if (editorState.playing) editorPause(); + else editorPlay(); +} + +function cyclePlaybackSpeed() { + if (!editorState) return; + const speeds = [1, 1.5, 2]; + const idx = speeds.indexOf(editorState.playbackSpeed); + editorState.playbackSpeed = speeds[(idx + 1) % speeds.length]; + if (editorState.playing && activeTakeId) { + const videos = getOrCreateTakeVideos(activeTakeId); + if (videos) { + videos.screen.playbackRate = editorState.playbackSpeed; + if (editorState.hasCamera && videos.camera) { + videos.camera.playbackRate = editorState.playbackSpeed; + } + } + } + updateEditorTimeDisplay(); +} + +function editorSeek(time) { + if (!editorState) return; + time = Math.max(0, Math.min(time, editorState.duration)); + editorState.currentTime = time; + + const resolved = resolveTimeToSource(time); + if (resolved) { + switchPlaybackSection(resolved.section, { + sourceTime: resolved.sourceTime, + resumePlayback: editorState.playing, + reason: 'seek', + fromSectionId: activePlaybackSection?.id }); - - exportAudioPresetSelect.addEventListener('change', () => { - exportAudioPresetSelect.value = normalizeExportAudioPreset(exportAudioPresetSelect.value); - if (activeProject?.settings) { - activeProject.settings.exportAudioPreset = exportAudioPresetSelect.value; + } + updateEditorTimeDisplay(); + updateScrubberPosition(); +} + +function updateScrubberPosition() { + if (!editorState || editorState.duration <= 0) return; + const pct = (editorState.currentTime / editorState.duration) * 100; + editorScrubber.style.left = pct + '%'; + if (editorState.playing) scrollTimelineToPlayhead(); +} + +function editorDrawLoop() { + if (!editorState) return; + + if (editorState.playing && activeTakeId && activePlaybackSection) { + const videos = getOrCreateTakeVideos(activeTakeId); + if (videos) { + const sourceTime = videos.screen.currentTime; + const timelineTime = + activePlaybackSection.start + (sourceTime - activePlaybackSection.sourceStart); + editorState.currentTime = timelineTime; + + // Check if we've passed the current section's end + if (sourceTime >= activePlaybackSection.sourceEnd - 0.01) { + const currentIdx = editorState.sections.indexOf(activePlaybackSection); + const nextSection = editorState.sections[currentIdx + 1]; + + if (nextSection) { + const fromSectionId = activePlaybackSection?.id; + const sameTake = activeTakeId === nextSection.takeId; + const contiguousSource = + sameTake && Math.abs(sourceTime - nextSection.sourceStart) <= 0.05; + switchPlaybackSection(nextSection, { + sourceTime: contiguousSource ? sourceTime : nextSection.sourceStart, + resumePlayback: true, + logSwitch: true, + reason: 'boundary', + fromSectionId + }); + } else { + // End of timeline + editorPause(); + } } - scheduleProjectSave(); - }); - exportVideoPresetSelect.addEventListener('change', () => { - exportVideoPresetSelect.value = normalizeExportVideoPreset(exportVideoPresetSelect.value); - if (activeProject?.settings) { - activeProject.settings.exportVideoPreset = exportVideoPresetSelect.value; - } + syncCameraPlayback(videos); + } + + updateEditorTimeDisplay(); + updateScrubberPosition(); + } + + // Draw composite frame + editorCtx.fillStyle = '#000'; + editorCtx.fillRect(0, 0, CANVAS_W, CANVAS_H); + + const activeVideos = activeTakeId ? getOrCreateTakeVideos(activeTakeId) : null; + const hasScreen = activeVideos && activeVideos.screen.videoWidth > 0; + const hasCamera = + editorState.hasCamera && activeVideos?.camera && activeVideos.camera.videoWidth > 0; + const state = getStateAtTime(editorState.currentTime); + + const currentSection = findSectionForTime(editorState.currentTime); + const sectionImage = currentSection?.imagePath + ? sectionImageCache.get(currentSection.imagePath) + : null; + + if (sectionImage) { + drawEditorScreenWithZoom( + editorCtx, + sectionImage, + editorState.screenFitMode, + state.backgroundZoom, + state.backgroundPanX, + state.backgroundPanY, + state.backgroundFocusX, + state.backgroundFocusY + ); + } else if (hasScreen) { + drawEditorScreenWithZoom( + editorCtx, + activeVideos.screen, + editorState.screenFitMode, + state.backgroundZoom, + state.backgroundPanX, + state.backgroundPanY, + state.backgroundFocusX, + state.backgroundFocusY + ); + } + + if (hasCamera) { + if (state.camTransition > 0 && state.opacity > 0) { + editorCtx.save(); + if (state.opacity < 1) editorCtx.globalAlpha = state.opacity; + const t = easeInOut(state.camTransition); + const camX = state.pipX * (1 - t); + const camY = state.pipY * (1 - t); + const camW = editorState.pipSize + (CANVAS_W - editorState.pipSize) * t; + const camH = editorState.pipSize + (CANVAS_H - editorState.pipSize) * t; + const camR = 12 * (1 - t); + drawCameraRect(editorCtx, activeVideos.camera, camX, camY, camW, camH, camR); + editorCtx.restore(); + } else if (state.opacity > 0) { + editorCtx.save(); + editorCtx.globalAlpha = state.opacity; + drawPip( + editorCtx, + activeVideos.camera, + state.pipX, + state.pipY, + editorState.pipSize, + editorState.pipSize + ); + editorCtx.restore(); + } + } + + scheduleEditorDrawLoop(); +} + +// ===== Keyframe management ===== + +function getMutableCameraKeyframe() { + if (!editorState) return null; + + const selectedSection = getSelectedSection(); + if (selectedSection) { + return getSectionAnchorKeyframe(selectedSection.id, true); + } + + const section = findSectionForTime(editorState.currentTime); + if (section) { + return getSectionAnchorKeyframe(section.id, true); + } + + return null; +} + +function toggleCameraVisibility() { + if (!editorState || editorState.rendering) return; + const target = getMutableCameraKeyframe(); + if (!target) return; + pushUndo(); + target.pipVisible = !target.pipVisible; + scheduleProjectSave(); +} + +function toggleCameraFullscreen() { + if (!editorState || editorState.rendering) return; + const target = getMutableCameraKeyframe(); + if (!target) return; + pushUndo(); + target.cameraFullscreen = !(target.cameraFullscreen || false); + scheduleProjectSave(); +} + +function setSelectedSectionBackgroundZoom(nextZoom, opts = {}) { + if (!editorState || editorState.rendering) return false; + const pushHistory = opts.pushHistory === true; + const selectedSection = getSelectedSection(); + if (!selectedSection) return false; + const anchor = getSectionAnchorKeyframe(selectedSection.id, true); + if (!anchor) return false; + const normalizedZoom = clampSectionZoom(nextZoom); + const currentZoom = clampSectionZoom(anchor.backgroundZoom); + if (Math.abs(normalizedZoom - currentZoom) < 0.0001) { + updateSectionZoomControls(); + return false; + } + if (pushHistory) pushUndo(); + anchor.backgroundZoom = normalizedZoom; + updateSectionZoomControls(); + return true; +} + +function setSectionBackgroundPan(sectionId, nextPanX, nextPanY) { + if (!editorState || editorState.rendering || !sectionId) return false; + const anchor = getSectionAnchorKeyframe(sectionId, true); + if (!anchor) return false; + const normalizedPanX = clampSectionPan(nextPanX); + const normalizedPanY = clampSectionPan(nextPanY); + const currentPanX = clampSectionPan(anchor.backgroundPanX); + const currentPanY = clampSectionPan(anchor.backgroundPanY); + if ( + Math.abs(normalizedPanX - currentPanX) < 0.0001 && + Math.abs(normalizedPanY - currentPanY) < 0.0001 + ) { + return false; + } + anchor.backgroundPanX = normalizedPanX; + anchor.backgroundPanY = normalizedPanY; + return true; +} + +function commitSectionZoomChange() { + if (!sectionZoomDragActive) return; + sectionZoomDragActive = false; + scheduleProjectSave(); +} + +// ===== PiP drag-to-reposition ===== + +function canvasToEditorCoords(clientX, clientY) { + const rect = editorCanvas.getBoundingClientRect(); + const scaleX = CANVAS_W / rect.width; + const scaleY = CANVAS_H / rect.height; + return { + x: (clientX - rect.left) * scaleX, + y: (clientY - rect.top) * scaleY + }; +} + +editorCanvas.addEventListener('mousedown', (e) => { + if (!editorState || editorState.rendering) return; + const activeSection = findSectionForTime(editorState.currentTime); + if (activeSection) selectEditorSection(activeSection.id); + const { x, y } = canvasToEditorCoords(e.clientX, e.clientY); + const kf = getStateAtTime(editorState.currentTime); + if (editorState.hasCamera && kf.pipVisible && kf.camTransition <= 0) { + const pipW = editorState.pipSize; + const pipH = editorState.pipSize; + if (x >= kf.pipX && x <= kf.pipX + pipW && y >= kf.pipY && y <= kf.pipY + pipH) { + pipDragMoved = false; + pushUndo(); + draggingPip = true; + e.preventDefault(); + return; + } + } + + if (!activeSection || kf.backgroundZoom <= 1.0001 || (kf.cameraFullscreen && kf.opacity > 0)) + return; + const initialPan = getSectionBackgroundPan(activeSection.id); + pushUndo(); + backgroundDragMoved = false; + draggingBackground = true; + backgroundDragState = { + sectionId: activeSection.id, + startMouseX: x, + startMouseY: y, + startPanX: initialPan.x, + startPanY: initialPan.y, + zoom: kf.backgroundZoom + }; + e.preventDefault(); +}); + +window.addEventListener('mousemove', (e) => { + if (draggingBackground && editorState && backgroundDragState) { + const { x, y } = canvasToEditorCoords(e.clientX, e.clientY); + const deltaX = x - backgroundDragState.startMouseX; + const deltaY = y - backgroundDragState.startMouseY; + const { maxOffsetX, maxOffsetY } = getZoomCropBounds(backgroundDragState.zoom); + const nextPanX = maxOffsetX > 0 ? backgroundDragState.startPanX - deltaX / maxOffsetX : 0; + const nextPanY = maxOffsetY > 0 ? backgroundDragState.startPanY - deltaY / maxOffsetY : 0; + backgroundDragMoved = + setSectionBackgroundPan(backgroundDragState.sectionId, nextPanX, nextPanY) || + backgroundDragMoved; + return; + } + + if (!draggingPip || !editorState) return; + pipDragMoved = true; + const { x, y } = canvasToEditorCoords(e.clientX, e.clientY); + const snapped = snapToNearestCorner(x, y); + + const selectedSection = getSelectedSection(); + const section = selectedSection || findSectionForTime(editorState.currentTime); + if (section) { + const anchor = getSectionAnchorKeyframe(section.id, true); + if (anchor) { + anchor.pipX = snapped.x; + anchor.pipY = snapped.y; + } + } +}); + +window.addEventListener('mouseup', () => { + const wasDraggingBackground = draggingBackground; + draggingBackground = false; + backgroundDragState = null; + if (wasDraggingBackground) { + if (backgroundDragMoved) { scheduleProjectSave(); - }); + } else { + undoStack.pop(); + updateUndoRedoButtons(); + } + backgroundDragMoved = false; + } - cameraSyncOffsetInput.addEventListener('change', () => { - const normalized = normalizeCameraSyncOffsetMs(cameraSyncOffsetInput.value); - cameraSyncOffsetInput.value = String(normalized); - if (activeProject?.settings) { - activeProject.settings.cameraSyncOffsetMs = normalized; - } - if (editorState) { - editorState.cameraSyncOffsetMs = normalized; - editorSeek(editorState.currentTime); - } + const wasDragging = draggingPip; + draggingPip = false; + if (wasDragging) { + if (pipDragMoved) { scheduleProjectSave(); - }); + } else { + undoStack.pop(); + updateUndoRedoButtons(); + } + pipDragMoved = false; + } +}); - // Source change handlers - screenFitSelect.addEventListener('change', () => { - if (editorState) editorState.screenFitMode = screenFitSelect.value; - updatePreview(); - scheduleProjectSave(); - }); +// ===== Timeline scrubber ===== - screenSelect.addEventListener('change', async () => { - try { await updateScreenStream(); } catch (e) { console.error(e); } - updatePreview(); - }); +editorTimeline.addEventListener('mousedown', (e) => { + if (!editorState || editorState.rendering) return; - cameraSelect.addEventListener('change', async () => { - try { await updateCameraStream(); } catch (e) { console.error(e); } - updatePreview(); - }); + const isSelected = (id) => + editorState.selectedSectionIds?.has(id) || id === editorState.selectedSectionId; - audioSelect.addEventListener('change', async () => { - try { await updateAudioStream(); } catch (e) { console.error(e); } - }); + // Trim handles only work on already-selected sections + const trimEdge = e.target?.dataset?.trimEdge; + if (trimEdge) { + const trimSectionId = e.target.dataset.sectionId; + if (trimSectionId && isSelected(trimSectionId)) { + startTrimDrag(e, trimSectionId, trimEdge); + return; + } + // Clicked trim handle on unselected section — just select it + if (trimSectionId) { + selectEditorSection(trimSectionId); + return; + } + } - recordBtn.addEventListener('click', toggleRecording); + const bandEl = e.target?.closest?.('[data-section-id]'); + const sectionId = bandEl?.dataset?.sectionId || null; - // Workspace navigation - goRecordingBtn.addEventListener('click', () => { - if (!activeProjectPath) return; - setWorkspaceView('recording'); - }); + if (sectionId) { + // Shift-click: range select, no drag + if (e.shiftKey) { + selectEditorSection(sectionId, true); + return; + } - goTimelineBtn.addEventListener('click', () => { - if (!activeProjectPath || !editorState) return; - setWorkspaceView('timeline'); - }); + // Click on section: select if needed, then set up potential drag + if (!isSelected(sectionId)) { + selectEditorSection(sectionId); + } - switchProjectBtn.addEventListener('click', async () => { - if (recording) return; - await flushScheduledProjectSave(); - setWorkspaceView('home'); - await refreshRecentProjects(); + if (editorState.sections.length > 1) { + sectionDragState = { + sectionId, + startX: e.clientX, + started: false, + dropIndicator: null + }; + const onMove = (e2) => updateSectionDrag(e2); + const onUp = (e2) => { + window.removeEventListener('mousemove', onMove); + window.removeEventListener('mouseup', onUp); + finishSectionDrag(e2); + }; + window.addEventListener('mousemove', onMove); + window.addEventListener('mouseup', onUp); + } + return; + } + + // Background click: seek + seekFromTimeline(e); + const onMove = (e2) => seekFromTimeline(e2); + const onUp = () => { + window.removeEventListener('mousemove', onMove); + window.removeEventListener('mouseup', onUp); + }; + window.addEventListener('mousemove', onMove); + window.addEventListener('mouseup', onUp); +}); + +function seekFromTimeline(e) { + const rect = editorTimeline.getBoundingClientRect(); + const pct = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width)); + editorSeek(pct * editorState.duration); +} + +const SECTION_DRAG_THRESHOLD = 5; + +function getDragSelectedIds() { + if (!editorState) return new Set(); + const ids = editorState.selectedSectionIds; + if (ids && ids.size > 0) return ids; + if (editorState.selectedSectionId) return new Set([editorState.selectedSectionId]); + return new Set(); +} + +function computeDropTarget(clientX) { + const dragIds = getDragSelectedIds(); + const remaining = editorState.sections.filter((s) => !dragIds.has(s.id)); + const bands = Array.from(editorSectionMarkers.querySelectorAll('[data-section-id]')); + + let slotIndex = 0; + for (const section of remaining) { + const band = bands.find((b) => b.dataset.sectionId === section.id); + if (!band) { + slotIndex++; + continue; + } + const rect = band.getBoundingClientRect(); + if (clientX < rect.left + rect.width / 2) { + return { insertIndex: slotIndex, section }; + } + slotIndex++; + } + return { insertIndex: remaining.length, section: null }; +} + +function showDropIndicator(target) { + removeDropIndicator(); + if (!editorState || editorState.sections.length === 0) return; + const indicator = document.createElement('div'); + indicator.className = 'section-drop-indicator'; + indicator.style.cssText = + 'position:absolute;top:0;bottom:0;width:3px;background:rgba(59,130,246,0.9);z-index:40;pointer-events:none;border-radius:1px;box-shadow:0 0 6px rgba(59,130,246,0.5);'; + + if (target.section) { + const pct = (target.section.start / editorState.duration) * 100; + indicator.style.left = pct + '%'; + } else { + // After all remaining sections — show at the end + indicator.style.right = '0'; + } + indicator.style.transform = 'translateX(-1.5px)'; + editorSectionMarkers.appendChild(indicator); + if (sectionDragState) sectionDragState.dropIndicator = indicator; +} + +function removeDropIndicator() { + if (sectionDragState?.dropIndicator) { + sectionDragState.dropIndicator.remove(); + sectionDragState.dropIndicator = null; + } + editorSectionMarkers.querySelectorAll('.section-drop-indicator').forEach((el) => el.remove()); +} + +function updateSectionDrag(e) { + if (!sectionDragState || !editorState) return; + const dx = Math.abs(e.clientX - sectionDragState.startX); + if (!sectionDragState.started && dx < SECTION_DRAG_THRESHOLD) return; + if (!sectionDragState.started) { + sectionDragState.started = true; + document.body.style.cursor = 'grabbing'; + } + e.preventDefault(); + showDropIndicator(computeDropTarget(e.clientX)); +} + +function finishSectionDrag(e) { + if (!sectionDragState || !editorState) { + sectionDragState = null; + return; + } + const wasDrag = sectionDragState.started; + removeDropIndicator(); + document.body.style.cursor = ''; + if (!wasDrag) { + seekFromTimeline(e); + sectionDragState = null; + return; + } + const { insertIndex } = computeDropTarget(e.clientX); + const dragIds = getDragSelectedIds(); + sectionDragState = null; + + pushUndo(); + const moved = + dragIds.size > 1 + ? moveSectionsToIndex(editorState.sections, dragIds, insertIndex) + : moveSectionToIndex( + editorState.sections, + editorState.sections.findIndex((s) => dragIds.has(s.id)), + insertIndex >= editorState.sections.length ? editorState.sections.length - 1 : insertIndex + ); + if (!moved) { + undoStack.pop(); + updateUndoRedoButtons(); + return; + } + recalculateTimelinePositions(); + syncSectionAnchorKeyframes(); + renderSectionMarkers(); + refreshWaveform(); + // Seek to the start of the first moved section + const firstMoved = editorState.sections.find((s) => dragIds.has(s.id)); + editorSeek(firstMoved?.start || 0); + scheduleProjectSave(); +} + +function applyTimelineZoom(newZoom, pivotClientX) { + const wrapper = editorTimelineWrapper; + const oldZoom = timelineZoom; + newZoom = Math.max(1, Math.min(50, newZoom)); + if (newZoom === oldZoom) return; + + // Compute pivot position as fraction of content + const rect = wrapper.getBoundingClientRect(); + const pivotX = pivotClientX !== undefined ? pivotClientX : rect.left + rect.width / 2; + const pivotFraction = (pivotX - rect.left + wrapper.scrollLeft) / (rect.width * oldZoom); + + timelineZoom = newZoom; + editorTimeline.style.minWidth = newZoom * 100 + '%'; + + // Recompute waveform with more detail + waveformPeaks = computeWaveformPeaksFromCache(Math.round(800 * newZoom)); + renderWaveform(); + + // Adjust scroll so the pivot point stays under the cursor + const newContentWidth = rect.width * newZoom; + wrapper.scrollLeft = pivotFraction * newContentWidth - (pivotX - rect.left); +} + +function scrollTimelineToPlayhead() { + if (!editorState || editorState.duration <= 0 || timelineZoom <= 1) return; + const wrapper = editorTimelineWrapper; + const wrapperWidth = wrapper.clientWidth; + const contentWidth = wrapperWidth * timelineZoom; + const playheadX = (editorState.currentTime / editorState.duration) * contentWidth; + const margin = wrapperWidth * 0.2; + if (playheadX < wrapper.scrollLeft + margin) { + wrapper.scrollLeft = playheadX - margin; + } else if (playheadX > wrapper.scrollLeft + wrapperWidth - margin) { + wrapper.scrollLeft = playheadX - wrapperWidth + margin; + } +} + +// ===== Screen track drag-and-drop for images ===== + +const IMAGE_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.webp', '.gif', '.bmp']; +const editorScreenTrack = document.getElementById('editorScreenTrack'); + +function isImageFile(fileName) { + const ext = '.' + fileName.split('.').pop().toLowerCase(); + return IMAGE_EXTENSIONS.includes(ext); +} + +async function importImageToSection(sourcePath, section) { + if (!section || !activeProjectPath) return; + try { + const copiedPath = await window.electronAPI.importFile(sourcePath, activeProjectPath); + pushUndo(); + section.imagePath = copiedPath; + await loadSectionImage(copiedPath); + renderSectionMarkers(); + scheduleProjectSave(); + } catch (err) { + console.error('Failed to import image:', err); + } +} + +editorScreenTrack.addEventListener('dragover', (e) => { + if (!editorState || editorState.rendering) return; + e.preventDefault(); + e.dataTransfer.dropEffect = 'copy'; + const bandEl = e.target.closest?.('[data-section-id]'); + // Clear previous highlights and highlight current target + editorScreenTrack.querySelectorAll('[data-section-id]').forEach((el) => (el.style.outline = '')); + if (bandEl) bandEl.style.outline = '2px solid rgba(59,130,246,0.6)'; +}); + +editorScreenTrack.addEventListener('dragleave', (e) => { + // Only clear if leaving the track entirely + if (!editorScreenTrack.contains(e.relatedTarget)) { + editorScreenTrack + .querySelectorAll('[data-section-id]') + .forEach((el) => (el.style.outline = '')); + } +}); + +editorScreenTrack.addEventListener('drop', async (e) => { + e.preventDefault(); + editorScreenTrack.querySelectorAll('[data-section-id]').forEach((el) => (el.style.outline = '')); + + if (!editorState || editorState.rendering || !activeProjectPath) return; + const file = e.dataTransfer?.files?.[0]; + if (!file || !file.path || !isImageFile(file.name)) return; + + const bandEl = e.target.closest?.('[data-section-id]'); + const sectionId = bandEl?.dataset?.sectionId; + const section = sectionId ? editorState.sections.find((s) => s.id === sectionId) : null; + if (!section) return; + + await importImageToSection(file.path, section); +}); + +// ===== Image picker button ===== +const editorImageBtn = document.getElementById('editorImageBtn'); +editorImageBtn.addEventListener('click', async () => { + if (!editorState || editorState.rendering || !activeProjectPath) return; + const section = getSelectedSection(); + if (!section) return; + + const filePath = await window.electronAPI.pickImageFile(); + if (!filePath) return; + + await importImageToSection(filePath, section); +}); + +// ===== Editor button handlers ===== + +new ResizeObserver(() => renderWaveform()).observe(editorTimelineWrapper); + +editorTimelineWrapper.addEventListener( + 'wheel', + (e) => { + if (!editorState) return; + // Pinch-to-zoom (trackpad) sends ctrlKey with deltaY + // Also support Ctrl+scroll wheel + if (e.ctrlKey || e.metaKey) { + e.preventDefault(); + const factor = 1 - e.deltaY * 0.01; + applyTimelineZoom(timelineZoom * factor, e.clientX); + } + }, + { passive: false } +); + +// Back button removed; user navigates via header tabs +editorUndoBtn.addEventListener('click', editorUndo); +editorRedoBtn.addEventListener('click', editorRedo); +editorPlayBtn.addEventListener('click', editorTogglePlay); +editorSplitBtn.addEventListener('click', splitSectionAtPlayhead); +editorToggleCamBtn.addEventListener('click', toggleCameraVisibility); +editorCamFullBtn.addEventListener('click', toggleCameraFullscreen); +editorApplyFutureBtn.addEventListener('click', applyStyleToFutureSections); +editorBgZoomInput.addEventListener('input', () => { + if (!editorState || editorState.rendering) return; + const changed = setSelectedSectionBackgroundZoom(editorBgZoomInput.value, { + pushHistory: !sectionZoomDragActive + }); + if (changed) sectionZoomDragActive = true; +}); +editorBgZoomInput.addEventListener('change', commitSectionZoomChange); +editorBgZoomInput.addEventListener('pointerup', commitSectionZoomChange); +editorBgZoomInput.addEventListener('blur', commitSectionZoomChange); +updateSectionZoomControls(); + +// ===== Render pipeline ===== + +editorRenderBtn.addEventListener('click', async () => { + if (!editorState || editorState.rendering) return; + await renderVideo(); +}); + +function setRenderBtnState(text, style = 'idle') { + clearTimeout(editorRenderTimeout); + editorRenderBtn.textContent = text; + if (style === 'busy') { + editorRenderBtn.className = + 'px-4 py-1.5 bg-neutral-700 text-neutral-300 rounded-lg text-sm font-medium transition-colors min-w-[80px] text-center cursor-wait'; + } else if (style === 'done') { + editorRenderBtn.className = + 'px-4 py-1.5 bg-emerald-600 text-white rounded-lg text-sm font-medium transition-colors min-w-[80px] text-center'; + } else if (style === 'error') { + editorRenderBtn.className = + 'px-4 py-1.5 bg-red-600 text-white rounded-lg text-sm font-medium transition-colors min-w-[80px] text-center'; + } else { + editorRenderBtn.className = + 'px-4 py-1.5 bg-white text-neutral-950 hover:bg-neutral-200 rounded-lg text-sm font-medium transition-colors min-w-[80px] text-center'; + } +} + +async function renderVideo() { + commitSectionZoomChange(); + editorState.rendering = true; + editorState.renderProgress = 0; + setRenderBtnState('Rendering...', 'busy'); + processingTitle.textContent = 'Rendering export...'; + processingStatus.textContent = 'Preparing render...'; + setProcessingProgress(0); + editorPause(); + + // Disable controls + editorUndoBtn.disabled = true; + editorRedoBtn.disabled = true; + editorPlayBtn.disabled = true; + editorSplitBtn.disabled = true; + editorToggleCamBtn.disabled = true; + editorCamFullBtn.disabled = true; + editorRenderBtn.disabled = true; + updateSectionZoomControls(); + + try { + const renderKeyframes = getRenderKeyframes(); + const renderSections = getRenderSections(); + + // Collect takes referenced by sections + const referencedTakeIds = new Set(editorState.sections.map((s) => s.takeId).filter(Boolean)); + const takes = []; + for (const takeId of referencedTakeIds) { + const take = activeProject?.takes?.find((t) => t.id === takeId); + if (take) { + takes.push({ id: take.id, screenPath: take.screenPath, cameraPath: take.cameraPath }); + } + } + + const mp4Path = await window.electronAPI.renderComposite({ + takes, + sections: renderSections, + keyframes: renderKeyframes, + pipSize: editorState.pipSize, + screenFitMode: editorState.screenFitMode, + exportAudioPreset: normalizeExportAudioPreset(exportAudioPresetSelect.value), + exportVideoPreset: normalizeExportVideoPreset(exportVideoPresetSelect.value), + cameraSyncOffsetMs: editorState.cameraSyncOffsetMs, + sourceWidth: editorState.sourceWidth || CANVAS_W, + sourceHeight: editorState.sourceHeight || CANVAS_H, + outputFolder: saveFolder }); - // Project home actions - async function openProjectByPath(projectPath, preferredView = 'timeline') { - if (!projectPath) return; - clearProjectHomeMessage(); - try { - const opened = await window.electronAPI.projectOpen(projectPath); - if (!opened?.projectPath || !opened?.project) return; - await activateProject(opened.projectPath, opened.project, preferredView); - if (opened?.recoveryTake) { - await recoverPendingTake(opened.recoveryTake); + editorState.rendering = false; + editorState.renderProgress = 1; + setProcessingProgress(1); + setRenderBtnState('Done!', 'done'); + console.log('Rendered:', mp4Path); + await persistProjectNow(); + } catch (err) { + editorState.rendering = false; + editorState.renderProgress = 0; + setProcessingProgress(null); + console.error('Render error:', err); + setRenderBtnState('Failed', 'error'); + } + + // Re-enable controls + updateUndoRedoButtons(); + editorPlayBtn.disabled = false; + editorSplitBtn.disabled = false; + editorToggleCamBtn.disabled = false; + editorCamFullBtn.disabled = false; + editorRenderBtn.disabled = false; + updateSectionZoomControls(); + + editorRenderTimeout = setTimeout(() => setRenderBtnState('Render', 'idle'), 3000); + editorSeek(0); +} + +// ===== Segment selection ===== + +transcriptContent.addEventListener('click', (e) => { + if (!e.target.closest('[data-segment-index]')) selectSegment(-1); +}); + +let selectedSegmentIndex = -1; +const recordingUndoStack = []; + +function selectSegment(index) { + // Deselect previous + if (selectedSegmentIndex >= 0) { + const prev = transcriptContent.querySelector(`[data-segment-index="${selectedSegmentIndex}"]`); + if (prev) prev.style.outline = ''; + } + selectedSegmentIndex = index; + if (index >= 0) { + const el = transcriptContent.querySelector(`[data-segment-index="${index}"]`); + if (el) el.style.outline = '2px solid rgba(255, 255, 255, 0.3)'; + } +} + +function applySegmentDeletedStyle(el, deleted) { + el.style.textDecoration = deleted ? 'line-through' : ''; + el.style.opacity = deleted ? '0.4' : ''; +} + +// ===== Keyboard shortcuts ===== + +function updateSegmentBadge() { + const total = speechSegments.length; + const removed = speechSegments.filter((s) => s.deleted).length; + const active = total - removed; + if (removed > 0) { + segmentBadge.textContent = `${active} segment${active !== 1 ? 's' : ''} (${removed} removed)`; + } else { + segmentBadge.textContent = `${total} segment${total !== 1 ? 's' : ''}`; + } +} + +document.addEventListener('keydown', (e) => { + // Backspace during recording: toggle delete on selected segment, or remove last non-deleted + if (recording && e.code === 'Backspace') { + e.preventDefault(); + if (selectedSegmentIndex >= 0 && selectedSegmentIndex < speechSegments.length) { + // Toggle delete on selected segment + const seg = speechSegments[selectedSegmentIndex]; + recordingUndoStack.push({ segmentIndex: selectedSegmentIndex, wasDeleted: seg.deleted }); + seg.deleted = !seg.deleted; + const el = transcriptContent.querySelector(`[data-segment-index="${selectedSegmentIndex}"]`); + if (el) applySegmentDeletedStyle(el, seg.deleted); + updateSegmentBadge(); + } else { + // No selection: delete last non-deleted segment + for (let i = speechSegments.length - 1; i >= 0; i--) { + if (!speechSegments[i].deleted) { + recordingUndoStack.push({ segmentIndex: i, wasDeleted: false }); + speechSegments[i].deleted = true; + const el = transcriptContent.querySelector(`[data-segment-index="${i}"]`); + if (el) applySegmentDeletedStyle(el, true); + updateSegmentBadge(); + break; } - await refreshRecentProjects(); - } catch (error) { - console.error('Failed to open project:', error); - showProjectHomeMessage(error?.message || 'Failed to open project folder.'); } } + return; + } - projectHomeView.addEventListener('click', (event) => { - const target = event.target?.id || event.target?.tagName || 'unknown'; - console.log('project-home-click', target); - }, true); - - createProjectBtn.addEventListener('click', async () => { - const name = (newProjectNameInput.value || '').trim() || 'Untitled Project'; - showProjectHomeMessage('Opening folder picker...', 'info'); - try { - const projectPath = await window.electronAPI.pickProjectLocation({ name }); - if (!projectPath) return; - const created = await window.electronAPI.projectCreate({ projectPath, name }); - if (!created?.projectPath || !created?.project) return; - newProjectNameInput.value = ''; - clearProjectHomeMessage(); - await activateProject(created.projectPath, created.project, 'recording'); - await refreshRecentProjects(); - } catch (error) { - console.error('Failed to create project:', error); - showProjectHomeMessage(error?.message || 'Failed to create project.'); - } - }); - - openProjectBtn.addEventListener('click', async () => { - showProjectHomeMessage('Opening folder picker...', 'info'); - try { - const folder = await window.electronAPI.pickFolder({ - title: 'Open Project Folder', - buttonLabel: 'Open Project' - }); - if (!folder) return; - await openProjectByPath(folder, 'timeline'); - } catch (error) { - console.error('Failed to choose project folder:', error); - showProjectHomeMessage(error?.message || 'Failed to choose project folder.'); - } - }); - - resumeLastBtn.addEventListener('click', async () => { - const projectPath = resumeLastBtn.dataset.projectPath; - if (!projectPath) return; - await openProjectByPath(projectPath, 'timeline'); - }); - - recentProjectsList.addEventListener('click', async (event) => { - const button = event.target.closest('button[data-project-path]'); - if (!button) return; - await openProjectByPath(button.dataset.projectPath, 'timeline'); - }); - - newProjectNameInput.addEventListener('keydown', async (event) => { - if (event.key !== 'Enter') return; - event.preventDefault(); - createProjectBtn.click(); - }); - - // Init - setWorkspaceView('home'); - syncContentProtection(); - updateWorkspaceHeader(); - refreshRecentProjects(); - - window.addEventListener('beforeunload', () => { - clearMediaIdleTimer(); - if (!recording && !hasActiveRecorders()) { - cleanupRendererMediaResources(); - } - flushScheduledProjectSave().catch((error) => { - console.warn('Failed to flush project save on exit:', error); - }); + // Ctrl+Z during recording: undo last delete/undelete action + if (recording && e.code === 'KeyZ' && (e.metaKey || e.ctrlKey) && !e.shiftKey) { + e.preventDefault(); + const action = recordingUndoStack.pop(); + if (action) { + speechSegments[action.segmentIndex].deleted = action.wasDeleted; + const el = transcriptContent.querySelector(`[data-segment-index="${action.segmentIndex}"]`); + if (el) applySegmentDeletedStyle(el, action.wasDeleted); + updateSegmentBadge(); + } + return; + } + + // Escape during recording: deselect segment + if (recording && e.code === 'Escape') { + selectSegment(-1); + return; + } + + if (!editorState || editorState.rendering || activeWorkspaceView !== 'timeline') return; + // Don't capture if focus is in an input + if (e.target.tagName === 'SELECT' || e.target.tagName === 'INPUT') return; + + if (e.code === 'KeyZ' && (e.metaKey || e.ctrlKey)) { + e.preventDefault(); + if (e.shiftKey) { + editorRedo(); + } else { + editorUndo(); + } + return; + } + + if (e.code === 'Space') { + e.preventDefault(); + editorTogglePlay(); + } else if (e.code === 'ArrowLeft') { + e.preventDefault(); + editorSeek(editorState.currentTime - 1 / 30); + } else if (e.code === 'ArrowRight') { + e.preventDefault(); + editorSeek(editorState.currentTime + 1 / 30); + } else if (e.code === 'Backspace' || e.code === 'Delete') { + e.preventDefault(); + deleteSelectedSection(); + } else if (e.code === 'KeyS') { + e.preventDefault(); + splitSectionAtPlayhead(); + } else if (e.code === 'KeyC') { + e.preventDefault(); + toggleCameraVisibility(); + } else if (e.code === 'KeyF') { + e.preventDefault(); + toggleCameraFullscreen(); + } else if (e.code === 'KeyL') { + e.preventDefault(); + cyclePlaybackSpeed(); + } else if (e.code === 'KeyI') { + e.preventDefault(); + editorImageBtn.click(); + } +}); + +// Settings +// Settings panel is always visible in the left sidebar + +openFolderBtn.addEventListener('click', () => { + if (activeProjectPath) window.electronAPI.openFolder(activeProjectPath); +}); + +activeProjectPathEl.addEventListener('click', () => { + if (activeProjectPath) window.electronAPI.openFolder(activeProjectPath); +}); + +pickFolderBtn.addEventListener('click', () => { + setWorkspaceView('home'); +}); + +contentProtectionToggle.addEventListener('change', async () => { + hideFromRecording = contentProtectionToggle.checked ? 'true' : 'false'; + await syncContentProtection(); + scheduleProjectSave(); +}); + +exportAudioPresetSelect.addEventListener('change', () => { + exportAudioPresetSelect.value = normalizeExportAudioPreset(exportAudioPresetSelect.value); + if (activeProject?.settings) { + activeProject.settings.exportAudioPreset = exportAudioPresetSelect.value; + } + scheduleProjectSave(); +}); + +exportVideoPresetSelect.addEventListener('change', () => { + exportVideoPresetSelect.value = normalizeExportVideoPreset(exportVideoPresetSelect.value); + if (activeProject?.settings) { + activeProject.settings.exportVideoPreset = exportVideoPresetSelect.value; + } + scheduleProjectSave(); +}); + +cameraSyncOffsetInput.addEventListener('change', () => { + const normalized = normalizeCameraSyncOffsetMs(cameraSyncOffsetInput.value); + cameraSyncOffsetInput.value = String(normalized); + if (activeProject?.settings) { + activeProject.settings.cameraSyncOffsetMs = normalized; + } + if (editorState) { + editorState.cameraSyncOffsetMs = normalized; + editorSeek(editorState.currentTime); + } + scheduleProjectSave(); +}); + +// Source change handlers +screenFitSelect.addEventListener('change', () => { + if (editorState) editorState.screenFitMode = screenFitSelect.value; + updatePreview(); + scheduleProjectSave(); +}); + +screenSelect.addEventListener('change', async () => { + try { + await updateScreenStream(); + } catch (e) { + console.error(e); + } + updatePreview(); +}); + +cameraSelect.addEventListener('change', async () => { + try { + await updateCameraStream(); + } catch (e) { + console.error(e); + } + updatePreview(); +}); + +audioSelect.addEventListener('change', async () => { + try { + await updateAudioStream(); + } catch (e) { + console.error(e); + } +}); + +recordBtn.addEventListener('click', toggleRecording); + +// Workspace navigation +goRecordingBtn.addEventListener('click', () => { + if (!activeProjectPath) return; + setWorkspaceView('recording'); +}); + +goTimelineBtn.addEventListener('click', () => { + if (!activeProjectPath || !editorState) return; + setWorkspaceView('timeline'); +}); + +switchProjectBtn.addEventListener('click', async () => { + if (recording) return; + await flushScheduledProjectSave(); + setWorkspaceView('home'); + await refreshRecentProjects(); +}); + +// Project home actions +async function openProjectByPath(projectPath, preferredView = 'timeline') { + if (!projectPath) return; + clearProjectHomeMessage(); + try { + const opened = await window.electronAPI.projectOpen(projectPath); + if (!opened?.projectPath || !opened?.project) return; + await activateProject(opened.projectPath, opened.project, preferredView); + if (opened?.recoveryTake) { + await recoverPendingTake(opened.recoveryTake); + } + await refreshRecentProjects(); + } catch (error) { + console.error('Failed to open project:', error); + showProjectHomeMessage(error?.message || 'Failed to open project folder.'); + } +} + +projectHomeView.addEventListener( + 'click', + (event) => { + const target = event.target?.id || event.target?.tagName || 'unknown'; + console.log('project-home-click', target); + }, + true +); + +createProjectBtn.addEventListener('click', async () => { + const name = (newProjectNameInput.value || '').trim() || 'Untitled Project'; + showProjectHomeMessage('Opening folder picker...', 'info'); + try { + const projectPath = await window.electronAPI.pickProjectLocation({ name }); + if (!projectPath) return; + const created = await window.electronAPI.projectCreate({ projectPath, name }); + if (!created?.projectPath || !created?.project) return; + newProjectNameInput.value = ''; + clearProjectHomeMessage(); + await activateProject(created.projectPath, created.project, 'recording'); + await refreshRecentProjects(); + } catch (error) { + console.error('Failed to create project:', error); + showProjectHomeMessage(error?.message || 'Failed to create project.'); + } +}); + +openProjectBtn.addEventListener('click', async () => { + showProjectHomeMessage('Opening folder picker...', 'info'); + try { + const folder = await window.electronAPI.pickFolder({ + title: 'Open Project Folder', + buttonLabel: 'Open Project' }); + if (!folder) return; + await openProjectByPath(folder, 'timeline'); + } catch (error) { + console.error('Failed to choose project folder:', error); + showProjectHomeMessage(error?.message || 'Failed to choose project folder.'); + } +}); + +resumeLastBtn.addEventListener('click', async () => { + const projectPath = resumeLastBtn.dataset.projectPath; + if (!projectPath) return; + await openProjectByPath(projectPath, 'timeline'); +}); + +recentProjectsList.addEventListener('click', async (event) => { + const button = event.target.closest('button[data-project-path]'); + if (!button) return; + await openProjectByPath(button.dataset.projectPath, 'timeline'); +}); + +newProjectNameInput.addEventListener('keydown', async (event) => { + if (event.key !== 'Enter') return; + event.preventDefault(); + createProjectBtn.click(); +}); + +// Init +setWorkspaceView('home'); +syncContentProtection(); +updateWorkspaceHeader(); +refreshRecentProjects(); + +window.addEventListener('beforeunload', () => { + clearMediaIdleTimer(); + if (!recording && !hasActiveRecorders()) { + cleanupRendererMediaResources(); + } + flushScheduledProjectSave().catch((error) => { + console.warn('Failed to flush project save on exit:', error); + }); +}); diff --git a/src/renderer/features/recording/recorder-utils.ts b/src/renderer/features/recording/recorder-utils.ts index 9d81507..d0b1435 100644 --- a/src/renderer/features/recording/recorder-utils.ts +++ b/src/renderer/features/recording/recorder-utils.ts @@ -1,7 +1,7 @@ export const RECORDER_MIME_CANDIDATES = [ 'video/webm; codecs=vp8', 'video/webm', - 'video/webm; codecs=vp9', + 'video/webm; codecs=vp9' ] as const; export const RECORDER_TIMESLICE_MS = 1000; @@ -24,25 +24,20 @@ export interface FinalizedRecordingResult { } export function getSupportedRecorderMimeType( - mediaRecorderCtor: MediaRecorderCtorLike | undefined = globalThis.MediaRecorder, + mediaRecorderCtor: MediaRecorderCtorLike | undefined = globalThis.MediaRecorder ): string { - if ( - !mediaRecorderCtor || - typeof mediaRecorderCtor.isTypeSupported !== 'function' - ) { + if (!mediaRecorderCtor || typeof mediaRecorderCtor.isTypeSupported !== 'function') { return ''; } return ( - RECORDER_MIME_CANDIDATES.find((mimeType) => - mediaRecorderCtor.isTypeSupported?.(mimeType), - ) || '' + RECORDER_MIME_CANDIDATES.find((mimeType) => mediaRecorderCtor.isTypeSupported?.(mimeType)) || '' ); } export function getRecorderOptions( { suffix, hasAudio = true }: { suffix?: string; hasAudio?: boolean } = {}, - mediaRecorderCtor: MediaRecorderCtorLike | undefined = globalThis.MediaRecorder, + mediaRecorderCtor: MediaRecorderCtorLike | undefined = globalThis.MediaRecorder ): MediaRecorderOptions { const mimeType = getSupportedRecorderMimeType(mediaRecorderCtor); const options: MediaRecorderOptions = mimeType ? { mimeType } : {}; @@ -69,7 +64,7 @@ export function getRecorderFinalizeTimeoutMs(): number { export function shouldRenderPreviewFrame( now: number, lastFrameAt: number, - isRecording: boolean, + isRecording: boolean ): boolean { const targetFps = isRecording ? PREVIEW_FPS_RECORDING : PREVIEW_FPS_IDLE; const minFrameIntervalMs = 1000 / targetFps; @@ -78,7 +73,7 @@ export function shouldRenderPreviewFrame( export function createCameraRecordingStream( cameraStream: MediaStream | null | undefined, - MediaStreamCtor: MediaStreamCtorLike = globalThis.MediaStream, + MediaStreamCtor: MediaStreamCtorLike = globalThis.MediaStream ): MediaStream | null { if (!cameraStream || typeof cameraStream.getVideoTracks !== 'function') { return null; @@ -95,14 +90,14 @@ export async function finalizeRecordingChunks({ saveVideo, suffix, BlobCtor = globalThis.Blob, - mimeType = 'video/webm', + mimeType = 'video/webm' }: { chunks: BlobPart[]; saveFolder: string; saveVideo: ( buffer: ArrayBuffer, saveFolder: string, - suffix: string, + suffix: string ) => Promise; suffix: string; BlobCtor?: BlobCtorLike; @@ -114,7 +109,7 @@ export async function finalizeRecordingChunks({ blob, error: `${suffix} recording produced no data`, path: null, - suffix, + suffix }; } @@ -129,14 +124,14 @@ export async function finalizeRecordingChunks({ blob, error: null, path: savedPath, - suffix, + suffix }; } catch (error) { return { blob, error: error instanceof Error ? error.message : String(error), path: null, - suffix, + suffix }; } } diff --git a/src/renderer/features/timeline/camera-sync.ts b/src/renderer/features/timeline/camera-sync.ts index 6b2c53d..57ec602 100644 --- a/src/renderer/features/timeline/camera-sync.ts +++ b/src/renderer/features/timeline/camera-sync.ts @@ -17,7 +17,7 @@ export function normalizeCameraSyncOffsetMs(value: unknown): number { export function resolveCameraPlaybackTargetTime( screenTime: unknown, - cameraSyncOffsetMs = 0, + cameraSyncOffsetMs = 0 ): number { const baseTime = Number(screenTime); if (!Number.isFinite(baseTime)) return 0; @@ -29,12 +29,12 @@ export function computePlaybackSeekPlan( currentCameraTime: unknown, targetSourceTime: unknown, cameraSyncOffsetMs = 0, - seekThreshold = 0.01, + seekThreshold = 0.01 ): PlaybackSeekPlan { const safeTargetSourceTime = Number(targetSourceTime); const targetCameraTime = resolveCameraPlaybackTargetTime( safeTargetSourceTime, - cameraSyncOffsetMs, + cameraSyncOffsetMs ); const screenTime = Number(currentScreenTime); const cameraTime = Number(currentCameraTime); @@ -43,25 +43,23 @@ export function computePlaybackSeekPlan( : 0.01; const screenNeedsSeek = - !Number.isFinite(screenTime) || - Math.abs(screenTime - safeTargetSourceTime) > safeSeekThreshold; + !Number.isFinite(screenTime) || Math.abs(screenTime - safeTargetSourceTime) > safeSeekThreshold; const cameraNeedsSeek = - !Number.isFinite(cameraTime) || - Math.abs(cameraTime - targetCameraTime) > safeSeekThreshold; + !Number.isFinite(cameraTime) || Math.abs(cameraTime - targetCameraTime) > safeSeekThreshold; return { targetSourceTime: safeTargetSourceTime, targetCameraTime, screenNeedsSeek, cameraNeedsSeek, - needsSeek: screenNeedsSeek || cameraNeedsSeek, + needsSeek: screenNeedsSeek || cameraNeedsSeek }; } export function computeCameraPlaybackDrift( screenTime: unknown, cameraTime: unknown, - cameraSyncOffsetMs = 0, + cameraSyncOffsetMs = 0 ): number { const targetTime = resolveCameraPlaybackTargetTime(screenTime, cameraSyncOffsetMs); const actualTime = Number(cameraTime); diff --git a/src/renderer/features/timeline/keyframe-ops.ts b/src/renderer/features/timeline/keyframe-ops.ts index f1a78de..18cdbf4 100644 --- a/src/renderer/features/timeline/keyframe-ops.ts +++ b/src/renderer/features/timeline/keyframe-ops.ts @@ -25,7 +25,7 @@ export function generateSectionId(): string { * Mutates the sections in place. */ export function reindexSections( - sections: Array<{ id: string; index: number; label: string }>, + sections: Array<{ id: string; index: number; label: string }> ): void { for (let index = 0; index < sections.length; index += 1) { sections[index].index = index; @@ -41,7 +41,7 @@ export function reindexSections( export function moveSectionToIndex( sections: Array<{ id: string; index: number; label: string }>, fromIndex: number, - toIndex: number, + toIndex: number ): boolean { if (fromIndex < 0 || fromIndex >= sections.length) return false; if (toIndex < 0 || toIndex >= sections.length) return false; @@ -63,7 +63,7 @@ export function moveSectionToIndex( export function moveSectionsToIndex( sections: Array<{ id: string; index: number; label: string }>, selectedIds: Set, - insertBefore: number, + insertBefore: number ): boolean { if (selectedIds.size === 0) return false; @@ -77,11 +77,7 @@ export function moveSectionsToIndex( if (selected.length === 0 || remaining.length === 0) return false; const clamped = Math.max(0, Math.min(insertBefore, remaining.length)); - const result = [ - ...remaining.slice(0, clamped), - ...selected, - ...remaining.slice(clamped), - ]; + const result = [...remaining.slice(0, clamped), ...selected, ...remaining.slice(clamped)]; if (result.every((s, i) => s.id === sections[i].id)) return false; @@ -100,11 +96,9 @@ export function buildSplitAnchorKeyframe( parentSectionId: string | null, newSectionId: string, newSectionStart: number, - defaults: KeyframeDefaults, + defaults: KeyframeDefaults ): Keyframe { - const parent = (keyframes || []).find( - (keyframe) => keyframe.sectionId === parentSectionId, - ); + const parent = (keyframes || []).find((keyframe) => keyframe.sectionId === parentSectionId); const defaultZoom = defaults.backgroundZoom ?? 1; return { time: newSectionStart, @@ -116,6 +110,6 @@ export function buildSplitAnchorKeyframe( backgroundPanX: parent?.backgroundPanX ?? 0, backgroundPanY: parent?.backgroundPanY ?? 0, sectionId: newSectionId, - autoSection: true, + autoSection: true }; } diff --git a/src/renderer/features/timeline/section-utils.ts b/src/renderer/features/timeline/section-utils.ts index 335db0d..44dfb76 100644 --- a/src/renderer/features/timeline/section-utils.ts +++ b/src/renderer/features/timeline/section-utils.ts @@ -22,7 +22,7 @@ export function roundMs(value: number): number { * Builds remapped sections from speech segments (padding, merge, timeline mapping). */ export function buildRemappedSectionsFromSegments( - segments: Array<{ start: number; end: number; text?: string }>, + segments: Array<{ start: number; end: number; text?: string }> ): Section[] { if (!Array.isArray(segments) || segments.length === 0) return []; @@ -36,10 +36,12 @@ export function buildRemappedSectionsFromSegments( return { start, end, - transcript: normalizeTranscriptText(segment.text), + transcript: normalizeTranscriptText(segment.text) }; }) - .filter((segment): segment is { start: number; end: number; transcript: string } => Boolean(segment)) + .filter((segment): segment is { start: number; end: number; transcript: string } => + Boolean(segment) + ) .sort((left, right) => left.start - right.start); if (padded.length === 0) return []; @@ -48,8 +50,8 @@ export function buildRemappedSectionsFromSegments( { start: padded[0].start, end: padded[0].end, - transcripts: padded[0].transcript ? [padded[0].transcript] : [], - }, + transcripts: padded[0].transcript ? [padded[0].transcript] : [] + } ]; for (let index = 1; index < padded.length; index += 1) { @@ -61,7 +63,7 @@ export function buildRemappedSectionsFromSegments( merged.push({ start: padded[index].start, end: padded[index].end, - transcripts: padded[index].transcript ? [padded[index].transcript] : [], + transcripts: padded[index].transcript ? [padded[index].transcript] : [] }); } } @@ -86,7 +88,7 @@ export function buildRemappedSectionsFromSegments( transcript: normalizeTranscriptText(segment.transcripts.join(' ')), label: `Section ${index + 1}`, takeId: null, - imagePath: null, + imagePath: null }); timelineCursor += duration; } @@ -99,7 +101,7 @@ export function buildRemappedSectionsFromSegments( */ export function normalizeSections( rawSections: TranscriptSection[] | unknown, - duration: number, + duration: number ): Section[] { const safeDuration = Math.max(0, Number(duration) || 0); const input = Array.isArray(rawSections) ? rawSections : []; @@ -122,7 +124,7 @@ export function normalizeSections( ? section.transcript : typeof section.text === 'string' ? section.text - : '', + : '' ); start = Math.max(0, start); @@ -138,23 +140,16 @@ export function normalizeSections( sourceStart: Number.isFinite(Number(section.sourceStart)) ? Number(section.sourceStart) : start, - sourceEnd: Number.isFinite(Number(section.sourceEnd)) - ? Number(section.sourceEnd) - : end, + sourceEnd: Number.isFinite(Number(section.sourceEnd)) ? Number(section.sourceEnd) : end, start: roundMs(start), end: roundMs(end), - takeId: - typeof section.takeId === 'string' && section.takeId - ? section.takeId - : null, + takeId: typeof section.takeId === 'string' && section.takeId ? section.takeId : null, transcript, index, label: `Section ${index + 1}`, duration: 0, imagePath: - typeof section.imagePath === 'string' && section.imagePath - ? section.imagePath - : null, + typeof section.imagePath === 'string' && section.imagePath ? section.imagePath : null } satisfies Section; }) .filter((section) => section.end - section.start > 0.0001) @@ -174,7 +169,7 @@ export function normalizeSections( normalized[index].index = index; normalized[index].label = `Section ${index + 1}`; normalized[index].duration = roundMs( - Math.max(0, normalized[index].end - normalized[index].start), + Math.max(0, normalized[index].end - normalized[index].start) ); } @@ -199,8 +194,8 @@ export function buildDefaultSectionsForDuration(duration: number): Section[] { duration: roundMs(safeDuration), transcript: '', takeId: null, - imagePath: null, - }, + imagePath: null + } ]; } @@ -209,7 +204,7 @@ export function buildDefaultSectionsForDuration(duration: number): Section[] { */ export function normalizeTakeSections( rawSections: TranscriptSection[] | unknown, - duration: number, + duration: number ): Section[] { const normalized = normalizeSections(rawSections, duration); if (normalized.length > 0) return normalized; @@ -221,12 +216,10 @@ export function normalizeTakeSections( */ export function attachSectionTranscripts( sections: TranscriptSection[] | unknown, - transcriptSections: TranscriptSection[] | unknown, + transcriptSections: TranscriptSection[] | unknown ): TranscriptSection[] { const baseSections = Array.isArray(sections) ? sections : []; - const transcriptSource = Array.isArray(transcriptSections) - ? transcriptSections - : []; + const transcriptSource = Array.isArray(transcriptSections) ? transcriptSections : []; return baseSections.map((section, index) => { const existing = normalizeTranscriptText( @@ -234,16 +227,14 @@ export function attachSectionTranscripts( ? section.transcript : typeof section.text === 'string' ? section.text - : '', + : '' ); if (existing) { return { ...section, transcript: existing }; } const byIndex = transcriptSource[index]; - let transcript = normalizeTranscriptText( - byIndex?.transcript || byIndex?.text || '', - ); + let transcript = normalizeTranscriptText(byIndex?.transcript || byIndex?.text || ''); if (!transcript) { const sourceStart = Number(section.sourceStart); @@ -259,9 +250,7 @@ export function attachSectionTranscripts( Math.abs(candidateEnd - sourceEnd) <= 0.05 ); }); - transcript = normalizeTranscriptText( - bySource?.transcript || bySource?.text || '', - ); + transcript = normalizeTranscriptText(bySource?.transcript || bySource?.text || ''); } } diff --git a/src/renderer/features/transcript/scribe-status.ts b/src/renderer/features/transcript/scribe-status.ts index b0866c0..62bd1c9 100644 --- a/src/renderer/features/transcript/scribe-status.ts +++ b/src/renderer/features/transcript/scribe-status.ts @@ -37,7 +37,7 @@ function normalizeReason(value: unknown): string { } export function getScribeFailureReason( - message: ScribeMessageLike | null | undefined, + message: ScribeMessageLike | null | undefined ): string | null { const messageType = normalizeReason(message?.message_type); const error = normalizeReason(message?.error); @@ -47,9 +47,7 @@ export function getScribeFailureReason( } if (SCRIBE_ERROR_TYPES.has(messageType)) { - return error && error !== messageType - ? `${messageType}: ${error}` - : messageType; + return error && error !== messageType ? `${messageType}: ${error}` : messageType; } if (!messageType && error) { @@ -60,7 +58,7 @@ export function getScribeFailureReason( } export function getScribeStatusFromMessage( - message: ScribeMessageLike | null | undefined, + message: ScribeMessageLike | null | undefined ): TranscriptStatus | null { const messageType = normalizeReason(message?.message_type); @@ -85,7 +83,7 @@ export function getScribeStatusFromMessage( export function getScribeStatusFromCloseEvent( event: ScribeCloseLike | null | undefined, - lastFailureReason?: string | null, + lastFailureReason?: string | null ): TranscriptStatus { const previousReason = normalizeReason(lastFailureReason); if (previousReason) { diff --git a/src/renderer/features/transcript/transcript-utils.ts b/src/renderer/features/transcript/transcript-utils.ts index 46ddbe1..0d61f41 100644 --- a/src/renderer/features/transcript/transcript-utils.ts +++ b/src/renderer/features/transcript/transcript-utils.ts @@ -13,7 +13,9 @@ export interface TranscriptToken { * Normalizes transcript text by collapsing whitespace and trimming. */ export function normalizeTranscriptText(value: unknown): string { - return String(value || '').replace(/\s+/g, ' ').trim(); + return String(value || '') + .replace(/\s+/g, ' ') + .trim(); } /** @@ -33,9 +35,7 @@ export function stripNonSpeechAnnotations(text: string): string { /** * Extracts spoken word tokens from Scribe token array, excluding annotations. */ -export function extractSpokenWordTokens( - tokens: TranscriptToken[], -): TranscriptToken[] { +export function extractSpokenWordTokens(tokens: TranscriptToken[]): TranscriptToken[] { const spoken: TranscriptToken[] = []; let parenDepth = 0; let bracketDepth = 0; @@ -45,8 +45,7 @@ export function extractSpokenWordTokens( const text = rawText.trim(); if (!text) continue; - const isInsideAnnotation = - parenDepth > 0 || bracketDepth > 0 || /[()[\]]/.test(rawText); + const isInsideAnnotation = parenDepth > 0 || bracketDepth > 0 || /[()[\]]/.test(rawText); if (token.type === 'word' && !isInsideAnnotation) { spoken.push(token); diff --git a/src/renderer/styles/main.css b/src/renderer/styles/main.css index 320941b..3417e66 100644 --- a/src/renderer/styles/main.css +++ b/src/renderer/styles/main.css @@ -1,3 +1,1054 @@ -@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap");*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: } +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); +*, +:after, +:before { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgba(59, 130, 246, 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + --tw-contain-size: ; + --tw-contain-layout: ; + --tw-contain-paint: ; + --tw-contain-style: ; +} +::backdrop { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgba(59, 130, 246, 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + --tw-contain-size: ; + --tw-contain-layout: ; + --tw-contain-paint: ; + --tw-contain-style: ; +} -/*! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Inter,system-ui,-apple-system,sans-serif;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{inset:0}.bottom-0{bottom:0}.left-0{left:0}.top-0{top:0}.z-0{z-index:0}.z-20{z-index:20}.z-\[1\]{z-index:1}.-mx-1{margin-left:-.25rem;margin-right:-.25rem}.mx-1{margin-left:.25rem;margin-right:.25rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.ml-3{margin-left:.75rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.block{display:block}.flex{display:flex}.hidden{display:none}.h-2{height:.5rem}.h-5{height:1.25rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-full{height:100%}.h-screen{height:100vh}.min-h-0{min-height:0}.w-0\.5{width:.125rem}.w-10{width:2.5rem}.w-20{width:5rem}.w-28{width:7rem}.w-56{width:14rem}.w-64{width:16rem}.w-full{width:100%}.w-px{width:1px}.min-w-0{min-width:0}.min-w-\[46px\]{min-width:46px}.min-w-\[80px\]{min-width:80px}.max-w-lg{max-width:32rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.cursor-pointer{cursor:pointer}.cursor-wait{cursor:wait}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.125rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem*var(--tw-space-y-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.375rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem*var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem*var(--tw-space-y-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.25rem*var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-y-hidden{overflow-y:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-blue-500\/40{border-color:rgba(59,130,246,.4)}.border-neutral-700{--tw-border-opacity:1;border-color:rgb(64 64 64/var(--tw-border-opacity,1))}.border-neutral-800{--tw-border-opacity:1;border-color:rgb(38 38 38/var(--tw-border-opacity,1))}.border-neutral-800\/40{border-color:rgba(38,38,38,.4)}.border-neutral-800\/60{border-color:rgba(38,38,38,.6)}.border-neutral-800\/80{border-color:rgba(38,38,38,.8)}.border-red-500\/40{border-color:rgba(239,68,68,.4)}.bg-amber-500{--tw-bg-opacity:1;background-color:rgb(245 158 11/var(--tw-bg-opacity,1))}.bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity,1))}.bg-blue-500\/10{background-color:rgba(59,130,246,.1)}.bg-emerald-500{--tw-bg-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity,1))}.bg-emerald-600{--tw-bg-opacity:1;background-color:rgb(5 150 105/var(--tw-bg-opacity,1))}.bg-neutral-700{--tw-bg-opacity:1;background-color:rgb(64 64 64/var(--tw-bg-opacity,1))}.bg-neutral-800{--tw-bg-opacity:1;background-color:rgb(38 38 38/var(--tw-bg-opacity,1))}.bg-neutral-900{--tw-bg-opacity:1;background-color:rgb(23 23 23/var(--tw-bg-opacity,1))}.bg-neutral-900\/40{background-color:hsla(0,0%,9%,.4)}.bg-neutral-900\/60{background-color:hsla(0,0%,9%,.6)}.bg-neutral-950{--tw-bg-opacity:1;background-color:rgb(10 10 10/var(--tw-bg-opacity,1))}.bg-neutral-950\/95{background-color:hsla(0,0%,4%,.95)}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.bg-red-500\/10{background-color:rgba(239,68,68,.1)}.bg-red-600{--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity,1))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.object-contain{-o-object-fit:contain;object-fit:contain}.p-1{padding:.25rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-3\.5{padding-left:.875rem;padding-right:.875rem}.px-4{padding-left:1rem;padding-right:1rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.pb-1{padding-bottom:.25rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.font-sans{font-family:Inter,system-ui,-apple-system,sans-serif}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[8px\]{font-size:8px}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-relaxed{line-height:1.625}.leading-snug{line-height:1.375}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.text-amber-400{--tw-text-opacity:1;color:rgb(251 191 36/var(--tw-text-opacity,1))}.text-blue-200{--tw-text-opacity:1;color:rgb(191 219 254/var(--tw-text-opacity,1))}.text-emerald-400{--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity,1))}.text-neutral-100{--tw-text-opacity:1;color:rgb(245 245 245/var(--tw-text-opacity,1))}.text-neutral-200{--tw-text-opacity:1;color:rgb(229 229 229/var(--tw-text-opacity,1))}.text-neutral-300{--tw-text-opacity:1;color:rgb(212 212 212/var(--tw-text-opacity,1))}.text-neutral-400{--tw-text-opacity:1;color:rgb(163 163 163/var(--tw-text-opacity,1))}.text-neutral-50{--tw-text-opacity:1;color:rgb(250 250 250/var(--tw-text-opacity,1))}.text-neutral-500{--tw-text-opacity:1;color:rgb(115 115 115/var(--tw-text-opacity,1))}.text-neutral-600{--tw-text-opacity:1;color:rgb(82 82 82/var(--tw-text-opacity,1))}.text-neutral-950{--tw-text-opacity:1;color:rgb(10 10 10/var(--tw-text-opacity,1))}.text-red-200{--tw-text-opacity:1;color:rgb(254 202 202/var(--tw-text-opacity,1))}.text-red-400{--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.line-through{text-decoration-line:line-through}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.accent-white{accent-color:#fff}.outline{outline-style:solid}.blur{--tw-blur:blur(8px)}.blur,.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-md{--tw-backdrop-blur:blur(12px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-300{transition-duration:.3s}.duration-75{transition-duration:75ms}select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23737373' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 10px center;padding-right:32px}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:#404040;border-radius:3px}::-webkit-scrollbar-thumb:hover{background:#525252}.placeholder\:text-neutral-600::-moz-placeholder{--tw-text-opacity:1;color:rgb(82 82 82/var(--tw-text-opacity,1))}.placeholder\:text-neutral-600::placeholder{--tw-text-opacity:1;color:rgb(82 82 82/var(--tw-text-opacity,1))}.hover\:border-neutral-700:hover{--tw-border-opacity:1;border-color:rgb(64 64 64/var(--tw-border-opacity,1))}.hover\:bg-neutral-200:hover{--tw-bg-opacity:1;background-color:rgb(229 229 229/var(--tw-bg-opacity,1))}.hover\:bg-neutral-600:hover{--tw-bg-opacity:1;background-color:rgb(82 82 82/var(--tw-bg-opacity,1))}.hover\:bg-neutral-700:hover{--tw-bg-opacity:1;background-color:rgb(64 64 64/var(--tw-bg-opacity,1))}.hover\:bg-neutral-800:hover{--tw-bg-opacity:1;background-color:rgb(38 38 38/var(--tw-bg-opacity,1))}.hover\:bg-neutral-800\/60:hover{background-color:rgba(38,38,38,.6)}.hover\:bg-neutral-900:hover{--tw-bg-opacity:1;background-color:rgb(23 23 23/var(--tw-bg-opacity,1))}.hover\:bg-red-700:hover{--tw-bg-opacity:1;background-color:rgb(185 28 28/var(--tw-bg-opacity,1))}.hover\:text-neutral-200:hover{--tw-text-opacity:1;color:rgb(229 229 229/var(--tw-text-opacity,1))}.hover\:text-neutral-300:hover{--tw-text-opacity:1;color:rgb(212 212 212/var(--tw-text-opacity,1))}.focus\:border-neutral-600:focus{--tw-border-opacity:1;border-color:rgb(82 82 82/var(--tw-border-opacity,1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-1:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-neutral-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(82 82 82/var(--tw-ring-opacity,1))}.disabled\:cursor-default:disabled{cursor:default}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:hover\:bg-neutral-800:hover:disabled{--tw-bg-opacity:1;background-color:rgb(38 38 38/var(--tw-bg-opacity,1))} \ No newline at end of file +/*! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com*/ +*, +:after, +:before { + box-sizing: border-box; + border: 0 solid #e5e7eb; +} +:after, +:before { + --tw-content: ''; +} +:host, +html { + line-height: 1.5; + -webkit-text-size-adjust: 100%; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + font-family: + Inter, + system-ui, + -apple-system, + sans-serif; + font-feature-settings: normal; + font-variation-settings: normal; + -webkit-tap-highlight-color: transparent; +} +body { + margin: 0; + line-height: inherit; +} +hr { + height: 0; + color: inherit; + border-top-width: 1px; +} +abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} +a { + color: inherit; + text-decoration: inherit; +} +b, +strong { + font-weight: bolder; +} +code, +kbd, +pre, +samp { + font-family: + ui-monospace, + SFMono-Regular, + Menlo, + Monaco, + Consolas, + Liberation Mono, + Courier New, + monospace; + font-feature-settings: normal; + font-variation-settings: normal; + font-size: 1em; +} +small { + font-size: 80%; +} +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} +sub { + bottom: -0.25em; +} +sup { + top: -0.5em; +} +table { + text-indent: 0; + border-color: inherit; + border-collapse: collapse; +} +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + font-feature-settings: inherit; + font-variation-settings: inherit; + font-size: 100%; + font-weight: inherit; + line-height: inherit; + letter-spacing: inherit; + color: inherit; + margin: 0; + padding: 0; +} +button, +select { + text-transform: none; +} +button, +input:where([type='button']), +input:where([type='reset']), +input:where([type='submit']) { + -webkit-appearance: button; + background-color: transparent; + background-image: none; +} +:-moz-focusring { + outline: auto; +} +:-moz-ui-invalid { + box-shadow: none; +} +progress { + vertical-align: baseline; +} +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} +[type='search'] { + -webkit-appearance: textfield; + outline-offset: -2px; +} +::-webkit-search-decoration { + -webkit-appearance: none; +} +::-webkit-file-upload-button { + -webkit-appearance: button; + font: inherit; +} +summary { + display: list-item; +} +blockquote, +dd, +dl, +figure, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +p, +pre { + margin: 0; +} +fieldset { + margin: 0; +} +fieldset, +legend { + padding: 0; +} +menu, +ol, +ul { + list-style: none; + margin: 0; + padding: 0; +} +dialog { + padding: 0; +} +textarea { + resize: vertical; +} +input::-moz-placeholder, +textarea::-moz-placeholder { + opacity: 1; + color: #9ca3af; +} +input::placeholder, +textarea::placeholder { + opacity: 1; + color: #9ca3af; +} +[role='button'], +button { + cursor: pointer; +} +:disabled { + cursor: default; +} +audio, +canvas, +embed, +iframe, +img, +object, +svg, +video { + display: block; + vertical-align: middle; +} +img, +video { + max-width: 100%; + height: auto; +} +[hidden]:where(:not([hidden='until-found'])) { + display: none; +} +.pointer-events-none { + pointer-events: none; +} +.visible { + visibility: visible; +} +.fixed { + position: fixed; +} +.absolute { + position: absolute; +} +.relative { + position: relative; +} +.inset-0 { + inset: 0; +} +.bottom-0 { + bottom: 0; +} +.left-0 { + left: 0; +} +.top-0 { + top: 0; +} +.z-0 { + z-index: 0; +} +.z-20 { + z-index: 20; +} +.z-\[1\] { + z-index: 1; +} +.-mx-1 { + margin-left: -0.25rem; + margin-right: -0.25rem; +} +.mx-1 { + margin-left: 0.25rem; + margin-right: 0.25rem; +} +.mb-1 { + margin-bottom: 0.25rem; +} +.mb-2 { + margin-bottom: 0.5rem; +} +.ml-3 { + margin-left: 0.75rem; +} +.mt-0\.5 { + margin-top: 0.125rem; +} +.mt-1 { + margin-top: 0.25rem; +} +.block { + display: block; +} +.flex { + display: flex; +} +.hidden { + display: none; +} +.h-2 { + height: 0.5rem; +} +.h-5 { + height: 1.25rem; +} +.h-7 { + height: 1.75rem; +} +.h-8 { + height: 2rem; +} +.h-full { + height: 100%; +} +.h-screen { + height: 100vh; +} +.min-h-0 { + min-height: 0; +} +.w-0\.5 { + width: 0.125rem; +} +.w-10 { + width: 2.5rem; +} +.w-20 { + width: 5rem; +} +.w-28 { + width: 7rem; +} +.w-56 { + width: 14rem; +} +.w-64 { + width: 16rem; +} +.w-full { + width: 100%; +} +.w-px { + width: 1px; +} +.min-w-0 { + min-width: 0; +} +.min-w-\[46px\] { + min-width: 46px; +} +.min-w-\[80px\] { + min-width: 80px; +} +.max-w-lg { + max-width: 32rem; +} +.flex-1 { + flex: 1 1 0%; +} +.shrink-0 { + flex-shrink: 0; +} +.transform { + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); +} +@keyframes pulse { + 50% { + opacity: 0.5; + } +} +.animate-pulse { + animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; +} +.cursor-pointer { + cursor: pointer; +} +.cursor-wait { + cursor: wait; +} +.select-none { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} +.flex-col { + flex-direction: column; +} +.items-start { + align-items: flex-start; +} +.items-center { + align-items: center; +} +.justify-center { + justify-content: center; +} +.justify-between { + justify-content: space-between; +} +.gap-2 { + gap: 0.5rem; +} +.gap-3 { + gap: 0.75rem; +} +.gap-4 { + gap: 1rem; +} +.space-y-0\.5 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.125rem * (1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.125rem * var(--tw-space-y-reverse)); +} +.space-y-1\.5 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.375rem * (1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.375rem * var(--tw-space-y-reverse)); +} +.space-y-2 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.5rem * (1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); +} +.space-y-3 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.75rem * (1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.75rem * var(--tw-space-y-reverse)); +} +.space-y-5 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1.25rem * (1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1.25rem * var(--tw-space-y-reverse)); +} +.overflow-hidden { + overflow: hidden; +} +.overflow-x-auto { + overflow-x: auto; +} +.overflow-y-auto { + overflow-y: auto; +} +.overflow-y-hidden { + overflow-y: hidden; +} +.truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.rounded { + border-radius: 0.25rem; +} +.rounded-full { + border-radius: 9999px; +} +.rounded-lg { + border-radius: 0.5rem; +} +.rounded-md { + border-radius: 0.375rem; +} +.rounded-xl { + border-radius: 0.75rem; +} +.border { + border-width: 1px; +} +.border-b { + border-bottom-width: 1px; +} +.border-r { + border-right-width: 1px; +} +.border-t { + border-top-width: 1px; +} +.border-blue-500\/40 { + border-color: rgba(59, 130, 246, 0.4); +} +.border-neutral-700 { + --tw-border-opacity: 1; + border-color: rgb(64 64 64 / var(--tw-border-opacity, 1)); +} +.border-neutral-800 { + --tw-border-opacity: 1; + border-color: rgb(38 38 38 / var(--tw-border-opacity, 1)); +} +.border-neutral-800\/40 { + border-color: rgba(38, 38, 38, 0.4); +} +.border-neutral-800\/60 { + border-color: rgba(38, 38, 38, 0.6); +} +.border-neutral-800\/80 { + border-color: rgba(38, 38, 38, 0.8); +} +.border-red-500\/40 { + border-color: rgba(239, 68, 68, 0.4); +} +.bg-amber-500 { + --tw-bg-opacity: 1; + background-color: rgb(245 158 11 / var(--tw-bg-opacity, 1)); +} +.bg-black { + --tw-bg-opacity: 1; + background-color: rgb(0 0 0 / var(--tw-bg-opacity, 1)); +} +.bg-blue-500\/10 { + background-color: rgba(59, 130, 246, 0.1); +} +.bg-emerald-500 { + --tw-bg-opacity: 1; + background-color: rgb(16 185 129 / var(--tw-bg-opacity, 1)); +} +.bg-emerald-600 { + --tw-bg-opacity: 1; + background-color: rgb(5 150 105 / var(--tw-bg-opacity, 1)); +} +.bg-neutral-700 { + --tw-bg-opacity: 1; + background-color: rgb(64 64 64 / var(--tw-bg-opacity, 1)); +} +.bg-neutral-800 { + --tw-bg-opacity: 1; + background-color: rgb(38 38 38 / var(--tw-bg-opacity, 1)); +} +.bg-neutral-900 { + --tw-bg-opacity: 1; + background-color: rgb(23 23 23 / var(--tw-bg-opacity, 1)); +} +.bg-neutral-900\/40 { + background-color: hsla(0, 0%, 9%, 0.4); +} +.bg-neutral-900\/60 { + background-color: hsla(0, 0%, 9%, 0.6); +} +.bg-neutral-950 { + --tw-bg-opacity: 1; + background-color: rgb(10 10 10 / var(--tw-bg-opacity, 1)); +} +.bg-neutral-950\/95 { + background-color: hsla(0, 0%, 4%, 0.95); +} +.bg-red-500 { + --tw-bg-opacity: 1; + background-color: rgb(239 68 68 / var(--tw-bg-opacity, 1)); +} +.bg-red-500\/10 { + background-color: rgba(239, 68, 68, 0.1); +} +.bg-red-600 { + --tw-bg-opacity: 1; + background-color: rgb(220 38 38 / var(--tw-bg-opacity, 1)); +} +.bg-white { + --tw-bg-opacity: 1; + background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1)); +} +.object-contain { + -o-object-fit: contain; + object-fit: contain; +} +.p-1 { + padding: 0.25rem; +} +.p-4 { + padding: 1rem; +} +.p-6 { + padding: 1.5rem; +} +.px-1 { + padding-left: 0.25rem; + padding-right: 0.25rem; +} +.px-1\.5 { + padding-left: 0.375rem; + padding-right: 0.375rem; +} +.px-2 { + padding-left: 0.5rem; + padding-right: 0.5rem; +} +.px-2\.5 { + padding-left: 0.625rem; + padding-right: 0.625rem; +} +.px-3 { + padding-left: 0.75rem; + padding-right: 0.75rem; +} +.px-3\.5 { + padding-left: 0.875rem; + padding-right: 0.875rem; +} +.px-4 { + padding-left: 1rem; + padding-right: 1rem; +} +.py-0\.5 { + padding-top: 0.125rem; + padding-bottom: 0.125rem; +} +.py-1 { + padding-top: 0.25rem; + padding-bottom: 0.25rem; +} +.py-1\.5 { + padding-top: 0.375rem; + padding-bottom: 0.375rem; +} +.py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} +.py-2\.5 { + padding-top: 0.625rem; + padding-bottom: 0.625rem; +} +.py-3 { + padding-top: 0.75rem; + padding-bottom: 0.75rem; +} +.py-6 { + padding-top: 1.5rem; + padding-bottom: 1.5rem; +} +.pb-1 { + padding-bottom: 0.25rem; +} +.text-left { + text-align: left; +} +.text-center { + text-align: center; +} +.text-right { + text-align: right; +} +.font-mono { + font-family: + ui-monospace, + SFMono-Regular, + Menlo, + Monaco, + Consolas, + Liberation Mono, + Courier New, + monospace; +} +.font-sans { + font-family: + Inter, + system-ui, + -apple-system, + sans-serif; +} +.text-\[10px\] { + font-size: 10px; +} +.text-\[11px\] { + font-size: 11px; +} +.text-\[8px\] { + font-size: 8px; +} +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} +.text-sm { + font-size: 0.875rem; + line-height: 1.25rem; +} +.text-xl { + font-size: 1.25rem; + line-height: 1.75rem; +} +.text-xs { + font-size: 0.75rem; + line-height: 1rem; +} +.font-medium { + font-weight: 500; +} +.font-semibold { + font-weight: 600; +} +.uppercase { + text-transform: uppercase; +} +.italic { + font-style: italic; +} +.tabular-nums { + --tw-numeric-spacing: tabular-nums; + font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) + var(--tw-numeric-spacing) var(--tw-numeric-fraction); +} +.leading-relaxed { + line-height: 1.625; +} +.leading-snug { + line-height: 1.375; +} +.tracking-tight { + letter-spacing: -0.025em; +} +.tracking-wider { + letter-spacing: 0.05em; +} +.text-amber-400 { + --tw-text-opacity: 1; + color: rgb(251 191 36 / var(--tw-text-opacity, 1)); +} +.text-blue-200 { + --tw-text-opacity: 1; + color: rgb(191 219 254 / var(--tw-text-opacity, 1)); +} +.text-emerald-400 { + --tw-text-opacity: 1; + color: rgb(52 211 153 / var(--tw-text-opacity, 1)); +} +.text-neutral-100 { + --tw-text-opacity: 1; + color: rgb(245 245 245 / var(--tw-text-opacity, 1)); +} +.text-neutral-200 { + --tw-text-opacity: 1; + color: rgb(229 229 229 / var(--tw-text-opacity, 1)); +} +.text-neutral-300 { + --tw-text-opacity: 1; + color: rgb(212 212 212 / var(--tw-text-opacity, 1)); +} +.text-neutral-400 { + --tw-text-opacity: 1; + color: rgb(163 163 163 / var(--tw-text-opacity, 1)); +} +.text-neutral-50 { + --tw-text-opacity: 1; + color: rgb(250 250 250 / var(--tw-text-opacity, 1)); +} +.text-neutral-500 { + --tw-text-opacity: 1; + color: rgb(115 115 115 / var(--tw-text-opacity, 1)); +} +.text-neutral-600 { + --tw-text-opacity: 1; + color: rgb(82 82 82 / var(--tw-text-opacity, 1)); +} +.text-neutral-950 { + --tw-text-opacity: 1; + color: rgb(10 10 10 / var(--tw-text-opacity, 1)); +} +.text-red-200 { + --tw-text-opacity: 1; + color: rgb(254 202 202 / var(--tw-text-opacity, 1)); +} +.text-red-400 { + --tw-text-opacity: 1; + color: rgb(248 113 113 / var(--tw-text-opacity, 1)); +} +.text-white { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity, 1)); +} +.line-through { + text-decoration-line: line-through; +} +.antialiased { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.accent-white { + accent-color: #fff; +} +.outline { + outline-style: solid; +} +.blur { + --tw-blur: blur(8px); +} +.blur, +.filter { + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) + var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} +.backdrop-blur-md { + --tw-backdrop-blur: blur(12px); + -webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) + var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) + var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) + var(--tw-backdrop-sepia); + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) + var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) + var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); +} +.transition { + transition-property: + color, + background-color, + border-color, + text-decoration-color, + fill, + stroke, + opacity, + box-shadow, + transform, + filter, + -webkit-backdrop-filter; + transition-property: + color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, + transform, filter, backdrop-filter; + transition-property: + color, + background-color, + border-color, + text-decoration-color, + fill, + stroke, + opacity, + box-shadow, + transform, + filter, + backdrop-filter, + -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 0.15s; +} +.transition-all { + transition-property: all; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 0.15s; +} +.transition-colors { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 0.15s; +} +.duration-300 { + transition-duration: 0.3s; +} +.duration-75 { + transition-duration: 75ms; +} +select { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23737373' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 10px center; + padding-right: 32px; +} +::-webkit-scrollbar { + width: 6px; + height: 6px; +} +::-webkit-scrollbar-track { + background: transparent; +} +::-webkit-scrollbar-thumb { + background: #404040; + border-radius: 3px; +} +::-webkit-scrollbar-thumb:hover { + background: #525252; +} +.placeholder\:text-neutral-600::-moz-placeholder { + --tw-text-opacity: 1; + color: rgb(82 82 82 / var(--tw-text-opacity, 1)); +} +.placeholder\:text-neutral-600::placeholder { + --tw-text-opacity: 1; + color: rgb(82 82 82 / var(--tw-text-opacity, 1)); +} +.hover\:border-neutral-700:hover { + --tw-border-opacity: 1; + border-color: rgb(64 64 64 / var(--tw-border-opacity, 1)); +} +.hover\:bg-neutral-200:hover { + --tw-bg-opacity: 1; + background-color: rgb(229 229 229 / var(--tw-bg-opacity, 1)); +} +.hover\:bg-neutral-600:hover { + --tw-bg-opacity: 1; + background-color: rgb(82 82 82 / var(--tw-bg-opacity, 1)); +} +.hover\:bg-neutral-700:hover { + --tw-bg-opacity: 1; + background-color: rgb(64 64 64 / var(--tw-bg-opacity, 1)); +} +.hover\:bg-neutral-800:hover { + --tw-bg-opacity: 1; + background-color: rgb(38 38 38 / var(--tw-bg-opacity, 1)); +} +.hover\:bg-neutral-800\/60:hover { + background-color: rgba(38, 38, 38, 0.6); +} +.hover\:bg-neutral-900:hover { + --tw-bg-opacity: 1; + background-color: rgb(23 23 23 / var(--tw-bg-opacity, 1)); +} +.hover\:bg-red-700:hover { + --tw-bg-opacity: 1; + background-color: rgb(185 28 28 / var(--tw-bg-opacity, 1)); +} +.hover\:text-neutral-200:hover { + --tw-text-opacity: 1; + color: rgb(229 229 229 / var(--tw-text-opacity, 1)); +} +.hover\:text-neutral-300:hover { + --tw-text-opacity: 1; + color: rgb(212 212 212 / var(--tw-text-opacity, 1)); +} +.focus\:border-neutral-600:focus { + --tw-border-opacity: 1; + border-color: rgb(82 82 82 / var(--tw-border-opacity, 1)); +} +.focus\:outline-none:focus { + outline: 2px solid transparent; + outline-offset: 2px; +} +.focus\:ring-1:focus { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) + var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) + var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} +.focus\:ring-neutral-600:focus { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(82 82 82/var(--tw-ring-opacity, 1)); +} +.disabled\:cursor-default:disabled { + cursor: default; +} +.disabled\:cursor-not-allowed:disabled { + cursor: not-allowed; +} +.disabled\:opacity-40:disabled { + opacity: 0.4; +} +.disabled\:opacity-50:disabled { + opacity: 0.5; +} +.disabled\:hover\:bg-neutral-800:hover:disabled { + --tw-bg-opacity: 1; + background-color: rgb(38 38 38 / var(--tw-bg-opacity, 1)); +} diff --git a/src/shared/domain/project.ts b/src/shared/domain/project.ts index 0fdc21f..c897351 100644 --- a/src/shared/domain/project.ts +++ b/src/shared/domain/project.ts @@ -138,18 +138,12 @@ export function sanitizeProjectName(name: unknown): string { return cleaned || fallback; } -export function toProjectAbsolutePath( - projectFolder: string, - value: unknown, -): string | null { +export function toProjectAbsolutePath(projectFolder: string, value: unknown): string | null { if (typeof value !== 'string' || !value.trim()) return null; return path.isAbsolute(value) ? value : path.join(projectFolder, value); } -export function toProjectRelativePath( - projectFolder: string, - value: unknown, -): string | null { +export function toProjectRelativePath(projectFolder: string, value: unknown): string | null { if (typeof value !== 'string' || !value.trim()) return null; if (!path.isAbsolute(value)) return value; @@ -181,43 +175,26 @@ export function normalizeSections(rawSections: unknown = []): Section[] { ? section.transcript : typeof section.text === 'string' ? section.text - : '', + : '' ) .replace(/\s+/g, ' ') .trim(); return { - id: - typeof section.id === 'string' && section.id - ? section.id - : `section-${index + 1}`, - index: Number.isFinite(Number(section.index)) - ? Number(section.index) - : index, - label: - typeof section.label === 'string' - ? section.label - : `Section ${index + 1}`, + id: typeof section.id === 'string' && section.id ? section.id : `section-${index + 1}`, + index: Number.isFinite(Number(section.index)) ? Number(section.index) : index, + label: typeof section.label === 'string' ? section.label : `Section ${index + 1}`, start: Number.isFinite(start) ? start : 0, end: Number.isFinite(end) ? end : 0, duration: Number.isFinite(Number(section.duration)) ? Number(section.duration) - : Math.max( - 0, - (Number.isFinite(end) ? end : 0) - - (Number.isFinite(start) ? start : 0), - ), + : Math.max(0, (Number.isFinite(end) ? end : 0) - (Number.isFinite(start) ? start : 0)), sourceStart: Number.isFinite(sourceStart) ? sourceStart : 0, sourceEnd: Number.isFinite(sourceEnd) ? sourceEnd : 0, - takeId: - typeof section.takeId === 'string' && section.takeId - ? section.takeId - : null, + takeId: typeof section.takeId === 'string' && section.takeId ? section.takeId : null, transcript, imagePath: - typeof section.imagePath === 'string' && section.imagePath - ? section.imagePath - : null, + typeof section.imagePath === 'string' && section.imagePath ? section.imagePath : null }; }) .filter((section) => section.end - section.start > 0.0001) @@ -246,20 +223,15 @@ export function normalizeKeyframes(rawKeyframes: unknown = []): Keyframe[] { return { time: Number.isFinite(Number(keyframe.time)) ? Number(keyframe.time) : 0, - pipX: Number.isFinite(Number(keyframe.pipX)) - ? Number(keyframe.pipX) - : 0, - pipY: Number.isFinite(Number(keyframe.pipY)) - ? Number(keyframe.pipY) - : 0, + pipX: Number.isFinite(Number(keyframe.pipX)) ? Number(keyframe.pipX) : 0, + pipY: Number.isFinite(Number(keyframe.pipY)) ? Number(keyframe.pipY) : 0, pipVisible: keyframe.pipVisible !== false, cameraFullscreen: Boolean(keyframe.cameraFullscreen), backgroundZoom: normalizeBackgroundZoom(keyframe.backgroundZoom), backgroundPanX: normalizeBackgroundPan(keyframe.backgroundPanX), backgroundPanY: normalizeBackgroundPan(keyframe.backgroundPanY), - sectionId: - typeof keyframe.sectionId === 'string' ? keyframe.sectionId : null, - autoSection: Boolean(keyframe.autoSection), + sectionId: typeof keyframe.sectionId === 'string' ? keyframe.sectionId : null, + autoSection: Boolean(keyframe.autoSection) }; }) .sort((left, right) => left.time - right.time); @@ -280,10 +252,7 @@ export function normalizeExportVideoPreset(value: unknown): ExportVideoPreset { export function normalizeCameraSyncOffsetMs(value: unknown): number { const offset = Math.round(Number(value)); if (!Number.isFinite(offset)) return 0; - return Math.max( - MIN_CAMERA_SYNC_OFFSET_MS, - Math.min(MAX_CAMERA_SYNC_OFFSET_MS, offset), - ); + return Math.max(MIN_CAMERA_SYNC_OFFSET_MS, Math.min(MAX_CAMERA_SYNC_OFFSET_MS, offset)); } export function createDefaultProject(name: unknown = 'Untitled Project'): ProjectData { @@ -298,7 +267,7 @@ export function createDefaultProject(name: unknown = 'Untitled Project'): Projec hideFromRecording: true, exportAudioPreset: EXPORT_AUDIO_PRESET_COMPRESSED, exportVideoPreset: EXPORT_VIDEO_PRESET_QUALITY, - cameraSyncOffsetMs: 0, + cameraSyncOffsetMs: 0 }, takes: [], timeline: { @@ -308,15 +277,12 @@ export function createDefaultProject(name: unknown = 'Untitled Project'): Projec selectedSectionId: null, hasCamera: false, sourceWidth: null, - sourceHeight: null, - }, + sourceHeight: null + } }; } -export function normalizeProjectData( - rawProject: unknown, - projectFolder?: string, -): ProjectData { +export function normalizeProjectData(rawProject: unknown, projectFolder?: string): ProjectData { const project = isRecord(rawProject) ? (rawProject as PartialProjectInput) : ({} as PartialProjectInput); @@ -336,38 +302,22 @@ export function normalizeProjectData( typeof project.name === 'string' && project.name.trim() ? sanitizeProjectName(project.name) : base.name, - createdAt: - typeof project.createdAt === 'string' ? project.createdAt : now, - updatedAt: - typeof project.updatedAt === 'string' ? project.updatedAt : now, + createdAt: typeof project.createdAt === 'string' ? project.createdAt : now, + updatedAt: typeof project.updatedAt === 'string' ? project.updatedAt : now, settings: { screenFitMode: rawSettings.screenFitMode === 'fit' ? 'fit' : 'fill', hideFromRecording: rawSettings.hideFromRecording !== false, - exportAudioPreset: normalizeExportAudioPreset( - rawSettings.exportAudioPreset, - ), - exportVideoPreset: normalizeExportVideoPreset( - rawSettings.exportVideoPreset, - ), - cameraSyncOffsetMs: normalizeCameraSyncOffsetMs( - rawSettings.cameraSyncOffsetMs, - ), + exportAudioPreset: normalizeExportAudioPreset(rawSettings.exportAudioPreset), + exportVideoPreset: normalizeExportVideoPreset(rawSettings.exportVideoPreset), + cameraSyncOffsetMs: normalizeCameraSyncOffsetMs(rawSettings.cameraSyncOffsetMs) }, takes: rawTakes.map((rawTake, index) => { - const take = isRecord(rawTake) - ? (rawTake as PartialTakeInput) - : ({} as PartialTakeInput); + const take = isRecord(rawTake) ? (rawTake as PartialTakeInput) : ({} as PartialTakeInput); return { - id: - typeof take.id === 'string' && take.id - ? take.id - : `take-${index + 1}-${Date.now()}`, - createdAt: - typeof take.createdAt === 'string' ? take.createdAt : now, - duration: Number.isFinite(Number(take.duration)) - ? Number(take.duration) - : 0, + id: typeof take.id === 'string' && take.id ? take.id : `take-${index + 1}-${Date.now()}`, + createdAt: typeof take.createdAt === 'string' ? take.createdAt : now, + duration: Number.isFinite(Number(take.duration)) ? Number(take.duration) : 0, screenPath: projectFolder ? toProjectAbsolutePath(projectFolder, take.screenPath) : typeof take.screenPath === 'string' @@ -383,31 +333,27 @@ export function normalizeProjectData( : typeof take.proxyPath === 'string' ? take.proxyPath : null, - sections: normalizeSections(take.sections), + sections: normalizeSections(take.sections) }; }), timeline: { - duration: Number.isFinite(Number(rawTimeline.duration)) - ? Number(rawTimeline.duration) - : 0, + duration: Number.isFinite(Number(rawTimeline.duration)) ? Number(rawTimeline.duration) : 0, sections: normalizeSections(rawTimeline.sections).map((section) => ({ ...section, imagePath: projectFolder ? toProjectAbsolutePath(projectFolder, section.imagePath) - : section.imagePath, + : section.imagePath })), keyframes: normalizeKeyframes(rawTimeline.keyframes), selectedSectionId: - typeof rawTimeline.selectedSectionId === 'string' - ? rawTimeline.selectedSectionId - : null, + typeof rawTimeline.selectedSectionId === 'string' ? rawTimeline.selectedSectionId : null, hasCamera: Boolean(rawTimeline.hasCamera), sourceWidth: Number.isFinite(Number(rawTimeline.sourceWidth)) ? Number(rawTimeline.sourceWidth) : null, sourceHeight: Number.isFinite(Number(rawTimeline.sourceHeight)) ? Number(rawTimeline.sourceHeight) - : null, - }, + : null + } }; } diff --git a/src/shared/electron-api.ts b/src/shared/electron-api.ts index 584f7fc..2432b55 100644 --- a/src/shared/electron-api.ts +++ b/src/shared/electron-api.ts @@ -53,10 +53,17 @@ export interface ElectronApi { pickProjectLocation: (opts?: { name?: string }) => Promise; pathToFileUrl: (filePath: string) => string; openFolder: (folder: string) => Promise; - projectCreate: (opts?: { name?: string; parentFolder?: string; projectPath?: string }) => Promise; + projectCreate: (opts?: { + name?: string; + parentFolder?: string; + projectPath?: string; + }) => Promise; projectOpen: (projectFolder: string) => Promise; projectSave: (payload: { projectPath: string; project: ProjectData }) => Promise; - projectSetRecoveryTake: (payload: { projectPath: string; take: RecoveryTake }) => Promise<{ projectPath: string; recoveryTake: RecoveryTake }>; + projectSetRecoveryTake: (payload: { + projectPath: string; + take: RecoveryTake; + }) => Promise<{ projectPath: string; recoveryTake: RecoveryTake }>; projectClearRecoveryTake: (projectFolder: string) => Promise; projectCompleteRecoveryTake: (projectFolder: string) => Promise; projectListRecent: (limit?: number) => Promise; diff --git a/tests/integration/project-service.test.ts b/tests/integration/project-service.test.ts index e81bac0..4e49106 100644 --- a/tests/integration/project-service.test.ts +++ b/tests/integration/project-service.test.ts @@ -107,9 +107,7 @@ describe('main/services/project-service integration', () => { expect(saved.project.takes[0].screenPath).toBe(screenPath); - const raw = JSON.parse( - fs.readFileSync(path.join(created.projectPath, 'project.json'), 'utf8') - ); + const raw = JSON.parse(fs.readFileSync(path.join(created.projectPath, 'project.json'), 'utf8')); expect(raw.takes[0].screenPath).toBe('screen.webm'); expect(raw.takes[0].cameraPath).toBe('camera.webm'); expect(raw.timeline.keyframes[0].backgroundZoom).toBe(2.2); @@ -149,16 +147,14 @@ describe('main/services/project-service integration', () => { screenPath, cameraPath: null, proxyPath, - sections: [], - }, - ], - }, + sections: [] + } + ] + } }); // Verify on-disk format uses relative path - const raw = JSON.parse( - fs.readFileSync(path.join(created.projectPath, 'project.json'), 'utf8'), - ); + const raw = JSON.parse(fs.readFileSync(path.join(created.projectPath, 'project.json'), 'utf8')); expect(raw.takes[0].proxyPath).toBe('screen-proxy.mp4'); // Verify open resolves back to absolute @@ -183,10 +179,10 @@ describe('main/services/project-service integration', () => { screenPath, cameraPath: null, proxyPath: null, - sections: [], - }, - ], - }, + sections: [] + } + ] + } }); const opened = service.openProject(created.projectPath); @@ -233,7 +229,7 @@ describe('main/services/project-service integration', () => { expect(Buffer.compare(written, data)).toBe(0); // No leftover temp files in the project folder - const files = fs.readdirSync(created.projectPath).filter(f => f.startsWith('.tmp-')); + const files = fs.readdirSync(created.projectPath).filter((f) => f.startsWith('.tmp-')); expect(files).toHaveLength(0); }); diff --git a/tests/unit/create-window.test.ts b/tests/unit/create-window.test.ts index 175d7b0..0315fdf 100644 --- a/tests/unit/create-window.test.ts +++ b/tests/unit/create-window.test.ts @@ -2,10 +2,7 @@ import path from 'node:path'; import { describe, expect, test, vi } from 'vitest'; -import { - createWindow, - type BrowserWindowConstructor -} from '../../src/main/app/create-window'; +import { createWindow, type BrowserWindowConstructor } from '../../src/main/app/create-window'; describe('main/app/create-window', () => { test('uses the provided app root for preload and html paths', () => { diff --git a/tests/unit/ffmpeg-runner.test.ts b/tests/unit/ffmpeg-runner.test.ts index 5c4cddd..3d61faf 100644 --- a/tests/unit/ffmpeg-runner.test.ts +++ b/tests/unit/ffmpeg-runner.test.ts @@ -51,7 +51,10 @@ describe('main/services/ffmpeg-runner', () => { onProgress: (update: FfmpegProgress) => updates.push(update) }); - child.stdout.emit('data', Buffer.from('frame=1\nout_time=00:00:01.000000\nprogress=continue\n')); + child.stdout.emit( + 'data', + Buffer.from('frame=1\nout_time=00:00:01.000000\nprogress=continue\n') + ); child.stdout.emit('data', Buffer.from('frame=2\nout_time=00:00:02.000000\nprogress=end\n')); child.stderr.emit('data', Buffer.from('encoding...\n')); child.emit('close', 0); diff --git a/tests/unit/fps-service.test.ts b/tests/unit/fps-service.test.ts index 864f762..488dbca 100644 --- a/tests/unit/fps-service.test.ts +++ b/tests/unit/fps-service.test.ts @@ -1,6 +1,10 @@ import { describe, expect, test } from 'vitest'; -import { chooseRenderFps, parseFpsToken, parseVideoFpsFromProbeOutput } from '../../src/main/services/fps-service'; +import { + chooseRenderFps, + parseFpsToken, + parseVideoFpsFromProbeOutput +} from '../../src/main/services/fps-service'; describe('main/services/fps-service', () => { test('parseFpsToken parses numeric and ratio tokens', () => { diff --git a/tests/unit/keyframe-ops.test.ts b/tests/unit/keyframe-ops.test.ts index 1c075eb..c7af831 100644 --- a/tests/unit/keyframe-ops.test.ts +++ b/tests/unit/keyframe-ops.test.ts @@ -41,7 +41,7 @@ describe('keyframe-ops', () => { return Array.from({ length: count }, (_, i) => ({ id: `s-${i + 1}`, index: i, - label: `Section ${i + 1}`, + label: `Section ${i + 1}` })); } @@ -49,7 +49,7 @@ describe('keyframe-ops', () => { const sections = makeSections(4); const result = moveSectionToIndex(sections, 0, 2); expect(result).toBe(true); - expect(sections.map(s => s.id)).toEqual(['s-2', 's-3', 's-1', 's-4']); + expect(sections.map((s) => s.id)).toEqual(['s-2', 's-3', 's-1', 's-4']); expect(sections[0].index).toBe(0); expect(sections[0].label).toBe('Section 1'); expect(sections[2].index).toBe(2); @@ -60,13 +60,13 @@ describe('keyframe-ops', () => { const sections = makeSections(4); const result = moveSectionToIndex(sections, 3, 1); expect(result).toBe(true); - expect(sections.map(s => s.id)).toEqual(['s-1', 's-4', 's-2', 's-3']); + expect(sections.map((s) => s.id)).toEqual(['s-1', 's-4', 's-2', 's-3']); }); test('returns false for same position', () => { const sections = makeSections(3); expect(moveSectionToIndex(sections, 1, 1)).toBe(false); - expect(sections.map(s => s.id)).toEqual(['s-1', 's-2', 's-3']); + expect(sections.map((s) => s.id)).toEqual(['s-1', 's-2', 's-3']); }); test('returns false for out of bounds indices', () => { @@ -79,29 +79,29 @@ describe('keyframe-ops', () => { test('handles move to first position', () => { const sections = makeSections(3); moveSectionToIndex(sections, 2, 0); - expect(sections.map(s => s.id)).toEqual(['s-3', 's-1', 's-2']); + expect(sections.map((s) => s.id)).toEqual(['s-3', 's-1', 's-2']); expect(sections[0].label).toBe('Section 1'); }); test('handles move to last position', () => { const sections = makeSections(3); moveSectionToIndex(sections, 0, 2); - expect(sections.map(s => s.id)).toEqual(['s-2', 's-3', 's-1']); + expect(sections.map((s) => s.id)).toEqual(['s-2', 's-3', 's-1']); expect(sections[2].label).toBe('Section 3'); }); test('preserves section IDs', () => { const sections = makeSections(4); - const originalIds = sections.map(s => s.id); + const originalIds = sections.map((s) => s.id); moveSectionToIndex(sections, 1, 3); - const newIds = sections.map(s => s.id); + const newIds = sections.map((s) => s.id); expect(newIds.sort()).toEqual(originalIds.sort()); }); test('works with two sections', () => { const sections = makeSections(2); moveSectionToIndex(sections, 0, 1); - expect(sections.map(s => s.id)).toEqual(['s-2', 's-1']); + expect(sections.map((s) => s.id)).toEqual(['s-2', 's-1']); }); }); @@ -110,7 +110,7 @@ describe('keyframe-ops', () => { return Array.from({ length: count }, (_, i) => ({ id: `s-${i + 1}`, index: i, - label: `Section ${i + 1}`, + label: `Section ${i + 1}` })); } @@ -118,21 +118,21 @@ describe('keyframe-ops', () => { const sections = makeSections(5); const result = moveSectionsToIndex(sections, new Set(['s-3', 's-4']), 0); expect(result).toBe(true); - expect(sections.map(s => s.id)).toEqual(['s-3', 's-4', 's-1', 's-2', 's-5']); + expect(sections.map((s) => s.id)).toEqual(['s-3', 's-4', 's-1', 's-2', 's-5']); }); test('moves a group of sections to the end', () => { const sections = makeSections(5); const result = moveSectionsToIndex(sections, new Set(['s-2', 's-3']), 3); expect(result).toBe(true); - expect(sections.map(s => s.id)).toEqual(['s-1', 's-4', 's-5', 's-2', 's-3']); + expect(sections.map((s) => s.id)).toEqual(['s-1', 's-4', 's-5', 's-2', 's-3']); }); test('moves non-contiguous sections preserving their relative order', () => { const sections = makeSections(5); const result = moveSectionsToIndex(sections, new Set(['s-1', 's-4']), 2); expect(result).toBe(true); - expect(sections.map(s => s.id)).toEqual(['s-2', 's-3', 's-1', 's-4', 's-5']); + expect(sections.map((s) => s.id)).toEqual(['s-2', 's-3', 's-1', 's-4', 's-5']); }); test('returns false when order does not change', () => { @@ -140,7 +140,7 @@ describe('keyframe-ops', () => { // s-2, s-3 are already at insert position 1 among remaining [s-1, s-4] const result = moveSectionsToIndex(sections, new Set(['s-2', 's-3']), 1); expect(result).toBe(false); - expect(sections.map(s => s.id)).toEqual(['s-1', 's-2', 's-3', 's-4']); + expect(sections.map((s) => s.id)).toEqual(['s-1', 's-2', 's-3', 's-4']); }); test('returns false for empty selectedIds', () => { @@ -156,7 +156,7 @@ describe('keyframe-ops', () => { test('clamps insertBefore to valid range', () => { const sections = makeSections(4); moveSectionsToIndex(sections, new Set(['s-1']), 99); - expect(sections.map(s => s.id)).toEqual(['s-2', 's-3', 's-4', 's-1']); + expect(sections.map((s) => s.id)).toEqual(['s-2', 's-3', 's-4', 's-1']); }); test('reindexes sections after move', () => { @@ -224,9 +224,33 @@ describe('keyframe-ops', () => { test('splitting adds one keyframe without modifying existing ones', () => { // 3 sections with custom camera states const keyframes = [ - { time: 0, pipX: 10, pipY: 20, pipVisible: true, cameraFullscreen: false, sectionId: 's-1', autoSection: true }, - { time: 5, pipX: 30, pipY: 40, pipVisible: false, cameraFullscreen: false, sectionId: 's-2', autoSection: true }, - { time: 10, pipX: 50, pipY: 60, pipVisible: true, cameraFullscreen: true, sectionId: 's-3', autoSection: true } + { + time: 0, + pipX: 10, + pipY: 20, + pipVisible: true, + cameraFullscreen: false, + sectionId: 's-1', + autoSection: true + }, + { + time: 5, + pipX: 30, + pipY: 40, + pipVisible: false, + cameraFullscreen: false, + sectionId: 's-2', + autoSection: true + }, + { + time: 10, + pipX: 50, + pipY: 60, + pipVisible: true, + cameraFullscreen: true, + sectionId: 's-3', + autoSection: true + } ] as Keyframe[]; const splitDefaults = { pipX: 100, pipY: 200 }; @@ -263,9 +287,33 @@ describe('keyframe-ops', () => { describe('delete preserves remaining section keyframes', () => { test('removing a section anchor does not affect other anchors', () => { const keyframes = [ - { time: 0, pipX: 10, pipY: 20, pipVisible: true, cameraFullscreen: false, sectionId: 's-1', autoSection: true }, - { time: 5, pipX: 30, pipY: 40, pipVisible: false, cameraFullscreen: false, sectionId: 's-2', autoSection: true }, - { time: 10, pipX: 50, pipY: 60, pipVisible: true, cameraFullscreen: true, sectionId: 's-3', autoSection: true } + { + time: 0, + pipX: 10, + pipY: 20, + pipVisible: true, + cameraFullscreen: false, + sectionId: 's-1', + autoSection: true + }, + { + time: 5, + pipX: 30, + pipY: 40, + pipVisible: false, + cameraFullscreen: false, + sectionId: 's-2', + autoSection: true + }, + { + time: 10, + pipX: 50, + pipY: 60, + pipVisible: true, + cameraFullscreen: true, + sectionId: 's-3', + autoSection: true + } ]; // Delete s-2: filter out its anchor, keep others diff --git a/tests/unit/media-cleanup.test.ts b/tests/unit/media-cleanup.test.ts index c64e5b8..2da4e0a 100644 --- a/tests/unit/media-cleanup.test.ts +++ b/tests/unit/media-cleanup.test.ts @@ -3,7 +3,7 @@ import { beforeEach, describe, expect, test, vi } from 'vitest'; import { cleanupAllMedia, stopStream, - type MediaRefs, + type MediaRefs } from '../../src/renderer/features/media-cleanup'; function makeFakeStream(trackCount = 1) { @@ -55,7 +55,7 @@ function makeFullRefs(): FakeMediaRefs { drawRAF: 1, meterRAF: 2, cancelEditorDrawLoop: vi.fn(), - stopAudioMeter: vi.fn(), + stopAudioMeter: vi.fn() } as FakeMediaRefs; } @@ -80,7 +80,13 @@ describe('stopStream', () => { test('does not throw when track.stop() throws', () => { const stream = { - getTracks: () => [{ stop: () => { throw new Error('already stopped'); } }], + getTracks: () => [ + { + stop: () => { + throw new Error('already stopped'); + } + } + ] }; expect(() => stopStream(stream as unknown as MediaStream)).not.toThrow(); }); @@ -111,7 +117,7 @@ describe('cleanupAllMedia', () => { drawRAF: null, meterRAF: null, cancelEditorDrawLoop: null, - stopAudioMeter: null, + stopAudioMeter: null } as FakeMediaRefs; expect(() => cleanupAllMedia(refs)).not.toThrow(); }); diff --git a/tests/unit/preload.test.ts b/tests/unit/preload.test.ts index c0b8099..86d08fd 100644 --- a/tests/unit/preload.test.ts +++ b/tests/unit/preload.test.ts @@ -32,7 +32,10 @@ describe('preload', () => { const listener = vi.fn(); const unsubscribe = electronAPI.onRenderProgress(listener); - expect(mockIpcRenderer.on).toHaveBeenCalledWith('render-composite-progress', expect.any(Function)); + expect(mockIpcRenderer.on).toHaveBeenCalledWith( + 'render-composite-progress', + expect.any(Function) + ); const handler = mockIpcRenderer.on.mock.calls[0][1] as ( _event: unknown, @@ -44,6 +47,9 @@ describe('preload', () => { unsubscribe(); - expect(mockIpcRenderer.removeListener).toHaveBeenCalledWith('render-composite-progress', handler); + expect(mockIpcRenderer.removeListener).toHaveBeenCalledWith( + 'render-composite-progress', + handler + ); }); }); diff --git a/tests/unit/project-domain.test.ts b/tests/unit/project-domain.test.ts index 9275b2a..722ce2c 100644 --- a/tests/unit/project-domain.test.ts +++ b/tests/unit/project-domain.test.ts @@ -80,7 +80,16 @@ describe('shared/domain/project', () => { name: ' demo<>name ', timeline: { sections: [{ start: 0, end: 2 }], - keyframes: [{ time: 0, pipX: 10, pipY: 20, backgroundZoom: 1.8, backgroundPanX: 0.25, backgroundPanY: -0.5 }] + keyframes: [ + { + time: 0, + pipX: 10, + pipY: 20, + backgroundZoom: 1.8, + backgroundPanX: 0.25, + backgroundPanY: -0.5 + } + ] } }, '/tmp/my-project' @@ -185,7 +194,7 @@ describe('shared/domain/project', () => { const sections = normalizeSections([ { start: 0, end: 2, imagePath: '/tmp/photo.png' }, { start: 3, end: 5 }, - { start: 6, end: 8, imagePath: '' }, + { start: 6, end: 8, imagePath: '' } ]); expect(sections).toHaveLength(3); @@ -198,11 +207,18 @@ describe('shared/domain/project', () => { const project = normalizeProjectData( { takes: [ - { id: 'take-1', screenPath: 'screen.webm', cameraPath: null, proxyPath: 'screen-proxy.mp4', duration: 10, sections: [] }, - { id: 'take-2', screenPath: 'screen2.webm', cameraPath: null, duration: 5, sections: [] }, - ], + { + id: 'take-1', + screenPath: 'screen.webm', + cameraPath: null, + proxyPath: 'screen-proxy.mp4', + duration: 10, + sections: [] + }, + { id: 'take-2', screenPath: 'screen2.webm', cameraPath: null, duration: 5, sections: [] } + ] }, - '/tmp/my-project', + '/tmp/my-project' ); expect(project.takes[0].proxyPath).toBe('/tmp/my-project/screen-proxy.mp4'); @@ -215,11 +231,11 @@ describe('shared/domain/project', () => { timeline: { sections: [ { start: 0, end: 2, imagePath: 'image-123-photo.png' }, - { start: 3, end: 5 }, - ], - }, + { start: 3, end: 5 } + ] + } }, - '/tmp/my-project', + '/tmp/my-project' ); expect(project.timeline.sections[0].imagePath).toBe('/tmp/my-project/image-123-photo.png'); diff --git a/tests/unit/proxy-service.test.ts b/tests/unit/proxy-service.test.ts index a7dc748..25e9e84 100644 --- a/tests/unit/proxy-service.test.ts +++ b/tests/unit/proxy-service.test.ts @@ -4,7 +4,7 @@ import { deriveProxyPath, generateProxy, _getQueueState, - _resetQueue, + _resetQueue } from '../../src/main/services/proxy-service'; describe('main/services/proxy-service', () => { @@ -13,13 +13,13 @@ describe('main/services/proxy-service', () => { }); test('deriveProxyPath replaces extension with -proxy.mp4', () => { - expect(deriveProxyPath('/project/recording-123-screen.webm')) - .toBe('/project/recording-123-screen-proxy.mp4'); + expect(deriveProxyPath('/project/recording-123-screen.webm')).toBe( + '/project/recording-123-screen-proxy.mp4' + ); }); test('deriveProxyPath handles paths without extension', () => { - expect(deriveProxyPath('/project/recording')) - .toBe('/project/recording-proxy.mp4'); + expect(deriveProxyPath('/project/recording')).toBe('/project/recording-proxy.mp4'); }); test('generateProxy calls runFfmpeg with correct args and renames on success', async () => { @@ -27,12 +27,12 @@ describe('main/services/proxy-service', () => { const fsStub = { existsSync: vi.fn().mockReturnValue(false), unlinkSync: vi.fn(), - renameSync: vi.fn(), + renameSync: vi.fn() }; await generateProxy( { screenPath: '/project/screen.webm', proxyPath: '/project/screen-proxy.mp4' }, - { runFfmpeg, fs: fsStub, ffmpegPath: '/usr/bin/ffmpeg' }, + { runFfmpeg, fs: fsStub, ffmpegPath: '/usr/bin/ffmpeg' } ); expect(runFfmpeg).toHaveBeenCalledOnce(); @@ -46,7 +46,7 @@ describe('main/services/proxy-service', () => { expect(fsStub.renameSync).toHaveBeenCalledWith( '/project/screen-proxy.mp4.tmp', - '/project/screen-proxy.mp4', + '/project/screen-proxy.mp4' ); }); @@ -56,12 +56,12 @@ describe('main/services/proxy-service', () => { const fsStub = { existsSync: vi.fn().mockReturnValue(false), unlinkSync: vi.fn(), - renameSync: vi.fn(), + renameSync: vi.fn() }; await generateProxy( { screenPath: '/project/screen.webm', proxyPath: '/project/screen-proxy.mp4', onProgress }, - { runFfmpeg, fs: fsStub, ffmpegPath: '/usr/bin/ffmpeg' }, + { runFfmpeg, fs: fsStub, ffmpegPath: '/usr/bin/ffmpeg' } ); expect(runFfmpeg.mock.calls[0][0].onProgress).toBe(onProgress); @@ -72,14 +72,14 @@ describe('main/services/proxy-service', () => { const fsStub = { existsSync: vi.fn().mockReturnValue(true), unlinkSync: vi.fn(), - renameSync: vi.fn(), + renameSync: vi.fn() }; await expect( generateProxy( { screenPath: '/project/screen.webm', proxyPath: '/project/screen-proxy.mp4' }, - { runFfmpeg, fs: fsStub, ffmpegPath: '/usr/bin/ffmpeg' }, - ), + { runFfmpeg, fs: fsStub, ffmpegPath: '/usr/bin/ffmpeg' } + ) ).rejects.toThrow('ffmpeg failed'); expect(fsStub.unlinkSync).toHaveBeenCalledWith('/project/screen-proxy.mp4.tmp'); @@ -91,12 +91,12 @@ describe('main/services/proxy-service', () => { const fsStub = { existsSync: vi.fn().mockReturnValue(true), unlinkSync: vi.fn(), - renameSync: vi.fn(), + renameSync: vi.fn() }; await generateProxy( { screenPath: '/project/screen.webm', proxyPath: '/project/screen-proxy.mp4' }, - { runFfmpeg, fs: fsStub, ffmpegPath: '/usr/bin/ffmpeg' }, + { runFfmpeg, fs: fsStub, ffmpegPath: '/usr/bin/ffmpeg' } ); // First call to unlinkSync is the stale tmp cleanup @@ -107,11 +107,18 @@ describe('main/services/proxy-service', () => { let resolveA: () => void; let resolveB: () => void; let resolveC: () => void; - const promiseA = new Promise<{ stderr: string }>((r) => { resolveA = () => r({ stderr: '' }); }); - const promiseB = new Promise<{ stderr: string }>((r) => { resolveB = () => r({ stderr: '' }); }); - const promiseC = new Promise<{ stderr: string }>((r) => { resolveC = () => r({ stderr: '' }); }); + const promiseA = new Promise<{ stderr: string }>((r) => { + resolveA = () => r({ stderr: '' }); + }); + const promiseB = new Promise<{ stderr: string }>((r) => { + resolveB = () => r({ stderr: '' }); + }); + const promiseC = new Promise<{ stderr: string }>((r) => { + resolveC = () => r({ stderr: '' }); + }); - const runFfmpeg = vi.fn() + const runFfmpeg = vi + .fn() .mockReturnValueOnce(promiseA) .mockReturnValueOnce(promiseB) .mockReturnValueOnce(promiseC); @@ -119,7 +126,7 @@ describe('main/services/proxy-service', () => { const fsStub = { existsSync: vi.fn().mockReturnValue(false), unlinkSync: vi.fn(), - renameSync: vi.fn(), + renameSync: vi.fn() }; const deps = { runFfmpeg, fs: fsStub, ffmpegPath: '/usr/bin/ffmpeg' }; diff --git a/tests/unit/register-handlers.test.ts b/tests/unit/register-handlers.test.ts index 46e4259..e7b5e13 100644 --- a/tests/unit/register-handlers.test.ts +++ b/tests/unit/register-handlers.test.ts @@ -23,7 +23,7 @@ function createProxyServiceStub() { deriveProxyPath: vi.fn((screenPath: string) => screenPath.replace(/\.webm$/, '-proxy.mp4')), generateProxy: vi.fn().mockResolvedValue(undefined), _getQueueState: vi.fn(), - _resetQueue: vi.fn(), + _resetQueue: vi.fn() }; } @@ -34,10 +34,12 @@ function registerWithHandlers() { handlers.set(channel, handler); } }; - const renderComposite = vi.fn(async (_opts: unknown, deps: { onProgress: (u: unknown) => void }) => { - deps.onProgress({ phase: 'rendering', percent: 0.5, status: 'Rendering 50%' }); - return '/tmp/output.mp4'; - }); + const renderComposite = vi.fn( + async (_opts: unknown, deps: { onProgress: (u: unknown) => void }) => { + deps.onProgress({ phase: 'rendering', percent: 0.5, status: 'Rendering 50%' }); + return '/tmp/output.mp4'; + } + ); const proxyService = createProxyServiceStub(); registerIpcHandlers({ @@ -51,7 +53,7 @@ function registerWithHandlers() { renderComposite, computeSections: vi.fn(), getScribeToken: vi.fn(), - proxyService, + proxyService } as unknown as Parameters[0]); return { handlers, renderComposite, proxyService }; @@ -84,18 +86,27 @@ describe('main/ipc/register-handlers', () => { const result = handlers.get('proxy:generate')!( { sender }, - { takeId: 'take-1', screenPath: '/project/screen.webm', projectFolder: '/project', durationSec: 10 }, + { + takeId: 'take-1', + screenPath: '/project/screen.webm', + projectFolder: '/project', + durationSec: 10 + } ); expect(result).toBe('/project/screen-proxy.mp4'); - expect(sender.send).toHaveBeenCalledWith('proxy:progress', { takeId: 'take-1', status: 'started', percent: 0 }); + expect(sender.send).toHaveBeenCalledWith('proxy:progress', { + takeId: 'take-1', + status: 'started', + percent: 0 + }); // Wait for the background generation to complete await vi.waitFor(() => { expect(sender.send).toHaveBeenCalledWith('proxy:progress', { takeId: 'take-1', status: 'done', - proxyPath: '/project/screen-proxy.mp4', + proxyPath: '/project/screen-proxy.mp4' }); }); }); @@ -107,14 +118,14 @@ describe('main/ipc/register-handlers', () => { handlers.get('proxy:generate')!( { sender }, - { takeId: 'take-1', screenPath: '/project/screen.webm', projectFolder: '/project' }, + { takeId: 'take-1', screenPath: '/project/screen.webm', projectFolder: '/project' } ); await vi.waitFor(() => { expect(sender.send).toHaveBeenCalledWith('proxy:progress', { takeId: 'take-1', status: 'error', - error: 'encode failed', + error: 'encode failed' }); }); }); @@ -126,7 +137,7 @@ describe('main/ipc/register-handlers', () => { handlers.get('proxy:generate')!( { sender }, - { takeId: 'take-1', screenPath: '/project/screen.webm', projectFolder: '/project' }, + { takeId: 'take-1', screenPath: '/project/screen.webm', projectFolder: '/project' } ); // Wait for the promise to settle @@ -134,7 +145,7 @@ describe('main/ipc/register-handlers', () => { // Only the initial 'started' is sent before isDestroyed check in the .then() const doneCalls = sender.send.mock.calls.filter( - (c: unknown[]) => c[0] === 'proxy:progress' && (c[1] as { status: string }).status === 'done', + (c: unknown[]) => c[0] === 'proxy:progress' && (c[1] as { status: string }).status === 'done' ); expect(doneCalls).toHaveLength(0); }); @@ -145,7 +156,7 @@ describe('main/ipc/register-handlers', () => { const result = handlers.get('proxy:generate')!( { sender }, - { takeId: 'take-1', screenPath: '', projectFolder: '/project' }, + { takeId: 'take-1', screenPath: '', projectFolder: '/project' } ); expect(result).toBeNull(); diff --git a/tests/unit/render-filter-service.test.ts b/tests/unit/render-filter-service.test.ts index 721d1ae..46bd01d 100644 --- a/tests/unit/render-filter-service.test.ts +++ b/tests/unit/render-filter-service.test.ts @@ -27,12 +27,10 @@ describe('main/services/render-filter-service', () => { }); test('buildAlphaExpr transitions before the keyframe time', () => { - const expr = buildAlphaExpr( - [ - { time: 0, pipVisible: true }, - { time: 2, pipVisible: false } - ] as Keyframe[] - ); + const expr = buildAlphaExpr([ + { time: 0, pipVisible: true }, + { time: 2, pipVisible: false } + ] as Keyframe[]); // At T=2.0 fully invisible, transition starts at T=1.7 expect(expr).toContain('if(gte(T,2.000),0'); expect(expr).toContain('if(gte(T,1.700)'); @@ -40,12 +38,10 @@ describe('main/services/render-filter-service', () => { }); test('buildCamFullAlphaExpr transitions before the keyframe time', () => { - const expr = buildCamFullAlphaExpr( - [ - { time: 0, pipVisible: true, cameraFullscreen: false }, - { time: 1, pipVisible: true, cameraFullscreen: true } - ] as Keyframe[] - ); + const expr = buildCamFullAlphaExpr([ + { time: 0, pipVisible: true, cameraFullscreen: false }, + { time: 1, pipVisible: true, cameraFullscreen: true } + ] as Keyframe[]); // At T=1.0 fully visible fullscreen, transition starts at T=0.7 expect(expr).toContain('if(gte(T,1.000),1'); expect(expr).toContain('if(gte(T,0.700)'); @@ -101,8 +97,26 @@ describe('main/services/render-filter-service', () => { test('buildFilterComplex returns overlay pipeline string', () => { const filter = buildFilterComplex( [ - { time: 0, pipX: 100, pipY: 100, pipVisible: true, cameraFullscreen: false, backgroundZoom: 1, backgroundPanX: 0, backgroundPanY: 0 }, - { time: 2, pipX: 120, pipY: 120, pipVisible: true, cameraFullscreen: false, backgroundZoom: 2, backgroundPanX: 1, backgroundPanY: -1 } + { + time: 0, + pipX: 100, + pipY: 100, + pipVisible: true, + cameraFullscreen: false, + backgroundZoom: 1, + backgroundPanX: 0, + backgroundPanY: 0 + }, + { + time: 2, + pipX: 120, + pipY: 120, + pipVisible: true, + cameraFullscreen: false, + backgroundZoom: 2, + backgroundPanX: 1, + backgroundPanY: -1 + } ] as Keyframe[], 320, 'fill', @@ -131,7 +145,9 @@ describe('main/services/render-filter-service', () => { 1920, 1080 ); - expect(filter).toContain('scale=1920:1080:flags=lanczos:force_original_aspect_ratio=increase,crop=1920:1080[screen]'); + expect(filter).toContain( + 'scale=1920:1080:flags=lanczos:force_original_aspect_ratio=increase,crop=1920:1080[screen]' + ); }); test('buildFilterComplex keeps PiP coordinates in editor canvas space', () => { @@ -158,7 +174,9 @@ describe('main/services/render-filter-service', () => { expect(filter).toContain('scale=422:422'); expect(filter).toContain("overlay=x='1478':y='638'"); - expect(filter).toContain('scale=1920:1080:flags=lanczos:force_original_aspect_ratio=increase,crop=1920:1080'); + expect(filter).toContain( + 'scale=1920:1080:flags=lanczos:force_original_aspect_ratio=increase,crop=1920:1080' + ); }); test('buildFilterComplex scales PiP size and position for 1440p export canvases', () => { @@ -185,7 +203,9 @@ describe('main/services/render-filter-service', () => { expect(filter).toContain('scale=562:562'); expect(filter).toContain("overlay=x='1971':y='851'"); - expect(filter).toContain('scale=2560:1440:flags=lanczos:force_original_aspect_ratio=increase,crop=2560:1440'); + expect(filter).toContain( + 'scale=2560:1440:flags=lanczos:force_original_aspect_ratio=increase,crop=2560:1440' + ); }); test('buildAlphaExpr collapses redundant visibility anchors for long timelines', () => { @@ -209,15 +229,7 @@ describe('main/services/render-filter-service', () => { backgroundPanY: 0 })) as Keyframe[]; - const filter = buildFilterComplex( - keyframes, - 320, - 'fill', - 1920, - 1080, - 1920, - 1080 - ); + const filter = buildFilterComplex(keyframes, 320, 'fill', 1920, 1080, 1920, 1080); expect(filter).toContain("overlay=x='120':y='180'"); expect(filter).not.toContain('if(gte(T,'); diff --git a/tests/unit/render-service.test.ts b/tests/unit/render-service.test.ts index 4c456d6..30d5b5a 100644 --- a/tests/unit/render-service.test.ts +++ b/tests/unit/render-service.test.ts @@ -31,10 +31,24 @@ function createRunFfmpegStub( describe('main/services/render-service', () => { test('normalizeSectionInput filters invalid sections', () => { const sections = normalizeSectionInput([ - { takeId: 'a', sourceStart: 0, sourceEnd: 1, backgroundZoom: 1.75, backgroundPanX: 0.5, backgroundPanY: -0.3 }, + { + takeId: 'a', + sourceStart: 0, + sourceEnd: 1, + backgroundZoom: 1.75, + backgroundPanX: 0.5, + backgroundPanY: -0.3 + }, { takeId: 'b', sourceStart: 2, sourceEnd: 1 }, { takeId: 'c', sourceStart: 'x', sourceEnd: 3 }, - { takeId: 'd', sourceStart: 0, sourceEnd: 2, backgroundZoom: 10, backgroundPanX: -9, backgroundPanY: 8 } + { + takeId: 'd', + sourceStart: 0, + sourceEnd: 2, + backgroundZoom: 10, + backgroundPanX: -9, + backgroundPanY: 8 + } ]); expect(sections).toHaveLength(2); @@ -51,7 +65,7 @@ describe('main/services/render-service', () => { const sections = normalizeSectionInput([ { takeId: 'a', sourceStart: 0, sourceEnd: 1, imagePath: '/tmp/photo.png' }, { takeId: 'b', sourceStart: 2, sourceEnd: 4 }, - { takeId: 'c', sourceStart: 5, sourceEnd: 7, imagePath: '' }, + { takeId: 'c', sourceStart: 5, sourceEnd: 7, imagePath: '' } ]); expect(sections).toHaveLength(3); @@ -110,7 +124,9 @@ describe('main/services/render-service', () => { outputFolder: outputDir, takes: [{ id: 'take-1', screenPath, cameraPath: null }], sections: [{ takeId: 'take-1', sourceStart: 0, sourceEnd: 1.25 }], - keyframes: [{ time: 0, pipX: 10, pipY: 10, pipVisible: false, cameraFullscreen: false }] as Keyframe[], + keyframes: [ + { time: 0, pipX: 10, pipY: 10, pipVisible: false, cameraFullscreen: false } + ] as Keyframe[], pipSize: 300, sourceWidth: 1920, sourceHeight: 1080, @@ -169,7 +185,9 @@ describe('main/services/render-service', () => { outputFolder: outputDir, takes: [{ id: 'take-1', screenPath, cameraPath }], sections: [{ takeId: 'take-1', sourceStart: 0, sourceEnd: 1.0 }], - keyframes: [{ time: 0, pipX: 1478, pipY: 638, pipVisible: true, cameraFullscreen: false }] as Keyframe[], + keyframes: [ + { time: 0, pipX: 1478, pipY: 638, pipVisible: true, cameraFullscreen: false } + ] as Keyframe[], pipSize: 422, sourceWidth: 3840, sourceHeight: 2160, @@ -204,7 +222,9 @@ describe('main/services/render-service', () => { ); const argString = execCalls[0].args.join(' '); - expect(argString).toContain('scale=1280:720:flags=lanczos:force_original_aspect_ratio=increase,crop=1280:720[screen]'); + expect(argString).toContain( + 'scale=1280:720:flags=lanczos:force_original_aspect_ratio=increase,crop=1280:720[screen]' + ); expect(argString).toContain('scale=280:280'); expect(argString).toContain("overlay=x='985':y='425'"); }); @@ -221,7 +241,9 @@ describe('main/services/render-service', () => { outputFolder: outputDir, takes: [{ id: 'take-1', screenPath, cameraPath: null }], sections: [{ takeId: 'take-1', sourceStart: 0, sourceEnd: 1.25 }], - keyframes: [{ time: 0, pipX: 10, pipY: 10, pipVisible: false, cameraFullscreen: false }] as Keyframe[], + keyframes: [ + { time: 0, pipX: 10, pipY: 10, pipVisible: false, cameraFullscreen: false } + ] as Keyframe[], pipSize: 300, sourceWidth: 1920, sourceHeight: 1080, @@ -257,7 +279,9 @@ describe('main/services/render-service', () => { outputFolder: outputDir, takes: [{ id: 'take-1', screenPath, cameraPath: null }], sections: [{ takeId: 'take-1', sourceStart: 0, sourceEnd: 1.25 }], - keyframes: [{ time: 0, pipX: 10, pipY: 10, pipVisible: false, cameraFullscreen: false }] as Keyframe[], + keyframes: [ + { time: 0, pipX: 10, pipY: 10, pipVisible: false, cameraFullscreen: false } + ] as Keyframe[], pipSize: 300, sourceWidth: 1920, sourceHeight: 1080, @@ -294,7 +318,18 @@ describe('main/services/render-service', () => { outputFolder: outputDir, takes: [{ id: 'take-1', screenPath, cameraPath }], sections: [{ takeId: 'take-1', sourceStart: 0, sourceEnd: 1.0, backgroundZoom: 2 }], - keyframes: [{ time: 0, pipX: 10, pipY: 10, pipVisible: true, cameraFullscreen: false, backgroundZoom: 2, backgroundPanX: 0, backgroundPanY: 0 }] as Keyframe[], + keyframes: [ + { + time: 0, + pipX: 10, + pipY: 10, + pipVisible: true, + cameraFullscreen: false, + backgroundZoom: 2, + backgroundPanX: 0, + backgroundPanY: 0 + } + ] as Keyframe[], pipSize: 300, sourceWidth: 1920, sourceHeight: 1080, @@ -311,7 +346,9 @@ describe('main/services/render-service', () => { ); const argString = execCalls[0].args.join(' '); - expect(argString).toContain("[screen_raw]scale=1920:1080:flags=lanczos:force_original_aspect_ratio=increase,crop=1920:1080[screen_base];[screen_base]zoompan=z='2.000'"); + expect(argString).toContain( + "[screen_raw]scale=1920:1080:flags=lanczos:force_original_aspect_ratio=increase,crop=1920:1080[screen_base];[screen_base]zoompan=z='2.000'" + ); expect(argString).toContain('[1:v]trim=start=0.000:end=1.000,setpts=PTS-STARTPTS[cv0]'); expect(argString).not.toContain('scale=3840:2160,crop=1920:1080:960:540[cv0]'); }); @@ -330,7 +367,9 @@ describe('main/services/render-service', () => { outputFolder: outputDir, takes: [{ id: 'take-1', screenPath, cameraPath }], sections: [{ takeId: 'take-1', sourceStart: 0, sourceEnd: 1.0 }], - keyframes: [{ time: 0, pipX: 1478, pipY: 638, pipVisible: true, cameraFullscreen: false }] as Keyframe[], + keyframes: [ + { time: 0, pipX: 1478, pipY: 638, pipVisible: true, cameraFullscreen: false } + ] as Keyframe[], pipSize: 422, sourceWidth: 3840, sourceHeight: 2160, @@ -347,7 +386,9 @@ describe('main/services/render-service', () => { ); const argString = execCalls[0].args.join(' '); - expect(argString).toContain('scale=2560:1440:flags=lanczos:force_original_aspect_ratio=increase,crop=2560:1440[screen]'); + expect(argString).toContain( + 'scale=2560:1440:flags=lanczos:force_original_aspect_ratio=increase,crop=2560:1440[screen]' + ); expect(argString).toContain('scale=562:562'); expect(argString).toContain("overlay=x='1971':y='851'"); }); @@ -364,7 +405,9 @@ describe('main/services/render-service', () => { outputFolder: outputDir, takes: [{ id: 'take-1', screenPath, cameraPath: null }], sections: [{ takeId: 'take-1', sourceStart: 0, sourceEnd: 1.0 }], - keyframes: [{ time: 0, pipX: 10, pipY: 10, pipVisible: false, cameraFullscreen: false }] as Keyframe[], + keyframes: [ + { time: 0, pipX: 10, pipY: 10, pipVisible: false, cameraFullscreen: false } + ] as Keyframe[], pipSize: 300, sourceWidth: 1510, sourceHeight: 982, @@ -381,7 +424,9 @@ describe('main/services/render-service', () => { ); const argString = execCalls[0].args.join(' '); - expect(argString).toContain('scale=1920:1080:flags=lanczos:force_original_aspect_ratio=increase,crop=1920:1080[out]'); + expect(argString).toContain( + 'scale=1920:1080:flags=lanczos:force_original_aspect_ratio=increase,crop=1920:1080[out]' + ); expect(argString).not.toContain('scale=1510:982'); }); @@ -399,7 +444,9 @@ describe('main/services/render-service', () => { outputFolder: outputDir, takes: [{ id: 'take-1', screenPath, cameraPath }], sections: [{ takeId: 'take-1', sourceStart: 0, sourceEnd: 1.0 }], - keyframes: [{ time: 0, pipX: 10, pipY: 10, pipVisible: true, cameraFullscreen: false }] as Keyframe[], + keyframes: [ + { time: 0, pipX: 10, pipY: 10, pipVisible: true, cameraFullscreen: false } + ] as Keyframe[], pipSize: 300, sourceWidth: 1920, sourceHeight: 1080, @@ -417,7 +464,9 @@ describe('main/services/render-service', () => { ); const argString = execCalls[0].args.join(' '); - expect(argString).toContain('[1:v]trim=start=0.120:end=1.120,setpts=PTS-STARTPTS,tpad=start_mode=clone:start_duration=0.000:stop_mode=clone:stop_duration=0.120,trim=duration=1.000,setpts=PTS-STARTPTS[cv0]'); + expect(argString).toContain( + '[1:v]trim=start=0.120:end=1.120,setpts=PTS-STARTPTS,tpad=start_mode=clone:start_duration=0.000:stop_mode=clone:stop_duration=0.120,trim=duration=1.000,setpts=PTS-STARTPTS[cv0]' + ); }); test('renderComposite applies clamped section pan to background crop', async () => { @@ -441,7 +490,18 @@ describe('main/services/render-service', () => { backgroundPanY: -1 } ], - keyframes: [{ time: 0, pipX: 10, pipY: 10, pipVisible: false, cameraFullscreen: false, backgroundZoom: 2, backgroundPanX: 1, backgroundPanY: -1 }] as Keyframe[], + keyframes: [ + { + time: 0, + pipX: 10, + pipY: 10, + pipVisible: false, + cameraFullscreen: false, + backgroundZoom: 2, + backgroundPanX: 1, + backgroundPanY: -1 + } + ] as Keyframe[], pipSize: 300, sourceWidth: 1920, sourceHeight: 1080, @@ -458,7 +518,9 @@ describe('main/services/render-service', () => { ); const argString = execCalls[0].args.join(' '); - expect(argString).toContain("zoompan=z='2.000':x='max(0,min(iw-iw/zoom,iw*(0.750000)-iw/zoom/2))':y='max(0,min(ih-ih/zoom,ih*(0.250000)-ih/zoom/2))'"); + expect(argString).toContain( + "zoompan=z='2.000':x='max(0,min(iw-iw/zoom,iw*(0.750000)-iw/zoom/2))':y='max(0,min(ih-ih/zoom,ih*(0.250000)-ih/zoom/2))'" + ); }); test('renderComposite animates background zoom and pan through section boundaries', async () => { @@ -473,12 +535,44 @@ describe('main/services/render-service', () => { outputFolder: outputDir, takes: [{ id: 'take-1', screenPath, cameraPath: null }], sections: [ - { takeId: 'take-1', sourceStart: 0, sourceEnd: 1.0, backgroundZoom: 1, backgroundPanX: 0, backgroundPanY: 0 }, - { takeId: 'take-1', sourceStart: 1.0, sourceEnd: 2.0, backgroundZoom: 2, backgroundPanX: 1, backgroundPanY: -1 } + { + takeId: 'take-1', + sourceStart: 0, + sourceEnd: 1.0, + backgroundZoom: 1, + backgroundPanX: 0, + backgroundPanY: 0 + }, + { + takeId: 'take-1', + sourceStart: 1.0, + sourceEnd: 2.0, + backgroundZoom: 2, + backgroundPanX: 1, + backgroundPanY: -1 + } ], keyframes: [ - { time: 0, pipX: 10, pipY: 10, pipVisible: false, cameraFullscreen: false, backgroundZoom: 1, backgroundPanX: 0, backgroundPanY: 0 }, - { time: 1, pipX: 10, pipY: 10, pipVisible: false, cameraFullscreen: false, backgroundZoom: 2, backgroundPanX: 1, backgroundPanY: -1 } + { + time: 0, + pipX: 10, + pipY: 10, + pipVisible: false, + cameraFullscreen: false, + backgroundZoom: 1, + backgroundPanX: 0, + backgroundPanY: 0 + }, + { + time: 1, + pipX: 10, + pipY: 10, + pipVisible: false, + cameraFullscreen: false, + backgroundZoom: 2, + backgroundPanX: 1, + backgroundPanY: -1 + } ] as Keyframe[], pipSize: 300, sourceWidth: 1920, @@ -518,7 +612,9 @@ describe('main/services/render-service', () => { { takeId: 'take-1', sourceStart: 0, sourceEnd: 1.0 }, { takeId: 'take-1', sourceStart: 1.0, sourceEnd: 2.0 } ], - keyframes: [{ time: 0, pipX: 10, pipY: 10, pipVisible: true, cameraFullscreen: false }] as Keyframe[], + keyframes: [ + { time: 0, pipX: 10, pipY: 10, pipVisible: true, cameraFullscreen: false } + ] as Keyframe[], pipSize: 300, sourceWidth: 1920, sourceHeight: 1080, @@ -540,8 +636,12 @@ describe('main/services/render-service', () => { expect(args.filter((value) => value === cameraPath)).toHaveLength(1); const argString = args.join(' '); - expect(argString).toContain('[0:v]trim=start=0.000:end=1.000,setpts=PTS-STARTPTS,setsar=1[sv0]'); - expect(argString).toContain('[0:v]trim=start=1.000:end=2.000,setpts=PTS-STARTPTS,setsar=1[sv1]'); + expect(argString).toContain( + '[0:v]trim=start=0.000:end=1.000,setpts=PTS-STARTPTS,setsar=1[sv0]' + ); + expect(argString).toContain( + '[0:v]trim=start=1.000:end=2.000,setpts=PTS-STARTPTS,setsar=1[sv1]' + ); expect(argString).toContain('[1:v]trim=start=0.000:end=1.000,setpts=PTS-STARTPTS[cv0]'); expect(argString).toContain('[1:v]trim=start=1.000:end=2.000,setpts=PTS-STARTPTS[cv1]'); }); @@ -571,7 +671,9 @@ describe('main/services/render-service', () => { { takeId: 'take-b', sourceStart: 1.0, sourceEnd: 2.0 }, { takeId: 'take-a', sourceStart: 2.0, sourceEnd: 3.0 } ], - keyframes: [{ time: 0, pipX: 10, pipY: 10, pipVisible: true, cameraFullscreen: false }] as Keyframe[], + keyframes: [ + { time: 0, pipX: 10, pipY: 10, pipVisible: true, cameraFullscreen: false } + ] as Keyframe[], pipSize: 300, sourceWidth: 1920, sourceHeight: 1080, @@ -589,9 +691,15 @@ describe('main/services/render-service', () => { const argString = execCalls[0].args.join(' '); expect(execCalls[0].args.filter((value) => value === '-i')).toHaveLength(4); - expect(argString).toContain('[0:v]trim=start=0.000:end=1.000,setpts=PTS-STARTPTS,setsar=1[sv0]'); - expect(argString).toContain('[2:v]trim=start=1.000:end=2.000,setpts=PTS-STARTPTS,setsar=1[sv1]'); - expect(argString).toContain('[0:v]trim=start=2.000:end=3.000,setpts=PTS-STARTPTS,setsar=1[sv2]'); + expect(argString).toContain( + '[0:v]trim=start=0.000:end=1.000,setpts=PTS-STARTPTS,setsar=1[sv0]' + ); + expect(argString).toContain( + '[2:v]trim=start=1.000:end=2.000,setpts=PTS-STARTPTS,setsar=1[sv1]' + ); + expect(argString).toContain( + '[0:v]trim=start=2.000:end=3.000,setpts=PTS-STARTPTS,setsar=1[sv2]' + ); expect(argString).toContain('[1:v]trim=start=0.000:end=1.000,setpts=PTS-STARTPTS[cv0]'); expect(argString).toContain('[3:v]trim=start=1.000:end=2.000,setpts=PTS-STARTPTS[cv1]'); expect(argString).toContain('[1:v]trim=start=2.000:end=3.000,setpts=PTS-STARTPTS[cv2]'); @@ -613,7 +721,9 @@ describe('main/services/render-service', () => { { takeId: 'take-1', sourceStart: 90.017, sourceEnd: 143.531 }, { takeId: 'take-1', sourceStart: 200.201, sourceEnd: 271.889 } ], - keyframes: [{ time: 0, pipX: 10, pipY: 10, pipVisible: false, cameraFullscreen: false }] as Keyframe[], + keyframes: [ + { time: 0, pipX: 10, pipY: 10, pipVisible: false, cameraFullscreen: false } + ] as Keyframe[], pipSize: 300, sourceWidth: 1920, sourceHeight: 1080, @@ -630,9 +740,15 @@ describe('main/services/render-service', () => { ); const argString = execCalls[0].args.join(' '); - expect(argString).toContain('[0:v]trim=start=0.000:end=61.113,setpts=PTS-STARTPTS,setsar=1[sv0]'); - expect(argString).toContain('[0:v]trim=start=90.017:end=143.531,setpts=PTS-STARTPTS,setsar=1[sv1]'); - expect(argString).toContain('[0:v]trim=start=200.201:end=271.889,setpts=PTS-STARTPTS,setsar=1[sv2]'); + expect(argString).toContain( + '[0:v]trim=start=0.000:end=61.113,setpts=PTS-STARTPTS,setsar=1[sv0]' + ); + expect(argString).toContain( + '[0:v]trim=start=90.017:end=143.531,setpts=PTS-STARTPTS,setsar=1[sv1]' + ); + expect(argString).toContain( + '[0:v]trim=start=200.201:end=271.889,setpts=PTS-STARTPTS,setsar=1[sv2]' + ); expect(argString).toContain('[out]fps=fps=30:round=near[out_cfr]'); expect(argString).not.toContain('setpts=PTS-STARTPTS,fps=fps=30,setsar=1[sv'); }); @@ -649,7 +765,9 @@ describe('main/services/render-service', () => { outputFolder: outputDir, takes: [{ id: 'take-1', screenPath, cameraPath: null }], sections: [{ takeId: 'take-1', sourceStart: 0, sourceEnd: 4.0 }], - keyframes: [{ time: 0, pipX: 10, pipY: 10, pipVisible: false, cameraFullscreen: false }] as Keyframe[], + keyframes: [ + { time: 0, pipX: 10, pipY: 10, pipVisible: false, cameraFullscreen: false } + ] as Keyframe[], pipSize: 300, sourceWidth: 1920, sourceHeight: 1080, @@ -661,7 +779,14 @@ describe('main/services/render-service', () => { probeVideoFpsWithFfmpeg: async () => 30, onProgress: (update) => updates.push(update), runFfmpeg: createRunFfmpegStub(({ onProgress }) => { - onProgress!({ status: 'continue', outTimeSec: 2, frame: 48, speed: 1.1, fps: null, raw: {} }); + onProgress!({ + status: 'continue', + outTimeSec: 2, + frame: 48, + speed: 1.1, + fps: null, + raw: {} + }); onProgress!({ status: 'end', outTimeSec: 4, frame: 96, speed: 0.9, fps: null, raw: {} }); }) } diff --git a/tests/unit/scribe-status.test.ts b/tests/unit/scribe-status.test.ts index 9210c09..7f87e74 100644 --- a/tests/unit/scribe-status.test.ts +++ b/tests/unit/scribe-status.test.ts @@ -23,17 +23,21 @@ describe('scribe-status', () => { }); test('combines explicit error details with the server error type', () => { - expect(getScribeFailureReason({ - message_type: 'session_time_limit_exceeded', - error: 'Maximum session time has been reached' - })).toBe('session_time_limit_exceeded: Maximum session time has been reached'); + expect( + getScribeFailureReason({ + message_type: 'session_time_limit_exceeded', + error: 'Maximum session time has been reached' + }) + ).toBe('session_time_limit_exceeded: Maximum session time has been reached'); }); test('uses generic error payloads as the failure reason', () => { - expect(getScribeStatusFromMessage({ - message_type: 'error', - error: 'quota_exceeded' - })).toEqual({ + expect( + getScribeStatusFromMessage({ + message_type: 'error', + error: 'quota_exceeded' + }) + ).toEqual({ text: 'Transcription error: quota_exceeded', tone: 'error', failureReason: 'quota_exceeded' @@ -41,9 +45,7 @@ describe('scribe-status', () => { }); test('prefers the last known server failure when the socket closes', () => { - expect( - getScribeStatusFromCloseEvent({ code: 1006 }, 'insufficient_audio_activity') - ).toEqual({ + expect(getScribeStatusFromCloseEvent({ code: 1006 }, 'insufficient_audio_activity')).toEqual({ text: 'Transcription disconnected: insufficient_audio_activity', tone: 'warning', failureReason: 'insufficient_audio_activity' diff --git a/tsconfig.main.tsbuildinfo b/tsconfig.main.tsbuildinfo new file mode 100644 index 0000000..b07ec05 --- /dev/null +++ b/tsconfig.main.tsbuildinfo @@ -0,0 +1 @@ +{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.dom.d.ts","./node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/typescript/lib/lib.dom.asynciterable.d.ts","./node_modules/typescript/lib/lib.webworker.importscripts.d.ts","./node_modules/typescript/lib/lib.scripthost.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/typescript/lib/lib.esnext.disposable.d.ts","./node_modules/typescript/lib/lib.esnext.float16.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/typescript/lib/lib.es2022.full.d.ts","./node_modules/dotenv/config.d.ts","./node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/@types/node/globals.typedarray.d.ts","./node_modules/@types/node/buffer.buffer.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/web-globals/abortcontroller.d.ts","./node_modules/@types/node/web-globals/blob.d.ts","./node_modules/@types/node/web-globals/console.d.ts","./node_modules/@types/node/web-globals/crypto.d.ts","./node_modules/@types/node/web-globals/domexception.d.ts","./node_modules/@types/node/web-globals/encoding.d.ts","./node_modules/@types/node/web-globals/events.d.ts","./node_modules/undici-types/utility.d.ts","./node_modules/undici-types/header.d.ts","./node_modules/undici-types/readable.d.ts","./node_modules/undici-types/fetch.d.ts","./node_modules/undici-types/formdata.d.ts","./node_modules/undici-types/connector.d.ts","./node_modules/undici-types/client-stats.d.ts","./node_modules/undici-types/client.d.ts","./node_modules/undici-types/errors.d.ts","./node_modules/undici-types/dispatcher.d.ts","./node_modules/undici-types/global-dispatcher.d.ts","./node_modules/undici-types/global-origin.d.ts","./node_modules/undici-types/pool-stats.d.ts","./node_modules/undici-types/pool.d.ts","./node_modules/undici-types/handlers.d.ts","./node_modules/undici-types/balanced-pool.d.ts","./node_modules/undici-types/round-robin-pool.d.ts","./node_modules/undici-types/h2c-client.d.ts","./node_modules/undici-types/agent.d.ts","./node_modules/undici-types/mock-interceptor.d.ts","./node_modules/undici-types/mock-call-history.d.ts","./node_modules/undici-types/mock-agent.d.ts","./node_modules/undici-types/mock-client.d.ts","./node_modules/undici-types/mock-pool.d.ts","./node_modules/undici-types/snapshot-agent.d.ts","./node_modules/undici-types/mock-errors.d.ts","./node_modules/undici-types/proxy-agent.d.ts","./node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/undici-types/retry-handler.d.ts","./node_modules/undici-types/retry-agent.d.ts","./node_modules/undici-types/api.d.ts","./node_modules/undici-types/cache-interceptor.d.ts","./node_modules/undici-types/interceptors.d.ts","./node_modules/undici-types/util.d.ts","./node_modules/undici-types/cookies.d.ts","./node_modules/undici-types/patch.d.ts","./node_modules/undici-types/websocket.d.ts","./node_modules/undici-types/eventsource.d.ts","./node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/undici-types/content-type.d.ts","./node_modules/undici-types/cache.d.ts","./node_modules/undici-types/index.d.ts","./node_modules/@types/node/web-globals/fetch.d.ts","./node_modules/@types/node/web-globals/importmeta.d.ts","./node_modules/@types/node/web-globals/messaging.d.ts","./node_modules/@types/node/web-globals/navigator.d.ts","./node_modules/@types/node/web-globals/performance.d.ts","./node_modules/@types/node/web-globals/storage.d.ts","./node_modules/@types/node/web-globals/streams.d.ts","./node_modules/@types/node/web-globals/timers.d.ts","./node_modules/@types/node/web-globals/url.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/assert/strict.d.ts","./node_modules/@types/node/async_hooks.d.ts","./node_modules/@types/node/buffer.d.ts","./node_modules/@types/node/child_process.d.ts","./node_modules/@types/node/cluster.d.ts","./node_modules/@types/node/console.d.ts","./node_modules/@types/node/constants.d.ts","./node_modules/@types/node/crypto.d.ts","./node_modules/@types/node/dgram.d.ts","./node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/@types/node/dns.d.ts","./node_modules/@types/node/dns/promises.d.ts","./node_modules/@types/node/domain.d.ts","./node_modules/@types/node/events.d.ts","./node_modules/@types/node/fs.d.ts","./node_modules/@types/node/fs/promises.d.ts","./node_modules/@types/node/http.d.ts","./node_modules/@types/node/http2.d.ts","./node_modules/@types/node/https.d.ts","./node_modules/@types/node/inspector.d.ts","./node_modules/@types/node/inspector.generated.d.ts","./node_modules/@types/node/inspector/promises.d.ts","./node_modules/@types/node/module.d.ts","./node_modules/@types/node/net.d.ts","./node_modules/@types/node/os.d.ts","./node_modules/@types/node/path.d.ts","./node_modules/@types/node/path/posix.d.ts","./node_modules/@types/node/path/win32.d.ts","./node_modules/@types/node/perf_hooks.d.ts","./node_modules/@types/node/process.d.ts","./node_modules/@types/node/punycode.d.ts","./node_modules/@types/node/querystring.d.ts","./node_modules/@types/node/quic.d.ts","./node_modules/@types/node/readline.d.ts","./node_modules/@types/node/readline/promises.d.ts","./node_modules/@types/node/repl.d.ts","./node_modules/@types/node/sea.d.ts","./node_modules/@types/node/sqlite.d.ts","./node_modules/@types/node/stream.d.ts","./node_modules/@types/node/stream/consumers.d.ts","./node_modules/@types/node/stream/promises.d.ts","./node_modules/@types/node/stream/web.d.ts","./node_modules/@types/node/string_decoder.d.ts","./node_modules/@types/node/test.d.ts","./node_modules/@types/node/test/reporters.d.ts","./node_modules/@types/node/timers.d.ts","./node_modules/@types/node/timers/promises.d.ts","./node_modules/@types/node/tls.d.ts","./node_modules/@types/node/trace_events.d.ts","./node_modules/@types/node/tty.d.ts","./node_modules/@types/node/url.d.ts","./node_modules/@types/node/util.d.ts","./node_modules/@types/node/util/types.d.ts","./node_modules/@types/node/v8.d.ts","./node_modules/@types/node/vm.d.ts","./node_modules/@types/node/wasi.d.ts","./node_modules/@types/node/worker_threads.d.ts","./node_modules/@types/node/zlib.d.ts","./node_modules/@types/node/index.d.ts","./node_modules/anymatch/index.d.ts","./node_modules/chokidar/types/index.d.ts","./node_modules/electron-reload/types/main.d.ts","./node_modules/electron/electron.d.ts","./src/main/app/create-window.ts","./dist/shared/types/domain.d.ts","./dist/shared/types/services.d.ts","./dist/shared/types/mouse-trail.d.ts","./src/main/ipc/register-handlers.ts","./src/main/infra/file-system.ts","./dist/shared/domain/project.d.ts","./src/main/services/project-service.ts","./src/main/services/fps-service.ts","./src/main/services/ffmpeg-runner.ts","./src/main/services/render-filter-service.ts","./dist/shared/domain/mouse-trail.d.ts","./node_modules/ffmpeg-static/types/index.d.ts","./src/main/services/render-service.ts","./src/main/services/sections-service.ts","./node_modules/@elevenlabs/elevenlabs-js/core/fetcher/rawresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/fetcher/apiresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/fetcher/binaryresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/fetcher/endpointmetadata.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/fetcher/supplier.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/fetcher/endpointsupplier.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/logging/logger.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/fetcher/fetcher.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/fetcher/getheader.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/fetcher/httpresponsepromise.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/fetcher/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/file/types.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/file/file.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/file/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/form-data-utils/encodeasformparameter.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/form-data-utils/formdatawrapper.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/form-data-utils/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/logging/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/runtime/runtime.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/runtime/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/schema.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/bigint/bigint.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/bigint/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/date/date.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/date/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/enum/enum.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/enum/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/lazy/lazy.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/utils/addquestionmarkstonullableproperties.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/object-like/types.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/object-like/getobjectlikeutils.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/object-like/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/schema-utils/getschemautils.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/schema-utils/jsonerror.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/schema-utils/parseerror.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/schema-utils/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/object/property.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/object/types.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/lazy/lazyobject.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/lazy/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/list/list.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/list/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/literals/booleanliteral.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/literals/stringliteral.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/literals/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/object/object.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/object/objectwithoutoptionalproperties.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/object/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/primitives/any.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/primitives/boolean.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/primitives/never.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/primitives/number.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/primitives/string.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/primitives/unknown.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/primitives/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/record/types.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/record/record.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/record/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/set/set.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/set/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/undiscriminated-union/types.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/undiscriminated-union/undiscriminatedunion.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/undiscriminated-union/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/union/discriminant.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/union/types.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/union/union.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/union/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/builders/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/schemas/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/stream/stream.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/stream/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/url/encodepathparam.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/url/join.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/url/qs.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/url/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/core/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/errors/elevenlabserror.d.ts","./node_modules/@elevenlabs/elevenlabs-js/errors/elevenlabstimeouterror.d.ts","./node_modules/@elevenlabs/elevenlabs-js/errors/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/errors/badrequesterror.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/errors/conflicterror.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/errors/forbiddenerror.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/errors/notfounderror.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/errors/tooearlyerror.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/errors/unauthorizederror.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/errors/unprocessableentityerror.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/errors/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/audioisolation/client/requests/bodyaudioisolationstreamv1audioisolationstreampost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/audioisolation/client/requests/bodyaudioisolationv1audioisolationpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/audioisolation/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/audioisolation/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/audioisolation/types/audioisolationconvertrequestfileformat.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/audioisolation/types/audioisolationstreamrequestfileformat.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/audioisolation/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/audioisolation/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/audionative/client/requests/bodycreatesaudionativeenabledprojectv1audionativepost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/audionative/client/requests/bodyupdateaudionativeprojectcontentv1audionativeprojectidcontentpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/audionative/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/audionative/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/audionative/types/audionativecreaterequestapplytextnormalization.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/audionative/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/audionative/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/client/requests/bodyaddtoknowledgebasev1convaiknowledgebasepost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/client/requests/agentsgetrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/client/requests/agentslistrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/client/requests/bodycreateagentv1convaiagentscreatepost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/client/requests/bodyduplicateagentv1convaiagentsagentidduplicatepost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/client/requests/bodysimulatesaconversationstreamv1convaiagentsagentidsimulateconversationstreampost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/client/requests/bodysimulatesaconversationv1convaiagentsagentidsimulateconversationpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/client/requests/runagenttestsrequestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/client/requests/updateagentrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/branches/client/requests/bodycreateanewbranchv1convaiagentsagentidbranchespost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/branches/client/requests/bodymergeabranchintoatargetbranchv1convaiagentsagentidbranchessourcebranchidmergepost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/branches/client/requests/bodyupdateagentbranchv1convaiagentsagentidbranchesbranchidpatch.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/branches/client/requests/brancheslistrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/branches/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/branches/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/branches/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/deployments/client/requests/bodycreateorupdatedeploymentsv1convaiagentsagentiddeploymentspost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/deployments/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/deployments/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/deployments/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/drafts/client/requests/bodycreateagentdraftv1convaiagentsagentiddraftspost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/drafts/client/requests/draftsdeleterequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/drafts/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/drafts/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/drafts/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/knowledgebase/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/knowledgebase/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/link/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/link/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/llmusage/client/requests/llmusagecalculatorrequestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/llmusage/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/llmusage/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/llmusage/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/summaries/client/requests/summariesgetrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/summaries/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/summaries/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/summaries/types/summariesgetresponsevalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/summaries/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/summaries/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/widget/client/requests/widgetgetrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/widget/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/widget/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/widget/resources/avatar/client/requests/bodypostagentavatarv1convaiagentsagentidavatarpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/widget/resources/avatar/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/widget/resources/avatar/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/widget/resources/avatar/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/widget/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/widget/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/types/bodypatchesanagentsettingsv1convaiagentsagentidpatchprocedurerefsitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/analytics/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/analytics/resources/livecount/client/requests/livecountgetrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/analytics/resources/livecount/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/analytics/resources/livecount/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/analytics/resources/livecount/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/analytics/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/analytics/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/batchcalls/client/requests/batchcallslistrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/batchcalls/client/requests/bodysubmitabatchcallrequestv1convaibatchcallingsubmitpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/batchcalls/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/batchcalls/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/batchcalls/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/conversations/client/requests/conversationsgetsignedurlrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/conversations/client/requests/conversationsgetwebrtctokenrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/conversations/client/requests/conversationslistrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/conversations/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/conversations/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/conversations/resources/audio/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/conversations/resources/audio/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/conversations/resources/feedback/client/requests/conversationfeedbackrequestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/conversations/resources/feedback/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/conversations/resources/feedback/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/conversations/resources/feedback/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/conversations/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/conversations/types/conversationslistrequestsummarymode.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/conversations/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/conversations/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/dashboard/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/dashboard/resources/settings/client/requests/patchconvaidashboardsettingsrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/dashboard/resources/settings/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/dashboard/resources/settings/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/dashboard/resources/settings/types/patchconvaidashboardsettingsrequestchartsitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/dashboard/resources/settings/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/dashboard/resources/settings/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/dashboard/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/dashboard/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/client/requests/bodycomputeragindexesinbatchv1convaiknowledgebaseragindexpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/client/requests/knowledgebaselistrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/document/client/requests/ragindexrequestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/document/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/document/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/document/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/documents/client/requests/bodybulkmoveentitiestofolderv1convaiknowledgebasebulkmovepost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/documents/client/requests/bodycreatefiledocumentv1convaiknowledgebasefilepost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/documents/client/requests/bodycreatefolderv1convaiknowledgebasefolderpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/documents/client/requests/bodycreatetextdocumentv1convaiknowledgebasetextpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/documents/client/requests/bodycreateurldocumentv1convaiknowledgebaseurlpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/documents/client/requests/bodymoveentitytofolderv1convaiknowledgebasedocumentidmovepost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/documents/client/requests/bodyupdatedocumentv1convaiknowledgebasedocumentationidpatch.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/documents/client/requests/documentsdeleterequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/documents/client/requests/documentsgetagentsrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/documents/client/requests/documentsgetrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/documents/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/documents/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/documents/resources/chunk/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/documents/resources/chunk/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/documents/resources/summaries/client/requests/summariesgetrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/documents/resources/summaries/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/documents/resources/summaries/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/documents/resources/summaries/types/summariesgetresponsevalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/documents/resources/summaries/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/documents/resources/summaries/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/documents/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/documents/types/documentsgetresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/documents/types/documentsupdateresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/documents/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/documents/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/types/knowledgebasegetorcreateragindexesresponsevalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/llmusage/client/requests/llmusagecalculatorpublicrequestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/llmusage/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/llmusage/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/llmusage/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/mcpservers/client/requests/mcpserverconfigupdaterequestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/mcpservers/client/requests/mcpserverrequestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/mcpservers/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/mcpservers/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/mcpservers/resources/approvalpolicy/client/requests/mcpapprovalpolicyupdaterequestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/mcpservers/resources/approvalpolicy/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/mcpservers/resources/approvalpolicy/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/mcpservers/resources/approvalpolicy/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/mcpservers/resources/toolapprovals/client/requests/mcptooladdapprovalrequestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/mcpservers/resources/toolapprovals/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/mcpservers/resources/toolapprovals/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/mcpservers/resources/toolapprovals/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/mcpservers/resources/toolconfigs/client/requests/mcptoolconfigoverridecreaterequestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/mcpservers/resources/toolconfigs/client/requests/mcptoolconfigoverrideupdaterequestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/mcpservers/resources/toolconfigs/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/mcpservers/resources/toolconfigs/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/mcpservers/resources/toolconfigs/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/mcpservers/resources/tools/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/mcpservers/resources/tools/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/mcpservers/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/mcpservers/types/mcpserverconfigupdaterequestmodelrequestheadersvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/mcpservers/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/mcpservers/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/phonenumbers/client/requests/updatephonenumberrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/phonenumbers/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/phonenumbers/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/phonenumbers/types/phonenumberscreaterequestbody.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/phonenumbers/types/phonenumbersgetresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/phonenumbers/types/phonenumberslistresponseitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/phonenumbers/types/phonenumbersupdateresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/phonenumbers/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/phonenumbers/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/secrets/client/requests/patchworkspacesecretrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/secrets/client/requests/postworkspacesecretrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/secrets/client/requests/secretslistrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/secrets/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/secrets/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/secrets/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/settings/client/requests/patchconvaisettingsrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/settings/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/settings/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/settings/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/siptrunk/client/requests/bodyhandleanoutboundcallviasiptrunkv1convaisiptrunkoutboundcallpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/siptrunk/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/siptrunk/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/siptrunk/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/tests/client/requests/listtestsbyidsrequestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/tests/client/requests/testslistrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/tests/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/tests/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/tests/resources/invocations/client/requests/invocationslistrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/tests/resources/invocations/client/requests/resubmittestsrequestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/tests/resources/invocations/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/tests/resources/invocations/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/tests/resources/invocations/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/tests/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/tests/types/testscreaterequestbody.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/tests/types/testsgetresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/tests/types/testsupdaterequestbody.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/tests/types/testsupdateresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/tests/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/tests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/tools/client/requests/toolsgetdependentagentsrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/tools/client/requests/toolslistrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/tools/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/tools/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/tools/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/twilio/client/requests/bodyhandleanoutboundcallviatwiliov1convaitwiliooutboundcallpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/twilio/client/requests/bodyregisteratwiliocallandreturntwimlv1convaitwilioregistercallpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/twilio/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/twilio/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/twilio/types/bodyregisteratwiliocallandreturntwimlv1convaitwilioregistercallpostdirection.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/twilio/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/twilio/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/users/client/requests/userslistrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/users/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/users/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/users/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/whatsapp/client/requests/bodymakeanoutboundcallviawhatsappv1convaiwhatsappoutboundcallpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/whatsapp/client/requests/bodysendanoutboundmessageviawhatsappv1convaiwhatsappoutboundmessagepost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/whatsapp/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/whatsapp/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/whatsapp/types/bodysendanoutboundmessageviawhatsappv1convaiwhatsappoutboundmessageposttemplateparamsitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/whatsapp/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/whatsapp/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/whatsappaccounts/client/requests/updatewhatsappaccountrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/whatsappaccounts/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/whatsappaccounts/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/whatsappaccounts/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/client/requests/bodydubavideooranaudiofilev1dubbingpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/client/requests/dubbinglistrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/audio/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/audio/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/client/requests/bodydubsallorsomesegmentsandlanguagesv1dubbingresourcedubbingiddubpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/client/requests/bodymovesegmentsbetweenspeakersv1dubbingresourcedubbingidmigratesegmentspost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/client/requests/bodyrenderaudioorvideoforthegivenlanguagev1dubbingresourcedubbingidrenderlanguagepost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/client/requests/bodytranscribessegmentsv1dubbingresourcedubbingidtranscribepost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/client/requests/bodytranslatesallorsomesegmentsandlanguagesv1dubbingresourcedubbingidtranslatepost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/resources/language/client/requests/bodyaddalanguagetotheresourcev1dubbingresourcedubbingidlanguagepost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/resources/language/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/resources/language/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/resources/language/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/resources/segment/client/requests/segmentupdatepayload.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/resources/segment/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/resources/segment/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/resources/segment/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/resources/speaker/client/requests/bodycreateanewspeakerv1dubbingresourcedubbingidspeakerpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/resources/speaker/client/requests/bodyupdatemetadataforaspeakerv1dubbingresourcedubbingidspeakerspeakeridpatch.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/resources/speaker/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/resources/speaker/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/resources/speaker/resources/segment/client/requests/segmentcreatepayload.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/resources/speaker/resources/segment/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/resources/speaker/resources/segment/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/resources/speaker/resources/segment/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/resources/speaker/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/resources/speaker/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/transcript/client/requests/transcriptgettranscriptfordubrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/transcript/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/transcript/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/transcript/types/transcriptgettranscriptfordubrequestformattype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/transcript/types/transcriptgettranscriptfordubresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/transcript/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/transcript/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/transcripts/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/transcripts/types/transcriptsgetrequestformattype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/transcripts/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/transcripts/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/types/dubbingcreaterequestmode.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/types/dubbinglistrequestdubbingstatus.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/types/dubbinglistrequestfilterbycreator.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/types/dubbinglistrequestorderdirection.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/forcedalignment/client/requests/bodycreateforcedalignmentv1forcedalignmentpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/forcedalignment/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/forcedalignment/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/forcedalignment/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/history/client/requests/downloadhistoryrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/history/client/requests/historylistrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/history/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/history/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/history/types/historylistrequestsortdirection.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/history/types/historylistrequestsource.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/history/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/history/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/models/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/models/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/music/client/requests/bodycomposemusicv1musicpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/music/client/requests/bodycomposemusicwithadetailedresponsev1musicdetailedpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/music/client/requests/bodystemseparationv1musicstemseparationpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/music/client/requests/bodystreamcomposedmusicv1musicstreampost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/music/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/music/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/music/resources/compositionplan/client/requests/bodygeneratecompositionplanv1musicplanpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/music/resources/compositionplan/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/music/resources/compositionplan/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/music/resources/compositionplan/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/music/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/music/types/musicseparatestemsrequeststemvariationid.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/music/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/music/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/pronunciationdictionaries/client/requests/bodyaddapronunciationdictionaryv1pronunciationdictionariesaddfromfilepost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/pronunciationdictionaries/client/requests/bodyaddapronunciationdictionaryv1pronunciationdictionariesaddfromrulespost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/pronunciationdictionaries/client/requests/bodyupdatepronunciationdictionaryv1pronunciationdictionariespronunciationdictionaryidpatch.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/pronunciationdictionaries/client/requests/pronunciationdictionarieslistrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/pronunciationdictionaries/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/pronunciationdictionaries/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/pronunciationdictionaries/resources/rules/client/requests/pronunciationdictionary.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/pronunciationdictionaries/resources/rules/client/requests/removepronunciationdictionaryrulesrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/pronunciationdictionaries/resources/rules/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/pronunciationdictionaries/resources/rules/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/pronunciationdictionaries/resources/rules/types/pronunciationdictionaryrule.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/pronunciationdictionaries/resources/rules/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/pronunciationdictionaries/resources/rules/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/pronunciationdictionaries/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/pronunciationdictionaries/types/bodyaddapronunciationdictionaryv1pronunciationdictionariesaddfromrulespostrulesitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/pronunciationdictionaries/types/bodyaddapronunciationdictionaryv1pronunciationdictionariesaddfromrulespostworkspaceaccess.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/pronunciationdictionaries/types/pronunciationdictionariescreatefromfilerequestworkspaceaccess.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/pronunciationdictionaries/types/pronunciationdictionarieslistrequestsort.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/pronunciationdictionaries/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/pronunciationdictionaries/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/samples/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/samples/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/serviceaccounts/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/serviceaccounts/resources/apikeys/client/requests/bodycreateserviceaccountapikeyv1serviceaccountsserviceaccountuseridapikeyspost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/serviceaccounts/resources/apikeys/client/requests/bodyeditserviceaccountapikeyv1serviceaccountsserviceaccountuseridapikeysapikeyidpatch.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/serviceaccounts/resources/apikeys/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/serviceaccounts/resources/apikeys/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/serviceaccounts/resources/apikeys/types/bodycreateserviceaccountapikeyv1serviceaccountsserviceaccountuseridapikeyspostpermissions.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/serviceaccounts/resources/apikeys/types/bodyeditserviceaccountapikeyv1serviceaccountsserviceaccountuseridapikeysapikeyidpatchpermissions.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/serviceaccounts/resources/apikeys/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/serviceaccounts/resources/apikeys/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/serviceaccounts/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/serviceaccounts/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtospeech/client/requests/bodyspeechtospeechstreamingv1speechtospeechvoiceidstreampost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtospeech/client/requests/bodyspeechtospeechv1speechtospeechvoiceidpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtospeech/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtospeech/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtospeech/types/speechtospeechconvertrequestfileformat.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtospeech/types/speechtospeechconvertrequestoutputformat.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtospeech/types/speechtospeechstreamrequestfileformat.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtospeech/types/speechtospeechstreamrequestoutputformat.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtospeech/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtospeech/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtotext/client/requests/bodyspeechtotextv1speechtotextpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtotext/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtotext/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtotext/resources/transcripts/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtotext/resources/transcripts/types/transcriptsgetresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtotext/resources/transcripts/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtotext/resources/transcripts/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtotext/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtotext/types/speechtotextconvertrequestentitydetection.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtotext/types/speechtotextconvertrequestfileformat.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtotext/types/speechtotextconvertrequestmodelid.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtotext/types/speechtotextconvertrequesttimestampsgranularity.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtotext/types/speechtotextconvertrequestwebhookmetadata.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtotext/types/speechtotextconvertresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtotext/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtotext/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/client/requests/bodycreatepodcastv1studiopodcastspost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/client/requests/bodycreatestudioprojectv1studioprojectspost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/client/requests/bodyupdatestudioprojectv1studioprojectsprojectidpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/client/requests/projectsgetrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/resources/chapters/client/requests/bodycreatechapterv1studioprojectsprojectidchapterspost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/resources/chapters/client/requests/bodyupdatechapterv1studioprojectsprojectidchapterschapteridpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/resources/chapters/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/resources/chapters/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/resources/chapters/resources/snapshots/client/requests/bodystreamchapteraudiov1studioprojectsprojectidchapterschapteridsnapshotschaptersnapshotidstreampost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/resources/chapters/resources/snapshots/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/resources/chapters/resources/snapshots/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/resources/chapters/resources/snapshots/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/resources/chapters/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/resources/chapters/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/resources/content/client/requests/bodyupdatestudioprojectcontentv1studioprojectsprojectidcontentpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/resources/content/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/resources/content/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/resources/content/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/resources/pronunciationdictionaries/client/requests/bodycreatepronunciationdictionariesv1studioprojectsprojectidpronunciationdictionariespost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/resources/pronunciationdictionaries/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/resources/pronunciationdictionaries/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/resources/pronunciationdictionaries/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/resources/snapshots/client/requests/bodystreamstudioprojectaudiov1studioprojectsprojectidsnapshotsprojectsnapshotidstreampost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/resources/snapshots/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/resources/snapshots/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/resources/snapshots/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/types/projectscreaterequestapplytextnormalization.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/types/projectscreaterequestfiction.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/types/projectscreaterequestqualitypreset.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/types/projectscreaterequestsourcetype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/types/projectscreaterequesttargetaudience.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/types/bodycreatepodcastv1studiopodcastspostapplytextnormalization.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/types/bodycreatepodcastv1studiopodcastspostdurationscale.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/types/bodycreatepodcastv1studiopodcastspostmode.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/types/bodycreatepodcastv1studiopodcastspostqualitypreset.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/types/bodycreatepodcastv1studiopodcastspostsource.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/types/bodycreatepodcastv1studiopodcastspostsourcetwoitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttodialogue/client/requests/bodytexttodialoguefullwithtimestamps.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttodialogue/client/requests/bodytexttodialoguemultivoicestreamingv1texttodialoguestreampost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttodialogue/client/requests/bodytexttodialoguemultivoicev1texttodialoguepost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttodialogue/client/requests/bodytexttodialoguestreamwithtimestamps.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttodialogue/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttodialogue/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttodialogue/types/bodytexttodialoguefullwithtimestampsapplytextnormalization.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttodialogue/types/bodytexttodialoguemultivoicestreamingv1texttodialoguestreampostapplytextnormalization.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttodialogue/types/bodytexttodialoguemultivoicev1texttodialoguepostapplytextnormalization.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttodialogue/types/bodytexttodialoguestreamwithtimestampsapplytextnormalization.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttodialogue/types/texttodialogueconvertrequestoutputformat.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttodialogue/types/texttodialogueconvertwithtimestampsrequestoutputformat.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttodialogue/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttodialogue/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttosoundeffects/client/requests/createsoundeffectrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttosoundeffects/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttosoundeffects/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttosoundeffects/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttospeech/client/requests/bodytexttospeechfull.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttospeech/client/requests/bodytexttospeechfullwithtimestamps.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttospeech/client/requests/streamtexttospeechrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttospeech/client/requests/streamtexttospeechwithtimestampsrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttospeech/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttospeech/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttospeech/types/bodytexttospeechfullapplytextnormalization.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttospeech/types/bodytexttospeechfullwithtimestampsapplytextnormalization.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttospeech/types/bodytexttospeechstreamapplytextnormalization.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttospeech/types/bodytexttospeechstreamwithtimestampsapplytextnormalization.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttospeech/types/texttospeechconvertrequestoutputformat.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttospeech/types/texttospeechconvertwithtimestampsrequestoutputformat.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttospeech/types/texttospeechstreamrequestoutputformat.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttospeech/types/texttospeechstreamwithtimestampsrequestoutputformat.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttospeech/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttospeech/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttovoice/client/requests/bodycreateanewvoicefromvoicepreviewv1texttovoicepost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttovoice/client/requests/voicedesignrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttovoice/client/requests/voicedesignrequestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttovoice/client/requests/voiceremixrequestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttovoice/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttovoice/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttovoice/resources/preview/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttovoice/resources/preview/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttovoice/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttovoice/types/voicedesignrequestmodelmodelid.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttovoice/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttovoice/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/tokens/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/tokens/resources/singleuse/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/tokens/resources/singleuse/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/tokens/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/tokens/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/usage/client/requests/usagegetrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/usage/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/usage/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/usage/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/user/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/user/resources/subscription/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/user/resources/subscription/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/user/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/user/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/v1speechtotextrealtime/types/receivetranscription.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/v1speechtotextrealtime/types/texttospeechcommitstrategy.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/v1speechtotextrealtime/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/v1speechtotextrealtime/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/v1texttospeechvoiceidmultistreaminput/types/receivemessagemulti.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/v1texttospeechvoiceidmultistreaminput/types/sendmessagemulti.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/v1texttospeechvoiceidmultistreaminput/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/v1texttospeechvoiceidmultistreaminput/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/v1texttospeechvoiceidstreaminput/types/receivemessage.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/v1texttospeechvoiceidstreaminput/types/sendmessage.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/v1texttospeechvoiceidstreaminput/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/v1texttospeechvoiceidstreaminput/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/client/requests/bodyaddsharedvoicev1voicesaddpublicuseridvoiceidpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/client/requests/bodyeditvoicev1voicesvoiceideditpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/client/requests/bodygetsimilarlibraryvoicesv1similarvoicespost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/client/requests/voicesgetallrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/client/requests/voicesgetrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/client/requests/voicesgetsharedrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/client/requests/voicessearchrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/ivc/client/requests/bodyaddvoicev1voicesaddpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/ivc/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/ivc/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/ivc/types/ivccreaterequestlabels.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/ivc/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/ivc/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/client/requests/bodyeditpvcvoicev1voicespvcvoiceidpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/client/requests/bodyrunpvctrainingv1voicespvcvoiceidtrainpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/client/requests/createpvcvoicerequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/samples/client/requests/bodyaddsamplestopvcvoicev1voicespvcvoiceidsamplespost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/samples/client/requests/bodyupdatepvcvoicesamplev1voicespvcvoiceidsamplessampleidpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/samples/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/samples/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/samples/resources/audio/client/requests/audiogetrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/samples/resources/audio/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/samples/resources/audio/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/samples/resources/audio/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/samples/resources/speakers/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/samples/resources/speakers/resources/audio/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/samples/resources/speakers/resources/audio/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/samples/resources/speakers/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/samples/resources/speakers/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/samples/resources/waveform/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/samples/resources/waveform/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/samples/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/samples/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/verification/client/requests/bodyrequestmanualverificationv1voicespvcvoiceidverificationpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/verification/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/verification/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/verification/resources/captcha/client/requests/bodyverifypvcvoicecaptchav1voicespvcvoiceidcaptchapost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/verification/resources/captcha/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/verification/resources/captcha/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/verification/resources/captcha/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/verification/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/verification/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/samples/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/samples/resources/audio/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/samples/resources/audio/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/samples/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/samples/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/settings/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/settings/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/types/voicesgetsharedrequestcategory.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/types/voicesupdaterequestlabels.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/webhooks/client/requests/bodycreateworkspacewebhookv1workspacewebhookspost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/webhooks/client/requests/bodyupdateworkspacewebhookv1workspacewebhookswebhookidpatch.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/webhooks/client/requests/webhookslistrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/webhooks/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/webhooks/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/webhooks/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/groups/client/requests/groupssearchrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/groups/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/groups/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/groups/resources/members/client/requests/addmembertogrouprequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/groups/resources/members/client/requests/bodydeletememberfromusergroupv1workspacegroupsgroupidmembersremovepost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/groups/resources/members/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/groups/resources/members/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/groups/resources/members/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/groups/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/groups/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/invites/client/requests/bodydeleteexistinginvitationv1workspaceinvitesdelete.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/invites/client/requests/bodyinvitemultipleusersv1workspaceinvitesaddbulkpost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/invites/client/requests/inviteuserrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/invites/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/invites/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/invites/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/members/client/requests/updatememberrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/members/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/members/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/members/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/resources/client/requests/bodyshareworkspaceresourcev1workspaceresourcesresourceidsharepost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/resources/client/requests/bodyunshareworkspaceresourcev1workspaceresourcesresourceidunsharepost.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/resources/client/requests/resourcesgetrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/resources/client/requests/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/resources/client/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/resources/types/bodyshareworkspaceresourcev1workspaceresourcesresourceidsharepostrole.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/resources/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/addchapterresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/additionalformatresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/additionalformats.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/addknowledgebaseresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/addprojectrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/addprojectresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/addpronunciationdictionaryresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/addpronunciationdictionaryresponsemodelpermissiononresource.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/addsharingvoicerequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/addvoiceivcresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/addvoiceresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/addworkspacegroupmemberresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/addworkspaceinviteresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/adhocagentconfigoverridefortestrequestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/age.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentbranchbasicinfo.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentbranchresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentbranchsummary.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentcalllimits.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentconfigapimodelworkflowoverrideinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentconfigapimodelworkflowoverrideoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentconfigoverrideconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentconfigoverrideinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentconfigoverrideoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentdefinitionsource.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentdeploymentpercentagestrategy.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentdeploymentrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentdeploymentrequestitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentdeploymentresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentfailureresponseexample.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentmetadata.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentmetadataresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentplatformsettingsrequestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentplatformsettingsresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentsimulatedchattestresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentsortby.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentsuccessfulresponseexample.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentsummarybatchsuccessfulresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentsummaryresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agenttestingsettings.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agenttransfer.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentversionmetadata.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentversionparents.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentworkflowrequestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentworkflowrequestmodelnodesvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentworkflowresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentworkflowresponsemodelnodesvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentworkspaceoverridesinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/agentworkspaceoverridesoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/alignment.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/alignmentguardrail.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/allowedoutputformats.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/allowlistitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/apiintegrationwebhookoverridesinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/apiintegrationwebhookoverridesinputrequestheadersvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/apiintegrationwebhookoverridesoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/apiintegrationwebhookoverridesoutputrequestheadersvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/apiintegrationwebhooktoolconfiginput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/apiintegrationwebhooktoolconfigoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/arrayjsonschemapropertyinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/arrayjsonschemapropertyinputitems.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/arrayjsonschemapropertyoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/arrayjsonschemapropertyoutputitems.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/asrconversationalconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/asrconversationalconfigworkflowoverride.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/asrinputformat.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/asrprovider.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/asrquality.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astandoperatornodeinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astandoperatornodeinputchildrenitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astandoperatornodeoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astandoperatornodeoutputchildrenitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astbooleannodeinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astbooleannodeoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astdynamicvariablenodeinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astdynamicvariablenodeoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astequalsoperatornodeinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astequalsoperatornodeinputleft.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astequalsoperatornodeinputright.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astequalsoperatornodeoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astequalsoperatornodeoutputleft.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astequalsoperatornodeoutputright.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astgreaterthanoperatornodeinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astgreaterthanoperatornodeinputleft.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astgreaterthanoperatornodeinputright.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astgreaterthanoperatornodeoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astgreaterthanoperatornodeoutputleft.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astgreaterthanoperatornodeoutputright.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astgreaterthanorequalsoperatornodeinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astgreaterthanorequalsoperatornodeinputleft.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astgreaterthanorequalsoperatornodeinputright.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astgreaterthanorequalsoperatornodeoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astgreaterthanorequalsoperatornodeoutputleft.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astgreaterthanorequalsoperatornodeoutputright.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astlessthanoperatornodeinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astlessthanoperatornodeinputleft.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astlessthanoperatornodeinputright.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astlessthanoperatornodeoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astlessthanoperatornodeoutputleft.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astlessthanoperatornodeoutputright.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astlessthanorequalsoperatornodeinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astlessthanorequalsoperatornodeinputleft.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astlessthanorequalsoperatornodeinputright.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astlessthanorequalsoperatornodeoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astlessthanorequalsoperatornodeoutputleft.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astlessthanorequalsoperatornodeoutputright.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astllmnodeinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astllmnodeoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astnotequalsoperatornodeinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astnotequalsoperatornodeinputleft.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astnotequalsoperatornodeinputright.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astnotequalsoperatornodeoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astnotequalsoperatornodeoutputleft.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astnotequalsoperatornodeoutputright.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astnumbernodeinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astnumbernodeoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astoroperatornodeinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astoroperatornodeinputchildrenitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astoroperatornodeoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/astoroperatornodeoutputchildrenitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/aststringnodeinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/aststringnodeoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/asyncconversationmetadata.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/asyncconversationmetadatadeliverystatus.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/attachedtestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/audioformatenum.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/audionativecreateprojectresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/audionativeeditcontentresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/audionativeprojectsettingsresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/audionativeprojectsettingsresponsemodelstatus.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/audiooutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/audiooutputmulti.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/audiowithtimestampsandvoicesegmentsresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/audiowithtimestampsresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/authconnectionlocator.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/authorizationmethod.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/authsettings.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/backupllmdefault.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/backupllmdisabled.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/backupllmoverride.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/badrequesterrorbody.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/batchcalldetailedresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/batchcallrecipientstatus.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/batchcallresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/batchcallstatus.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/batchcallwhatsappparams.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/batchfailureresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/billingperiod.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/bodygeneratearandomvoicev1voicegenerationgeneratevoicepostage.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/bodygeneratearandomvoicev1voicegenerationgeneratevoicepostgender.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/branchprotectionstatus.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/breakdowntypes.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/builtintoolsinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/builtintoolsoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/builtintoolsworkflowoverrideinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/builtintoolsworkflowoverrideoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/canvasplacement.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/captionstylecharacteranimationmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/captionstylecharacteranimationmodelentertype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/captionstylecharacteranimationmodelexittype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/captionstylehorizontalplacementmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/captionstylehorizontalplacementmodelalign.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/captionstylemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/captionstylemodeltextalign.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/captionstylemodeltextstyle.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/captionstylemodeltextweight.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/captionstylesectionanimationmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/captionstylesectionanimationmodelentertype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/captionstylesectionanimationmodelexittype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/captionstyletemplatemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/captionstyleverticalplacementmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/captionstyleverticalplacementmodelalign.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/captionstylewordanimationmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/captionstylewordanimationmodelentertype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/captionstylewordanimationmodelexittype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/chaptercontentblockextendablenoderesponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/chaptercontentblockinputmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/chaptercontentblockinputmodelsubtype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/chaptercontentblockresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/chaptercontentblockresponsemodelnodesitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/chaptercontentblockttsnoderesponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/chaptercontentinputmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/chaptercontentparagraphttsnodeinputmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/chaptercontentresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/chapterresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/chaptersnapshotextendedresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/chaptersnapshotresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/chaptersnapshotsresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/chapterstate.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/chapterstatisticsresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/chapterwithcontentresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/chapterwithcontentresponsemodelstate.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/characteralignmentmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/characteralignmentresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/characterrefreshperiod.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/characterusageresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/clientevent.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/clienttoolconfiginput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/clienttoolconfigoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/closeconnection.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/closecontext.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/closesocket.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/committedtranscriptpayload.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/committedtranscriptwithtimestampspayload.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/contentconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/contentguardrailinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/contentguardrailoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/contributor.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/convaidynamicvariable.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/convaisecretlocator.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/convaistoredsecretdependencies.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/convaistoredsecretdependenciesagentsitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/convaistoredsecretdependenciestoolsitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/convaiusersecretdbmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/convaiwebhooks.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/convaiworkspacestoredsecretconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationalconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationalconfigapimodelworkflowoverrideinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationalconfigapimodelworkflowoverrideoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationchargingcommonmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationconfigclientoverrideconfiginput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationconfigclientoverrideconfigoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationconfigclientoverrideinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationconfigclientoverrideoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationconfigoverride.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationconfigoverrideconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationconfigworkflowoverride.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationdeletionsettings.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationfeedbacktype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistoryanalysiscommonmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorybatchcallmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistoryelevenassistantcommonmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistoryerrorcommonmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistoryevaluationcriteriaresultcommonmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistoryfeedbackcommonmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorymetadatacommonmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorymetadatacommonmodelphonecall.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorymultivoicemessagemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorymultivoicemessagepartmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistoryragusagecommonmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorysiptrunkingphonecallmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorysiptrunkingphonecallmodeldirection.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorytranscriptapiintegrationwebhooktoolsresultcommonmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorytranscriptcommonmodelinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorytranscriptcommonmodelinputrole.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorytranscriptcommonmodelinputsourcemedium.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorytranscriptcommonmodelinputtoolresultsitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorytranscriptcommonmodeloutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorytranscriptcommonmodeloutputrole.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorytranscriptcommonmodeloutputsourcemedium.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorytranscriptcommonmodeloutputtoolresultsitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorytranscriptothertoolsresultcommonmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorytranscriptothertoolsresultcommonmodeltype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorytranscriptsystemtoolresultcommonmodelinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorytranscriptsystemtoolresultcommonmodelinputresult.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorytranscriptsystemtoolresultcommonmodeloutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorytranscriptsystemtoolresultcommonmodeloutputresult.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorytranscripttoolcallapiintegrationwebhookdetails.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorytranscripttoolcallclientdetails.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorytranscripttoolcallcommonmodelinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorytranscripttoolcallcommonmodelinputtooldetails.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorytranscripttoolcallcommonmodeloutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorytranscripttoolcallcommonmodeloutputtooldetails.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorytranscripttoolcallmcpdetails.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorytranscripttoolcallwebhookdetails.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorytranscriptworkflowtoolsresultcommonmodelinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorytranscriptworkflowtoolsresultcommonmodeloutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorytwiliophonecallmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationhistorytwiliophonecallmodeldirection.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationinitiationclientdataconfiginput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationinitiationclientdataconfigoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationinitiationclientdatainternal.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationinitiationclientdatainternaldynamicvariablesvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationinitiationclientdatarequestinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationinitiationclientdatarequestinputdynamicvariablesvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationinitiationclientdatarequestoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationinitiationclientdatarequestoutputdynamicvariablesvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationinitiationclientdatawebhook.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationinitiationclientdatawebhookrequestheadersvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationinitiationsource.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationinitiationsourceinfo.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationsignedurlresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationsimulationspecification.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationsimulationspecificationdynamicvariablesvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationsummaryresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationsummaryresponsemodeldirection.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationsummaryresponsemodelstatus.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationtokendbmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationtokenpurpose.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationturnmetrics.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/conversationuserresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/convertchapterresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/convertprojectresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/createagentbranchresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/createagentresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/createagenttestresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/createaudionativeprojectrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/createphonenumberresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/createpreviouslygeneratedvoicerequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/createpronunciationdictionaryresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/createresponseunittestrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/createresponseunittestrequestdynamicvariablesvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/createsimulationtestrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/createsimulationtestrequestdynamicvariablesvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/createsiptrunkphonenumberrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/createtoolcallunittestrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/createtoolcallunittestrequestdynamicvariablesvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/createtranscriptrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/createtwiliophonenumberrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/customguardrailconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/customguardrailconfigmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/customguardrailinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/customguardrailoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/customguardrailsconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/customllm.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/customllmapitype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/customllmrequestheadersvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/customsipheader.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/customsipheaderwithdynamicvariable.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dashboardcallsuccesschartmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dashboardcriteriachartmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dashboarddatacollectionchartmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/datacollectionresultcommonmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/defaultsharinggroupresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/defaultsharinggroupresponsemodelpermissionlevel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/deletechapterrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/deletechapterresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/deletedubbingresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/deletehistoryitemresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/deleteprojectrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/deleteprojectresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/deletesampleresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/deletevoiceresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/deletevoicesampleresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/deleteworkspacegroupmemberresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/deleteworkspaceinviteresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/deleteworkspacewebhookresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dependentavailableagentidentifier.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dependentavailableagentidentifieraccesslevel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dependentavailabletoolidentifier.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dependentavailabletoolidentifieraccesslevel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dependentphonenumberidentifier.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dependentunknownagentidentifier.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dependentunknowntoolidentifier.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/detailedmusicresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/detectedentity.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dialogueinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dialogueinputresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/directpublishingreadresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/directpublishingreadresponsemodeldisplaymode.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/directpublishingreadresponsemodelgenreitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/directpublishingreadresponsemodelpayouttype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/directpublishingreadresponsemodeltargetaudience.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/discountresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/documentusagemodeenum.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/docxexportoptions.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dodubbingresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dubbedsegment.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dubbingmediametadata.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dubbingmediareference.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dubbingmetadatapageresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dubbingmetadataresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dubbingmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dubbingrenderresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dubbingresource.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dubbingtranscript.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dubbingtranscriptcharacter.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dubbingtranscriptresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dubbingtranscriptsresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dubbingtranscriptsresponsemodeltranscriptformat.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dubbingtranscriptutterance.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dubbingtranscriptword.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dynamicvariableassignment.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dynamicvariablesconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dynamicvariablesconfigdynamicvariableplaceholdersvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dynamicvariablesconfigworkflowoverride.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dynamicvariablesconfigworkflowoverridedynamicvariableplaceholdersvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/dynamicvariableupdatecommonmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/editchapterresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/editprojectresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/editvoiceresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/editvoicesettingsrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/editvoicesettingsresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/embeddingmodelenum.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/embedvariant.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/endcalltoolconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/endcalltoolresultmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/evaluationsettings.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/evaluationsuccessresult.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/exactparameterevaluationstrategy.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/exportoptions.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/extendedsubscriptionresponsemodelcurrency.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/extendedsubscriptionresponsemodelpendingchange.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/featurestatuscommonmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/featuresusagecommonmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/feedbackitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/finaloutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/finaloutputmulti.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/finetuningresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/finetuningresponsemodelstatevalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/flushcontext.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/forcedalignmentcharacterresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/forcedalignmentresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/forcedalignmentwordresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/gender.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/generatevoicerequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/generationconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getagentembedresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getagentknowledgebasesizeresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getagentlinkresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getagentresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getagentresponsemodelphonenumbersitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getagentspageresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getaudionativeprojectsettingsresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getchapterrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getchaptersnapshotsrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getchaptersrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getchaptersresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getconvaidashboardsettingsresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getconvaidashboardsettingsresponsemodelchartsitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getconvaisettingsresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getconversationresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getconversationresponsemodelstatus.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getconversationspageresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getconversationuserspageresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getknowledgebasedependentagentsresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getknowledgebasedependentagentsresponsemodelagentsitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getknowledgebasefileresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getknowledgebasefolderresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getknowledgebaselistresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getknowledgebaselistresponsemodeldocumentsitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getknowledgebasesummaryfileresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getknowledgebasesummaryfileresponsemodeldependentagentsitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getknowledgebasesummaryfolderresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getknowledgebasesummaryfolderresponsemodeldependentagentsitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getknowledgebasesummarytextresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getknowledgebasesummarytextresponsemodeldependentagentsitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getknowledgebasesummaryurlresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getknowledgebasesummaryurlresponsemodeldependentagentsitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getknowledgebasetextresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getknowledgebaseurlresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getlibraryvoicesresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getlivecountresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getorcreateragindexrequestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getphonenumberinboundsiptrunkconfigresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getphonenumberoutboundsiptrunkconfigresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getphonenumberresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getphonenumbersiptrunkresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getphonenumbertwilioresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getprojectrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getprojectsrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getprojectsresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getpronunciationdictionariesmetadataresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getpronunciationdictionariesresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getpronunciationdictionarymetadataresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getpronunciationdictionarymetadataresponsemodelpermissiononresource.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getpronunciationdictionaryresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getpronunciationdictionarywithrulesresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getpronunciationdictionarywithrulesresponsemodelpermissiononresource.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getpronunciationdictionarywithrulesresponsemodelrulesitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getresponseunittestresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getresponseunittestresponsemodeldynamicvariablesvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getsimulationtestresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getsimulationtestresponsemodeldynamicvariablesvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getspeechhistoryresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/gettestinvocationspageresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/gettestsuiteinvocationresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/gettestspageresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/gettestssummariesbyidsresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/gettoolcallunittestresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/gettoolcallunittestresponsemodeldynamicvariablesvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/gettooldependentagentsresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/gettooldependentagentsresponsemodelagentsitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getvoicesresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getvoicesv2response.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getwhatsappaccountresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/getworkspacesecretsresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/guardrailsv1input.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/guardrailsv1output.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/historyalignmentresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/historyalignmentsresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/historyitemresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/htmlexportoptions.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/httpvalidationerror.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/imageavatar.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/inboundsiptrunkconfigrequestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/initialisecontext.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/initializeconnection.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/initializeconnectionmulti.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/inputaudiochunkpayload.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/integrationtype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/invoiceresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/invoiceresponsemodelpaymentintentstatus.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/invoiceresponsemodelpaymentintentstatussesitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/keepcontextalive.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/knowledgebasedependenttype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/knowledgebasedocumentchunkresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/knowledgebasedocumentmetadataresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/knowledgebasedocumenttype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/knowledgebasefolderpathsegmentresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/knowledgebasefolderpathsegmentsummaryresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/knowledgebaselocator.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/knowledgebasesortby.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/knowledgebasesourcefileurlresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/knowledgebasesummarybatchsuccessfulresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/knowledgebasesummarybatchsuccessfulresponsemodeldata.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/languageaddedresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/languagedetectiontoolconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/languagedetectiontoolresultmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/languagepresetinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/languagepresetoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/languagepresettranslation.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/languageresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/libraryvoiceresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/libraryvoiceresponsemodelcategory.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/listmcptoolsresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/listresponseagentbranchsummary.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/listresponsemeta.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/listwhatsappaccountsresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/literaljsonschemaproperty.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/literaljsonschemapropertyconstantvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/literaljsonschemapropertytype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/literaloverride.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/literaloverrideconstantvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/livekitstacktype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/llm.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/llmcategoryusage.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/llminputoutputtokensusage.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/llmparameterevaluationstrategy.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/llmreasoningeffort.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/llmtokenscategoryusage.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/llmusagecalculatorllmresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/llmusagecalculatorresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/llmusageinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/llmusageoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/manualverificationfileresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/manualverificationresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/matchanythingparameterevaluationstrategy.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/mcpapprovalpolicy.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/mcpserverconfiginput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/mcpserverconfiginputrequestheadersvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/mcpserverconfiginputsecrettoken.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/mcpserverconfiginputurl.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/mcpserverconfigoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/mcpserverconfigoutputrequestheadersvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/mcpserverconfigoutputsecrettoken.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/mcpserverconfigoutputurl.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/mcpservermetadataresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/mcpserverresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/mcpserverresponsemodeldependentagentsitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/mcpserversresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/mcpservertransport.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/mcptoolapprovalhash.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/mcptoolapprovalpolicy.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/mcptoolconfigoverride.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/mergingstrategy.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/metricrecord.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/metrictype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/model.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/modelratesresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/modelsettingsresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/moderationconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/moderationguardrailinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/moderationguardrailoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/multichannelspeechtotextresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/multipartmusicresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/multisourceconfigjson.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/musicprompt.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/nonstreamingoutputformats.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/normalizedalignment.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/objectjsonschemapropertyinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/objectjsonschemapropertyinputpropertiesvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/objectjsonschemapropertyoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/objectjsonschemapropertyoutputpropertiesvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/objectoverrideinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/objectoverrideinputpropertiesvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/objectoverrideoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/objectoverrideoutputpropertiesvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/orbavatar.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/outboundcallrecipient.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/outboundcallrecipientresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/outboundsiptrunkconfigrequestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/outputformat.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/partialtranscriptpayload.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/patchworkspacewebhookresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/pdfexportoptions.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/pendingcancellationresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/pendingcliptask.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/pendingcliptasktype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/pendingsubscriptionswitchresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/pendingsubscriptionswitchresponsemodelnexttier.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/permissiontype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/phonenumberagentinfo.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/phonenumberdynamicvariabletransferdestination.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/phonenumbertransfer.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/phonenumbertransfercustomsipheadersitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/phonenumbertransferdestination.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/phonenumbertransferpostdialdigits.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/phonenumbertransfertransferdestination.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/playdtmfresulterrormodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/playdtmfresultsuccessmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/playdtmftoolconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/podcastbulletinmode.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/podcastbulletinmodedata.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/podcastconversationmode.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/podcastconversationmodedata.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/podcastprojectresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/podcasttextsource.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/podcasturlsource.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/positioninput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/positionoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/postagentavatarresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/postdialdigitsdynamicvariable.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/postdialdigitsstatic.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/postworkspacesecretresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/previewaudiodbmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/privacyconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/proceduredraftref.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/procedureversionref.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/projectcreationmetaresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/projectcreationmetaresponsemodelstatus.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/projectcreationmetaresponsemodeltype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/projectextendedresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/projectextendedresponsemodelaccesslevel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/projectextendedresponsemodelapplytextnormalization.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/projectextendedresponsemodelaspectratio.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/projectextendedresponsemodelassetsitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/projectextendedresponsemodelfiction.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/projectextendedresponsemodelqualitypreset.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/projectextendedresponsemodelsourcetype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/projectextendedresponsemodeltargetaudience.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/projectexternalaudioresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/projectimageresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/projectmutedtracksresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/projectresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/projectresponsemodelaccesslevel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/projectresponsemodelaspectratio.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/projectresponsemodelfiction.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/projectresponsemodelsourcetype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/projectresponsemodeltargetaudience.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/projectsnapshotextendedresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/projectsnapshotresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/projectsnapshotsresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/projectstate.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/projectvideoresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/projectvideothumbnailsheetresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/projectvoiceresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/promptagent.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/promptagentapimodelinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/promptagentapimodelinputbackupllmconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/promptagentapimodelinputtoolsitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/promptagentapimodeloutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/promptagentapimodeloutputbackupllmconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/promptagentapimodeloutputtoolsitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/promptagentapimodeloverride.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/promptagentapimodeloverrideconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/promptagentapimodelworkflowoverrideinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/promptagentapimodelworkflowoverrideinputbackupllmconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/promptagentapimodelworkflowoverrideinputtoolsitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/promptagentapimodelworkflowoverrideoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/promptagentapimodelworkflowoverrideoutputbackupllmconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/promptagentapimodelworkflowoverrideoutputtoolsitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/promptagentdbmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/promptevaluationcriteria.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/pronunciationdictionaryaliasrulerequestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/pronunciationdictionaryaliasruleresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/pronunciationdictionarylocator.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/pronunciationdictionarylocatorresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/pronunciationdictionaryphonemerulerequestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/pronunciationdictionaryphonemeruleresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/pronunciationdictionaryrulesresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/pronunciationdictionaryversionlocator.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/pronunciationdictionaryversionresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/pronunciationdictionaryversionresponsemodelpermissiononresource.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/pydanticpronunciationdictionaryversionlocator.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/queryoverride.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/queryparamsjsonschema.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/ragchunkmetadata.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/ragconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/ragconfigworkflowoverride.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/ragdocumentindexesresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/ragdocumentindexresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/ragdocumentindexusage.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/ragindexbatchsuccessfulresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/ragindexoverviewembeddingmodelresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/ragindexoverviewresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/ragindexstatus.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/ragretrievalinfo.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/readerresourceresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/readerresourceresponsemodelresourcetype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/readlegalterms.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/readmetadatachapterdbmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/realtimevoicesettings.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/recordingresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/referencedtoolcommonmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/referencedtoolcommonmodeltype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/regexparameterevaluationstrategy.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/regionconfigrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/removememberfromgrouprequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/render.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/renderstatus.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/rendertype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/requestpvcmanualverificationresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/resourceaccessinfo.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/resourceaccessinforole.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/resourcemetadataresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/resourcemetadataresponsemodelanonymousaccessleveloverride.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/responsefiltermode.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/responseunittestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/responseunittestmodeldynamicvariablesvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/reviewresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/reviewresponsemodelrejectreasonsitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/reviewresponsemodelreviewstatus.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/reviewstatus.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/safetycommonmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/safetyevaluation.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/safetyresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/safetyrule.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/sampleconfigdbmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/sampleconfigdbmodelparenttype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/savevoicepreviewrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/scribeautherrorpayload.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/scribechunksizeexceedederrorpayload.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/scribeerrorpayload.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/scribeinputerrorpayload.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/scribeinsufficientaudioactivityerrorpayload.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/scribequeueoverflowerrorpayload.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/scribequotaexceedederrorpayload.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/scriberatelimitederrorpayload.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/scriberesourceexhaustederrorpayload.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/scribesessiontimelimitexceedederrorpayload.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/scribethrottlederrorpayload.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/scribetranscribererrorpayload.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/scribeunacceptedtermserrorpayload.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/searchdocumentationtoolconfiginput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/searchdocumentationtoolconfigoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/seattype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/secretdependencytype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/sectionsource.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/segmentcreateresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/segmentdeleteresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/segmentdubresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/segmentedjsonexportoptions.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/segmentmigrationresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/segmentsubtitleframe.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/segmenttranscriptionresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/segmenttranslationresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/segmentupdateresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/sendtext.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/sendtextmulti.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/sessionstartedpayload.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/sessionstartedpayloadconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/sessionstartedpayloadconfigcommitstrategy.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/shareoptionresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/shareoptionresponsemodeltype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/similarvoice.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/similarvoicesforspeakerresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/simulationtestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/simulationtestmodeldynamicvariablesvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/singletestrunrequestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/singleusetokenresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/singleusetokentype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/sipmediaencryptionenum.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/siptrunkcredentialsrequestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/siptrunkoutboundcallresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/siptrunktransportenum.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/sipuridynamicvariabletransferdestination.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/sipuritransferdestination.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/skipturntoolconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/skipturntoolresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/softtimeoutconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/softtimeoutconfigoverride.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/softtimeoutconfigoverrideconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/softtimeoutconfigworkflowoverride.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/songmetadata.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/songsection.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/sortdirection.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/sourceconfigjson.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/sourceretrievalconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/speakeraudioresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/speakercreatedresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/speakerresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/speakersegment.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/speakerseparationresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/speakerseparationresponsemodelstatus.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/speakertrack.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/speakerupdatedresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/speechhistoryitemresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/speechhistoryitemresponsemodelsource.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/speechhistoryitemresponsemodelvoicecategory.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/speechtotextcharacterresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/speechtotextchunkresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/speechtotextwebhookresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/speechtotextwordresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/speechtotextwordresponsemodeltype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/spellingpatience.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/srtexportoptions.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/startpvcvoicetrainingresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/startspeakerseparationresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/streamingaudiochunkwithtimestampsandvoicesegmentsresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/streamingaudiochunkwithtimestampsresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/subscription.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/subscriptionresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/subscriptionresponsemodelcurrency.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/subscriptionstatustype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/suggestedaudiotag.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/supportedvoice.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/systemtoolconfiginput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/systemtoolconfiginputparams.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/systemtoolconfigoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/systemtoolconfigoutputparams.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/telephonyprovider.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/testconditionrationalecommonmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/testconditionresultcommonmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/testfromconversationmetadatainput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/testfromconversationmetadataoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/testinvocationsummaryresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/testrunmetadata.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/testrunmetadatatesttype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/testrunstatus.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/testsfeatureusagecommonmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/testtoolresultmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/testtype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/textnormalisationtype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/texttospeechapplytextnormalizationenum.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/texttospeechoutputformatenum.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/texttospeechrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/texttospeechstreamrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/texttospeechstreamwithtimestampsrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/texttospeechwithtimestampsrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/thresholdguardrail.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/timerange.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/tokenresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/tool.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/toolannotations.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/toolcallsoundbehavior.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/toolcallsoundtype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/toolcallunittestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/toolcallunittestmodeldynamicvariablesvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/toolerrorhandlingmode.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/toolexecutionmode.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/toolmockconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/toolrequestmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/toolrequestmodeltoolconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/toolresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/toolresponsemodeltoolconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/toolsortby.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/toolsresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/tooltype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/tooltypefilter.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/toolusagestatsresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/transcriptionword.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/transcriptionwordtype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/transferbranchinfodefaultingtomain.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/transferbranchinfotrafficsplit.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/transfertoagenttoolconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/transfertoagenttoolresulterrormodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/transfertoagenttoolresultsuccessmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/transfertoagenttoolresultsuccessmodelbranchinfo.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/transfertonumberresulterrormodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/transfertonumberresultsipsuccessmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/transfertonumberresulttwiliosuccessmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/transfertonumbertoolconfiginput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/transfertonumbertoolconfigoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/transfertypeenum.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/ttsconversationalconfiginput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/ttsconversationalconfigoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/ttsconversationalconfigoverride.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/ttsconversationalconfigoverrideconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/ttsconversationalconfigworkflowoverrideinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/ttsconversationalconfigworkflowoverrideoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/ttsconversationalmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/ttsmodelfamily.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/ttsoptimizestreaminglatency.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/ttsoutputformat.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/turnconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/turnconfigoverride.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/turnconfigoverrideconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/turnconfigworkflowoverride.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/turneagerness.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/turnmode.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/turnmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/twilioedgelocation.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/twiliooutboundcallresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/twilioregionid.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/txtexportoptions.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/unittestrunresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/unittestrunresponsemodeltestinfo.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/unittestsummaryresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/unittesttoolcallevaluationmodelinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/unittesttoolcallevaluationmodeloutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/unittesttoolcallparameter.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/unittesttoolcallparametereval.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/unittestworkflownodetransitionevaluationnodeid.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/updateaudionativeprojectrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/updatechapterrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/updateprojectrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/updatepronunciationdictionariesrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/updateresponseunittestrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/updateresponseunittestrequestdynamicvariablesvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/updatesimulationtestrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/updatesimulationtestrequestdynamicvariablesvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/updatetoolcallunittestrequest.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/updatetoolcallunittestrequestdynamicvariablesvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/updateworkspacememberresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/urlavatar.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/usageaggregationinterval.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/usagecharactersresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/user.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/userfeedback.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/userfeedbackscore.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/utteranceresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/vadconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/vadconfigworkflowoverride.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/validationerror.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/validationerrorlocitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/verificationattemptresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/verifiedvoicelanguageresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/verifypvcvoicecaptcharesponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/voice.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/voicecategory.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/voicedesignpreviewresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/voicegenerationparameteroptionresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/voicegenerationparameterresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/voicemaildetectionresultsuccessmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/voicemaildetectiontoolconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/voicepreviewresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/voiceresponsemodelcategory.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/voiceresponsemodelsafetycontrol.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/voicesample.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/voicesamplepreviewresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/voicesamplevisualwaveformresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/voicesegment.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/voicesettings.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/voicesharingmoderationcheckresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/voicesharingresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/voicesharingresponsemodelcategory.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/voicesharingstate.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/voiceverificationresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/webhookauthmethodtype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/webhookeventtype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/webhookhmacsettings.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/webhooktoolapischemaconfiginput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/webhooktoolapischemaconfiginputcontenttype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/webhooktoolapischemaconfiginputmethod.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/webhooktoolapischemaconfiginputrequestheadersvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/webhooktoolapischemaconfigoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/webhooktoolapischemaconfigoutputcontenttype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/webhooktoolapischemaconfigoutputmethod.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/webhooktoolapischemaconfigoutputrequestheadersvalue.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/webhooktoolconfiginput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/webhooktoolconfigoutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/webhookusagetype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/websocketttsclientmessagemulti.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/websocketttsservermessagemulti.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/whatsappconversationinfo.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/whatsappconversationinfodirection.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/whatsappoutboundcallresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/whatsappoutboundmessageresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/whatsapptemplatebodycomponentparams.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/whatsapptemplatebuttoncomponentparams.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/whatsapptemplatedocumentparam.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/whatsapptemplatedocumentparamdetails.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/whatsapptemplateheadercomponentparams.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/whatsapptemplateheadercomponentparamsparametersitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/whatsapptemplateimageparam.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/whatsapptemplateimageparamdetails.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/whatsapptemplatelocationparam.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/whatsapptemplatelocationparamdetails.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/whatsapptemplatetextparam.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/widgetconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/widgetconfiginputavatar.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/widgetconfigoutputavatar.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/widgetconfigresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/widgetconfigresponsemodelavatar.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/widgetendfeedbackconfig.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/widgetendfeedbacktype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/widgetexpandable.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/widgetfeedbackmode.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/widgetlanguagepreset.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/widgetlanguagepresetresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/widgetplacement.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/widgetstyles.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/widgettermstranslation.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/widgettextcontents.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/wordtimestamp.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowedgemodelinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowedgemodelinputbackwardcondition.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowedgemodelinputforwardcondition.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowedgemodeloutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowedgemodeloutputbackwardcondition.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowedgemodeloutputforwardcondition.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowendnodemodelinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowendnodemodeloutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowexpressionconditionmodelinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowexpressionconditionmodelinputexpression.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowexpressionconditionmodeloutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowexpressionconditionmodeloutputexpression.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowfeaturesusagecommonmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowllmconditionmodelinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowllmconditionmodeloutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowoverrideagentnodemodelinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowoverrideagentnodemodeloutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowphonenumbernodemodelinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowphonenumbernodemodelinputcustomsipheadersitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowphonenumbernodemodelinputpostdialdigits.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowphonenumbernodemodelinputtransferdestination.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowphonenumbernodemodeloutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowphonenumbernodemodeloutputcustomsipheadersitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowphonenumbernodemodeloutputpostdialdigits.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowphonenumbernodemodeloutputtransferdestination.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowresultconditionmodelinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowresultconditionmodeloutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowstandaloneagentnodemodelinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowstandaloneagentnodemodeloutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowstartnodemodelinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowstartnodemodeloutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowtooledgestepmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowtoollocator.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowtoolmaxiterationsexceededstepmodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowtoolnestedtoolsstepmodelinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowtoolnestedtoolsstepmodelinputresultsitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowtoolnestedtoolsstepmodeloutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowtoolnestedtoolsstepmodeloutputresultsitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowtoolnodemodelinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowtoolnodemodeloutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowtoolresponsemodelinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowtoolresponsemodelinputstepsitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowtoolresponsemodeloutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowtoolresponsemodeloutputstepsitem.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowunconditionalmodelinput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workflowunconditionalmodeloutput.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workspaceapikeylistresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workspaceapikeyresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workspacebatchcallsresponse.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workspacecreateapikeyresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workspacecreatewebhookresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workspacegroupbynameresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workspacegrouppermission.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workspacegroupresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workspacegroupresponsemodelgroupusagelimit.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workspaceresourcetype.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workspaceserviceaccountlistresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workspaceserviceaccountresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workspacewebhooklistresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workspacewebhookresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/workspacewebhookusageresponsemodel.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/types/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/environments.d.ts","./node_modules/@elevenlabs/elevenlabs-js/baseclient.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/audioisolation/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/audionative/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/branches/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/deployments/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/drafts/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/knowledgebase/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/link/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/llmusage/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/summaries/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/widget/resources/avatar/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/resources/widget/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/agents/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/analytics/resources/livecount/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/analytics/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/batchcalls/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/conversations/resources/audio/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/conversations/resources/feedback/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/conversations/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/dashboard/resources/settings/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/dashboard/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/document/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/documents/resources/chunk/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/documents/resources/summaries/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/resources/documents/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/knowledgebase/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/llmusage/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/mcpservers/resources/approvalpolicy/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/mcpservers/resources/toolapprovals/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/mcpservers/resources/toolconfigs/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/mcpservers/resources/tools/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/mcpservers/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/phonenumbers/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/secrets/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/settings/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/siptrunk/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/tests/resources/invocations/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/tests/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/tools/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/twilio/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/users/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/whatsapp/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/resources/whatsappaccounts/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/conversationalai/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/audio/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/resources/language/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/resources/segment/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/resources/speaker/resources/segment/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/resources/speaker/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/resource/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/transcript/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/resources/transcripts/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/dubbing/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/forcedalignment/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/history/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/models/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/music/resources/compositionplan/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/music/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/pronunciationdictionaries/resources/rules/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/pronunciationdictionaries/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/samples/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/serviceaccounts/resources/apikeys/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/serviceaccounts/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtospeech/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtotext/resources/transcripts/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/speechtotext/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/resources/chapters/resources/snapshots/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/resources/chapters/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/resources/content/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/resources/pronunciationdictionaries/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/resources/snapshots/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/resources/projects/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/studio/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttodialogue/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttosoundeffects/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttospeech/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttovoice/resources/preview/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/texttovoice/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/tokens/resources/singleuse/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/tokens/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/usage/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/user/resources/subscription/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/user/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/ivc/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/samples/resources/audio/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/samples/resources/speakers/resources/audio/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/samples/resources/speakers/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/samples/resources/waveform/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/samples/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/verification/resources/captcha/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/resources/verification/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/pvc/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/samples/resources/audio/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/samples/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/resources/settings/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/voices/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/webhooks/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/groups/resources/members/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/groups/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/invites/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/members/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/resources/resources/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/api/resources/workspace/client/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/client.d.ts","./node_modules/@elevenlabs/elevenlabs-js/wrapper/webhooks.d.ts","./node_modules/@elevenlabs/elevenlabs-js/wrapper/realtime/scribe.d.ts","./node_modules/@elevenlabs/elevenlabs-js/wrapper/realtime/connection.d.ts","./node_modules/@elevenlabs/elevenlabs-js/wrapper/realtime/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/wrapper/speechtotext.d.ts","./node_modules/@elevenlabs/elevenlabs-js/wrapper/elevenlabsclient.d.ts","./node_modules/@elevenlabs/elevenlabs-js/wrapper/music.d.ts","./node_modules/@elevenlabs/elevenlabs-js/wrapper/play.d.ts","./node_modules/@elevenlabs/elevenlabs-js/wrapper/stream.d.ts","./node_modules/@elevenlabs/elevenlabs-js/wrapper/index.d.ts","./node_modules/@elevenlabs/elevenlabs-js/index.d.ts","./src/main/services/scribe-service.ts","./src/main/services/proxy-service.ts","./src/main.ts","./src/audio-processor.ts","./src/types/audio-worklet.d.ts","./src/types/electron-reload.d.ts","./src/types/ffmpeg-static.d.ts"],"fileIdsList":[[69,132,140,144,147,149,150,151,163,196],[69,132,140,144,147,149,150,151,163,194],[69,132,140,144,147,149,150,151,163],[69,132,133,140,144,147,149,150,151,163,194],[69,132,140,144,147,149,150,151,163,283,286,1939],[69,132,140,144,147,149,150,151,163,283,286],[69,132,140,144,147,149,150,151,163,287,288,289,290,291,292,293],[69,132,140,144,147,149,150,151,163,294,888,1938],[69,132,140,144,147,149,150,151,163,283,1939,1941],[69,132,140,144,147,149,150,151,163,297],[69,132,140,144,147,149,150,151,163,283,1939],[69,132,140,144,147,149,150,151,163,295,296],[69,132,140,144,147,149,150,151,163,298,301],[69,132,140,144,147,149,150,151,163,299,300],[69,132,140,144,147,149,150,151,163,305],[69,132,140,144,147,149,150,151,163,283],[69,132,140,144,147,149,150,151,163,303,304],[69,132,140,144,147,149,150,151,163,306,308],[69,132,140,144,147,149,150,151,163,307],[69,132,140,144,147,149,150,151,163,283,1939,1941,1953,1955,1956,1959,1961,1966,1967,1972,1973,1974,1975,1976,1978,1979,1980,1981,1982,1983],[69,132,140,144,147,149,150,151,163,311],[69,132,140,144,147,149,150,151,163,310],[69,132,140,144,147,149,150,151,163,312,532],[69,132,140,144,147,149,150,151,163,283,1939,1941,1944,1945,1946,1947,1948,1949,1950,1952],[69,132,140,144,147,149,150,151,163,321],[69,132,140,144,147,149,150,151,163,1939],[69,132,140,144,147,149,150,151,163,313,314,315,316,317,318,319,320],[69,132,140,144,147,149,150,151,163,322,362,364],[69,132,140,144,147,149,150,151,163,327],[69,132,140,144,147,149,150,151,163,323,324,325,326],[69,132,140,144,147,149,150,151,163,328],[69,132,140,144,147,149,150,151,163,331],[69,132,140,144,147,149,150,151,163,330],[69,132,140,144,147,149,150,151,163,332],[69,132,140,144,147,149,150,151,163,336],[69,132,140,144,147,149,150,151,163,334,335],[69,132,140,144,147,149,150,151,163,337],[69,132,140,144,147,149,150,151,163,327,329,331,333,336,338,340,342,344,346,348,351,352,354,361],[69,132,140,144,147,149,150,151,163,339],[69,132,140,144,147,149,150,151,163,341],[69,132,140,144,147,149,150,151,163,344],[69,132,140,144,147,149,150,151,163,343],[69,132,140,144,147,149,150,151,163,345],[69,132,140,144,147,149,150,151,163,348],[69,132,140,144,147,149,150,151,163,347],[69,132,140,144,147,149,150,151,163,349,351],[69,132,140,144,147,149,150,151,163,350],[69,132,140,144,147,149,150,151,163,283,1939,1941,1951],[69,132,140,144,147,149,150,151,163,354],[69,132,140,144,147,149,150,151,163,353],[69,132,140,144,147,149,150,151,163,355,360],[69,132,140,144,147,149,150,151,163,357],[69,132,140,144,147,149,150,151,163,356],[69,132,140,144,147,149,150,151,163,358],[69,132,140,144,147,149,150,151,163,357,359],[69,132,140,144,147,149,150,151,163,363],[69,132,140,144,147,149,150,151,163,1941,1954],[69,132,140,144,147,149,150,151,163,366,371],[69,132,140,144,147,149,150,151,163,368,370],[69,132,140,144,147,149,150,151,163,368],[69,132,140,144,147,149,150,151,163,367],[69,132,140,144,147,149,150,151,163,369],[69,132,140,144,147,149,150,151,163,375],[69,132,140,144,147,149,150,151,163,373,374],[69,132,140,144,147,149,150,151,163,376],[69,132,140,144,147,149,150,151,163,283,1939,1941,1957,1958],[69,132,140,144,147,149,150,151,163,381],[69,132,140,144,147,149,150,151,163,378,379,380],[69,132,140,144,147,149,150,151,163,382,389,391],[69,132,140,144,147,149,150,151,163,283,1941],[69,132,140,144,147,149,150,151,163,383],[69,132,140,144,147,149,150,151,163,386],[69,132,140,144,147,149,150,151,163,385],[69,132,140,144,147,149,150,151,163,387],[69,132,140,144,147,149,150,151,163,384,386,388],[69,132,140,144,147,149,150,151,163,390],[69,132,140,144,147,149,150,151,163,1941,1960],[69,132,140,144,147,149,150,151,163,393,400],[69,132,140,144,147,149,150,151,163,395,398,399],[69,132,140,144,147,149,150,151,163,395],[69,132,140,144,147,149,150,151,163,394],[69,132,140,144,147,149,150,151,163,396,398],[69,132,140,144,147,149,150,151,163,397],[69,132,140,144,147,149,150,151,163,321,364,365,372,375,377,381,391,392,401,404,437,438,440,442,445,464,465,467,473,474,478,480,482,484,486,488,491,503,504,507,509,512,515,516,518,520,523,526,527,529,531],[69,132,140,144,147,149,150,151,163,283,1939,1941,1962,1965],[69,132,140,144,147,149,150,151,163,404],[69,132,140,144,147,149,150,151,163,402,403],[69,132,140,144,147,149,150,151,163,405,435,437],[69,132,140,144,147,149,150,151,163,407],[69,132,140,144,147,149,150,151,163,406],[69,132,140,144,147,149,150,151,163,408],[69,132,140,144,147,149,150,151,163,283,1939,1941,1963,1964],[69,132,140,144,147,149,150,151,163,420],[69,132,140,144,147,149,150,151,163,410,411,412,413,414,415,416,417,418,419],[69,132,140,144,147,149,150,151,163,421,430,433],[69,132,140,144,147,149,150,151,163,422],[69,132,140,144,147,149,150,151,163,423,425,428,429],[69,132,140,144,147,149,150,151,163,425],[69,132,140,144,147,149,150,151,163,424],[69,132,140,144,147,149,150,151,163,426,428],[69,132,140,144,147,149,150,151,163,427],[69,132,140,144,147,149,150,151,163,431,432],[69,132,140,144,147,149,150,151,163,407,409,420,433,434],[69,132,140,144,147,149,150,151,163,436],[69,132,140,144,147,149,150,151,163,440],[69,132,140,144,147,149,150,151,163,439],[69,132,140,144,147,149,150,151,163,441],[69,132,140,144,147,149,150,151,163,283,1939,1941,1968,1969,1970,1971],[69,132,140,144,147,149,150,151,163,445],[69,132,140,144,147,149,150,151,163,443,444],[69,132,140,144,147,149,150,151,163,446,462,464],[69,132,140,144,147,149,150,151,163,448],[69,132,140,144,147,149,150,151,163,447],[69,132,140,144,147,149,150,151,163,449],[69,132,140,144,147,149,150,151,163,448,450,452,454,457,459,461],[69,132,140,144,147,149,150,151,163,452],[69,132,140,144,147,149,150,151,163,451],[69,132,140,144,147,149,150,151,163,453],[69,132,140,144,147,149,150,151,163,457],[69,132,140,144,147,149,150,151,163,455,456],[69,132,140,144,147,149,150,151,163,458],[69,132,140,144,147,149,150,151,163,460],[69,132,140,144,147,149,150,151,163,463],[69,132,140,144,147,149,150,151,163,467],[69,132,140,144,147,149,150,151,163,466],[69,132,140,144,147,149,150,151,163,468,473],[69,132,140,144,147,149,150,151,163,469,470,471,472],[69,132,140,144,147,149,150,151,163,478],[69,132,140,144,147,149,150,151,163,475,476,477],[69,132,140,144,147,149,150,151,163,479],[69,132,140,144,147,149,150,151,163,482],[69,132,140,144,147,149,150,151,163,481],[69,132,140,144,147,149,150,151,163,483],[69,132,140,144,147,149,150,151,163,486],[69,132,140,144,147,149,150,151,163,485],[69,132,140,144,147,149,150,151,163,487],[69,132,140,144,147,149,150,151,163,283,1939,1941,1977],[69,132,140,144,147,149,150,151,163,491],[69,132,140,144,147,149,150,151,163,489,490],[69,132,140,144,147,149,150,151,163,492,498,503],[69,132,140,144,147,149,150,151,163,495,497],[69,132,140,144,147,149,150,151,163,495],[69,132,140,144,147,149,150,151,163,493,494],[69,132,140,144,147,149,150,151,163,496],[69,132,140,144,147,149,150,151,163,499,500,501,502],[69,132,140,144,147,149,150,151,163,507],[69,132,140,144,147,149,150,151,163,505,506],[69,132,140,144,147,149,150,151,163,508],[69,132,140,144,147,149,150,151,163,512],[69,132,140,144,147,149,150,151,163,510,511],[69,132,140,144,147,149,150,151,163,513,515],[69,132,140,144,147,149,150,151,163,514],[69,132,140,144,147,149,150,151,163,518],[69,132,140,144,147,149,150,151,163,517],[69,132,140,144,147,149,150,151,163,519],[69,132,140,144,147,149,150,151,163,523],[69,132,140,144,147,149,150,151,163,521,522],[69,132,140,144,147,149,150,151,163,524,526],[69,132,140,144,147,149,150,151,163,525],[69,132,140,144,147,149,150,151,163,529],[69,132,140,144,147,149,150,151,163,528],[69,132,140,144,147,149,150,151,163,530],[69,132,140,144,147,149,150,151,163,283,1939,1941,1985,1990,1991,1992],[69,132,140,144,147,149,150,151,163,536],[69,132,140,144,147,149,150,151,163,534,535],[69,132,140,144,147,149,150,151,163,537,578,583],[69,132,140,144,147,149,150,151,163,538],[69,132,140,144,147,149,150,151,163,539,545,566,568,572,573,576,577],[69,132,140,144,147,149,150,151,163,283,1939,1941,1986,1987,1989],[69,132,140,144,147,149,150,151,163,545],[69,132,140,144,147,149,150,151,163,540,541,542,543,544],[69,132,140,144,147,149,150,151,163,546,565],[69,132,140,144,147,149,150,151,163,548,550,552,554,557,564],[69,132,140,144,147,149,150,151,163,548],[69,132,140,144,147,149,150,151,163,547],[69,132,140,144,147,149,150,151,163,549],[69,132,140,144,147,149,150,151,163,552],[69,132,140,144,147,149,150,151,163,551],[69,132,140,144,147,149,150,151,163,553],[69,132,140,144,147,149,150,151,163,283,1939,1941,1988],[69,132,140,144,147,149,150,151,163,557],[69,132,140,144,147,149,150,151,163,555,556],[69,132,140,144,147,149,150,151,163,558,563],[69,132,140,144,147,149,150,151,163,560,562],[69,132,140,144,147,149,150,151,163,560],[69,132,140,144,147,149,150,151,163,559],[69,132,140,144,147,149,150,151,163,561],[69,132,140,144,147,149,150,151,163,568],[69,132,140,144,147,149,150,151,163,567],[69,132,140,144,147,149,150,151,163,569,572],[69,132,140,144,147,149,150,151,163,570,571],[69,132,140,144,147,149,150,151,163,574,576],[69,132,140,144,147,149,150,151,163,575],[69,132,140,144,147,149,150,151,163,579,580,581,582],[69,132,140,144,147,149,150,151,163,586],[69,132,140,144,147,149,150,151,163,585],[69,132,140,144,147,149,150,151,163,587],[69,132,140,144,147,149,150,151,163,591],[69,132,140,144,147,149,150,151,163,589,590],[69,132,140,144,147,149,150,151,163,592,595],[69,132,140,144,147,149,150,151,163,593,594],[69,132,140,144,147,149,150,151,163,297,301,302,305,308,309,311,533,536,583,584,586,588,591,595,596,598,603,611,612,617,631,632,634,645,648,654,655,657,670,671,673,717,718,723,731,732,734,736,741,751,752,757,763,764,769,771,773,778,781,782,785,786,789,790,798,849,850,854,856,887],[69,132,140,144,147,149,150,151,163,597],[69,132,140,144,147,149,150,151,163,283,1939,1941,1997],[69,132,140,144,147,149,150,151,163,603],[69,132,140,144,147,149,150,151,163,599,600,601,602],[69,132,140,144,147,149,150,151,163,604,609,611],[69,132,140,144,147,149,150,151,163,606],[69,132,140,144,147,149,150,151,163,605],[69,132,140,144,147,149,150,151,163,607],[69,132,140,144,147,149,150,151,163,606,608],[69,132,140,144,147,149,150,151,163,610],[69,132,140,144,147,149,150,151,163,283,1939,1941,1999],[69,132,140,144,147,149,150,151,163,617],[69,132,140,144,147,149,150,151,163,613,614,615,616],[69,132,140,144,147,149,150,151,163,618,626,631],[69,132,140,144,147,149,150,151,163,621,624,625],[69,132,140,144,147,149,150,151,163,621],[69,132,140,144,147,149,150,151,163,619,620],[69,132,140,144,147,149,150,151,163,622,624],[69,132,140,144,147,149,150,151,163,623],[69,132,140,144,147,149,150,151,163,627,628,629,630],[69,132,140,144,147,149,150,151,163,633],[69,132,140,144,147,149,150,151,163,283,1939,1941,2002],[69,132,140,144,147,149,150,151,163,635,644],[69,132,140,144,147,149,150,151,163,638],[69,132,140,144,147,149,150,151,163,636,637],[69,132,140,144,147,149,150,151,163,639,642],[69,132,140,144,147,149,150,151,163,640,641],[69,132,140,144,147,149,150,151,163,638,642,643],[69,132,140,144,147,149,150,151,163,648],[69,132,140,144,147,149,150,151,163,646,647],[69,132,140,144,147,149,150,151,163,649,654],[69,132,140,144,147,149,150,151,163,650,651,652,653],[69,132,140,144,147,149,150,151,163,283,1939,1941,2005],[69,132,140,144,147,149,150,151,163,657],[69,132,140,144,147,149,150,151,163,656],[69,132,140,144,147,149,150,151,163,658,663,670],[69,132,140,144,147,149,150,151,163,661,662],[69,132,140,144,147,149,150,151,163,659,661],[69,132,140,144,147,149,150,151,163,660],[69,132,140,144,147,149,150,151,163,664,665,666,667,668,669],[69,132,140,144,147,149,150,151,163,283,1939,1941,2012],[69,132,140,144,147,149,150,151,163,673],[69,132,140,144,147,149,150,151,163,672],[69,132,140,144,147,149,150,151,163,674,710,717],[69,132,140,144,147,149,150,151,163,678,708,709],[69,132,140,144,147,149,150,151,163,283,1939,1941,2008,2009,2010,2011],[69,132,140,144,147,149,150,151,163,678],[69,132,140,144,147,149,150,151,163,675,676,677],[69,132,140,144,147,149,150,151,163,679,702,708],[69,132,140,144,147,149,150,151,163,283,1939,1941,2007],[69,132,140,144,147,149,150,151,163,682],[69,132,140,144,147,149,150,151,163,680,681],[69,132,140,144,147,149,150,151,163,683,688],[69,132,140,144,147,149,150,151,163,685,687],[69,132,140,144,147,149,150,151,163,685],[69,132,140,144,147,149,150,151,163,684],[69,132,140,144,147,149,150,151,163,686],[69,132,140,144,147,149,150,151,163,691],[69,132,140,144,147,149,150,151,163,690],[69,132,140,144,147,149,150,151,163,692],[69,132,140,144,147,149,150,151,163,682,689,691,693,695,697,699,701],[69,132,140,144,147,149,150,151,163,695],[69,132,140,144,147,149,150,151,163,694],[69,132,140,144,147,149,150,151,163,696],[69,132,140,144,147,149,150,151,163,699],[69,132,140,144,147,149,150,151,163,698],[69,132,140,144,147,149,150,151,163,700],[69,132,140,144,147,149,150,151,163,703,704,705,706,707],[69,132,140,144,147,149,150,151,163,711,712,713,714,715,716],[69,132,140,144,147,149,150,151,163,723],[69,132,140,144,147,149,150,151,163,719,720,721,722],[69,132,140,144,147,149,150,151,163,724,731],[69,132,140,144,147,149,150,151,163,725,726,727,728,729,730],[69,132,140,144,147,149,150,151,163,734],[69,132,140,144,147,149,150,151,163,733],[69,132,140,144,147,149,150,151,163,735],[69,132,140,144,147,149,150,151,163,741],[69,132,140,144,147,149,150,151,163,737,738,739,740],[69,132,140,144,147,149,150,151,163,742,751],[69,132,140,144,147,149,150,151,163,743,744,745,746,747,748,749,750],[69,132,140,144,147,149,150,151,163,283,1939,1941,2017],[69,132,140,144,147,149,150,151,163,757],[69,132,140,144,147,149,150,151,163,753,754,755,756],[69,132,140,144,147,149,150,151,163,758,761,763],[69,132,140,144,147,149,150,151,163,760],[69,132,140,144,147,149,150,151,163,759],[69,132,140,144,147,149,150,151,163,762],[69,132,140,144,147,149,150,151,163,1941,2019],[69,132,140,144,147,149,150,151,163,765,768],[69,132,140,144,147,149,150,151,163,767],[69,132,140,144,147,149,150,151,163,766],[69,132,140,144,147,149,150,151,163,771],[69,132,140,144,147,149,150,151,163,770],[69,132,140,144,147,149,150,151,163,772],[69,132,140,144,147,149,150,151,163,283,1939,1941,2022],[69,132,140,144,147,149,150,151,163,774,777],[69,132,140,144,147,149,150,151,163,776],[69,132,140,144,147,149,150,151,163,775],[69,132,140,144,147,149,150,151,163,781],[69,132,140,144,147,149,150,151,163,779,780],[69,132,140,144,147,149,150,151,163,785],[69,132,140,144,147,149,150,151,163,783,784],[69,132,140,144,147,149,150,151,163,789],[69,132,140,144,147,149,150,151,163,787,788],[69,132,140,144,147,149,150,151,163,283,1939,1941,2024,2032,2034,2035],[69,132,140,144,147,149,150,151,163,798],[69,132,140,144,147,149,150,151,163,791,792,793,794,795,796,797],[69,132,140,144,147,149,150,151,163,799,846,849],[69,132,140,144,147,149,150,151,163,801,804,805,809,838,843,845],[69,132,140,144,147,149,150,151,163,801],[69,132,140,144,147,149,150,151,163,800],[69,132,140,144,147,149,150,151,163,802,804],[69,132,140,144,147,149,150,151,163,803],[69,132,140,144,147,149,150,151,163,283,1939,1941,2029,2031],[69,132,140,144,147,149,150,151,163,809],[69,132,140,144,147,149,150,151,163,806,807,808],[69,132,140,144,147,149,150,151,163,810,837],[69,132,140,144,147,149,150,151,163,813,827,829,836],[69,132,140,144,147,149,150,151,163,283,1939,1941,2025,2027,2028],[69,132,140,144,147,149,150,151,163,813],[69,132,140,144,147,149,150,151,163,811,812],[69,132,140,144,147,149,150,151,163,814,826],[69,132,140,144,147,149,150,151,163,816],[69,132,140,144,147,149,150,151,163,815],[69,132,140,144,147,149,150,151,163,817],[69,132,140,144,147,149,150,151,163,816,818,823,825],[69,132,140,144,147,149,150,151,163,283,1939,1941,2026],[69,132,140,144,147,149,150,151,163,819,822],[69,132,140,144,147,149,150,151,163,820],[69,132,140,144,147,149,150,151,163,821],[69,132,140,144,147,149,150,151,163,824],[69,132,140,144,147,149,150,151,163,283,1939,1941,2030],[69,132,140,144,147,149,150,151,163,829],[69,132,140,144,147,149,150,151,163,828],[69,132,140,144,147,149,150,151,163,830,835],[69,132,140,144,147,149,150,151,163,832],[69,132,140,144,147,149,150,151,163,831],[69,132,140,144,147,149,150,151,163,833],[69,132,140,144,147,149,150,151,163,832,834],[69,132,140,144,147,149,150,151,163,1941,2033],[69,132,140,144,147,149,150,151,163,839,842],[69,132,140,144,147,149,150,151,163,840],[69,132,140,144,147,149,150,151,163,841],[69,132,140,144,147,149,150,151,163,844],[69,132,140,144,147,149,150,151,163,847,848],[69,132,140,144,147,149,150,151,163,854],[69,132,140,144,147,149,150,151,163,851,852,853],[69,132,140,144,147,149,150,151,163,855],[69,132,140,144,147,149,150,151,163,1941,2039,2040,2041,2042],[69,132,140,144,147,149,150,151,163,857,886],[69,132,140,144,147,149,150,151,163,283,1939,1941,2038],[69,132,140,144,147,149,150,151,163,859],[69,132,140,144,147,149,150,151,163,858],[69,132,140,144,147,149,150,151,163,860,866],[69,132,140,144,147,149,150,151,163,863,865],[69,132,140,144,147,149,150,151,163,863],[69,132,140,144,147,149,150,151,163,861,862],[69,132,140,144,147,149,150,151,163,864],[69,132,140,144,147,149,150,151,163,859,867,871,873,875,877,881,884,885],[69,132,140,144,147,149,150,151,163,871],[69,132,140,144,147,149,150,151,163,868,869,870],[69,132,140,144,147,149,150,151,163,872],[69,132,140,144,147,149,150,151,163,875],[69,132,140,144,147,149,150,151,163,874],[69,132,140,144,147,149,150,151,163,876],[69,132,140,144,147,149,150,151,163,881],[69,132,140,144,147,149,150,151,163,878,879,880],[69,132,140,144,147,149,150,151,163,882,884],[69,132,140,144,147,149,150,151,163,883],[69,132,140,144,147,149,150,151,163,889,890,891,892,893,894,895,896,897,898,899,900,901,902,903,904,905,906,907,908,909,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,930,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,975,976,977,978,979,980,981,982,983,984,985,986,987,988,989,990,991,992,993,994,995,996,997,998,999,1000,1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1011,1012,1013,1014,1015,1016,1017,1018,1019,1020,1021,1022,1023,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1117,1118,1119,1120,1121,1122,1123,1124,1125,1126,1127,1128,1129,1130,1131,1132,1133,1134,1135,1136,1137,1138,1139,1140,1141,1142,1143,1144,1145,1146,1147,1148,1149,1150,1151,1152,1153,1154,1155,1156,1157,1158,1159,1160,1161,1162,1163,1164,1165,1166,1167,1168,1169,1170,1171,1172,1173,1174,1175,1176,1177,1178,1179,1180,1181,1182,1183,1184,1185,1186,1187,1188,1189,1190,1191,1192,1193,1194,1195,1196,1197,1198,1199,1200,1201,1202,1203,1204,1205,1206,1207,1208,1209,1210,1211,1212,1213,1214,1215,1216,1217,1218,1219,1220,1221,1222,1223,1224,1225,1226,1227,1228,1229,1230,1231,1232,1233,1234,1235,1236,1237,1238,1239,1240,1241,1242,1243,1244,1245,1246,1247,1248,1249,1250,1251,1252,1253,1254,1255,1256,1257,1258,1259,1260,1261,1262,1263,1264,1265,1266,1267,1268,1269,1270,1271,1272,1273,1274,1275,1276,1277,1278,1279,1280,1281,1282,1283,1284,1285,1286,1287,1288,1289,1290,1291,1292,1293,1294,1295,1296,1297,1298,1299,1300,1301,1302,1303,1304,1305,1306,1307,1308,1309,1310,1311,1312,1313,1314,1315,1316,1317,1318,1319,1320,1321,1322,1323,1324,1325,1326,1327,1328,1329,1330,1331,1332,1333,1334,1335,1336,1337,1338,1339,1340,1341,1342,1343,1344,1345,1346,1347,1348,1349,1350,1351,1352,1353,1354,1355,1356,1357,1358,1359,1360,1361,1362,1363,1364,1365,1366,1367,1368,1369,1370,1371,1372,1373,1374,1375,1376,1377,1378,1379,1380,1381,1382,1383,1384,1385,1386,1387,1388,1389,1390,1391,1392,1393,1394,1395,1396,1397,1398,1399,1400,1401,1402,1403,1404,1405,1406,1407,1408,1409,1410,1411,1412,1413,1414,1415,1416,1417,1418,1419,1420,1421,1422,1423,1424,1425,1426,1427,1428,1429,1430,1431,1432,1433,1434,1435,1436,1437,1438,1439,1440,1441,1442,1443,1444,1445,1446,1447,1448,1449,1450,1451,1452,1453,1454,1455,1456,1457,1458,1459,1460,1461,1462,1463,1464,1465,1466,1467,1468,1469,1470,1471,1472,1473,1474,1475,1476,1477,1478,1479,1480,1481,1482,1483,1484,1485,1486,1487,1488,1489,1490,1491,1492,1493,1494,1495,1496,1497,1498,1499,1500,1501,1502,1503,1504,1505,1506,1507,1508,1509,1510,1511,1512,1513,1514,1515,1516,1517,1518,1519,1520,1521,1522,1523,1524,1525,1526,1527,1528,1529,1530,1531,1532,1533,1534,1535,1536,1537,1538,1539,1540,1541,1542,1543,1544,1545,1546,1547,1548,1549,1550,1551,1552,1553,1554,1555,1556,1557,1558,1559,1560,1561,1562,1563,1564,1565,1566,1567,1568,1569,1570,1571,1572,1573,1574,1575,1576,1577,1578,1579,1580,1581,1582,1583,1584,1585,1586,1587,1588,1589,1590,1591,1592,1593,1594,1595,1596,1597,1598,1599,1600,1601,1602,1603,1604,1605,1606,1607,1608,1609,1610,1611,1612,1613,1614,1615,1616,1617,1618,1619,1620,1621,1622,1623,1624,1625,1626,1627,1628,1629,1630,1631,1632,1633,1634,1635,1636,1637,1638,1639,1640,1641,1642,1643,1644,1645,1646,1647,1648,1649,1650,1651,1652,1653,1654,1655,1656,1657,1658,1659,1660,1661,1662,1663,1664,1665,1666,1667,1668,1669,1670,1671,1672,1673,1674,1675,1676,1677,1678,1679,1680,1681,1682,1683,1684,1685,1686,1687,1688,1689,1690,1691,1692,1693,1694,1695,1696,1697,1698,1699,1700,1701,1702,1703,1704,1705,1706,1707,1708,1709,1710,1711,1712,1713,1714,1715,1716,1717,1718,1719,1720,1721,1722,1723,1724,1725,1726,1727,1728,1729,1730,1731,1732,1733,1734,1735,1736,1737,1738,1739,1740,1741,1742,1743,1744,1745,1746,1747,1748,1749,1750,1751,1752,1753,1754,1755,1756,1757,1758,1759,1760,1761,1762,1763,1764,1765,1766,1767,1768,1769,1770,1771,1772,1773,1774,1775,1776,1777,1778,1779,1780,1781,1782,1783,1784,1785,1786,1787,1788,1789,1790,1791,1792,1793,1794,1795,1796,1797,1798,1799,1800,1801,1802,1803,1804,1805,1806,1807,1808,1809,1810,1811,1812,1813,1814,1815,1816,1817,1818,1819,1820,1821,1822,1823,1824,1825,1826,1827,1828,1829,1830,1831,1832,1833,1834,1835,1836,1837,1838,1839,1840,1841,1842,1843,1844,1845,1846,1847,1848,1849,1850,1851,1852,1853,1854,1855,1856,1857,1858,1859,1860,1861,1862,1863,1864,1865,1866,1867,1868,1869,1870,1871,1872,1873,1874,1875,1876,1877,1878,1879,1880,1881,1882,1883,1884,1885,1886,1887,1888,1889,1890,1891,1892,1893,1894,1895,1896,1897,1898,1899,1900,1901,1902,1903,1904,1905,1906,1907,1908,1909,1910,1911,1912,1913,1914,1915,1916,1917,1918,1919,1920,1921,1922,1923,1924,1925,1926,1927,1928,1929,1930,1931,1932,1933,1934,1935,1936,1937],[69,132,140,144,147,149,150,151,163,283,1940],[69,132,140,144,147,149,150,151,163,283,1941,1942,1943,1984,1993,1994,1995,1996,1998,2000,2001,2003,2004,2006,2013,2014,2015,2016,2018,2020,2021,2023,2036,2037,2043],[69,132,140,144,147,149,150,151,163,208],[69,132,140,144,147,149,150,151,163,211,212],[69,132,140,144,147,149,150,151,163,209,211,213,214],[69,132,140,144,147,149,150,151,163,208,209,210,211,212,213,215,216,217],[69,132,140,144,147,149,150,151,163,219],[69,132,140,144,147,149,150,151,163,219,220],[69,132,140,144,147,149,150,151,163,168,171],[69,132,140,144,147,149,150,151,163,221],[69,132,140,144,147,149,150,151,163,222,223],[69,132,140,144,147,149,150,151,163,218,221,224,225,227,276,278,282],[69,132,140,144,147,149,150,151,163,214],[69,132,140,144,147,149,150,151,163,226],[69,132,140,144,147,149,150,151,163,228],[69,132,140,144,147,149,150,151,163,229],[69,132,140,144,147,149,150,151,163,231],[69,132,140,144,147,149,150,151,163,233],[69,132,140,144,147,149,150,151,163,230,232,234,239,243,247,249,252,255,262,265,267,270,274],[69,132,140,144,147,149,150,151,163,235,246],[69,132,140,144,147,149,150,151,163,235,245],[69,132,140,144,147,149,150,151,163,248],[69,132,140,144,147,149,150,151,163,250,251],[69,132,140,144,147,149,150,151,163,228,237],[69,132,140,144,147,149,150,151,163,237,238],[69,132,140,144,147,149,150,151,163,244,245,253,254],[69,132,140,144,147,149,150,151,163,245],[69,132,140,144,147,149,150,151,163,228,236,239,243,244],[69,132,140,144,147,149,150,151,163,256,257,258,259,260,261],[69,132,140,144,147,149,150,151,163,263,264],[69,132,140,144,147,149,150,151,163,228,263],[69,132,140,144,147,149,150,151,163,228,243],[69,132,140,144,147,149,150,151,163,240,241,242],[69,132,140,144,147,149,150,151,163,266],[69,132,140,144,147,149,150,151,163,268,269],[69,132,140,144,147,149,150,151,163,228,268],[69,132,140,144,147,149,150,151,163,271,272,273],[69,132,140,144,147,149,150,151,163,255,271],[69,132,140,144,147,149,150,151,163,239,271,272],[69,132,140,144,147,149,150,151,163,228,275],[69,132,140,144,147,149,150,151,163,275],[69,132,140,144,147,149,150,151,163,277],[69,132,140,144,147,149,150,151,163,279,280,281],[69,132,140,144,147,149,150,151,163,284,285],[69,132,140,144,147,149,150,151,163,286,1939,1940,2054],[69,132,140,144,147,149,150,151,163,283,1998,2044,2045,2049],[69,132,140,144,147,149,150,151,163,2048,2049,2050,2051,2052,2053],[69,132,140,144,147,149,150,151,163,283,1939,1997,1998],[69,132,133,140,144,147,149,150,151,163,2046],[69,132,140,144,147,149,150,151,163,2046,2047],[69,132,140,144,147,149,150,151,163,2006,2047],[69,132,140,144,147,149,150,151,163,283,1939,2006,2048],[69,132,140,144,147,149,150,151,163,2037],[69,129,130,132,140,144,147,149,150,151,163],[69,131,132,140,144,147,149,150,151,163],[132,140,144,147,149,150,151,163],[69,132,140,144,147,149,150,151,163,171],[69,132,133,138,140,143,144,147,149,150,151,153,163,168,180],[69,132,133,134,140,143,144,147,149,150,151,163],[69,132,135,140,144,147,149,150,151,163,181],[69,132,136,137,140,144,147,149,150,151,154,163],[69,132,137,140,144,147,149,150,151,163,168,177],[69,132,138,140,143,144,147,149,150,151,153,163],[69,131,132,139,140,144,147,149,150,151,163],[69,132,140,141,144,147,149,150,151,163],[69,132,140,142,143,144,147,149,150,151,163],[69,131,132,140,143,144,147,149,150,151,163],[69,132,140,143,144,145,147,149,150,151,163,168,180],[69,132,140,143,144,145,147,149,150,151,163,168,171],[69,119,132,140,143,144,146,147,149,150,151,153,163,168,180],[69,132,140,143,144,146,147,149,150,151,153,163,168,177,180],[69,132,140,144,146,147,148,149,150,151,163,168,177,180],[67,68,69,70,71,72,73,74,75,76,77,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187],[69,132,140,143,144,147,149,150,151,163],[69,132,140,144,147,149,151,163],[69,132,140,144,147,149,150,151,152,163,180],[69,132,140,143,144,147,149,150,151,153,163,168],[69,132,140,144,147,149,150,151,154,163],[69,132,140,144,147,149,150,151,155,163],[69,132,140,143,144,147,149,150,151,158,163],[69,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187],[69,132,140,144,147,149,150,151,160,163],[69,132,140,144,147,149,150,151,161,163],[69,132,137,140,144,147,149,150,151,153,163,171],[69,132,140,143,144,147,149,150,151,163,164],[69,132,140,144,147,149,150,151,163,165,181,184],[69,132,140,143,144,147,149,150,151,163,168,170,171],[69,132,140,144,147,149,150,151,163,169,171],[69,132,140,144,147,149,150,151,163,171,181],[69,132,140,144,147,149,150,151,163,172],[69,129,132,140,144,147,149,150,151,163,168,174,180],[69,132,140,144,147,149,150,151,163,168,173],[69,132,140,143,144,147,149,150,151,163,175,176],[69,132,140,144,147,149,150,151,163,175,176],[69,132,137,140,144,147,149,150,151,153,163,168,177],[69,132,140,144,147,149,150,151,163,178],[69,132,140,144,147,149,150,151,153,163,179],[69,132,140,144,146,147,149,150,151,161,163,180],[69,132,140,144,147,149,150,151,163,181,182],[69,132,137,140,144,147,149,150,151,163,182],[69,132,140,144,147,149,150,151,163,168,183],[69,132,140,144,147,149,150,151,152,163,184],[69,132,140,144,147,149,150,151,163,185],[69,132,135,140,144,147,149,150,151,163],[69,132,137,140,144,147,149,150,151,163],[69,132,140,144,147,149,150,151,163,181],[69,119,132,140,144,147,149,150,151,163],[69,132,140,144,147,149,150,151,163,180],[69,132,140,144,147,149,150,151,163,186],[69,132,140,144,147,149,150,151,158,163],[69,132,140,144,147,149,150,151,163,176],[69,119,132,140,143,144,145,147,149,150,151,158,163,168,171,180,183,184,186],[69,132,140,144,147,149,150,151,163,168,187],[69,132,140,143,144,147,149,150,151,163,188,189],[69,132,140,144,147,149,150,151,163,190],[69,132,140,143,144,147,149,150,151,163,188],[69,84,87,90,91,132,140,144,147,149,150,151,163,180],[69,87,132,140,144,147,149,150,151,163,168,180],[69,87,91,132,140,144,147,149,150,151,163,180],[69,132,140,144,147,149,150,151,163,168],[69,81,132,140,144,147,149,150,151,163],[69,85,132,140,144,147,149,150,151,163],[69,83,84,87,132,140,144,147,149,150,151,163,180],[69,132,140,144,147,149,150,151,153,163,177],[69,132,140,144,147,149,150,151,163,188],[69,81,132,140,144,147,149,150,151,163,188],[69,83,87,132,140,144,147,149,150,151,153,163,180],[69,78,79,80,82,86,132,140,143,144,147,149,150,151,163,168,180],[69,87,96,104,132,140,144,147,149,150,151,163],[69,79,85,132,140,144,147,149,150,151,163],[69,87,113,114,132,140,144,147,149,150,151,163],[69,79,82,87,132,140,144,147,149,150,151,163,171,180,188],[69,87,132,140,144,147,149,150,151,163],[69,83,87,132,140,144,147,149,150,151,163,180],[69,78,132,140,144,147,149,150,151,163],[69,81,82,83,85,86,87,88,89,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,114,115,116,117,118,132,140,144,147,149,150,151,163],[69,87,106,109,132,140,144,147,149,150,151,163],[69,87,96,97,98,132,140,144,147,149,150,151,163],[69,85,87,97,99,132,140,144,147,149,150,151,163],[69,86,132,140,144,147,149,150,151,163],[69,79,81,87,132,140,144,147,149,150,151,163],[69,87,91,97,99,132,140,144,147,149,150,151,163],[69,91,132,140,144,147,149,150,151,163],[69,85,87,90,132,140,144,147,149,150,151,163,180],[69,79,83,87,96,132,140,144,147,149,150,151,163],[69,87,106,132,140,144,147,149,150,151,163],[69,99,132,140,144,147,149,150,151,163],[69,81,87,113,132,140,144,147,149,150,151,163,171,186,188],[66,69,132,140,144,147,149,150,151,163,192,193,197,200,206,207,2056,2057,2061],[69,132,140,144,147,149,150,151,155,163,192],[69,132,140,144,147,149,150,151,155,163,192,195,196],[69,132,133,140,144,147,149,150,151,163,195],[69,132,133,140,144,147,149,150,151,163],[69,132,140,144,147,149,150,151,155,163,194,195,198,199],[69,132,140,144,147,149,150,151,155,163,195,202,2062],[69,132,140,144,147,149,150,151,163,194,195],[69,132,140,144,147,149,150,151,155,163,194,195,196,198,199,201,202,203,204,2062],[69,132,140,144,147,149,150,151,163,2055],[69,132,140,144,147,149,150,151,163,195]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2e80ee7a49e8ac312cc11b77f1475804bee36b3b2bc896bead8b6e1266befb43","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7a3c8b952931daebdfc7a2897c53c0a1c73624593fa070e46bd537e64dcd20a","affectsGlobalScope":true,"impliedFormat":1},{"version":"80e18897e5884b6723488d4f5652167e7bb5024f946743134ecc4aa4ee731f89","affectsGlobalScope":true,"impliedFormat":1},{"version":"cd034f499c6cdca722b60c04b5b1b78e058487a7085a8e0d6fb50809947ee573","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"8cdf8847677ac7d20486e54dd3fcf09eda95812ac8ace44b4418da1bbbab6eb8","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"51ad4c928303041605b4d7ae32e0c1ee387d43a24cd6f1ebf4a2699e1076d4fa","affectsGlobalScope":true,"impliedFormat":1},{"version":"196cb558a13d4533a5163286f30b0509ce0210e4b316c56c38d4c0fd2fb38405","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"3cbad9a1ba4453443026ed38e4b8be018abb26565fa7c944376463ad9df07c41","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"d153a11543fd884b596587ccd97aebbeed950b26933ee000f94009f1ab142848","affectsGlobalScope":true,"impliedFormat":1},{"version":"0ccdaa19852d25ecd84eec365c3bfa16e7859cadecf6e9ca6d0dbbbee439743f","affectsGlobalScope":true,"impliedFormat":1},{"version":"438b41419b1df9f1fbe33b5e1b18f5853432be205991d1b19f5b7f351675541e","affectsGlobalScope":true,"impliedFormat":1},{"version":"096116f8fedc1765d5bd6ef360c257b4a9048e5415054b3bf3c41b07f8951b0b","affectsGlobalScope":true,"impliedFormat":1},{"version":"e5e01375c9e124a83b52ee4b3244ed1a4d214a6cfb54ac73e164a823a4a7860a","affectsGlobalScope":true,"impliedFormat":1},{"version":"f90ae2bbce1505e67f2f6502392e318f5714bae82d2d969185c4a6cecc8af2fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"4b58e207b93a8f1c88bbf2a95ddc686ac83962b13830fe8ad3f404ffc7051fb4","affectsGlobalScope":true,"impliedFormat":1},{"version":"1fefabcb2b06736a66d2904074d56268753654805e829989a46a0161cd8412c5","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"c18a99f01eb788d849ad032b31cafd49de0b19e083fe775370834c5675d7df8e","affectsGlobalScope":true,"impliedFormat":1},{"version":"5247874c2a23b9a62d178ae84f2db6a1d54e6c9a2e7e057e178cc5eea13757fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"cdcf9ea426ad970f96ac930cd176d5c69c6c24eebd9fc580e1572d6c6a88f62c","impliedFormat":1},{"version":"23cd712e2ce083d68afe69224587438e5914b457b8acf87073c22494d706a3d0","impliedFormat":1},{"version":"156a859e21ef3244d13afeeba4e49760a6afa035c149dda52f0c45ea8903b338","impliedFormat":1},{"version":"10ec5e82144dfac6f04fa5d1d6c11763b3e4dbbac6d99101427219ab3e2ae887","impliedFormat":1},{"version":"615754924717c0b1e293e083b83503c0a872717ad5aa60ed7f1a699eb1b4ea5c","impliedFormat":1},{"version":"074de5b2fdead0165a2757e3aaef20f27a6347b1c36adea27d51456795b37682","impliedFormat":1},{"version":"68834d631c8838c715f225509cfc3927913b9cc7a4870460b5b60c8dbdb99baf","impliedFormat":1},{"version":"24371e69a38fc33e268d4a8716dbcda430d6c2c414a99ff9669239c4b8f40dea","impliedFormat":1},{"version":"ccab02f3920fc75c01174c47fcf67882a11daf16baf9e81701d0a94636e94556","impliedFormat":1},{"version":"3e11fce78ad8c0e1d1db4ba5f0652285509be3acdd519529bc8fcef85f7dafd9","impliedFormat":1},{"version":"ea6bc8de8b59f90a7a3960005fd01988f98fd0784e14bc6922dde2e93305ec7d","impliedFormat":1},{"version":"36107995674b29284a115e21a0618c4c2751b32a8766dd4cb3ba740308b16d59","impliedFormat":1},{"version":"914a0ae30d96d71915fc519ccb4efbf2b62c0ddfb3a3fc6129151076bc01dc60","impliedFormat":1},{"version":"9c32412007b5662fd34a8eb04292fb5314ec370d7016d1c2fb8aa193c807fe22","impliedFormat":1},{"version":"7fd1b31fd35876b0aa650811c25ec2c97a3c6387e5473eb18004bed86cdd76b6","impliedFormat":1},{"version":"4d327f7d72ad0918275cea3eee49a6a8dc8114ae1d5b7f3f5d0774de75f7439a","impliedFormat":1},{"version":"6ebe8ebb8659aaa9d1acbf3710d7dae3e923e97610238b9511c25dc39023a166","impliedFormat":1},{"version":"e85d7f8068f6a26710bff0cc8c0fc5e47f71089c3780fbede05857331d2ddec9","impliedFormat":1},{"version":"7befaf0e76b5671be1d47b77fcc65f2b0aad91cc26529df1904f4a7c46d216e9","impliedFormat":1},{"version":"0a60a292b89ca7218b8616f78e5bbd1c96b87e048849469cccb4355e98af959a","impliedFormat":1},{"version":"0b6e25234b4eec6ed96ab138d96eb70b135690d7dd01f3dd8a8ab291c35a683a","impliedFormat":1},{"version":"9666f2f84b985b62400d2e5ab0adae9ff44de9b2a34803c2c5bd3c8325b17dc0","impliedFormat":1},{"version":"40cd35c95e9cf22cfa5bd84e96408b6fcbca55295f4ff822390abb11afbc3dca","impliedFormat":1},{"version":"b1616b8959bf557feb16369c6124a97a0e74ed6f49d1df73bb4b9ddf68acf3f3","impliedFormat":1},{"version":"5b03a034c72146b61573aab280f295b015b9168470f2df05f6080a2122f9b4df","impliedFormat":1},{"version":"40b463c6766ca1b689bfcc46d26b5e295954f32ad43e37ee6953c0a677e4ae2b","impliedFormat":1},{"version":"249b9cab7f5d628b71308c7d9bb0a808b50b091e640ba3ed6e2d0516f4a8d91d","impliedFormat":1},{"version":"80aae6afc67faa5ac0b32b5b8bc8cc9f7fa299cff15cf09cc2e11fd28c6ae29e","impliedFormat":1},{"version":"f473cd2288991ff3221165dcf73cd5d24da30391f87e85b3dd4d0450c787a391","impliedFormat":1},{"version":"499e5b055a5aba1e1998f7311a6c441a369831c70905cc565ceac93c28083d53","impliedFormat":1},{"version":"8aee8b6d4f9f62cf3776cda1305fb18763e2aade7e13cea5bbe699112df85214","impliedFormat":1},{"version":"c63b9ada8c72f95aac5db92aea07e5e87ec810353cdf63b2d78f49a58662cf6c","impliedFormat":1},{"version":"1cc2a09e1a61a5222d4174ab358a9f9de5e906afe79dbf7363d871a7edda3955","impliedFormat":1},{"version":"5d0375ca7310efb77e3ef18d068d53784faf62705e0ad04569597ae0e755c401","impliedFormat":1},{"version":"59af37caec41ecf7b2e76059c9672a49e682c1a2aa6f9d7dc78878f53aa284d6","impliedFormat":1},{"version":"addf417b9eb3f938fddf8d81e96393a165e4be0d4a8b6402292f9c634b1cb00d","impliedFormat":1},{"version":"b64d4d1c5f877f9c666e98e833f0205edb9384acc46e98a1fef344f64d6aba44","impliedFormat":1},{"version":"adf27937dba6af9f08a68c5b1d3fce0ca7d4b960c57e6d6c844e7d1a8e53adae","impliedFormat":1},{"version":"12950411eeab8563b349cb7959543d92d8d02c289ed893d78499a19becb5a8cc","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"c9381908473a1c92cb8c516b184e75f4d226dad95c3a85a5af35f670064d9a2f","impliedFormat":1},{"version":"c3f5289820990ab66b70c7fb5b63cb674001009ff84b13de40619619a9c8175f","affectsGlobalScope":true,"impliedFormat":1},{"version":"b3275d55fac10b799c9546804126239baf020d220136163f763b55a74e50e750","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa68a0a3b7cb32c00e39ee3cd31f8f15b80cac97dce51b6ee7fc14a1e8deb30b","affectsGlobalScope":true,"impliedFormat":1},{"version":"1cf059eaf468efcc649f8cf6075d3cb98e9a35a0fe9c44419ec3d2f5428d7123","affectsGlobalScope":true,"impliedFormat":1},{"version":"6c36e755bced82df7fb6ce8169265d0a7bb046ab4e2cb6d0da0cb72b22033e89","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"7a93de4ff8a63bafe62ba86b89af1df0ccb5e40bb85b0c67d6bbcfdcf96bf3d4","affectsGlobalScope":true,"impliedFormat":1},{"version":"90e85f9bc549dfe2b5749b45fe734144e96cd5d04b38eae244028794e142a77e","affectsGlobalScope":true,"impliedFormat":1},{"version":"e0a5deeb610b2a50a6350bd23df6490036a1773a8a71d70f2f9549ab009e67ee","affectsGlobalScope":true,"impliedFormat":1},{"version":"3fad5618174d74a34ee006406d4eb37e8d07dd62eb1315dbf52f48d31a337547","impliedFormat":1},{"version":"7e49f52a159435fc8df4de9dc377ef5860732ca2dc9efec1640531d3cf5da7a3","impliedFormat":1},{"version":"dd4bde4bdc2e5394aed6855e98cf135dfdf5dd6468cad842e03116d31bbcc9bc","impliedFormat":1},{"version":"4d4e879009a84a47c05350b8dca823036ba3a29a3038efed1be76c9f81e45edf","affectsGlobalScope":true,"impliedFormat":1},{"version":"8b50a819485ffe0d237bf0d131e92178d14d11e2aa873d73615a9ec578b341f5","impliedFormat":1},{"version":"9ba13b47cb450a438e3076c4a3f6afb9dc85e17eae50f26d4b2d72c0688c9251","impliedFormat":1},{"version":"b64cd4401633ea4ecadfd700ddc8323a13b63b106ac7127c1d2726f32424622c","impliedFormat":1},{"version":"37c6e5fe5715814412b43cc9b50b24c67a63c4e04e753e0d1305970d65417a60","impliedFormat":1},{"version":"1d024184fb57c58c5c91823f9d10b4915a4867b7934e89115fd0d861a9df27c8","impliedFormat":1},{"version":"ee0e4946247f842c6dd483cbb60a5e6b484fee07996e3a7bc7343dfb68a04c5d","impliedFormat":1},{"version":"ef051f42b7e0ef5ca04552f54c4552eac84099d64b6c5ad0ef4033574b6035b8","impliedFormat":1},{"version":"853a43154f1d01b0173d9cbd74063507ece57170bad7a3b68f3fa1229ad0a92f","impliedFormat":1},{"version":"56231e3c39a031bfb0afb797690b20ed4537670c93c0318b72d5180833d98b72","impliedFormat":1},{"version":"5cc7c39031bfd8b00ad58f32143d59eb6ffc24f5d41a20931269011dccd36c5e","impliedFormat":1},{"version":"12d602a8fe4c2f2ba4f7804f5eda8ba07e0c83bf5cf0cda8baffa2e9967bfb77","affectsGlobalScope":true,"impliedFormat":1},{"version":"a856ab781967b62b288dfd85b860bef0e62f005ed4b1b8fa25c53ce17856acaf","impliedFormat":1},{"version":"cc25940cfb27aa538e60d465f98bb5068d4d7d33131861ace43f04fe6947d68f","impliedFormat":1},{"version":"8db46b61a690f15b245cf16270db044dc047dce9f93b103a59f50262f677ea1f","impliedFormat":1},{"version":"01ff95aa1443e3f7248974e5a771f513cb2ac158c8898f470a1792f817bee497","impliedFormat":1},{"version":"757227c8b345c57d76f7f0e3bbad7a91ffca23f1b2547cbed9e10025816c9cb7","impliedFormat":1},{"version":"959d0327c96dd9bb5521f3ed6af0c435996504cc8dd46baa8e12cb3b3518cef1","impliedFormat":1},{"version":"e1c1a0b4d1ead0de9eca52203aeb1f771f21e6238d6fcd15aa56ac2a02f1b7bf","impliedFormat":1},{"version":"101f482fd48cb4c7c0468dcc6d62c843d842977aea6235644b1edd05e81fbf22","impliedFormat":1},{"version":"266bee0a41e9c3ba335583e21e9277ae03822402cf5e8e1d99f5196853613b98","affectsGlobalScope":true,"impliedFormat":1},{"version":"386606f8a297988535cb1401959041cfa7f59d54b8a9ed09738e65c98684c976","impliedFormat":1},{"version":"3ef397f12387eff17f550bc484ea7c27d21d43816bbe609d495107f44b97e933","impliedFormat":1},{"version":"1023282e2ba810bc07905d3668349fbd37a26411f0c8f94a70ef3c05fe523fcf","impliedFormat":1},{"version":"b214ebcf76c51b115453f69729ee8aa7b7f8eccdae2a922b568a45c2d7ff52f7","impliedFormat":1},{"version":"429c9cdfa7d126255779efd7e6d9057ced2d69c81859bbab32073bad52e9ba76","impliedFormat":1},{"version":"e236b5eba291f51bdf32c231673e6cab81b5410850e61f51a7a524dddadc0f95","impliedFormat":1},{"version":"ce8653341224f8b45ff46d2a06f2cacb96f841f768a886c9d8dd8ec0878b11bd","affectsGlobalScope":true,"impliedFormat":1},{"version":"7f2c62938251b45715fd2a9887060ec4fbc8724727029d1cbce373747252bdd7","impliedFormat":1},{"version":"e3ace08b6bbd84655d41e244677b474fd995923ffef7149ddb68af8848b60b05","impliedFormat":1},{"version":"132580b0e86c48fab152bab850fc57a4b74fe915c8958d2ccb052b809a44b61c","impliedFormat":1},{"version":"90a278f5fab7557e69e97056c0841adf269c42697194f0bd5c5e69152637d4b3","impliedFormat":1},{"version":"69c9a5a9392e8564bd81116e1ed93b13205201fb44cb35a7fde8c9f9e21c4b23","impliedFormat":1},{"version":"5f8fc37f8434691ffac1bfd8fc2634647da2c0e84253ab5d2dd19a7718915b35","impliedFormat":1},{"version":"5981c2340fd8b076cae8efbae818d42c11ffc615994cb060b1cd390795f1be2b","impliedFormat":1},{"version":"f263485c9ca90df9fe7bb3a906db9701997dc6cae86ace1f8106ac8d2f7f677b","impliedFormat":1},{"version":"1edcf2f36fc332615846bde6dcc71a8fe526065505bc5e3dcfd65a14becdf698","affectsGlobalScope":true,"impliedFormat":1},{"version":"0250da3eb85c99624f974e77ef355cdf86f43980251bc371475c2b397ba55bcd","impliedFormat":1},{"version":"f1c93e046fb3d9b7f8249629f4b63dc068dd839b824dd0aa39a5e68476dc9420","impliedFormat":1},{"version":"3d3a5f27ffbc06c885dd4d5f9ee20de61faf877fe2c3a7051c4825903d9a7fdc","impliedFormat":1},{"version":"12806f9f085598ef930edaf2467a5fa1789a878fba077cd27e85dc5851e11834","impliedFormat":1},{"version":"1dbca38aa4b0db1f4f9e6edacc2780af7e028b733d2a98dd3598cd235ca0c97d","impliedFormat":1},{"version":"a43fe41c33d0a192a0ecaf9b92e87bef3709c9972e6d53c42c49251ccb962d69","impliedFormat":1},{"version":"a177959203c017fad3ecc4f3d96c8757a840957a4959a3ae00dab9d35961ca6c","affectsGlobalScope":true,"impliedFormat":1},{"version":"6fc727ccf9b36e257ff982ea0badeffbfc2c151802f741bddff00c6af3b784cf","impliedFormat":1},{"version":"19143c930aef7ccf248549f3e78992f2f1049118ec5d4622e95025057d8e392b","impliedFormat":1},{"version":"4844a4c9b4b1e812b257676ed8a80b3f3be0e29bf05e742cc2ea9c3c6865e6c6","impliedFormat":1},{"version":"064878a60367e0407c42fb7ba02a2ea4d83257357dc20088e549bd4d89433e9c","impliedFormat":1},{"version":"cca8917838a876e2d7016c9b6af57cbf11fdf903c5fdd8e613fa31840b2957bf","impliedFormat":1},{"version":"d91ae55e4282c22b9c21bc26bd3ef637d3fe132507b10529ae68bf76f5de785b","impliedFormat":1},{"version":"b484ec11ba00e3a2235562a41898d55372ccabe607986c6fa4f4aba72093749f","impliedFormat":1},{"version":"7e8a671604329e178bb479c8f387715ebd40a091fc4a7552a0a75c2f3a21c65c","impliedFormat":1},{"version":"41ef7992c555671a8fe54db302788adefa191ded810a50329b79d20a6772d14c","impliedFormat":1},{"version":"041a7781b9127ab568d2cdcce62c58fdea7c7407f40b8c50045d7866a2727130","impliedFormat":1},{"version":"4c5e90ddbcd177ad3f2ffc909ae217c87820f1e968f6959e4b6ba38a8cec935e","impliedFormat":1},{"version":"b70dd9a44e1ac42f030bb12e7d79117eac7cb74170d72d381a1e7913320af23a","impliedFormat":1},{"version":"55cdbeebe76a1fa18bbd7e7bf73350a2173926bd3085bb050cf5a5397025ee4e","impliedFormat":1},{"version":"eac647a94fb1f09789e12dfecb52dcd678d05159a4796b4e415aa15892f3b103","impliedFormat":1},{"version":"0744807211f8cd16343fb1a796f53a8f7b7f95d4bd278c48febf657679bf28e6","impliedFormat":1},{"version":"f3e4471dee615dbcec8eeda782b8381f0a4a3ef18db63f0e3a90eaca5a6afca9","impliedFormat":1},{"version":"94a0fcd4f758fe669bc78f1c9146f5fc07879783787364ca89ce486b0cf37920","affectsGlobalScope":true,"impliedFormat":1},{"version":"58b52fa0e12bbf236bd5a505aea489a4829d3acab752a20cddad7a506d27aa67","signature":"d5480203bd3be8d382825090a50e9df6a770121723ee785f54691e7bd129f23b","impliedFormat":1},{"version":"54cd69c604e5a7d2e28a6dbdb2422f1d634ee6b037ce84e79924b4310fc50191","impliedFormat":1},{"version":"f9cdeb314ae8e1b58e8d37db1c1a1f00ca62c1790e1881113d41e62dbf12d159","impliedFormat":1},{"version":"ba6aa78375568ff7ba83768f3cf2c237670288a1de47476f27b9c84b5901da1b","impliedFormat":1},{"version":"c79f851f1e6c0a1438ab49d43bd390ee334342b9404ada6137c6aa511cda4bec","signature":"037d7bd768e759e211ad7e4d355f9d8480ad87aa159ad6cc6a7bb147e5779fdd","impliedFormat":1},{"version":"317976fb2b6870c9f12aaa0f2732e03b4664bf6081229b8dc6f02cfcdb31ed2a","signature":"0c7153108176d6c68d5b0c89dff0412c286dc5c632ee8896006d24b9289fbcab","impliedFormat":1},{"version":"738701f595a46acde6f49c4ea80dd03f94fa555cc960311451809c0e189d2282","impliedFormat":1},{"version":"4b3476e92039ed23faf1971b0b2712cf3da1db39ef71ff0d1413e4ddc3964a74","signature":"bdd6cdff0310d7b839a162e883c5f2cf7b3e5e9463316c366ba0905b3eb1f846","impliedFormat":1},{"version":"86f2c3e51bbbbea3c5a13e331ba2b3b74d6ad70fa1f676f64b2986d19572c185","signature":"9a10f8039bbd94a361b46c7c46397cfbc4f980666208591a4241fd387b11f66d","impliedFormat":1},{"version":"01c0ff6a454ded6e138251e9858ffce9c5272c60943347b2e18184abe0fb99b2","signature":"257632bfe53d6444928e414d344d04109db3a991840708f2b44164614e7f8c9b","impliedFormat":1},{"version":"40d0f497b34368632224ba99de740d7addf8a73e8e0c0433f26aadb4a9d2e2fa","signature":"7cb4da9e04e1d24d024790bdb7cb4ae548a4eca82cb7d9076413e868aa2515b7","impliedFormat":1},{"version":"508469f47005b8771109e7929292fbc08c4697224077c982ca078e87c22948cc","impliedFormat":1},{"version":"97a9ddf781a25e4b67c52a6ab069b3ed9a7f8c3878d7a908824a8797bebe8c0e","impliedFormat":1},{"version":"b43695bdd8dd20bf5295ff709ac69b7045fcf2678342e37cbc68fac3f0ff91d6","signature":"59409b5f451fcc1a3fc02b5bbb8936f8845eb477ba83054478f37e9a0647973d","impliedFormat":1},{"version":"8d259c23cb1d027f703de4ec96a8f56e6e0432818b5e46938ef990dda8608932","signature":"e60fae741cb3f91c5e6a61834a14429956ab7abeeb966a5348758f92bed58e17","impliedFormat":1},{"version":"17b1c252f52574032c68966f7b1bbd9534d0e4ee564cf6516a8984fea037b37e","impliedFormat":1},{"version":"cc1191a3791a528db581cec5e2a13c129b3c0433969f466663707b99c142a511","impliedFormat":1},{"version":"282c2939ae80bba26a45a1fc7ed914cb2123f2fc51a8919256f4b37246a4579d","impliedFormat":1},{"version":"ecdc15de1f6e419f31c09d53332f6aebeba3f7198fddd6f1482206d5cb44d4f7","impliedFormat":1},{"version":"deea7574d96c41452a00ef2f5b26f460606f0c8a60335f3698bffe92fd740d34","impliedFormat":1},{"version":"4c028c4da6e0062b63b5f441d75db46bc641926f7f1581b8b821b3855f4e6f42","impliedFormat":1},{"version":"521e5374d5b3eb5d5dfdc5c13835e1936f5d240bf919f0090610e61cd20c9801","impliedFormat":1},{"version":"19434b38cb6f857c758e13c9ecfbbbe86be9da6fb30cf5755419005500468549","impliedFormat":1},{"version":"22359a932ca9834f40cbf2df2246f5cf61c6813f8f231190c8fe6f12236fb6ac","impliedFormat":1},{"version":"53cd752433eae9c96df50b521e087897a1ef9ddd1aa43b9924b499c8230ae591","impliedFormat":1},{"version":"dadd75d76f03a049fb02e3ebb6a3730d1666f45d2f88af4c062d97d503aa934f","impliedFormat":1},{"version":"5d87f1649941cb667b8627dd6326a3fa056f3f806fd869a5e2427510ab07893f","impliedFormat":1},{"version":"e6e89e9fa8b4593a440a648559a14621fcab133b477074e7bdb57b76ec78a09f","impliedFormat":1},{"version":"f4a19161e0431a80acf995461643873adfc03bf81f77203911709d8847c8cf9c","impliedFormat":1},{"version":"6115e47e2dc0ba9e9ca1081559c9cd2a0589b9c909be894c248b56581ef96c4f","impliedFormat":1},{"version":"2370e835f162485c7d53a67a602e7bdfe955a6455c2bd974d04e969acf9ddbe7","impliedFormat":1},{"version":"8bf50f1c1c148498c98b5019c501fd584a2bb1617545e6f7ebf391376776e5d3","impliedFormat":1},{"version":"c967d7ca4d2ecc37d4a12833fad823f02e935542f6be307214ea7d2a9af02995","impliedFormat":1},{"version":"7c466c18cf421bd1f032c6e824331e785d6e3aba00e8c6b131d43695356e1c09","impliedFormat":1},{"version":"8d484a8d584ce3f409e58825bf6ea4913aa68696ffb7c34dd76486762fa6823b","impliedFormat":1},{"version":"4690245111b1f1a805f4e232cb13fbbb88e09ce31c2ef4c6e4ee49fb9c8e5ef9","impliedFormat":1},{"version":"3b1d991d81a4344f5c1bd7ec5200b009cbcd0934bd2ac483fbc84922b0efc597","impliedFormat":1},{"version":"85583f9825ec8635deee638d078e2f9dc37940404eb4f4c9a492e4978ac3e5f4","impliedFormat":1},{"version":"36d888203713fb31943748c4b596643af32093dfe50e8d7237cefc1ebf9a5d34","impliedFormat":1},{"version":"732cf95e13a7e3af09f293dc8bf0e85d81a1cfde6bf7d32175ccb357b8070a69","impliedFormat":1},{"version":"44034e382559b53bf1f54c49d3dbe523fe1079a3fbd6a4a13a790fac3f1a1c28","impliedFormat":1},{"version":"58372ae8d89a39a7639c559fb4dba6ae0e09495e154e3b01c36b242bcd4d7e94","impliedFormat":1},{"version":"ca7f05daa08231776db869c458d7d1260e22308cb76c779a1ca745508ce84bf8","impliedFormat":1},{"version":"767cf0a4c59d85e960a57dafd8f2fcfe2680e07dae3a58c9322840ac1295ec2a","impliedFormat":1},{"version":"6f54ac8a1bc3513f4c490fdaecb1f75ccce9aea11c543f7217d30364f5ee7091","impliedFormat":1},{"version":"35a2cf0a4c48322eac026bdf71304719ce65678c13da32b9a2c6aea1160e073e","impliedFormat":1},{"version":"4b61abc402fdc73e6ecb0c1eac701ba90aa3ceb3d0b3c5e63c596554070c9f2e","impliedFormat":1},{"version":"3ef75df5bf8e9a5973bc4c5b6814ce64e4f21415ac3c0cff2855ae2c1444e3ea","impliedFormat":1},{"version":"bf3a4b2e1dee3fc51db6305418d3153197a619daca45c78a70ec5da9c9a0ceba","impliedFormat":1},{"version":"f0ce59a61ae03eb43fcd9af5f90ebb55c8fe5626a35a9d6517c5fb2f7a322e9f","impliedFormat":1},{"version":"73314ae5246e6927bf169623bd0950b02f540c2760e438bcea3feb9fd3c3040b","impliedFormat":1},{"version":"2ee9c3a1e25fb85dfd06978a4f0a75aae61e0aa98f3fbe332d7b821a0b73cccd","impliedFormat":1},{"version":"1bbb4849a6cac5dd805f83da8f15c1d968d8be63206f10d8fe62436e1beffec7","impliedFormat":1},{"version":"df1842ed0088bd9e95addbcb2217c2ec4361ebc697b8be88143d3b508a8ead7e","impliedFormat":1},{"version":"6abfc76b7f47310f7ad4a51710f174b05880ccccc387419cac7514781ff3915b","impliedFormat":1},{"version":"5f6cc9e49ce43f1ea32d2f32d67f6f4e44e19699926d9615536a599e2fcf86a4","impliedFormat":1},{"version":"3967b04c8f9958b7c4420a62b593c2bd21f0654713fddea6a9204d83d91fc2e0","impliedFormat":1},{"version":"c730947c21f4ff623a85e1af8f6e191f3333716e77ccf3971db5afeff83a0b6d","impliedFormat":1},{"version":"8061a394462fc9f9bd3016b52c5c78c84c2f6b57507705efba9d447d313a23bf","impliedFormat":1},{"version":"cbf0ff639996395b234b263f47a0b9d631e094c9cc2bf4180957679cb6b7c64d","impliedFormat":1},{"version":"1e5ea2dd6ed99310bbba06de61f824cdff314206c1678112934ae718389d0304","impliedFormat":1},{"version":"843454e39a80a27866a0363bf2379b4309b3e6fd98c5b49a7f198048e6b25970","impliedFormat":1},{"version":"86a7438695131d138b0e8640d447282820898aa3239920358d8e5cd37e5ad58a","impliedFormat":1},{"version":"ebc8158512fbbdfe5cc4efffd4fef8e86e5fbe0bfcc42ccc0d2433a8c823fb47","impliedFormat":1},{"version":"5f06ab692937cec79dc302b5917542d98ba3c380ea2f711a1598f7e5bf952454","impliedFormat":1},{"version":"ca1efb8c9831f6b82ecbafb75a4618be40c62973ba61bfc58745768204674b97","impliedFormat":1},{"version":"21f1a0d3a642f854e90e5578dccf322d51537491e14c3040b761eb2e7a8799bc","impliedFormat":1},{"version":"0c1ba538539759846b7c6a0048d9d5b7a15b65998746e3fc37a896163ac20e2a","impliedFormat":1},{"version":"4d0a48e89a574edb61bb99dd50b9868a21c5c2569a11e7baac5b154e2615b770","impliedFormat":1},{"version":"814635244a606e656cd34986ba86258febd5eac6a1142487b529845f7ebe3da7","impliedFormat":1},{"version":"12c8967461df7de15d651f425a5e63eaf834a4be72e9159e0c29749365cc95e9","impliedFormat":1},{"version":"f81ccfc19741ebef8c54776b179a3b1a4b106e11598eecff1efae105c75fdeec","impliedFormat":1},{"version":"0ea5dd72eb2c9abd91a5606c1ba661bc1e8fa556895319303ce1fac7f333cdd1","impliedFormat":1},{"version":"1655b84587db66ff2f8380c574c535d9e023538b71a8052579a9358a9d2b2219","impliedFormat":1},{"version":"abd41e7ea7d4813c0fbb189689838cd4603766518540311d84b5670458a076d7","impliedFormat":1},{"version":"817506b0bc88360dcfaf7b1745b2cdec2103adfba1528782644a9e9e000b553c","impliedFormat":1},{"version":"41d07cdf8e08570bb5144ba157e58499940789c3de4c387dd60555f853705b5b","impliedFormat":1},{"version":"30f001d022306ef18d4ac3bba4f8535362fb8eef5acb305d5cba0204371358c0","impliedFormat":1},{"version":"55a541a20fa293d5571b3673b72e82fa8b0f085242b2077a674f061982d5bbc0","impliedFormat":1},{"version":"19896a1c0a1d70ab46991c084648941a5cf5f24a7a51b0bfc876deb9e8485c97","impliedFormat":1},{"version":"a2b3391ed21e14ea0daadd64c8601ccc298453d37ade557fe934ed7a8ff08413","impliedFormat":1},{"version":"874db7a776cee4842625e8ed70498cae4dc6aa1e9f694408d09ff6211debd442","impliedFormat":1},{"version":"deacf14aff5357484fe00f5c9d85622acfe76e194b2f41134637b1d4ddfb104c","impliedFormat":1},{"version":"5cb6b032fce82bf31b8ab04b473442d954c11fefcd1056aba03dd114cff64da5","impliedFormat":1},{"version":"f2fd2eb462c669032669696123d0ab39ed880d5c3c74c08b64cff5703980e129","impliedFormat":1},{"version":"30db2969625406bb0857aed0af80596d354bcf1802e0af1d133ee73197518f66","impliedFormat":1},{"version":"57b1b87351e0cbff1eb206942616b26f046f07ac2e6c1267ad3680a4390d4b7f","impliedFormat":1},{"version":"92fdc2b53bca735fc61d03c20ecd89b52a50af9d1072ff5cb087156f31ba76eb","impliedFormat":1},{"version":"2300a29bd72ffebad1f7f57e7f14424682b544cf77244a1e282ea321c156a48f","impliedFormat":1},{"version":"f83e56156471b9df7ac814b88cb59b2d56dfc9f5e34f0afcd39ff82e5eacd5e0","impliedFormat":1},{"version":"0f8ab933e899d946ff9e1848cd4b8986ca46240c36f2c13bd4962b2e033f8a0d","impliedFormat":1},{"version":"a808a1cf63af9ca0bdc6cf25772384e8bfcd55214ed7dbae19fcd8d963b06358","impliedFormat":1},{"version":"0cd7e5106ac88ee02b747690ae872b2876def662d8838177f7c79715611b774f","impliedFormat":1},{"version":"de393231996dc66d208ebdbd0d04b86e1bfdd0d59a0fcda388444300f8122024","impliedFormat":1},{"version":"007a42e2adaa415b117a5fb23ac237bfb9b0d6c8cd0862872d4ae5c31c41eb2f","impliedFormat":1},{"version":"48aa5b41c5f663b3586fbb15effab12b2085450743eebf861ef1c3983ed6c5af","impliedFormat":1},{"version":"f194ec037eee362977a7099304aeb8fae29a54e103015012bcccb60f62abc369","impliedFormat":1},{"version":"6d365ca48a0d32aa03b18163fbf93e9be0888414b32a2aeb64c6185301c71595","impliedFormat":1},{"version":"8729ccf1f42b66cbed3c571c4556d2b6b1cb82a3f9e8c2aab00d1167c27e4deb","impliedFormat":1},{"version":"f52ed5a38799848090cdeb6d6e296ccad21beac5c96f37c14b207dd79efadb94","impliedFormat":1},{"version":"abfce04da58bc76daaeec6281a503018527d22b75bfe66d57513aa50c36f8574","impliedFormat":1},{"version":"9feaa668cc9359a0723b887890d8d7456c0204cdaa1361423051e8befc73aa1b","impliedFormat":1},{"version":"e4e323984c32471f3788ea4823207f8e2220108b85f7745155e5eb64e5fb02ca","impliedFormat":1},{"version":"f7fe5e56204bcf2e45c0e8ec883fac3f7220578aaf025820c7b210a826ae7ad0","impliedFormat":1},{"version":"c151ad51ee2d8792890236f7930e08e7a53e09ec94a8a9daf84bbf64c1205af9","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"de9a2ab89de342e829a8fc5a9bbc788c5b033b7f8a485bb5a304330f119ffa89","impliedFormat":1},{"version":"d0dba2d75b5ae2ce286eafc016f907319473abe717eb8adac97b9a016a0a1e48","impliedFormat":1},{"version":"9ffc45a907c9359d95e225a47778710cf7a03e3095ec7406f0d48faa38557094","impliedFormat":1},{"version":"a7fc8bb34e43781bed4ffaaafd19dd678b90c18337fca50f725cde934307ae23","impliedFormat":1},{"version":"b4f76ed31b363a013b3ed3851b9b3a02c6d376c6f08b59c70d6df32e2a5caa88","impliedFormat":1},{"version":"e45261a215e8a0976fb733f315a9b0e11a50127715e2943adc61dda23958ad66","impliedFormat":1},{"version":"eb91f999abd7499b48fbf6a5b950af755e88d935c3be22f07f1f8e32a7db9c70","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"2ec2d9054af8115a470e4195621e65b25c9e12e3af2350dd9e9fd8a3c1b8e5ed","impliedFormat":1},{"version":"ab5f64e568980a7215eee5e75aec9dcbc80fc332ff498df5b5a12e9c8436099a","impliedFormat":1},{"version":"a7fc8bb34e43781bed4ffaaafd19dd678b90c18337fca50f725cde934307ae23","impliedFormat":1},{"version":"d7a384db245838e1dc978e40eae330614e89a5d5c4c941d54f6401c79de9bced","impliedFormat":1},{"version":"eef3c5f853eb82ab7617d5ca042970d8e76d59c6eb8f12996049b63b48e7a977","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"722dd2af1cbc818f759f570a196eb56708f2140a0850a2d8d863688536e206d0","impliedFormat":1},{"version":"91b8bfe9c07e519bd3f3915f59d305d3272b6873e325ec39c7dd5cf7d0415923","impliedFormat":1},{"version":"b76225940793f972f76072285291ebe4abbbfa41eb4b0352544146a2351ed7fa","impliedFormat":1},{"version":"90ed7a1af5e818957c2b6c2d2ba1aceb03bfb6634445e36600591422b5e16a7e","impliedFormat":1},{"version":"52d553bef41b4e3db076c574d5461bc2adbb9dc83cfe7ab5cec49fd730b1e71e","impliedFormat":1},{"version":"9c4b3478706440fa2bb3828f0d82797cc32337323d698404b062d3868f66b5a1","impliedFormat":1},{"version":"57ede770bc1617adc1524c4b1f32160a2f5cbca44682c89880a2a16e786411a8","impliedFormat":1},{"version":"02e582ba3f4e1ba2543d2497eed9a2f20b510c6e59d485cd8bc35bbf67738ca0","impliedFormat":1},{"version":"49dd2eb1c82d9000366853ba412ce48503d9eca0d30a7e2052ff180f7ff045a2","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"7f5e2fe03b1d58b5d9f462f517d4b20c382763b6953c682aa9430fef97b1c497","impliedFormat":1},{"version":"88d74ea902a6bc87d7aacf9fcf2f6da04d17685e38026cec845dde35101adb21","impliedFormat":1},{"version":"048fa81718fc79125dd510090c304032fa78ce8d5443d4f6983d6b365aa06643","impliedFormat":1},{"version":"c294a6fc25113fb35dd177824e5b5a822140e3c7c46e9d516766e091c6236b1e","impliedFormat":1},{"version":"1eb1c19697fc745ed978bde69fad605dfd46f7b669b381a63b1053f3def93b43","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"0635758a0bd3a91e81dc3ffd6c52d1f415bca6a54d475b17c9dc22ca5c4eed8d","impliedFormat":1},{"version":"c4e8a77a584dbe6b702026bb2e319d1d4fc8ba09c0fd889e7b9f656476681f19","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"0144e1b4e65a0a9db4d950d08ac127839e1c3d708fb3ab2f4b30c53fd58fe1ce","impliedFormat":1},{"version":"8170b6dce90be05837e95f86811c36fa724d9fc06b863f4fbc7a39def31dca19","impliedFormat":1},{"version":"2fd95e4d78660a16aceaa3f4a55f181765c6957b65764e3f3a09e299f528f6dc","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"e3e032d1af82d889a1cc86bb346c8eda23696f074fe6164b3d2057bea10a16aa","impliedFormat":1},{"version":"067f50567d6f57839348b79f8cf43cf27f4741ba8754a2504afd532c6cd9f572","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"3b88fbbe3bf8e61a427e83599435bb6950ebe402767b144ef4d9ef477f92d0f5","impliedFormat":1},{"version":"95718a004c7c0207cd738f24d83ec88b833cecd6a8fe6a9a99561b43870d64ed","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"a2d3ed8539d83c385eb6f5457a5261bf5487f9ee1936574738f6b83b2a985f89","impliedFormat":1},{"version":"8b2133dccdf55174b8dd4dbc8d007aba48fc2c15c707605eae3628bfd71026c3","impliedFormat":1},{"version":"a7fc8bb34e43781bed4ffaaafd19dd678b90c18337fca50f725cde934307ae23","impliedFormat":1},{"version":"b041eeaca10760c4797aed1adfc795d13d16744a526c72e612af7a0892977388","impliedFormat":1},{"version":"5d5b456ab0487d9e516e3ecee2fd59077a736d593fabd5f00f41feee295b079b","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"4972b3ac8b18cf8c3aaad687d80da81c1b4762c9e51d3e22dd3b0bdf0f27fe75","impliedFormat":1},{"version":"d6c2c3769f98f798655fdba5c35bce12979d7f9615d3f92f7895e79644b82ce1","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"ecd28c2c148527009c076dfa900e75b5783710d3e1cbf9c11efa758f02843a4a","impliedFormat":1},{"version":"08fb0b286201fc501142dfebc3774d105474ba744a09d9abd318cf2b4508f440","impliedFormat":1},{"version":"0a12293b029ba0adc21ddf80c3f7b5c4b2cb76cfb3a014a76cf10ada51b0a707","impliedFormat":1},{"version":"e43f9e516b789b8d595c38749ede922406681ca3200124da878800518bdbab73","impliedFormat":1},{"version":"0ab7c61ee015b4d789addf0be8aa4b35af0ea0c1e92e2c9a4e29dbf776c62b88","impliedFormat":1},{"version":"29bf98953565891d53d7c41239e43e17eb1883ba01f683c83f292694a5503115","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"994625884e6f928333621bc06ebbabfc94415b7396b2c4a7ef9670e18ea6e39b","impliedFormat":1},{"version":"235321e8a252640cf60f4aefaa8918ff4ee9b84375e6e462f5d118bf420c0475","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"e7c0072983db0b6af36cbe0bdeef68b3cc48ab2e23186bf79e9d4e591045ff27","impliedFormat":1},{"version":"08fb0b286201fc501142dfebc3774d105474ba744a09d9abd318cf2b4508f440","impliedFormat":1},{"version":"0a6305be7d3c9975fc285864b153c212b30137a30f3369f4a0fed843ab7064ca","impliedFormat":1},{"version":"d1ff8ee0a59fab0a6faac2131d737ae8e727968c19981060ee3999656e52349d","impliedFormat":1},{"version":"271ba50d3794844218622222e698aeb2e63409e848607088449034e46a344dc1","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"084e4662f6a1ab8e969d3beb8abaf3605392ac63f064e56227bdd18b057f008d","impliedFormat":1},{"version":"605c43560c091f2a5809a1c3f1deef941c54de80189a0e3de30df0993c0038f7","impliedFormat":1},{"version":"9fa703e374eab94192ecd6fa2123554567a62392bad1044e25665aca41dc9d8b","impliedFormat":1},{"version":"bf7e902009135850d2cf6b9d97ccf3efaf4bcb4834ab50ca9d9fd5ed5e98681a","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"c45f896f14ea50c2bc0c2c37a0d55cd5c2d03e432810ca303f41d082004a0f20","impliedFormat":1},{"version":"60e3f36dd549680dcee23414f25a58b18512c29d07b8c4351270364a30243795","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"02f4ecf9e395dcc194574cad3872cb68d77623f73938ede09b90d852bd18fb3c","impliedFormat":1},{"version":"1ac3152d8693d1485a46bdf5de83400d58a1bbc120241ec20ecec57eaeb43d32","impliedFormat":1},{"version":"ccd1893d8bf1e325c89007a55169e3bf4476262cc332091ced7feab2fe7f5f49","impliedFormat":1},{"version":"29bf98953565891d53d7c41239e43e17eb1883ba01f683c83f292694a5503115","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"13731147d47612fafde68786892bdd36b9fc5be262396bdf26c4382079c66b31","impliedFormat":1},{"version":"912254d9dfadea996a436a4af7cd0a9138c934f79af69bbef3cfa929ccc01d9d","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"8ecad66a73be41f80a3fc1c57c229b9293b2acb284bd9dd804e0719e225a8121","impliedFormat":1},{"version":"c49eaa66958ed60e747ea2f5a9aabdbd21364956bbcf7e5436f2dd3d9bc95bda","impliedFormat":1},{"version":"a7fc8bb34e43781bed4ffaaafd19dd678b90c18337fca50f725cde934307ae23","impliedFormat":1},{"version":"0f45ef8048dafcbc8c6a34cc1dade613d6c921fd7c0fd9e05155355db78266bc","impliedFormat":1},{"version":"08fb0b286201fc501142dfebc3774d105474ba744a09d9abd318cf2b4508f440","impliedFormat":1},{"version":"63741dd899b8f3d2f100db4f6fe870358377a58d05f326d68a1e2ca1468ac7c5","impliedFormat":1},{"version":"d65f8ef4e2bc59f8857d745b039c0a761d8606ea1725252af6ed2b0a4ea6be48","impliedFormat":1},{"version":"c5a7e373c800d0bebc3bfdf6f07214962771ac5d5377a27745bc23216a0c2301","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"27f5d322d31e0ec1e5d6275df9dd28f705209f3a34f4b194234d1d989807b2d0","impliedFormat":1},{"version":"8313e4af2eefe705d9c28b10fb516741d2f37667aa720abd983da375c4ba2390","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"0c949cc543cac5c1a425ad5ba93dbf2885bfc58afc28322a15f0b57c94ddf688","impliedFormat":1},{"version":"c2504e2dab818cd1b9a7276e3c164b1e9ad004280e1fe8c964ac274c2773cbb3","impliedFormat":1},{"version":"12d422fa5b0ef3799341232181afe47ba8dedbedf16eecebd04a66c547113a96","impliedFormat":1},{"version":"70905c4028850021c2b86dd6d1a5af2a22fc8da783bf1fa894be795f57844247","impliedFormat":1},{"version":"ca76a1481031e2541ea701b9d5e703d6b52cb3d05f2fed7d11c0ec5a9cc1dcf4","impliedFormat":1},{"version":"af993aea4bb708bee08dd84de79c55c93b7ac3496bb6cf5dde783fda923ce3de","impliedFormat":1},{"version":"b0ff050f5d2d86a7ba0c465459568973b4072ed77dc629d16d91f612045fed38","impliedFormat":1},{"version":"51520ad23ed34e3ba989361b32a6f4287613aa53e6f67389528d8b6f382ecef6","impliedFormat":1},{"version":"8c344b80f6bbaa1353497c3950cbc21c513a4d24c8f814b76cc7437b16ccae37","impliedFormat":1},{"version":"ab4fa7bff002ef58e9715c971f8d19f4835fe2662368dd43ccb85f1d8e21a9ce","impliedFormat":1},{"version":"cac2931923add0e6f0c9264584fd3573548eef12d47a6938b71cf9f5ac953928","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"6751ad97159bcd89d00e484a447e9c63af21eb90ae07abb2c3a3541434905528","impliedFormat":1},{"version":"95718a004c7c0207cd738f24d83ec88b833cecd6a8fe6a9a99561b43870d64ed","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"6091636e17f84aaad50b23a1aff5d38b65fa19fea695f345e5f1ac13c3edee76","impliedFormat":1},{"version":"8b2133dccdf55174b8dd4dbc8d007aba48fc2c15c707605eae3628bfd71026c3","impliedFormat":1},{"version":"a7fc8bb34e43781bed4ffaaafd19dd678b90c18337fca50f725cde934307ae23","impliedFormat":1},{"version":"02e01adcbab16dafca6917e21184acdab61277eafa8e3d2bbe12ec2c442385bd","impliedFormat":1},{"version":"cee2c6cd3a7e7624adb2934b4b2db64b5074f7a69abf8fad6897fd6da78081ca","impliedFormat":1},{"version":"c6f3eb5eabed0eeece408dd0516ef220be67ca2c08aa1a9bc61d7980d222f118","impliedFormat":1},{"version":"ce14c46de8a4ece3c6c75747cfdb0d90368602997345d2c5aa0e2c1aa8243d96","impliedFormat":1},{"version":"29bf98953565891d53d7c41239e43e17eb1883ba01f683c83f292694a5503115","impliedFormat":1},{"version":"d2707aa123c701bc5a76b2af6c509b6651621d00cef17aeb5ebd4f273e53ac6d","impliedFormat":1},{"version":"70af75a087f2fdbb84cdaef237a8084e63e1d111a0e5019c310c1b06769f6184","impliedFormat":1},{"version":"16d6613eac95d77c07b41080235a62cece877705a1c23d355d2f27d8104a132f","impliedFormat":1},{"version":"29bf98953565891d53d7c41239e43e17eb1883ba01f683c83f292694a5503115","impliedFormat":1},{"version":"9340cc9eae2c86eaa16f493d2984ef1a1b234cf7eee5441b6a40f264a71c7fce","impliedFormat":1},{"version":"948bafb0a5cab80475b0173b1b636b1dd25195980c79f325e1e82e4df3768060","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"30a3e2da0f60df129af9c1a95b6c3af4a9c85ea6fa963d207ae81f60782b0eb7","impliedFormat":1},{"version":"0ef50f03dae90c196273a2c1acb98be1acd57113f44e6577bf78afc83857ec96","impliedFormat":1},{"version":"1ce190d7927bfc2c499775099f20361ca9403b8e8b0de544177c90dae0482c21","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"511223b94bf35c9f34fc1a2610033e3cada02d8f9accc993fdbc04a33e3f83cb","impliedFormat":1},{"version":"c7bae52b9668d7fa3ed376c6a8261c358141656f13b23b347d3eac72c6d2badc","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"d23bfbe4911cccdffe2f23ea0e9eba976d91fd682a1522f5395826afb6d151db","impliedFormat":1},{"version":"3ebb9aab50c0a2d0f7727d237def83da3632891b3187bac7650d4684fdf55cc8","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"2c7813404706d40ffb8a243d206c9c1c45f9ee0f3663352c34e4c1d6642f6017","impliedFormat":1},{"version":"5ebc76d633475a5353aa6931ef6e9fa2f324d04d783da31a6d0fb43d2fb7ced5","impliedFormat":1},{"version":"ac129dfca5120d70678ae38d0e260f839913a263fc2d9ef05b80b75ebe35e0a0","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"dd7cc9fd012231790c4e98f503fcb81b218aebe62a29a1cd93ec11b5419fac42","impliedFormat":1},{"version":"b631e3f8b1b1026b35f528d966300834d7b6c084483ae5f758f35a318443818a","impliedFormat":1},{"version":"2118de28fe1e5a46208cd569f48ddef51b9d051b17e172ed784700beb4afba2d","impliedFormat":1},{"version":"29bf98953565891d53d7c41239e43e17eb1883ba01f683c83f292694a5503115","impliedFormat":1},{"version":"f2259e70e81bffa4efd9f9f075226698fd85b0d5526cfc340f720ade086d2a38","impliedFormat":1},{"version":"714ca7c8666cc4a37a360dcc3edbcb61cc1bbc1bcf08428756ae259887777ee0","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"d2bda151d2b5210f51674e0f2d8089fe0a3d39b6cad6598210eb3ec3d7e782b8","impliedFormat":1},{"version":"c3f154b5f60a0283832b9310273616ff79768566c46e0f1b3993daab65ecdece","impliedFormat":1},{"version":"ee7d132ac85a32b07fe0244e5c035510a0175a18eaf71bc7b5c46b88d6c00802","impliedFormat":1},{"version":"62f5c764e2f1c949cd1b5020d785d83b4106d79e5e9b9827d9afc86e4b3be75c","impliedFormat":1},{"version":"e85a37ffd7cd5831677d52069d2691c521eee22c87b078814ba8553973b8953b","impliedFormat":1},{"version":"a7fc8bb34e43781bed4ffaaafd19dd678b90c18337fca50f725cde934307ae23","impliedFormat":1},{"version":"781b0c3e96d9f0c103ca5469658ccb90239ab2428816ddd3ff2f6fe20ea04e89","impliedFormat":1},{"version":"fa3ffa3fcab71f5d89af4c2ee89e0c781ee61555b71c1ca68078f54a982520c5","impliedFormat":1},{"version":"3d35f276195fc919a0aa845190bd8fa5536b84cebb5aae5736a57931a4bd7cad","impliedFormat":1},{"version":"8b71ec75c38df0e7b770ae8497cd56fec7df512ce0ffa77b650daaf0f305bc74","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"f1d6f01be8326aeca398378ffcf2eb034eaa7419e9d6ccb23d0912adcf417861","impliedFormat":1},{"version":"6ebfcf9c9d839bbbce0297bbe7f3ada579f554eabbb169e9ccd8f53084345eb1","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"896c8059984318a04fd982accbeecf06276a057ea75e58ac9a92ef475b4910a0","impliedFormat":1},{"version":"28c078e2c75dfa679f3db863ef57c6a0f965c2a00e3972f189e9fe3d134569ac","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"3de721b76f35245f7d8fd903e1c85acc81444f4b2ddebdd04d8af3a3fa0b64a0","impliedFormat":1},{"version":"adf24cb0ccbe67f7c73617dca22e6ccb0f212e84707b57bead4c853fd438bc29","impliedFormat":1},{"version":"383f4a0bec6af8b560a090c39f02654761a596e9bee9787107ff7cb75847f825","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"0ce6ad24324a54bd440bdf0a90192afb67bc53c48c5dfdfa2354aec71e743f1d","impliedFormat":1},{"version":"65325ba25c0915ac339660736c5e454524d97b15e9a46c47d73eea723b8a7c4b","impliedFormat":1},{"version":"7d42f0863d76561e7f46eaad5eda04f2385a8e387f24539f5b8ae97b87ae213f","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"b3cab899bfe067bb051e8d4c1ce136e2281436d9122d0fb6597c82af0396f951","impliedFormat":1},{"version":"d0d21860572b016aec8b0b88a1ca41a63762d76d20cd3d18071ddf4c0402c622","impliedFormat":1},{"version":"f29d851ea184bdaf64ca554ef7d7c8045a1f52f5088fb4d19d79344caefaec58","impliedFormat":1},{"version":"5f7752165aef5d6cb302f74a51998a7610a64452e2384b1ede730a42130927cf","impliedFormat":1},{"version":"75b4863d51afa0ea8a65c2de31f347883444e644b26dd9056289feec2434d9fd","impliedFormat":1},{"version":"3291fa44eaad55321624d417adfe497df866dd1be295a148ac03c26f6d6c8897","impliedFormat":1},{"version":"29bf98953565891d53d7c41239e43e17eb1883ba01f683c83f292694a5503115","impliedFormat":1},{"version":"b4c6c7adbadb97fb18113321f443782b75f220320e6320700eb3578009e4fc52","impliedFormat":1},{"version":"fcaf092a8c54eee39671d1085652d4d358e24cc67763cded5d5a906a278cff44","impliedFormat":1},{"version":"f1cebee6bc8ba02a7662987728e1a27e84f67a614374b6829a489f7d36091b0f","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"d796013e9ac4e4894a60547f42911cdacca685cc3d1771f0a05ef8e379b3439b","impliedFormat":1},{"version":"3f7ddad9efa223a1236e6a48296c482e66bf756764b3fbd7a826aba2a899dd62","impliedFormat":1},{"version":"3c8d87e77f442d18fcf09b52c8bfec7f4f8bebb772ed9164366091c27c55d307","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"f5eca1d9c8a2391012fc662a042387c1981a6d09d8b6ab4caf4e54232884b251","impliedFormat":1},{"version":"dfe2b7b9f563918c02364b199ec18ff3c012288ffc280f9d8b55e49c48c89662","impliedFormat":1},{"version":"a7fc8bb34e43781bed4ffaaafd19dd678b90c18337fca50f725cde934307ae23","impliedFormat":1},{"version":"17361475f2357592d25c53f21a9d612946e9ff595f4c7bf095dd7b2486061940","impliedFormat":1},{"version":"bb0a889aff1ef0b67a205ddf822d65f3407cd2a9eb91fde0a26af13ed096d061","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"a0a5c1a524b32ba598864585b70e3fc04802943e67f4e458465ded843f0171b5","impliedFormat":1},{"version":"79a6da2231a9eeb802628f70624579210f772cb33ef9364d18406017ab7661d4","impliedFormat":1},{"version":"6e308abe79f67a510dd3efce53a2da997b8009baa692b01cb607510c912db8a2","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"0859a4f2629a3c6d643ca894f7960c4c1bf8186054e624dc1f43af45be0f78ca","impliedFormat":1},{"version":"7371c369f77ccedede7ff924c75bbb2620358e4d5f80e9d107458488481c6c84","impliedFormat":1},{"version":"a7fc8bb34e43781bed4ffaaafd19dd678b90c18337fca50f725cde934307ae23","impliedFormat":1},{"version":"bba53864ae5f90d513aaf8ec74319545090edb57ea22f84522ee319cbefe53ff","impliedFormat":1},{"version":"11359b3a0d322f59256ab2c61ba8ed1c291dbe156a028b422f2df08d5489b1eb","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"1ef95e0d01b607d2e9c9268a7adfbe5c6bc8497902cd27390bb87520af80dccd","impliedFormat":1},{"version":"08fb0b286201fc501142dfebc3774d105474ba744a09d9abd318cf2b4508f440","impliedFormat":1},{"version":"2edbd97d465e53f5624a7bd7ec00035cba6922dd777116caac97ca09a80ca2a0","impliedFormat":1},{"version":"b72277cd1fd10e77036585962bb35a8bb515c113e4a699d871fd2f308f67f79e","impliedFormat":1},{"version":"f158601b0507e61c27adf34099817b5f6de68ba89a2c15d5452a281bf1b89f70","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"3cf119655bbbfbf6d0a1a9fa2bd8f3bc8ff3f75e7b18f32b7861dcdf688ab876","impliedFormat":1},{"version":"dcb0fe8f3a25610fc90cabf2427a3eb4c08c67950a60dfae358275a976a1a2af","impliedFormat":1},{"version":"6b0288a9e97b877008a344bbc76ab68bf74505e48715c981071fd1d7c786f576","impliedFormat":1},{"version":"9cc2b909b814c5cbe073974532db701939c11135aa08d16aef1e0ce2298ff63f","impliedFormat":1},{"version":"ea45882dd5d328ba853db7ded8b5a52c543fde47f2e23691d5424ffc61a8559d","impliedFormat":1},{"version":"c01e3acd14d10c641800965362caf2829d8f59d3d636dd123eab57b5da67fa4e","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"e64bd67fb787a8332d278260a4e371b82cfc143f5a3f11dfbec827236607a34a","impliedFormat":1},{"version":"ce55e391d2771ed7e17ed15bcc7596a6af1f86a473e3669f39efee5992f9ef00","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"5c0cbb5b310c52edb0881409508e47f08ac921c1288a7d213c25ec1bd31d320e","impliedFormat":1},{"version":"c45375e9ce917ec098a56ba3e1bd2d8ce9ff445a5c1e01a7f4d12cd98c6273bc","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"5bf6726c803f58d64452f1a84aaec779c18b5cfa1de435542278b9eeddd84c7c","impliedFormat":1},{"version":"ec8b9c00bb307d72cd8f158f7ca4a15f27a5361c0c896db89047219d66530842","impliedFormat":1},{"version":"4bc5d658096f1c2a69233b690fd0fdfe35f408df723d45a50255689fa6adaa53","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"fd8e9cf99381dccee39b8566c404da383c78a94a08042bf7d843a2b0d57ae89e","impliedFormat":1},{"version":"2b6ea9177b44fce0bd803b6c5d27186e7ec84ab673cb4e834005619f8a0e776b","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"c78f97dc5ca434084bea9daba106c3769dcd42f51df1134433bee2bac99cf864","impliedFormat":1},{"version":"08fb0b286201fc501142dfebc3774d105474ba744a09d9abd318cf2b4508f440","impliedFormat":1},{"version":"1db3416c1d383f158ccbd5f7e0b0671c484257d362530d4392062fd77e729ab8","impliedFormat":1},{"version":"08fb0b286201fc501142dfebc3774d105474ba744a09d9abd318cf2b4508f440","impliedFormat":1},{"version":"d1c922b4f022ee499b392d4918eb9bffbebfe2558b7a6f31923bd2edc5264b3c","impliedFormat":1},{"version":"68c2876921c1a6ec730656e8d74680bf10418ac416669db40f64e388b37dbd0c","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"b15debbece22e16a7e6f5132635679a16e53a1f0b1659d4409040e3377d8971b","impliedFormat":1},{"version":"2b8d2d9948cc0f5a8daa18ba9c65d735d7d29fb771d8bfe31a0c5cb78f148cc2","impliedFormat":1},{"version":"8b5df15e19391d1757197e0da723070f94e15861338cbf345dcbb4d669de1e0a","impliedFormat":1},{"version":"a7fc8bb34e43781bed4ffaaafd19dd678b90c18337fca50f725cde934307ae23","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"52051cd8c3ada9b8e54f639eee9027f2c15515c0fd6d044637e4cf3ef894f082","impliedFormat":1},{"version":"14ba5e2927f8f99b261e28657a962ce41026ee359c8574a9450bf556bad721c7","impliedFormat":1},{"version":"a7fc8bb34e43781bed4ffaaafd19dd678b90c18337fca50f725cde934307ae23","impliedFormat":1},{"version":"f25b038b2d7d342531519a449de49ea318c2306660f7ca759ef73adca87b8298","impliedFormat":1},{"version":"035d03b9db9005d88e691ab54b407a3ff47e0cae56f02aa3aa3b03046cde2be6","impliedFormat":1},{"version":"1752eaff39798987da9c6ef63a2dcf4991d2a784e87ce0c25761b0dcff8eedcb","impliedFormat":1},{"version":"e56a57527b4642dd64e959b45e31b68ec2e4ad93c7c22b12723384e19e6a3838","impliedFormat":1},{"version":"dbc4288dc8d93d734bb854dba19cc7b2b2e37ea73b1004b7bceb329574675995","impliedFormat":1},{"version":"177250bb17d8bc22a13665b4dcf05bdbd72e099d85217d9be4c9011c7418412a","impliedFormat":1},{"version":"29bf98953565891d53d7c41239e43e17eb1883ba01f683c83f292694a5503115","impliedFormat":1},{"version":"01831357103370337898a15b817db19096d5c75e7261bc6cb87d8996ecc5d215","impliedFormat":1},{"version":"72b77064b3ded5f093a390ffce64e00d1578b6250df9f412c33a1f051600373c","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"f440e52b4ab50c47df0ab22eb2b33de066b1b475e9b0d3daf323269eb3c58282","impliedFormat":1},{"version":"22873f44bce41662c61e89df45fef3dc28dbe773a4bb6408a211a76b9f09ce26","impliedFormat":1},{"version":"41b2c54d2c203220d07327143a80f16b48a829b2d3084557a5d0fa778987b98c","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"5c2ffe1e47d54807a83fd5c981b1160852d9cb8f1bb282ab9b1de9ef539cadf3","impliedFormat":1},{"version":"41bfe813e57b533518018a6749e4a1a28d0bc9945c80af5521438c1f5757cf4c","impliedFormat":1},{"version":"030f9b2ae088670167f4d537118a1a4fe6d74ca50df580c09c5564063844bda8","impliedFormat":1},{"version":"a7fc8bb34e43781bed4ffaaafd19dd678b90c18337fca50f725cde934307ae23","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"44d93f1b209be47e0ecd83b4469f73f7ee7ed7739c3c195b038263c85baa0a73","impliedFormat":1},{"version":"0fdc2f8881f5ac858a044403f257e13e726d426c84bfddc43c19ae05828ceeff","impliedFormat":1},{"version":"75dfed0e7713160667841bd2be8181d4fac47dee14339c0312cec414a465fe7d","impliedFormat":1},{"version":"13c9d6cf3e5c3c770bf673b0ceccc8c036aa0103b494e1718e5cf4c8cd825c72","impliedFormat":1},{"version":"ba162d195b46be4a8230f722a23c0d693accef27f55d03f92e99ce5363ab82ea","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"254066b66debb8ce731e22d28fc8ebba836e710fa91388f02c18ae60d8f0c580","impliedFormat":1},{"version":"7e823c23dfa607dee4d94c8f0c160ee76710467561cd7621d203584093388915","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"10dd0c7af2a2407210ed4b38ac2fd50ee8c7603a9e193d2bb1e0835b396ba92d","impliedFormat":1},{"version":"d29e2342f800c04257516d0e98928d61d2c554162efc57180a4f8cad177ed188","impliedFormat":1},{"version":"0af0a89af9a15329cab85831d8dd59b61d960fd49bd0f22cd788bb60ee580968","impliedFormat":1},{"version":"29bf98953565891d53d7c41239e43e17eb1883ba01f683c83f292694a5503115","impliedFormat":1},{"version":"fde465aa7a5b3ddcdc3644a96c832a1ff3df9c6d929407008f3b8aad1e0418c9","impliedFormat":1},{"version":"3e7039aa578dbe60059aa074bf4c026f36883d345181055b8b9475da1c284b26","impliedFormat":1},{"version":"fc89c74cfd6de424baa1f2d37431326448a437f2154fc798c06c881721abc490","impliedFormat":1},{"version":"b65ab4ae051dc89283ff98306733bfe1e508c71666a56a93e85863f8297292d8","impliedFormat":1},{"version":"797859b471c666ab23bf8cc9658db5446b93f4bc4cdc4c95693a51cb07441b6d","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"d2eca670c1c8ffe0bd94a0c638882ec6ba63304dfea952ad679bd0bfdc0f3ec7","impliedFormat":1},{"version":"83539529de430e07465afbcf80d3b2097222d831419588f63cbe4c8d6fc8bc8e","impliedFormat":1},{"version":"06e61eede83b8f7425bf5ff5a5a03cfe79b9351654ed9185093602ff797d0f74","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"4dafd380a5e2501310ce5e5fa111e50baebca293d20b43ff0d4c148edab6ed9d","impliedFormat":1},{"version":"13bba6c15acd53ef0cd42750a59353f4320ca15670ad1272ff558bc767796f28","impliedFormat":1},{"version":"a7fc8bb34e43781bed4ffaaafd19dd678b90c18337fca50f725cde934307ae23","impliedFormat":1},{"version":"c2ce3dec91ec03654c75f13ed66f55478292368fa755c5c63107fd1668d5c447","impliedFormat":1},{"version":"a0c76c6714c145d840ca0e1504edf68a41d8ad825603d285b38ec9fbda462999","impliedFormat":1},{"version":"87639a73ee77cc6ef376d5df36bf11b5f29337962846132c232361726fbcd518","impliedFormat":1},{"version":"e1a40f8c2c265ded81e91cf953948f09f1adcd098fccada4f9d7846d3657cb23","impliedFormat":1},{"version":"0b4fc3ff610057fdf7a8defc2e81193c7f8d55cbbf240fefcd53db9131e32c89","impliedFormat":1},{"version":"5f48d7c00a076e3560183e81cd60fbc8eb6d3bc6102c1ee2f7559f7183e708f4","impliedFormat":1},{"version":"29bf98953565891d53d7c41239e43e17eb1883ba01f683c83f292694a5503115","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"c202eda5972fe05a43fb47df8b442f7e81bb773b4f090d7de0a45a8ecda4544a","impliedFormat":1},{"version":"682241ec9c8da30c4019bb2201464b859098ff3e250136f67efc53658257871c","impliedFormat":1},{"version":"40c0beeed8dcac29bb719d5b4a5589a1a1c05c749d39c03085b014c9e9f33bec","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"b4a6d611647d863fa2bf7801ffa9d9bdb1922b579720919df645ac72013136c5","impliedFormat":1},{"version":"3f485b482fd272cb1fbdfbb7e9949b20bb2626796373d0bcd65495bc46ea5107","impliedFormat":1},{"version":"21f08e1c37b682aacdcaba33f6db24fb00bf6746eb67a5405f05ce314bf75dc8","impliedFormat":1},{"version":"a7fc8bb34e43781bed4ffaaafd19dd678b90c18337fca50f725cde934307ae23","impliedFormat":1},{"version":"9653e1ba4da7fb4ccb94ee21ca5f8478e22333236584aa7e2e592570999d4b2f","impliedFormat":1},{"version":"08fb0b286201fc501142dfebc3774d105474ba744a09d9abd318cf2b4508f440","impliedFormat":1},{"version":"d04e93a9af4c2582b987c46a9f0906c44dae04f4a53cc16c701a43fc88fb66e7","impliedFormat":1},{"version":"1264d096e4949e45f24ce159af5c89a8117f4f58b261c60d494ce05bcca2650d","impliedFormat":1},{"version":"7745f3945b2a2c340cd67bf85f9ae27d4fcfab01c3c267fae88eb80afece9d7b","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"8b361a96d9f2d9912bda3a1dd90af74836f8b6232083bb2a43f4da3cd461c631","impliedFormat":1},{"version":"2cdb017d5e3fcf51457c78f91781cd76d3707d2d7f663c13886676144ebf13d2","impliedFormat":1},{"version":"1007aa3ee70e343bc0f94531873bde811573531ea7b10418889908f97dc3e703","impliedFormat":1},{"version":"22bd91cd831150b529ed8cfa22c26bf5280b80d9b1a9cc6d5549afc3758fd586","impliedFormat":1},{"version":"df32e57da104787cd222a4dcfa1c8df08713bb924aa28b028d001893a4c8a75a","impliedFormat":1},{"version":"a7fc8bb34e43781bed4ffaaafd19dd678b90c18337fca50f725cde934307ae23","impliedFormat":1},{"version":"4ac67df3227a15de0ff859db2fe98cf336212249ce2f2a22c1879703e99a614d","impliedFormat":1},{"version":"7e4cd25c0f606241d4c167d023cdcd20f7d4f608bddf997dfb4473355edc99ed","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"c136ddd9ec2c62a396336add31f296678b5aef182b6ddab00a18dbe8006583fa","impliedFormat":1},{"version":"71dabed83d051d0656ffad3ea831e9493e9d3f707d1b4790004c9642d3e9b4c8","impliedFormat":1},{"version":"a7fc8bb34e43781bed4ffaaafd19dd678b90c18337fca50f725cde934307ae23","impliedFormat":1},{"version":"cbdc7c1747d24e38125c0c7d9c0747817aeb99cd6c8747a2c3d6940e42b8b8e3","impliedFormat":1},{"version":"c0522638e61d73a5feea97e2b91178ff5896e7da5457f2a384551a63fae14695","impliedFormat":1},{"version":"793a048400dbc06746d0947f32bcf8ceea2b29445e9544ccad5142d651dec438","impliedFormat":1},{"version":"1be974276146033a61a708d47d89ea09962132c8c79d751c39efa8d35c7c5714","impliedFormat":1},{"version":"3d5f1e800de9276956f963b7c2f9a03c39085bf4d1774e7973c5d36028c918a3","impliedFormat":1},{"version":"b932fdd91a6dc9cfc5d12b004c96fa77547602767724828e552ec903efdd735d","impliedFormat":1},{"version":"423987324fe2a641da1d796d8c1446b6a9f591b9d4225b68393b8f0e1a912b91","impliedFormat":1},{"version":"238bfdaef741a0b71b4be95750943cb70bc615a9feb4ae799aaa3547732e55f2","impliedFormat":1},{"version":"29bf98953565891d53d7c41239e43e17eb1883ba01f683c83f292694a5503115","impliedFormat":1},{"version":"6fd5386536c3135280c9701688fdf90d67cdcc6f330f038768fdd5cd5e3b245b","impliedFormat":1},{"version":"d9d576a32900815ed41b245c30f1b5b612deaeaa49e5ca8c2c0e230c9a517a78","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"146aebb77b61cf8185995046974dbd815906327a1ef5c4913fb66f53d9eab16a","impliedFormat":1},{"version":"f45eb07bff78a64e6cfa22ea7831d33892b713f276d2671938c2f1a5301a6875","impliedFormat":1},{"version":"2053f4ed67d8de4cd9a7b3a8d91c26988d80442e7eb8563249f2c3f60b063365","impliedFormat":1},{"version":"5ccdfe7ca378c1ace9fa4500537e1b0845c86caa0a230d413b340a810b808804","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"04091999d7c201cbcb2f18038563390945194767ce3bb98fd18f478e8da16774","impliedFormat":1},{"version":"f3575864112d485a2c4d6c051d02c086a5d595ec16f7b8c078be565ba3a08848","impliedFormat":1},{"version":"8ca609404a4e2d93939f02f5973b746fcf5454a99ad83968668f75e9fe7992f1","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"24f5f6aab4c2796dec3cbd5778cd09a8c16940c13eec67664a651784a48ff7fa","impliedFormat":1},{"version":"5c5e04cbeb8504986b6f82746936c8040a8cc76c31a203eed8b402ac3612ab33","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"c81abaf9b0a543d2d4ca7fa10460818a82f92df7fd46a7019f5202f4095e09d2","impliedFormat":1},{"version":"08fb0b286201fc501142dfebc3774d105474ba744a09d9abd318cf2b4508f440","impliedFormat":1},{"version":"51e919bb5b7a0accebddeff8bc72404c1ccf1d03b46d7d19e291d40d13960e58","impliedFormat":1},{"version":"84de46f565ad98006f08c0fd071c769328e885f09d1a99b10198f615398e06f2","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"21624107e8d8adb74a58dbc93ebe4af864b8cf9e977065cd72e7dc6a9426981e","impliedFormat":1},{"version":"b5c274cb1ea013b3b5485263efc360dbc3abd9475dcc85bb0a8bf7d2fc1cdb2a","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"9969042b692d929c66118a9c787b58fc2b73be63bf61199f48e29dcad3d0cafa","impliedFormat":1},{"version":"803619ef47d0457466abc1065cb7fbdb324bb21196b7b5485789db6e794b9d62","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"509389f064d708d8b87b501cd4670bacc173f9d5bb39a2b2aff5905e40a982d6","impliedFormat":1},{"version":"424a9251d2911b2282707731950f19e3ee39372eb4692b366f681d106463a16b","impliedFormat":1},{"version":"76935e194d7a4dca1e9fc779c6ae6f75089c944b5923dc7b1fba4dea3c79de87","impliedFormat":1},{"version":"c9f22d045fbae3874b96a7e2bc594bc10ca50bdc3e6cb8d7634b858970a47033","impliedFormat":1},{"version":"2f0d5067a8c80d9b393ad97e0aeac4a0d85c56306c71cea3212a9bc32654f2ce","impliedFormat":1},{"version":"16d7ee6627901611861e21ce5b7573c85ccdcaa9fb71f96b7a11ae0e7ccd6fd3","impliedFormat":1},{"version":"0e020757000d51d97340a03ddad3a6887eff2b684d36490688183d6247134c89","impliedFormat":1},{"version":"29bf98953565891d53d7c41239e43e17eb1883ba01f683c83f292694a5503115","impliedFormat":1},{"version":"d43c13f3f22a01ebd7a4027d59ad69bd4d564591aa1a743829d8613ece2b50d5","impliedFormat":1},{"version":"b8aef5042f9115b38ad87e8436eb1538dec8b9ca74b56b5d9816cff67ef67d0d","impliedFormat":1},{"version":"1b8e1c9415a64657b12201e806cf7e8fa85df8158371a08ce973f85a720364ee","impliedFormat":1},{"version":"3162b4f4adf6c88d1f9481e77482bb940c6dc604d82ac30dd11344ec6d61e8c2","impliedFormat":1},{"version":"82f478df0a1f981510cdacbfc62047e9f17ff2e46d960ca15b3bfa67be12e386","impliedFormat":1},{"version":"9ed928e233243679c545b99c8147ea865a2d7e7ad5209ddf2035f9d6ecf52871","impliedFormat":1},{"version":"b2155b4b6abefe1dc6e3e827a24d0ceefd705d8c9180559851b6030be50b6895","impliedFormat":1},{"version":"e68a36353b5bb2c1233e860648dc50b8713008876077b1dd58e82417e0e94f75","impliedFormat":1},{"version":"29bf98953565891d53d7c41239e43e17eb1883ba01f683c83f292694a5503115","impliedFormat":1},{"version":"c16392e31f843ea9ca48c45946a37b0c12e2ce7fc242c492488343d7c5963150","impliedFormat":1},{"version":"6909625d99ff704dad22277c961e6b7afe900093ca0cfedf41cbbb3b9eefd0fd","impliedFormat":1},{"version":"97bd9eb75faee67263089806c283a61580a5a6a9eb49fc34cd375dc4abec029e","impliedFormat":1},{"version":"e74e2a43832b43823480d13db4c7fbfec6e6daf0795fe6a410f52eba2386d90d","impliedFormat":1},{"version":"3d0395d81f1750129c4af0d9491738cc85b2e4a2ce330e9569d039f4959a2481","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"a7f964ee3627ff2f5abb5e8fdf0de5992fc3b8142032d27c2cda3c5368d5e9ee","impliedFormat":1},{"version":"603899039da9d839da59fbd914fb2f37ebf7d8a088dff094b9f36ef6cfb5bc2a","impliedFormat":1},{"version":"e924db4dee9f08d432d1a0626108a349f089a6d80aea3627d0429f3ca8e99a56","impliedFormat":1},{"version":"e8ce9a77c6d8bde74a6fe3cbe54a37d332aab35a493fee66d4f0aba0c325c3e8","impliedFormat":1},{"version":"95846f27f0ba9bcb47d545f9de0b5203da360e7e4dd1403e8b4dd4236d57bcb1","impliedFormat":1},{"version":"ce4e1cbc9eded60b84697d9776eaca85d9cb545a282ac53ba97194ed48be97b2","impliedFormat":1},{"version":"5def7111f6e3cb5748762904f2bf0893d5801c82d045835cafcdb0a9b7afe9c5","impliedFormat":1},{"version":"a7fc8bb34e43781bed4ffaaafd19dd678b90c18337fca50f725cde934307ae23","impliedFormat":1},{"version":"86602d9d2deca26a709a3cb24438d71baf5b7988587036e49999dc48f2fdd99a","impliedFormat":1},{"version":"f6da3de0f7f110ec0fce426bce3392fb27349c60a58cb35777a799d6af575b0f","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"349ec47dee473f8509ebdbf5e664c22821af1755bee0be6abdf88c3a5e9f2002","impliedFormat":1},{"version":"9b614fde6dc81a899e3b78e3ec80fccafbb9887aaccd18ce3e95ce2588dc8a8a","impliedFormat":1},{"version":"7b00d8bcbc8fa0a1f3d1d27d95267ba2364e71dbc3bd03268a33a344fd094f60","impliedFormat":1},{"version":"121caa6030b0c8566c5023bafa7a385ec29309f5e1fdce01a42d204763c78156","impliedFormat":1},{"version":"d31b4bcd88304709cb53d988f491b11d28c8488e99bd573c10b5afe9860a62d1","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"a6fd3de51d5731cae753563b4885cd2b884b38af610e054c9bd6af930c1d1c2d","impliedFormat":1},{"version":"ea35befadc3a5ae6736be1ba6541507dd2a112e32200ab79ae3f91c6290bc55b","impliedFormat":1},{"version":"4be4622374af26a9b9b5f2b8e957454ad335550820badfbc96a139a4b815cee4","impliedFormat":1},{"version":"0a2416c3c283b852640adaec97f674c28bc72303158525070befddc834236760","impliedFormat":1},{"version":"faebd58f1ad5502468f33a3bdeb32bbbdff913dbe7063b23fd4ef0eba00457f6","impliedFormat":1},{"version":"dc78e87b8a731bcdab706c90b2f98714552db78679cd98ea9d358039a9403dcf","impliedFormat":1},{"version":"47afb5fa264dacfb57e12c5b6ec2c0644e6282baf654c710989c704963c6fbb5","impliedFormat":1},{"version":"31a3bc3573a138703601a86d61a5601df0840cdb44a8c5e1b0b0f42daf45b7f9","impliedFormat":1},{"version":"67229d5fc3b7a404cf569bdbd40071681e67af2b39079d5157470ca5e1544beb","impliedFormat":1},{"version":"a7fc8bb34e43781bed4ffaaafd19dd678b90c18337fca50f725cde934307ae23","impliedFormat":1},{"version":"2d6e2945ae8d6d2adecd99005e00c6876899c68b0f9b2fed2592b39598f01963","impliedFormat":1},{"version":"4054636a6f2b5bcdee5f2d294232cd7447ef41fa52dfd908d098c5fe2e5b8060","impliedFormat":1},{"version":"d18d66c70be7d0a9ef25ad48051eedaf665d2f97bd06031ae8501ad7357dbcef","impliedFormat":1},{"version":"877fb70ea781695ead062019710920a1f53ccd538182d61e95ca5e5a3c6bf181","impliedFormat":1},{"version":"e7bd226a3eaece64732b5bc9e6bfbea9c12c0314ce3754662bb4197607d2ec4d","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"42792a63af9335d9b462b68c550a422260a71c525d3ab6cbb971f339be995221","impliedFormat":1},{"version":"0a8c6cbbe3e60496406c7a7a513e1215644c52e9c430679b241292f17b164680","impliedFormat":1},{"version":"43c98346aff36de0463c614f9882f0bdfd14f27043fabc071005938255f56df6","impliedFormat":1},{"version":"29bf98953565891d53d7c41239e43e17eb1883ba01f683c83f292694a5503115","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"ecf1b7c238fa9965e43f2c5d469b2d8893e8bbe5036c6549c795f48dab163abf","impliedFormat":1},{"version":"08fb0b286201fc501142dfebc3774d105474ba744a09d9abd318cf2b4508f440","impliedFormat":1},{"version":"ebe173a68b5a4b33a732d848a05bbc26496bee826c5e3d44e9fc8e1d63e02b18","impliedFormat":1},{"version":"87dbe25a2492fecfc461c6f89e220cbfe0f804b2d29a76726a70f91300a0055e","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"9bdccaeebef7d02b2926fad38b3299125ac2536bdbd5b8cc552e1cd78b1a07ea","impliedFormat":1},{"version":"08fb0b286201fc501142dfebc3774d105474ba744a09d9abd318cf2b4508f440","impliedFormat":1},{"version":"4e7c9cd44dd392dd7cabb0903b473a533b78b7f6ed9b209761674e0da470c36f","impliedFormat":1},{"version":"15cfd5e97c475abd116bd2e68d580ba9bbeddcbc79de4396853f58f713b6b064","impliedFormat":1},{"version":"e291542bdbba593666e35f8b3cda7f2cc6eb2050384fe998b75b7a5cd9cff8e0","impliedFormat":1},{"version":"d51485b82b29d07016e9d3359f271622966aca8edfc59648a1d2369e6404123d","impliedFormat":1},{"version":"3c9341f879c53c0d91138ed0baab870b42b3cb8d22ae672cdbd1b9f21ab2c090","impliedFormat":1},{"version":"a89e929c09cb0ae91653537c0e206ef34f389bc3b691697815d4985c02cbd87c","impliedFormat":1},{"version":"a56ab660c369c7cd5b2c17c04fd68c920aca2c0938c5ce650e8d689b04aae649","impliedFormat":1},{"version":"d51485b82b29d07016e9d3359f271622966aca8edfc59648a1d2369e6404123d","impliedFormat":1},{"version":"4e75733c38e0c29daf2feaaa3495c3f0db9fd9fe25ec7d6375b08751771d8e08","impliedFormat":1},{"version":"d7d2fdd53e284c3caf7bf0a09c4407aeb9eadc8f8d5e40a97d17f0b168a3ac05","impliedFormat":1},{"version":"5721d7e91a064a2ad6836870a2334926be8651cfb1fc8ce94f7662f98d98de23","impliedFormat":1},{"version":"d51485b82b29d07016e9d3359f271622966aca8edfc59648a1d2369e6404123d","impliedFormat":1},{"version":"02b21907eeba94df118faee6903be6f23171e3eb507ff4373edacbba929ba38c","impliedFormat":1},{"version":"5a5bc6dbfc61a70c8d8f955d8847825c464345cd160cd8c272e187f2c4d93a97","impliedFormat":1},{"version":"a00591450b19e22ccd8aa99a365c91b81ab5391cd5849167e303c552ba92b62e","impliedFormat":1},{"version":"b321920d607e855dd23d2a8d9b04e1ba5ef33c9a64985ab104f5cb8594a4ff9b","impliedFormat":1},{"version":"2fbd36a7e1d97225ad07f8953dc8ec7adb219eede74702d2d5482223d2524437","impliedFormat":1},{"version":"04435f74ef7f683b7126cba24518aeba633d324f6fb73f43bd577a27ccb1908b","impliedFormat":1},{"version":"a5e08f7b349b00e48407877ca127406358df8b74c8cb3809cdeb8a997b226df2","impliedFormat":1},{"version":"caaf837a4b76969443899b4646faa474a65ee3b45ab57fedf2cb60f8679d640d","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"f0270d8ea9e994769f9375219eaaa2befb61a9a70dac31f59993c9af81c6d50a","impliedFormat":1},{"version":"4410a121bdd35661bbffd9c476eed4692999ee38a47f90c7ddaeef5eac181db2","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"6fe4ebe17c7be6f423ab5e43d0e4201f52ba86145c87fa6559c477590b1199e5","impliedFormat":1},{"version":"1ca285021a6403d6adc89edff8e5fe0e6f491a975ffff413c1e1229b356a2f13","impliedFormat":1},{"version":"a7fc8bb34e43781bed4ffaaafd19dd678b90c18337fca50f725cde934307ae23","impliedFormat":1},{"version":"83724e99682be97a6a5381a75673909180a353523660868b51ea3acc2542c552","impliedFormat":1},{"version":"3cbea73b9649f8a986b8faeeb16f9346d96b4eb074bb3398819cff2429ff3369","impliedFormat":1},{"version":"bd1f5ef73803c1e20bcc8cab66bff32c880cd784efa59c9512dc19acb2814fc7","impliedFormat":1},{"version":"c12eb224066908b635ed7e2264d4ace849db549c11844165e2a7694f5f4a6e56","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"d3f63f88b4836d540a154034bce54c45e3d7e844cb4fe353636ec462f008ba3d","impliedFormat":1},{"version":"aa2f03585a50135f66bfa249f9e33b71699e38ff98b5b5c1402bb33f074d54ea","impliedFormat":1},{"version":"53bf12c6620bd615c44f5f159d356850b73c25aa6a454ebd32d3d587d2327f24","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"ddc5b5a0bdd7537b2d0e995c81e8d5337cde1eeeb4a9831fcb7c418d02db9be7","impliedFormat":1},{"version":"16ec9c92227341ec95d55c2684e0828d05f548d092e9d5f18d6b976866942683","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"5eea8d8349eadfab1f68930ee9fb3c13212f11519aa3f3df4ec49598a1c8910c","impliedFormat":1},{"version":"08fb0b286201fc501142dfebc3774d105474ba744a09d9abd318cf2b4508f440","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"b0144ebd8fc57db514d949c4879235cf01a059817874b1bcae2f9309052bf446","impliedFormat":1},{"version":"08fb0b286201fc501142dfebc3774d105474ba744a09d9abd318cf2b4508f440","impliedFormat":1},{"version":"a6dc821ba106821f55c1ecc65708f1688272a5355dfca68a52820d52e4927109","impliedFormat":1},{"version":"cb98fa7b2fb88cead6249463597201a55c3f826dc0eaac3ef4d9b418df4e50d1","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"00678d16e7dca726d58f0a484c5e14db1a791a31746bbfae5962404464e4ffa2","impliedFormat":1},{"version":"ec03a8473e14043e16888dd2c1a30620de8b1862ce2639c982027aa9d2f57375","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"967dfc9922b25a2e4171a349f6c5930f7436db45526049fadcd16f5f3f9a5b5a","impliedFormat":1},{"version":"08fb0b286201fc501142dfebc3774d105474ba744a09d9abd318cf2b4508f440","impliedFormat":1},{"version":"763039fabe12b0e6a182144cb00da365122ca5dea154db8b8c37feb8b39a7bb6","impliedFormat":1},{"version":"08fb0b286201fc501142dfebc3774d105474ba744a09d9abd318cf2b4508f440","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"5eea8d8349eadfab1f68930ee9fb3c13212f11519aa3f3df4ec49598a1c8910c","impliedFormat":1},{"version":"08fb0b286201fc501142dfebc3774d105474ba744a09d9abd318cf2b4508f440","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"e5a797ffee136a38ef5b9dec45c9f8caa1971627e26688b693005a1ae30336df","impliedFormat":1},{"version":"ce003e17b891ee7b9d981e0cdb494f0578a153d3bfd543a1e6013c522c2e21ec","impliedFormat":1},{"version":"905952c70e8aea19f82d8117453753c9dc50db967986341c6cabc8829c2825ee","impliedFormat":1},{"version":"5966fcf6e81757420e57664fbc71276d092aa5479d62684b107ab3e486e4e94a","impliedFormat":1},{"version":"29bf98953565891d53d7c41239e43e17eb1883ba01f683c83f292694a5503115","impliedFormat":1},{"version":"6da4d59d56dc24c0a3ced294abd02b71313bc08598bf90b4ae43de449e0343af","impliedFormat":1},{"version":"2d2881681e3102cc6de5ea266d3bf97f94f4bf8894aa588f9da3e4e49d054c54","impliedFormat":1},{"version":"d3e4856ae50bb0294f92f44d15b5b8b0ad450615c526cb7b38f31fe8ac9a2032","impliedFormat":1},{"version":"d824cacde372578dcc10a3f0658524e98f2b2b362667e6b3d068f1cb87f83d81","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"e78011b2d49460cf1dd5ef8cdf0d414d6fd735df3c29b48b1e05117a90fc3d9c","impliedFormat":1},{"version":"7d8e88db0a80cdd1a993b23e1d4e1ce3574d38eee3d57b669490654492b27cd4","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"9cf9fb22aa3300f4fe036b84ee45f7016388e4995eb178b5f34bb3c2d3ac40c1","impliedFormat":1},{"version":"9bddc129b57dd28c23c48e693c4b296f06502acdde285d6e728996bbba21e5c1","impliedFormat":1},{"version":"ead13677c9e383e70e0137d45e86d2c4907e236802f2d365043eb94d79840399","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"db988f6e381abe96d404fae4daf274395ff1d4270579fe1853eb60aeca415c46","impliedFormat":1},{"version":"08fb0b286201fc501142dfebc3774d105474ba744a09d9abd318cf2b4508f440","impliedFormat":1},{"version":"533e6e6dcaa2337419f4c1bdb945cc9bee6224f38a5b49f7795ae8fefe7c3326","impliedFormat":1},{"version":"2beb959c30cf6794966d990b818b08a13ae7659fa1ee6a8847b91ee698c6961d","impliedFormat":1},{"version":"37ae509f423aed34e045234a0bd6e722a5168519e7a73ead69377bad64cc2438","impliedFormat":1},{"version":"1c38b8f0e4da162d18521b531318bc847032aa9645d13d7d4c5d91de330a491c","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"1260e1b7a31e26b9fb2d48833082c3002acc58b2ce677c0b803c01b6608c5467","impliedFormat":1},{"version":"a4191e9b52d01ffca5f6a358f66177ebaa9c3e380c3ad0bf0cb02ad0aaac4302","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"92de66a439951e124b409156b1bdae046393cac7d23e341933b624baa15588c7","impliedFormat":1},{"version":"1b4157fd3927ccadc0c9bb2f13a5df776eb465b771a3e1855d56c14cd694b754","impliedFormat":1},{"version":"b718cec116d24578c7da7f806efdb5c6f3cc50865de75dfaa164a598b9ef0d81","impliedFormat":1},{"version":"ddd6ba6ad9d5c36ba2054101ee2179436ca1638450bd905a7f8381f975be0c56","impliedFormat":1},{"version":"ee91906bbd54b4717fecbcb06131657c86065b69db88a1c4e50df6652e9063ad","impliedFormat":1},{"version":"eeb7cfe4e1e3db257d2e76e6c30ef8aac481782d70852f4298a417efa6f11360","impliedFormat":1},{"version":"a266f92c448e9cd8ad239d3a1d8bed9a6b8fb1cedc12da6b38912f860a6b8b3a","impliedFormat":1},{"version":"ceb184af5c8937984c247b2d7527a061a0076fe101f753653259156fb2972ffb","impliedFormat":1},{"version":"a7fc8bb34e43781bed4ffaaafd19dd678b90c18337fca50f725cde934307ae23","impliedFormat":1},{"version":"4305cae371bea0a59474a992a97a3fd2c79af841edf5f720239924ef0b7e1ff2","impliedFormat":1},{"version":"08fb0b286201fc501142dfebc3774d105474ba744a09d9abd318cf2b4508f440","impliedFormat":1},{"version":"c713bc377e433cb85d701c473b1729b8fc639976ea815108a1b5acfd9c43e848","impliedFormat":1},{"version":"74f38825ab97f299aed3dc74c876b119da4ae8c3fb1414c0d72bbecbae185016","impliedFormat":1},{"version":"793f725d6dfc5f9752bb031a9a65b64fed8145f1046b94abbae38890d78e7bb6","impliedFormat":1},{"version":"34da69a42a5ffc6ee57ca40227fefbfb8c149714218e7b07685f43ddac617acc","impliedFormat":1},{"version":"9900fe92cd51828bced1c8b6516d9a6accce4387e38c7df168125d16836112ef","impliedFormat":1},{"version":"352b74c9645b8e04249f982f2e31c129c3db5f760b721546d41c1e2c382e3f8f","impliedFormat":1},{"version":"0e1da9078db3b04eb8561a6212e5f9b10db10f6e11d2751fc21bc9fd921a3883","impliedFormat":1},{"version":"d50fc48e9a456df1a5be7438a85fe1fced135eef5757efd34cdf1ce0beadced9","impliedFormat":1},{"version":"9057300104971ea3e923f32d2075438b984c92b4aeb16ead688cdcebb540d7f0","impliedFormat":1},{"version":"3ebff5dc95fe3423e50f244d3bea7bf8ee8181c3ac2054e1c9de9d9bbe35cac3","impliedFormat":1},{"version":"9f0eea297bf474845f0e7e244db5baceb3bce4d927d420fdea392a90a02e3595","impliedFormat":1},{"version":"5a3315e7ca32debe3ec7c10cfb4c9141324ee28b98103f692633e329b5b40b4c","impliedFormat":1},{"version":"6e373f79212d3482e09bfc383b40202e970a3fbab6d6598b9f6396bc181e6369","impliedFormat":1},{"version":"f27a6967cbf7485cfb541e1cc4656d9970b841d71d5e648f51ddaffb31cd613e","impliedFormat":1},{"version":"2dc0d81e5658a634bc1b0c2b336ba7127829866d0e61d7d5b30b42634babce86","impliedFormat":1},{"version":"c0e5559582ee155fdd9999d5d785a95e92955304de16273ed302e778324478f6","impliedFormat":1},{"version":"92ca2bd635cfe90c60800b533ee3d5a7f498435e0985528c845d50c4b848b538","impliedFormat":1},{"version":"c1e0b4f9735a074f342c360425a13b20e7813909f94e9f23d0e5f90135fe0a5e","impliedFormat":1},{"version":"5ef82cc609601c321b1c5d59a3e15e9f164acab9058a19c616ac2e4ee08d35b6","impliedFormat":1},{"version":"f41260db9ca27dbe307fb1abb7df7478c64026ee078f0b01a65b475543fb0435","impliedFormat":1},{"version":"ee6b34572cc84410ef4358532fa12530444b621b1f4ac5187c7f15491d26f43b","impliedFormat":1},{"version":"e9144be696b0e4ce4cf0e3b0d9a74b947ccc7bc89692f0eb6cd0dbfe934bfcac","impliedFormat":1},{"version":"0f4810b4ee6170c0a1211ee27c5f481df485d0cdf5d02e96840cc079e52fd389","impliedFormat":1},{"version":"11a8318433c937a433a00580e0261380e3e239c37be00d1866994fec224a1f61","impliedFormat":1},{"version":"c336718abaca4a05d6fb87a7d1b36e85ae857f0c1d415a71f59cc58b4110bb41","impliedFormat":1},{"version":"3169475cbeea76025a1a72c74460cab1225cb102367f3672300680023ca1885d","impliedFormat":1},{"version":"a6cca15daff065b54e460262b4e18ee462e1487247cda39cca759614b3848385","impliedFormat":1},{"version":"b4c59e76774ab6dc8c16c5890310d62206780887e155003a47d5cd2911e2f96c","impliedFormat":1},{"version":"0146f5a136e37f5341e509031d246fbe447b3e687349a46cc9e37a7368e46582","impliedFormat":1},{"version":"5185f1904971f550f4fe0207a2539d8a7446871e136869cbf645602e6843f4cd","impliedFormat":1},{"version":"96ea6662bf7e178f87b05ecc2bf1a8eb0c5a1674964cac1fe5bf6c322c673d2f","impliedFormat":1},{"version":"fbcb7cd8274c4891285c6d9bcf47868c0f8d31a2cdda22b571c3325da75b7dcb","impliedFormat":1},{"version":"bef8c168fce89bff5cc813a3ae818237912423332a6fe448a55f77921e5c0f3f","impliedFormat":1},{"version":"d867a430b6172ac22c79abfe230e137c38fcbb4323907e468b02dd682c8ee50e","impliedFormat":1},{"version":"f003c0e5eae340ec49191ebcd9af5ecb3881c8e2637e38220b750c3db1ba6b1f","impliedFormat":1},{"version":"6fdb72bb5adddde2aefa825c3f633f12f641b33b742b82dd491e61152a7cfbc6","impliedFormat":1},{"version":"0a23d98328279e34e345753253c266e84ea97d4b00ab763015069c9d1fb5771f","impliedFormat":1},{"version":"2ed7b58cba580a4c53c792cc997dbdd3a6d586a359339d82e1685466491f93d1","impliedFormat":1},{"version":"9cb9bc2f1aad13d17417f960d7f8d5fdc765ca8eb2629840890035b2a323e68e","impliedFormat":1},{"version":"2d84d6219eab2ae2cb72e7b308cdb710187a43aa7647e56d01a8563d0310d28c","impliedFormat":1},{"version":"bd106e41e00d3f44b0b931cbf103a3d96f43ce855884ca071cab98eaa52de8c7","impliedFormat":1},{"version":"70885a32453f417b88167d1bd5542287e6e93dc8299e9f10d954e1ac90029719","impliedFormat":1},{"version":"b4e90b158531051cd7e2e2e48d1739be4e98d4cf62d0e60f79cbca9a4bf4ec78","impliedFormat":1},{"version":"022688f07b5cefe15ff06ec4931d8d2cb84f5d7199211e788dbfb0a6d2cbb380","impliedFormat":1},{"version":"acadc3e8df6d39ff8faa60a09f76f1ab8124bc2dc094a0076eb2e0bc58d21807","impliedFormat":1},{"version":"8fbde2904017ded286f826ef73acfc56e0bbb3499f0a9595a558ad5837e592d1","impliedFormat":1},{"version":"38739e4ffcb2dfa217cafe180e06ee957e28fc76c41db070f8050ae0f46436f1","impliedFormat":1},{"version":"1dd227080b37223a0d12b0c5b2552a130e6016b972c70ef5791a51d03153ddf7","impliedFormat":1},{"version":"82d1790e55436e03e18748ed55d036e109f8aba192d4d13abf58cbbaef3b8cc0","impliedFormat":1},{"version":"e34a8a548aa5b9d683e831f1a80ea36c4eb64322f6584d069f05d7ddb005d4fa","impliedFormat":1},{"version":"e9f41467e0d7abcc4815304f462bf95797ced997e5b7d0bd89bf687051639ed7","impliedFormat":1},{"version":"8dbb7562096c4b60cbf21b8c1a68a3031e6fb34f7fea5268e597a6ebe372ec79","impliedFormat":1},{"version":"ed9dfa8d2ade30a4c9d6dfca3eca21ff256963ffd55e4ba12aa294fdd239b5f9","impliedFormat":1},{"version":"4d7b72bbaf1426e6512e1be8ff62e299d9cc409a5c2a3d51f0b5bb5f83913a5f","impliedFormat":1},{"version":"bd8e5561b9bf3f95c4cd3883a715647f7d5cbb1762a3b367de71ffc33bc78373","impliedFormat":1},{"version":"631d607890865ec568950248c6c32216c83f96b5c397a3ab3e85e8739efd3238","impliedFormat":1},{"version":"405926ce12c9c1cc64a4396ef2c677c12afe89414295c7ea2b16636f7af330e3","impliedFormat":1},{"version":"66965925e4efd9b5e5b19296c4fe22ae7244fc9ae251d4fd2b27f6cf315646fa","impliedFormat":1},{"version":"5d1800b4e9083b7d856a9a7c36aa369793de5209a79911fb3ffa71f192aea219","impliedFormat":1},{"version":"b9478ebd1a1bde7887b6facc87eb476b9360c8a96d5a4377eac4dc8a8442ea06","impliedFormat":1},{"version":"19d74f730530cf84a30503c986668cfec02e645db9c828ee45599b50e61272c2","impliedFormat":1},{"version":"e351e81e22772ed035eee3badaf463aab1cc2a6c92c374e640c517a1da5cca71","impliedFormat":1},{"version":"f10a193927f982a8b3817d90f41e93e17b6304224b741e07b1cff60c8ff086f8","impliedFormat":1},{"version":"97739e4777ae759447d9c0eb90b053f171cdac39e3f29c5e2d13be1a5e26c82e","impliedFormat":1},{"version":"7b6911a0ff3aa9999b916d6c777c8c125b45970b67e9a92efbbff80233e6adf6","impliedFormat":1},{"version":"85b6884bb3d1b11d2be83b02433a694b54de5d4eae5f1896afd4f0745c96ba5b","impliedFormat":1},{"version":"859737260d9ca241a3cf4e3c94762b13e87dc25a7cb1fa8b0d790a59440619a8","impliedFormat":1},{"version":"84f41080c4eb8f3939326fcdbb06f04ae8b18485d68bf192c25911de07ab6641","impliedFormat":1},{"version":"6b1383ba2ca0c8bb34b70b4380ae443c175cdecb410a2161c3dfef1a298fee86","impliedFormat":1},{"version":"fdc551d1d2edd5c7b2964c77c3a1ea151e6b60f578bc476f7c1b4662ccf588de","impliedFormat":1},{"version":"010fd09b42a849ded7e16894e3effd27b8cf18d268ab32e6b3acc1f6046e0854","impliedFormat":1},{"version":"8b35885be445d1e86006b1bff6452d7c248d9ad2efba52227fd7ac17615be6cb","impliedFormat":1},{"version":"a72baaa4428b523dfee36caab72b102b7c2e72d8e6100a05c89a82e03fee9340","impliedFormat":1},{"version":"8b09f064750938f59c768027950b3278f97e80a035d7af43314b5a95ef672103","impliedFormat":1},{"version":"dcce52b5703dc0e73ad861b3a92ebc30ec42fdb0eaf293a8e4e41b657f396454","impliedFormat":1},{"version":"0151055879c65de98d8df809e9edb2447419a36a9398ae45a8e9d168226ed7e4","impliedFormat":1},{"version":"589e392bd21d7dc5ff98e8b97ec70c0c924f1b7ae42491c74b5f3c592bbe8434","impliedFormat":1},{"version":"34b72bb0fe82c1037b40b6582c97f030a8b4eaac068bf16b4ed646336158df37","impliedFormat":1},{"version":"66c3ea8968e113367a821c15f7f5b26dbe25b35b5583f7ef694af1e86a96baeb","impliedFormat":1},{"version":"5e72efc6d33e1fa6d80eff89d0c745d69586fc55978819c2be94cf7f59efb0a3","impliedFormat":1},{"version":"52fc9ee4b26579c6c645d4f474f840cebd80f5dee6fac51fdaa7bfa91f3df266","impliedFormat":1},{"version":"512c3dc8e4cfb67323428de12627aab9251dfb0247ca0b69d4e189ac3acdf6cb","impliedFormat":1},{"version":"92648aded574f188213df87c6527faa748100164a8777351bf8d1b1edc12d4a6","impliedFormat":1},{"version":"d93e001ee471928a4da7a8d400e2fb0128f77d202d0009c7e61a9aac6c2b4b69","impliedFormat":1},{"version":"3ed77e5ec06e0282a1b53aee1d3e286e7b1c3d3f647af03135508db7b3397c86","impliedFormat":1},{"version":"24be77a98b2b25b2ffb4a7c2aa86aff5b1b23be5d8177088a529170f0263fc78","impliedFormat":1},{"version":"74f06bbd27efc3d0d1e36f055b8b84d15e1ec06ac2e7822c207c7cba247e7645","impliedFormat":1},{"version":"417ce7dfd176cada5af7c9d2eef781a6d0cc4b8d8824a36bccbce1190343262d","impliedFormat":1},{"version":"9a12588eb10a316e9fe38485109516a5c9edf591c49d0590617534b0e7215db4","impliedFormat":1},{"version":"5c69ff08262eba4cf2daef681617a79acaf1b94e385667cffd0c6be627010af3","impliedFormat":1},{"version":"b2beb93649cd8a581a54524ba14192856a4123beea4e6f4ff1a7111801f6967a","impliedFormat":1},{"version":"aff3d9f9cf74670b009817a387dd25f5c7fce685569c4a1d6e99e1b70199b9fa","impliedFormat":1},{"version":"874643d8843a5f4dfb735dd62cdb3c665735bbbdfbd15e47a3f9026acc04ce26","impliedFormat":1},{"version":"80a75e9c29374b86f8b8bc7d8038dbefec8761f8f815c584dbe88577e7b3a2d6","impliedFormat":1},{"version":"33ce9eb3686b4636c44508adcc4a2f1852bee7ab1a8d51efb6bac0381d25e199","impliedFormat":1},{"version":"756ec4f4eadeff59b8a0fbf9bf597b379ebe61131334d37b3cc6af4870bc3c5c","impliedFormat":1},{"version":"ce65e16b25d8bdaa939f28c5ae0e9c36df87aa59050f12d3d75b7258a7fc3bda","impliedFormat":1},{"version":"4e646c101f45f2de819dc62decb4b21ff77c5d3d4932e4a2b76947523fd3fcd5","impliedFormat":1},{"version":"0551cc44727433c13cfb448f97a8c77bdde36cf054ec90506ab12b22ed839b49","impliedFormat":1},{"version":"2d95c36fbfa7c0fa9d80a3051758c9a63ed177295266c6706486f3e1ccb94169","impliedFormat":1},{"version":"5c5dce5ecaa7b9ea138aeef3d8508af3f4fee25ec7cef4571a061a6b57b8220d","impliedFormat":1},{"version":"8844cbc376ee6beb891e8cdc6aedff98c49188225b5d0b542ea0dc4256b7a293","impliedFormat":1},{"version":"df7a4047dc9199bbebd13d3fe43ec120b827fe8fa6c24c1d313cf0988f1092d7","impliedFormat":1},{"version":"ea69246b4eccfc1bcce413c4804049ff3136dc361c47695dcbaa0fcc344235d7","impliedFormat":1},{"version":"afd9f8f7eef80ad61fd22a99d0b73dd9d5899a58375966cca02e45ad2fc93e9d","impliedFormat":1},{"version":"29b3981f0f17367b258d0d6151d866b3291170052424c75e8419a4f3e2ebb19d","impliedFormat":1},{"version":"8c8a67ea36c8d7662052512a4a1e80221686b863ca018a882ee0592717430d6e","impliedFormat":1},{"version":"f062291361efddfafc414753a6bb529b56f5612853ee4d9d7a3059b81390dc6a","impliedFormat":1},{"version":"5c6a09cd4b041b184a26ce6c3f0b5f3d9cc45b1ea2c6dd6a3aa8f67cd905998a","impliedFormat":1},{"version":"4872f8400ef8818d490ecb245ab080adad9b9b2327a7d89e7acbb4d81d05e2a6","impliedFormat":1},{"version":"10b1168bf2453280be83acaf2877f95a9ec0c4887820a565a5c9767ef6126493","impliedFormat":1},{"version":"07221ff04db8edf40b93dc160a88d2fd911ced4c0e4b751dbd8e7043a22331c5","impliedFormat":1},{"version":"8cbaf3f8946ee33a061354adb3d390dbf2bcd06a858322a0eb06fac2013680e0","impliedFormat":1},{"version":"1df9817eb4d1e58576c04191ab0552b74403a9826fabf140b5e6e05212c0ef34","impliedFormat":1},{"version":"f7385a118e47139fc6c983b6d7f490e089db890d96833bf229e4a7463045e60d","impliedFormat":1},{"version":"2d4ac1511301aa34d5da7a762bd74ead7ca3177c0ffa091168047cb9b2c0144e","impliedFormat":1},{"version":"ae5937a8ae5718425f08c48d5666ddd19cb00acfdd2efc8254ce6b7bbc279751","impliedFormat":1},{"version":"44461f48a7067238183c473e8c6900ff27418797a7063e23ac3523f4333469ae","impliedFormat":1},{"version":"8e7ac0666db8f67ba6f642f31f4ebefaa8febe0b1f80a459a1f52f8e6754451c","impliedFormat":1},{"version":"5f00a81ecb53ab387e7ccfdedf1c31e8cf2e849403749c0848268fc43453398f","impliedFormat":1},{"version":"ae4b00775fc882a4215c0b7c2cf83d7dd4eeffa561401f3f9b4d5e70658ee7ab","impliedFormat":1},{"version":"4b469d0f839f97eb3b760f555d2ff1526d76248c9e0e3533c2b1fc8c6e6ffb67","impliedFormat":1},{"version":"95e934d2773c19499082ec02ab0c3c6134135ca34ae1c948f75ee3011abbaac0","impliedFormat":1},{"version":"8424dca1d16cf6ceef7f75f9530a31d132550ce15b75b9c860b74fe39afa88a9","impliedFormat":1},{"version":"34e70cc24aa63f6feaaf40fcc62198d5080d371d9ab691e03f6d6a1889a851fb","impliedFormat":1},{"version":"8be484df6a135cbef6e499063a052da9d26fd8196de578fb46e3a4cb2aaac3df","impliedFormat":1},{"version":"8d465858cfbcd7b55f343ab915d18cd6c3d71055766d3aec27f5593bd6ef3f38","impliedFormat":1},{"version":"1516e90c6683156b689ac214bcda342b4598131d7e7b729a312d731380c4f55c","impliedFormat":1},{"version":"4d42a77d1ab7c3eff9509b528dba9ea051dc7cefe8d431b98c959fd499a2af3a","impliedFormat":1},{"version":"5572bba5adcda3619469533161b867a33c602a634b583aa14058ae3fb0baf3cc","impliedFormat":1},{"version":"e5a9ead1bed1b9df13bded008a9427b343626cac08dd125a884ea6c98adfb2e5","impliedFormat":1},{"version":"423ddfe451928f0b6c09be58d816ec72354b5388bf522983b488b24f13935f5d","impliedFormat":1},{"version":"251f7893eae3b004264594d35c72750664357d25724b188b271314269513675e","impliedFormat":1},{"version":"d0751d19ed55c09b226d1fff1f54689cbbf860bc7a6538403f07abadae4b7423","impliedFormat":1},{"version":"0d233019594587f42d05755ca6157ed2b3cc77b81f1f06fbb67b318b0ce23e2a","impliedFormat":1},{"version":"f48c5210092027c859fa6deb9813ad8472644a4ebd0e86f97baa45e876fde942","impliedFormat":1},{"version":"ca87baa5f64ee2023e116a4c360c1fd91e634dfcd67eb3552f785c10252c1912","impliedFormat":1},{"version":"6c9f914f2dc75e00b968a31b34aa1a82fe6be803aa00f4b9dd9cfd0707206ed2","impliedFormat":1},{"version":"6a9e72c5cb4fad142f23672c8c2a8209195a303523f799ce2328c4aa27946e95","impliedFormat":1},{"version":"dd1ef51dcbcab1bc3301b00085d5700c495fa6e5fc641d4c6296c7e155ee16e1","impliedFormat":1},{"version":"7126101b1ff5b8661b3b2a56efedcf93243c63275cf86a3b15ac29949b11b8f0","impliedFormat":1},{"version":"1d02770f792c35f6fed1d905b61596a34d8e6255b7eeb6418f30135d0db576ec","impliedFormat":1},{"version":"21eb5abfaeb3b7b48252c81a456477989d00c19f4a6a7c01666939cc9356df86","impliedFormat":1},{"version":"e567c4c36bdc8a7157d994b4cce11ee7e5d47e43b229977362e96fc79da35fb9","impliedFormat":1},{"version":"0b5e86b6c17a678f34a2cb88c412cc860dc5dd63ea4600d03d5cc9c84a4a79f2","impliedFormat":1},{"version":"d8141e89119115927232d82737cfb53fded8defb35cb5b7c7e4d6755846b958d","impliedFormat":1},{"version":"761b25dc2ed19f82ef79c31dffad5df2455e4da72da8be965b3464d3c948cfb8","impliedFormat":1},{"version":"70fd842a092dd8fb22d42ea7992d3bde2b59c74936eb8715f6d29661ff28a645","impliedFormat":1},{"version":"572a2b929dd6e294dd911127f8829ab71b3f4e3fbeff7b72168feeeb5d7058d1","impliedFormat":1},{"version":"46ce1ab52fe147a2ca7c7d89a75771d7dc7ecd3133239323a10238a7b773fd51","impliedFormat":1},{"version":"36aec88bef5b031ebcbe334597252c9f16c0f688d214e5e9586c559fe7f8ffc2","impliedFormat":1},{"version":"4b5a0f9b3c79d45597ef4da3a61cbf8e59f6d25a9cfb7706cb6081463b9c2a48","impliedFormat":1},{"version":"ad7810b5f57799d5f91c94cf8dd19ef72a530a7663cb5f7f0d7316d5c34cde32","impliedFormat":1},{"version":"e7ac56841cae51f5f44dcf7d73448495d38fb96920c4f884b2fd53f2cf307513","impliedFormat":1},{"version":"1669804d66d6e92afbc9862cb8ec7faffa08491f0923f92a40993c46599f0f93","impliedFormat":1},{"version":"f170467034c6275cdc67a3b3fac692d423c35392a24a338cc5191d639c0d62ee","impliedFormat":1},{"version":"b4a20cf747efafb32c090b9c9d6cf044e7fb728b2cf95663f125225552dc2ae4","impliedFormat":1},{"version":"70b7d442ac9343610024fd25da18c34b6d815b3fec1acfa67515b8e5e06f83a8","impliedFormat":1},{"version":"06983b05cfee206b6522a566323b3c952f7af3ad86e6a59caa7dd7788662b24b","impliedFormat":1},{"version":"77d54893ff38e2c4e9de6c0159582efd3aa450b7e2aec460a12c441e7c9f4509","impliedFormat":1},{"version":"e9dabdebc934669ac5bc0e1368554072f7655ca31c8266ce428b34f6506396f7","impliedFormat":1},{"version":"200ae039e4edd581b51d0b958fbc3a5927f42c27ced09b6a964007e13c7a108d","impliedFormat":1},{"version":"41ea5e7a1f00ee88d73db1540350af42438e42ee499000447c080102edd237a1","impliedFormat":1},{"version":"895f0ba7a59efa6a10dfa95b01fc24a335f49c3562da6a7ef80c5819a0809981","impliedFormat":1},{"version":"09e71ea478043bff3183435821c348ca25032aa8c43a3b5dfc9bfdaa65cfde0f","impliedFormat":1},{"version":"f232805ea639298b38d0880a2db782b309cd4ef1921bc59fe1baaa8f7be5db03","impliedFormat":1},{"version":"750eb232d9703e5ae51b897e23f6ad0d35a2b3c58385e7c87c5c937e2e165103","impliedFormat":1},{"version":"ac8630f4d9ecd7f86e7e2ea91ec6f75eda8027561c07bd082bc43f170edb2741","impliedFormat":1},{"version":"3497e4cdbcff5f5244eac8589244c23f77b67027eb19dd4dcea8fc10cd850de8","impliedFormat":1},{"version":"5b6968a5f33515823ad70c1a040a1d7e4d1226e20efb09750d1d703d397aa889","impliedFormat":1},{"version":"8ba805745326b7e732f80ec507dd79c360746cd44ef12f9b926d3e9f9289a96b","impliedFormat":1},{"version":"08b12baa5f58a9d2ea8128f6da135dc6eeb5c07281a7a5be81155a0a78762bfa","impliedFormat":1},{"version":"5e6cbe208a05fc4c8f2f1781afe311cc0a7e8be275cf7649fc4102a1aea6bdf0","impliedFormat":1},{"version":"75eceeaa7506acd621f275dc6497d3062f6c7f8987cf639d205752933588836b","impliedFormat":1},{"version":"0aca3b432b3354cca8990550ab3ad860420eff7653363cbccabf10c58cc45a61","impliedFormat":1},{"version":"1769f6a238381e15dd5df4e38ba1b90db89928b45c75898499992bcbd0a4cae9","impliedFormat":1},{"version":"3d463e8de697b39fe611a982193fb0dea6ca9bd447deb60667b105682558d715","impliedFormat":1},{"version":"ff73ca4fb4b691504a2236b6e0fb9cdcb5675cc15a01039ed299bde0a76dff35","impliedFormat":1},{"version":"111456884ce834121d2f12e39b0561fa9cb8c7fd094ad954cb2d5d771bef1987","impliedFormat":1},{"version":"de788aa02071405d0a9416c1c80eac116022b905424dc95ffba85d0d4551a9ac","impliedFormat":1},{"version":"6ff7ae085d87e14635daefe68ed177f39419f9960c37fbf9b1ff0ad68c3ae83b","impliedFormat":1},{"version":"18d1f659058555837f49199eb6b106519dbdabb01a15e1771ef62bbe40160e24","impliedFormat":1},{"version":"caef0f0df50bbd8895e9c31eb4bbe72df9532f819be97a2778eec9f384b47117","impliedFormat":1},{"version":"f15bcb50a93f349cf0b2134e6668ccf17aadb33dc1f619d7e2d7924993ad292a","impliedFormat":1},{"version":"9126fa13d92dbfec3031ca5fc1bad65383107475d36e34febcd86aad4c35a4eb","impliedFormat":1},{"version":"a744d4f40862822af6029efc08b8bf61ffbac8e09ce075681132cfe239130c74","impliedFormat":1},{"version":"c7160fce2f0734f5eee98139b34a193200fd20e20a409a6c387a3fc64db99281","impliedFormat":1},{"version":"4061072f0a70d785498ad50e16a1ca4209c94468110a0ac800176b63323fa289","impliedFormat":1},{"version":"185ffe67086fc9d74b1d39551b7c4ba9c6c9161b14a8534bf3a19927bf435f06","impliedFormat":1},{"version":"98649dc10637bcc2b39f91a2c1720320ab4354135bf59e4050d2195052e7d553","impliedFormat":1},{"version":"ada9d7f52dc0d34b53a82d9abb4b1141239575254c2887dcf98a7100b646b7f2","impliedFormat":1},{"version":"cb0fc0382b68ebd1e420cde262f9a326ef0edfda73292f3942f6588a4e6aa8dc","impliedFormat":1},{"version":"d2ea6d4d5e8d46b27efd714002bba2a0cca308a174fbf40b53dc6afdb597c683","impliedFormat":1},{"version":"d5c1e681f5ee13e37ea5b553220de475eb562490e1a9960aa60e5c39fbddbd70","impliedFormat":1},{"version":"e51f6df10fd7c6c4fadf6cf1ebbda0a0faa5aa85e54e838704b4faf2446fff2b","impliedFormat":1},{"version":"d19c6729b48e1eb9e8006d59a856f48836fe57a68110f7b66d48977cf6a86d42","impliedFormat":1},{"version":"9431bc5daa345880de6c59c9a50ff0902fa9256eca294f1ea22c5464ed2d23fa","impliedFormat":1},{"version":"473d0df9d34816dfac70946912051e1e69ad37d3e734e590e8140d658c33fffa","impliedFormat":1},{"version":"c84b8fd5ff1855571d3b7edbafb09b1861514088b0358e56fde373c987500a58","impliedFormat":1},{"version":"d4e8d205e18a3d2b214c00c9770baddaf9faae389f3487d46963cfa12b42cb44","impliedFormat":1},{"version":"d22e4127561f8e13310a3f3ae7e830d5a3761584a7ab56b959f40c493155d813","impliedFormat":1},{"version":"00ccb22634a0d010a820333534fdce0176717a5fe7986845172b3d6fa5ece0f2","impliedFormat":1},{"version":"52ac1f8169aee346e0b6f5ceae52bc528743d2a3c951dc31af4d92d907d04877","impliedFormat":1},{"version":"4ead49ae88b1e0033ff9e51fe46b5fdb2a603819d5531189ae3b8b139c0ad492","impliedFormat":1},{"version":"a5abbdd26bac0e008d24f8d319b9f4e6d722dceb98266fda6adb52f32df975d7","impliedFormat":1},{"version":"32f3d94e52fd5dbc5724daed3bd6d4fd054548470a01c3dec8d5b3c3b2792aaa","impliedFormat":1},{"version":"a10c447528679f05b1a6c79ce70eb6cd62894b3de0f32bf6ae37202db91b4943","impliedFormat":1},{"version":"c86a4787f596a9fcbb8c8c04334d1ca7cd21c9fdd5ecb411ead7e6b1b83eab8c","impliedFormat":1},{"version":"23515a3d8b21e606586b5d543cc4a4426accd4285bc38f35a254ce0a14a48b9f","impliedFormat":1},{"version":"802c8139d535b922a467d9a32e8a03d9f4fa89553d55a9f0ba55e475d58e4cee","impliedFormat":1},{"version":"d955b5db3712dff0b7a1da22c89cfdd8f30924dd548238fcac56b0698163ae83","impliedFormat":1},{"version":"c122f255a6cdb803d4927028ba792c9fe4607bc674d197b1ee69e2a43659cbf7","impliedFormat":1},{"version":"aac34d91e616d9cb2a28527a5d30d8403630dfdb307da86b4b04b16a1338fbfc","impliedFormat":1},{"version":"facb42ba54cefafd9132558bd5bfc6c0f5a306b1b085ec974313b84b9fc0dc29","impliedFormat":1},{"version":"985a77c5add1a9f0c3cf8eb34cfcd15106a5b3e16f53413d915cb5b488179174","impliedFormat":1},{"version":"c006400bced4e5c8112ec7323981b89d7e7128d7f28c2011dbaa8e1996b5774e","impliedFormat":1},{"version":"9e1825f69e535c983a6e6eaa2c1d85f0f34c0a3e3a8f38e5bf9ee13af6fe93a8","impliedFormat":1},{"version":"45800dedd60d6a455d6f2a09a412b67b3c7125bf449086d8f6e3a6a2b79ce448","impliedFormat":1},{"version":"b2d42b756afc13d05956e71a2dfbab95560f0d03c601c1ec10c96aa30b2a1855","impliedFormat":1},{"version":"5c59460be7d9bb7e508450f4f9dbfa02011da0b7e5e18191f66b14bf0898fe02","impliedFormat":1},{"version":"2c12c226874a3f26c717761fb9eb1b8f3716075bc27eb86a1cf816a0c0322fe1","impliedFormat":1},{"version":"d07f076060a69fd6a22e3be1d8dbb91e5f68787456303e98efa09d5304f81d9e","impliedFormat":1},{"version":"ac6cd894695640b49ec374167c0cf5ab2177cd480fe4de32af60e1a7aaadc57e","impliedFormat":1},{"version":"8f6329132a33e78e78ba204ed9379d82b36ad1a8a3934eb586c23f9db6378db9","impliedFormat":1},{"version":"f379014a91e7c5a3e1d890c21f1d3605e824b6c9259e5e631000df65491bad12","impliedFormat":1},{"version":"b4e792b81b42acac0658c9758eac10c42e4a9530ea51bef57ef75385c8f32775","impliedFormat":1},{"version":"070caf0b4a8a90adea44868c689f4547dd990beffe5e674369fc47ff47c7ff8f","impliedFormat":1},{"version":"5d2728604f7add236783856f7d361ccfa70c93a966faea273479bb660fb834d6","impliedFormat":1},{"version":"4fcf5554cfc0e3887aeaee49b6fb6c47e52d195f83a0758999391b917d7d448b","impliedFormat":1},{"version":"861a78fb0d93245c7240b83841ba5608890855c1e0b1e42422225a8b945abac1","impliedFormat":1},{"version":"379cad233beff629bf5115c917a130792f67818cc06e8bd8ba104263c27c18e9","impliedFormat":1},{"version":"7d294b13d4d80448bd51064f5480fa579544787f4bb383a49ad5ad228900d7c4","impliedFormat":1},{"version":"60637aea184cc3302a6fe338c7115919cc50ec91f82a7a5025a1b12dc94db04e","impliedFormat":1},{"version":"c6a649b2185188534219c8ec4ca87dcd7109d0e20c7bacfc9a647680cc51d5dd","impliedFormat":1},{"version":"0360cb1306ca4178bcff2624cf57d0c2d0eb76659a6a47dfbbd3223695422a98","impliedFormat":1},{"version":"26f0896d7227902550e6c0516cc72e270592162a65186a6a2ce9f756b8c916dd","impliedFormat":1},{"version":"cb80aacd2be805443d8e133fb349a4182d2b88e66a0b7e6c1b6e7ee2d5a82beb","impliedFormat":1},{"version":"699b1e99c0be507325f605e9237b692fddee740385a600dff4baa3b5c254eddd","impliedFormat":1},{"version":"534284f441afb22cf8a656315c63a58ae6eb2a4eb4ea8198e3bbe079f4f7f52b","impliedFormat":1},{"version":"831f70908b37571a3e8379c27a7856037f682d8c3807aa9a6457f5f34681de7d","impliedFormat":1},{"version":"0e359b91891b975739ebac59f24f2b912e2de4856337c0d3797f103ab741b7da","impliedFormat":1},{"version":"066b8f54d364a3af826dc8f6f5b504adde07ff6c74e26df59186db089d3cbf02","impliedFormat":1},{"version":"89a37d919d349bbedbea154f94947625063fda71fefd4622e25665dcd0c8b303","impliedFormat":1},{"version":"dd9a706e60c216ddf018332b4daf285234d9aa10914162f8ff6bf65fb0f64972","impliedFormat":1},{"version":"7866ba701c53181cff579da663b30ac50c0d8553469af5e2596ff13bfc8000b8","impliedFormat":1},{"version":"d7c6f9990437066ee3cf5d242a192e603d8c5fd6c9dbdc0221efdde4918da0a2","impliedFormat":1},{"version":"b2f81b06c7da147fa4a4bc62ea1d3cb819174adc696b40e56f6c27021a00dace","impliedFormat":1},{"version":"9f2257469fc12e84b5ab6a1f12228643fbf302c13cb24227a54a0998c18d92cc","impliedFormat":1},{"version":"dbfdf85a3f34563ebb314500eee184c5e77933124895f4f1725e070256cee874","impliedFormat":1},{"version":"ef6ce13d4fcd56d1129969f503d5c0fb0bf8959f39e57d86f47928de5612a5e3","impliedFormat":1},{"version":"31b631f3156831f838bbc7e58b0c4c5eaa35e1ddbb30adb200380fb7ec742c81","impliedFormat":1},{"version":"8b17b5983755a97f921845a588013a4889f11afbe5e6ece3f375899b0d7e5818","impliedFormat":1},{"version":"2cfa00a23964d342b5d43d309b37f9caa4f09c4c58cc122815168b4fd339ad3d","impliedFormat":1},{"version":"6dcfb6d01165fa3292b7214ecaee7195a7dfc03a9dda8e669cbb5d15eb9ae963","impliedFormat":1},{"version":"12d220e519cbb62e9e26a099d892cf34a01c2db8476468e97f63bbb35b3a9419","impliedFormat":1},{"version":"cab26e7ac49d068083949753f8a76b4df00fc9ec76426fdfb8fcc27afbea0ed7","impliedFormat":1},{"version":"3a9c9ceb13d049653c10c07c053b88e6e0a6c274c3ee2c97ed2e2d6311030d16","impliedFormat":1},{"version":"fbb8f7626e3d6fceab99919f745d8fad54ad490ebda486f763903a7390157fbc","impliedFormat":1},{"version":"ab8fb8f380d2a1d153ab7b5a45ee91521883de5acec6999c3467e140b8441559","impliedFormat":1},{"version":"d65ecab0fbb6c3083e14a922040b91c4b979e51266aeb8471034685429e38af7","impliedFormat":1},{"version":"28d71eb4e15afdffc0d016cccba3ea2cb6a61724d4d7acace03d99d302cd6cd1","impliedFormat":1},{"version":"219ce3693abd985077c53e72abdb3594bed6a0276c4312ff04208ab4ead77367","impliedFormat":1},{"version":"4a4c0578d643884d8f1e40d9f8ed381519e33eae8db75b8bfad8a6a77dd069bf","impliedFormat":1},{"version":"09dc1fd0d97adc5e124918cee870eb0df5076682496d4acd7d1b8624e7e0e54d","impliedFormat":1},{"version":"15f3347c77df4e16204916094e38eec246864a0c3082532252095f1709b34cad","impliedFormat":1},{"version":"f1bd4870165359ac6a36538781e81b50f3deae4ee9ed839e80436f143ed46b58","impliedFormat":1},{"version":"b6ac8eb2c9c9f7238b6cbb1359c0506a31a1471d59211288ac10f08f09a80ed4","impliedFormat":1},{"version":"39a6bec7ee4dcee453b4ba3992a3af8b581fe91f7b44e162013825ea7df708a4","impliedFormat":1},{"version":"0056f271d22f8998725a38e91fbf50216fdc0fdb6cb529ecc6054e67ff392075","impliedFormat":1},{"version":"3a2fd78249e70b68a3d22ed47d0519b4b1095ce4381cad292f3dfaaa5c773338","impliedFormat":1},{"version":"ac4a22e9fe604aa918177bba3f89e971226acb5ab4ac47fd265e43f4792586ad","impliedFormat":1},{"version":"a2ffb5fed48217ac31ec3a2207f023e74752508718d349abbd7d4cd069793806","impliedFormat":1},{"version":"62021e7a61c0b9b37643899969929caa847350433d55ab281fc6c0c375ddff1d","impliedFormat":1},{"version":"cca3383c4d73f155b01515c792548324fb06a297190d53cde9a5b1a7a8f04e41","impliedFormat":1},{"version":"b7355c123386964aa7be9c10978c40446c6a2cdba14f73f3926334507d20c964","impliedFormat":1},{"version":"8aa744f90173f17362c085f17d86d4a3a1fb9a32a4d7969f0763f641a32543a8","impliedFormat":1},{"version":"7f783dce98b969ed89695e7c9764a960e1d1f6073484a57a58da3c6a9cd0ae1f","impliedFormat":1},{"version":"ee8fd8fd9003730f61fa0c579a176daa23613db0ad3d65866a025a3c0c9a2215","impliedFormat":1},{"version":"54f9a24fa12358cb53e19ebfc3560104ab046b988dac9626dad370f981286368","impliedFormat":1},{"version":"0a9cc67e52f42bd908ab54d5d702bb9e26a45502920656c34f5f2c7a7da0d414","impliedFormat":1},{"version":"a7bf3bedfc911717e9b5bc7a393b818d8b19a5536db006e9a2ec25392bb2e601","impliedFormat":1},{"version":"0d0e4044562ba56900ca0e9eaf37117be00fec5a77f8b35c2dbb29f55f6d6e9b","impliedFormat":1},{"version":"e4c7a5e694c3dba371393fe489c40c732033f98734154dcc7e6c47f9df15324b","impliedFormat":1},{"version":"dead9c01917ba658c8ae724d5ef69ede49210289e8fcbe8da982822674f48678","impliedFormat":1},{"version":"e4bae72355961ddad69cdc22e46cff2514968d287e01a03a4917bf2f75bb942c","impliedFormat":1},{"version":"1650cc721eb2643cb3e273b3a2ac4169bac91a704c0d22e9bde25e3a06f53612","impliedFormat":1},{"version":"868fec6baca6f7839d701982126a2f7e0272478e84e763bf796bbb095be67adb","impliedFormat":1},{"version":"0ad20726fa7ef0f7d08a17c59b6c11344a641cca6764d09892167032d54b7135","impliedFormat":1},{"version":"3089a1f36131e04d5ba68105bdc48c8985c58fc7353512c735a7217ead341417","impliedFormat":1},{"version":"e882b228c177799202ffa23c6284682f1589b6b88c8c9e48e00a6bf5108e79aa","impliedFormat":1},{"version":"34a3d47bd4bbcfa5155d5b6cca4e18064b9ff36e03a761292cc843fb6e4dc7fe","impliedFormat":1},{"version":"31d01cd11ffd558cbf3bccd8dd57ad79e2828cf135f03ed6fb9bbf5f0fd94bb8","impliedFormat":1},{"version":"bac1f96f766a3b9ce3b40890e8de8f787ccd13588087a7fff7a2d849d98eaab7","impliedFormat":1},{"version":"ed859869ad66af47c594d169530af6eb36bddc61824bf49f2482d6fee9f57795","impliedFormat":1},{"version":"85842a98547adf0be0307eb218c1ec415bf5f5010e5839505be1fe253739e17a","impliedFormat":1},{"version":"e83c305750e7a22dc8c2fc70362c79a44ec1cf4d30d26c65fe2de8be4bde76ab","impliedFormat":1},{"version":"5d0d49bb6ae7338dccd13eb1110d77a6df46d555577b26d197c326a74f739080","impliedFormat":1},{"version":"05a1b86a85a4aabefa9a9b5a79267d4c433ca5d5df456ba4b5c689c288f97e58","impliedFormat":1},{"version":"9a06bf2409393308c212c52c4ebc2081b9c0170407af03b9fdc95d4b03198065","impliedFormat":1},{"version":"63a198466a5c5c798bd5c6db1cd1c0fa81a6f9ceaf679cec0afe14be365c035c","impliedFormat":1},{"version":"5402036a2361174f1ac3dc2b4794cd43e1232202a4e71a2b77a37534061fefe2","impliedFormat":1},{"version":"641d5c6886c93b756c653b267397f2f2c2af501862c0ad7578b76443f9c08100","impliedFormat":1},{"version":"b547a745a0c9a7a8f430c809cf5969195f0bf02ecd9acb8b186db5ca82cf6046","impliedFormat":1},{"version":"ba66e0baf0ef7c12dff81f7629f6f31086a942d24ff4a40172603d98186a8910","impliedFormat":1},{"version":"4dc270a5d299f8cbf61d5f1836e44f504bb8ddaba2aa6a0e2a1c2302b2ab08bd","impliedFormat":1},{"version":"c528e6617dc6909a7195acf479d48ab94ef0da6c0bbd5607126a5fea32cec926","impliedFormat":1},{"version":"714e16a7142036d17bfc88699dafce23f19f416ed55fdf26b305d5e7359963fa","impliedFormat":1},{"version":"ea35f9c1fc4ace3db30559665dded952a0fa1ea64baa2389f20f4a5d6ac1793b","impliedFormat":1},{"version":"321b4e689c711163d41e09c3c1525805033c03f3b9256fd49f7c5aeb9b6fe8cc","impliedFormat":1},{"version":"4545a61c19ce4ac9d509c4b27f47360cd226ed2a2ea204e949d3ef4db4efd88f","impliedFormat":1},{"version":"b6b29f93e837edafad065c50e1249a332288386b4eeb4cf53e7473a4f6d7472b","impliedFormat":1},{"version":"c209cc13619581b3bee8027a2c201d4bebd2552c5a05e440a4828ad881120d14","impliedFormat":1},{"version":"eca794784e68330323d62f18b6ee0933efeb90b32cea0645b3e7e6f7a3fada99","impliedFormat":1},{"version":"0574360dc7c7ec0cddcfb88f01861c9bb0e4b4abcb762f1f31b8d36debfa64df","impliedFormat":1},{"version":"094a2c4b5deb971a8eb3510434d40bc9474230efd33b0ac8a445f64f9a254c3c","impliedFormat":1},{"version":"8f0b09b33edf6d7258dff8629ab5c47f7a4ff539a556cddae986d92398fd8ef5","impliedFormat":1},{"version":"acbfb0b20163fdd664c76cefe935684ec9e0846f84dfa6ca32a2de0daae83370","impliedFormat":1},{"version":"c743d1d270d0b7c4d1cbf0f3d0cc74087485e04467077d7887141128a9b3f18e","impliedFormat":1},{"version":"f501c3c47906cefcfaccf4dde885a82ab42bfd186224fc09a3aa342e7cc5c15c","impliedFormat":1},{"version":"3b2983cdaa5231dba04ea33482e11cb6771c7f0e8211a4832071e004548a4e05","impliedFormat":1},{"version":"570d2e0a1543768323e8ffa7f00e961cf39d4a564d40543dfc7ad375d0244867","impliedFormat":1},{"version":"9abef7b72aadaf93fb5549fb551f1d929e46bcd1fc8c611914c7dc5775b741d2","impliedFormat":1},{"version":"5d7e050c8e64fc03748948b9bf685fc5d66a1bcbfc4877f824b4ac88c9395d9d","impliedFormat":1},{"version":"c634a499809811e217b1362e7c66df0641d3e8010fcc9d4f2279b597c1f31bc5","impliedFormat":1},{"version":"133ce5d3d1edda7ec73983eee529f4875ad69903df9d652b6376f5853b3adef0","impliedFormat":1},{"version":"e97b8883b9f992d7f6c19ff56ac9d341a47dbff8a9d76219b71f7688c6ca0867","impliedFormat":1},{"version":"f5bc5f8866d694ca381d4ce459df397376d552acc6c11c9838ac2edc2029d040","impliedFormat":1},{"version":"d662ead4bbdf2c78da7bdd3e00192ae404c90ff52ebf820f5d9d4a03f77f89d0","impliedFormat":1},{"version":"2de95a887908e242d47f2f4f0e0885e2810a932302cc1de902ee750c519f3a1e","impliedFormat":1},{"version":"120795858ede4786218bf6a117281fada49195e7122117b0b0a0d74613890a18","impliedFormat":1},{"version":"a6a6c65d220ef2900d466d2e127c69594bf36d9d52f5f2b3b0248b28a3af169a","impliedFormat":1},{"version":"39e714d0ae8ceaf8948062741b4960ed5867ffc1f62ee016eba2bd7171d01a04","impliedFormat":1},{"version":"64bf17668e6ba0553aa6d099882fb615b27a339a7f08b66355d827a66c0f3c2d","impliedFormat":1},{"version":"b1a10467a331d2e49f5b4931c59277d54be64d4bd0185adc0cb1c3db6702075d","impliedFormat":1},{"version":"a13575d8b3fde528d39eba200672236cb569f94e9c9405cfe0a57bb0aed617a1","impliedFormat":1},{"version":"f1c4ab21d1a207fc47b2cd0226f24eb077cde54648ce367301d106b5277e2743","impliedFormat":1},{"version":"171220b836e0c8faedf33e5a8010effd556aae3ffc6a460239a4e989216a9a54","impliedFormat":1},{"version":"b81cd816330033bd4003dafb6febe15c554762309cd711d05ddc92226a687c36","impliedFormat":1},{"version":"6a9850d631f5545ce7ef2474ba42cbba8b73227d1759ffcb73c5949841810f43","impliedFormat":1},{"version":"e16ed41eb06bd4e2314e5c31f0674f6058105f9135000cfa1ed533aaf77737e6","impliedFormat":1},{"version":"d49253254e095fed3ec924af5207908056a4b7cdc0cf33cf0f0adbdacf03b76a","impliedFormat":1},{"version":"f056b11a8b78cb4a05ebc20f2bd7a969e814408fa50bced8fa8f3f64e13e0380","impliedFormat":1},{"version":"26877238499c793040be7654eb7b1389dc9ec0a496a735f62285830647464674","impliedFormat":1},{"version":"c5c8e0707681d6206d03d9bef1f0bcc1e44a98c0a8a79faeddb74ea7ff79fa97","impliedFormat":1},{"version":"f5cf98c01c506b87af40834f741d4e669e4b25b8aab9e7e099f2a0d8b9081e50","impliedFormat":1},{"version":"b9e118b70c663500219d58725ed02bd1fd1b6c014795b0e9227b1f69dd9b1f4d","impliedFormat":1},{"version":"2641713baea1b5e28799dac58f69364ea4a40204a4ea1417a8b0ac6ebe3664bd","impliedFormat":1},{"version":"e591885dddf5b7df013a8fae0bfc2fd515954a6644205eb48c75209521535e16","impliedFormat":1},{"version":"683f523f486c543c3b716b5ea95ce39cd84e8ef961b02bca726fceecd4c06fc8","impliedFormat":1},{"version":"7caae8d4bfdd378b65e8b65cbda7164f5b290f39c357a77c15abe2b06a02d848","impliedFormat":1},{"version":"aa0acd0a8815601c8600232c87faf4906f15f7825ed2cd21149f13267aaa9dac","impliedFormat":1},{"version":"0e584bdefbeb881fe35cc3e429639082415dd6a55e7d0cd04edda97cf5cbf811","impliedFormat":1},{"version":"e04e6898da1a5689eac3044ecc8e5b164ad7c46152ba4d930b15f4ef46c793c3","impliedFormat":1},{"version":"7fe0823c85df0bc3a756bc59c38bb81a0937ce5f8a9d91255e81b9461ffae12e","impliedFormat":1},{"version":"29d536ba77d376bd01982ea3f4c203514480d9b64e216a871eca2cf583686287","impliedFormat":1},{"version":"c5a336b07804051e4679b1d670a6d1c325b9b67e1f8f448f4fe080ad7a77a61c","impliedFormat":1},{"version":"e1c11b68e8db62e7ed5f119a30ca5ac9ffc738a4027d0ad20a2ae42f18f769cb","impliedFormat":1},{"version":"b0c22e511c261051b0208a4349a95d1321f4ab94c2c4f325a4f8419cc2059a53","impliedFormat":1},{"version":"3de0bc975c80b0140f8bb69ff66a32caf3433a296ec0c2786ef9cdee433ebbf2","impliedFormat":1},{"version":"4fae70c33e26d81c0079a382e8372b0a15a5cae70a72cf57b1bc21df11f9557b","impliedFormat":1},{"version":"d4fc6b2f038186eee4c7932dc005aa383c2ac1616c43dc2cca2c8624d1608aae","impliedFormat":1},{"version":"310b7f872c1220aba920a4d0026125fc56bfa49e8019de5889016cbffc4e601f","impliedFormat":1},{"version":"8fa8780ed613835fd3ae9a34a712e4d7bbc3df26409b3d4912641dd3d6c3d0a1","impliedFormat":1},{"version":"1448c1c678e8a4da3bf450771c0feaaea3928e1cfc7b4188ab2cbf139b9c2f6c","impliedFormat":1},{"version":"9e6907c1d1c789780bb48a8eb43318dbe1a850dbb3356fecace6ec2ef901c3af","impliedFormat":1},{"version":"7d1b04da1d8632669fb051b79a43a9825a1ab9f626b354f59e237c979a61385a","impliedFormat":1},{"version":"e1151164c832671f1966128f7723b993dc5847137b91769f2eea172212eadd06","impliedFormat":1},{"version":"19de6075f255a0ffd5102794044de9316e458e224d63c85b00016c0d64b96976","impliedFormat":1},{"version":"ad9289d1896df761dfe04c4c0de4f11927a487f021f81da8112adaa9b5642358","impliedFormat":1},{"version":"db6322f061919ce835f47fcf670abec221e75e0061eeab6f56c0a4fc71ca475f","impliedFormat":1},{"version":"a9b3f461ff6fb6ecbca8c831cca3a5ac4f43ba8dbf5ac2cdad51c3512cbcbd92","impliedFormat":1},{"version":"306777701ae65cbc20ba0897af46848c11ce2923f00ed4b4f289d6f03fdf0e39","impliedFormat":1},{"version":"3ff0717ea23d49dc1dfbc9146cf90e50a4512791bb4c436fedc295e1a246a56e","impliedFormat":1},{"version":"545ab701c8e010079d3be41a144543e517603ad2a3631c74ca9b0b99b362bd92","impliedFormat":1},{"version":"20be7214f90d42c2632f18773a44502ff4a8057278287d57b52f8c41d0a35693","impliedFormat":1},{"version":"93a8e286144ef37c54c18941a9c77b65d16d6809510c3cac90932432a52320d1","impliedFormat":1},{"version":"c4c165dd5948034c6302855017c3b438443963d5ec2eaee6f3fdff46c2dc11e2","impliedFormat":1},{"version":"678c3f3fc8a1866670ead832f9f103d8f0486c30838b5bea9fa3b2aa0e487e02","impliedFormat":1},{"version":"765b77476230ed4c471156a5f7d8566c1871cf45318e9b6e0f832d4b5eef0ff9","impliedFormat":1},{"version":"74cec50a8045f1aa0604717ea0d1c82e3c00d6b30bb4fc2de47aaca9b77c2f65","impliedFormat":1},{"version":"e32a3e2305560771d8639258ad00cd5883b4a238c4ffb73f636baa30281e7a76","impliedFormat":1},{"version":"f0c8230b2e90436deb79416cd93521c501fe34c30ab92ff8745fa3b7693d4b21","impliedFormat":1},{"version":"0ee377db1ad20876089f9d6952306fd2f4788566e5c4700366d36d2f86ea6e45","impliedFormat":1},{"version":"fe85a864fd5f030374abfed0401c1c2af7e4eaf0503f6873617e8f632b545e19","impliedFormat":1},{"version":"7eb823875e7d8c2d961a0f7712c3e3a7764558440c9e5543be8072745cd90aea","impliedFormat":1},{"version":"fd8898807b0cd563fc3919b6967e8304e9cae2b503dc71b828bd51c543c63ef8","impliedFormat":1},{"version":"75b5a543054e65e701bc28218bde28bf9f3e9ce2acc9350a2747bfc81853c409","impliedFormat":1},{"version":"954a67c3be15d44e963af6ccb3022c77adf83509b2fc6b245d666a8348cbabb4","impliedFormat":1},{"version":"e4e765efd47120fd9fc387546b2689e18581966ad6422109526687460e1c159b","impliedFormat":1},{"version":"3fa64ad9425c1f9b66b53023f4512841cd26e8d8073bd5c18223b94efe0f10dc","impliedFormat":1},{"version":"eff57c4fb65b9eddea53f5ba08da0e7102037d45c1fe4c91720280859d65ff2e","impliedFormat":1},{"version":"8bf418291a4c2960943bfaf4ee6a6f86dbde7e3a65a2fcc40c70dd6f5c837b7e","impliedFormat":1},{"version":"df912cf09439c6f99663f71da6aba9051eff21b12e1c5317f3b88a07458bab8c","impliedFormat":1},{"version":"c85052cf556e46e9b89aeee55377f860aa919c04a6443687eb3e184571274fec","impliedFormat":1},{"version":"55c8c3c21d8b46ac8d5f1e138df8e3d45172a05383986bdfa3a2a2ba4a37136e","impliedFormat":1},{"version":"115fa813f7062e70d58fb799b39b60422205cc105a601666b41d5d7799ed6919","impliedFormat":1},{"version":"a940f949a7ca3c1939d010f346a4c866bf6c3e23aacdcafe324c79fa52d8204b","impliedFormat":1},{"version":"07646b6aa669a0f4ebcea6607ca44c44993ade6a493da95a0249b956941a4182","impliedFormat":1},{"version":"a0fad81db8a337dca2eafb5b707681352fc87c18846d2fa665132c2031ef440d","impliedFormat":1},{"version":"43a288eedb40aa2cbdbe17783855f37f0688d17e6270f78c78f8eaf8500600ed","impliedFormat":1},{"version":"0446331d015f9969ac5c6b38221292a7541e2cbf344d0ab4bb909a5606dace9a","impliedFormat":1},{"version":"f8987f9532ab2ee62d73ff7682a901bfef10957da2d3e448c5db9fef06938c33","impliedFormat":1},{"version":"e527b11035857e544ca7298df8b5f5b317b6846cda95bb56e75788dc7a9e25a4","impliedFormat":1},{"version":"63caf3989f80dbe2e1f43d69923a9cca0319b90b2bfb18ac58e4fa288e4b59ae","impliedFormat":1},{"version":"7fb487cee03f41553f6830cb0716d5ad85623f5f23bda0a4048aa84e44c45746","impliedFormat":1},{"version":"955179f89247cf44c7e4988199d1f3e8b82bb33d1b55beb2c033e7f485d551f5","impliedFormat":1},{"version":"7469c0ef7a4c72e7eaed08b526d7a862a990f303822e9ad6f8e5500e29c152e3","impliedFormat":1},{"version":"aa5d60718258e2603947135f2592c4fd31fef8409fe2d00e8540f60a39b8997c","impliedFormat":1},{"version":"5deb7834bcec00fa4083a4c52348348c0eb176257722e5ee87bf395eebbefcab","impliedFormat":1},{"version":"e8ed6b1e9c4f5a2f30c6c8314abdb621b8819afb28c34a26fc7f4cdf3fa2e2bb","impliedFormat":1},{"version":"34d343d49b0b6731d278621d340ef44257860fee5b4b0a18ae5f2c2638c2aae3","impliedFormat":1},{"version":"36faaeeb8b4f35b548ff963a3111e80573effa3f3d83d4fccd6492b4d1a5731d","impliedFormat":1},{"version":"822d69e66d9c7d3369f153fbe91724304cabf906d40c27a06bc27dadb118d07f","impliedFormat":1},{"version":"180ed98c072ef3315dca4c32ef220ea53159d922031df57f6bb23beb8ed8a985","impliedFormat":1},{"version":"c3b09cad2114292da49635369c454ad3da7178bbe714bda509b0e89b7afadf40","impliedFormat":1},{"version":"a2ed9e2c0b50bea98cae7de920dac0eaf72da14e9f2fbc495977d622719c2386","impliedFormat":1},{"version":"241a298aeec3bd23d161d1162fb5afc184dc27b2228d459b1549a9a21c1454df","impliedFormat":1},{"version":"beabc7d1295ca790c37e7c8acadcb74f31d4877b7f60a4f9e97ed3e95f5e6215","impliedFormat":1},{"version":"c4ae452f63a508091a1d95b0e0979f6a57faed2901835888aad9151e803e18c2","impliedFormat":1},{"version":"e54f155d7b2331dc7a74ec7d3d60f5cefa653f95acde88dec84eeb3e02a133c6","impliedFormat":1},{"version":"361e55b3b10b08c977f18bea405eb96e4b6902f565b01e647cf5cc710fe85edc","impliedFormat":1},{"version":"6c912256b2069d911ff875c9d631296bff7fda085a92e1921da915e6bbaf4684","impliedFormat":1},{"version":"29b645f0aa12f1b6ef8d5b2bb733f144077a41c8d8489abe1ab442a68e8becd6","impliedFormat":1},{"version":"f00936a0ea6112da36e40d8b70a1daf16d240272849b4cbf1f1c470d205e2c0d","impliedFormat":1},{"version":"f35378db395a987462732953f7771e19d878c82a70d04e0b2ff6ed5853ba7d87","impliedFormat":1},{"version":"87e6a130095c340b059bca12c4d9d3362d6c486f48a6e2dc9072b61843b89c53","impliedFormat":1},{"version":"8bfe6ea0dfbddd1648e45f1484f7c95de96268f4be38a6a350342ed3b7b1a916","impliedFormat":1},{"version":"496bf0814aebe9cb49bde18d72998c046582eb19164e15d2b70e1c3e4d17c2c3","impliedFormat":1},{"version":"3473e10a6aba1558a51ded4e73c372ae358200472b9b156fd59a93ca08e3f3a2","impliedFormat":1},{"version":"991bd288e700cbe244b9b67b4d69b6dd338accd4f543b08d462939c6fb5504b0","impliedFormat":1},{"version":"556185a4eb756aa77a921e9bb9d4358f1da7be3708ec9f3f9bc43ee1f6ea2e11","impliedFormat":1},{"version":"da172d28ae83a2417a6073d75f856c5427add48fbb633d6d47250b73c372cfb7","impliedFormat":1},{"version":"55b1f786b67e8c6636cb3fd24ee982bbbc3c06792a561d08974289604fc5e1e8","impliedFormat":1},{"version":"557fae5313ba7ee3a5a2584a7988c9ff2c8d66c7a24ea79375e6480a9ea186ba","impliedFormat":1},{"version":"f6f543698e0aeebf51b38ba200574963bb9a52d218a7cb2c0a9861b35f31bd3f","impliedFormat":1},{"version":"129aa6ab416178f97ba3e63209136cff3b7a0f8d2cdc9534a5ac04a3c84fe83e","impliedFormat":1},{"version":"f006150adbdede78595c20a3ee22677ed5ee457eff5f9dff111b88de2269486f","impliedFormat":1},{"version":"16377294b3a4da7bd0b1df7965f5f76fdc2b766e0913844a729b17b41aef17f2","impliedFormat":1},{"version":"606e1be72d7d2fc64e2434c7bcf52c91a46ec51bcec31d720adb78d05870e9f4","impliedFormat":1},{"version":"c280881394b518a0b21a252f1831e1007ad80be67abc056a4594e91f8d4a51dd","impliedFormat":1},{"version":"fddbb23b9d33aabb7e1520a7b11385b800207a3be4ad98d857d2dd9104d219ea","impliedFormat":1},{"version":"510647bac63d038fbd90cc967f11eef7e234f340e4717a5ff611fd290b9c8d99","impliedFormat":1},{"version":"abdbec0003e712eb57e13357cca1823860c639ff1fe4b980a4ece635b75e46d3","impliedFormat":1},{"version":"dc0d375d7a71a504559e9ec7cf7ac6301ddd0abf1be2db2c307f5ee1bbf98d07","impliedFormat":1},{"version":"e061cc396d28e164cf0ae07cab1202ceff9d96cf7e25b03e05075d395258cb3b","impliedFormat":1},{"version":"d09cc603147bc8ebc038dfd37be589864d6256718d0aa41866548701cf10262d","impliedFormat":1},{"version":"3124bc4828e5a3bcf34fb0b2b32e0c0896da89ede67276eb81e503e5ece584a8","impliedFormat":1},{"version":"fbd729606fe90f78a7493eac9544d7609bb3d6142a5cb9e781a8aad737741ef6","impliedFormat":1},{"version":"b2584b5e0c00036433c0f36378ce90f470ec84fdfcba7aa1ffbbfeda3aa42e79","impliedFormat":1},{"version":"fb3e3ca16b366965a07fa4fb35ca6c027d1688fefe66501a9094c8c398704e1b","impliedFormat":1},{"version":"9a675602c7d93b6713673b90e8d6a1600ae313f9a286dc3e21556154d9163eac","impliedFormat":1},{"version":"b1c7e025da2cf73d77de904c4fda91602db4333331af983ab5466935027553f4","impliedFormat":1},{"version":"572047413a6241083a52132805909059710f6c527b9822546ca142f99dab8840","impliedFormat":1},{"version":"b149201aa80a406416138dbe23593b2c78dd8afa639153f9d15d2e69d5505404","impliedFormat":1},{"version":"b61666025cb6c0b190aef95a9085c93574af804849eff95434a6b25612a85e95","impliedFormat":1},{"version":"944b580e484a6a6d69db618c9cb06e29602ff689018eedcc2735b0131653d4fd","impliedFormat":1},{"version":"868d3e60db68c34e3bcf0358937782801be80b29f475b39ec632289e463bcce6","impliedFormat":1},{"version":"d75e1715f00fab59acf66c4cd749f01185ff2ded00a65d537ff8e4f1b85e5019","impliedFormat":1},{"version":"f5ca5cb79760bf47f350a7de374514017848df1fa5811fa6c5870bed041f7e6e","impliedFormat":1},{"version":"1e92d1e395f72723ff6b29607cfd108991902584ea7c834b81e8531f40b03ec0","impliedFormat":1},{"version":"573d6aaf300fa52fb766d9cb213d52d25c829516035a7008d4a474e26bb95e85","impliedFormat":1},{"version":"c379faf4cc1084d18b134f37e131f4871cef7ad6b7cde5611120f3b86133a597","impliedFormat":1},{"version":"52f04d1f8494ff4329909ff7d6b6b605320920c3c4c1a0f940d74c56236227df","impliedFormat":1},{"version":"671b04d17b8f5e7852ec98a63113157028f7b8dd55f4921e1c181dc5ab16c49a","impliedFormat":1},{"version":"3b96ca722b5eea763d7eac2ee1133600f0ad9e191bb335c0db18d25a630571c6","impliedFormat":1},{"version":"c08ef329eb1baf93b9ba5e1c70ff843f11fc8c446f1412a6686b797111aada62","impliedFormat":1},{"version":"b52ff88d305d7f66cf6ecc21c4fb4f1f4eeef135cc367cf1229ff24e943ea7aa","impliedFormat":1},{"version":"02a3cbc8b2919435783157e1f61a45e6c7c0a72241e8a43ed36d3e0465218dd7","impliedFormat":1},{"version":"df8659acebb91ba9e15f2248a7675dc87daf5b52668e1ff3693cbd79e8c9867f","impliedFormat":1},{"version":"e157d474a17ad1acec58afb109ae0f32e0611b1478e59082906c52309f970e26","impliedFormat":1},{"version":"7029712153e3ee8f596761974fc69cabaa581e52ec3914db7f64c47978ac348b","impliedFormat":1},{"version":"963bd95bd09b9ef9ff56193c24e16e094526eefde01dbf8313705073acd668f8","impliedFormat":1},{"version":"357e9af5481c60f68411d8b2bb6a90c993ff4b5253797a71133e402673f3b502","impliedFormat":1},{"version":"10315be5dd099b47ec0f7e2abcff33e8de611721d306f704e92e2234eac244a7","impliedFormat":1},{"version":"5cd6e14655a2154e684d5193a311b6d9744339297deec3f46a0af0bc9e4578a0","impliedFormat":1},{"version":"e3f5a3c540ed15338abcaf4ac9cab458e00a56446a3b82306b3dc5838e98fb02","impliedFormat":1},{"version":"a6ad59667e54036c35b5ca35c357ca56eab652fca5d26a95c7ffccd1f2831489","impliedFormat":1},{"version":"3de094ef900fce3f56f5239a573f2746ff4fe0f7751f829f7bc8a565f47bffd8","impliedFormat":1},{"version":"83a0bc66a64b393dd490e34cc48a7a5e2b66a05bbed3bac3798f187b0b943bfc","impliedFormat":1},{"version":"8becf315d7dd7ed7cd203eeabf13b010d4d97ae7c5495642736a45b6376426ac","impliedFormat":1},{"version":"d33a563964a28d6a438c7e610c3892fa704057a5c4aac78117230ecc6807ebe9","impliedFormat":1},{"version":"173da0b5ba9772997fa11a1139a9ca95cb04fa441d9fe3b0fcc31c5cdad47fe0","impliedFormat":1},{"version":"db16dfc289c8b3a898de1706b0adec52fa294c8be8fb12deff4e20b1f4881f11","impliedFormat":1},{"version":"b95ab5b0a36fffe949cb1bee71f7a079e9bb8e01699301cf8b82247df75fb246","impliedFormat":1},{"version":"5707b8e50fd7617962d04bf3ff9fe83fab9204e1423bb568a2e2d434103ec136","impliedFormat":1},{"version":"d10bdb4030670b0b090f3a7dece5b1067778a91f0adcdaa4673f8ea9fb596fb8","impliedFormat":1},{"version":"191bdababea1dfde835e4519043898a147c4ee3cf78940f5a62b3a936da89fb5","impliedFormat":1},{"version":"54ce36c522af6ce3992cb10c6ce99e1ebe4c429801b9a39e712916316a92e61a","impliedFormat":1},{"version":"9909cd29bce3bdfa664d91c4024670730ee945d66f970856e90763575d355c57","impliedFormat":1},{"version":"03962ddf9176f1e4c9763ad2203430943395ca30ef23f5279eaf3b9e28c9fc6b","impliedFormat":1},{"version":"1e3658873057decced4ba0a7cddf1470de75e246a47585c98fc218ae03fe0e2a","impliedFormat":1},{"version":"f675f69a376d8a0b2e431fb48e1905e9dffe019a564b28eef6fcf51e32ab13fb","impliedFormat":1},{"version":"3b3a6338e8b37e013136bbe8954398614e45b88513353d842b8b10f5969784bd","impliedFormat":1},{"version":"104069ce6b7f0968bd19cdeb0dda8b9a780aa94bda35007700266b4b79db87ed","impliedFormat":1},{"version":"0b17b85a1c22cf29149d3ed1e216ebee57c71cc3226de26a4d9d47ba41e2df3a","impliedFormat":1},{"version":"9c3ae6b5f8961cfe70cf773c064a042c7762cf39b62659c27018685526a75f39","impliedFormat":1},{"version":"caaa547bb96d77e1e7afcf0939969d2c068fb9d8b84f05a0e69512cbc32aaea6","impliedFormat":1},{"version":"17339b8018e45ac1df01cbf039745f4f10298f311af41f121c7bbf4202429690","impliedFormat":1},{"version":"47fb60c959fec5dc792ad068e1d8ed510721e57c4e4092a357e8c0d175cc9c3f","impliedFormat":1},{"version":"08e5711eb3061b952e34dabd0073adf8ebcc6b772d95bf47baf8cb853adf350f","impliedFormat":1},{"version":"683f792f9a9f0c561000869d3cb5615744610d7b9c65b6bf6ccadbdacfd7fdfd","impliedFormat":1},{"version":"257ac4a84075c498aa9820e07581873fa44a10007bcfdb507b853af36d020c93","impliedFormat":1},{"version":"4177e6c0463eda4666e738b2c5fa271f44f08a0c31bbff3611651700521de9f1","impliedFormat":1},{"version":"c23bb6ccbadad7c3cfe281d106ba577c5bded7fcf68ad8c2469b516663e8f1d2","impliedFormat":1},{"version":"08b3a6bf2b415611b4eb3634d06b36999996a4f90a8025e82174bbfd2d7be036","impliedFormat":1},{"version":"f38b790490e565d71f7046edc6a7e69bd716c70dfbd72a358d6e952739f9867d","impliedFormat":1},{"version":"2f5c1cb5939c355ee6f5bb6462330efe6a9820744ed0d11e7183a5a6e6f92557","impliedFormat":1},{"version":"2d58ca5dd132b4097d2f2c6222215a9d41f2cf9a51554915a094bae07a879859","impliedFormat":1},{"version":"e82c7b55431b8753818dbbf6ec81d0a9e2ed6275276dd3ec3eacac34bcc8aa9d","impliedFormat":1},{"version":"6b964e2fd4a046ccf4ec7ed3d52d98142f1d3fa2406d301db9fd739c68d2c683","impliedFormat":1},{"version":"c40b20907c98f66c9d74ae531d53ea8a566b63513d693f3f44b97433f0d811a2","impliedFormat":1},{"version":"5660c0ec799724ad9800f91cc26e275bc22a779a4fee24961ba7758df239d6d5","impliedFormat":1},{"version":"d2af491661ba8b10e5e37c54215245df25d92c3b5d8d1ea519a6e2555bca4825","impliedFormat":1},{"version":"47902ca384e9084c5d7db63abd2b6b466c78cd0e80535bb9abc2bcdc1d937888","impliedFormat":1},{"version":"8177a9d146112bc4a574033326e22192c136f63ef9088fa9e65096cec885197b","impliedFormat":1},{"version":"17f3b6a010c5cba7a0affbb44d400105cff1ec1714b190417aab6583a736b830","impliedFormat":1},{"version":"867a174624d91b2775c2969418ed77afea6b88c1a0c2a9ce8231878d76175e16","impliedFormat":1},{"version":"4bbbd7a9e625cfe0994e9f7db7e8a01af2c3b4b19338f886d8b9154d8de500ef","impliedFormat":1},{"version":"ecf28b632eefb05979f930b07d7dbd9464740457dd467c6e12f331e47cd7b643","impliedFormat":1},{"version":"503de1e56a9b6056b970e6c314189546a7ee718f58addeca5ab6f8379a503785","impliedFormat":1},{"version":"1d610c2301ae3a0ac1bd3e1aaa773fd90b142cafe03f1818f718cddfd069a078","impliedFormat":1},{"version":"1b6e3d3c798048410ecb0f10f7859d33e36137c047c17069478788e59017fd0f","impliedFormat":1},{"version":"6840cbe4b6c843d76d202e4c4798819029838f7ae5870a905135ceaa389e26c9","impliedFormat":1},{"version":"ba66bf118b2c3e2af5253fbf753d462c9e1b4c51153862d69231efc00b53712f","impliedFormat":1},{"version":"2f38812bb92b4d916b762935210e32cb306dde2c751748846c67193dee0b2535","impliedFormat":1},{"version":"455deaa7e622069ca4757ffa3fa862f0d580876a2c5c1c93f2099df30b21e435","impliedFormat":1},{"version":"b283707accf71037f6b7c9380c969b5cd9e6de531902f53cf011cdaad2b6f1a0","impliedFormat":1},{"version":"7c45934184177cb474b4c9c6d047a73babdae0d19778829297bdc09d82127a02","impliedFormat":1},{"version":"741a98244730625b17667fe7b29d2bb64eeff1e1f3be691bce5fa841e50173b5","impliedFormat":1},{"version":"34fc41a07218cce7fb0c6694d98de8c63e69e322150e71121c225991bc4787c2","impliedFormat":1},{"version":"4e3745b2ee686b551595234530866fefa6322e13085723af56f1de0869a75568","impliedFormat":1},{"version":"700439c653b09f13679f7b1fffc907b8b39f191b0006264627b09e620f8c70f7","impliedFormat":1},{"version":"b6f87e1701f1db9de478f004b8ccab126cc216886765c9f2620c80c324c17860","impliedFormat":1},{"version":"45f3343a25030085a4cce86f47c497841fa4174462298135da3eeaf4cc57bce0","impliedFormat":1},{"version":"8b558049669e04ec899328cd0c9ea7c36d061f942538c9d2e1a0c032cb091c78","impliedFormat":1},{"version":"a3ac4ea698f0f68665efbd329e0f5293cd8ad1950538acb5cde6131ce7f0b903","impliedFormat":1},{"version":"916c444ac79fd7b1c07d7617bb125ed2b82ce0d32fb1631e4f12800f83dcf5d3","impliedFormat":1},{"version":"eac759f00c125328099e54d57347dc08ce585a643cd9e09ee0f236ff61689a87","impliedFormat":1},{"version":"0d1625c42505aa49a4c3cf3c0ab9f19e8743f8682d27c45894dd0cd2e6fbfc61","impliedFormat":1},{"version":"e5a999a7141f64ff7a091bb9eafac4dba48bc3cc0791a592728d4d86bcf094d3","impliedFormat":1},{"version":"18fd02345e632e2ffb5b80749eb50d7cee1169981df2410b8ecff22fc6f2f6c7","impliedFormat":1},{"version":"fa37fdfaadc5213e54a1bb6df94c089244b5eb0893c479e6801e2774fb058854","impliedFormat":1},{"version":"e2677997d05cf83c26cabb8533143447343563a975250ba7bc8a303fe3d31335","impliedFormat":1},{"version":"122d8c8d92112fefdec5c36e4b440fd9cf7b339231142cbcbae1265b5e6d8aed","impliedFormat":1},{"version":"a8c4138b6561bd0335334d457700201254833e890dddc45b66d04a3289471524","impliedFormat":1},{"version":"2ee03a2941ae8e87faddd9b532178406e6fecabf699f61ebbc30537068d8946b","impliedFormat":1},{"version":"459cdf1ba9d43673fe4fecf268f65665148a97b9d8a3cb98336adc98182eb61a","impliedFormat":1},{"version":"ad789b547e4a2ded3ecdbdc2ff768efaab5fa6a9c9525924f6cb181cd535cdd5","impliedFormat":1},{"version":"f76fb10cdb6b76951b2bb895804f656d279c41b8a209b63f292723ad23ee2ff4","impliedFormat":1},{"version":"9c0cc99b34f5437146edb201c0d731c1cc783f8472b2a459134588e08cde25af","impliedFormat":1},{"version":"6f180e41791424a87ce3d97e69cd50ee6373f7a9242229d8ec03e4fdbcc0952f","impliedFormat":1},{"version":"6cc12b983b58b3e5c7b2ec0eb95af4a19954929d73d95164b916de912198dbc0","impliedFormat":1},{"version":"49e44e8c7ea8b05d17c1f4fdc005b03c7a3fba0b8f945ec52699cbee69717e0b","impliedFormat":1},{"version":"db637632853e3856b68937bad921525635e6d9c1f4df632e7667d04ac7e6749e","impliedFormat":1},{"version":"b4a5fd5a8eee35db40a4487c156e38838cf56a5474baf884b9dd3f05b6d310e9","impliedFormat":1},{"version":"db335ad5913d8f77575d5c09a73bb16e3a2725176d549f4a438e89f3fca7ae19","impliedFormat":1},{"version":"a16ffa68e7a0c68c8e7e9fcb305db36a49949ff227bbc138214c43bb050c3095","impliedFormat":1},{"version":"1dde721f169cab738c9dae644b14767e7d9492c0c6bc57752cfd757edff187d7","impliedFormat":1},{"version":"8b7144ea57878255c0986057657a3c88bf6d966b58ed18a2db6ca310a1e63609","impliedFormat":1},{"version":"ff42c4b7ec25c5ae02db2ab309a2810da307bc939564bd4ed34f35f09e55d7a7","impliedFormat":1},{"version":"309bba3d393a393ad499cbc54d365570500bc8da25683d559aece3c2ab74dbbc","impliedFormat":1},{"version":"326d616c65a328ab215dd3c60c7c7cdc151c89216dfe69e60f3f12cd242234b6","impliedFormat":1},{"version":"5afdb87cd7678043025ff3124e1b53c8e1aa8b9b5f7bb4b5b41f7ec4aa7f19c2","impliedFormat":1},{"version":"a4e4c2cee6836187a3bb865a372cfeb427d8c155e7366f5b371264b8d4662354","impliedFormat":1},{"version":"3dce0f0238c8b668eb55fc05bc1ebcf1b8dd6bf5a437004491996fbf43aaf661","impliedFormat":1},{"version":"1be7733bae62614c6ba1e38a643a1d9b386f6463bf25f4e3f19e2698cd2f95b8","impliedFormat":1},{"version":"b9491b1b3c06d096ec9b381be6560fb932445aae0e84cccc6640473f7bba833d","impliedFormat":1},{"version":"4954556ba83175f9b4c3fcb644fc033a3edbc3159df82d76e286d9c0d5ce63b0","impliedFormat":1},{"version":"cc880a72a2f15ae6a3c4d2fa6e6b67488b8fcccf537f6cf7a5b1a835d920045e","impliedFormat":1},{"version":"bf546ecd678b92398251e3d9110d4042be1581f3d327ef7f6cc7939643a4fe77","impliedFormat":1},{"version":"b226ef66e1ed48f0722e7450a3e670520c0d37dffb24c8cfbfe010ee8579590c","impliedFormat":1},{"version":"0406ceb8febfe97d32bb2ded07418c67ad6fb45f137c9b14c37817a4cdbd2eed","impliedFormat":1},{"version":"7aa9e3ee219d32e2d486608d5e34e953baa22ca6cfa8b6342deb727787abf831","impliedFormat":1},{"version":"335b81892adda2bbacaf4dbcbbaf4f68318d71901a037ec828bc17a13f034619","impliedFormat":1},{"version":"69c07825b65029df6160059b407cdf1e7887a438120c1fa2a64664109b2a35b1","impliedFormat":1},{"version":"c3b222df50b99844effdf0485225d9b74276d923a62d08c033b2ffc6cdd25c6e","impliedFormat":1},{"version":"92e071e4c2b751d2d5aaba2e69e3b9a32068690e18d5d9e7222c3e27b2753198","impliedFormat":1},{"version":"d97d8927db7c03309690623c79cf79080d5c35778b9197c0f54e9e11d3984e8b","impliedFormat":1},{"version":"6f85ecee1b59cd9ed5599336242d612ae1a7f76674d3fe7b8ac71de11bab27ca","impliedFormat":1},{"version":"35270c3ed46c7a0ab3cca6733e2dae3fbe37e2505de69744a39cf20d4601a66f","impliedFormat":1},{"version":"7a0839f870cb97aece61b169c94f3eca22f0c04b300a0f1ad5d166750cd0714a","impliedFormat":1},{"version":"3204e156aef05a10bfcff6e5ac8ab9da7d5640f4313e6f38bdc6326b591c56d2","impliedFormat":1},{"version":"75ed65d8f255c2f8f4f33c4b75663293a18047e4869a47b3b2e3251333a6fa6e","impliedFormat":1},{"version":"325347b8ce890d8c1a2297f6f3d0d295efb6901d9e3b11f2e4c4c3276ca1a9d1","impliedFormat":1},{"version":"b2a82b8de3bd82c498b11c46629e84ad9a6afab02537964e729dcef1add56e9f","impliedFormat":1},{"version":"3da265a65723800a339ed680a3b2fa9fa7f312d9e401807fc294133f8a2ccbf2","impliedFormat":1},{"version":"4aed95514865a089d5de53dfec019a6389030c1c8ee39245d15c315db454f154","impliedFormat":1},{"version":"f0927d8a7fe0a596afeb0fb532299387433e24d20e0715f3400c0b9f116707ad","impliedFormat":1},{"version":"52e0994f4de975d71d0100225c09781f4d4338c7deaadf4f65cfda2a91129a4f","impliedFormat":1},{"version":"2698e1a9db75ec70cd979525198440d5850727d8186d5437ef253779f9c37de8","impliedFormat":1},{"version":"e39f5ed79bd2bd41e05cb4f3962a209238e5e08f23df79b934be97d813cc933d","impliedFormat":1},{"version":"42368f2a4b7732cf12712ef569f8f3abfecfb6bc3b2f0c0fa00982ab466d7e54","impliedFormat":1},{"version":"410962dfd9568a439369f3d9e99aabc80e66334cdb942c789b19481cf6223dca","impliedFormat":1},{"version":"a6b2f4fe3f20bcb115310fff03736c9b16b717e7c54ca3284dc896238d1c384a","impliedFormat":1},{"version":"c7309ca0e50cc3597d957eeb22513baf8d053cac3a1c70e996fd028fe767f4ad","impliedFormat":1},{"version":"ebea555278efb8745479d93c20abb4428b8494abe683edbc66592be92d7928ac","impliedFormat":1},{"version":"dabe77390581558da1d11a5a53cbe2ea13272a17e83e5828e05fda38fc31b584","impliedFormat":1},{"version":"cd8d4300418f216353abc66289b5cb9081fff40e1beb40762877586f3cf8df4d","impliedFormat":1},{"version":"227fadb4f190b756f64b21163fc3cf40ab65cd9509f4726208e1b2286e3f3322","impliedFormat":1},{"version":"f32db231052cfd06b1ff0860203475a0fb42ae55fdf3b3d6a781f448015cd1d3","impliedFormat":1},{"version":"e22da4f68136eccc823df9801730f1f14ec5e24bc5937afb2d423427f45befdd","impliedFormat":1},{"version":"128f6d6d52f8f3e7397ac45785862c4257bdb9a762b80b27424474dcce53e5af","impliedFormat":1},{"version":"189086258cc1db9d773a9cba5fc53c8b96a963b541ebab1d31ffa6dc4f6f04c1","impliedFormat":1},{"version":"f1f85f522baf8c6d66dfbee64d44ec1764fc8a4331a5e9c94b47fcab7b49ffee","impliedFormat":1},{"version":"4d424f5d098bc86f782440a35867824009949abb2903f1b368d0ef6f31c33271","impliedFormat":1},{"version":"78249ad169d0415dd632f455a48cb2057da6f2d83ef4b600cc555a10a99eade4","impliedFormat":1},{"version":"80921359e2a713bdf96ed2b460357ac599144e2a5eaaa40d940e196f165a0a54","impliedFormat":1},{"version":"8040b366d61459d34833c0dabf19684fe5acd1527ac6d5391efce44adc9b3ced","impliedFormat":1},{"version":"6e006e7f8963f4312a64dc110c43f3eeee42e22143846c1ff5ef207b1b9e7024","impliedFormat":1},{"version":"6db7701cd202ad574ced3e1af326ebc5165511dc7281e25d8977b8aee418243b","impliedFormat":1},{"version":"676da4358adc4c5e43c578cfc3fd42dd21bded2b87d8c72ac0431658b5a02e16","impliedFormat":1},{"version":"14636caab0e3025d43ef78a8b3a99d1529083566dd9aacbd24927da4d1f3c4c7","impliedFormat":1},{"version":"c49b33aef05292b435826135e053c0495d48614db685f61aed5ceee6bdeb0e8a","impliedFormat":1},{"version":"005a5fc6b24bd7a7107e8a1feb0893c1a25ccdaae7c00eb7482ff5b74ccd350f","impliedFormat":1},{"version":"a624bcf1d83a75d0a58be6ec25071c1a4b21a4cb820427c4e39c1e766e965364","impliedFormat":1},{"version":"0072b179979d06542bbd05c3bbbf1e149a4ea1953d95f60247183c9fda350197","impliedFormat":1},{"version":"07af42ad6ae0e622983619b9ef85de5f3d15cc66bd4440d3d719e8ed7d35fc15","impliedFormat":1},{"version":"49977016e5bd5de70c0e269a0384943b7e2c78234d39c4ffd343e290ff4f0c4f","impliedFormat":1},{"version":"c639b93ed8b0a936fea05b3599b2ea4bbf14becd9dade8714d6ed3fc3b56d8b3","impliedFormat":1},{"version":"01c0b669ce60baa0f78e09f5d3b7e2b0866b5cd8d8c0ec2425f66bf92d491bc9","impliedFormat":1},{"version":"02826e7a1eafbc10cfe5b58e7cf2f61bf5ff69c2bed070e6f8750ee7b2db22cf","impliedFormat":1},{"version":"758a53eff79215b7f8c345272461f824ce33ebca827105acc873159bcf04353d","impliedFormat":1},{"version":"4c7116db0c1b2817acdc6233fbf5eb9eb74c3fc1b24e71226eb68182176478af","impliedFormat":1},{"version":"e869a9b88efa93d6600ac5097f52819a8614b4c4de069f26595bc4cfc2e59a99","impliedFormat":1},{"version":"82928b63a613d873e8e3c4931e751c9ceb40e4defebaa40ff570fc605969d3d6","impliedFormat":1},{"version":"b3200e1d002bf1ef184c38b61ee058e1c373c0c5de1aac15bc6e595cc251b02a","impliedFormat":1},{"version":"f4c777aa331dadf49ccbd03d4b77b4d2f5881eb5b31926a5f54edba040326d56","impliedFormat":1},{"version":"83cc1d9adbee2071731792b38b2ab40a4a9a453641e8a2a1d24ed12f203250fe","impliedFormat":1},{"version":"1d0cdf112838c100d9bfe18fcbcd93bf874425197240e4c738642e4469e67420","impliedFormat":1},{"version":"6e8300c3174e4c883e1aa43fbab9b63cfa1fcda009c5e3c5afe1857644c4e9d3","impliedFormat":1},{"version":"83085eb82e71cf05b38025b61f7df77e71595400511053112dbfe1a6f04587c7","impliedFormat":1},{"version":"b000c2960902d215a8a2339ff310af85108af05ee6a2cdc084130a015fc9d655","impliedFormat":1},{"version":"80951547d8a90317362dad4d3784b566667ceb9a59949d211088430da5188446","impliedFormat":1},{"version":"0ef19fc1f4e87b88c5a129628bd68be945a98b0ffa410e39754591f5f1090077","impliedFormat":1},{"version":"3dab161e57601f74345bf66cb50662c383248db8cd899ef5c2db8efdbff85c47","impliedFormat":1},{"version":"034611d66eba2d3943a737f2a1d14b52624cda578a46565f053635b4c3f195c4","impliedFormat":1},{"version":"b3d6e4152aa3a17d61aa456845c02a085d8632e394044529864db2c50e593424","impliedFormat":1},{"version":"d0c55d1d8128fca17d5a220e7456324aa5b20767b07595328b97972909cf46a4","impliedFormat":1},{"version":"b2e7ecce3d0f4606683bd3b002e8d5af1fc5007e14c59455a07b2e53f53a7515","impliedFormat":1},{"version":"a0736b52e5d728997232d14b22e6bb2ea215f486a7f7c99f77571d1029a39c3c","impliedFormat":1},{"version":"ca068b5cf8e7e55de79fc4fec18ddcd5b19919d4211d68f3d5c0b81f41aafddd","impliedFormat":1},{"version":"60c651853033922f10002d0252fe2badf413f6ebdcc1a1106ee14e1de97f60b0","impliedFormat":1},{"version":"ef243156840aa4bf3bfb1ef6a33b793fef91cf98a828d2a85be299e30073ba00","impliedFormat":1},{"version":"26d5527d1c681abf9d45ee7c4614755ac3578699fc4a75fcc8c7b10c1c4f5a4c","impliedFormat":1},{"version":"e2bfd3e3657a7fe3f2ad2547e405c4941a13c36261934a945a1fcab47b490304","impliedFormat":1},{"version":"084815dc3aca9833e395604d598bff6fafdf6500c4916183e578b83cd1edb8d7","impliedFormat":1},{"version":"133639fd7e5591168f3c98ba3c5a7839e60f6015120c66e707ba705e15e5fa34","impliedFormat":1},{"version":"987b1141ad4671d779930808ac18aecdc13152c01efa7160263ed9da1c10e058","impliedFormat":1},{"version":"07197d9b6bc50f216846741d866d0f5f627e741e56d610c9a4016796c525c765","impliedFormat":1},{"version":"67db7690de1e0a232718fee750267e1c27a4765c4b06d4d4ff77be4dd49cb59c","impliedFormat":1},{"version":"121be383ef39addb17042567fba1aea4f29a56f06fd6f156034970fc1d8ac7b1","impliedFormat":1},{"version":"bf695c7a24c23dbc34e3c5bd3d381acf3af53352594e964294c53e0a0e6cc529","impliedFormat":1},{"version":"4421371f37c17a4583e9dd441cd6861b6563c7eb488b01fd8441586b05eabefd","impliedFormat":1},{"version":"9c15056af29d58626349db733f2a9f597788c0bb19491cbe13a64f9e4c99980e","impliedFormat":1},{"version":"eeeed680f40119dde17434680fa1645945c1130fc690d5d7340272697b58e8d0","impliedFormat":1},{"version":"1babf418fdc3601f2c297877ef16eb30e5e7c0e888863583319977b451eb7026","impliedFormat":1},{"version":"7733cad861b8895a05ebb2de3795ef29d086cc977c4f99bd3464ea07bc7e8a14","impliedFormat":1},{"version":"4292c9a1ed88431995f5406e080b93f9667f21b44d6ee71fa18004797dcbc594","impliedFormat":1},{"version":"b70876b8b78c85bdef19fedd90b622f7947a4ff177d9e10ebd3adfd60867dcaf","impliedFormat":1},{"version":"a03195286f30f9f2e0d086b5e8cbba46ecba20c817d5239dfddf74b56a61ab24","impliedFormat":1},{"version":"4dbce764ecc5cf6fe41b003e582d6ea5a947532322eaed747725eaed3a29fd33","impliedFormat":1},{"version":"470a04a6af13255acccd7faf42be8bfc3daa450d0e36327995f67ab6d4c136ad","impliedFormat":1},{"version":"12e6cf045c9b99c451a572921a68f7806fe86f68d12cfa8325afd27c90855974","impliedFormat":1},{"version":"f224908e5ab45748436ef1271a96f3bd9f300a782b4df93f9e13bfa9ae3f18d5","impliedFormat":1},{"version":"d0b2a2bfa4dcb2fe3325e14259c7228978f01dc9da3274521f789f1dfaa77c23","impliedFormat":1},{"version":"2bad73d24f73b0547b3900b1c2dd903f345bb3b3e2c20ea0036b66b38118c0c5","impliedFormat":1},{"version":"4e4681b6c62667ccba27cb831fbef88042056c32acea4517b507db0469dd1586","impliedFormat":1},{"version":"c761a5fab2285dcf80f3e5afeb235dad31667adf2848a79c5629e6e21ceb709a","impliedFormat":1},{"version":"0405dcd3f8fd9f4cd76ecf9c4ca5ca678270c009df398bbdf493cf50d8f2862b","impliedFormat":1},{"version":"02d386b3c38c38969f60abff3ccb8c576fd292377842906be88be47edb8626e8","impliedFormat":1},{"version":"35cd6128dcb9adcd10e566c29a1da1969a6ea0d1a579a4b60df198d3d0df45ac","impliedFormat":1},{"version":"f3138db8c5a6c20ba6b318a1a57d0b488c4f8d1504888bd0481b5c2c972f0805","impliedFormat":1},{"version":"d9b7a5b48fda922ee569327f4fc2d5d717bf5aee6eca363abd71f21218381831","impliedFormat":1},{"version":"28848437495ce8782335dcfcc2fb3b6526155153a1ccef9ad2737161df3ac3cd","impliedFormat":1},{"version":"d95d7b15dc8d79043de24368ffe6d1e4bc4bff7e6e1fda6974877dbeebe9f0f1","impliedFormat":1},{"version":"235c68daa4f6577c1c9befb561e939255edd4c3391965dfd27a06979b7775e25","impliedFormat":1},{"version":"2376250fc937613b33cf3349c5bb38c0cfc5513f845b0adc2c22621baae1e0f4","impliedFormat":1},{"version":"4317c03f8f7967acce209a28ec8e01fe153072a6ec06b612efcd3bc234763d1d","impliedFormat":1},{"version":"2b08468940324b0fbdd6742d5fb39d7da8b4ebe056de5b00d33c09618ff02e11","impliedFormat":1},{"version":"aea44756d1df87ca6816cd829bce540cb3f19297d7f33aa86fca94b1acbb0953","impliedFormat":1},{"version":"f87ac6c8e2dbf808fda67ec13b2d574f83022dad4de499a8fa224f37a18d99e1","impliedFormat":1},{"version":"fd0a9604bf44717b360a57f7cb962538eb173a101e9b0681f94f9461a851010f","impliedFormat":1},{"version":"51b99ce58019427651c422bd23fe908fed0635b51df1cb3565debe500aa431a3","impliedFormat":1},{"version":"eab238cfc81f2beb36844b205aa0a32219b54bd18dad459f19bc5fe7dd1f6aac","impliedFormat":1},{"version":"757c6fbbdad3fb4200563a9125ca7bc57e49a2d450ad78dbd903e878c9e931bb","impliedFormat":1},{"version":"0b08cba9be86b42afe5b410117d08f99ca6ad5c5daf7e82d0d51aa2515c682c5","impliedFormat":1},{"version":"e583f1c92a3b9e42ccf70e1c0b98692cdf20d986ecced89cc0a9cb0a74d9730c","impliedFormat":1},{"version":"7deb69337268492573200a6d7551425dd7c058728b4584e9105e8f17586868a1","impliedFormat":1},{"version":"3c6385419d4a4820115dd27bdee843ef377562f67dd439424252f5ac2d293428","impliedFormat":1},{"version":"a7e4e6191e96973c14f23966fb496dd8141f1c8cddb2c14c5059579b975bcac5","impliedFormat":1},{"version":"ddbdd41ccc2828c848f576cabd4e32cb9e80d6955d540c15c645d914b3e3580a","impliedFormat":1},{"version":"7c052ca9ccc263fe6b558f81555d92d1ad416e5a72e802a2192ed31e284d362d","impliedFormat":1},{"version":"24f79c09d986ffb8387300dd51d1395af12852bd6746573e8d42922ae3461b20","impliedFormat":1},{"version":"484d92a804229e12c405b9f9f418f808fb9b0253c04aeb17a9682e928115a988","impliedFormat":1},{"version":"623a2ba3a65652d905f997c94b73d3723220eb1e01a7644b3ddfb5adec085826","impliedFormat":1},{"version":"5df12ccc92c238ae0263f8ac8852d24a1aab6c2eb5e7d133abfd91cb6bff465f","impliedFormat":1},{"version":"4de755bf293d9fa98b70cde62d5b5d6bdd3edb5b4a94187b7f96b9e487e8e563","impliedFormat":1},{"version":"16202ac71998e6b0e076ce58055bef9b377191459efcc8db9fedc602be581dfc","impliedFormat":1},{"version":"78e744efe21d6963a680603edf29c76db6b4a014020892898897006a3385f1a3","impliedFormat":1},{"version":"92577e05328f133a2a5e9c63c155b26ebcc7ea7835494912d9b0c7fb08d86931","impliedFormat":1},{"version":"ce52c1e13480f42842149c3b7bbcdeb4aa5073da9373c2f9cadca9228a677b4f","impliedFormat":1},{"version":"5c1a32b6b3e34872e92370692a1a70257a5427c063277ff6680192f48c58a54d","impliedFormat":1},{"version":"373783b7297efe269b2dd22cf3dd6fe594a69709354d9f15c7f94c2357dc9995","impliedFormat":1},{"version":"693e7088b28102513eff80926c0a4305a281bfa71b033c1702d7b1f816e5fab5","impliedFormat":1},{"version":"2f900efea041a1259de8c0876df064148f7a6cbd7407f92f5903c213d3041ec0","impliedFormat":1},{"version":"a4487fd1a20cc34d2518d67c9fc0cbbb20c44c4af167fcbf1ac3b12c92118eb0","impliedFormat":1},{"version":"1bc2f327d5569a5f5eb2fc52f5c312f286b62dcaa648f7265148e823b9385e4b","impliedFormat":1},{"version":"71e795e876be72be5913f3ae15a1f9d559c26f2054614cecb7914e098c0c97aa","impliedFormat":1},{"version":"38a7acb22e4038a7be3cb7ac878ab3f48fb322d5c6e3db547a80c8df8b126576","impliedFormat":1},{"version":"e6f5fb6695871281c51922c726270314c877e6ad224450ddaa09a9203acd502e","impliedFormat":1},{"version":"2a525ba8a22d3bb8cf23b13257d08a3addc3321c9fc2cf33e27b861e567e4e3e","impliedFormat":1},{"version":"40918dc252cf226d10e8726daa7560adcfd639805dcaa57c17ef0ba5778664bc","impliedFormat":1},{"version":"4b750dd19dc220737d728972ad6022999ef78077dc59f7e06530222c352aed44","impliedFormat":1},{"version":"eb73f798753c2cd343912ad25dec69079924e3b3d907712e42f1e538f0ac708d","impliedFormat":1},{"version":"01b269f426dd934af595b6fee799f536677eb5639207066008cf77e44c0975bb","impliedFormat":1},{"version":"b90c0c883de8ac6907997a100548bef181d6d842f33cae3dc0331d73f5ce0dd2","impliedFormat":1},{"version":"af6a09b7445f241479bcde7b889716122df3967bfa1a20ce18f2873e52048c49","impliedFormat":1},{"version":"3427ce5885698eeaecb36e31b023bd44557d7968a09710704e3edc4d068b2837","impliedFormat":1},{"version":"270b440c7f74cffd89031c369d67332546e0ad8a0175453c2c4e81a2c2aff672","impliedFormat":1},{"version":"2a008a1a127000d5009411ac67435f73a9972e15971e48935f47aaca1681da10","impliedFormat":1},{"version":"174b942e87ced5b65226bdfb35e907003ea65645c1b731adca3f6d1170052298","impliedFormat":1},{"version":"167e401c544fd79ace54bb6ac13b42497bcf934c006e00024097ca5822ba85bc","impliedFormat":1},{"version":"a6af221eac9160089e07c80caffa4f7eb5b99781f5caf3b7b0e8c2bdecb7b4ec","impliedFormat":1},{"version":"52ea09674fddabaa4dbced89c9e5c833ed2380c0449e0828edb659b418bff405","impliedFormat":1},{"version":"e88272cdf8f316dffa792101ae7a8c277b7698fe8c8b2bd80e7026c82657528d","impliedFormat":1},{"version":"7b021c8cb0aaf9e05fd23c1f4b46c5f5f6f403276f49eb07e7ecb004f6a9f503","impliedFormat":1},{"version":"b03ea3fc92010ae63f90c6ba0a4747af65b33268351aa8a1bd0a303fac5394f5","impliedFormat":1},{"version":"b99c471c5aae2deec9df55f330666b8775f839b988be9a7ed2fc4feab6126764","impliedFormat":1},{"version":"9fc9fcf7909bcfaacb7d5f68633ad13f0392f11cd3d2ddac6660b58a48c39e83","impliedFormat":1},{"version":"7c43488dabfc684e7b848fc58fdd64b3b810a9da05139b83f6da33026e7c8878","impliedFormat":1},{"version":"46d5cdce95eca92c3a12906a726d05c7b04f7e3c8068e7382604679e5e8bf1f1","impliedFormat":1},{"version":"7ded1aefc3f24f57479025edfc997bb468060e7f689357e0c071822ad9000faa","impliedFormat":1},{"version":"f00f4c79678dbbc7b9c9b858b276f89a7861bbec4c28d10b70ef9c837a7a6682","impliedFormat":1},{"version":"aee91052d0e2653405f13c0b2df3825c8d7710e4195ae94c1471f93bcd3aaec8","impliedFormat":1},{"version":"ff1db6af9f1657d4815e9313df4e08538b475dec8c598eeeb6ed6bf9722f1547","impliedFormat":1},{"version":"816c6c60bd6bc1631d1d467a644b66347ff29988590f14da495c4791c654f4ec","impliedFormat":1},{"version":"384d69880f2d375e50772ecf13624ecf7c424312dc4cd3262a4c25e3235466bc","impliedFormat":1},{"version":"0fbcb03f950d708ba77174af5ca630b005de5af130ef4243ed38932dec135184","impliedFormat":1},{"version":"eadd4b354e3d40f321a498f1b586bc49104c8dd20884d20bd6449fcbb589598a","impliedFormat":1},{"version":"5c385e3005852ca759d461cdd85f9027eabdf62c89e3ca2397e2d8620f59ab3b","impliedFormat":1},{"version":"5ad34065e0c2cb51c60a1ab591a3769a6d461c4084df3c2d9bc7c2fbe941baa5","impliedFormat":1},{"version":"f9e05acf6f4e4845967daf8b0f6da2ec96e33b2542fa10a959a6d67bf609b9b4","impliedFormat":1},{"version":"73a05cd466be1e1a6a1fbef5fa294f2c4c097526eef16fc7c3e397a2c7ccb96a","impliedFormat":1},{"version":"dc1ee21f0f05658db0dc4e95d3361afd5c995359f14b70121df0e257eef2867f","impliedFormat":1},{"version":"c963d79d519e61811aa1093d69f208070ec50bb83dfdc47c57a0d9ac8ff71dc3","impliedFormat":1},{"version":"8c9738470809fd3b5f983e306cd940fe6ec284667cbe0e160fa913c1958420e4","impliedFormat":1},{"version":"59388c488e8c9a74ef5284f96e1f48d6cba7a8f07ab806ccd2da978ef1373d5c","impliedFormat":1},{"version":"bcd2596708c39da23b359dc2e6da75559f84381144e3c75125c89937dbd75ef1","impliedFormat":1},{"version":"b89c2fca53bd4023855f0dd04644134a23e9c2db9ae41e35970e819e310684ee","impliedFormat":1},{"version":"68638b25fdf47d27fc7795683245c30fd35a997ce952f82b0fe499f7ae7cca45","impliedFormat":1},{"version":"9772b185d7d3f3a1de7c60b59b0ccc5647a175b84579553af03d6f44fba9e5b1","impliedFormat":1},{"version":"88b3155212b9436529064fb6746406f14b9889f89eb4bb68603e50c5374c4d62","impliedFormat":1},{"version":"048863ddb0817c0ff5270b9eb34c88d7753a8eb7aea530aaae4452a307bab7ee","impliedFormat":1},{"version":"0726e42a8711c56682dfe54633cdb39a2d75e024a42bf929ad9753982fd8ea38","impliedFormat":1},{"version":"8f8d77beb2771156c301c0131e013eb00bbc840d3dd3a1db080dbeed1fc1d089","impliedFormat":1},{"version":"5e590768409678341e0597f70a361790098d401b4dfa011ebd33f11e703f0aa6","impliedFormat":1},{"version":"dac57917eb75e744afc56762f277dfbd5e08b40d028c7656cf05dcee1abc4ed8","impliedFormat":1},{"version":"bde2c05b34a2e47004988460fe6bcd76f1f7ac180120730bbea327d08113a6c1","impliedFormat":1},{"version":"92fcf1c5cb310f78fa4cfee9568c99261f684ef10dc45e78082726515a1d452c","impliedFormat":1},{"version":"2978af20385fb5738cf80b89478f54af7d83d418ffaa62ca7ff6ecf3e3ce765f","impliedFormat":1},{"version":"6d864c181ff5c9eb6348ab99733363da930c6caf34617ab2ceac826b77452bf2","impliedFormat":1},{"version":"d2194fbf847dd74eda1a935b78270ec930dc1adfc43022a34684729be5ba1c9f","impliedFormat":1},{"version":"dc1703e5057a339df43c123fcce08e8325a8f9f4e408251083360a815e24b916","impliedFormat":1},{"version":"c1ff7b23c3712182ab6fe43f99fbf4b8ed5faa120e114aea6032b0dfe4a54598","impliedFormat":1},{"version":"6d957028af65f37773934646a56c69e74f4f6808436805c3af8fa0b04b01a02b","impliedFormat":1},{"version":"dcdd6f99978f18ec2db36d6709afac2aa6247c9a52791909c447312388239aa3","impliedFormat":1},{"version":"549a04c7b9b1e9626b47e688d1c6e0c45b8e2a239f0b645938c08e498599c8d5","impliedFormat":1},{"version":"a9acd68fe473cc7ffe15837640d4a370067075b6a092819c9e8ef4f3cca3d934","impliedFormat":1},{"version":"e645fa765d158a781eaf70c81a5b7841f4840964bd59fe5221031323f6dadf36","impliedFormat":1},{"version":"2ae7a2aeffe77eb64532d6e24eb1bd7ade091a18fddbf094361aba5e8d83f108","impliedFormat":1},{"version":"e37e861a52005d63492a5d82032fe9322989309828e1ea464686a7e92aa9570d","impliedFormat":1},{"version":"20e88cb655fef8a381c2ca3141a6db3f777ad8ad224f6b9fdc118477f45bb423","impliedFormat":1},{"version":"6009c37a123f19fe6fae85636e33593f7aa49174e7744d95434fdbe1a69e3463","impliedFormat":1},{"version":"0ed456e0d7251ac19e74fc6d2bebca12a50672ded17d599d13d94451da179457","impliedFormat":1},{"version":"034e095725a762fea163d32c91a01e44756524b3b06f8aa6c05163475b6de81a","impliedFormat":1},{"version":"13d706190a23bd3fe0520460510c79e04d9f5bbbcfee5cb8b3af619013cd83ba","impliedFormat":1},{"version":"40872301c587db113ae52109c4a135785a9d2ac8c82e8b36244ca9e2c612ed03","impliedFormat":1},{"version":"7025e510c419681006886b5ac72c9616bead6ad040e46d6cd132f4fa68799856","impliedFormat":1},{"version":"c439de53400e6c268c1ea293006b26151b8f11ef10df33c6ed16c836eeebdcbd","impliedFormat":1},{"version":"0e7f747d943af8d666e70f608456dd7093828ae1b0a5fd5f05c34cf153f46aad","impliedFormat":1},{"version":"ea6a30039549650537845df2957b3e8d44b3fc8a6ecd5e798bce062534359f7f","impliedFormat":1},{"version":"54dc1a78cb6dfa5cc3681db763ba79c51b7b497a7f6880cadc00ea42268c8160","impliedFormat":1},{"version":"be44967ef3b8572dd23805b8d6ae85b8e80f42271b07a3ab2215f8597c9a035f","impliedFormat":1},{"version":"b518b5adc7d4082efb0bb7ee07047f013e7782500e2645e16101b70326f30924","impliedFormat":1},{"version":"fe9a0c5c49e5dfc9776295bef19611c72058dc272ffc3c7ffce93183d0107315","impliedFormat":1},{"version":"262d5fbc8ac7cb23f7ff3395791b0fb1380bc73810d0626118e5385cf53807b0","impliedFormat":1},{"version":"6784b01b1700d1ae32e2b12ca027bf2c643166fc134f1ecb3c9c7bb8c040a0cf","impliedFormat":1},{"version":"30c874bd1eb5d20f10d570f7190e32d7d6b345f935f23857ba07af45fd77abca","impliedFormat":1},{"version":"b3f18483b555e98c5ae764c22b2a015c544842bdad4f5f909fbed0fe65d364c0","impliedFormat":1},{"version":"48970d0e1e8b1ed1333b77d7330de57ddefbc81e82ae2339fe46fec8f69b6052","impliedFormat":1},{"version":"b3e0dbaaed032da23b1b76914d52a33ba2192a7f30d12e17a58528d8d39c862b","impliedFormat":1},{"version":"4444f5a7b01cc3c3264f752df2ef6ef54051c72dcd106465a3ad79afa50e1ce1","impliedFormat":1},{"version":"97f1e7c1b5d5405691d2f53055ef1518dbf65685e616d8076e6ab20daf216960","impliedFormat":1},{"version":"5d147be0eebb6259b7209d7aebb16ba459331ee6f32e931c2f980ee0727c1aa5","impliedFormat":1},{"version":"5af775d7e7411ff89be0adc6df409e44f545ba8af949ec0cce1f51a65fd459b4","impliedFormat":1},{"version":"9098e5ab7aeda368e4e73ea0befcc1ceac10ad9bff47ff627ff6faf45bdc1217","impliedFormat":1},{"version":"48edacee9526539b1a88d2fcf4c189bdccadc3fffa48c2378e5c5b9c1c06ad97","impliedFormat":1},{"version":"3c318c95fdb48bc3085feb89b53e4110162c4d98a05218f6a95804be2caadcb0","impliedFormat":1},{"version":"fd3f10a53ed3a5469a5e9527f41019f8bf35e91e5869ae4ec77203fe4f3b11e7","impliedFormat":1},{"version":"c7efe9dfa4de6aeb69c5d1d97f7e970122c9c904e7f37f3bbc7d56556265cad2","impliedFormat":1},{"version":"af9e3bd0a3ac1bc98534ae61b248846e8cb21004321c6acee225022be8fd408e","impliedFormat":1},{"version":"271b18eb2266adc3fcbc0dc0c4054021b5999e7b57ed4553e166577081a28aaf","impliedFormat":1},{"version":"1acb359116e4431ce1721c2dba9e820d4ab1b190524ec51adc5927b5135ba3c8","impliedFormat":1},{"version":"acda5bd1f922b91e02ed4ce50a8099b2e239fbcee063461b22a14550094ec41a","impliedFormat":1},{"version":"75806931c3954c1de4555cb3b0343a4a9a245b4bf80310d314bab76998533a48","impliedFormat":1},{"version":"fac00bf295429715bc388d6f020782bb96867376e3dde08a2ef8dc02a99036e9","impliedFormat":1},{"version":"ae5f4b92c18b60a175a7d52406eaa706e6f4f6b8a0ca241ec872c0c9aca12c69","impliedFormat":1},{"version":"762936fbe353b047cc545df71db72caec7a9e4434f412c0b0fe386bbdb72b4dd","impliedFormat":1},{"version":"e6995f12afd7c0417a7530c4f89ecd59c998bb852d0cc9ab565c1556d1ae210a","impliedFormat":1},{"version":"e51c0f8b648037e62036e8ef9819ca332c4dcd9ad6f9d599db4fa1fc087e67b5","impliedFormat":1},{"version":"c5f23668596c59f82bf3546ebdc205c8ddf0bbbd3876f1b96fcab9408c896366","impliedFormat":1},{"version":"672f33cb6ffe6920a490972205106888fc2dcd9bc33be03a2bb2e241769cfd80","impliedFormat":1},{"version":"e5445dc93463d52d0a6372f53790e5655068447acfd5225a0ad6d3317c24d7ca","impliedFormat":1},{"version":"c65624e04adcc8e075174227285f62165da8a4efa7fb37ca0f32ecc2598484f9","impliedFormat":1},{"version":"9e141b63ae5b93b7ca6040b627a4d029b2993c70dc4ef380e850b3934a81d5b5","impliedFormat":1},{"version":"c8b7f4adc9251081c28eb8c8a24481f73c8adfb3d2b7cf1652ccbe2e9234e20c","impliedFormat":1},{"version":"8f0066a050f76fc96c618dcdab67dde39457a40c80fa4fc0f2abc2382f2a54a2","impliedFormat":1},{"version":"41f094c56517a6567ac27a61a0245f5df9fd8fcc3114fce3c4cf0dd7653d1cb8","impliedFormat":1},{"version":"5cbea76cbcb6dacc1face1c737cd1e87c5ad56b4d428cf66950fa1753b42fede","impliedFormat":1},{"version":"9850ba1d18ee21e93382fb08aa3cb45933169c76cb764e0f0b24c83d6350db39","impliedFormat":1},{"version":"57fba776632fb81fcce1e1bb7982bb211eccb9de0578fe12a49e7907fcbc250c","impliedFormat":1},{"version":"c3045962d1c74a9dca741f9180b06e9877915396af0fb14abe69508982661280","impliedFormat":1},{"version":"0a67f1fda5451be17cae743c98c8d016af8574c615b5de2f5e182da0b4f8da83","impliedFormat":1},{"version":"1e382be7a6941288691716de02205a643255d418641a1feb97b4ed48727d2a08","impliedFormat":1},{"version":"8d3f683163c6f15da3b3e7faaf7dd39707479fe60855d3dedf27c9d9c3941776","impliedFormat":1},{"version":"65b643310a6dae3ce9a8174971883af0d88d8098efce704cc2840e385a644e54","impliedFormat":1},{"version":"a40e9400875d9181324861b06420c0506993a36d915da67e36f2e67cbe188389","impliedFormat":1},{"version":"33d22e3a077e6102f7e9ceaf9d378e9eb3dfd7d5410174c9d85c0fa698066cee","impliedFormat":1},{"version":"da8035175767ae1d00d06ae152338ce7e4837d8b3d98e7850599ddae9b44774e","impliedFormat":1},{"version":"5e59bc209c90bac2a3b57e91b2ec9ecf3c8834ba3858d8cb1a179e9cb49b2f98","impliedFormat":1},{"version":"3f660781dc31cf2a3c2d951e7365f4411463b0239da1d55c1579e61cd5fb14bf","impliedFormat":1},{"version":"eb7d1f556981c61654215f87f75dffad7edb1f633e39ade5f2bfda10860e9cb4","impliedFormat":1},{"version":"f9f5fdd22aaece151bc917519b4cbcd62adefe8a8a203392cccef0af11f701b5","impliedFormat":1},{"version":"fa095f470004cf4db449c46ce5a511614c23df2210554dbc58a0998572aa803b","impliedFormat":1},{"version":"4086f629a63197bfca92332723f22bbe9af4c23a3c09f97a490bb8a15529ec6d","impliedFormat":1},{"version":"4ee53ddf94e8fd79130be849e1d529042ab580324a4685d59cc2b6186cddd109","impliedFormat":1},{"version":"12a03aaa8396d202c17760ef359749d40d61e187046e95c1c3a638f53deeee8e","impliedFormat":1},{"version":"d105f2c52e314374d8835adb13faf00cdfa31d439bf82d8e2da9585cb68e0aa5","impliedFormat":1},{"version":"31e2731c5153fe50d4cc0a697c6b8e2f5e8ffd24a711406ad8bb289eed32b050","impliedFormat":1},{"version":"64ebec635ceb48fa8d8663da7845cdf41bb26e519933d14570f2d2e2aa3cbecd","impliedFormat":1},{"version":"613be4f9822f125e562571cbddc5c245d3d838bb66929caa98df193553232795","impliedFormat":1},{"version":"7a4b9b8a70dcca43e6131a4ad9e7661dfd0b2a2b1210c0d0b44d273bba6dbd31","impliedFormat":1},{"version":"436f58d470527f734f553f5e6e8dbad7ecb636ca4320757fb926e0dadd2571d5","impliedFormat":1},{"version":"d478af09776893353cd42bba7d5b98f989140a86119474c15ed75718830c86b6","impliedFormat":1},{"version":"b8c47ca4e21da33d77a4481b91dcc7591955069a0e4623dc1e3817decac62684","impliedFormat":1},{"version":"dcbb54e305cab564ff84e01bfad0c26280ade1c4355803c4202f011475655c70","impliedFormat":1},{"version":"41b009cd281b16a5948e3e9db8770df7554fda4991af1f557b3150360b665999","impliedFormat":1},{"version":"fcf6a112bfd6b3d4e87a35bba7dbd9c924fe6ec58bd80c52f0824bb850668403","impliedFormat":1},{"version":"4b49df9c4f409f1239c7e1231f21f7234069b3ab16f327fbb8df03f6ea599f15","impliedFormat":1},{"version":"83734c33372c0c2f55adb3e7b0d7188d9d57a2946fab54438273a8c22c0da699","impliedFormat":1},{"version":"95f4d407ca9403cbbabd9b02fb4a6cb77f144714073b6dc7db979bc3376178df","impliedFormat":1},{"version":"ef28a3f38f3adaf51d14af4ac960167b254dc69c5e48929a75108d2f858ee004","impliedFormat":1},{"version":"aaca7158efa26be6bc03b94729e4b469f79e8ce1005fd18eb02dacb597559901","impliedFormat":1},{"version":"c4e3be1a86355734d26d264d264b36907070eaa3eb9e97e264ef50bb6d900325","impliedFormat":1},{"version":"8f07376333507522cfaae5d5b51d1e2f31d2ef6abc2edca00a28933e5ab25a14","impliedFormat":1},{"version":"3f142cb642513f54bf8fcf91db75f4fed93317c803865486dae6b4342d1aa6e3","impliedFormat":1},{"version":"bf3885be2cb505a72ce12aadf546aa87f8ab1708ed8019dba3119ba354f81a72","impliedFormat":1},{"version":"8fe84b8776f157959c34d0a60d46bd51649ba17c2fd4fccbfe4ffc217573572b","impliedFormat":1},{"version":"9ebf354122e09eca0bab10b00b14e7fe11f98f96f7099639feb3c6d8988b9fd5","impliedFormat":1},{"version":"7564243559701f2def6fb964949ed309468725999d83365759faf3ea1dddd05e","impliedFormat":1},{"version":"88662b4a76f8166fb042fe2705f8f8b476695b015b955922fcea3ac9cd9c3c73","impliedFormat":1},{"version":"eb67ed797d2f1d10b97fddff999e5189ea8bcd40381d3aa44fb0025d1fc6b3be","impliedFormat":1},{"version":"763766a8e64bec97dd91b896374b65b1b72bd4059bdb4e6c4c8b6a2548da15ec","impliedFormat":1},{"version":"d93a72bb4509a1304ff3e87f3d10d39b754d5fb054aa1385efa27c614d8e0884","impliedFormat":1},{"version":"8e3592826a7fe7f0bb8fcaa64ae35ff665971a7e0ff8f17a70dc9d2b5c6071be","impliedFormat":1},{"version":"f29890ed7b1f34bd9972f19a5805e1b4431840e619ae026760a7dc2ef91a67a0","impliedFormat":1},{"version":"022ab989671ef7edb01056949cca4e08f0dc61e2f385f04d7121f55880092a4e","impliedFormat":1},{"version":"ed074a6465b27b82236bdfabce771811f55565c916498e180fd24d032bed3648","impliedFormat":1},{"version":"f7bf293a6a14304b54caa9c2c2c3dd767fa2aaa54b1c74813c127e4f07c94c00","impliedFormat":1},{"version":"7534da11f7abde1ffc622fe5a6cbb623d5b6933d2778d3c2a3f9f96a206023e4","impliedFormat":1},{"version":"7b0af1213a0e13a065e4b6af20cc9d73280d94254cabea138046803264a6f71c","impliedFormat":1},{"version":"24a3a0f8b6ce5c3ec3d10b16fa77623539231ba9ae7b16e35a3ecbb1029624fb","impliedFormat":1},{"version":"aa6685dd6c66a500ee696b7cd5c62afeee2fa026c40be412bf76b94357658031","impliedFormat":1},{"version":"09549b5404b7da0d41ea6a3c770c558657ac6df00bf47db9ca2358edbe3eaf3a","impliedFormat":1},{"version":"ccacdd11b8d57f9d3f78abd7e539b36e88f543a5f5e61a817ff83632ab7af932","impliedFormat":1},{"version":"fc8228fe0d74474649ab8cab4b9fc7cd56331fc9810da000e3fb3ede8682e115","impliedFormat":1},{"version":"06ac1414b8ce4c71e4e18bd925aa40116408a285f00480bb04ca28fe85b93686","impliedFormat":1},{"version":"c1994397157f2ae8ffc81e7f3a589af752dd1871058ccd4d7585c96d44d3f368","impliedFormat":1},{"version":"47c109e329ee1f85e19e06f5b68a9f5e4bda34d466e31ba68eb6c0b60ae0ce24","impliedFormat":1},{"version":"31944591e2fcfbcf30cc0f0ef542c94cdd5ff454a50009d7222973a962c140d8","impliedFormat":1},{"version":"93751f9e20a6733a0d45fdcacebd6405746d5095ffe0ba4498da65b11d8f5ec7","impliedFormat":1},{"version":"4dfe83634f8ae1f47882d181cdc4f800dc27398ee22674658b98b16fd1513a44","impliedFormat":1},{"version":"fbf0ad850695a03c3ac0671363abe65d6b31c8402e3c9acebb2cb06a6d5557ab","impliedFormat":1},{"version":"d759ce793b01a9debc5822d72fc34355a5df2a06cd7a03a55d805e9d2899ef12","impliedFormat":1},{"version":"9b5184e1b712431edcc88ae1d9c33f4b1c854cdee4efb86c68c3efa63bbe1bd9","impliedFormat":1},{"version":"92cf6b7cc25ef145b60426866b6e9474263452cb3b4145da451dcb5d7219a4d5","impliedFormat":1},{"version":"1a8414881d6de736be1e4684ebbb390f6382ec329c51f3a4b28cc2be0ca0329e","impliedFormat":1},{"version":"77b54b7ae413dc93aaa70fae01849b8fdf547a1f1f71aeb879d096fa8afff799","impliedFormat":1},{"version":"55e19463965c42f135894de100bde6370d2c29c68baaa6d78c28c7993efece40","impliedFormat":1},{"version":"124464272b1b636cc9f1424877439cbbcbba5b5f7d829f3aca0a3e09d7c04ab9","impliedFormat":1},{"version":"afca170740f0b4519edbe74a8e60fd94b04a3a0c7d21cbc4c1661c435840584f","impliedFormat":1},{"version":"0100658462980e5110b17b2ee0d3fd956e34df062454d4af459c8d9dd0d1fb63","impliedFormat":1},{"version":"1b972e3f683e63d1f863fe812add87f56c675f30a40cb2da186b7ae53314608d","impliedFormat":1},{"version":"d44f1a7a775bb246e4f23f9026556d0550dbfd00f57edcef60e90c5e8f8f5687","impliedFormat":1},{"version":"ed1d92bc079858b0c60f34adaf1a53cde4ccabffb32aa6cd6ddeed945cc2cba7","impliedFormat":1},{"version":"747184070ec82acd07e92bd3bc5d41ca51acfb09811d44146d86408cb0e020ab","impliedFormat":1},{"version":"890d3b3fe29ce1159660c834a1f391bc79ea922b25e0462e2f7f1115b4a51856","impliedFormat":1},{"version":"10e4fdfea0178944199ce08c05f787c18e53f450d5fd754593bab1c0fe04da28","impliedFormat":1},{"version":"57954f9c6df8d3588653acec7d8e1c1188179e5938048628e75a71cb32906804","impliedFormat":1},{"version":"87614aadec124fa5dccf4686508809f44433866c76e5fac66e7c081d191d04d9","impliedFormat":1},{"version":"685ad422f9a9e38178f2f5defddb2411169d84100d5115daa367dbc3d7584ce5","impliedFormat":1},{"version":"496b10229842ec76f992713f04417179192ae451cc895c8c494afa4708a7b5e8","impliedFormat":1},{"version":"d6cc4fe0fd0c165bdd34d1fe44b2770b33a4681c9373828b2656ce5c1bdedcba","impliedFormat":1},{"version":"71ede92f5760c66f71ebe15f84f4e583b309f5dd107cfc819115ad75e204831e","impliedFormat":1},{"version":"23f25bfbdb9371817619f407b277edb68a90ddcd0f1ab350e2c6d1be8284ec83","impliedFormat":1},{"version":"5e7a66dff09c3f2e551271bc554ccf5791e4e8a8328c7d765b8b39df7d214de3","impliedFormat":1},{"version":"58acdacc08c623d457a4b72c4de45c7a44ac4a2a123ab3e92f711cdd242db130","impliedFormat":1},{"version":"c7ef31e32e394e9ca38fffafe28623c2428257e4ca8c985b0ebe258f0c217cde","impliedFormat":1},{"version":"6fccb285f8db7b27bb38ff1e408c0671c34a3c2c42cd670d997d36be0e280dce","impliedFormat":1},{"version":"ee8ad5e246878b53586c532439d084cf5cbe7cc2c62ccb8dc5ffbc71c8e01ac2","impliedFormat":1},{"version":"7a2120165ba074faaf8f9dbbce425d412e280ee77a561fadc1cc259a312518a6","impliedFormat":1},{"version":"b0d3e01645863fed9cf5df8b0f3cbda654aa871e2698e6967a3ce96b1b844bd1","impliedFormat":1},{"version":"22dc18d239ab54ecba2f90baf9547275076edbb4aa2df8606ba0fc25f91b0c4c","impliedFormat":1},{"version":"64ba1c6bba49f309440c1fdba745bd08ff834cdaea44b0caf246a74b96bbc886","impliedFormat":1},{"version":"28d7b78f08b30ac7c97eeedf25e9e9424bfb3aeeddca08ed5865e47b4aaf6e11","impliedFormat":1},{"version":"7115c5810224e19bf5fd05818530f33fc6927e9ae4537adfccf360f936e169c8","impliedFormat":1},{"version":"363c77fe2c241291411f2ab5a9758964a4b0bcfe6cd534fff88a63e5cc36c804","impliedFormat":1},{"version":"6586b6eb74ac3283f86c5ec9c7b63d3f5c848cb6684020f0008f228cab0ca951","impliedFormat":1},{"version":"b5b8b22755c32d44571f676197cd39ea6994e1c53710ebcdf128df824c02fae9","impliedFormat":1},{"version":"1adfa61c114122349d6bf15f0289a678069888d4e1b57435f420c203fff983df","impliedFormat":1},{"version":"4ada1f793e5192dde69ffb1baeee86adb7d358541ea265e2f288e07bf4cf00f7","impliedFormat":1},{"version":"6c63faa80e4957d069a9d92d7051c942d359c46670d825876b3d096f847b0eef","impliedFormat":1},{"version":"4dad55f2b594e6621d49c29367c84de2f696b067704b5d43ec65fd0ef3d9aefc","impliedFormat":1},{"version":"0d2b0a4daa93e520b4dc85d33d5f66c30ddbe3109f0239d38f57721dc22d3399","impliedFormat":1},{"version":"f97452a007e9baddd44ae19f0efcf1f7e19bb6d0a2d74c47230b0bf73f90d6f0","impliedFormat":1},{"version":"ee5205f99a44d5a1644badec42b7530ec3544ca51ae65146382103dce9ab8d51","impliedFormat":1},{"version":"74a722e20d2c2ae4260a40489c36a2fb89e96602da04a1f1d19f24cd0140bf2a","impliedFormat":1},{"version":"030a068ea03a4c6d6e45a2ed427633b0e720968f5746ade51c0d7cbe1be08805","impliedFormat":1},{"version":"28f941c630092b5115610c9028d0e54b1cf90f1edb5d292d90c4a4bb9e198e70","impliedFormat":1},{"version":"efd947267e12c19999dea0574aba82e99100cbb0bbe1543c4de29a2f8bd9effb","impliedFormat":1},{"version":"3dc3a0259762d0e58fa5bd64cd3c5b044fc6567aa79f0fc7c5b6b13557acdd6d","impliedFormat":1},{"version":"f124b37e4ddcffc105382207923151174228f1148afebaf60d0c1396e2f14f10","impliedFormat":1},{"version":"4c2692c5605e40008bc4d6c0ddca50ac6f697627ce9e97a5eae850938cb143d4","impliedFormat":1},{"version":"c9bc23082648ddb21fcb7bba7c11303305c3161bd9b83c3c91a8392a0aecf42f","impliedFormat":1},{"version":"385148a5d235cd28ddd96eebfb7e0aad6817eb1ce17e11479d679e70d94801b7","impliedFormat":1},{"version":"26bc16f02e8cda979933c8de3eece59baf56bc284f159dc5f043a0f5b19995a7","impliedFormat":1},{"version":"592a608b9f2a887b01b64f8c0382ff4d6b8366b2ff1a87a44384a1b23530311c","impliedFormat":1},{"version":"aa79ae6ce449be0532f0e611e415899817e86311e8bf5f221e3e8a2f01f7ce87","impliedFormat":1},{"version":"a3f0324b19ffb2eef9ae14da5dc41f67630ec686c0eb1062d77d6643184f763e","impliedFormat":1},{"version":"13ecd891d095b3025f4b47dd299e4b808b63caf03f9d3a1cbfe66eda8ca82271","impliedFormat":1},{"version":"b3608dd071425ecba5eb8bdcd6d560b5549ef13d0ba4f57eef66b81d3dfeec59","impliedFormat":1},{"version":"889f798c6ca511a822e886bf8eabdef7c18dd4fb2775408085afa13a2e077b23","impliedFormat":1},{"version":"e1d26c9bf81ca9b381a544c295f68714518701cc79dea7200d9adc1ede06ed97","impliedFormat":1},{"version":"99ed30fb85e71ec027b19ec1c040db708b1b8af706313adf4b7b2467dba7e9b3","impliedFormat":1},{"version":"ec82dea99446f2e0bedea5ec78c4cb593f4006ba3d2c6e80555dfc4b849950b0","impliedFormat":1},{"version":"f2d35b6be8605377beaa975a75ed7e0779f5b9216fcec4d021d2c8fe29a420f8","impliedFormat":1},{"version":"d947151f893dc37c6cc19b78e7cd0a53ebc15f7362f1aa8c86a54bfadfe472b8","impliedFormat":1},{"version":"cdba3bf90304ad6f28534879be94c4d3e94a7fcf8afcbad2d1b10ace9b37233c","impliedFormat":1},{"version":"2c4be38ec6882e64afb21d130d9cb9e2feffc96759272352cbd8852e651ae336","impliedFormat":1},{"version":"5b65a29e9c0c42bc80c6a1eef23c4d61d90fd4abdc7ed580c943e1f9b8573d64","impliedFormat":1},{"version":"911e9be95f856e3571d9cb65920c40b7c9b8bab2159edf45cd3aa15943249b47","impliedFormat":1},{"version":"d4c84c43fcecb6547ca39b01c8252fa15830bb07d3b7e56a652c56188fe063f2","impliedFormat":1},{"version":"1a12b9d3beadd9671a78e1f999071670252e681154d1c1c4be14d33056405567","impliedFormat":1},{"version":"d1246cbd3a0c3a6bb419f6847648a49318104ac50c281fa24e7e26f750827a94","impliedFormat":1},{"version":"d88853411c9241ec2dcebff66714278f0a08e71182517f71898a7ce3ab6a8604","impliedFormat":1},{"version":"8d3fdab65ca32373c2a98c631b8c21db0779b7bb29c6a4a6809606a3a8ea545b","impliedFormat":1},{"version":"d441b66f4b9b8ccdefe514c378875e38bf32e3e342ee665acc3f71e2d38f7e65","impliedFormat":1},{"version":"1b5d4b8661894a81e40ec979ecd29657747878e869428583ed9a11b4fafddc54","impliedFormat":1},{"version":"cf5f49b943c7ffb214b0e2de03cbc3e1a7c064485a52d87d74bb7a844a9e9883","impliedFormat":1},{"version":"364eafb35b444eee9d8c5cad3f6c861c2dd15c91ee094aea9f2fce452a199f62","impliedFormat":1},{"version":"e84df26f16c23ec529c7f12b9d37c8a6a5faee4a821e98e7b92bc3e0ee07c412","impliedFormat":1},{"version":"c84717690c4cbe5ad7848a50c28ef3c758ef6e27943dc2df67b36002760a09be","impliedFormat":1},{"version":"9722000c4346c85b3b4a0718ca916184f928d3624c988a52d2277d2ed2f78909","impliedFormat":1},{"version":"54d977144654bb588cd91235de729bb53861f1f305026a830bbae2d11241baf7","impliedFormat":1},{"version":"45e62faa6541c97c390e7a4cdf79d91c98016c55e81b99ae11c91e32b68aee52","impliedFormat":1},{"version":"0bd243a9429b0802c028203fb691ec3ca20427d0be1bdf18c0ed7f26b109d145","impliedFormat":1},{"version":"1a6a1f05e23cb9cfcc7076c417ea4f79e772554996618cdbdc82d88f8b0ed0f9","impliedFormat":1},{"version":"09829a67d5313fb27b09bae6e26c89ef605b3fda620c8fec117b358a5b13459b","impliedFormat":1},{"version":"a54301577106e064072765c831b94e202198b80ac6b68775eaebc6855763f87e","impliedFormat":1},{"version":"fe8477fc0d77c613fd0185281f99f6e380b95a70cdf6990f4297e35a7f033ed7","impliedFormat":1},{"version":"1d76a90ee1bf8957b5991f875d42dbccbe42b839fe4c93799e84e5da716fc6e1","impliedFormat":1},{"version":"90a8c5c88bb16b5c3311f79d0727622732a848d7096e56dd0f055e51d9775082","impliedFormat":1},{"version":"c08a696304b0e5436189e8da2110ef289c8d68d27242651788df8b05b72d4da7","impliedFormat":1},{"version":"382d340458d343738fd41289cd839fa6af5431f272b8af087cba775e5fb1ee87","impliedFormat":1},{"version":"c56c6cd8036e97410302ced8beacc3d6713815f2a710518d5e87927f17d24b8e","impliedFormat":1},{"version":"8d4564fe1d5bedb3d66f8c24717deb4febfd48853e9b8f5e9eb3ae9b92c292cc","impliedFormat":1},{"version":"35a8ddc373ef4ed2a62f72b4a46ac79797954dd14889960488f647fe2512628a","impliedFormat":1},{"version":"612181a1ef038005a1ba04fbadeda825475bfbf9f915050193948a37af9088cc","impliedFormat":1},{"version":"f4e9e44795f6fc86bd528889e245c3d602def2fee451c8a451b7c8f7f6abbe19","impliedFormat":1},{"version":"68212d1f0d94dc7f9f47a96f562570d635d9f4c068ec5440754efe45bf85d555","impliedFormat":1},{"version":"d4484f02230cc5c616ff0103d5a94ee9d044ac6e20d5fc2030911f6ec2f44883","impliedFormat":1},{"version":"b7c4a11dc058ae81c80ad13c951727df085aae3a90bec90e510bff2b146a16cb","impliedFormat":1},{"version":"ae0832a5e360be3173543dd43148963c9962d03a87e3f437b017189c33c41599","impliedFormat":1},{"version":"e12ed5d132cc12dc9774db23bc8e09dd76b53ece991960f7a79303f30f21848f","impliedFormat":1},{"version":"fa5d8d4abf62b0e60a7ce681f9b0f85cc0196f89c0c9296b82956966dbef9554","impliedFormat":1},{"version":"93227d14560f521e8116bd03d79a67b30bda2c016e77d39e43ccbd39ccf5e8ae","impliedFormat":1},{"version":"740e3c1159e392a9921b17ee22ad3a0bcaa03e228d62220fdf95cd8365a739bd","impliedFormat":1},{"version":"4f02aea4aa56516d70e4138f8df41af8414ac14ba45fdc9902f4cb90e5ff1d29","impliedFormat":1},{"version":"42de229ddb69c7838d8a23ddf92f975783358f8fff8541b291af89ce5d67e497","impliedFormat":1},{"version":"e4d57e8dcd19b07c72b358d605f7ed131187f923c13ee46b9417b8caf37dff73","impliedFormat":1},{"version":"587cf3d68ee9372b24750e4269059e5c6343f267b55ce4405f7d295f5b1821cf","impliedFormat":1},{"version":"51df1030e2b637641e5dc69f21ff7b8bd17117fad2242cd67fc1de7a0c8abdd0","impliedFormat":1},{"version":"288953572c9fffdcdc177b9f64dfbfea2a1e78d6dc550102609ad112f818ea86","impliedFormat":1},{"version":"d50c597f9007b4be5d69679bdf4885f00df643cfd63756daabdcca55de9ece6c","impliedFormat":1},{"version":"ab56a8294565d05472fd1396c08ff9b134a9cd8547722152c9e5a014490d5cec","impliedFormat":1},{"version":"7c52c2cca71aa62534de4df99b80179cfedbe3720a0ecd4cd8af58ef9c2938e5","impliedFormat":1},{"version":"b826170187945aba2457d78315e02403bb06c4b022ffe1e32d69d19ee81b6bea","impliedFormat":1},{"version":"107b4f0aebc2ad30670d7ac3d1a7be094f3fccd90eb6a9c7fe54f1c0145a2ca5","impliedFormat":1},{"version":"b65b9ac92a052f19dcccb936251339a032eaf0277bd8e5073596fea9396a4bc8","impliedFormat":1},{"version":"c14c6cdbc3d897b14fdb296fc6d3124697a62ea34405e153a963868770347c82","impliedFormat":1},{"version":"2321a726ad2c56242e7e98a7c015652e90a48f915f79d91270e2ae72c9728546","impliedFormat":1},{"version":"47838795666d37b634ec7e2e3fe636fc5f8c4aca1642ad7c3a08610c2b330f29","impliedFormat":1},{"version":"1900be52a43cd3d42cb19dc1a2e6db4fc9e0b177d18d9e14e5a46c44c195ab6f","impliedFormat":1},{"version":"fec089324c3049652a300fff0187df10840c2ef95a3cf21123d16ff3a27495be","impliedFormat":1},{"version":"db169d2e5951f601ca959914d8fa6570feb25797f000fd11c89e606fa5c9fbff","impliedFormat":1},{"version":"d180dff12471f94de5fae0c8ba988c9563772e2320b9edc8b16a19d47b25e71e","impliedFormat":1},{"version":"2308bc95d1d843970c69a3a97b4b35d5b4be1ea343e674205914cb3f55dd1c06","impliedFormat":1},{"version":"08f3e51059401eac66413cd11e53e87e4ae85af3a112ea582394a2b2d4a1a628","impliedFormat":1},{"version":"05724ec0e2f3242b7bc70a8b636f63962a402e861d8062046c60c0604e103d44","impliedFormat":1},{"version":"92e62ffe6c4e2cbf53bf304213755d44013ede3fe6128aac4c91c2bfe7b605f4","impliedFormat":1},{"version":"cfbd0ae53d9652709bb7c374ac5e45557cf4b87b78c7d5a8565c6d63e624e7e5","impliedFormat":1},{"version":"88badc46b1cbcb8ac7ae80e0d1cb34021d284b1fac5a6a95cdbd2c45a1375b61","impliedFormat":1},{"version":"610afaa39b6dec749a61753af6c8a2fee2fce0af7bd886f998f955fa55b2dae2","impliedFormat":1},{"version":"99e0e018c2db072d3a51792f94b4adaad1ae73964b813137141214d8457d131d","impliedFormat":1},{"version":"5f2afa53975266e09f5a9d47aaa2e3d83891622ac808586f633bd6f800f95e0a","impliedFormat":1},{"version":"6374bdcfe301fab5091b1511a4130dce70cc07a2c9aad3c17b208397594e658e","impliedFormat":1},{"version":"68dc809032ee8e3c5e4632cc87425f95204a852d76adb719d732932cbb5bbee6","impliedFormat":1},{"version":"279a1682a9b5052eae2ed5b22b38e60f024f7cbc5a864c08a1a79f4962bfe6a9","impliedFormat":1},{"version":"894c54d0eeaf2bb16496116a79743fa767b644c6137359ba797205629cd2cb74","impliedFormat":1},{"version":"b0686f550b3265e68a3fecf01a6b670fa223e7c8b7c6e1c28aaf14bce45f92b4","impliedFormat":1},{"version":"716c9adb70592ad8e55436562c4731e95d3431ebc856b9561fc63f67efc8f484","impliedFormat":1},{"version":"79a1a6ddcca14acf760e991fa2e3cc3dd596bc7c299f3616d705189b62b6ab25","impliedFormat":1},{"version":"d60c79ce6cae373b2c098dd2e91c3a711d211f235e45639e8287bea40b416450","impliedFormat":1},{"version":"2bc6b9b0a5caa0ea5e6a60ab6a25d37fd9106b0c665faeb0bb004c5eb92e28fe","impliedFormat":1},{"version":"b3a7fa3cf5a181045d700213885a1724a35ac228e41664bc5a68422d52bc10fb","impliedFormat":1},{"version":"894e989fd92733bf46120d0a7ab9a1b9aca3b1abb3b52c15ce30d4d021582448","impliedFormat":1},{"version":"e394e2ceb3a22fe136db684b1491999d605cda3e470d6cb3145424ab0e5ceebe","impliedFormat":1},{"version":"645b6e3e8c2aa75bffc63403a37404268716cd05bccabe73270bd2227df72c20","impliedFormat":1},{"version":"1586d1a30581805347b436b3550c8c236fb061e721055a0801235a4c5aab7d5a","impliedFormat":1},{"version":"37631c0723b6dc4c5bb3ef2590935faa04a8a95b5afa502edb7848a980fdba88","impliedFormat":1},{"version":"d577f95a832462e42dc74fdefd8166757c27746ae2cc86d4e859c82a1f2cfba2","impliedFormat":1},{"version":"fd192e3d8e0643b600ba5729faec12293ad9757b7106b1c3c69188b902105484","impliedFormat":1},{"version":"6d8a34b4ca1005234d2a86516dc39173cddffe49999d7e095906578a1febd178","impliedFormat":1},{"version":"c2dc3b4c5109d337ef17f14f27abac28f37548f614ffaef50e145c768b1cb402","impliedFormat":1},{"version":"5f0afe1448dd646908dea03edcc12e897eb4c1f548f6e793e0eb6e7c38cdb699","impliedFormat":1},{"version":"7a39808ac5b4dd03086265724794e1888b7c7c1e8c6406fa059cd6f74bf44fea","impliedFormat":1},{"version":"0f2231232d5a45bc86a83937805ff96ce3a9e361baa72507f4d6872914a4cb82","impliedFormat":1},{"version":"4e3293e12882572d6f57e4aea38bef99c788a2bce9792600cdc33590597cb3b4","impliedFormat":1},{"version":"b171120694ef008de625704558137014170b2476bb7d0003c9ca287a7397b6fb","impliedFormat":1},{"version":"861f02e37b7df7afa020436a33ffa427582afa46d172b658cd76582d6e247c17","impliedFormat":1},{"version":"ed90345cb6d89818cb4971481e78aaa17309b947d0f28b378009402f33fc58f9","impliedFormat":1},{"version":"998e5d8d80dcf1f1c9ddc3f758a50afce056ae31491806f22f114c3dcf616f64","impliedFormat":1},{"version":"d55483c4b30bd723135f9c9a474519203544852605b75ba042614863f0b0ca64","impliedFormat":1},{"version":"e31e5922ececbf20cbbc3728dfda31f3c3c7a114e1a0cfb5b65d5677c105b344","impliedFormat":1},{"version":"bef77605f7f2e5a3b3bf5c56c1e7b7de6d300b9c796ec6efbb99986044993230","impliedFormat":1},{"version":"af3158baa5c0d08c70f615c3fcbc5f628f93e0840a70833ae72712ec696a612b","impliedFormat":1},{"version":"1f1d54d2994d145042bed90681270a87f4ee8221102f152997bdc1ec0801bc87","impliedFormat":1},{"version":"7ba781ae6971dd08123e8b867d203b51efa1023b9bd228c1a7bfb61428306dbd","impliedFormat":1},{"version":"ba5cd2a700140ec254e6c908e928a8a0db8af10d06d1aac3627ba5bff14892b4","impliedFormat":1},{"version":"ede926038a986544ba5465471959b453d5ba1183937714d3cf6ed6602e56e244","impliedFormat":1},{"version":"a698df8edb4c4d01ca471f95255a132ed73046c3a20fa159c8f42dc5a7738fd2","impliedFormat":1},{"version":"904d352dcfe31edb02bdcb5eb783dff28cd5ce9803abdde51348af708a6775ac","impliedFormat":1},{"version":"0b96d7c6944eebe821cf2b0a7445f9a65bb79764d9cef68dbaaec97b5be3679a","impliedFormat":1},{"version":"6fc6d9aeb5b3487b3d502d83753535c8637f8fd79794e134268497cf597d4875","impliedFormat":1},{"version":"8ea3cbcba43ae276e549190b8f5c9b3633ab300b5d161aa21c4b8883086c0abf","impliedFormat":1},{"version":"287f13b27b45ea4507bfc101d9cec2d132bff445493f5c3c61677eda07366b62","impliedFormat":1},{"version":"b03a935f25c61ce2fdee8eabf438bd862a5e76c7e270783cea00a8b81b710fda","impliedFormat":1},{"version":"5e2830efab012797d6e84c8ac34c9ccc785e1fb3108b142d9530bf3b155a8e63","impliedFormat":1},{"version":"109f9f938916aaa8fbc471974348857a1eb8c6931db5c3976d9ad148101c2f69","impliedFormat":1},{"version":"48ead9ffb914f4bc0a8318415590fc1f87e0b21832bab7c6036dc989913fd65f","impliedFormat":1},{"version":"535f62c362c647aff64dae3c1a0096af95f88eea9d3224f4cdf2d0cfbc53ce21","impliedFormat":1},{"version":"21dbf44129ab4395d9ab1c452b9fa24dff3991c40b8e791b5484a6982267e4c8","impliedFormat":1},{"version":"30bc16159eee6823a0583e86d0d4ae497f93d29bcdde14fa3ecae01d2ac21681","impliedFormat":1},{"version":"1f0ec1fe6b1a63e9c6d7bf0e9070eae21c41b4cc5a1dd0f78406f3f6cb163399","impliedFormat":1},{"version":"e5b20d0924a3844cb1f68fd8657df3d48ecf67d0102792b332244b6919c397a3","impliedFormat":1},{"version":"c798a743fdeac5568cfe80ece19a06eb429f8d37fdd9935fd8aaad7da57a7f43","impliedFormat":1},{"version":"4eaf99c96c6ffe17b53bfc959fbe0a88bfacfa764ccc9098a859d8471e739466","impliedFormat":1},{"version":"d2d2f96317b6bdc881aaab1760082410e23ce1ba0e173b93c7ae68f2e3d0ed0a","impliedFormat":1},{"version":"0cce875bb5baafe3f250db52628b20bbdd28f7b04d0cd5742689aa9c0440aaba","impliedFormat":1},{"version":"0c44c0b40157d78e036c0eda991abb42feaf1687482e5daa017c9aec4fc61b40","impliedFormat":1},{"version":"b369c1f495472852d9a71dea8edda610862e0316bf83bd8dc32b2922ffc2d75c","impliedFormat":1},{"version":"2291a2e8d3b21dcea23ad0ac1f4092a70ac1c17b5057eff87a8e711f5acffdd2","impliedFormat":1},{"version":"ade5356e843aecc1cd26538b01f78d97e63c660ef62665d48e47717e34eaa746","impliedFormat":1},{"version":"052866da0db4f275c80ab717b3477a7d5cb1d2f193fd34cb6356c6908d3620e0","impliedFormat":1},{"version":"e833a3a7abbc21ca03ce286831767908f38f232bae2d8b75f2d59c9a2028ab80","impliedFormat":1},{"version":"e4eee70317fcc519dd8f28d210653830be8fd654bdd291f41d86e4f28a07dc55","impliedFormat":1},{"version":"33e30486b113a91dce608e7b767def4697cbcc8f2fcd49e8f5ceee9526aedf53","impliedFormat":1},{"version":"8ae9426e106564756f74748dbc0a682a5a3cbaa2d4852a0fb39131f1ab682e16","impliedFormat":1},{"version":"c4a778391c52d66c607c67505f70464234597b42f888cf7c5ee94eb869a57e09","impliedFormat":1},{"version":"fd0a281132535f7c975e570b41e789b6ff53633e7277f16090d7e6e97caca861","impliedFormat":1},{"version":"1854950d3a496b9947f48ba68ccb59c25950e49096a3f455218d4b7a0658edeb","impliedFormat":1},{"version":"626ae77bfaffb24ba9f447da66e0943e5b29b94f9a5dd8ef7778050d39cdadcb","impliedFormat":1},{"version":"e4406a705cb683301c1a4d51b1372f3e2f162889b38ac45bb1b34a1a9e3fcce2","impliedFormat":1},{"version":"9d5288124c09ec085f53d6cb7f33d97aad4cd46779533ccd259414fb7620b747","impliedFormat":1},{"version":"146e37c11e85d4b46738a61a449d8e280444cdd350c577f55d752d4594d01504","impliedFormat":1},{"version":"bb1a3916cfa17c411a6986bd4ca9fd1c73a7be78bfa90e8a1ae4939f23a36d87","impliedFormat":1},{"version":"f1d7df66d23d3f6f0c2be21169fc561dfc7ceb447ccd71b158c0b408b948eba3","impliedFormat":1},{"version":"94468e8e8a77031ddd038f76b68a75ea640d638ba6fc87b5d2febd3c9b98dfeb","impliedFormat":1},{"version":"669925dba4f836fb2b88ff190fc972d5090b8a96934eafd33abd952d6a521068","impliedFormat":1},{"version":"232203e33fddf9df74b2f22d0c21034371c29166ca4475aab203305c38857e0f","impliedFormat":1},{"version":"a101014dc36982cb8dfac513c09e66811d45b0673ed4f42bf15c06fe371f27cb","impliedFormat":1},{"version":"c62ae2acccb5295c06418b673f94343dbc9bd7ed6091f64695270d8d5338e4d2","impliedFormat":1},{"version":"949d7532d306ee6fbfbc00e20e41fa90f521778a392126aa3447b4135c852708","impliedFormat":1},{"version":"4642033011652037fc5fe077ce6697f12788d95f7494085b9bb230184801ed70","impliedFormat":1},{"version":"b5a14089ffeeddf66501ba70dd4067e5b4543a65f0774c5ba457665d751cd1ea","impliedFormat":1},{"version":"9e9ab3af96f5decbdd5b5ad7cca13232841756bed6df874496c77b8d401e515d","impliedFormat":1},{"version":"c304ab8ab3ef18b522c3c99896448a080ea813a9c1420de3bbd35533397d9f54","impliedFormat":1},{"version":"753ccb7381f509ea3ce3fb65ee308cb5750fd6b05e198ce81f11f059b334b4c9","impliedFormat":1},{"version":"7e632c25361c562e8cb237206eb14b47fcba0c70581c0d914019048e1251afbe","impliedFormat":1},{"version":"4c515011ecac21e58ee66b0e8dbcbff5e415f5c216bbf1b0702d02f162508fcd","impliedFormat":1},{"version":"9fa78195ad653a0151c5375432f44635213d1bcb3af52920a69302ab547385ac","impliedFormat":1},{"version":"83ce80a1c6b1e063dd46012eaebe178c08b8ced54ecf1a5dd215159db50f1b20","impliedFormat":1},{"version":"5f9341977d9adfeb6d8205f5bca4dc9abdb31d276f0e21416c17ab6b490725fb","impliedFormat":1},{"version":"074f2774d966170489322cb8434af71d9cb2d2af571421b5443814059e942b6e","impliedFormat":1},{"version":"280cbf9acd33a1af68deb603a79e88adbe8106d46958c0a7338e16a9018501d1","impliedFormat":1},{"version":"56df42ed0429345bd81689b59c915ac40ef55d74c1bbc4b30c6d6f1ee9582b80","impliedFormat":1},{"version":"ae8ba05e6bef7f620bbb4b2f69e43e10d2805bd529dfe4733426e9d8a51376b5","impliedFormat":1},{"version":"c25b675487e4db9a4b2dd2db91572504c519057755733b6730f7c59a47089cc5","impliedFormat":1},{"version":"f5007747a32bd037142990542c691864cdc106fd3d44db7b8060fcb5bf831cf2","impliedFormat":1},{"version":"856ee6b88a857808ba704e1968a200c0593e4925567bb370133b5eb831db6ea0","impliedFormat":1},{"version":"1077605d09519e62ac8f8299414aafc418eceec98bba478e78281dfc944b3715","impliedFormat":1},{"version":"9938fddc857ab77c148ec5a051ac57c27279985158eb14eb11e5b430fb8142a8","impliedFormat":1},{"version":"2e16c438dc7b33f00a189edc957dc6b586cbc14198909417b11ae1e0e2b215b1","impliedFormat":1},{"version":"2029c2469c11f67af9230d0cd220fcd905eecf85c32926a56bba7eb541c65617","impliedFormat":1},{"version":"9d2ec01fd89f860c92ca7c377619936c395342719fc23c0aedbfd083d2577969","impliedFormat":1},{"version":"79d6929feec19a1633b853cc193dba513702fa7dcfea2ebe35d0332bcf39ecb2","impliedFormat":1},{"version":"afe8fcc530e7635193f756a3906d096aa65f6aeef68b6876962ebf463e8f8f4b","impliedFormat":1},{"version":"49a2e0de35ad732b592070a840d077701131d6334926e6adf3774da1a674d433","impliedFormat":1},{"version":"19361634f59cd33faad6fbcf9332e1f43b2c99826561740a48ea3bc1487b4ca9","impliedFormat":1},{"version":"371f485c594ab26449e55a2163fd925fbe7f30d755e9f9ee9c5442d8d777bdcd","impliedFormat":1},{"version":"21da34fe592786cde4eda9695ed1027f75d924f11910afff631dff9ed7874ba2","impliedFormat":1},{"version":"1da1845aff688185dab28447e0b2648201e1f68ed3d9c3a2ce85c207adba9dd8","impliedFormat":1},{"version":"9d4348bc0564638ee6aad0cdb874e63ff995eebd8714067f513e194c498f828a","impliedFormat":1},{"version":"b8aff5a7cbcb75a60a481e66ac122b723a0c9025f3708468528d22191e599f7c","impliedFormat":1},{"version":"af8258605178e1abe2607fecd87ebfd36a718fa64e572f44a62186f0dd61fdc5","impliedFormat":1},{"version":"abe0de26d2be26f46cf4d6f376f1f73c29a27cb5f85ddd8ddba129ad2f79cba0","impliedFormat":1},{"version":"360b9981cad1e05ee045f6ff94aabd94701a6702668250e5be7b7c415b8949e8","impliedFormat":1},{"version":"c3f2c9805d0b85d56b6188f455a16a46221c6fc5a1966978237c3ef7a6756d95","impliedFormat":1},{"version":"4ef6addc4676ffd9d05c8afce96ddac5753fc23fe0833a27d1fd09b9680fc060","impliedFormat":1},{"version":"dd09b143c7696a5147799d23ef2cd6a888348b313f16d4c74d5ca34fe5570186","impliedFormat":1},{"version":"ce2e39450181a9f453a36cb546589dbee9be76247a3ee574463b55ad96c1957d","impliedFormat":1},{"version":"85685486560923b5e7224f09a1cc0036ad250afd596c2aafed004eaf708e1486","impliedFormat":1},{"version":"624a706170110f3633fe87530e26521de9ffd34118bdb5a19c680be6f1126a19","impliedFormat":1},{"version":"1579d62ac4496c141eb48fdfe5a1d34f035b60bcc7e0f67b9d51785a5d049064","impliedFormat":1},{"version":"df6a1b6f4d5fa53fd18207e111125c90cf3195e3c1f6e96f7fd6e5bc1485e21c","impliedFormat":1},{"version":"846ce145df81689c99600bd8305b3cd038610255762bdc309f9f090a1007b874","impliedFormat":1},{"version":"726d5aaae05cb621529935f92af5afdc1de7c399a51c08e07d1693452e1e52b1","impliedFormat":1},{"version":"aa7b3e667c28dc7e8416a90ee4e83d9303407eefca23698e32873a73ffefa982","impliedFormat":1},{"version":"6330b0dbabbb97865226d48c63e350aca069b095856cffcbb7905d3e8b987a7a","impliedFormat":1},{"version":"6104ba83ee1729d9b6dd0e03e805090af750aa27941118f903b3d32c36effbe7","impliedFormat":1},{"version":"120ed3632ac24a58084f0c9d8ab361774d4082e3b0fe9c9d96548700d2a41389","impliedFormat":1},{"version":"3b2f12bd79ccd28f34a4c8088fdf65878ce6a47290b79806adfb8bae4e2906f7","impliedFormat":1},{"version":"2d748f3a175e169133ad0ac78c39c9a1de941d322f6ec4b84cacf10f9617b3dd","impliedFormat":1},{"version":"fcf58b931d710dc2505396a8a31c5c3700408ad3d95e1499146cfbe38b0f0660","impliedFormat":1},{"version":"700cbd24936c9c86c7dd85d41021c433326e081d5753120ddfb1093e3f1daad8","impliedFormat":1},{"version":"7717f3d631366e1b6fb203264fe88ffb45f7b899b3effd86c8d0922dda259803","impliedFormat":1},{"version":"2adefed7821823a516dbd347bb1459e512011d9d33d999d3089227bad6b45fa6","impliedFormat":1},{"version":"568136547127c422189db729243deee8fa4a309050e4cfc63e95ba49481d3800","impliedFormat":1},{"version":"bc9fd0623bb057bf10489d0934dbb34f42c4c5aeb6104f52f599ed795647260b","impliedFormat":1},{"version":"7f49edb27fe7570cd61f023c0fe0da0fb9d2fe94af1b8b8624e84c77e13fee41","impliedFormat":1},{"version":"56cebf165eff568ff21b1194b41aa1d729f0cadafe88f26c612b8d8ceb3d35fd","impliedFormat":1},{"version":"88801e918cfa106e7386e9163fff0cf43da9723e11cf1799d7d0b1c4fffe64a0","impliedFormat":1},{"version":"79bf435dd91dc64bf9c05fc07dd4da713a97557f06fa80ee87d7380045c3113e","impliedFormat":1},{"version":"85b5ccb476c5a19d7797580ef61c120d69befee1ff8d7665438c77e5aa13dc98","impliedFormat":1},{"version":"e2734167b0585639eac4ba5b6f0eb16cc08fbc2e624e2ee666db29b13f8712fc","impliedFormat":1},{"version":"d7639fac55a6b97aa993734e88d9efd9a4bccf0b965285ca7ac9a6533104e3cb","impliedFormat":1},{"version":"0296bde98868f351841512387587a62c6be128eba59c67be887a651d11d53516","impliedFormat":1},{"version":"82e70cad3f18a59a42ab7e63a315eca80d2c36e2f014b55308c75db43bf96638","impliedFormat":1},{"version":"f292f36fe589ba187dc2ac18ee4d5c25bbb13d609fc5bae5d7058791fa822d95","impliedFormat":1},{"version":"94d11ce834b8daf91c4a6cd5392beecbfeb37e4af3ad9ef2c3a57a04627dadff","impliedFormat":1},{"version":"e3572cd5bef31202a38116bc0660e961ad33ff3e75cc4b41aed861ae6cc2047d","impliedFormat":1},{"version":"bb60f122b09e3a4d0fca092809b929f13389085bfcd6f27d2d4bf8a2949f2f49","impliedFormat":1},{"version":"66acdd62c8d961cc9eca7efc79cfcb49a33013b807512669d398bfa7bb71e04b","impliedFormat":1},{"version":"eaadaa072b7a5672f9232c85cf90ed29a59a73fde5610d0d2eab9130dfd6d8be","impliedFormat":1},{"version":"e5de4eda5970a9db4cad8d9fcde2c727a060ecf6e1f7ec89bd751278631c5262","impliedFormat":1},{"version":"82344c9191876e759019334b2f4b088ae29fc4112e0de5b8ba34bd13a63cfdb6","impliedFormat":1},{"version":"ff6d26ff5bbb7b9a6a916d0bbb07e2cca53e74f3e65d9b3dc36a545685f11b1e","impliedFormat":1},{"version":"1146ae5e0d971eb3d5adddb3569745a6bf7af0766ba2a8dcc15515574eb0ea63","impliedFormat":1},{"version":"d0e40b83b3f1f0603772a8c3240827ddc7741eb2dd74b8170be3b84f3b505d6d","signature":"4054459a787bb469155a223cb134928acd7a15de977c9ac3d33d6d85462a44b1","impliedFormat":1},{"version":"5303f0195186dd03159aa6cf7568f55c11ffa02876610c995f661a49d473dc44","signature":"d1b74750ac2873da3ea73e0958230e6ebd6f56a2cf4e50680004064c6e27e25f","impliedFormat":1},{"version":"b2fd2e0133039371c1744a3ab1931c9d6e2e915a3d897af00ba20dd4da833219","signature":"5c9cd8ba4f81982e8002c709ba0f6675b7aaf1def7f8a98a9996040a3e1c2bc0","impliedFormat":1},{"version":"6aeefc0a13f8877975461713d3dd27060da13729937022ebeedd146f3200c7e0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"68d5e9cdcbc6017641816c6feb161136e42a6d705fb97268c67d8a9c01056bca","affectsGlobalScope":true,"impliedFormat":1},{"version":"6a7a30712a8b338148d8828670bf575d8f8970639cfa9f52dcd0a9360fb08c47","impliedFormat":1},{"version":"693f2c53da1bf27e6eec1173ea9d19524398ead2ae7730e24dce9682e269291b","impliedFormat":1}],"root":[193,197,198,[200,203],206,207,[2056,2062]],"options":{"composite":true,"module":199,"noUncheckedIndexedAccess":true,"outDir":"./dist","rootDir":"./src","skipLibCheck":true,"sourceMap":true,"strict":true,"target":9},"referencedMap":[[204,1],[199,2],[194,3],[196,3],[195,4],[287,5],[288,6],[289,6],[294,7],[290,6],[291,6],[292,6],[293,5],[1939,8],[1942,9],[298,10],[295,11],[296,11],[297,12],[302,13],[299,3],[300,3],[301,14],[1943,9],[306,15],[303,11],[304,16],[305,17],[309,18],[307,3],[308,19],[1984,20],[312,21],[310,16],[311,22],[533,23],[1953,24],[322,25],[313,3],[314,26],[315,26],[316,3],[317,26],[318,26],[321,27],[319,26],[320,26],[365,28],[1944,9],[328,29],[323,26],[324,3],[325,26],[326,3],[327,30],[329,31],[1945,9],[332,32],[330,26],[331,33],[333,34],[1946,9],[337,35],[334,26],[335,3],[336,36],[338,37],[362,38],[1947,9],[339,3],[340,39],[1948,9],[341,3],[342,40],[1949,9],[345,41],[344,42],[343,3],[346,43],[1950,9],[349,44],[348,45],[347,3],[352,46],[351,47],[350,26],[1952,48],[355,49],[354,50],[353,3],[361,51],[1951,9],[358,52],[356,16],[357,53],[359,54],[360,55],[363,26],[364,56],[1955,57],[366,3],[372,58],[371,59],[1954,9],[369,60],[368,61],[367,3],[370,62],[1956,9],[376,63],[373,3],[374,26],[375,64],[377,65],[1959,66],[382,67],[378,3],[379,3],[380,26],[381,68],[392,69],[1957,70],[383,3],[384,71],[1958,9],[387,72],[385,26],[386,73],[388,74],[389,75],[390,3],[391,76],[1961,77],[393,3],[401,78],[400,79],[1960,9],[396,80],[395,81],[394,26],[399,82],[398,83],[397,26],[532,84],[1966,85],[405,86],[402,26],[404,87],[403,26],[438,88],[1962,9],[408,89],[407,90],[406,26],[409,91],[1965,92],[421,93],[410,3],[411,16],[412,3],[413,3],[414,3],[415,3],[416,3],[417,3],[418,26],[419,3],[420,94],[434,95],[1963,9],[422,3],[423,96],[430,97],[1964,9],[426,98],[425,99],[424,3],[429,100],[428,101],[427,26],[431,26],[432,26],[433,102],[435,103],[437,104],[436,26],[1967,9],[441,105],[440,106],[439,3],[442,107],[1972,108],[446,109],[445,110],[443,26],[444,26],[465,111],[1968,9],[449,112],[448,113],[447,26],[450,114],[462,115],[1969,9],[453,116],[452,117],[451,26],[454,118],[1970,9],[458,119],[457,120],[455,26],[456,26],[459,121],[1971,9],[460,3],[461,122],[464,123],[463,26],[1973,9],[468,124],[467,125],[466,26],[474,126],[473,127],[469,26],[470,26],[471,26],[472,26],[1974,9],[479,128],[478,129],[475,3],[476,3],[477,3],[480,130],[1975,9],[483,131],[482,132],[481,26],[484,133],[1976,9],[487,134],[485,26],[486,135],[488,136],[1978,137],[492,138],[491,139],[489,3],[490,3],[504,140],[498,141],[1977,9],[496,142],[495,143],[493,3],[494,26],[497,144],[503,145],[499,26],[500,26],[501,26],[502,26],[1979,9],[508,146],[507,147],[505,3],[506,26],[509,148],[1980,9],[513,149],[510,26],[511,26],[512,150],[516,151],[514,3],[515,152],[1981,9],[519,153],[518,154],[517,3],[520,155],[1982,9],[524,156],[521,26],[522,26],[523,157],[527,158],[525,26],[526,159],[1983,9],[530,160],[529,161],[528,3],[531,162],[1993,163],[537,164],[534,11],[535,26],[536,165],[584,166],[1985,70],[538,3],[539,167],[578,168],[1990,169],[546,170],[540,3],[541,3],[542,26],[543,3],[544,3],[545,171],[566,172],[565,173],[1986,9],[549,174],[547,3],[548,175],[550,176],[1987,9],[553,177],[552,178],[551,3],[554,179],[1989,180],[558,181],[555,3],[556,3],[557,182],[564,183],[563,184],[1988,9],[561,185],[560,186],[559,3],[562,187],[1991,9],[569,188],[568,189],[567,26],[573,190],[572,191],[570,3],[571,26],[1992,9],[574,3],[577,192],[576,193],[575,3],[579,3],[580,3],[581,3],[582,3],[583,194],[1994,9],[587,195],[585,16],[586,196],[588,197],[1995,9],[592,198],[589,3],[590,26],[591,199],[596,200],[593,3],[594,3],[595,201],[888,202],[1996,9],[597,3],[598,203],[1998,204],[604,205],[599,26],[600,26],[601,11],[602,26],[603,206],[612,207],[1997,9],[607,208],[605,26],[606,209],[608,210],[609,211],[611,212],[610,3],[2000,213],[618,214],[613,11],[614,26],[615,3],[617,215],[616,26],[632,216],[626,217],[1999,9],[622,218],[621,219],[619,26],[620,3],[625,220],[624,221],[623,26],[627,26],[628,3],[631,222],[629,3],[630,3],[2001,9],[633,3],[634,223],[2003,224],[635,3],[645,225],[2002,9],[639,226],[636,26],[637,26],[638,227],[643,228],[640,26],[641,26],[642,229],[644,230],[2004,9],[649,231],[646,11],[647,11],[648,232],[655,233],[654,234],[650,3],[651,3],[652,3],[653,3],[2006,235],[658,236],[656,11],[657,237],[671,238],[663,239],[2005,9],[659,3],[662,240],[661,241],[660,26],[670,242],[664,3],[665,3],[666,3],[667,3],[668,3],[669,26],[2013,243],[674,244],[672,26],[673,245],[718,246],[710,247],[2012,248],[679,249],[675,11],[676,3],[678,250],[677,3],[709,251],[2008,252],[683,253],[680,3],[681,26],[682,254],[689,255],[688,256],[2007,9],[686,257],[684,3],[685,258],[687,259],[2009,9],[692,260],[690,16],[691,261],[693,262],[702,263],[2010,9],[696,264],[694,26],[695,265],[697,266],[2011,9],[700,267],[698,3],[699,268],[701,269],[708,270],[703,3],[704,3],[705,3],[706,3],[707,3],[711,3],[712,3],[713,26],[714,3],[715,26],[716,26],[717,271],[2014,9],[724,272],[719,26],[720,26],[721,26],[722,26],[723,273],[732,274],[725,3],[726,3],[727,3],[728,3],[731,275],[729,3],[730,3],[2015,9],[735,276],[733,26],[734,277],[736,278],[2016,9],[742,279],[737,26],[738,26],[741,280],[739,26],[740,26],[752,281],[743,3],[744,3],[745,3],[746,3],[751,282],[747,3],[748,3],[749,3],[750,3],[2018,283],[758,284],[753,3],[757,285],[754,26],[755,26],[756,26],[764,286],[761,287],[2017,70],[759,3],[760,288],[763,289],[762,3],[2020,290],[765,3],[769,291],[768,292],[2019,9],[766,3],[767,293],[2021,9],[772,294],[771,295],[770,26],[773,296],[2023,297],[774,3],[778,298],[777,299],[2022,9],[775,3],[776,300],[782,301],[781,302],[779,26],[780,3],[786,303],[785,304],[783,26],[784,26],[790,305],[789,306],[787,26],[788,26],[2036,307],[799,308],[791,3],[792,11],[793,16],[798,309],[794,3],[795,3],[796,26],[797,3],[850,310],[846,311],[2024,9],[802,312],[800,11],[801,313],[805,314],[804,315],[803,3],[2032,316],[810,317],[806,3],[807,3],[808,3],[809,318],[838,319],[837,320],[2029,321],[814,322],[811,16],[812,3],[813,323],[827,324],[2025,9],[817,325],[815,3],[816,326],[818,327],[826,328],[2027,329],[819,3],[823,330],[2026,9],[820,3],[821,331],[822,332],[2028,9],[824,3],[825,333],[2031,334],[830,335],[828,16],[829,336],[836,337],[2030,9],[833,338],[831,16],[832,339],[834,340],[835,341],[2034,342],[839,3],[843,343],[2033,70],[840,3],[841,344],[842,345],[2035,9],[844,3],[845,346],[849,347],[847,3],[848,3],[2037,9],[855,348],[851,26],[852,3],[854,349],[853,3],[856,350],[2043,351],[857,3],[887,352],[2039,353],[860,354],[858,3],[859,355],[867,356],[866,357],[2038,9],[864,358],[861,3],[862,3],[863,359],[865,360],[886,361],[2040,9],[872,362],[868,3],[869,3],[871,363],[870,26],[873,364],[2041,9],[876,365],[875,366],[874,26],[877,367],[2042,9],[882,368],[878,26],[879,26],[881,369],[880,26],[885,370],[883,3],[884,371],[889,26],[890,3],[891,26],[892,26],[893,3],[894,26],[895,26],[896,3],[897,3],[898,3],[899,3],[900,3],[901,3],[902,26],[903,3],[904,3],[905,26],[906,26],[907,3],[908,26],[909,26],[910,26],[911,26],[912,26],[913,26],[914,3],[915,3],[916,26],[917,26],[918,3],[919,3],[920,3],[921,3],[922,26],[923,26],[924,26],[925,3],[926,3],[927,26],[928,26],[929,26],[930,3],[931,26],[932,3],[933,26],[934,26],[935,26],[936,26],[937,26],[938,26],[939,3],[940,3],[941,3],[942,3],[943,26],[944,26],[945,26],[946,26],[947,26],[948,26],[949,26],[950,26],[951,26],[952,26],[953,26],[954,26],[955,3],[956,3],[957,3],[958,26],[959,26],[960,26],[961,26],[962,3],[963,3],[964,3],[965,3],[966,26],[967,26],[968,26],[969,26],[970,26],[971,26],[972,26],[973,26],[974,26],[975,26],[976,26],[977,26],[978,26],[979,26],[980,26],[981,26],[982,26],[983,26],[984,26],[985,26],[986,26],[987,26],[988,26],[989,26],[990,26],[991,26],[992,26],[993,26],[994,26],[995,26],[996,3],[997,3],[998,26],[999,26],[1000,26],[1001,26],[1002,26],[1003,26],[1004,3],[1005,3],[1006,26],[1007,26],[1008,26],[1009,26],[1010,3],[1011,3],[1012,26],[1013,3],[1014,3],[1015,3],[1016,3],[1017,3],[1018,26],[1019,3],[1020,26],[1021,26],[1022,26],[1023,26],[1024,3],[1025,3],[1026,26],[1027,3],[1028,3],[1029,26],[1030,3],[1031,26],[1032,3],[1033,26],[1034,3],[1035,3],[1036,3],[1037,3],[1038,3],[1039,3],[1040,3],[1041,3],[1042,26],[1043,26],[1044,26],[1045,26],[1046,3],[1047,26],[1048,3],[1049,3],[1050,26],[1051,3],[1052,26],[1053,3],[1054,3],[1055,3],[1056,26],[1057,3],[1058,3],[1059,3],[1060,26],[1061,3],[1062,26],[1063,3],[1064,3],[1065,3],[1066,26],[1067,3],[1068,26],[1069,26],[1070,3],[1071,26],[1072,3],[1073,26],[1074,26],[1075,26],[1076,3],[1077,26],[1078,3],[1079,3],[1080,26],[1081,3],[1082,3],[1083,3],[1084,3],[1085,3],[1086,3],[1087,26],[1088,26],[1089,3],[1090,3],[1091,3],[1092,3],[1093,26],[1094,26],[1095,26],[1096,26],[1097,3],[1098,3],[1099,3],[1100,26],[1101,26],[1102,26],[1103,3],[1104,26],[1105,26],[1106,26],[1107,26],[1108,26],[1109,26],[1110,26],[1111,26],[1112,26],[1113,26],[1114,26],[1115,3],[1116,3],[1117,26],[1118,3],[1119,3],[1120,26],[1121,3],[1122,3],[1123,3],[1124,26],[1125,26],[1126,26],[1127,26],[1128,26],[1129,3],[1130,3],[1131,26],[1132,3],[1133,26],[1134,26],[1135,3],[1136,3],[1137,26],[1138,26],[1139,3],[1140,3],[1141,26],[1142,26],[1143,3],[1144,26],[1145,26],[1146,26],[1147,26],[1148,26],[1149,3],[1150,26],[1151,26],[1152,26],[1153,26],[1154,3],[1155,3],[1156,26],[1157,26],[1158,26],[1159,3],[1160,26],[1161,26],[1162,26],[1163,3],[1164,26],[1165,3],[1166,26],[1167,3],[1168,26],[1169,26],[1170,3],[1171,26],[1172,3],[1173,26],[1174,3],[1175,26],[1176,3],[1177,3],[1178,26],[1179,3],[1180,26],[1181,3],[1182,3],[1183,3],[1184,3],[1185,3],[1186,3],[1187,3],[1188,3],[1189,3],[1190,3],[1191,26],[1192,3],[1193,26],[1194,3],[1195,26],[1196,26],[1197,3],[1198,3],[1199,26],[1200,26],[1201,3],[1202,26],[1203,26],[1204,26],[1205,26],[1206,3],[1207,26],[1208,3],[1209,3],[1210,3],[1211,3],[1212,3],[1213,26],[1214,26],[1215,3],[1216,3],[1217,3],[1218,3],[1219,3],[1220,3],[1221,3],[1222,3],[1223,3],[1224,3],[1225,3],[1226,3],[1227,3],[1228,26],[1229,3],[1230,26],[1231,3],[1232,26],[1233,3],[1234,3],[1235,26],[1236,3],[1237,3],[1238,3],[1239,26],[1240,3],[1241,3],[1242,3],[1243,3],[1244,3],[1245,3],[1246,3],[1247,3],[1248,26],[1249,3],[1250,3],[1251,26],[1252,26],[1253,3],[1254,3],[1255,26],[1256,26],[1257,3],[1258,26],[1259,26],[1260,3],[1261,26],[1262,26],[1263,3],[1264,26],[1265,3],[1266,26],[1267,3],[1268,3],[1269,26],[1270,26],[1271,3],[1272,3],[1273,3],[1274,3],[1275,3],[1276,3],[1277,3],[1278,26],[1279,3],[1280,3],[1281,26],[1282,3],[1283,26],[1284,3],[1285,26],[1286,3],[1287,3],[1288,3],[1289,26],[1290,3],[1291,3],[1292,3],[1293,26],[1294,3],[1295,3],[1296,26],[1297,3],[1298,26],[1299,3],[1300,26],[1301,26],[1302,26],[1303,26],[1304,26],[1305,3],[1306,3],[1307,3],[1308,26],[1309,26],[1310,26],[1311,26],[1312,26],[1313,3],[1314,26],[1315,26],[1316,26],[1317,26],[1318,26],[1319,26],[1320,26],[1321,26],[1322,26],[1323,26],[1324,26],[1325,26],[1326,26],[1327,26],[1328,26],[1329,26],[1330,26],[1331,26],[1332,26],[1333,3],[1334,26],[1335,26],[1336,26],[1337,3],[1338,26],[1339,26],[1340,3],[1341,3],[1342,26],[1343,26],[1344,3],[1345,26],[1346,3],[1347,3],[1348,26],[1349,3],[1350,26],[1351,26],[1352,3],[1353,26],[1354,3],[1355,26],[1356,26],[1358,26],[1359,26],[1357,26],[1360,26],[1361,3],[1362,26],[1363,26],[1364,26],[1365,26],[1366,3],[1367,26],[1368,26],[1369,26],[1370,3],[1371,26],[1372,3],[1373,3],[1374,26],[1375,3],[1376,26],[1938,372],[1377,26],[1378,26],[1379,26],[1380,3],[1381,3],[1382,26],[1383,3],[1384,3],[1385,3],[1386,3],[1387,3],[1388,3],[1389,3],[1390,3],[1391,3],[1392,26],[1393,3],[1394,3],[1395,26],[1396,26],[1397,3],[1398,3],[1399,3],[1400,26],[1401,26],[1402,3],[1403,3],[1404,26],[1405,3],[1406,26],[1407,26],[1408,3],[1409,26],[1410,26],[1411,3],[1412,3],[1413,26],[1414,3],[1415,3],[1416,3],[1417,26],[1418,26],[1419,3],[1420,3],[1421,3],[1422,26],[1423,26],[1424,26],[1425,26],[1426,3],[1427,26],[1428,3],[1429,3],[1430,26],[1431,26],[1432,26],[1433,26],[1434,26],[1435,26],[1436,26],[1437,26],[1438,3],[1439,26],[1440,26],[1441,26],[1442,3],[1443,26],[1444,3],[1445,26],[1446,3],[1447,3],[1448,3],[1449,26],[1450,3],[1451,3],[1452,26],[1453,26],[1454,26],[1455,26],[1456,26],[1457,26],[1458,26],[1459,3],[1460,3],[1461,26],[1462,26],[1463,26],[1464,26],[1465,26],[1466,26],[1467,26],[1468,26],[1469,3],[1470,26],[1471,26],[1472,26],[1473,3],[1474,3],[1475,3],[1476,3],[1477,3],[1478,26],[1479,3],[1480,26],[1481,3],[1482,3],[1483,3],[1484,3],[1485,26],[1486,26],[1487,3],[1488,26],[1489,26],[1490,3],[1491,3],[1492,3],[1493,26],[1494,3],[1495,26],[1496,3],[1497,26],[1498,3],[1499,3],[1500,3],[1501,3],[1502,3],[1503,3],[1504,3],[1505,3],[1506,3],[1507,3],[1508,3],[1509,3],[1510,26],[1511,3],[1512,3],[1513,26],[1514,3],[1515,3],[1516,3],[1517,26],[1518,3],[1519,3],[1520,3],[1521,3],[1522,26],[1523,26],[1524,3],[1525,26],[1526,3],[1527,3],[1528,3],[1529,3],[1530,3],[1531,26],[1532,3],[1533,26],[1534,3],[1535,26],[1536,3],[1537,3],[1538,3],[1539,26],[1540,26],[1541,26],[1542,26],[1543,26],[1544,26],[1545,26],[1546,3],[1547,26],[1548,26],[1549,26],[1550,26],[1551,26],[1552,26],[1553,3],[1554,3],[1555,3],[1556,3],[1557,3],[1558,3],[1559,3],[1560,3],[1561,3],[1562,3],[1563,26],[1564,3],[1565,3],[1566,26],[1567,26],[1568,3],[1569,26],[1570,26],[1571,26],[1572,26],[1573,3],[1574,26],[1575,26],[1576,26],[1577,3],[1578,26],[1579,26],[1580,3],[1581,3],[1582,3],[1583,3],[1584,3],[1585,26],[1586,3],[1587,3],[1588,26],[1589,3],[1590,26],[1591,3],[1592,3],[1593,3],[1594,26],[1595,3],[1596,26],[1597,3],[1598,3],[1599,26],[1600,3],[1601,26],[1602,3],[1603,3],[1604,3],[1605,26],[1606,26],[1607,3],[1608,3],[1609,26],[1610,3],[1611,3],[1612,3],[1613,3],[1614,3],[1615,3],[1616,3],[1617,3],[1618,3],[1619,3],[1620,3],[1621,3],[1622,3],[1623,3],[1624,3],[1625,26],[1626,26],[1627,3],[1628,3],[1629,26],[1630,3],[1631,3],[1632,3],[1633,3],[1634,3],[1635,3],[1636,3],[1637,3],[1638,3],[1639,26],[1640,3],[1641,26],[1642,26],[1643,3],[1644,26],[1645,3],[1646,26],[1647,26],[1648,26],[1649,3],[1650,3],[1651,3],[1652,3],[1653,3],[1654,3],[1655,3],[1656,3],[1657,3],[1658,3],[1659,3],[1660,3],[1661,3],[1662,3],[1663,3],[1664,3],[1665,3],[1666,26],[1667,3],[1668,3],[1669,3],[1670,3],[1671,3],[1672,26],[1673,26],[1674,26],[1675,3],[1676,26],[1677,3],[1678,26],[1679,3],[1680,3],[1681,3],[1682,26],[1683,3],[1684,26],[1685,3],[1686,3],[1687,3],[1688,3],[1689,3],[1690,26],[1691,26],[1692,26],[1693,26],[1694,3],[1695,3],[1696,3],[1697,26],[1698,26],[1699,26],[1700,26],[1701,26],[1702,3],[1703,3],[1704,26],[1705,26],[1706,26],[1707,26],[1708,26],[1709,3],[1710,3],[1711,3],[1712,3],[1713,3],[1714,3],[1715,3],[1716,3],[1717,3],[1718,3],[1719,3],[1720,3],[1721,3],[1722,3],[1723,3],[1724,26],[1725,3],[1726,3],[1727,3],[1728,26],[1729,3],[1730,3],[1731,3],[1732,3],[1733,26],[1734,26],[1735,26],[1736,26],[1737,3],[1738,26],[1739,3],[1740,3],[1741,3],[1742,26],[1743,3],[1744,3],[1745,3],[1746,26],[1747,3],[1748,26],[1749,26],[1750,3],[1751,3],[1752,3],[1753,26],[1754,26],[1755,3],[1756,26],[1757,26],[1758,3],[1759,3],[1760,26],[1761,26],[1762,3],[1763,3],[1764,3],[1765,3],[1766,26],[1767,26],[1768,26],[1769,26],[1770,3],[1771,3],[1772,3],[1773,3],[1774,3],[1775,3],[1776,3],[1777,26],[1778,26],[1779,26],[1780,26],[1781,26],[1782,26],[1783,26],[1784,3],[1785,3],[1786,3],[1787,3],[1788,3],[1789,26],[1790,3],[1791,26],[1792,3],[1793,26],[1794,3],[1795,3],[1796,3],[1797,3],[1798,3],[1799,26],[1800,26],[1801,3],[1802,3],[1803,3],[1804,3],[1805,26],[1806,3],[1807,26],[1808,3],[1809,3],[1810,26],[1811,3],[1812,26],[1813,3],[1814,26],[1815,3],[1816,3],[1817,3],[1818,3],[1819,3],[1820,26],[1821,3],[1822,3],[1823,3],[1824,3],[1825,3],[1826,26],[1827,3],[1828,3],[1829,26],[1830,3],[1831,3],[1832,3],[1833,26],[1834,3],[1835,3],[1836,26],[1837,26],[1838,3],[1839,3],[1840,26],[1841,26],[1842,26],[1843,3],[1844,26],[1845,26],[1846,26],[1847,3],[1848,3],[1849,3],[1850,26],[1851,26],[1852,26],[1853,3],[1854,26],[1855,26],[1856,26],[1857,3],[1858,26],[1859,3],[1860,3],[1861,26],[1862,26],[1863,26],[1864,26],[1865,26],[1866,26],[1867,3],[1868,3],[1869,3],[1870,26],[1871,26],[1872,3],[1873,3],[1874,3],[1875,3],[1876,3],[1877,26],[1878,26],[1879,26],[1880,26],[1881,26],[1882,26],[1883,26],[1884,26],[1885,26],[1886,26],[1887,26],[1888,26],[1889,26],[1890,3],[1891,3],[1892,26],[1893,26],[1894,26],[1895,26],[1896,26],[1897,26],[1898,26],[1899,26],[1900,26],[1901,26],[1902,3],[1903,3],[1904,26],[1905,26],[1906,26],[1907,26],[1908,3],[1909,3],[1910,3],[1911,26],[1912,26],[1913,26],[1914,26],[1915,26],[1916,26],[1917,26],[1918,26],[1919,26],[1920,26],[1921,3],[1922,3],[1923,26],[1924,26],[1925,26],[1926,3],[1927,3],[1928,3],[1929,3],[1930,26],[1931,3],[1932,3],[1933,26],[1934,26],[1935,26],[1936,26],[1937,26],[1941,373],[2044,374],[209,375],[210,3],[211,3],[213,376],[215,377],[216,3],[217,375],[218,378],[208,3],[212,3],[220,379],[221,380],[219,381],[222,3],[223,382],[224,383],[283,384],[225,385],[214,3],[227,386],[226,3],[229,387],[230,388],[231,387],[232,389],[233,387],[234,390],[275,391],[247,392],[235,387],[246,393],[249,394],[248,387],[250,387],[252,395],[251,387],[238,396],[239,397],[237,387],[255,398],[253,399],[254,399],[244,387],[245,400],[256,387],[257,387],[262,401],[258,387],[259,387],[260,387],[261,387],[265,402],[264,403],[263,404],[240,387],[243,405],[241,387],[242,387],[267,406],[266,387],[270,407],[268,387],[269,408],[271,3],[274,409],[272,410],[273,411],[276,412],[228,413],[236,3],[278,414],[277,3],[279,3],[282,415],[280,3],[281,3],[1940,3],[284,16],[285,3],[286,416],[2055,417],[2050,418],[2054,419],[2051,420],[2052,3],[2047,421],[2048,422],[2046,423],[2049,424],[2053,3],[2045,425],[129,426],[130,426],[131,427],[69,428],[132,429],[133,430],[134,431],[67,3],[135,432],[136,433],[137,434],[138,435],[139,436],[140,437],[141,437],[142,438],[143,439],[144,440],[145,441],[70,3],[68,3],[146,442],[147,443],[148,444],[188,445],[149,446],[150,447],[151,446],[152,448],[153,449],[154,450],[155,451],[156,451],[157,451],[158,452],[159,453],[160,454],[161,455],[162,456],[163,457],[164,457],[165,458],[166,3],[167,3],[168,459],[169,460],[170,459],[171,461],[172,462],[173,463],[174,464],[175,465],[176,466],[177,467],[178,468],[179,469],[180,470],[181,471],[182,472],[183,473],[184,474],[185,475],[71,446],[72,3],[73,476],[74,477],[75,3],[76,478],[77,3],[120,479],[121,480],[122,481],[123,481],[124,482],[125,3],[126,429],[127,483],[128,480],[186,484],[187,485],[189,3],[190,486],[66,3],[191,487],[192,488],[205,3],[63,3],[64,3],[12,3],[10,3],[11,3],[16,3],[15,3],[2,3],[17,3],[18,3],[19,3],[20,3],[21,3],[22,3],[23,3],[24,3],[3,3],[25,3],[26,3],[4,3],[27,3],[31,3],[28,3],[29,3],[30,3],[32,3],[33,3],[34,3],[5,3],[35,3],[36,3],[37,3],[38,3],[6,3],[42,3],[39,3],[40,3],[41,3],[43,3],[7,3],[44,3],[49,3],[50,3],[45,3],[46,3],[47,3],[48,3],[8,3],[54,3],[51,3],[52,3],[53,3],[55,3],[9,3],[56,3],[65,3],[57,3],[58,3],[60,3],[59,3],[1,3],[61,3],[62,3],[14,3],[13,3],[96,489],[108,490],[93,491],[109,492],[118,493],[84,494],[85,495],[83,496],[117,497],[112,498],[116,499],[87,500],[105,501],[86,502],[115,503],[81,504],[82,498],[88,505],[89,3],[95,506],[92,505],[79,507],[119,508],[110,509],[99,510],[98,505],[100,511],[103,512],[97,513],[101,514],[113,497],[90,515],[91,516],[104,517],[80,492],[107,518],[106,505],[94,516],[102,519],[111,3],[78,3],[114,520],[2059,3],[2058,521],[193,522],[198,451],[197,523],[202,524],[201,525],[200,526],[2057,527],[203,528],[206,529],[2056,530],[207,531],[2060,3],[2061,3],[2062,3]],"latestChangedDtsFile":"./dist/audio-processor.d.ts","version":"5.9.3"} \ No newline at end of file diff --git a/tsconfig.preload.tsbuildinfo b/tsconfig.preload.tsbuildinfo new file mode 100644 index 0000000..50d2323 --- /dev/null +++ b/tsconfig.preload.tsbuildinfo @@ -0,0 +1 @@ +{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.dom.d.ts","./node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/typescript/lib/lib.dom.asynciterable.d.ts","./node_modules/typescript/lib/lib.webworker.importscripts.d.ts","./node_modules/typescript/lib/lib.scripthost.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/typescript/lib/lib.esnext.disposable.d.ts","./node_modules/typescript/lib/lib.esnext.float16.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/typescript/lib/lib.es2022.full.d.ts","./node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/@types/node/globals.typedarray.d.ts","./node_modules/@types/node/buffer.buffer.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/web-globals/abortcontroller.d.ts","./node_modules/@types/node/web-globals/blob.d.ts","./node_modules/@types/node/web-globals/console.d.ts","./node_modules/@types/node/web-globals/crypto.d.ts","./node_modules/@types/node/web-globals/domexception.d.ts","./node_modules/@types/node/web-globals/encoding.d.ts","./node_modules/@types/node/web-globals/events.d.ts","./node_modules/undici-types/utility.d.ts","./node_modules/undici-types/header.d.ts","./node_modules/undici-types/readable.d.ts","./node_modules/undici-types/fetch.d.ts","./node_modules/undici-types/formdata.d.ts","./node_modules/undici-types/connector.d.ts","./node_modules/undici-types/client-stats.d.ts","./node_modules/undici-types/client.d.ts","./node_modules/undici-types/errors.d.ts","./node_modules/undici-types/dispatcher.d.ts","./node_modules/undici-types/global-dispatcher.d.ts","./node_modules/undici-types/global-origin.d.ts","./node_modules/undici-types/pool-stats.d.ts","./node_modules/undici-types/pool.d.ts","./node_modules/undici-types/handlers.d.ts","./node_modules/undici-types/balanced-pool.d.ts","./node_modules/undici-types/round-robin-pool.d.ts","./node_modules/undici-types/h2c-client.d.ts","./node_modules/undici-types/agent.d.ts","./node_modules/undici-types/mock-interceptor.d.ts","./node_modules/undici-types/mock-call-history.d.ts","./node_modules/undici-types/mock-agent.d.ts","./node_modules/undici-types/mock-client.d.ts","./node_modules/undici-types/mock-pool.d.ts","./node_modules/undici-types/snapshot-agent.d.ts","./node_modules/undici-types/mock-errors.d.ts","./node_modules/undici-types/proxy-agent.d.ts","./node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/undici-types/retry-handler.d.ts","./node_modules/undici-types/retry-agent.d.ts","./node_modules/undici-types/api.d.ts","./node_modules/undici-types/cache-interceptor.d.ts","./node_modules/undici-types/interceptors.d.ts","./node_modules/undici-types/util.d.ts","./node_modules/undici-types/cookies.d.ts","./node_modules/undici-types/patch.d.ts","./node_modules/undici-types/websocket.d.ts","./node_modules/undici-types/eventsource.d.ts","./node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/undici-types/content-type.d.ts","./node_modules/undici-types/cache.d.ts","./node_modules/undici-types/index.d.ts","./node_modules/@types/node/web-globals/fetch.d.ts","./node_modules/@types/node/web-globals/importmeta.d.ts","./node_modules/@types/node/web-globals/messaging.d.ts","./node_modules/@types/node/web-globals/navigator.d.ts","./node_modules/@types/node/web-globals/performance.d.ts","./node_modules/@types/node/web-globals/storage.d.ts","./node_modules/@types/node/web-globals/streams.d.ts","./node_modules/@types/node/web-globals/timers.d.ts","./node_modules/@types/node/web-globals/url.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/assert/strict.d.ts","./node_modules/@types/node/async_hooks.d.ts","./node_modules/@types/node/buffer.d.ts","./node_modules/@types/node/child_process.d.ts","./node_modules/@types/node/cluster.d.ts","./node_modules/@types/node/console.d.ts","./node_modules/@types/node/constants.d.ts","./node_modules/@types/node/crypto.d.ts","./node_modules/@types/node/dgram.d.ts","./node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/@types/node/dns.d.ts","./node_modules/@types/node/dns/promises.d.ts","./node_modules/@types/node/domain.d.ts","./node_modules/@types/node/events.d.ts","./node_modules/@types/node/fs.d.ts","./node_modules/@types/node/fs/promises.d.ts","./node_modules/@types/node/http.d.ts","./node_modules/@types/node/http2.d.ts","./node_modules/@types/node/https.d.ts","./node_modules/@types/node/inspector.d.ts","./node_modules/@types/node/inspector.generated.d.ts","./node_modules/@types/node/inspector/promises.d.ts","./node_modules/@types/node/module.d.ts","./node_modules/@types/node/net.d.ts","./node_modules/@types/node/os.d.ts","./node_modules/@types/node/path.d.ts","./node_modules/@types/node/path/posix.d.ts","./node_modules/@types/node/path/win32.d.ts","./node_modules/@types/node/perf_hooks.d.ts","./node_modules/@types/node/process.d.ts","./node_modules/@types/node/punycode.d.ts","./node_modules/@types/node/querystring.d.ts","./node_modules/@types/node/quic.d.ts","./node_modules/@types/node/readline.d.ts","./node_modules/@types/node/readline/promises.d.ts","./node_modules/@types/node/repl.d.ts","./node_modules/@types/node/sea.d.ts","./node_modules/@types/node/sqlite.d.ts","./node_modules/@types/node/stream.d.ts","./node_modules/@types/node/stream/consumers.d.ts","./node_modules/@types/node/stream/promises.d.ts","./node_modules/@types/node/stream/web.d.ts","./node_modules/@types/node/string_decoder.d.ts","./node_modules/@types/node/test.d.ts","./node_modules/@types/node/test/reporters.d.ts","./node_modules/@types/node/timers.d.ts","./node_modules/@types/node/timers/promises.d.ts","./node_modules/@types/node/tls.d.ts","./node_modules/@types/node/trace_events.d.ts","./node_modules/@types/node/tty.d.ts","./node_modules/@types/node/url.d.ts","./node_modules/@types/node/util.d.ts","./node_modules/@types/node/util/types.d.ts","./node_modules/@types/node/v8.d.ts","./node_modules/@types/node/vm.d.ts","./node_modules/@types/node/wasi.d.ts","./node_modules/@types/node/worker_threads.d.ts","./node_modules/@types/node/zlib.d.ts","./node_modules/@types/node/index.d.ts","./node_modules/electron/electron.d.ts","./dist/shared/types/domain.d.ts","./dist/shared/types/services.d.ts","./dist/shared/types/mouse-trail.d.ts","./dist/shared/types/ipc.d.ts","./src/preload.ts"],"fileIdsList":[[68,131,139,143,146,148,149,150,162],[68,131,139,143,146,148,149,150,162,190,191],[68,131,132,139,143,146,148,149,150,162,189],[68,128,129,131,139,143,146,148,149,150,162],[68,130,131,139,143,146,148,149,150,162],[131,139,143,146,148,149,150,162],[68,131,139,143,146,148,149,150,162,170],[68,131,132,137,139,142,143,146,148,149,150,152,162,167,179],[68,131,132,133,139,142,143,146,148,149,150,162],[68,131,134,139,143,146,148,149,150,162,180],[68,131,135,136,139,143,146,148,149,150,153,162],[68,131,136,139,143,146,148,149,150,162,167,176],[68,131,137,139,142,143,146,148,149,150,152,162],[68,130,131,138,139,143,146,148,149,150,162],[68,131,139,140,143,146,148,149,150,162],[68,131,139,141,142,143,146,148,149,150,162],[68,130,131,139,142,143,146,148,149,150,162],[68,131,139,142,143,144,146,148,149,150,162,167,179],[68,131,139,142,143,144,146,148,149,150,162,167,170],[68,118,131,139,142,143,145,146,148,149,150,152,162,167,179],[68,131,139,142,143,145,146,148,149,150,152,162,167,176,179],[68,131,139,143,145,146,147,148,149,150,162,167,176,179],[66,67,68,69,70,71,72,73,74,75,76,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186],[68,131,139,142,143,146,148,149,150,162],[68,131,139,143,146,148,150,162],[68,131,139,143,146,148,149,150,151,162,179],[68,131,139,142,143,146,148,149,150,152,162,167],[68,131,139,143,146,148,149,150,153,162],[68,131,139,143,146,148,149,150,154,162],[68,131,139,142,143,146,148,149,150,157,162],[68,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186],[68,131,139,143,146,148,149,150,159,162],[68,131,139,143,146,148,149,150,160,162],[68,131,136,139,143,146,148,149,150,152,162,170],[68,131,139,142,143,146,148,149,150,162,163],[68,131,139,143,146,148,149,150,162,164,180,183],[68,131,139,142,143,146,148,149,150,162,167,169,170],[68,131,139,143,146,148,149,150,162,168,170],[68,131,139,143,146,148,149,150,162,170,180],[68,131,139,143,146,148,149,150,162,171],[68,128,131,139,143,146,148,149,150,162,167,173,179],[68,131,139,143,146,148,149,150,162,167,172],[68,131,139,142,143,146,148,149,150,162,174,175],[68,131,139,143,146,148,149,150,162,174,175],[68,131,136,139,143,146,148,149,150,152,162,167,176],[68,131,139,143,146,148,149,150,162,177],[68,131,139,143,146,148,149,150,152,162,178],[68,131,139,143,145,146,148,149,150,160,162,179],[68,131,139,143,146,148,149,150,162,180,181],[68,131,136,139,143,146,148,149,150,162,181],[68,131,139,143,146,148,149,150,162,167,182],[68,131,139,143,146,148,149,150,151,162,183],[68,131,139,143,146,148,149,150,162,184],[68,131,134,139,143,146,148,149,150,162],[68,131,136,139,143,146,148,149,150,162],[68,131,139,143,146,148,149,150,162,180],[68,118,131,139,143,146,148,149,150,162],[68,131,139,143,146,148,149,150,162,179],[68,131,139,143,146,148,149,150,162,185],[68,131,139,143,146,148,149,150,157,162],[68,131,139,143,146,148,149,150,162,175],[68,118,131,139,142,143,144,146,148,149,150,157,162,167,170,179,182,183,185],[68,131,139,143,146,148,149,150,162,167,186],[68,131,139,142,143,146,148,149,150,162,187],[68,83,86,89,90,131,139,143,146,148,149,150,162,179],[68,86,131,139,143,146,148,149,150,162,167,179],[68,86,90,131,139,143,146,148,149,150,162,179],[68,131,139,143,146,148,149,150,162,167],[68,80,131,139,143,146,148,149,150,162],[68,84,131,139,143,146,148,149,150,162],[68,82,83,86,131,139,143,146,148,149,150,162,179],[68,131,139,143,146,148,149,150,152,162,176],[68,131,139,143,146,148,149,150,162,187],[68,80,131,139,143,146,148,149,150,162,187],[68,82,86,131,139,143,146,148,149,150,152,162,179],[68,77,78,79,81,85,131,139,142,143,146,148,149,150,162,167,179],[68,86,95,103,131,139,143,146,148,149,150,162],[68,78,84,131,139,143,146,148,149,150,162],[68,86,112,113,131,139,143,146,148,149,150,162],[68,78,81,86,131,139,143,146,148,149,150,162,170,179,187],[68,86,131,139,143,146,148,149,150,162],[68,82,86,131,139,143,146,148,149,150,162,179],[68,77,131,139,143,146,148,149,150,162],[68,80,81,82,84,85,86,87,88,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,113,114,115,116,117,131,139,143,146,148,149,150,162],[68,86,105,108,131,139,143,146,148,149,150,162],[68,86,95,96,97,131,139,143,146,148,149,150,162],[68,84,86,96,98,131,139,143,146,148,149,150,162],[68,85,131,139,143,146,148,149,150,162],[68,78,80,86,131,139,143,146,148,149,150,162],[68,86,90,96,98,131,139,143,146,148,149,150,162],[68,90,131,139,143,146,148,149,150,162],[68,84,86,89,131,139,143,146,148,149,150,162,179],[68,78,82,86,95,131,139,143,146,148,149,150,162],[68,86,105,131,139,143,146,148,149,150,162],[68,98,131,139,143,146,148,149,150,162],[68,80,86,112,131,139,143,146,148,149,150,162,170,185,187],[68,131,139,143,146,148,149,150,162,179,188,192]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2e80ee7a49e8ac312cc11b77f1475804bee36b3b2bc896bead8b6e1266befb43","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7a3c8b952931daebdfc7a2897c53c0a1c73624593fa070e46bd537e64dcd20a","affectsGlobalScope":true,"impliedFormat":1},{"version":"80e18897e5884b6723488d4f5652167e7bb5024f946743134ecc4aa4ee731f89","affectsGlobalScope":true,"impliedFormat":1},{"version":"cd034f499c6cdca722b60c04b5b1b78e058487a7085a8e0d6fb50809947ee573","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"8cdf8847677ac7d20486e54dd3fcf09eda95812ac8ace44b4418da1bbbab6eb8","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"51ad4c928303041605b4d7ae32e0c1ee387d43a24cd6f1ebf4a2699e1076d4fa","affectsGlobalScope":true,"impliedFormat":1},{"version":"196cb558a13d4533a5163286f30b0509ce0210e4b316c56c38d4c0fd2fb38405","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"3cbad9a1ba4453443026ed38e4b8be018abb26565fa7c944376463ad9df07c41","impliedFormat":1},{"version":"d153a11543fd884b596587ccd97aebbeed950b26933ee000f94009f1ab142848","affectsGlobalScope":true,"impliedFormat":1},{"version":"0ccdaa19852d25ecd84eec365c3bfa16e7859cadecf6e9ca6d0dbbbee439743f","affectsGlobalScope":true,"impliedFormat":1},{"version":"438b41419b1df9f1fbe33b5e1b18f5853432be205991d1b19f5b7f351675541e","affectsGlobalScope":true,"impliedFormat":1},{"version":"096116f8fedc1765d5bd6ef360c257b4a9048e5415054b3bf3c41b07f8951b0b","affectsGlobalScope":true,"impliedFormat":1},{"version":"e5e01375c9e124a83b52ee4b3244ed1a4d214a6cfb54ac73e164a823a4a7860a","affectsGlobalScope":true,"impliedFormat":1},{"version":"f90ae2bbce1505e67f2f6502392e318f5714bae82d2d969185c4a6cecc8af2fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"4b58e207b93a8f1c88bbf2a95ddc686ac83962b13830fe8ad3f404ffc7051fb4","affectsGlobalScope":true,"impliedFormat":1},{"version":"1fefabcb2b06736a66d2904074d56268753654805e829989a46a0161cd8412c5","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"c18a99f01eb788d849ad032b31cafd49de0b19e083fe775370834c5675d7df8e","affectsGlobalScope":true,"impliedFormat":1},{"version":"5247874c2a23b9a62d178ae84f2db6a1d54e6c9a2e7e057e178cc5eea13757fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"cdcf9ea426ad970f96ac930cd176d5c69c6c24eebd9fc580e1572d6c6a88f62c","impliedFormat":1},{"version":"23cd712e2ce083d68afe69224587438e5914b457b8acf87073c22494d706a3d0","impliedFormat":1},{"version":"156a859e21ef3244d13afeeba4e49760a6afa035c149dda52f0c45ea8903b338","impliedFormat":1},{"version":"10ec5e82144dfac6f04fa5d1d6c11763b3e4dbbac6d99101427219ab3e2ae887","impliedFormat":1},{"version":"615754924717c0b1e293e083b83503c0a872717ad5aa60ed7f1a699eb1b4ea5c","impliedFormat":1},{"version":"074de5b2fdead0165a2757e3aaef20f27a6347b1c36adea27d51456795b37682","impliedFormat":1},{"version":"68834d631c8838c715f225509cfc3927913b9cc7a4870460b5b60c8dbdb99baf","impliedFormat":1},{"version":"24371e69a38fc33e268d4a8716dbcda430d6c2c414a99ff9669239c4b8f40dea","impliedFormat":1},{"version":"ccab02f3920fc75c01174c47fcf67882a11daf16baf9e81701d0a94636e94556","impliedFormat":1},{"version":"3e11fce78ad8c0e1d1db4ba5f0652285509be3acdd519529bc8fcef85f7dafd9","impliedFormat":1},{"version":"ea6bc8de8b59f90a7a3960005fd01988f98fd0784e14bc6922dde2e93305ec7d","impliedFormat":1},{"version":"36107995674b29284a115e21a0618c4c2751b32a8766dd4cb3ba740308b16d59","impliedFormat":1},{"version":"914a0ae30d96d71915fc519ccb4efbf2b62c0ddfb3a3fc6129151076bc01dc60","impliedFormat":1},{"version":"9c32412007b5662fd34a8eb04292fb5314ec370d7016d1c2fb8aa193c807fe22","impliedFormat":1},{"version":"7fd1b31fd35876b0aa650811c25ec2c97a3c6387e5473eb18004bed86cdd76b6","impliedFormat":1},{"version":"4d327f7d72ad0918275cea3eee49a6a8dc8114ae1d5b7f3f5d0774de75f7439a","impliedFormat":1},{"version":"6ebe8ebb8659aaa9d1acbf3710d7dae3e923e97610238b9511c25dc39023a166","impliedFormat":1},{"version":"e85d7f8068f6a26710bff0cc8c0fc5e47f71089c3780fbede05857331d2ddec9","impliedFormat":1},{"version":"7befaf0e76b5671be1d47b77fcc65f2b0aad91cc26529df1904f4a7c46d216e9","impliedFormat":1},{"version":"0a60a292b89ca7218b8616f78e5bbd1c96b87e048849469cccb4355e98af959a","impliedFormat":1},{"version":"0b6e25234b4eec6ed96ab138d96eb70b135690d7dd01f3dd8a8ab291c35a683a","impliedFormat":1},{"version":"9666f2f84b985b62400d2e5ab0adae9ff44de9b2a34803c2c5bd3c8325b17dc0","impliedFormat":1},{"version":"40cd35c95e9cf22cfa5bd84e96408b6fcbca55295f4ff822390abb11afbc3dca","impliedFormat":1},{"version":"b1616b8959bf557feb16369c6124a97a0e74ed6f49d1df73bb4b9ddf68acf3f3","impliedFormat":1},{"version":"5b03a034c72146b61573aab280f295b015b9168470f2df05f6080a2122f9b4df","impliedFormat":1},{"version":"40b463c6766ca1b689bfcc46d26b5e295954f32ad43e37ee6953c0a677e4ae2b","impliedFormat":1},{"version":"249b9cab7f5d628b71308c7d9bb0a808b50b091e640ba3ed6e2d0516f4a8d91d","impliedFormat":1},{"version":"80aae6afc67faa5ac0b32b5b8bc8cc9f7fa299cff15cf09cc2e11fd28c6ae29e","impliedFormat":1},{"version":"f473cd2288991ff3221165dcf73cd5d24da30391f87e85b3dd4d0450c787a391","impliedFormat":1},{"version":"499e5b055a5aba1e1998f7311a6c441a369831c70905cc565ceac93c28083d53","impliedFormat":1},{"version":"8aee8b6d4f9f62cf3776cda1305fb18763e2aade7e13cea5bbe699112df85214","impliedFormat":1},{"version":"c63b9ada8c72f95aac5db92aea07e5e87ec810353cdf63b2d78f49a58662cf6c","impliedFormat":1},{"version":"1cc2a09e1a61a5222d4174ab358a9f9de5e906afe79dbf7363d871a7edda3955","impliedFormat":1},{"version":"5d0375ca7310efb77e3ef18d068d53784faf62705e0ad04569597ae0e755c401","impliedFormat":1},{"version":"59af37caec41ecf7b2e76059c9672a49e682c1a2aa6f9d7dc78878f53aa284d6","impliedFormat":1},{"version":"addf417b9eb3f938fddf8d81e96393a165e4be0d4a8b6402292f9c634b1cb00d","impliedFormat":1},{"version":"b64d4d1c5f877f9c666e98e833f0205edb9384acc46e98a1fef344f64d6aba44","impliedFormat":1},{"version":"adf27937dba6af9f08a68c5b1d3fce0ca7d4b960c57e6d6c844e7d1a8e53adae","impliedFormat":1},{"version":"12950411eeab8563b349cb7959543d92d8d02c289ed893d78499a19becb5a8cc","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"c9381908473a1c92cb8c516b184e75f4d226dad95c3a85a5af35f670064d9a2f","impliedFormat":1},{"version":"c3f5289820990ab66b70c7fb5b63cb674001009ff84b13de40619619a9c8175f","affectsGlobalScope":true,"impliedFormat":1},{"version":"b3275d55fac10b799c9546804126239baf020d220136163f763b55a74e50e750","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa68a0a3b7cb32c00e39ee3cd31f8f15b80cac97dce51b6ee7fc14a1e8deb30b","affectsGlobalScope":true,"impliedFormat":1},{"version":"1cf059eaf468efcc649f8cf6075d3cb98e9a35a0fe9c44419ec3d2f5428d7123","affectsGlobalScope":true,"impliedFormat":1},{"version":"6c36e755bced82df7fb6ce8169265d0a7bb046ab4e2cb6d0da0cb72b22033e89","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"7a93de4ff8a63bafe62ba86b89af1df0ccb5e40bb85b0c67d6bbcfdcf96bf3d4","affectsGlobalScope":true,"impliedFormat":1},{"version":"90e85f9bc549dfe2b5749b45fe734144e96cd5d04b38eae244028794e142a77e","affectsGlobalScope":true,"impliedFormat":1},{"version":"e0a5deeb610b2a50a6350bd23df6490036a1773a8a71d70f2f9549ab009e67ee","affectsGlobalScope":true,"impliedFormat":1},{"version":"3fad5618174d74a34ee006406d4eb37e8d07dd62eb1315dbf52f48d31a337547","impliedFormat":1},{"version":"7e49f52a159435fc8df4de9dc377ef5860732ca2dc9efec1640531d3cf5da7a3","impliedFormat":1},{"version":"dd4bde4bdc2e5394aed6855e98cf135dfdf5dd6468cad842e03116d31bbcc9bc","impliedFormat":1},{"version":"4d4e879009a84a47c05350b8dca823036ba3a29a3038efed1be76c9f81e45edf","affectsGlobalScope":true,"impliedFormat":1},{"version":"8b50a819485ffe0d237bf0d131e92178d14d11e2aa873d73615a9ec578b341f5","impliedFormat":1},{"version":"9ba13b47cb450a438e3076c4a3f6afb9dc85e17eae50f26d4b2d72c0688c9251","impliedFormat":1},{"version":"b64cd4401633ea4ecadfd700ddc8323a13b63b106ac7127c1d2726f32424622c","impliedFormat":1},{"version":"37c6e5fe5715814412b43cc9b50b24c67a63c4e04e753e0d1305970d65417a60","impliedFormat":1},{"version":"1d024184fb57c58c5c91823f9d10b4915a4867b7934e89115fd0d861a9df27c8","impliedFormat":1},{"version":"ee0e4946247f842c6dd483cbb60a5e6b484fee07996e3a7bc7343dfb68a04c5d","impliedFormat":1},{"version":"ef051f42b7e0ef5ca04552f54c4552eac84099d64b6c5ad0ef4033574b6035b8","impliedFormat":1},{"version":"853a43154f1d01b0173d9cbd74063507ece57170bad7a3b68f3fa1229ad0a92f","impliedFormat":1},{"version":"56231e3c39a031bfb0afb797690b20ed4537670c93c0318b72d5180833d98b72","impliedFormat":1},{"version":"5cc7c39031bfd8b00ad58f32143d59eb6ffc24f5d41a20931269011dccd36c5e","impliedFormat":1},{"version":"12d602a8fe4c2f2ba4f7804f5eda8ba07e0c83bf5cf0cda8baffa2e9967bfb77","affectsGlobalScope":true,"impliedFormat":1},{"version":"a856ab781967b62b288dfd85b860bef0e62f005ed4b1b8fa25c53ce17856acaf","impliedFormat":1},{"version":"cc25940cfb27aa538e60d465f98bb5068d4d7d33131861ace43f04fe6947d68f","impliedFormat":1},{"version":"8db46b61a690f15b245cf16270db044dc047dce9f93b103a59f50262f677ea1f","impliedFormat":1},{"version":"01ff95aa1443e3f7248974e5a771f513cb2ac158c8898f470a1792f817bee497","impliedFormat":1},{"version":"757227c8b345c57d76f7f0e3bbad7a91ffca23f1b2547cbed9e10025816c9cb7","impliedFormat":1},{"version":"959d0327c96dd9bb5521f3ed6af0c435996504cc8dd46baa8e12cb3b3518cef1","impliedFormat":1},{"version":"e1c1a0b4d1ead0de9eca52203aeb1f771f21e6238d6fcd15aa56ac2a02f1b7bf","impliedFormat":1},{"version":"101f482fd48cb4c7c0468dcc6d62c843d842977aea6235644b1edd05e81fbf22","impliedFormat":1},{"version":"266bee0a41e9c3ba335583e21e9277ae03822402cf5e8e1d99f5196853613b98","affectsGlobalScope":true,"impliedFormat":1},{"version":"386606f8a297988535cb1401959041cfa7f59d54b8a9ed09738e65c98684c976","impliedFormat":1},{"version":"3ef397f12387eff17f550bc484ea7c27d21d43816bbe609d495107f44b97e933","impliedFormat":1},{"version":"1023282e2ba810bc07905d3668349fbd37a26411f0c8f94a70ef3c05fe523fcf","impliedFormat":1},{"version":"b214ebcf76c51b115453f69729ee8aa7b7f8eccdae2a922b568a45c2d7ff52f7","impliedFormat":1},{"version":"429c9cdfa7d126255779efd7e6d9057ced2d69c81859bbab32073bad52e9ba76","impliedFormat":1},{"version":"e236b5eba291f51bdf32c231673e6cab81b5410850e61f51a7a524dddadc0f95","impliedFormat":1},{"version":"ce8653341224f8b45ff46d2a06f2cacb96f841f768a886c9d8dd8ec0878b11bd","affectsGlobalScope":true,"impliedFormat":1},{"version":"7f2c62938251b45715fd2a9887060ec4fbc8724727029d1cbce373747252bdd7","impliedFormat":1},{"version":"e3ace08b6bbd84655d41e244677b474fd995923ffef7149ddb68af8848b60b05","impliedFormat":1},{"version":"132580b0e86c48fab152bab850fc57a4b74fe915c8958d2ccb052b809a44b61c","impliedFormat":1},{"version":"90a278f5fab7557e69e97056c0841adf269c42697194f0bd5c5e69152637d4b3","impliedFormat":1},{"version":"69c9a5a9392e8564bd81116e1ed93b13205201fb44cb35a7fde8c9f9e21c4b23","impliedFormat":1},{"version":"5f8fc37f8434691ffac1bfd8fc2634647da2c0e84253ab5d2dd19a7718915b35","impliedFormat":1},{"version":"5981c2340fd8b076cae8efbae818d42c11ffc615994cb060b1cd390795f1be2b","impliedFormat":1},{"version":"f263485c9ca90df9fe7bb3a906db9701997dc6cae86ace1f8106ac8d2f7f677b","impliedFormat":1},{"version":"1edcf2f36fc332615846bde6dcc71a8fe526065505bc5e3dcfd65a14becdf698","affectsGlobalScope":true,"impliedFormat":1},{"version":"0250da3eb85c99624f974e77ef355cdf86f43980251bc371475c2b397ba55bcd","impliedFormat":1},{"version":"f1c93e046fb3d9b7f8249629f4b63dc068dd839b824dd0aa39a5e68476dc9420","impliedFormat":1},{"version":"3d3a5f27ffbc06c885dd4d5f9ee20de61faf877fe2c3a7051c4825903d9a7fdc","impliedFormat":1},{"version":"12806f9f085598ef930edaf2467a5fa1789a878fba077cd27e85dc5851e11834","impliedFormat":1},{"version":"1dbca38aa4b0db1f4f9e6edacc2780af7e028b733d2a98dd3598cd235ca0c97d","impliedFormat":1},{"version":"a43fe41c33d0a192a0ecaf9b92e87bef3709c9972e6d53c42c49251ccb962d69","impliedFormat":1},{"version":"a177959203c017fad3ecc4f3d96c8757a840957a4959a3ae00dab9d35961ca6c","affectsGlobalScope":true,"impliedFormat":1},{"version":"6fc727ccf9b36e257ff982ea0badeffbfc2c151802f741bddff00c6af3b784cf","impliedFormat":1},{"version":"19143c930aef7ccf248549f3e78992f2f1049118ec5d4622e95025057d8e392b","impliedFormat":1},{"version":"4844a4c9b4b1e812b257676ed8a80b3f3be0e29bf05e742cc2ea9c3c6865e6c6","impliedFormat":1},{"version":"064878a60367e0407c42fb7ba02a2ea4d83257357dc20088e549bd4d89433e9c","impliedFormat":1},{"version":"cca8917838a876e2d7016c9b6af57cbf11fdf903c5fdd8e613fa31840b2957bf","impliedFormat":1},{"version":"d91ae55e4282c22b9c21bc26bd3ef637d3fe132507b10529ae68bf76f5de785b","impliedFormat":1},{"version":"b484ec11ba00e3a2235562a41898d55372ccabe607986c6fa4f4aba72093749f","impliedFormat":1},{"version":"7e8a671604329e178bb479c8f387715ebd40a091fc4a7552a0a75c2f3a21c65c","impliedFormat":1},{"version":"41ef7992c555671a8fe54db302788adefa191ded810a50329b79d20a6772d14c","impliedFormat":1},{"version":"041a7781b9127ab568d2cdcce62c58fdea7c7407f40b8c50045d7866a2727130","impliedFormat":1},{"version":"4c5e90ddbcd177ad3f2ffc909ae217c87820f1e968f6959e4b6ba38a8cec935e","impliedFormat":1},{"version":"b70dd9a44e1ac42f030bb12e7d79117eac7cb74170d72d381a1e7913320af23a","impliedFormat":1},{"version":"55cdbeebe76a1fa18bbd7e7bf73350a2173926bd3085bb050cf5a5397025ee4e","impliedFormat":1},{"version":"94a0fcd4f758fe669bc78f1c9146f5fc07879783787364ca89ce486b0cf37920","affectsGlobalScope":true,"impliedFormat":1},{"version":"54cd69c604e5a7d2e28a6dbdb2422f1d634ee6b037ce84e79924b4310fc50191","impliedFormat":1},{"version":"f9cdeb314ae8e1b58e8d37db1c1a1f00ca62c1790e1881113d41e62dbf12d159","impliedFormat":1},{"version":"ba6aa78375568ff7ba83768f3cf2c237670288a1de47476f27b9c84b5901da1b","impliedFormat":1},{"version":"0b8e33fe92a0c4732985f77b42d1b89c286b473acb47a484fc81d12ba890263a","impliedFormat":1},{"version":"0953018676db128a85de69262b2f9e55b2b6ae92e1d9f6685475a1bbc7304b87","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1}],"root":[193],"options":{"composite":true,"module":199,"noUncheckedIndexedAccess":true,"outDir":"./dist","rootDir":"./src","skipLibCheck":true,"sourceMap":true,"strict":true,"target":9},"referencedMap":[[189,1],[192,2],[191,1],[190,3],[128,4],[129,4],[130,5],[68,6],[131,7],[132,8],[133,9],[66,1],[134,10],[135,11],[136,12],[137,13],[138,14],[139,15],[140,15],[141,16],[142,17],[143,18],[144,19],[69,1],[67,1],[145,20],[146,21],[147,22],[187,23],[148,24],[149,25],[150,24],[151,26],[152,27],[153,28],[154,29],[155,29],[156,29],[157,30],[158,31],[159,32],[160,33],[161,34],[162,35],[163,35],[164,36],[165,1],[166,1],[167,37],[168,38],[169,37],[170,39],[171,40],[172,41],[173,42],[174,43],[175,44],[176,45],[177,46],[178,47],[179,48],[180,49],[181,50],[182,51],[183,52],[184,53],[70,24],[71,1],[72,54],[73,55],[74,1],[75,56],[76,1],[119,57],[120,58],[121,59],[122,59],[123,60],[124,1],[125,7],[126,61],[127,58],[185,62],[186,63],[188,64],[63,1],[64,1],[12,1],[10,1],[11,1],[16,1],[15,1],[2,1],[17,1],[18,1],[19,1],[20,1],[21,1],[22,1],[23,1],[24,1],[3,1],[25,1],[26,1],[4,1],[27,1],[31,1],[28,1],[29,1],[30,1],[32,1],[33,1],[34,1],[5,1],[35,1],[36,1],[37,1],[38,1],[6,1],[42,1],[39,1],[40,1],[41,1],[43,1],[7,1],[44,1],[49,1],[50,1],[45,1],[46,1],[47,1],[48,1],[8,1],[54,1],[51,1],[52,1],[53,1],[55,1],[9,1],[56,1],[65,1],[57,1],[58,1],[60,1],[59,1],[1,1],[61,1],[62,1],[14,1],[13,1],[95,65],[107,66],[92,67],[108,68],[117,69],[83,70],[84,71],[82,72],[116,73],[111,74],[115,75],[86,76],[104,77],[85,78],[114,79],[80,80],[81,74],[87,81],[88,1],[94,82],[91,81],[78,83],[118,84],[109,85],[98,86],[97,81],[99,87],[102,88],[96,89],[100,90],[112,73],[89,91],[90,92],[103,93],[79,68],[106,94],[105,81],[93,92],[101,95],[110,1],[77,1],[113,96],[193,97]],"latestChangedDtsFile":"./dist/preload.d.ts","version":"5.9.3"} \ No newline at end of file diff --git a/tsconfig.renderer.tsbuildinfo b/tsconfig.renderer.tsbuildinfo new file mode 100644 index 0000000..5e0ba86 --- /dev/null +++ b/tsconfig.renderer.tsbuildinfo @@ -0,0 +1 @@ +{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.dom.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/typescript/lib/lib.esnext.disposable.d.ts","./node_modules/typescript/lib/lib.esnext.float16.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./src/renderer/features/transcript/transcript-utils.ts","./dist/shared/types/domain.d.ts","./src/renderer/features/timeline/section-utils.ts","./src/renderer/features/timeline/keyframe-ops.ts","./src/renderer/features/timeline/camera-sync.ts","./src/renderer/features/timeline/overlay-utils.ts","./dist/shared/types/mouse-trail.d.ts","./src/renderer/features/timeline/mouse-trail.ts","./src/renderer/features/media-cleanup.ts","./dist/shared/types/services.d.ts","./src/renderer/app.ts","./dist/shared/types/ipc.d.ts","./src/renderer/globals.d.ts","./node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/@types/node/globals.typedarray.d.ts","./node_modules/@types/node/buffer.buffer.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/web-globals/abortcontroller.d.ts","./node_modules/@types/node/web-globals/blob.d.ts","./node_modules/@types/node/web-globals/console.d.ts","./node_modules/@types/node/web-globals/crypto.d.ts","./node_modules/@types/node/web-globals/domexception.d.ts","./node_modules/@types/node/web-globals/encoding.d.ts","./node_modules/@types/node/web-globals/events.d.ts","./node_modules/undici-types/utility.d.ts","./node_modules/undici-types/header.d.ts","./node_modules/undici-types/readable.d.ts","./node_modules/undici-types/fetch.d.ts","./node_modules/undici-types/formdata.d.ts","./node_modules/undici-types/connector.d.ts","./node_modules/undici-types/client-stats.d.ts","./node_modules/undici-types/client.d.ts","./node_modules/undici-types/errors.d.ts","./node_modules/undici-types/dispatcher.d.ts","./node_modules/undici-types/global-dispatcher.d.ts","./node_modules/undici-types/global-origin.d.ts","./node_modules/undici-types/pool-stats.d.ts","./node_modules/undici-types/pool.d.ts","./node_modules/undici-types/handlers.d.ts","./node_modules/undici-types/balanced-pool.d.ts","./node_modules/undici-types/round-robin-pool.d.ts","./node_modules/undici-types/h2c-client.d.ts","./node_modules/undici-types/agent.d.ts","./node_modules/undici-types/mock-interceptor.d.ts","./node_modules/undici-types/mock-call-history.d.ts","./node_modules/undici-types/mock-agent.d.ts","./node_modules/undici-types/mock-client.d.ts","./node_modules/undici-types/mock-pool.d.ts","./node_modules/undici-types/snapshot-agent.d.ts","./node_modules/undici-types/mock-errors.d.ts","./node_modules/undici-types/proxy-agent.d.ts","./node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/undici-types/retry-handler.d.ts","./node_modules/undici-types/retry-agent.d.ts","./node_modules/undici-types/api.d.ts","./node_modules/undici-types/cache-interceptor.d.ts","./node_modules/undici-types/interceptors.d.ts","./node_modules/undici-types/util.d.ts","./node_modules/undici-types/cookies.d.ts","./node_modules/undici-types/patch.d.ts","./node_modules/undici-types/websocket.d.ts","./node_modules/undici-types/eventsource.d.ts","./node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/undici-types/content-type.d.ts","./node_modules/undici-types/cache.d.ts","./node_modules/undici-types/index.d.ts","./node_modules/@types/node/web-globals/fetch.d.ts","./node_modules/@types/node/web-globals/importmeta.d.ts","./node_modules/@types/node/web-globals/messaging.d.ts","./node_modules/@types/node/web-globals/navigator.d.ts","./node_modules/@types/node/web-globals/performance.d.ts","./node_modules/@types/node/web-globals/storage.d.ts","./node_modules/@types/node/web-globals/streams.d.ts","./node_modules/@types/node/web-globals/timers.d.ts","./node_modules/@types/node/web-globals/url.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/assert/strict.d.ts","./node_modules/@types/node/async_hooks.d.ts","./node_modules/@types/node/buffer.d.ts","./node_modules/@types/node/child_process.d.ts","./node_modules/@types/node/cluster.d.ts","./node_modules/@types/node/console.d.ts","./node_modules/@types/node/constants.d.ts","./node_modules/@types/node/crypto.d.ts","./node_modules/@types/node/dgram.d.ts","./node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/@types/node/dns.d.ts","./node_modules/@types/node/dns/promises.d.ts","./node_modules/@types/node/domain.d.ts","./node_modules/@types/node/events.d.ts","./node_modules/@types/node/fs.d.ts","./node_modules/@types/node/fs/promises.d.ts","./node_modules/@types/node/http.d.ts","./node_modules/@types/node/http2.d.ts","./node_modules/@types/node/https.d.ts","./node_modules/@types/node/inspector.d.ts","./node_modules/@types/node/inspector.generated.d.ts","./node_modules/@types/node/inspector/promises.d.ts","./node_modules/@types/node/module.d.ts","./node_modules/@types/node/net.d.ts","./node_modules/@types/node/os.d.ts","./node_modules/@types/node/path.d.ts","./node_modules/@types/node/path/posix.d.ts","./node_modules/@types/node/path/win32.d.ts","./node_modules/@types/node/perf_hooks.d.ts","./node_modules/@types/node/process.d.ts","./node_modules/@types/node/punycode.d.ts","./node_modules/@types/node/querystring.d.ts","./node_modules/@types/node/quic.d.ts","./node_modules/@types/node/readline.d.ts","./node_modules/@types/node/readline/promises.d.ts","./node_modules/@types/node/repl.d.ts","./node_modules/@types/node/sea.d.ts","./node_modules/@types/node/sqlite.d.ts","./node_modules/@types/node/stream.d.ts","./node_modules/@types/node/stream/consumers.d.ts","./node_modules/@types/node/stream/promises.d.ts","./node_modules/@types/node/stream/web.d.ts","./node_modules/@types/node/string_decoder.d.ts","./node_modules/@types/node/test.d.ts","./node_modules/@types/node/test/reporters.d.ts","./node_modules/@types/node/timers.d.ts","./node_modules/@types/node/timers/promises.d.ts","./node_modules/@types/node/tls.d.ts","./node_modules/@types/node/trace_events.d.ts","./node_modules/@types/node/tty.d.ts","./node_modules/@types/node/url.d.ts","./node_modules/@types/node/util.d.ts","./node_modules/@types/node/util/types.d.ts","./node_modules/@types/node/v8.d.ts","./node_modules/@types/node/vm.d.ts","./node_modules/@types/node/wasi.d.ts","./node_modules/@types/node/worker_threads.d.ts","./node_modules/@types/node/zlib.d.ts","./node_modules/@types/node/index.d.ts","./node_modules/keyv/src/index.d.ts","./node_modules/@types/http-cache-semantics/index.d.ts","./node_modules/@types/responselike/index.d.ts","./node_modules/@types/cacheable-request/index.d.ts","./node_modules/@types/deep-eql/index.d.ts","./node_modules/assertion-error/index.d.ts","./node_modules/@types/chai/index.d.ts","./node_modules/@types/esrecurse/index.d.ts","./node_modules/@types/estree/index.d.ts","./node_modules/@types/json-schema/index.d.ts","./node_modules/@types/keyv/index.d.ts","./node_modules/form-data/index.d.ts","./node_modules/@types/node-fetch/externals.d.ts","./node_modules/@types/node-fetch/index.d.ts","./node_modules/@types/yauzl/index.d.ts"],"fileIdsList":[[76,139,147,151,154,156,157,158,170],[67,70,76,139,147,151,154,156,157,158,170],[62,76,139,140,147,151,154,156,157,158,170],[76,139,147,150,151,153,154,156,157,158,170,187,195,196,197,198],[76,139,147,151,154,156,157,158,170,200,201],[76,139,147,150,151,154,156,157,158,170,195],[76,139,147,151,153,154,156,157,158,170,187,195,207,208],[76,136,137,139,147,151,154,156,157,158,170],[76,138,139,147,151,154,156,157,158,170],[139,147,151,154,156,157,158,170],[76,139,147,151,154,156,157,158,170,178],[76,139,140,145,147,150,151,154,156,157,158,160,170,175,187],[76,139,140,141,147,150,151,154,156,157,158,170],[76,139,142,147,151,154,156,157,158,170,188],[76,139,143,144,147,151,154,156,157,158,161,170],[76,139,144,147,151,154,156,157,158,170,175,184],[76,139,145,147,150,151,154,156,157,158,160,170],[76,138,139,146,147,151,154,156,157,158,170],[76,139,147,148,151,154,156,157,158,170],[76,139,147,149,150,151,154,156,157,158,170],[76,138,139,147,150,151,154,156,157,158,170],[76,139,147,150,151,152,154,156,157,158,170,175,187],[76,139,147,150,151,152,154,156,157,158,170,175,178],[76,126,139,147,150,151,153,154,156,157,158,160,170,175,187],[76,139,147,150,151,153,154,156,157,158,160,170,175,184,187],[76,139,147,151,153,154,155,156,157,158,170,175,184,187],[74,75,76,77,78,79,80,81,82,83,84,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194],[76,139,147,150,151,154,156,157,158,170],[76,139,147,151,154,156,158,170],[76,139,147,151,154,156,157,158,159,170,187],[76,139,147,150,151,154,156,157,158,160,170,175],[76,139,147,151,154,156,157,158,161,170],[76,139,147,151,154,156,157,158,162,170],[76,139,147,150,151,154,156,157,158,165,170],[76,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194],[76,139,147,151,154,156,157,158,167,170],[76,139,147,151,154,156,157,158,168,170],[76,139,144,147,151,154,156,157,158,160,170,178],[76,139,147,150,151,154,156,157,158,170,171],[76,139,147,151,154,156,157,158,170,172,188,191],[76,139,147,150,151,154,156,157,158,170,175,177,178],[76,139,147,151,154,156,157,158,170,176,178],[76,139,147,151,154,156,157,158,170,178,188],[76,139,147,151,154,156,157,158,170,179],[76,136,139,147,151,154,156,157,158,170,175,181,187],[76,139,147,151,154,156,157,158,170,175,180],[76,139,147,150,151,154,156,157,158,170,182,183],[76,139,147,151,154,156,157,158,170,182,183],[76,139,144,147,151,154,156,157,158,160,170,175,184],[76,139,147,151,154,156,157,158,170,185],[76,139,147,151,154,156,157,158,160,170,186],[76,139,147,151,153,154,156,157,158,168,170,187],[76,139,147,151,154,156,157,158,170,188,189],[76,139,144,147,151,154,156,157,158,170,189],[76,139,147,151,154,156,157,158,170,175,190],[76,139,147,151,154,156,157,158,159,170,191],[76,139,147,151,154,156,157,158,170,192],[76,139,142,147,151,154,156,157,158,170],[76,139,144,147,151,154,156,157,158,170],[76,139,147,151,154,156,157,158,170,188],[76,126,139,147,151,154,156,157,158,170],[76,139,147,151,154,156,157,158,170,187],[76,139,147,151,154,156,157,158,170,193],[76,139,147,151,154,156,157,158,165,170],[76,139,147,151,154,156,157,158,170,183],[76,126,139,147,150,151,152,154,156,157,158,165,170,175,178,187,190,191,193],[76,139,147,151,154,156,157,158,170,175,194],[76,139,147,151,153,154,156,157,158,170,175,195],[76,139,147,150,151,154,156,157,158,170,175,195],[76,91,94,97,98,139,147,151,154,156,157,158,170,187],[76,94,139,147,151,154,156,157,158,170,175,187],[76,94,98,139,147,151,154,156,157,158,170,187],[76,139,147,151,154,156,157,158,170,175],[76,88,139,147,151,154,156,157,158,170],[76,92,139,147,151,154,156,157,158,170],[76,90,91,94,139,147,151,154,156,157,158,170,187],[76,139,147,151,154,156,157,158,160,170,184],[76,139,147,151,154,156,157,158,170,195],[76,88,139,147,151,154,156,157,158,170,195],[76,90,94,139,147,151,154,156,157,158,160,170,187],[76,85,86,87,89,93,139,147,150,151,154,156,157,158,170,175,187],[76,94,103,111,139,147,151,154,156,157,158,170],[76,86,92,139,147,151,154,156,157,158,170],[76,94,120,121,139,147,151,154,156,157,158,170],[76,86,89,94,139,147,151,154,156,157,158,170,178,187,195],[76,94,139,147,151,154,156,157,158,170],[76,90,94,139,147,151,154,156,157,158,170,187],[76,85,139,147,151,154,156,157,158,170],[76,88,89,90,92,93,94,95,96,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,121,122,123,124,125,139,147,151,154,156,157,158,170],[76,94,113,116,139,147,151,154,156,157,158,170],[76,94,103,104,105,139,147,151,154,156,157,158,170],[76,92,94,104,106,139,147,151,154,156,157,158,170],[76,93,139,147,151,154,156,157,158,170],[76,86,88,94,139,147,151,154,156,157,158,170],[76,94,98,104,106,139,147,151,154,156,157,158,170],[76,98,139,147,151,154,156,157,158,170],[76,92,94,97,139,147,151,154,156,157,158,170,187],[76,86,90,94,103,139,147,151,154,156,157,158,170],[76,94,113,139,147,151,154,156,157,158,170],[76,106,139,147,151,154,156,157,158,170],[76,88,94,120,139,147,151,154,156,157,158,170,178,193,195],[61,62,63,64,65,66,67,68,69,70,76,139,147,151,154,156,157,158,170],[62,76,139,147,151,154,156,157,158,170],[67,76,139,147,151,154,156,157,158,170],[61,62,76,139,147,151,154,156,157,158,170],[72,76,139,147,151,154,156,157,158,170]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"8cdf8847677ac7d20486e54dd3fcf09eda95812ac8ace44b4418da1bbbab6eb8","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"51ad4c928303041605b4d7ae32e0c1ee387d43a24cd6f1ebf4a2699e1076d4fa","affectsGlobalScope":true,"impliedFormat":1},{"version":"196cb558a13d4533a5163286f30b0509ce0210e4b316c56c38d4c0fd2fb38405","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"23ba02ca3a43fd1b9093ba9c339056d1a45682862cc607ddbbbcfed2b4112b7e","signature":"a8f5eb30166a8b72b5ca7cf417d05306c9bd81cf24101e0390c955b4bf826f1e"},"54cd69c604e5a7d2e28a6dbdb2422f1d634ee6b037ce84e79924b4310fc50191",{"version":"f50ea7bb076cf7ad2a2510e00db464a3964d422714cb39c352a2ca02ebe30b09","signature":"d5e3b503bae7675365bc9fe7b65385e30552e5c11f1354703d993213433bb05d"},{"version":"736d1b0fe288f1e23713fae7ee1d2f037f605a5e774a9a120fde164b52b44085","signature":"60f66750e92ac6b87c962da5d32cdf19afa6a2451fb772e57fa79c6b8ad576d1"},{"version":"0a95bafab6d8e9b66816308cf0f9430d655a6e7c6c2aaf4c8d050bf25cfd0630","signature":"ec8c9a03bb6fd1c410d1febf0337fadc3f3f032324ad610fe11201cc35cbe325"},{"version":"20fb4bc622037b5dcedc1f8ed84d8be5ca4797bda120339eb2aacc90acc335a5","signature":"937ab116bc96b08acf8a1cf11f9ce4131f894568ef40e7aef8787346303d0b60"},"ba6aa78375568ff7ba83768f3cf2c237670288a1de47476f27b9c84b5901da1b",{"version":"14e05483004de14159a28588a71f5304a84353d2b1144bb31df46bbac9e4df0e","signature":"f946f494800b1aab607738dad27be8706bfc5b91454d3f1b6464c40a017d2910"},{"version":"c0489b3698936d94d2bddf728b59c15ee74eba589781750c755ae9b76dac2ff0","signature":"e1944e700598aaaa11e4d3f8a27a5717a057def6565dc6459c53571f37aa0c1b"},"f9cdeb314ae8e1b58e8d37db1c1a1f00ca62c1790e1881113d41e62dbf12d159",{"version":"eabab637410c432394b39e8e620333ae938e41d0674040b4869b6d2ae1edb3da","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"0b8e33fe92a0c4732985f77b42d1b89c286b473acb47a484fc81d12ba890263a",{"version":"f3fd18af90430ef7c6e905b81ffba7571f82014fd6739473bd6d2450ba287b2e","affectsGlobalScope":true},{"version":"d153a11543fd884b596587ccd97aebbeed950b26933ee000f94009f1ab142848","affectsGlobalScope":true,"impliedFormat":1},{"version":"0ccdaa19852d25ecd84eec365c3bfa16e7859cadecf6e9ca6d0dbbbee439743f","affectsGlobalScope":true,"impliedFormat":1},{"version":"438b41419b1df9f1fbe33b5e1b18f5853432be205991d1b19f5b7f351675541e","affectsGlobalScope":true,"impliedFormat":1},{"version":"096116f8fedc1765d5bd6ef360c257b4a9048e5415054b3bf3c41b07f8951b0b","affectsGlobalScope":true,"impliedFormat":1},{"version":"e5e01375c9e124a83b52ee4b3244ed1a4d214a6cfb54ac73e164a823a4a7860a","affectsGlobalScope":true,"impliedFormat":1},{"version":"f90ae2bbce1505e67f2f6502392e318f5714bae82d2d969185c4a6cecc8af2fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"4b58e207b93a8f1c88bbf2a95ddc686ac83962b13830fe8ad3f404ffc7051fb4","affectsGlobalScope":true,"impliedFormat":1},{"version":"1fefabcb2b06736a66d2904074d56268753654805e829989a46a0161cd8412c5","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"c18a99f01eb788d849ad032b31cafd49de0b19e083fe775370834c5675d7df8e","affectsGlobalScope":true,"impliedFormat":1},{"version":"5247874c2a23b9a62d178ae84f2db6a1d54e6c9a2e7e057e178cc5eea13757fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"cdcf9ea426ad970f96ac930cd176d5c69c6c24eebd9fc580e1572d6c6a88f62c","impliedFormat":1},{"version":"23cd712e2ce083d68afe69224587438e5914b457b8acf87073c22494d706a3d0","impliedFormat":1},{"version":"156a859e21ef3244d13afeeba4e49760a6afa035c149dda52f0c45ea8903b338","impliedFormat":1},{"version":"10ec5e82144dfac6f04fa5d1d6c11763b3e4dbbac6d99101427219ab3e2ae887","impliedFormat":1},{"version":"615754924717c0b1e293e083b83503c0a872717ad5aa60ed7f1a699eb1b4ea5c","impliedFormat":1},{"version":"074de5b2fdead0165a2757e3aaef20f27a6347b1c36adea27d51456795b37682","impliedFormat":1},{"version":"68834d631c8838c715f225509cfc3927913b9cc7a4870460b5b60c8dbdb99baf","impliedFormat":1},{"version":"24371e69a38fc33e268d4a8716dbcda430d6c2c414a99ff9669239c4b8f40dea","impliedFormat":1},{"version":"ccab02f3920fc75c01174c47fcf67882a11daf16baf9e81701d0a94636e94556","impliedFormat":1},{"version":"3e11fce78ad8c0e1d1db4ba5f0652285509be3acdd519529bc8fcef85f7dafd9","impliedFormat":1},{"version":"ea6bc8de8b59f90a7a3960005fd01988f98fd0784e14bc6922dde2e93305ec7d","impliedFormat":1},{"version":"36107995674b29284a115e21a0618c4c2751b32a8766dd4cb3ba740308b16d59","impliedFormat":1},{"version":"914a0ae30d96d71915fc519ccb4efbf2b62c0ddfb3a3fc6129151076bc01dc60","impliedFormat":1},{"version":"9c32412007b5662fd34a8eb04292fb5314ec370d7016d1c2fb8aa193c807fe22","impliedFormat":1},{"version":"7fd1b31fd35876b0aa650811c25ec2c97a3c6387e5473eb18004bed86cdd76b6","impliedFormat":1},{"version":"4d327f7d72ad0918275cea3eee49a6a8dc8114ae1d5b7f3f5d0774de75f7439a","impliedFormat":1},{"version":"6ebe8ebb8659aaa9d1acbf3710d7dae3e923e97610238b9511c25dc39023a166","impliedFormat":1},{"version":"e85d7f8068f6a26710bff0cc8c0fc5e47f71089c3780fbede05857331d2ddec9","impliedFormat":1},{"version":"7befaf0e76b5671be1d47b77fcc65f2b0aad91cc26529df1904f4a7c46d216e9","impliedFormat":1},{"version":"0a60a292b89ca7218b8616f78e5bbd1c96b87e048849469cccb4355e98af959a","impliedFormat":1},{"version":"0b6e25234b4eec6ed96ab138d96eb70b135690d7dd01f3dd8a8ab291c35a683a","impliedFormat":1},{"version":"9666f2f84b985b62400d2e5ab0adae9ff44de9b2a34803c2c5bd3c8325b17dc0","impliedFormat":1},{"version":"40cd35c95e9cf22cfa5bd84e96408b6fcbca55295f4ff822390abb11afbc3dca","impliedFormat":1},{"version":"b1616b8959bf557feb16369c6124a97a0e74ed6f49d1df73bb4b9ddf68acf3f3","impliedFormat":1},{"version":"5b03a034c72146b61573aab280f295b015b9168470f2df05f6080a2122f9b4df","impliedFormat":1},{"version":"40b463c6766ca1b689bfcc46d26b5e295954f32ad43e37ee6953c0a677e4ae2b","impliedFormat":1},{"version":"249b9cab7f5d628b71308c7d9bb0a808b50b091e640ba3ed6e2d0516f4a8d91d","impliedFormat":1},{"version":"80aae6afc67faa5ac0b32b5b8bc8cc9f7fa299cff15cf09cc2e11fd28c6ae29e","impliedFormat":1},{"version":"f473cd2288991ff3221165dcf73cd5d24da30391f87e85b3dd4d0450c787a391","impliedFormat":1},{"version":"499e5b055a5aba1e1998f7311a6c441a369831c70905cc565ceac93c28083d53","impliedFormat":1},{"version":"8aee8b6d4f9f62cf3776cda1305fb18763e2aade7e13cea5bbe699112df85214","impliedFormat":1},{"version":"c63b9ada8c72f95aac5db92aea07e5e87ec810353cdf63b2d78f49a58662cf6c","impliedFormat":1},{"version":"1cc2a09e1a61a5222d4174ab358a9f9de5e906afe79dbf7363d871a7edda3955","impliedFormat":1},{"version":"5d0375ca7310efb77e3ef18d068d53784faf62705e0ad04569597ae0e755c401","impliedFormat":1},{"version":"59af37caec41ecf7b2e76059c9672a49e682c1a2aa6f9d7dc78878f53aa284d6","impliedFormat":1},{"version":"addf417b9eb3f938fddf8d81e96393a165e4be0d4a8b6402292f9c634b1cb00d","impliedFormat":1},{"version":"b64d4d1c5f877f9c666e98e833f0205edb9384acc46e98a1fef344f64d6aba44","impliedFormat":1},{"version":"adf27937dba6af9f08a68c5b1d3fce0ca7d4b960c57e6d6c844e7d1a8e53adae","impliedFormat":1},{"version":"12950411eeab8563b349cb7959543d92d8d02c289ed893d78499a19becb5a8cc","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"c9381908473a1c92cb8c516b184e75f4d226dad95c3a85a5af35f670064d9a2f","impliedFormat":1},{"version":"c3f5289820990ab66b70c7fb5b63cb674001009ff84b13de40619619a9c8175f","affectsGlobalScope":true,"impliedFormat":1},{"version":"b3275d55fac10b799c9546804126239baf020d220136163f763b55a74e50e750","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa68a0a3b7cb32c00e39ee3cd31f8f15b80cac97dce51b6ee7fc14a1e8deb30b","affectsGlobalScope":true,"impliedFormat":1},{"version":"1cf059eaf468efcc649f8cf6075d3cb98e9a35a0fe9c44419ec3d2f5428d7123","affectsGlobalScope":true,"impliedFormat":1},{"version":"6c36e755bced82df7fb6ce8169265d0a7bb046ab4e2cb6d0da0cb72b22033e89","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"7a93de4ff8a63bafe62ba86b89af1df0ccb5e40bb85b0c67d6bbcfdcf96bf3d4","affectsGlobalScope":true,"impliedFormat":1},{"version":"90e85f9bc549dfe2b5749b45fe734144e96cd5d04b38eae244028794e142a77e","affectsGlobalScope":true,"impliedFormat":1},{"version":"e0a5deeb610b2a50a6350bd23df6490036a1773a8a71d70f2f9549ab009e67ee","affectsGlobalScope":true,"impliedFormat":1},{"version":"3fad5618174d74a34ee006406d4eb37e8d07dd62eb1315dbf52f48d31a337547","impliedFormat":1},{"version":"7e49f52a159435fc8df4de9dc377ef5860732ca2dc9efec1640531d3cf5da7a3","impliedFormat":1},{"version":"dd4bde4bdc2e5394aed6855e98cf135dfdf5dd6468cad842e03116d31bbcc9bc","impliedFormat":1},{"version":"4d4e879009a84a47c05350b8dca823036ba3a29a3038efed1be76c9f81e45edf","affectsGlobalScope":true,"impliedFormat":1},{"version":"8b50a819485ffe0d237bf0d131e92178d14d11e2aa873d73615a9ec578b341f5","impliedFormat":1},{"version":"9ba13b47cb450a438e3076c4a3f6afb9dc85e17eae50f26d4b2d72c0688c9251","impliedFormat":1},{"version":"b64cd4401633ea4ecadfd700ddc8323a13b63b106ac7127c1d2726f32424622c","impliedFormat":1},{"version":"37c6e5fe5715814412b43cc9b50b24c67a63c4e04e753e0d1305970d65417a60","impliedFormat":1},{"version":"1d024184fb57c58c5c91823f9d10b4915a4867b7934e89115fd0d861a9df27c8","impliedFormat":1},{"version":"ee0e4946247f842c6dd483cbb60a5e6b484fee07996e3a7bc7343dfb68a04c5d","impliedFormat":1},{"version":"ef051f42b7e0ef5ca04552f54c4552eac84099d64b6c5ad0ef4033574b6035b8","impliedFormat":1},{"version":"853a43154f1d01b0173d9cbd74063507ece57170bad7a3b68f3fa1229ad0a92f","impliedFormat":1},{"version":"56231e3c39a031bfb0afb797690b20ed4537670c93c0318b72d5180833d98b72","impliedFormat":1},{"version":"5cc7c39031bfd8b00ad58f32143d59eb6ffc24f5d41a20931269011dccd36c5e","impliedFormat":1},{"version":"12d602a8fe4c2f2ba4f7804f5eda8ba07e0c83bf5cf0cda8baffa2e9967bfb77","affectsGlobalScope":true,"impliedFormat":1},{"version":"a856ab781967b62b288dfd85b860bef0e62f005ed4b1b8fa25c53ce17856acaf","impliedFormat":1},{"version":"cc25940cfb27aa538e60d465f98bb5068d4d7d33131861ace43f04fe6947d68f","impliedFormat":1},{"version":"8db46b61a690f15b245cf16270db044dc047dce9f93b103a59f50262f677ea1f","impliedFormat":1},{"version":"01ff95aa1443e3f7248974e5a771f513cb2ac158c8898f470a1792f817bee497","impliedFormat":1},{"version":"757227c8b345c57d76f7f0e3bbad7a91ffca23f1b2547cbed9e10025816c9cb7","impliedFormat":1},{"version":"959d0327c96dd9bb5521f3ed6af0c435996504cc8dd46baa8e12cb3b3518cef1","impliedFormat":1},{"version":"e1c1a0b4d1ead0de9eca52203aeb1f771f21e6238d6fcd15aa56ac2a02f1b7bf","impliedFormat":1},{"version":"101f482fd48cb4c7c0468dcc6d62c843d842977aea6235644b1edd05e81fbf22","impliedFormat":1},{"version":"266bee0a41e9c3ba335583e21e9277ae03822402cf5e8e1d99f5196853613b98","affectsGlobalScope":true,"impliedFormat":1},{"version":"386606f8a297988535cb1401959041cfa7f59d54b8a9ed09738e65c98684c976","impliedFormat":1},{"version":"3ef397f12387eff17f550bc484ea7c27d21d43816bbe609d495107f44b97e933","impliedFormat":1},{"version":"1023282e2ba810bc07905d3668349fbd37a26411f0c8f94a70ef3c05fe523fcf","impliedFormat":1},{"version":"b214ebcf76c51b115453f69729ee8aa7b7f8eccdae2a922b568a45c2d7ff52f7","impliedFormat":1},{"version":"429c9cdfa7d126255779efd7e6d9057ced2d69c81859bbab32073bad52e9ba76","impliedFormat":1},{"version":"e236b5eba291f51bdf32c231673e6cab81b5410850e61f51a7a524dddadc0f95","impliedFormat":1},{"version":"ce8653341224f8b45ff46d2a06f2cacb96f841f768a886c9d8dd8ec0878b11bd","affectsGlobalScope":true,"impliedFormat":1},{"version":"7f2c62938251b45715fd2a9887060ec4fbc8724727029d1cbce373747252bdd7","impliedFormat":1},{"version":"e3ace08b6bbd84655d41e244677b474fd995923ffef7149ddb68af8848b60b05","impliedFormat":1},{"version":"132580b0e86c48fab152bab850fc57a4b74fe915c8958d2ccb052b809a44b61c","impliedFormat":1},{"version":"90a278f5fab7557e69e97056c0841adf269c42697194f0bd5c5e69152637d4b3","impliedFormat":1},{"version":"69c9a5a9392e8564bd81116e1ed93b13205201fb44cb35a7fde8c9f9e21c4b23","impliedFormat":1},{"version":"5f8fc37f8434691ffac1bfd8fc2634647da2c0e84253ab5d2dd19a7718915b35","impliedFormat":1},{"version":"5981c2340fd8b076cae8efbae818d42c11ffc615994cb060b1cd390795f1be2b","impliedFormat":1},{"version":"f263485c9ca90df9fe7bb3a906db9701997dc6cae86ace1f8106ac8d2f7f677b","impliedFormat":1},{"version":"1edcf2f36fc332615846bde6dcc71a8fe526065505bc5e3dcfd65a14becdf698","affectsGlobalScope":true,"impliedFormat":1},{"version":"0250da3eb85c99624f974e77ef355cdf86f43980251bc371475c2b397ba55bcd","impliedFormat":1},{"version":"f1c93e046fb3d9b7f8249629f4b63dc068dd839b824dd0aa39a5e68476dc9420","impliedFormat":1},{"version":"3d3a5f27ffbc06c885dd4d5f9ee20de61faf877fe2c3a7051c4825903d9a7fdc","impliedFormat":1},{"version":"12806f9f085598ef930edaf2467a5fa1789a878fba077cd27e85dc5851e11834","impliedFormat":1},{"version":"1dbca38aa4b0db1f4f9e6edacc2780af7e028b733d2a98dd3598cd235ca0c97d","impliedFormat":1},{"version":"a43fe41c33d0a192a0ecaf9b92e87bef3709c9972e6d53c42c49251ccb962d69","impliedFormat":1},{"version":"a177959203c017fad3ecc4f3d96c8757a840957a4959a3ae00dab9d35961ca6c","affectsGlobalScope":true,"impliedFormat":1},{"version":"6fc727ccf9b36e257ff982ea0badeffbfc2c151802f741bddff00c6af3b784cf","impliedFormat":1},{"version":"19143c930aef7ccf248549f3e78992f2f1049118ec5d4622e95025057d8e392b","impliedFormat":1},{"version":"4844a4c9b4b1e812b257676ed8a80b3f3be0e29bf05e742cc2ea9c3c6865e6c6","impliedFormat":1},{"version":"064878a60367e0407c42fb7ba02a2ea4d83257357dc20088e549bd4d89433e9c","impliedFormat":1},{"version":"cca8917838a876e2d7016c9b6af57cbf11fdf903c5fdd8e613fa31840b2957bf","impliedFormat":1},{"version":"d91ae55e4282c22b9c21bc26bd3ef637d3fe132507b10529ae68bf76f5de785b","impliedFormat":1},{"version":"b484ec11ba00e3a2235562a41898d55372ccabe607986c6fa4f4aba72093749f","impliedFormat":1},{"version":"7e8a671604329e178bb479c8f387715ebd40a091fc4a7552a0a75c2f3a21c65c","impliedFormat":1},{"version":"41ef7992c555671a8fe54db302788adefa191ded810a50329b79d20a6772d14c","impliedFormat":1},{"version":"041a7781b9127ab568d2cdcce62c58fdea7c7407f40b8c50045d7866a2727130","impliedFormat":1},{"version":"4c5e90ddbcd177ad3f2ffc909ae217c87820f1e968f6959e4b6ba38a8cec935e","impliedFormat":1},{"version":"b70dd9a44e1ac42f030bb12e7d79117eac7cb74170d72d381a1e7913320af23a","impliedFormat":1},{"version":"55cdbeebe76a1fa18bbd7e7bf73350a2173926bd3085bb050cf5a5397025ee4e","impliedFormat":1},{"version":"42baf4ca38c38deaf411ea73f37bc39ff56c6e5c761a968b64ac1b25c92b5cd8","impliedFormat":1},{"version":"4f6ae308c5f2901f2988c817e1511520619e9025b9b12cc7cce2ab2e6ffed78a","impliedFormat":1},{"version":"8718fa41d7cf4aa91de4e8f164c90f88e0bf343aa92a1b9b725a9c675c64e16b","impliedFormat":1},{"version":"f992cd6cc0bcbaa4e6c810468c90f2d8595f8c6c3cf050c806397d3de8585562","impliedFormat":1},{"version":"427fe2004642504828c1476d0af4270e6ad4db6de78c0b5da3e4c5ca95052a99","impliedFormat":1},{"version":"2eeffcee5c1661ddca53353929558037b8cf305ffb86a803512982f99bcab50d","impliedFormat":99},{"version":"9afb4cb864d297e4092a79ee2871b5d3143ea14153f62ef0bb04ede25f432030","affectsGlobalScope":true,"impliedFormat":99},{"version":"d5799bcf7fe4e6de3063abf4e321c14b051706f03cf5716d8a19f22fe1f69519","impliedFormat":1},{"version":"151ff381ef9ff8da2da9b9663ebf657eac35c4c9a19183420c05728f31a6761d","impliedFormat":1},{"version":"f3d8c757e148ad968f0d98697987db363070abada5f503da3c06aefd9d4248c1","impliedFormat":1},{"version":"fec943fdb3275eb6e006b35e04a8e2e99e9adf3f4b969ddf15315ac7575a93e4","impliedFormat":1},{"version":"736097ddbb2903bef918bb3b5811ef1c9c5656f2a73bd39b22a91b9cc2525e50","impliedFormat":1},{"version":"4340936f4e937c452ae783514e7c7bbb7fc06d0c97993ff4865370d0962bb9cf","impliedFormat":1},{"version":"b70c7ea83a7d0de17a791d9b5283f664033a96362c42cc4d2b2e0bdaa65ef7d1","impliedFormat":1},{"version":"74d5a87c3616cd5d8691059d531504403aa857e09cbaecb1c64dfb9ace0db185","impliedFormat":1}],"root":[61,[63,66],68,69,71,73],"options":{"composite":true,"module":7,"noUncheckedIndexedAccess":true,"outDir":"./dist","rootDir":"./src","skipLibCheck":true,"sourceMap":true,"strict":true,"target":9},"referencedMap":[[62,1],[72,2],[67,1],[70,3],[199,4],[202,5],[200,1],[203,1],[204,1],[197,1],[205,1],[206,6],[208,1],[209,7],[136,8],[137,8],[138,9],[76,10],[139,11],[140,12],[141,13],[74,1],[142,14],[143,15],[144,16],[145,17],[146,18],[147,19],[148,19],[149,20],[150,21],[151,22],[152,23],[77,1],[75,1],[153,24],[154,25],[155,26],[195,27],[156,28],[157,29],[158,28],[159,30],[160,31],[161,32],[162,33],[163,33],[164,33],[165,34],[166,35],[167,36],[168,37],[169,38],[170,39],[171,39],[172,40],[173,1],[174,1],[175,41],[176,42],[177,41],[178,43],[179,44],[180,45],[181,46],[182,47],[183,48],[184,49],[185,50],[186,51],[187,52],[188,53],[189,54],[190,55],[191,56],[192,57],[78,28],[79,1],[80,58],[81,59],[82,1],[83,60],[84,1],[127,61],[128,62],[129,63],[130,63],[131,64],[132,1],[133,11],[134,65],[135,62],[193,66],[194,67],[198,68],[210,69],[201,1],[207,68],[196,28],[59,1],[60,1],[10,1],[12,1],[11,1],[2,1],[13,1],[14,1],[15,1],[16,1],[17,1],[18,1],[19,1],[20,1],[3,1],[21,1],[22,1],[4,1],[23,1],[27,1],[24,1],[25,1],[26,1],[28,1],[29,1],[30,1],[5,1],[31,1],[32,1],[33,1],[34,1],[6,1],[38,1],[35,1],[36,1],[37,1],[39,1],[7,1],[40,1],[45,1],[46,1],[41,1],[42,1],[43,1],[44,1],[8,1],[50,1],[47,1],[48,1],[49,1],[51,1],[9,1],[52,1],[53,1],[54,1],[56,1],[55,1],[1,1],[57,1],[58,1],[103,70],[115,71],[100,72],[116,73],[125,74],[91,75],[92,76],[90,77],[124,78],[119,79],[123,80],[94,81],[112,82],[93,83],[122,84],[88,85],[89,79],[95,86],[96,1],[102,87],[99,86],[86,88],[126,89],[117,90],[106,91],[105,86],[107,92],[110,93],[104,94],[108,95],[120,78],[97,96],[98,97],[111,98],[87,73],[114,99],[113,86],[101,97],[109,100],[118,1],[85,1],[121,101],[71,102],[69,1],[65,1],[64,103],[68,104],[66,103],[63,105],[61,1],[73,106]],"latestChangedDtsFile":"./dist/renderer/app.d.ts","version":"5.9.3"} \ No newline at end of file diff --git a/tsconfig.shared.tsbuildinfo b/tsconfig.shared.tsbuildinfo new file mode 100644 index 0000000..5d79edd --- /dev/null +++ b/tsconfig.shared.tsbuildinfo @@ -0,0 +1 @@ +{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.dom.d.ts","./node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/typescript/lib/lib.dom.asynciterable.d.ts","./node_modules/typescript/lib/lib.webworker.importscripts.d.ts","./node_modules/typescript/lib/lib.scripthost.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/typescript/lib/lib.esnext.disposable.d.ts","./node_modules/typescript/lib/lib.esnext.float16.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/typescript/lib/lib.es2022.full.d.ts","./src/shared/types/mouse-trail.ts","./src/shared/domain/mouse-trail.ts","./src/shared/types/domain.ts","./src/shared/domain/project.ts","./src/shared/types/services.ts","./src/shared/types/ipc.ts","./src/shared/types/index.ts","./node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/@types/node/globals.typedarray.d.ts","./node_modules/@types/node/buffer.buffer.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/web-globals/abortcontroller.d.ts","./node_modules/@types/node/web-globals/blob.d.ts","./node_modules/@types/node/web-globals/console.d.ts","./node_modules/@types/node/web-globals/crypto.d.ts","./node_modules/@types/node/web-globals/domexception.d.ts","./node_modules/@types/node/web-globals/encoding.d.ts","./node_modules/@types/node/web-globals/events.d.ts","./node_modules/undici-types/utility.d.ts","./node_modules/undici-types/header.d.ts","./node_modules/undici-types/readable.d.ts","./node_modules/undici-types/fetch.d.ts","./node_modules/undici-types/formdata.d.ts","./node_modules/undici-types/connector.d.ts","./node_modules/undici-types/client-stats.d.ts","./node_modules/undici-types/client.d.ts","./node_modules/undici-types/errors.d.ts","./node_modules/undici-types/dispatcher.d.ts","./node_modules/undici-types/global-dispatcher.d.ts","./node_modules/undici-types/global-origin.d.ts","./node_modules/undici-types/pool-stats.d.ts","./node_modules/undici-types/pool.d.ts","./node_modules/undici-types/handlers.d.ts","./node_modules/undici-types/balanced-pool.d.ts","./node_modules/undici-types/round-robin-pool.d.ts","./node_modules/undici-types/h2c-client.d.ts","./node_modules/undici-types/agent.d.ts","./node_modules/undici-types/mock-interceptor.d.ts","./node_modules/undici-types/mock-call-history.d.ts","./node_modules/undici-types/mock-agent.d.ts","./node_modules/undici-types/mock-client.d.ts","./node_modules/undici-types/mock-pool.d.ts","./node_modules/undici-types/snapshot-agent.d.ts","./node_modules/undici-types/mock-errors.d.ts","./node_modules/undici-types/proxy-agent.d.ts","./node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/undici-types/retry-handler.d.ts","./node_modules/undici-types/retry-agent.d.ts","./node_modules/undici-types/api.d.ts","./node_modules/undici-types/cache-interceptor.d.ts","./node_modules/undici-types/interceptors.d.ts","./node_modules/undici-types/util.d.ts","./node_modules/undici-types/cookies.d.ts","./node_modules/undici-types/patch.d.ts","./node_modules/undici-types/websocket.d.ts","./node_modules/undici-types/eventsource.d.ts","./node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/undici-types/content-type.d.ts","./node_modules/undici-types/cache.d.ts","./node_modules/undici-types/index.d.ts","./node_modules/@types/node/web-globals/fetch.d.ts","./node_modules/@types/node/web-globals/importmeta.d.ts","./node_modules/@types/node/web-globals/messaging.d.ts","./node_modules/@types/node/web-globals/navigator.d.ts","./node_modules/@types/node/web-globals/performance.d.ts","./node_modules/@types/node/web-globals/storage.d.ts","./node_modules/@types/node/web-globals/streams.d.ts","./node_modules/@types/node/web-globals/timers.d.ts","./node_modules/@types/node/web-globals/url.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/assert/strict.d.ts","./node_modules/@types/node/async_hooks.d.ts","./node_modules/@types/node/buffer.d.ts","./node_modules/@types/node/child_process.d.ts","./node_modules/@types/node/cluster.d.ts","./node_modules/@types/node/console.d.ts","./node_modules/@types/node/constants.d.ts","./node_modules/@types/node/crypto.d.ts","./node_modules/@types/node/dgram.d.ts","./node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/@types/node/dns.d.ts","./node_modules/@types/node/dns/promises.d.ts","./node_modules/@types/node/domain.d.ts","./node_modules/@types/node/events.d.ts","./node_modules/@types/node/fs.d.ts","./node_modules/@types/node/fs/promises.d.ts","./node_modules/@types/node/http.d.ts","./node_modules/@types/node/http2.d.ts","./node_modules/@types/node/https.d.ts","./node_modules/@types/node/inspector.d.ts","./node_modules/@types/node/inspector.generated.d.ts","./node_modules/@types/node/inspector/promises.d.ts","./node_modules/@types/node/module.d.ts","./node_modules/@types/node/net.d.ts","./node_modules/@types/node/os.d.ts","./node_modules/@types/node/path.d.ts","./node_modules/@types/node/path/posix.d.ts","./node_modules/@types/node/path/win32.d.ts","./node_modules/@types/node/perf_hooks.d.ts","./node_modules/@types/node/process.d.ts","./node_modules/@types/node/punycode.d.ts","./node_modules/@types/node/querystring.d.ts","./node_modules/@types/node/quic.d.ts","./node_modules/@types/node/readline.d.ts","./node_modules/@types/node/readline/promises.d.ts","./node_modules/@types/node/repl.d.ts","./node_modules/@types/node/sea.d.ts","./node_modules/@types/node/sqlite.d.ts","./node_modules/@types/node/stream.d.ts","./node_modules/@types/node/stream/consumers.d.ts","./node_modules/@types/node/stream/promises.d.ts","./node_modules/@types/node/stream/web.d.ts","./node_modules/@types/node/string_decoder.d.ts","./node_modules/@types/node/test.d.ts","./node_modules/@types/node/test/reporters.d.ts","./node_modules/@types/node/timers.d.ts","./node_modules/@types/node/timers/promises.d.ts","./node_modules/@types/node/tls.d.ts","./node_modules/@types/node/trace_events.d.ts","./node_modules/@types/node/tty.d.ts","./node_modules/@types/node/url.d.ts","./node_modules/@types/node/util.d.ts","./node_modules/@types/node/util/types.d.ts","./node_modules/@types/node/v8.d.ts","./node_modules/@types/node/vm.d.ts","./node_modules/@types/node/wasi.d.ts","./node_modules/@types/node/worker_threads.d.ts","./node_modules/@types/node/zlib.d.ts","./node_modules/@types/node/index.d.ts"],"fileIdsList":[[75,135,136,138,146,150,153,155,156,157,169],[75,137,138,146,150,153,155,156,157,169],[138,146,150,153,155,156,157,169],[75,138,146,150,153,155,156,157,169,177],[75,138,139,144,146,149,150,153,155,156,157,159,169,174,186],[75,138,139,140,146,149,150,153,155,156,157,169],[75,138,146,150,153,155,156,157,169],[75,138,141,146,150,153,155,156,157,169,187],[75,138,142,143,146,150,153,155,156,157,160,169],[75,138,143,146,150,153,155,156,157,169,174,183],[75,138,144,146,149,150,153,155,156,157,159,169],[75,137,138,145,146,150,153,155,156,157,169],[75,138,146,147,150,153,155,156,157,169],[75,138,146,148,149,150,153,155,156,157,169],[75,137,138,146,149,150,153,155,156,157,169],[75,138,146,149,150,151,153,155,156,157,169,174,186],[75,138,146,149,150,151,153,155,156,157,169,174,177],[75,125,138,146,149,150,152,153,155,156,157,159,169,174,186],[75,138,146,149,150,152,153,155,156,157,159,169,174,183,186],[75,138,146,150,152,153,154,155,156,157,169,174,183,186],[73,74,75,76,77,78,79,80,81,82,83,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193],[75,138,146,149,150,153,155,156,157,169],[75,138,146,150,153,155,157,169],[75,138,146,150,153,155,156,157,158,169,186],[75,138,146,149,150,153,155,156,157,159,169,174],[75,138,146,150,153,155,156,157,160,169],[75,138,146,150,153,155,156,157,161,169],[75,138,146,149,150,153,155,156,157,164,169],[75,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193],[75,138,146,150,153,155,156,157,166,169],[75,138,146,150,153,155,156,157,167,169],[75,138,143,146,150,153,155,156,157,159,169,177],[75,138,146,149,150,153,155,156,157,169,170],[75,138,146,150,153,155,156,157,169,171,187,190],[75,138,146,149,150,153,155,156,157,169,174,176,177],[75,138,146,150,153,155,156,157,169,175,177],[75,138,146,150,153,155,156,157,169,177,187],[75,138,146,150,153,155,156,157,169,178],[75,135,138,146,150,153,155,156,157,169,174,180,186],[75,138,146,150,153,155,156,157,169,174,179],[75,138,146,149,150,153,155,156,157,169,181,182],[75,138,146,150,153,155,156,157,169,181,182],[75,138,143,146,150,153,155,156,157,159,169,174,183],[75,138,146,150,153,155,156,157,169,184],[75,138,146,150,153,155,156,157,159,169,185],[75,138,146,150,152,153,155,156,157,167,169,186],[75,138,146,150,153,155,156,157,169,187,188],[75,138,143,146,150,153,155,156,157,169,188],[75,138,146,150,153,155,156,157,169,174,189],[75,138,146,150,153,155,156,157,158,169,190],[75,138,146,150,153,155,156,157,169,191],[75,138,141,146,150,153,155,156,157,169],[75,138,143,146,150,153,155,156,157,169],[75,138,146,150,153,155,156,157,169,187],[75,125,138,146,150,153,155,156,157,169],[75,138,146,150,153,155,156,157,169,186],[75,138,146,150,153,155,156,157,169,192],[75,138,146,150,153,155,156,157,164,169],[75,138,146,150,153,155,156,157,169,182],[75,125,138,146,149,150,151,153,155,156,157,164,169,174,177,186,189,190,192],[75,138,146,150,153,155,156,157,169,174,193],[75,90,93,96,97,138,146,150,153,155,156,157,169,186],[75,93,138,146,150,153,155,156,157,169,174,186],[75,93,97,138,146,150,153,155,156,157,169,186],[75,138,146,150,153,155,156,157,169,174],[75,87,138,146,150,153,155,156,157,169],[75,91,138,146,150,153,155,156,157,169],[75,89,90,93,138,146,150,153,155,156,157,169,186],[75,138,146,150,153,155,156,157,159,169,183],[75,138,146,150,153,155,156,157,169,194],[75,87,138,146,150,153,155,156,157,169,194],[75,89,93,138,146,150,153,155,156,157,159,169,186],[75,84,85,86,88,92,138,146,149,150,153,155,156,157,169,174,186],[75,93,102,110,138,146,150,153,155,156,157,169],[75,85,91,138,146,150,153,155,156,157,169],[75,93,119,120,138,146,150,153,155,156,157,169],[75,85,88,93,138,146,150,153,155,156,157,169,177,186,194],[75,93,138,146,150,153,155,156,157,169],[75,89,93,138,146,150,153,155,156,157,169,186],[75,84,138,146,150,153,155,156,157,169],[75,87,88,89,91,92,93,94,95,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,120,121,122,123,124,138,146,150,153,155,156,157,169],[75,93,112,115,138,146,150,153,155,156,157,169],[75,93,102,103,104,138,146,150,153,155,156,157,169],[75,91,93,103,105,138,146,150,153,155,156,157,169],[75,92,138,146,150,153,155,156,157,169],[75,85,87,93,138,146,150,153,155,156,157,169],[75,93,97,103,105,138,146,150,153,155,156,157,169],[75,97,138,146,150,153,155,156,157,169],[75,91,93,96,138,146,150,153,155,156,157,169,186],[75,85,89,93,102,138,146,150,153,155,156,157,169],[75,93,112,138,146,150,153,155,156,157,169],[75,105,138,146,150,153,155,156,157,169],[75,87,93,119,138,146,150,153,155,156,157,169,177,192,194],[66,75,138,146,150,153,155,156,157,169],[68,75,138,146,150,153,155,156,157,161,169],[66,68,70,71,75,138,146,150,153,155,156,157,169],[66,70,75,138,146,150,153,155,156,157,169],[68,75,138,139,146,150,153,155,156,157,169]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2e80ee7a49e8ac312cc11b77f1475804bee36b3b2bc896bead8b6e1266befb43","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7a3c8b952931daebdfc7a2897c53c0a1c73624593fa070e46bd537e64dcd20a","affectsGlobalScope":true,"impliedFormat":1},{"version":"80e18897e5884b6723488d4f5652167e7bb5024f946743134ecc4aa4ee731f89","affectsGlobalScope":true,"impliedFormat":1},{"version":"cd034f499c6cdca722b60c04b5b1b78e058487a7085a8e0d6fb50809947ee573","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"8cdf8847677ac7d20486e54dd3fcf09eda95812ac8ace44b4418da1bbbab6eb8","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"51ad4c928303041605b4d7ae32e0c1ee387d43a24cd6f1ebf4a2699e1076d4fa","affectsGlobalScope":true,"impliedFormat":1},{"version":"196cb558a13d4533a5163286f30b0509ce0210e4b316c56c38d4c0fd2fb38405","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"3cbad9a1ba4453443026ed38e4b8be018abb26565fa7c944376463ad9df07c41","impliedFormat":1},{"version":"3df278aee6bebd1d2c31b19d922ba4a39b76e6449a7ee2061f1e3efe9dad3fa1","signature":"ba6aa78375568ff7ba83768f3cf2c237670288a1de47476f27b9c84b5901da1b","impliedFormat":1},{"version":"f30d5dc6e97a53de04b47feaa812cfab3bb1f1fd689544aee9fdc28be8ba4340","signature":"508469f47005b8771109e7929292fbc08c4697224077c982ca078e87c22948cc","impliedFormat":1},{"version":"ae78e9e8904c14b092008c7f446730e1e5a0fb4de5dd2f608549e74d1a67a2a9","signature":"54cd69c604e5a7d2e28a6dbdb2422f1d634ee6b037ce84e79924b4310fc50191","impliedFormat":1},{"version":"71a5296d8582dbd5c633a9129bef6095f8501c07ad33fe777c40e3a0cc59e819","signature":"738701f595a46acde6f49c4ea80dd03f94fa555cc960311451809c0e189d2282","impliedFormat":1},{"version":"9bfa85c53f2437f11a3c7a4ceffac2220f09d9158b694afe60d153d7a9b0ab4e","signature":"f9cdeb314ae8e1b58e8d37db1c1a1f00ca62c1790e1881113d41e62dbf12d159","impliedFormat":1},{"version":"e7679924ba7ad3fb473f050bc5f35c705b823e192b6c2a94e1a88f7de99bbc1e","signature":"0b8e33fe92a0c4732985f77b42d1b89c286b473acb47a484fc81d12ba890263a","impliedFormat":1},{"version":"39e94d019e8d280c299b35e6dbb235f0b449ba9aa7923d9e19fb3dad0f88c61f","impliedFormat":1},{"version":"d153a11543fd884b596587ccd97aebbeed950b26933ee000f94009f1ab142848","affectsGlobalScope":true,"impliedFormat":1},{"version":"0ccdaa19852d25ecd84eec365c3bfa16e7859cadecf6e9ca6d0dbbbee439743f","affectsGlobalScope":true,"impliedFormat":1},{"version":"438b41419b1df9f1fbe33b5e1b18f5853432be205991d1b19f5b7f351675541e","affectsGlobalScope":true,"impliedFormat":1},{"version":"096116f8fedc1765d5bd6ef360c257b4a9048e5415054b3bf3c41b07f8951b0b","affectsGlobalScope":true,"impliedFormat":1},{"version":"e5e01375c9e124a83b52ee4b3244ed1a4d214a6cfb54ac73e164a823a4a7860a","affectsGlobalScope":true,"impliedFormat":1},{"version":"f90ae2bbce1505e67f2f6502392e318f5714bae82d2d969185c4a6cecc8af2fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"4b58e207b93a8f1c88bbf2a95ddc686ac83962b13830fe8ad3f404ffc7051fb4","affectsGlobalScope":true,"impliedFormat":1},{"version":"1fefabcb2b06736a66d2904074d56268753654805e829989a46a0161cd8412c5","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"c18a99f01eb788d849ad032b31cafd49de0b19e083fe775370834c5675d7df8e","affectsGlobalScope":true,"impliedFormat":1},{"version":"5247874c2a23b9a62d178ae84f2db6a1d54e6c9a2e7e057e178cc5eea13757fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"cdcf9ea426ad970f96ac930cd176d5c69c6c24eebd9fc580e1572d6c6a88f62c","impliedFormat":1},{"version":"23cd712e2ce083d68afe69224587438e5914b457b8acf87073c22494d706a3d0","impliedFormat":1},{"version":"156a859e21ef3244d13afeeba4e49760a6afa035c149dda52f0c45ea8903b338","impliedFormat":1},{"version":"10ec5e82144dfac6f04fa5d1d6c11763b3e4dbbac6d99101427219ab3e2ae887","impliedFormat":1},{"version":"615754924717c0b1e293e083b83503c0a872717ad5aa60ed7f1a699eb1b4ea5c","impliedFormat":1},{"version":"074de5b2fdead0165a2757e3aaef20f27a6347b1c36adea27d51456795b37682","impliedFormat":1},{"version":"68834d631c8838c715f225509cfc3927913b9cc7a4870460b5b60c8dbdb99baf","impliedFormat":1},{"version":"24371e69a38fc33e268d4a8716dbcda430d6c2c414a99ff9669239c4b8f40dea","impliedFormat":1},{"version":"ccab02f3920fc75c01174c47fcf67882a11daf16baf9e81701d0a94636e94556","impliedFormat":1},{"version":"3e11fce78ad8c0e1d1db4ba5f0652285509be3acdd519529bc8fcef85f7dafd9","impliedFormat":1},{"version":"ea6bc8de8b59f90a7a3960005fd01988f98fd0784e14bc6922dde2e93305ec7d","impliedFormat":1},{"version":"36107995674b29284a115e21a0618c4c2751b32a8766dd4cb3ba740308b16d59","impliedFormat":1},{"version":"914a0ae30d96d71915fc519ccb4efbf2b62c0ddfb3a3fc6129151076bc01dc60","impliedFormat":1},{"version":"9c32412007b5662fd34a8eb04292fb5314ec370d7016d1c2fb8aa193c807fe22","impliedFormat":1},{"version":"7fd1b31fd35876b0aa650811c25ec2c97a3c6387e5473eb18004bed86cdd76b6","impliedFormat":1},{"version":"4d327f7d72ad0918275cea3eee49a6a8dc8114ae1d5b7f3f5d0774de75f7439a","impliedFormat":1},{"version":"6ebe8ebb8659aaa9d1acbf3710d7dae3e923e97610238b9511c25dc39023a166","impliedFormat":1},{"version":"e85d7f8068f6a26710bff0cc8c0fc5e47f71089c3780fbede05857331d2ddec9","impliedFormat":1},{"version":"7befaf0e76b5671be1d47b77fcc65f2b0aad91cc26529df1904f4a7c46d216e9","impliedFormat":1},{"version":"0a60a292b89ca7218b8616f78e5bbd1c96b87e048849469cccb4355e98af959a","impliedFormat":1},{"version":"0b6e25234b4eec6ed96ab138d96eb70b135690d7dd01f3dd8a8ab291c35a683a","impliedFormat":1},{"version":"9666f2f84b985b62400d2e5ab0adae9ff44de9b2a34803c2c5bd3c8325b17dc0","impliedFormat":1},{"version":"40cd35c95e9cf22cfa5bd84e96408b6fcbca55295f4ff822390abb11afbc3dca","impliedFormat":1},{"version":"b1616b8959bf557feb16369c6124a97a0e74ed6f49d1df73bb4b9ddf68acf3f3","impliedFormat":1},{"version":"5b03a034c72146b61573aab280f295b015b9168470f2df05f6080a2122f9b4df","impliedFormat":1},{"version":"40b463c6766ca1b689bfcc46d26b5e295954f32ad43e37ee6953c0a677e4ae2b","impliedFormat":1},{"version":"249b9cab7f5d628b71308c7d9bb0a808b50b091e640ba3ed6e2d0516f4a8d91d","impliedFormat":1},{"version":"80aae6afc67faa5ac0b32b5b8bc8cc9f7fa299cff15cf09cc2e11fd28c6ae29e","impliedFormat":1},{"version":"f473cd2288991ff3221165dcf73cd5d24da30391f87e85b3dd4d0450c787a391","impliedFormat":1},{"version":"499e5b055a5aba1e1998f7311a6c441a369831c70905cc565ceac93c28083d53","impliedFormat":1},{"version":"8aee8b6d4f9f62cf3776cda1305fb18763e2aade7e13cea5bbe699112df85214","impliedFormat":1},{"version":"c63b9ada8c72f95aac5db92aea07e5e87ec810353cdf63b2d78f49a58662cf6c","impliedFormat":1},{"version":"1cc2a09e1a61a5222d4174ab358a9f9de5e906afe79dbf7363d871a7edda3955","impliedFormat":1},{"version":"5d0375ca7310efb77e3ef18d068d53784faf62705e0ad04569597ae0e755c401","impliedFormat":1},{"version":"59af37caec41ecf7b2e76059c9672a49e682c1a2aa6f9d7dc78878f53aa284d6","impliedFormat":1},{"version":"addf417b9eb3f938fddf8d81e96393a165e4be0d4a8b6402292f9c634b1cb00d","impliedFormat":1},{"version":"b64d4d1c5f877f9c666e98e833f0205edb9384acc46e98a1fef344f64d6aba44","impliedFormat":1},{"version":"adf27937dba6af9f08a68c5b1d3fce0ca7d4b960c57e6d6c844e7d1a8e53adae","impliedFormat":1},{"version":"12950411eeab8563b349cb7959543d92d8d02c289ed893d78499a19becb5a8cc","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"c9381908473a1c92cb8c516b184e75f4d226dad95c3a85a5af35f670064d9a2f","impliedFormat":1},{"version":"c3f5289820990ab66b70c7fb5b63cb674001009ff84b13de40619619a9c8175f","affectsGlobalScope":true,"impliedFormat":1},{"version":"b3275d55fac10b799c9546804126239baf020d220136163f763b55a74e50e750","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa68a0a3b7cb32c00e39ee3cd31f8f15b80cac97dce51b6ee7fc14a1e8deb30b","affectsGlobalScope":true,"impliedFormat":1},{"version":"1cf059eaf468efcc649f8cf6075d3cb98e9a35a0fe9c44419ec3d2f5428d7123","affectsGlobalScope":true,"impliedFormat":1},{"version":"6c36e755bced82df7fb6ce8169265d0a7bb046ab4e2cb6d0da0cb72b22033e89","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"7a93de4ff8a63bafe62ba86b89af1df0ccb5e40bb85b0c67d6bbcfdcf96bf3d4","affectsGlobalScope":true,"impliedFormat":1},{"version":"90e85f9bc549dfe2b5749b45fe734144e96cd5d04b38eae244028794e142a77e","affectsGlobalScope":true,"impliedFormat":1},{"version":"e0a5deeb610b2a50a6350bd23df6490036a1773a8a71d70f2f9549ab009e67ee","affectsGlobalScope":true,"impliedFormat":1},{"version":"3fad5618174d74a34ee006406d4eb37e8d07dd62eb1315dbf52f48d31a337547","impliedFormat":1},{"version":"7e49f52a159435fc8df4de9dc377ef5860732ca2dc9efec1640531d3cf5da7a3","impliedFormat":1},{"version":"dd4bde4bdc2e5394aed6855e98cf135dfdf5dd6468cad842e03116d31bbcc9bc","impliedFormat":1},{"version":"4d4e879009a84a47c05350b8dca823036ba3a29a3038efed1be76c9f81e45edf","affectsGlobalScope":true,"impliedFormat":1},{"version":"8b50a819485ffe0d237bf0d131e92178d14d11e2aa873d73615a9ec578b341f5","impliedFormat":1},{"version":"9ba13b47cb450a438e3076c4a3f6afb9dc85e17eae50f26d4b2d72c0688c9251","impliedFormat":1},{"version":"b64cd4401633ea4ecadfd700ddc8323a13b63b106ac7127c1d2726f32424622c","impliedFormat":1},{"version":"37c6e5fe5715814412b43cc9b50b24c67a63c4e04e753e0d1305970d65417a60","impliedFormat":1},{"version":"1d024184fb57c58c5c91823f9d10b4915a4867b7934e89115fd0d861a9df27c8","impliedFormat":1},{"version":"ee0e4946247f842c6dd483cbb60a5e6b484fee07996e3a7bc7343dfb68a04c5d","impliedFormat":1},{"version":"ef051f42b7e0ef5ca04552f54c4552eac84099d64b6c5ad0ef4033574b6035b8","impliedFormat":1},{"version":"853a43154f1d01b0173d9cbd74063507ece57170bad7a3b68f3fa1229ad0a92f","impliedFormat":1},{"version":"56231e3c39a031bfb0afb797690b20ed4537670c93c0318b72d5180833d98b72","impliedFormat":1},{"version":"5cc7c39031bfd8b00ad58f32143d59eb6ffc24f5d41a20931269011dccd36c5e","impliedFormat":1},{"version":"12d602a8fe4c2f2ba4f7804f5eda8ba07e0c83bf5cf0cda8baffa2e9967bfb77","affectsGlobalScope":true,"impliedFormat":1},{"version":"a856ab781967b62b288dfd85b860bef0e62f005ed4b1b8fa25c53ce17856acaf","impliedFormat":1},{"version":"cc25940cfb27aa538e60d465f98bb5068d4d7d33131861ace43f04fe6947d68f","impliedFormat":1},{"version":"8db46b61a690f15b245cf16270db044dc047dce9f93b103a59f50262f677ea1f","impliedFormat":1},{"version":"01ff95aa1443e3f7248974e5a771f513cb2ac158c8898f470a1792f817bee497","impliedFormat":1},{"version":"757227c8b345c57d76f7f0e3bbad7a91ffca23f1b2547cbed9e10025816c9cb7","impliedFormat":1},{"version":"959d0327c96dd9bb5521f3ed6af0c435996504cc8dd46baa8e12cb3b3518cef1","impliedFormat":1},{"version":"e1c1a0b4d1ead0de9eca52203aeb1f771f21e6238d6fcd15aa56ac2a02f1b7bf","impliedFormat":1},{"version":"101f482fd48cb4c7c0468dcc6d62c843d842977aea6235644b1edd05e81fbf22","impliedFormat":1},{"version":"266bee0a41e9c3ba335583e21e9277ae03822402cf5e8e1d99f5196853613b98","affectsGlobalScope":true,"impliedFormat":1},{"version":"386606f8a297988535cb1401959041cfa7f59d54b8a9ed09738e65c98684c976","impliedFormat":1},{"version":"3ef397f12387eff17f550bc484ea7c27d21d43816bbe609d495107f44b97e933","impliedFormat":1},{"version":"1023282e2ba810bc07905d3668349fbd37a26411f0c8f94a70ef3c05fe523fcf","impliedFormat":1},{"version":"b214ebcf76c51b115453f69729ee8aa7b7f8eccdae2a922b568a45c2d7ff52f7","impliedFormat":1},{"version":"429c9cdfa7d126255779efd7e6d9057ced2d69c81859bbab32073bad52e9ba76","impliedFormat":1},{"version":"e236b5eba291f51bdf32c231673e6cab81b5410850e61f51a7a524dddadc0f95","impliedFormat":1},{"version":"ce8653341224f8b45ff46d2a06f2cacb96f841f768a886c9d8dd8ec0878b11bd","affectsGlobalScope":true,"impliedFormat":1},{"version":"7f2c62938251b45715fd2a9887060ec4fbc8724727029d1cbce373747252bdd7","impliedFormat":1},{"version":"e3ace08b6bbd84655d41e244677b474fd995923ffef7149ddb68af8848b60b05","impliedFormat":1},{"version":"132580b0e86c48fab152bab850fc57a4b74fe915c8958d2ccb052b809a44b61c","impliedFormat":1},{"version":"90a278f5fab7557e69e97056c0841adf269c42697194f0bd5c5e69152637d4b3","impliedFormat":1},{"version":"69c9a5a9392e8564bd81116e1ed93b13205201fb44cb35a7fde8c9f9e21c4b23","impliedFormat":1},{"version":"5f8fc37f8434691ffac1bfd8fc2634647da2c0e84253ab5d2dd19a7718915b35","impliedFormat":1},{"version":"5981c2340fd8b076cae8efbae818d42c11ffc615994cb060b1cd390795f1be2b","impliedFormat":1},{"version":"f263485c9ca90df9fe7bb3a906db9701997dc6cae86ace1f8106ac8d2f7f677b","impliedFormat":1},{"version":"1edcf2f36fc332615846bde6dcc71a8fe526065505bc5e3dcfd65a14becdf698","affectsGlobalScope":true,"impliedFormat":1},{"version":"0250da3eb85c99624f974e77ef355cdf86f43980251bc371475c2b397ba55bcd","impliedFormat":1},{"version":"f1c93e046fb3d9b7f8249629f4b63dc068dd839b824dd0aa39a5e68476dc9420","impliedFormat":1},{"version":"3d3a5f27ffbc06c885dd4d5f9ee20de61faf877fe2c3a7051c4825903d9a7fdc","impliedFormat":1},{"version":"12806f9f085598ef930edaf2467a5fa1789a878fba077cd27e85dc5851e11834","impliedFormat":1},{"version":"1dbca38aa4b0db1f4f9e6edacc2780af7e028b733d2a98dd3598cd235ca0c97d","impliedFormat":1},{"version":"a43fe41c33d0a192a0ecaf9b92e87bef3709c9972e6d53c42c49251ccb962d69","impliedFormat":1},{"version":"a177959203c017fad3ecc4f3d96c8757a840957a4959a3ae00dab9d35961ca6c","affectsGlobalScope":true,"impliedFormat":1},{"version":"6fc727ccf9b36e257ff982ea0badeffbfc2c151802f741bddff00c6af3b784cf","impliedFormat":1},{"version":"19143c930aef7ccf248549f3e78992f2f1049118ec5d4622e95025057d8e392b","impliedFormat":1},{"version":"4844a4c9b4b1e812b257676ed8a80b3f3be0e29bf05e742cc2ea9c3c6865e6c6","impliedFormat":1},{"version":"064878a60367e0407c42fb7ba02a2ea4d83257357dc20088e549bd4d89433e9c","impliedFormat":1},{"version":"cca8917838a876e2d7016c9b6af57cbf11fdf903c5fdd8e613fa31840b2957bf","impliedFormat":1},{"version":"d91ae55e4282c22b9c21bc26bd3ef637d3fe132507b10529ae68bf76f5de785b","impliedFormat":1},{"version":"b484ec11ba00e3a2235562a41898d55372ccabe607986c6fa4f4aba72093749f","impliedFormat":1},{"version":"7e8a671604329e178bb479c8f387715ebd40a091fc4a7552a0a75c2f3a21c65c","impliedFormat":1},{"version":"41ef7992c555671a8fe54db302788adefa191ded810a50329b79d20a6772d14c","impliedFormat":1},{"version":"041a7781b9127ab568d2cdcce62c58fdea7c7407f40b8c50045d7866a2727130","impliedFormat":1},{"version":"4c5e90ddbcd177ad3f2ffc909ae217c87820f1e968f6959e4b6ba38a8cec935e","impliedFormat":1},{"version":"b70dd9a44e1ac42f030bb12e7d79117eac7cb74170d72d381a1e7913320af23a","impliedFormat":1},{"version":"55cdbeebe76a1fa18bbd7e7bf73350a2173926bd3085bb050cf5a5397025ee4e","impliedFormat":1}],"root":[[66,72]],"options":{"composite":true,"declaration":true,"declarationMap":true,"module":199,"noUncheckedIndexedAccess":true,"outDir":"./dist/shared","rootDir":"./src/shared","skipLibCheck":true,"sourceMap":true,"strict":true,"target":9},"referencedMap":[[135,1],[136,1],[137,2],[75,3],[138,4],[139,5],[140,6],[73,7],[141,8],[142,9],[143,10],[144,11],[145,12],[146,13],[147,13],[148,14],[149,15],[150,16],[151,17],[76,7],[74,7],[152,18],[153,19],[154,20],[194,21],[155,22],[156,23],[157,22],[158,24],[159,25],[160,26],[161,27],[162,27],[163,27],[164,28],[165,29],[166,30],[167,31],[168,32],[169,33],[170,33],[171,34],[172,7],[173,7],[174,35],[175,36],[176,35],[177,37],[178,38],[179,39],[180,40],[181,41],[182,42],[183,43],[184,44],[185,45],[186,46],[187,47],[188,48],[189,49],[190,50],[191,51],[77,22],[78,7],[79,52],[80,53],[81,7],[82,54],[83,7],[126,55],[127,56],[128,57],[129,57],[130,58],[131,7],[132,4],[133,59],[134,56],[192,60],[193,61],[63,7],[64,7],[12,7],[10,7],[11,7],[16,7],[15,7],[2,7],[17,7],[18,7],[19,7],[20,7],[21,7],[22,7],[23,7],[24,7],[3,7],[25,7],[26,7],[4,7],[27,7],[31,7],[28,7],[29,7],[30,7],[32,7],[33,7],[34,7],[5,7],[35,7],[36,7],[37,7],[38,7],[6,7],[42,7],[39,7],[40,7],[41,7],[43,7],[7,7],[44,7],[49,7],[50,7],[45,7],[46,7],[47,7],[48,7],[8,7],[54,7],[51,7],[52,7],[53,7],[55,7],[9,7],[56,7],[65,7],[57,7],[58,7],[60,7],[59,7],[1,7],[61,7],[62,7],[14,7],[13,7],[102,62],[114,63],[99,64],[115,65],[124,66],[90,67],[91,68],[89,69],[123,70],[118,71],[122,72],[93,73],[111,74],[92,75],[121,76],[87,77],[88,71],[94,78],[95,7],[101,79],[98,78],[85,80],[125,81],[116,82],[105,83],[104,78],[106,84],[109,85],[103,86],[107,87],[119,70],[96,88],[97,89],[110,90],[86,65],[113,91],[112,78],[100,89],[108,92],[117,7],[84,7],[120,93],[67,94],[69,95],[68,7],[72,96],[71,97],[66,7],[70,98]],"latestChangedDtsFile":"./dist/shared/types/index.d.ts","version":"5.9.3"} \ No newline at end of file From 1e2b3a5ecf293e18bd88cc5f90af585c1cb0dc83 Mon Sep 17 00:00:00 2001 From: Tadas Petra <60107328+tadaspetra@users.noreply.github.com> Date: Sun, 29 Mar 2026 20:33:28 -0500 Subject: [PATCH 3/5] camera flip --- .gitignore | 1 + src/main/services/render-filter-service.ts | 6 +- src/renderer/app.ts | 27 +- src/renderer/features/camera/camera-render.ts | 50 + src/renderer/styles/main.css | 1054 ----------------- tests/unit/camera-render.test.ts | 47 + tests/unit/render-filter-service.test.ts | 37 + 7 files changed, 160 insertions(+), 1062 deletions(-) create mode 100644 src/renderer/features/camera/camera-render.ts delete mode 100644 src/renderer/styles/main.css create mode 100644 tests/unit/camera-render.test.ts diff --git a/.gitignore b/.gitignore index 42033b3..482f325 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ coverage dist-smoke .env .cursor/hooks/state/ +src/renderer/styles/main.css .claude/worktrees/* pnpm-lock.yaml diff --git a/src/main/services/render-filter-service.ts b/src/main/services/render-filter-service.ts index 16a3161..bc2d28e 100644 --- a/src/main/services/render-filter-service.ts +++ b/src/main/services/render-filter-service.ts @@ -293,18 +293,18 @@ export function buildFilterComplex( const xExpr = buildPosExpr(scaledKeyframes, 'pipX'); const yExpr = buildPosExpr(scaledKeyframes, 'pipY'); - return `${screenFilter};[1:v]split[cam1][cam2];${camPipFilter};${camFullFilter};[screen][cam]overlay=x='${xExpr}':y='${yExpr}':format=auto[with_pip];[with_pip][camfull]overlay=0:0:format=auto[out]`; + return `${screenFilter};[1:v]setpts=PTS-STARTPTS,hflip,split[cam1][cam2];${camPipFilter};${camFullFilter};[screen][cam]overlay=x='${xExpr}':y='${yExpr}':format=auto[with_pip];[with_pip][camfull]overlay=0:0:format=auto[out]`; } if (hasCamFull) { const camFullAlpha = buildCamFullAlphaExpr(keyframes); - const camFullFilter = `[1:v]setpts=PTS-STARTPTS,scale=${outW}:${outH}:flags=lanczos:force_original_aspect_ratio=increase,crop=${outW}:${outH},format=yuva420p,geq=lum='lum(X,Y)':cb='cb(X,Y)':cr='cr(X,Y)':a='255*(${camFullAlpha})'[camfull]`; + const camFullFilter = `[1:v]setpts=PTS-STARTPTS,hflip,scale=${outW}:${outH}:flags=lanczos:force_original_aspect_ratio=increase,crop=${outW}:${outH},format=yuva420p,geq=lum='lum(X,Y)':cb='cb(X,Y)':cr='cr(X,Y)':a='255*(${camFullAlpha})'[camfull]`; return `${screenFilter};${camFullFilter};[screen][camfull]overlay=0:0:format=auto[out]`; } const alphaExpr = buildAlphaExpr(keyframes); const roundCornerExpr = `lte(pow(max(0,max(${radius}-X,X-${maxCoord})),2)+pow(max(0,max(${radius}-Y,Y-${maxCoord})),2),${radiusSquared})`; - const camFilter = `[1:v]setpts=PTS-STARTPTS,crop='min(iw,ih)':'min(iw,ih)':'(iw-min(iw,ih))/2':'(ih-min(iw,ih))/2',scale=${actualPipSize}:${actualPipSize},format=yuva420p,geq=lum='lum(X,Y)':cb='cb(X,Y)':cr='cr(X,Y)':a='255*${roundCornerExpr}*(${alphaExpr})'[cam]`; + const camFilter = `[1:v]setpts=PTS-STARTPTS,hflip,crop='min(iw,ih)':'min(iw,ih)':'(iw-min(iw,ih))/2':'(ih-min(iw,ih))/2',scale=${actualPipSize}:${actualPipSize},format=yuva420p,geq=lum='lum(X,Y)':cb='cb(X,Y)':cr='cr(X,Y)':a='255*${roundCornerExpr}*(${alphaExpr})'[cam]`; const xExpr = buildPosExpr(scaledKeyframes, 'pipX'); const yExpr = buildPosExpr(scaledKeyframes, 'pipY'); diff --git a/src/renderer/app.ts b/src/renderer/app.ts index dd7b0f4..0f66e54 100644 --- a/src/renderer/app.ts +++ b/src/renderer/app.ts @@ -37,6 +37,10 @@ import { shouldRenderPreviewFrame, createCameraRecordingStream } from './features/recording/recorder-utils'; +import { + drawMirroredImage, + getCenteredSquareCropRect +} from './features/camera/camera-render'; import { cleanupAllMedia } from './features/media-cleanup'; const projectHomeView = document.getElementById('projectHomeView'); @@ -1709,10 +1713,23 @@ function drawPip(targetCtx, video, pipX, pipY, pipW, pipH) { targetCtx.clip(); const camW = video.videoWidth; const camH = video.videoHeight; - const cropSize = Math.min(camW, camH); - const sx = (camW - cropSize) / 2; - const sy = (camH - cropSize) / 2; - targetCtx.drawImage(video, sx, sy, cropSize, cropSize, pipX, pipY, pipW, pipH); + const crop = getCenteredSquareCropRect(camW, camH); + if (!crop) { + targetCtx.restore(); + return; + } + drawMirroredImage( + targetCtx, + video, + crop.sourceX, + crop.sourceY, + crop.size, + crop.size, + pipX, + pipY, + pipW, + pipH + ); targetCtx.restore(); } @@ -1742,7 +1759,7 @@ function drawCameraRect(targetCtx, video, x, y, w, h, r) { const dh = vh * scale; const dx = x + (w - dw) / 2; const dy = y + (h - dh) / 2; - targetCtx.drawImage(video, dx, dy, dw, dh); + drawMirroredImage(targetCtx, video, 0, 0, vw, vh, dx, dy, dw, dh); targetCtx.restore(); } diff --git a/src/renderer/features/camera/camera-render.ts b/src/renderer/features/camera/camera-render.ts new file mode 100644 index 0000000..b87f46f --- /dev/null +++ b/src/renderer/features/camera/camera-render.ts @@ -0,0 +1,50 @@ +export interface CenteredSquareCropRect { + sourceX: number; + sourceY: number; + size: number; +} + +type MirrorDrawContext = Pick< + CanvasRenderingContext2D, + 'drawImage' | 'restore' | 'save' | 'scale' | 'translate' +>; + +export function getCenteredSquareCropRect( + sourceWidth: number, + sourceHeight: number +): CenteredSquareCropRect | null { + if ( + !Number.isFinite(sourceWidth) || + !Number.isFinite(sourceHeight) || + sourceWidth <= 0 || + sourceHeight <= 0 + ) { + return null; + } + + const size = Math.min(sourceWidth, sourceHeight); + return { + sourceX: (sourceWidth - size) / 2, + sourceY: (sourceHeight - size) / 2, + size + }; +} + +export function drawMirroredImage( + targetCtx: MirrorDrawContext, + source: CanvasImageSource, + sourceX: number, + sourceY: number, + sourceWidth: number, + sourceHeight: number, + destX: number, + destY: number, + destWidth: number, + destHeight: number +): void { + targetCtx.save(); + targetCtx.translate(destX + destWidth, destY); + targetCtx.scale(-1, 1); + targetCtx.drawImage(source, sourceX, sourceY, sourceWidth, sourceHeight, 0, 0, destWidth, destHeight); + targetCtx.restore(); +} diff --git a/src/renderer/styles/main.css b/src/renderer/styles/main.css deleted file mode 100644 index 3417e66..0000000 --- a/src/renderer/styles/main.css +++ /dev/null @@ -1,1054 +0,0 @@ -@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); -*, -:after, -:before { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-gradient-from-position: ; - --tw-gradient-via-position: ; - --tw-gradient-to-position: ; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgba(59, 130, 246, 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; - --tw-contain-size: ; - --tw-contain-layout: ; - --tw-contain-paint: ; - --tw-contain-style: ; -} -::backdrop { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-gradient-from-position: ; - --tw-gradient-via-position: ; - --tw-gradient-to-position: ; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgba(59, 130, 246, 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; - --tw-contain-size: ; - --tw-contain-layout: ; - --tw-contain-paint: ; - --tw-contain-style: ; -} - -/*! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com*/ -*, -:after, -:before { - box-sizing: border-box; - border: 0 solid #e5e7eb; -} -:after, -:before { - --tw-content: ''; -} -:host, -html { - line-height: 1.5; - -webkit-text-size-adjust: 100%; - -moz-tab-size: 4; - -o-tab-size: 4; - tab-size: 4; - font-family: - Inter, - system-ui, - -apple-system, - sans-serif; - font-feature-settings: normal; - font-variation-settings: normal; - -webkit-tap-highlight-color: transparent; -} -body { - margin: 0; - line-height: inherit; -} -hr { - height: 0; - color: inherit; - border-top-width: 1px; -} -abbr:where([title]) { - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; -} -h1, -h2, -h3, -h4, -h5, -h6 { - font-size: inherit; - font-weight: inherit; -} -a { - color: inherit; - text-decoration: inherit; -} -b, -strong { - font-weight: bolder; -} -code, -kbd, -pre, -samp { - font-family: - ui-monospace, - SFMono-Regular, - Menlo, - Monaco, - Consolas, - Liberation Mono, - Courier New, - monospace; - font-feature-settings: normal; - font-variation-settings: normal; - font-size: 1em; -} -small { - font-size: 80%; -} -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} -sub { - bottom: -0.25em; -} -sup { - top: -0.5em; -} -table { - text-indent: 0; - border-color: inherit; - border-collapse: collapse; -} -button, -input, -optgroup, -select, -textarea { - font-family: inherit; - font-feature-settings: inherit; - font-variation-settings: inherit; - font-size: 100%; - font-weight: inherit; - line-height: inherit; - letter-spacing: inherit; - color: inherit; - margin: 0; - padding: 0; -} -button, -select { - text-transform: none; -} -button, -input:where([type='button']), -input:where([type='reset']), -input:where([type='submit']) { - -webkit-appearance: button; - background-color: transparent; - background-image: none; -} -:-moz-focusring { - outline: auto; -} -:-moz-ui-invalid { - box-shadow: none; -} -progress { - vertical-align: baseline; -} -::-webkit-inner-spin-button, -::-webkit-outer-spin-button { - height: auto; -} -[type='search'] { - -webkit-appearance: textfield; - outline-offset: -2px; -} -::-webkit-search-decoration { - -webkit-appearance: none; -} -::-webkit-file-upload-button { - -webkit-appearance: button; - font: inherit; -} -summary { - display: list-item; -} -blockquote, -dd, -dl, -figure, -h1, -h2, -h3, -h4, -h5, -h6, -hr, -p, -pre { - margin: 0; -} -fieldset { - margin: 0; -} -fieldset, -legend { - padding: 0; -} -menu, -ol, -ul { - list-style: none; - margin: 0; - padding: 0; -} -dialog { - padding: 0; -} -textarea { - resize: vertical; -} -input::-moz-placeholder, -textarea::-moz-placeholder { - opacity: 1; - color: #9ca3af; -} -input::placeholder, -textarea::placeholder { - opacity: 1; - color: #9ca3af; -} -[role='button'], -button { - cursor: pointer; -} -:disabled { - cursor: default; -} -audio, -canvas, -embed, -iframe, -img, -object, -svg, -video { - display: block; - vertical-align: middle; -} -img, -video { - max-width: 100%; - height: auto; -} -[hidden]:where(:not([hidden='until-found'])) { - display: none; -} -.pointer-events-none { - pointer-events: none; -} -.visible { - visibility: visible; -} -.fixed { - position: fixed; -} -.absolute { - position: absolute; -} -.relative { - position: relative; -} -.inset-0 { - inset: 0; -} -.bottom-0 { - bottom: 0; -} -.left-0 { - left: 0; -} -.top-0 { - top: 0; -} -.z-0 { - z-index: 0; -} -.z-20 { - z-index: 20; -} -.z-\[1\] { - z-index: 1; -} -.-mx-1 { - margin-left: -0.25rem; - margin-right: -0.25rem; -} -.mx-1 { - margin-left: 0.25rem; - margin-right: 0.25rem; -} -.mb-1 { - margin-bottom: 0.25rem; -} -.mb-2 { - margin-bottom: 0.5rem; -} -.ml-3 { - margin-left: 0.75rem; -} -.mt-0\.5 { - margin-top: 0.125rem; -} -.mt-1 { - margin-top: 0.25rem; -} -.block { - display: block; -} -.flex { - display: flex; -} -.hidden { - display: none; -} -.h-2 { - height: 0.5rem; -} -.h-5 { - height: 1.25rem; -} -.h-7 { - height: 1.75rem; -} -.h-8 { - height: 2rem; -} -.h-full { - height: 100%; -} -.h-screen { - height: 100vh; -} -.min-h-0 { - min-height: 0; -} -.w-0\.5 { - width: 0.125rem; -} -.w-10 { - width: 2.5rem; -} -.w-20 { - width: 5rem; -} -.w-28 { - width: 7rem; -} -.w-56 { - width: 14rem; -} -.w-64 { - width: 16rem; -} -.w-full { - width: 100%; -} -.w-px { - width: 1px; -} -.min-w-0 { - min-width: 0; -} -.min-w-\[46px\] { - min-width: 46px; -} -.min-w-\[80px\] { - min-width: 80px; -} -.max-w-lg { - max-width: 32rem; -} -.flex-1 { - flex: 1 1 0%; -} -.shrink-0 { - flex-shrink: 0; -} -.transform { - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -@keyframes pulse { - 50% { - opacity: 0.5; - } -} -.animate-pulse { - animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; -} -.cursor-pointer { - cursor: pointer; -} -.cursor-wait { - cursor: wait; -} -.select-none { - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; -} -.flex-col { - flex-direction: column; -} -.items-start { - align-items: flex-start; -} -.items-center { - align-items: center; -} -.justify-center { - justify-content: center; -} -.justify-between { - justify-content: space-between; -} -.gap-2 { - gap: 0.5rem; -} -.gap-3 { - gap: 0.75rem; -} -.gap-4 { - gap: 1rem; -} -.space-y-0\.5 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(0.125rem * (1 - var(--tw-space-y-reverse))); - margin-bottom: calc(0.125rem * var(--tw-space-y-reverse)); -} -.space-y-1\.5 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(0.375rem * (1 - var(--tw-space-y-reverse))); - margin-bottom: calc(0.375rem * var(--tw-space-y-reverse)); -} -.space-y-2 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(0.5rem * (1 - var(--tw-space-y-reverse))); - margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); -} -.space-y-3 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(0.75rem * (1 - var(--tw-space-y-reverse))); - margin-bottom: calc(0.75rem * var(--tw-space-y-reverse)); -} -.space-y-5 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(1.25rem * (1 - var(--tw-space-y-reverse))); - margin-bottom: calc(1.25rem * var(--tw-space-y-reverse)); -} -.overflow-hidden { - overflow: hidden; -} -.overflow-x-auto { - overflow-x: auto; -} -.overflow-y-auto { - overflow-y: auto; -} -.overflow-y-hidden { - overflow-y: hidden; -} -.truncate { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.rounded { - border-radius: 0.25rem; -} -.rounded-full { - border-radius: 9999px; -} -.rounded-lg { - border-radius: 0.5rem; -} -.rounded-md { - border-radius: 0.375rem; -} -.rounded-xl { - border-radius: 0.75rem; -} -.border { - border-width: 1px; -} -.border-b { - border-bottom-width: 1px; -} -.border-r { - border-right-width: 1px; -} -.border-t { - border-top-width: 1px; -} -.border-blue-500\/40 { - border-color: rgba(59, 130, 246, 0.4); -} -.border-neutral-700 { - --tw-border-opacity: 1; - border-color: rgb(64 64 64 / var(--tw-border-opacity, 1)); -} -.border-neutral-800 { - --tw-border-opacity: 1; - border-color: rgb(38 38 38 / var(--tw-border-opacity, 1)); -} -.border-neutral-800\/40 { - border-color: rgba(38, 38, 38, 0.4); -} -.border-neutral-800\/60 { - border-color: rgba(38, 38, 38, 0.6); -} -.border-neutral-800\/80 { - border-color: rgba(38, 38, 38, 0.8); -} -.border-red-500\/40 { - border-color: rgba(239, 68, 68, 0.4); -} -.bg-amber-500 { - --tw-bg-opacity: 1; - background-color: rgb(245 158 11 / var(--tw-bg-opacity, 1)); -} -.bg-black { - --tw-bg-opacity: 1; - background-color: rgb(0 0 0 / var(--tw-bg-opacity, 1)); -} -.bg-blue-500\/10 { - background-color: rgba(59, 130, 246, 0.1); -} -.bg-emerald-500 { - --tw-bg-opacity: 1; - background-color: rgb(16 185 129 / var(--tw-bg-opacity, 1)); -} -.bg-emerald-600 { - --tw-bg-opacity: 1; - background-color: rgb(5 150 105 / var(--tw-bg-opacity, 1)); -} -.bg-neutral-700 { - --tw-bg-opacity: 1; - background-color: rgb(64 64 64 / var(--tw-bg-opacity, 1)); -} -.bg-neutral-800 { - --tw-bg-opacity: 1; - background-color: rgb(38 38 38 / var(--tw-bg-opacity, 1)); -} -.bg-neutral-900 { - --tw-bg-opacity: 1; - background-color: rgb(23 23 23 / var(--tw-bg-opacity, 1)); -} -.bg-neutral-900\/40 { - background-color: hsla(0, 0%, 9%, 0.4); -} -.bg-neutral-900\/60 { - background-color: hsla(0, 0%, 9%, 0.6); -} -.bg-neutral-950 { - --tw-bg-opacity: 1; - background-color: rgb(10 10 10 / var(--tw-bg-opacity, 1)); -} -.bg-neutral-950\/95 { - background-color: hsla(0, 0%, 4%, 0.95); -} -.bg-red-500 { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity, 1)); -} -.bg-red-500\/10 { - background-color: rgba(239, 68, 68, 0.1); -} -.bg-red-600 { - --tw-bg-opacity: 1; - background-color: rgb(220 38 38 / var(--tw-bg-opacity, 1)); -} -.bg-white { - --tw-bg-opacity: 1; - background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1)); -} -.object-contain { - -o-object-fit: contain; - object-fit: contain; -} -.p-1 { - padding: 0.25rem; -} -.p-4 { - padding: 1rem; -} -.p-6 { - padding: 1.5rem; -} -.px-1 { - padding-left: 0.25rem; - padding-right: 0.25rem; -} -.px-1\.5 { - padding-left: 0.375rem; - padding-right: 0.375rem; -} -.px-2 { - padding-left: 0.5rem; - padding-right: 0.5rem; -} -.px-2\.5 { - padding-left: 0.625rem; - padding-right: 0.625rem; -} -.px-3 { - padding-left: 0.75rem; - padding-right: 0.75rem; -} -.px-3\.5 { - padding-left: 0.875rem; - padding-right: 0.875rem; -} -.px-4 { - padding-left: 1rem; - padding-right: 1rem; -} -.py-0\.5 { - padding-top: 0.125rem; - padding-bottom: 0.125rem; -} -.py-1 { - padding-top: 0.25rem; - padding-bottom: 0.25rem; -} -.py-1\.5 { - padding-top: 0.375rem; - padding-bottom: 0.375rem; -} -.py-2 { - padding-top: 0.5rem; - padding-bottom: 0.5rem; -} -.py-2\.5 { - padding-top: 0.625rem; - padding-bottom: 0.625rem; -} -.py-3 { - padding-top: 0.75rem; - padding-bottom: 0.75rem; -} -.py-6 { - padding-top: 1.5rem; - padding-bottom: 1.5rem; -} -.pb-1 { - padding-bottom: 0.25rem; -} -.text-left { - text-align: left; -} -.text-center { - text-align: center; -} -.text-right { - text-align: right; -} -.font-mono { - font-family: - ui-monospace, - SFMono-Regular, - Menlo, - Monaco, - Consolas, - Liberation Mono, - Courier New, - monospace; -} -.font-sans { - font-family: - Inter, - system-ui, - -apple-system, - sans-serif; -} -.text-\[10px\] { - font-size: 10px; -} -.text-\[11px\] { - font-size: 11px; -} -.text-\[8px\] { - font-size: 8px; -} -.text-lg { - font-size: 1.125rem; - line-height: 1.75rem; -} -.text-sm { - font-size: 0.875rem; - line-height: 1.25rem; -} -.text-xl { - font-size: 1.25rem; - line-height: 1.75rem; -} -.text-xs { - font-size: 0.75rem; - line-height: 1rem; -} -.font-medium { - font-weight: 500; -} -.font-semibold { - font-weight: 600; -} -.uppercase { - text-transform: uppercase; -} -.italic { - font-style: italic; -} -.tabular-nums { - --tw-numeric-spacing: tabular-nums; - font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) - var(--tw-numeric-spacing) var(--tw-numeric-fraction); -} -.leading-relaxed { - line-height: 1.625; -} -.leading-snug { - line-height: 1.375; -} -.tracking-tight { - letter-spacing: -0.025em; -} -.tracking-wider { - letter-spacing: 0.05em; -} -.text-amber-400 { - --tw-text-opacity: 1; - color: rgb(251 191 36 / var(--tw-text-opacity, 1)); -} -.text-blue-200 { - --tw-text-opacity: 1; - color: rgb(191 219 254 / var(--tw-text-opacity, 1)); -} -.text-emerald-400 { - --tw-text-opacity: 1; - color: rgb(52 211 153 / var(--tw-text-opacity, 1)); -} -.text-neutral-100 { - --tw-text-opacity: 1; - color: rgb(245 245 245 / var(--tw-text-opacity, 1)); -} -.text-neutral-200 { - --tw-text-opacity: 1; - color: rgb(229 229 229 / var(--tw-text-opacity, 1)); -} -.text-neutral-300 { - --tw-text-opacity: 1; - color: rgb(212 212 212 / var(--tw-text-opacity, 1)); -} -.text-neutral-400 { - --tw-text-opacity: 1; - color: rgb(163 163 163 / var(--tw-text-opacity, 1)); -} -.text-neutral-50 { - --tw-text-opacity: 1; - color: rgb(250 250 250 / var(--tw-text-opacity, 1)); -} -.text-neutral-500 { - --tw-text-opacity: 1; - color: rgb(115 115 115 / var(--tw-text-opacity, 1)); -} -.text-neutral-600 { - --tw-text-opacity: 1; - color: rgb(82 82 82 / var(--tw-text-opacity, 1)); -} -.text-neutral-950 { - --tw-text-opacity: 1; - color: rgb(10 10 10 / var(--tw-text-opacity, 1)); -} -.text-red-200 { - --tw-text-opacity: 1; - color: rgb(254 202 202 / var(--tw-text-opacity, 1)); -} -.text-red-400 { - --tw-text-opacity: 1; - color: rgb(248 113 113 / var(--tw-text-opacity, 1)); -} -.text-white { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity, 1)); -} -.line-through { - text-decoration-line: line-through; -} -.antialiased { - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} -.accent-white { - accent-color: #fff; -} -.outline { - outline-style: solid; -} -.blur { - --tw-blur: blur(8px); -} -.blur, -.filter { - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.backdrop-blur-md { - --tw-backdrop-blur: blur(12px); - -webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) - var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) - var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) - var(--tw-backdrop-sepia); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.transition { - transition-property: - color, - background-color, - border-color, - text-decoration-color, - fill, - stroke, - opacity, - box-shadow, - transform, - filter, - -webkit-backdrop-filter; - transition-property: - color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, - transform, filter, backdrop-filter; - transition-property: - color, - background-color, - border-color, - text-decoration-color, - fill, - stroke, - opacity, - box-shadow, - transform, - filter, - backdrop-filter, - -webkit-backdrop-filter; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 0.15s; -} -.transition-all { - transition-property: all; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 0.15s; -} -.transition-colors { - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 0.15s; -} -.duration-300 { - transition-duration: 0.3s; -} -.duration-75 { - transition-duration: 75ms; -} -select { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23737373' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); - background-repeat: no-repeat; - background-position: right 10px center; - padding-right: 32px; -} -::-webkit-scrollbar { - width: 6px; - height: 6px; -} -::-webkit-scrollbar-track { - background: transparent; -} -::-webkit-scrollbar-thumb { - background: #404040; - border-radius: 3px; -} -::-webkit-scrollbar-thumb:hover { - background: #525252; -} -.placeholder\:text-neutral-600::-moz-placeholder { - --tw-text-opacity: 1; - color: rgb(82 82 82 / var(--tw-text-opacity, 1)); -} -.placeholder\:text-neutral-600::placeholder { - --tw-text-opacity: 1; - color: rgb(82 82 82 / var(--tw-text-opacity, 1)); -} -.hover\:border-neutral-700:hover { - --tw-border-opacity: 1; - border-color: rgb(64 64 64 / var(--tw-border-opacity, 1)); -} -.hover\:bg-neutral-200:hover { - --tw-bg-opacity: 1; - background-color: rgb(229 229 229 / var(--tw-bg-opacity, 1)); -} -.hover\:bg-neutral-600:hover { - --tw-bg-opacity: 1; - background-color: rgb(82 82 82 / var(--tw-bg-opacity, 1)); -} -.hover\:bg-neutral-700:hover { - --tw-bg-opacity: 1; - background-color: rgb(64 64 64 / var(--tw-bg-opacity, 1)); -} -.hover\:bg-neutral-800:hover { - --tw-bg-opacity: 1; - background-color: rgb(38 38 38 / var(--tw-bg-opacity, 1)); -} -.hover\:bg-neutral-800\/60:hover { - background-color: rgba(38, 38, 38, 0.6); -} -.hover\:bg-neutral-900:hover { - --tw-bg-opacity: 1; - background-color: rgb(23 23 23 / var(--tw-bg-opacity, 1)); -} -.hover\:bg-red-700:hover { - --tw-bg-opacity: 1; - background-color: rgb(185 28 28 / var(--tw-bg-opacity, 1)); -} -.hover\:text-neutral-200:hover { - --tw-text-opacity: 1; - color: rgb(229 229 229 / var(--tw-text-opacity, 1)); -} -.hover\:text-neutral-300:hover { - --tw-text-opacity: 1; - color: rgb(212 212 212 / var(--tw-text-opacity, 1)); -} -.focus\:border-neutral-600:focus { - --tw-border-opacity: 1; - border-color: rgb(82 82 82 / var(--tw-border-opacity, 1)); -} -.focus\:outline-none:focus { - outline: 2px solid transparent; - outline-offset: 2px; -} -.focus\:ring-1:focus { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) - var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) - var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); -} -.focus\:ring-neutral-600:focus { - --tw-ring-opacity: 1; - --tw-ring-color: rgb(82 82 82/var(--tw-ring-opacity, 1)); -} -.disabled\:cursor-default:disabled { - cursor: default; -} -.disabled\:cursor-not-allowed:disabled { - cursor: not-allowed; -} -.disabled\:opacity-40:disabled { - opacity: 0.4; -} -.disabled\:opacity-50:disabled { - opacity: 0.5; -} -.disabled\:hover\:bg-neutral-800:hover:disabled { - --tw-bg-opacity: 1; - background-color: rgb(38 38 38 / var(--tw-bg-opacity, 1)); -} diff --git a/tests/unit/camera-render.test.ts b/tests/unit/camera-render.test.ts new file mode 100644 index 0000000..f8d7eaf --- /dev/null +++ b/tests/unit/camera-render.test.ts @@ -0,0 +1,47 @@ +import { describe, expect, test } from 'vitest'; + +import { + drawMirroredImage, + getCenteredSquareCropRect +} from '../../src/renderer/features/camera/camera-render'; + +describe('renderer/features/camera/camera-render', () => { + test('getCenteredSquareCropRect centers the square crop', () => { + expect(getCenteredSquareCropRect(1280, 720)).toEqual({ + sourceX: 280, + sourceY: 0, + size: 720 + }); + expect(getCenteredSquareCropRect(720, 1280)).toEqual({ + sourceX: 0, + sourceY: 280, + size: 720 + }); + expect(getCenteredSquareCropRect(0, 720)).toBeNull(); + }); + + test('drawMirroredImage flips the destination rect horizontally', () => { + const calls: Array<{ name: string; args: unknown[] }> = []; + const targetCtx = { + save: () => calls.push({ name: 'save', args: [] }), + translate: (...args: unknown[]) => calls.push({ name: 'translate', args }), + scale: (...args: unknown[]) => calls.push({ name: 'scale', args }), + drawImage: (...args: unknown[]) => calls.push({ name: 'drawImage', args }), + restore: () => calls.push({ name: 'restore', args: [] }) + } as unknown as Pick< + CanvasRenderingContext2D, + 'drawImage' | 'restore' | 'save' | 'scale' | 'translate' + >; + const source = { id: 'camera' } as unknown as CanvasImageSource; + + drawMirroredImage(targetCtx, source, 10, 20, 300, 200, 100, 150, 320, 180); + + expect(calls).toEqual([ + { name: 'save', args: [] }, + { name: 'translate', args: [420, 150] }, + { name: 'scale', args: [-1, 1] }, + { name: 'drawImage', args: [source, 10, 20, 300, 200, 0, 0, 320, 180] }, + { name: 'restore', args: [] } + ]); + }); +}); diff --git a/tests/unit/render-filter-service.test.ts b/tests/unit/render-filter-service.test.ts index 46bd01d..168b8a2 100644 --- a/tests/unit/render-filter-service.test.ts +++ b/tests/unit/render-filter-service.test.ts @@ -128,10 +128,47 @@ describe('main/services/render-filter-service', () => { expect(filter).toContain('[screen]'); expect(filter).toContain('[cam]'); expect(filter).toContain('overlay'); + expect(filter).toContain('[1:v]setpts=PTS-STARTPTS,hflip,crop='); expect(filter).toContain("zoompan=z='if(gte(it,2.000),2.000"); expect(filter).toContain(":x='max(0,min(iw-iw/zoom,iw*(if(gte(it,2.000),0.750000"); }); + test('buildFilterComplex mirrors the shared camera source before fullscreen transitions', () => { + const filter = buildFilterComplex( + [ + { + time: 0, + pipX: 100, + pipY: 100, + pipVisible: true, + cameraFullscreen: false, + backgroundZoom: 1, + backgroundPanX: 0, + backgroundPanY: 0 + }, + { + time: 2, + pipX: 120, + pipY: 120, + pipVisible: true, + cameraFullscreen: true, + backgroundZoom: 1, + backgroundPanX: 0, + backgroundPanY: 0 + } + ] as Keyframe[], + 320, + 'fill', + 1920, + 1080, + 1920, + 1080 + ); + + expect(filter).toContain('[1:v]setpts=PTS-STARTPTS,hflip,split[cam1][cam2]'); + expect(filter).toContain('[with_pip][camfull]overlay=0:0:format=auto[out]'); + }); + test('buildFilterComplex scales screen into the editor canvas', () => { const filter = buildFilterComplex( [ From 1622e8529d799313952e01b6f79e531e3b67035f Mon Sep 17 00:00:00 2001 From: Tadas Petra <60107328+tadaspetra@users.noreply.github.com> Date: Sun, 29 Mar 2026 20:33:48 -0500 Subject: [PATCH 4/5] prettier --- src/renderer/app.ts | 5 +---- src/renderer/features/camera/camera-render.ts | 12 +++++++++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/renderer/app.ts b/src/renderer/app.ts index 0f66e54..27c05a6 100644 --- a/src/renderer/app.ts +++ b/src/renderer/app.ts @@ -37,10 +37,7 @@ import { shouldRenderPreviewFrame, createCameraRecordingStream } from './features/recording/recorder-utils'; -import { - drawMirroredImage, - getCenteredSquareCropRect -} from './features/camera/camera-render'; +import { drawMirroredImage, getCenteredSquareCropRect } from './features/camera/camera-render'; import { cleanupAllMedia } from './features/media-cleanup'; const projectHomeView = document.getElementById('projectHomeView'); diff --git a/src/renderer/features/camera/camera-render.ts b/src/renderer/features/camera/camera-render.ts index b87f46f..3d7013b 100644 --- a/src/renderer/features/camera/camera-render.ts +++ b/src/renderer/features/camera/camera-render.ts @@ -45,6 +45,16 @@ export function drawMirroredImage( targetCtx.save(); targetCtx.translate(destX + destWidth, destY); targetCtx.scale(-1, 1); - targetCtx.drawImage(source, sourceX, sourceY, sourceWidth, sourceHeight, 0, 0, destWidth, destHeight); + targetCtx.drawImage( + source, + sourceX, + sourceY, + sourceWidth, + sourceHeight, + 0, + 0, + destWidth, + destHeight + ); targetCtx.restore(); } From 3de2667204f766678c44246c0746d730098d928e Mon Sep 17 00:00:00 2001 From: Tadas Petra <60107328+tadaspetra@users.noreply.github.com> Date: Sun, 29 Mar 2026 20:44:32 -0500 Subject: [PATCH 5/5] fix ci --- AGENTS.md | 3 ++ scripts/package-smoke-options.mjs | 27 +++++++++++++ scripts/package-smoke.mjs | 20 ++-------- tests/unit/package-smoke-options.test.mjs | 46 +++++++++++++++++++++++ vitest.config.mjs | 2 +- 5 files changed, 80 insertions(+), 18 deletions(-) create mode 100644 scripts/package-smoke-options.mjs create mode 100644 tests/unit/package-smoke-options.test.mjs diff --git a/AGENTS.md b/AGENTS.md index 14e64ec..f5a561d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -237,3 +237,6 @@ Default to: - Very long timelines with dense camera-overlay keyframes can produce extremely nested ffmpeg filter expressions that exceed parser limits and fail render; the overlay graph needs to stay bounded for long sessions. - `pnpm dev` runs the full build pipeline (clean, styles, main/preload compile, renderer bundle) then launches Electron; it is not a lightweight watch-only dev server unless that workflow is added separately. - Heavy H.264 screen exports (high resolution, high quality, dense UI detail) can stress QuickTime playback more than NLEs; explicit yuv420p and a compatible profile/level—and avoiding unnecessary output resolution—improves default macOS playback. +- `@electron/packager` dependency pruning can miss transitive packages under `pnpm`’s symlinked `node_modules` layout (fresh CI installs surface this); the packaging smoke harness avoids relying on that prune path. +- The Scribe live-transcription `AudioWorklet` (`audio-processor`) must load as a plain browser worklet script; compiling it with the main-process TypeScript target (e.g. CommonJS/NodeNext) breaks loading (`exports` undefined) and silently disables transcription-driven features. +- `src/renderer/styles/main.css` is Tailwind build output (`build:styles`, minified); full `check` rebuilds styles via nested steps, so tracking a non-matching formatted copy causes perpetual diffs—prefer generating locally and not treating it as hand-edited source. diff --git a/scripts/package-smoke-options.mjs b/scripts/package-smoke-options.mjs new file mode 100644 index 0000000..361488b --- /dev/null +++ b/scripts/package-smoke-options.mjs @@ -0,0 +1,27 @@ +export function createPackagerOptions({ + projectRoot, + outDir, + platform = process.platform, + arch = process.arch +}) { + return { + dir: projectRoot, + out: outDir, + overwrite: true, + platform, + arch, + // electron-packager pruning does not support pnpm's symlinked layout reliably. + // Keep node_modules intact so packaging smoke verifies packager startup instead + // of failing on known dependency-walk issues in CI. + prune: false, + quiet: true, + ignore: [ + /^\/tests($|\/)/, + /^\/docs($|\/)/, + /^\/coverage($|\/)/, + /^\/\.github($|\/)/, + /^\/tmp($|\/)/, + /^\/dist-smoke($|\/)/ + ] + }; +} diff --git a/scripts/package-smoke.mjs b/scripts/package-smoke.mjs index d7dda7e..44b122e 100644 --- a/scripts/package-smoke.mjs +++ b/scripts/package-smoke.mjs @@ -4,6 +4,8 @@ import { fileURLToPath } from 'node:url'; import * as packagerModule from '@electron/packager'; +import { createPackagerOptions } from './package-smoke-options.mjs'; + const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const projectRoot = path.resolve(__dirname, '..'); @@ -14,23 +16,7 @@ async function run() { await fs.rm(outDir, { recursive: true, force: true }); - await packager({ - dir: projectRoot, - out: outDir, - overwrite: true, - platform: process.platform, - arch: process.arch, - prune: true, - quiet: true, - ignore: [ - /^\/tests($|\/)/, - /^\/docs($|\/)/, - /^\/coverage($|\/)/, - /^\/\.github($|\/)/, - /^\/tmp($|\/)/, - /^\/dist-smoke($|\/)/ - ] - }); + await packager(createPackagerOptions({ projectRoot, outDir })); await fs.rm(outDir, { recursive: true, force: true }); console.log('Packaging smoke succeeded'); diff --git a/tests/unit/package-smoke-options.test.mjs b/tests/unit/package-smoke-options.test.mjs new file mode 100644 index 0000000..9f1f103 --- /dev/null +++ b/tests/unit/package-smoke-options.test.mjs @@ -0,0 +1,46 @@ +import { describe, expect, test } from 'vitest'; + +import { createPackagerOptions } from '../../scripts/package-smoke-options.mjs'; + +describe('scripts/package-smoke-options', () => { + test('disables pruning so packaging smoke works with pnpm installs', () => { + const options = createPackagerOptions({ + projectRoot: '/tmp/loop', + outDir: '/tmp/loop/dist-smoke', + platform: 'linux', + arch: 'x64' + }); + + expect(options).toMatchObject({ + dir: '/tmp/loop', + out: '/tmp/loop/dist-smoke', + overwrite: true, + platform: 'linux', + arch: 'x64', + prune: false, + quiet: true + }); + }); + + test('ignores non-package inputs and the smoke output directory', () => { + const options = createPackagerOptions({ + projectRoot: '/tmp/loop', + outDir: '/tmp/loop/dist-smoke' + }); + + const ignoredPaths = [ + '/tests/smoke.test.ts', + '/docs/runbook.md', + '/coverage/index.html', + '/.github/workflows/ci.yml', + '/tmp/cache.json', + '/dist-smoke/Loop-darwin-x64' + ]; + + for (const ignoredPath of ignoredPaths) { + expect(options.ignore.some((pattern) => pattern.test(ignoredPath))).toBe(true); + } + + expect(options.ignore.some((pattern) => pattern.test('/src/main.ts'))).toBe(false); + }); +}); diff --git a/vitest.config.mjs b/vitest.config.mjs index 60bbe38..64eb71a 100644 --- a/vitest.config.mjs +++ b/vitest.config.mjs @@ -8,7 +8,7 @@ export default defineConfig({ test: { globals: true, environment: 'node', - include: ['tests/**/*.test.ts'], + include: ['tests/**/*.test.ts', 'tests/**/*.test.mjs'], exclude: ['tests/e2e/**'], coverage: { provider: 'v8',