@bendyline/squisq-editor-react 2.11.17 → 2.11.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-US5DALTZ.js → chunk-5EB3BGPE.js} +2419 -2025
- package/dist/{chunk-QZWFSSDE.js → chunk-DJS5I5NO.js} +1 -1
- package/dist/chunk-DNEVUOBZ.js +14 -0
- package/dist/chunk-DNJ6HOES.js +93 -0
- package/dist/{chunk-DPAMJV6A.js → chunk-HSWWEWT4.js} +31 -2
- package/dist/{chunk-EJKGXWNK.js → chunk-LR6Q2MK4.js} +1 -1
- package/dist/chunk-LRZJD5YD.js +107 -0
- package/dist/{chunk-2XXBDECJ.js → chunk-OCQEWWHI.js} +1 -1
- package/dist/{chunk-HTO5MTTJ.js → chunk-TZX7RKZV.js} +1107 -130
- package/dist/{chunk-C6QVA6TO.js → chunk-UFK47ZAI.js} +58 -133
- package/dist/{chunk-UBFNLWCN.js → chunk-V2OLZOYJ.js} +2 -2
- package/dist/{chunk-XZVMKR5Q.js → chunk-ZS6G2P57.js} +41 -96
- package/dist/dictationSession-PN3OT5LJ.js +521 -0
- package/dist/index.d.ts +30 -11
- package/dist/index.js +53 -21
- package/dist/json-editor/index.js +3 -3
- package/dist/recorder/index.d.ts +2 -2
- package/dist/recorder/index.js +17 -13
- package/dist/{recorder-CRsw0eC7.d.ts → recorder-DKN3vZPi.d.ts} +1 -1
- package/dist/review/index.js +3 -2
- package/dist/shell/index.d.ts +2 -1
- package/dist/shell/index.js +10 -7
- package/dist/{shell-X0EoQbbN.d.ts → shell-Cje1A3zN.d.ts} +176 -5
- package/dist/speech/index.d.ts +126 -0
- package/dist/speech/index.js +10 -0
- package/dist/styles/index.css +142 -3
- package/dist/teleprompter/index.d.ts +13 -5
- package/dist/teleprompter/index.js +3 -2
- package/dist/{useNarrationStage-vKtk6H-V.d.ts → useNarrationStage-CB8dynUg.d.ts} +15 -1
- package/package.json +10 -6
|
@@ -1,25 +1,35 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_RECORDER_DEVICE_SETTINGS,
|
|
3
|
+
buildRecorderAudioConstraints,
|
|
4
|
+
buildRecorderCameraConstraints,
|
|
5
|
+
buildRecorderScreenAudioConstraints,
|
|
6
|
+
buildRecorderScreenConstraints,
|
|
7
|
+
recorderBitsPerSecond
|
|
8
|
+
} from "./chunk-LRZJD5YD.js";
|
|
1
9
|
import {
|
|
2
10
|
Icon
|
|
3
11
|
} from "./chunk-GS7QWYFT.js";
|
|
4
12
|
import {
|
|
5
13
|
NarrationStage,
|
|
6
|
-
buildFilename,
|
|
7
14
|
buildTimingJson,
|
|
8
15
|
encodeNarrationTimingJson,
|
|
9
16
|
encodeTimingJson,
|
|
10
17
|
formatRecordingBytes,
|
|
11
18
|
requestCameraStream,
|
|
19
|
+
resolveMaxRecordingBytes,
|
|
20
|
+
timingPathFor,
|
|
21
|
+
useNarrationStage,
|
|
22
|
+
useStreamPreview
|
|
23
|
+
} from "./chunk-ZS6G2P57.js";
|
|
24
|
+
import {
|
|
25
|
+
buildFilename,
|
|
12
26
|
requestMicStream,
|
|
13
27
|
resolveFormat,
|
|
14
|
-
resolveMaxRecordingBytes,
|
|
15
28
|
supportsDisplayMedia,
|
|
16
29
|
supportsMediaRecorder,
|
|
17
30
|
supportsSystemAudioCapture,
|
|
18
|
-
supportsUserMedia
|
|
19
|
-
|
|
20
|
-
useNarrationStage,
|
|
21
|
-
useStreamPreview
|
|
22
|
-
} from "./chunk-XZVMKR5Q.js";
|
|
31
|
+
supportsUserMedia
|
|
32
|
+
} from "./chunk-DNJ6HOES.js";
|
|
23
33
|
|
|
24
34
|
// src/recorder/sources/screenRegion.ts
|
|
25
35
|
function validateScreenCaptureRegion(region, width, height) {
|
|
@@ -1707,105 +1717,6 @@ function RecorderDeviceQuickPicks({
|
|
|
1707
1717
|
] });
|
|
1708
1718
|
}
|
|
1709
1719
|
|
|
1710
|
-
// src/recorder/deviceSettings.ts
|
|
1711
|
-
var DEFAULT_RECORDER_DEVICE_SETTINGS = {
|
|
1712
|
-
constraintMode: "ideal",
|
|
1713
|
-
audio: { deviceId: "" },
|
|
1714
|
-
camera: { deviceId: "" },
|
|
1715
|
-
screen: {},
|
|
1716
|
-
screenAudio: {},
|
|
1717
|
-
encoding: {
|
|
1718
|
-
audioMimeType: "",
|
|
1719
|
-
videoMimeType: ""
|
|
1720
|
-
}
|
|
1721
|
-
};
|
|
1722
|
-
function preferredNumber(value, mode) {
|
|
1723
|
-
if (value === void 0 || !Number.isFinite(value) || value <= 0) return void 0;
|
|
1724
|
-
return { [mode]: value };
|
|
1725
|
-
}
|
|
1726
|
-
function preferredInteger(value, mode) {
|
|
1727
|
-
if (value === void 0 || !Number.isFinite(value) || value <= 0) return void 0;
|
|
1728
|
-
return { [mode]: Math.round(value) };
|
|
1729
|
-
}
|
|
1730
|
-
function preferredString(value, mode) {
|
|
1731
|
-
const trimmed = value?.trim();
|
|
1732
|
-
return trimmed ? { [mode]: trimmed } : void 0;
|
|
1733
|
-
}
|
|
1734
|
-
function buildRecorderAudioConstraints(settings) {
|
|
1735
|
-
const { audio, constraintMode } = settings;
|
|
1736
|
-
const result = {};
|
|
1737
|
-
if (audio.deviceId) result.deviceId = { exact: audio.deviceId };
|
|
1738
|
-
const groupId = preferredString(audio.groupId, constraintMode);
|
|
1739
|
-
if (groupId) result.groupId = groupId;
|
|
1740
|
-
if (audio.autoGainControl !== void 0) result.autoGainControl = audio.autoGainControl;
|
|
1741
|
-
if (audio.echoCancellation !== void 0) result.echoCancellation = audio.echoCancellation;
|
|
1742
|
-
if (audio.noiseSuppression !== void 0) result.noiseSuppression = audio.noiseSuppression;
|
|
1743
|
-
const channelCount = preferredInteger(audio.channelCount, constraintMode);
|
|
1744
|
-
const latency = preferredNumber(audio.latency, constraintMode);
|
|
1745
|
-
const sampleRate = preferredInteger(audio.sampleRate, constraintMode);
|
|
1746
|
-
const sampleSize = preferredInteger(audio.sampleSize, constraintMode);
|
|
1747
|
-
if (channelCount) result.channelCount = channelCount;
|
|
1748
|
-
if (latency) result.latency = latency;
|
|
1749
|
-
if (sampleRate) result.sampleRate = sampleRate;
|
|
1750
|
-
if (sampleSize) result.sampleSize = sampleSize;
|
|
1751
|
-
return result;
|
|
1752
|
-
}
|
|
1753
|
-
function buildRecorderCameraConstraints(settings) {
|
|
1754
|
-
const { camera, constraintMode } = settings;
|
|
1755
|
-
const result = {};
|
|
1756
|
-
if (camera.deviceId) result.deviceId = { exact: camera.deviceId };
|
|
1757
|
-
const groupId = preferredString(camera.groupId, constraintMode);
|
|
1758
|
-
if (groupId) result.groupId = groupId;
|
|
1759
|
-
if (camera.backgroundBlur !== void 0) result.backgroundBlur = camera.backgroundBlur;
|
|
1760
|
-
const aspectRatio = preferredNumber(camera.aspectRatio, constraintMode);
|
|
1761
|
-
const facingMode = preferredString(camera.facingMode, constraintMode);
|
|
1762
|
-
const frameRate = preferredNumber(camera.frameRate, constraintMode);
|
|
1763
|
-
const height = preferredInteger(camera.height, constraintMode);
|
|
1764
|
-
const resizeMode = preferredString(camera.resizeMode, constraintMode);
|
|
1765
|
-
const width = preferredInteger(camera.width, constraintMode);
|
|
1766
|
-
if (aspectRatio) result.aspectRatio = aspectRatio;
|
|
1767
|
-
if (facingMode) result.facingMode = facingMode;
|
|
1768
|
-
if (frameRate) result.frameRate = frameRate;
|
|
1769
|
-
if (height) result.height = height;
|
|
1770
|
-
if (resizeMode) result.resizeMode = resizeMode;
|
|
1771
|
-
if (width) result.width = width;
|
|
1772
|
-
return result;
|
|
1773
|
-
}
|
|
1774
|
-
function buildRecorderScreenConstraints(settings) {
|
|
1775
|
-
const { screen } = settings;
|
|
1776
|
-
const result = {};
|
|
1777
|
-
const aspectRatio = preferredNumber(screen.aspectRatio, "ideal");
|
|
1778
|
-
const cursor = preferredString(screen.cursor, "ideal");
|
|
1779
|
-
const displaySurface = preferredString(screen.displaySurface, "ideal");
|
|
1780
|
-
const frameRate = preferredNumber(screen.frameRate, "ideal");
|
|
1781
|
-
const height = preferredInteger(screen.height, "ideal");
|
|
1782
|
-
const resizeMode = preferredString(screen.resizeMode, "ideal");
|
|
1783
|
-
const width = preferredInteger(screen.width, "ideal");
|
|
1784
|
-
if (screen.logicalSurface !== void 0) result.logicalSurface = screen.logicalSurface;
|
|
1785
|
-
if (aspectRatio) result.aspectRatio = aspectRatio;
|
|
1786
|
-
if (cursor) result.cursor = cursor;
|
|
1787
|
-
if (displaySurface) result.displaySurface = displaySurface;
|
|
1788
|
-
if (frameRate) result.frameRate = frameRate;
|
|
1789
|
-
if (height) result.height = height;
|
|
1790
|
-
if (resizeMode) result.resizeMode = resizeMode;
|
|
1791
|
-
if (width) result.width = width;
|
|
1792
|
-
return result;
|
|
1793
|
-
}
|
|
1794
|
-
function buildRecorderScreenAudioConstraints(settings) {
|
|
1795
|
-
const result = {};
|
|
1796
|
-
if (settings.screenAudio.restrictOwnAudio !== void 0) {
|
|
1797
|
-
result.restrictOwnAudio = settings.screenAudio.restrictOwnAudio;
|
|
1798
|
-
}
|
|
1799
|
-
if (settings.screenAudio.suppressLocalAudioPlayback !== void 0) {
|
|
1800
|
-
result.suppressLocalAudioPlayback = settings.screenAudio.suppressLocalAudioPlayback;
|
|
1801
|
-
}
|
|
1802
|
-
return result;
|
|
1803
|
-
}
|
|
1804
|
-
function recorderBitsPerSecond(value) {
|
|
1805
|
-
if (value === void 0 || !Number.isFinite(value) || value <= 0) return void 0;
|
|
1806
|
-
return Math.round(value * 1e3);
|
|
1807
|
-
}
|
|
1808
|
-
|
|
1809
1720
|
// src/recorder/recordedMediaKind.ts
|
|
1810
1721
|
function recordedMediaKind(source, stream, mimeType) {
|
|
1811
1722
|
if (stream) return stream.getVideoTracks().length > 0 ? "video" : "audio";
|
|
@@ -2701,6 +2612,7 @@ function RecorderModal({
|
|
|
2701
2612
|
const narrationRecording = narrationOn && Boolean(narration?.recording);
|
|
2702
2613
|
const expanded = isExpandedPanel(panelMode);
|
|
2703
2614
|
const [narrationRequesting, setNarrationRequesting] = useState3(false);
|
|
2615
|
+
const [countingIn, setCountingIn] = useState3(false);
|
|
2704
2616
|
const [narrationPreview, setNarrationPreview] = useState3(null);
|
|
2705
2617
|
const [deviceSettings, setDeviceSettings] = useState3(() => ({
|
|
2706
2618
|
...DEFAULT_RECORDER_DEVICE_SETTINGS,
|
|
@@ -3016,17 +2928,27 @@ function RecorderModal({
|
|
|
3016
2928
|
} catch {
|
|
3017
2929
|
}
|
|
3018
2930
|
}, [recorder]);
|
|
3019
|
-
const handleStart = useCallback4(() => {
|
|
2931
|
+
const handleStart = useCallback4(async () => {
|
|
3020
2932
|
setSaveError(null);
|
|
3021
2933
|
dualSaveProgressRef.current = {};
|
|
2934
|
+
if (narrationOn) {
|
|
2935
|
+
setCountingIn(true);
|
|
2936
|
+
const rolled = await stageRef.current.controller.countIn();
|
|
2937
|
+
setCountingIn(false);
|
|
2938
|
+
if (!rolled) return;
|
|
2939
|
+
}
|
|
3022
2940
|
recorder.start();
|
|
3023
2941
|
const blockId = slideDeckRef.current[slideIndexRef.current]?.blockId;
|
|
3024
2942
|
setAdvanceLog(blockId ? recordSlideShown(EMPTY_ADVANCE_LOG, blockId, 0) : EMPTY_ADVANCE_LOG);
|
|
3025
|
-
}, [recorder]);
|
|
2943
|
+
}, [narrationOn, recorder]);
|
|
3026
2944
|
const handleStop = useCallback4(async () => {
|
|
3027
2945
|
setSaveError(null);
|
|
2946
|
+
if (countingIn) {
|
|
2947
|
+
stageRef.current.controller.pause();
|
|
2948
|
+
return;
|
|
2949
|
+
}
|
|
3028
2950
|
await recorder.stop();
|
|
3029
|
-
}, [recorder]);
|
|
2951
|
+
}, [countingIn, recorder]);
|
|
3030
2952
|
const writeSlideTimingSidecar = useCallback4(
|
|
3031
2953
|
async (mediaRelativePath, durationSec) => {
|
|
3032
2954
|
const doc = slides?.doc;
|
|
@@ -3219,8 +3141,8 @@ function RecorderModal({
|
|
|
3219
3141
|
);
|
|
3220
3142
|
const isAudioOnly = recordedMediaKind(source, recorder.stream, recorder.mimeType) === "audio";
|
|
3221
3143
|
const showPreview = recorder.state !== "idle" && recorder.state !== "error";
|
|
3222
|
-
const canRecord = recorder.state === "ready";
|
|
3223
|
-
const canStop = recorder.state === "recording";
|
|
3144
|
+
const canRecord = recorder.state === "ready" && !countingIn;
|
|
3145
|
+
const canStop = recorder.state === "recording" || countingIn;
|
|
3224
3146
|
const canSave = recorder.state === "stopped" && recorder.blob !== null;
|
|
3225
3147
|
const isBusy = recorder.state === "requesting" || recorder.state === "stopping" || isSaving;
|
|
3226
3148
|
const slidesTimingCapture = slidesOn && slidesCaptureTimings;
|
|
@@ -3274,7 +3196,7 @@ function RecorderModal({
|
|
|
3274
3196
|
sourceText
|
|
3275
3197
|
]);
|
|
3276
3198
|
const slideCoverageWarning = slideCoverage ? unshownSlidesWarning(slideCoverage.total, slideCoverage.unshown) : null;
|
|
3277
|
-
const togglesLocked = recorder.state === "recording" || recorder.state === "requesting" || recorder.state === "stopping" || canSave;
|
|
3199
|
+
const togglesLocked = countingIn || recorder.state === "recording" || recorder.state === "requesting" || recorder.state === "stopping" || canSave;
|
|
3278
3200
|
const toggleLockReason = canSave ? "Save or discard this recording before changing sources" : void 0;
|
|
3279
3201
|
const systemAudioHasCompanion = micOn || cameraOn || screenOn;
|
|
3280
3202
|
const simpleToggleProps = (key) => {
|
|
@@ -3769,24 +3691,33 @@ function RecorderModal({
|
|
|
3769
3691
|
children: recorder.state === "requesting" ? "Requesting\u2026" : "Start preview"
|
|
3770
3692
|
}
|
|
3771
3693
|
),
|
|
3772
|
-
canRecord && /* @__PURE__ */ jsxs5(
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3694
|
+
canRecord && /* @__PURE__ */ jsxs5(
|
|
3695
|
+
"button",
|
|
3696
|
+
{
|
|
3697
|
+
type: "button",
|
|
3698
|
+
style: btnRecord,
|
|
3699
|
+
onClick: () => void handleStart(),
|
|
3700
|
+
disabled: isBusy,
|
|
3701
|
+
children: [
|
|
3702
|
+
/* @__PURE__ */ jsx6(
|
|
3780
3703
|
"span",
|
|
3781
3704
|
{
|
|
3782
|
-
className: "squisq-recorder-record-dot
|
|
3783
|
-
style:
|
|
3705
|
+
className: "squisq-recorder-record-dot",
|
|
3706
|
+
style: recordDotFrameStyle,
|
|
3707
|
+
"aria-hidden": "true",
|
|
3708
|
+
children: /* @__PURE__ */ jsx6(
|
|
3709
|
+
"span",
|
|
3710
|
+
{
|
|
3711
|
+
className: "squisq-recorder-record-dot-center",
|
|
3712
|
+
style: recordDotStyle
|
|
3713
|
+
}
|
|
3714
|
+
)
|
|
3784
3715
|
}
|
|
3785
|
-
)
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3716
|
+
),
|
|
3717
|
+
"Record"
|
|
3718
|
+
]
|
|
3719
|
+
}
|
|
3720
|
+
),
|
|
3790
3721
|
canStop && /* @__PURE__ */ jsx6("button", { type: "button", style: btnDanger, onClick: handleStop, disabled: isBusy, children: "Stop" }),
|
|
3791
3722
|
canSave && /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
3792
3723
|
/* @__PURE__ */ jsx6(
|
|
@@ -3922,12 +3853,6 @@ export {
|
|
|
3922
3853
|
useMediaDevices,
|
|
3923
3854
|
RecorderDeviceSettingsPanel,
|
|
3924
3855
|
RecorderDeviceQuickPicks,
|
|
3925
|
-
DEFAULT_RECORDER_DEVICE_SETTINGS,
|
|
3926
|
-
buildRecorderAudioConstraints,
|
|
3927
|
-
buildRecorderCameraConstraints,
|
|
3928
|
-
buildRecorderScreenConstraints,
|
|
3929
|
-
buildRecorderScreenAudioConstraints,
|
|
3930
|
-
recorderBitsPerSecond,
|
|
3931
3856
|
recordedMediaKind,
|
|
3932
3857
|
isExpandedPanel,
|
|
3933
3858
|
panelModeAfterToggle,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
MarkdownEscape
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-OCQEWWHI.js";
|
|
4
4
|
import {
|
|
5
5
|
markdownToTiptap,
|
|
6
6
|
tiptapToMarkdown
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-HSWWEWT4.js";
|
|
8
8
|
|
|
9
9
|
// src/jsonEditor/JsonEditorContext.tsx
|
|
10
10
|
import { createContext, useContext, useEffect, useMemo } from "react";
|
|
@@ -1,86 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
"audio/ogg;codecs=opus"
|
|
8
|
-
];
|
|
9
|
-
var VIDEO_CANDIDATES = [
|
|
10
|
-
"video/webm;codecs=vp9,opus",
|
|
11
|
-
"video/webm;codecs=vp8,opus",
|
|
12
|
-
"video/webm",
|
|
13
|
-
"video/mp4;codecs=avc1.42E01E,mp4a.40.2",
|
|
14
|
-
"video/mp4"
|
|
15
|
-
];
|
|
16
|
-
function extensionForMime(mimeType) {
|
|
17
|
-
const m = mimeType.toLowerCase();
|
|
18
|
-
if (m.startsWith("audio/webm")) return ".webm";
|
|
19
|
-
if (m.startsWith("audio/ogg")) return ".ogg";
|
|
20
|
-
if (m.startsWith("audio/mp4")) return ".m4a";
|
|
21
|
-
if (m.startsWith("audio/mpeg")) return ".mp3";
|
|
22
|
-
if (m.startsWith("audio/wav")) return ".wav";
|
|
23
|
-
if (m.startsWith("video/webm")) return ".webm";
|
|
24
|
-
if (m.startsWith("video/mp4")) return ".mp4";
|
|
25
|
-
return ".bin";
|
|
26
|
-
}
|
|
27
|
-
function probeMimeType(candidates) {
|
|
28
|
-
if (typeof MediaRecorder === "undefined") return null;
|
|
29
|
-
for (const candidate of candidates) {
|
|
30
|
-
try {
|
|
31
|
-
if (MediaRecorder.isTypeSupported(candidate)) return candidate;
|
|
32
|
-
} catch {
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
function resolveFormat(kind, preferred) {
|
|
38
|
-
const candidates = kind === "audio" ? AUDIO_CANDIDATES : VIDEO_CANDIDATES;
|
|
39
|
-
const probed = (preferred && probeMimeType([preferred])) ?? probeMimeType(candidates) ?? "";
|
|
40
|
-
const directory = kind === "audio" ? "audio" : "video";
|
|
41
|
-
const extension = probed ? extensionForMime(probed) : ".webm";
|
|
42
|
-
return { mimeType: probed, extension, directory };
|
|
43
|
-
}
|
|
44
|
-
function supportsMediaRecorder() {
|
|
45
|
-
return typeof MediaRecorder !== "undefined";
|
|
46
|
-
}
|
|
47
|
-
function supportsUserMedia() {
|
|
48
|
-
return typeof navigator !== "undefined" && typeof navigator.mediaDevices !== "undefined" && typeof navigator.mediaDevices.getUserMedia === "function";
|
|
49
|
-
}
|
|
50
|
-
function supportsDisplayMedia() {
|
|
51
|
-
return typeof navigator !== "undefined" && typeof navigator.mediaDevices !== "undefined" && typeof navigator.mediaDevices.getDisplayMedia === "function";
|
|
52
|
-
}
|
|
53
|
-
function supportsSystemAudioCapture() {
|
|
54
|
-
if (!supportsDisplayMedia()) return false;
|
|
55
|
-
const nav = navigator;
|
|
56
|
-
const uaData = nav.userAgentData;
|
|
57
|
-
if (uaData?.mobile === true) return false;
|
|
58
|
-
if (uaData?.brands?.length) {
|
|
59
|
-
return uaData.brands.some(({ brand }) => /chromium/i.test(brand));
|
|
60
|
-
}
|
|
61
|
-
const userAgent = nav.userAgent ?? "";
|
|
62
|
-
if (/Android|Mobile|iPhone|iPad|iPod/i.test(userAgent)) return false;
|
|
63
|
-
return /(?:Chrome|Chromium|Edg|OPR)\//.test(userAgent);
|
|
64
|
-
}
|
|
65
|
-
function buildFilename(kind, extension, basename, seed) {
|
|
66
|
-
const safe = basename ? basename.trim().replace(/[\\/:*?"<>|]+/g, "-").replace(/\s+/g, "-") : "";
|
|
67
|
-
if (safe) return `${safe}${extension}`;
|
|
68
|
-
const now = /* @__PURE__ */ new Date();
|
|
69
|
-
const stamp = now.getFullYear().toString().padStart(4, "0") + (now.getMonth() + 1).toString().padStart(2, "0") + now.getDate().toString().padStart(2, "0") + "-" + now.getHours().toString().padStart(2, "0") + now.getMinutes().toString().padStart(2, "0") + now.getSeconds().toString().padStart(2, "0");
|
|
70
|
-
const prefix = seed ?? (kind === "audio" ? "narration" : "recording");
|
|
71
|
-
return `${prefix}-${stamp}${extension}`;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// src/recorder/sources/micStream.ts
|
|
75
|
-
async function requestMicStream(constraints) {
|
|
76
|
-
if (!supportsUserMedia()) {
|
|
77
|
-
throw new Error("navigator.mediaDevices.getUserMedia is not available in this environment.");
|
|
78
|
-
}
|
|
79
|
-
return navigator.mediaDevices.getUserMedia({
|
|
80
|
-
audio: constraints ?? true,
|
|
81
|
-
video: false
|
|
82
|
-
});
|
|
83
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
buildFilename,
|
|
3
|
+
requestMicStream,
|
|
4
|
+
resolveFormat,
|
|
5
|
+
supportsUserMedia
|
|
6
|
+
} from "./chunk-DNJ6HOES.js";
|
|
84
7
|
|
|
85
8
|
// src/recorder/sources/cameraStream.ts
|
|
86
9
|
async function requestCameraStream(options) {
|
|
@@ -506,6 +429,7 @@ function useTeleprompter(opts) {
|
|
|
506
429
|
const levelRef = useRef2(0);
|
|
507
430
|
const voiceRef = useRef2(false);
|
|
508
431
|
const countdownTimerRef = useRef2(null);
|
|
432
|
+
const countInWaitersRef = useRef2([]);
|
|
509
433
|
scriptRef.current = script;
|
|
510
434
|
prefsRef.current = prefs;
|
|
511
435
|
transportRef.current = transport;
|
|
@@ -530,13 +454,19 @@ function useTeleprompter(opts) {
|
|
|
530
454
|
const notifyTick = useCallback2(() => {
|
|
531
455
|
for (const cb of tickSubsRef.current) cb(wordPosRef.current);
|
|
532
456
|
}, []);
|
|
457
|
+
const settleCountIn = useCallback2((rolled) => {
|
|
458
|
+
const waiters = countInWaitersRef.current;
|
|
459
|
+
countInWaitersRef.current = [];
|
|
460
|
+
for (const resolve of waiters) resolve(rolled);
|
|
461
|
+
}, []);
|
|
533
462
|
const clearCountdown = useCallback2(() => {
|
|
534
463
|
if (countdownTimerRef.current !== null) {
|
|
535
464
|
clearInterval(countdownTimerRef.current);
|
|
536
465
|
countdownTimerRef.current = null;
|
|
537
466
|
}
|
|
538
467
|
setCountdownRemaining(null);
|
|
539
|
-
|
|
468
|
+
settleCountIn(false);
|
|
469
|
+
}, [settleCountIn]);
|
|
540
470
|
const finish = useCallback2(() => {
|
|
541
471
|
setTransport("finished");
|
|
542
472
|
transportRef.current = "finished";
|
|
@@ -610,13 +540,14 @@ function useTeleprompter(opts) {
|
|
|
610
540
|
return () => cancelAnimationFrame(raf);
|
|
611
541
|
}, [transport, mic.status, finish, notifyTick, publish]);
|
|
612
542
|
const beginRolling = useCallback2(() => {
|
|
543
|
+
settleCountIn(true);
|
|
613
544
|
clearCountdown();
|
|
614
545
|
if (sessionRef.current) {
|
|
615
546
|
sessionRef.current = reanchorSession(sessionRef.current, wordPosRef.current);
|
|
616
547
|
}
|
|
617
548
|
setTransport("rolling");
|
|
618
549
|
transportRef.current = "rolling";
|
|
619
|
-
}, [clearCountdown]);
|
|
550
|
+
}, [clearCountdown, settleCountIn]);
|
|
620
551
|
const play = useCallback2(() => {
|
|
621
552
|
if (transportRef.current === "rolling" || transportRef.current === "countdown") return;
|
|
622
553
|
if (transportRef.current === "finished") {
|
|
@@ -641,6 +572,14 @@ function useTeleprompter(opts) {
|
|
|
641
572
|
beginRolling();
|
|
642
573
|
}
|
|
643
574
|
}, [beginRolling, mic, publish]);
|
|
575
|
+
const countIn = useCallback2(() => {
|
|
576
|
+
if (transportRef.current === "rolling") return Promise.resolve(true);
|
|
577
|
+
const rolled = new Promise((resolve) => {
|
|
578
|
+
countInWaitersRef.current.push(resolve);
|
|
579
|
+
});
|
|
580
|
+
play();
|
|
581
|
+
return rolled;
|
|
582
|
+
}, [play]);
|
|
644
583
|
const pause = useCallback2(() => {
|
|
645
584
|
if (transportRef.current === "countdown") {
|
|
646
585
|
clearCountdown();
|
|
@@ -776,6 +715,7 @@ function useTeleprompter(opts) {
|
|
|
776
715
|
prefs,
|
|
777
716
|
setPrefs,
|
|
778
717
|
play,
|
|
718
|
+
countIn,
|
|
779
719
|
pause,
|
|
780
720
|
restart,
|
|
781
721
|
nudge,
|
|
@@ -1527,6 +1467,11 @@ function useNarrationRecorder(options) {
|
|
|
1527
1467
|
cameraMime = videoFormat.mimeType;
|
|
1528
1468
|
cameraExt = videoFormat.extension;
|
|
1529
1469
|
}
|
|
1470
|
+
if (opts.countIn) {
|
|
1471
|
+
setCameraStream(camera);
|
|
1472
|
+
if (!await opts.countIn()) throw new StartAborted();
|
|
1473
|
+
if (superseded()) throw new StartAborted();
|
|
1474
|
+
}
|
|
1530
1475
|
const capture = {
|
|
1531
1476
|
audioRecorder,
|
|
1532
1477
|
audioChunks: [],
|
|
@@ -1604,6 +1549,7 @@ function useNarrationRecorder(options) {
|
|
|
1604
1549
|
if (!capture) {
|
|
1605
1550
|
if (startingRef.current) {
|
|
1606
1551
|
cancelPendingStart();
|
|
1552
|
+
optionsRef.current.onRecordingStop?.();
|
|
1607
1553
|
setState("idle");
|
|
1608
1554
|
}
|
|
1609
1555
|
return;
|
|
@@ -1793,11 +1739,16 @@ function insertNarrationPreamble(source, audioPath, cameraPath) {
|
|
|
1793
1739
|
|
|
1794
1740
|
// src/teleprompter/recording/narrationSave.ts
|
|
1795
1741
|
import {
|
|
1796
|
-
buildNarrationTimingJson
|
|
1742
|
+
buildNarrationTimingJson,
|
|
1743
|
+
resolveNarrationTimingGenerator
|
|
1797
1744
|
} from "@bendyline/squisq/narration";
|
|
1798
1745
|
function buildNarrationSavePlan(args) {
|
|
1746
|
+
const timingOptions = {
|
|
1747
|
+
...args.baseWpm !== void 0 ? { baseWpm: args.baseWpm } : {},
|
|
1748
|
+
...args.generator ? { generator: args.generator } : {}
|
|
1749
|
+
};
|
|
1799
1750
|
const sidecarPayload = args.alignment ? buildNarrationTimingJson(args.script, args.alignment, args.durationSec, {
|
|
1800
|
-
|
|
1751
|
+
...timingOptions,
|
|
1801
1752
|
...args.cameraOffsetSec !== void 0 ? { cameraOffsetSec: args.cameraOffsetSec } : {}
|
|
1802
1753
|
}) : {
|
|
1803
1754
|
version: 3,
|
|
@@ -1805,7 +1756,7 @@ function buildNarrationSavePlan(args) {
|
|
|
1805
1756
|
duration: args.durationSec,
|
|
1806
1757
|
bookmarks: [],
|
|
1807
1758
|
blocks: [],
|
|
1808
|
-
generator:
|
|
1759
|
+
generator: resolveNarrationTimingGenerator(timingOptions)
|
|
1809
1760
|
};
|
|
1810
1761
|
return {
|
|
1811
1762
|
audioRelativeName: `audio/${buildFilename("audio", args.audioExt, args.audioBasename)}`,
|
|
@@ -1898,7 +1849,8 @@ function useNarrationStage(opts) {
|
|
|
1898
1849
|
maxRecordingBytes: opts.maxRecordingBytes,
|
|
1899
1850
|
audioRecorderOptions: opts.audioRecorderOptions,
|
|
1900
1851
|
cameraRecorderOptions: opts.cameraRecorderOptions,
|
|
1901
|
-
|
|
1852
|
+
// The take starts when the countdown ends, with the prompter already rolling.
|
|
1853
|
+
countIn: () => controllerRef.current.countIn(),
|
|
1902
1854
|
onRecordingStop: () => controllerRef.current.pause()
|
|
1903
1855
|
});
|
|
1904
1856
|
const recorderRef = useRef5(recorder);
|
|
@@ -2874,13 +2826,6 @@ function NarrationStage(props) {
|
|
|
2874
2826
|
export {
|
|
2875
2827
|
resolveMaxRecordingBytes,
|
|
2876
2828
|
formatRecordingBytes,
|
|
2877
|
-
resolveFormat,
|
|
2878
|
-
supportsMediaRecorder,
|
|
2879
|
-
supportsUserMedia,
|
|
2880
|
-
supportsDisplayMedia,
|
|
2881
|
-
supportsSystemAudioCapture,
|
|
2882
|
-
buildFilename,
|
|
2883
|
-
requestMicStream,
|
|
2884
2829
|
requestCameraStream,
|
|
2885
2830
|
useStreamPreview,
|
|
2886
2831
|
buildTimingJson,
|