@bendyline/squisq-editor-react 2.13.22 → 2.13.23
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-JCIK2QU5.js → chunk-2KTMXJWB.js} +1 -1
- package/dist/{chunk-LZO3HOCQ.js → chunk-CBFPGSYR.js} +16525 -14882
- package/dist/{chunk-MMK26QF7.js → chunk-EPMIOQAZ.js} +23 -4
- package/dist/{chunk-GI2D2XS5.js → chunk-IOG66XFC.js} +1 -1
- package/dist/{chunk-5O3RU2HO.js → chunk-KMTQ4M2G.js} +1 -1
- package/dist/{chunk-APTVXGK6.js → chunk-QI5SHUVT.js} +125 -18
- package/dist/{chunk-DNJ6HOES.js → chunk-QXHX7N4V.js} +16 -0
- package/dist/{chunk-7ADGH53Y.js → chunk-RVHUTOHG.js} +1 -1
- package/dist/{dictationSession-AVCXB3JR.js → dictationSession-WEXEY5NS.js} +1 -1
- package/dist/document-parse-worker/index.js +14 -14
- package/dist/index.d.ts +123 -8
- package/dist/index.js +79 -8
- package/dist/recorder/index.d.ts +2 -2
- package/dist/recorder/index.js +4 -4
- package/dist/{recorder-rQGYsS6B.d.ts → recorder-pdsnPhLa.d.ts} +1 -1
- package/dist/review/index.js +1 -1
- package/dist/shell/index.d.ts +1 -1
- package/dist/shell/index.js +6 -6
- package/dist/{shell-CdtncgKy.d.ts → shell-CKXPqADV.d.ts} +24 -3
- package/dist/speech/index.js +1 -1
- package/dist/styles/index.css +374 -13
- package/dist/teleprompter/index.d.ts +2 -2
- package/dist/teleprompter/index.js +3 -3
- package/dist/{useNarrationStage-DlighI1z.d.ts → useNarrationStage-BsSUp9Zr.d.ts} +1 -0
- package/package.json +6 -6
|
@@ -582,12 +582,17 @@ import {
|
|
|
582
582
|
useRef as useRef3,
|
|
583
583
|
useEffect as useEffect3
|
|
584
584
|
} from "react";
|
|
585
|
+
import { useVideoDurations } from "@bendyline/squisq-react";
|
|
585
586
|
import {
|
|
586
587
|
parseMarkdown as parseMarkdown4,
|
|
587
588
|
stringifyMarkdown,
|
|
588
589
|
resolveWikiLinks
|
|
589
590
|
} from "@bendyline/squisq/markdown";
|
|
590
|
-
import {
|
|
591
|
+
import {
|
|
592
|
+
applyVideoDurations,
|
|
593
|
+
collectVideoDurationSources,
|
|
594
|
+
markdownToDoc
|
|
595
|
+
} from "@bendyline/squisq/doc";
|
|
591
596
|
|
|
592
597
|
// src/speech/useDictationController.ts
|
|
593
598
|
import {
|
|
@@ -1022,7 +1027,7 @@ function useDictationController({
|
|
|
1022
1027
|
}
|
|
1023
1028
|
return;
|
|
1024
1029
|
}
|
|
1025
|
-
const { startDictationSession } = await import("./dictationSession-
|
|
1030
|
+
const { startDictationSession } = await import("./dictationSession-WEXEY5NS.js");
|
|
1026
1031
|
if (!isCurrent()) return;
|
|
1027
1032
|
const handle = await startDictationSession({
|
|
1028
1033
|
provider: active,
|
|
@@ -1659,6 +1664,7 @@ function EditorProvider({
|
|
|
1659
1664
|
versioningAutoSaveIdleMs = DEFAULT_AUTOSAVE_IDLE_MS,
|
|
1660
1665
|
onSaveVersion,
|
|
1661
1666
|
mediaProvider = null,
|
|
1667
|
+
mediaBasePath = "/",
|
|
1662
1668
|
mediaEditRenders = null,
|
|
1663
1669
|
imageDisplayMode = "inline",
|
|
1664
1670
|
mentionProvider = null,
|
|
@@ -1679,6 +1685,7 @@ function EditorProvider({
|
|
|
1679
1685
|
fileName,
|
|
1680
1686
|
saveCoverImageOutput,
|
|
1681
1687
|
saveDashboardImageOutput,
|
|
1688
|
+
saveMediaEditOutput,
|
|
1682
1689
|
language,
|
|
1683
1690
|
findMode: controlledFindMode,
|
|
1684
1691
|
onFindModeChange,
|
|
@@ -2037,6 +2044,15 @@ function EditorProvider({
|
|
|
2037
2044
|
cancelled = true;
|
|
2038
2045
|
};
|
|
2039
2046
|
}, [markdownDoc, wikiLinks]);
|
|
2047
|
+
const videoDurationSources = useMemo4(
|
|
2048
|
+
() => presentationDoc ? collectVideoDurationSources(presentationDoc) : [],
|
|
2049
|
+
[presentationDoc]
|
|
2050
|
+
);
|
|
2051
|
+
const videoDurations = useVideoDurations(videoDurationSources, mediaBasePath, mediaProvider);
|
|
2052
|
+
const timedDoc = useMemo4(
|
|
2053
|
+
() => presentationDoc && videoDurations.size > 0 ? applyVideoDurations(presentationDoc, (src) => videoDurations.get(src)) : presentationDoc,
|
|
2054
|
+
[presentationDoc, videoDurations]
|
|
2055
|
+
);
|
|
2040
2056
|
useEffect3(() => {
|
|
2041
2057
|
if (editorMode !== "markdown") return;
|
|
2042
2058
|
if (skipInitialDebouncedParseRef.current) {
|
|
@@ -2396,7 +2412,7 @@ function EditorProvider({
|
|
|
2396
2412
|
() => ({
|
|
2397
2413
|
markdownSource,
|
|
2398
2414
|
markdownDoc,
|
|
2399
|
-
doc:
|
|
2415
|
+
doc: timedDoc,
|
|
2400
2416
|
activeView,
|
|
2401
2417
|
parseError,
|
|
2402
2418
|
isParsing,
|
|
@@ -2451,6 +2467,7 @@ function EditorProvider({
|
|
|
2451
2467
|
fileName,
|
|
2452
2468
|
saveCoverImageOutput,
|
|
2453
2469
|
saveDashboardImageOutput,
|
|
2470
|
+
saveMediaEditOutput,
|
|
2454
2471
|
setMarkdownSource,
|
|
2455
2472
|
setEditorSource,
|
|
2456
2473
|
setLayoutMode,
|
|
@@ -2487,7 +2504,7 @@ function EditorProvider({
|
|
|
2487
2504
|
[
|
|
2488
2505
|
markdownSource,
|
|
2489
2506
|
markdownDoc,
|
|
2490
|
-
|
|
2507
|
+
timedDoc,
|
|
2491
2508
|
activeView,
|
|
2492
2509
|
parseError,
|
|
2493
2510
|
isParsing,
|
|
@@ -2536,6 +2553,7 @@ function EditorProvider({
|
|
|
2536
2553
|
fileName,
|
|
2537
2554
|
saveCoverImageOutput,
|
|
2538
2555
|
saveDashboardImageOutput,
|
|
2556
|
+
saveMediaEditOutput,
|
|
2539
2557
|
setMarkdownSource,
|
|
2540
2558
|
setEditorSource,
|
|
2541
2559
|
setLayoutMode,
|
|
@@ -3174,6 +3192,7 @@ export {
|
|
|
3174
3192
|
resolveEmbeddedMediaTiming,
|
|
3175
3193
|
collectEmbeddedVideoSchedule,
|
|
3176
3194
|
collectTimelinePlaybackSchedule,
|
|
3195
|
+
listEditableMedia,
|
|
3177
3196
|
findEditableMedia,
|
|
3178
3197
|
groupCompanions,
|
|
3179
3198
|
mediaTargetFromElement,
|
|
@@ -16,16 +16,18 @@ import {
|
|
|
16
16
|
resolveMaxRecordingBytes,
|
|
17
17
|
useNarrationStage,
|
|
18
18
|
useStreamPreview
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-2KTMXJWB.js";
|
|
20
20
|
import {
|
|
21
21
|
buildFilename,
|
|
22
|
+
dualFilenameSeeds,
|
|
23
|
+
recordingFilenameSeed,
|
|
22
24
|
requestMicStream,
|
|
23
25
|
resolveFormat,
|
|
24
26
|
supportsDisplayMedia,
|
|
25
27
|
supportsMediaRecorder,
|
|
26
28
|
supportsSystemAudioCapture,
|
|
27
29
|
supportsUserMedia
|
|
28
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-QXHX7N4V.js";
|
|
29
31
|
import {
|
|
30
32
|
buildTimingJson,
|
|
31
33
|
encodeNarrationTimingJson,
|
|
@@ -33,6 +35,113 @@ import {
|
|
|
33
35
|
timingPathFor
|
|
34
36
|
} from "./chunk-GG35EBRB.js";
|
|
35
37
|
|
|
38
|
+
// src/recorder/captureErrors.ts
|
|
39
|
+
var displayCaptureErrors = /* @__PURE__ */ new WeakSet();
|
|
40
|
+
function markDisplayCaptureError(error) {
|
|
41
|
+
if (typeof error === "object" && error !== null) displayCaptureErrors.add(error);
|
|
42
|
+
return error;
|
|
43
|
+
}
|
|
44
|
+
var PRIVACY_HINT = "your system's privacy settings allow apps to use it";
|
|
45
|
+
function errorName(error) {
|
|
46
|
+
return typeof error === "object" && error !== null && "name" in error ? String(error.name) : "";
|
|
47
|
+
}
|
|
48
|
+
function errorMessage(error) {
|
|
49
|
+
if (error instanceof Error) return error.message;
|
|
50
|
+
if (typeof error === "object" && error !== null && "message" in error) {
|
|
51
|
+
return String(error.message);
|
|
52
|
+
}
|
|
53
|
+
return typeof error === "string" ? error : "";
|
|
54
|
+
}
|
|
55
|
+
function failedConstraint(error) {
|
|
56
|
+
return typeof error === "object" && error !== null && "constraint" in error ? String(error.constraint ?? "") : "";
|
|
57
|
+
}
|
|
58
|
+
function countDevices(devices, kind) {
|
|
59
|
+
if (!devices || devices.length === 0) return null;
|
|
60
|
+
return devices.filter((device) => device.kind === kind).length;
|
|
61
|
+
}
|
|
62
|
+
function requestedNoun(context) {
|
|
63
|
+
if (context.camera && context.microphone) return "camera or microphone";
|
|
64
|
+
if (context.camera) return "camera";
|
|
65
|
+
if (context.microphone) return "microphone";
|
|
66
|
+
return "capture device";
|
|
67
|
+
}
|
|
68
|
+
function describeDisplayError(name, message) {
|
|
69
|
+
if (name === "NotAllowedError" || name === "AbortError") {
|
|
70
|
+
return /system/i.test(message) ? "Your system is blocking screen recording. Allow it in your privacy settings, then try again." : "Screen sharing was cancelled. Click Start preview to choose a screen, window, or tab.";
|
|
71
|
+
}
|
|
72
|
+
if (name === "NotFoundError" || name === "NotReadableError") {
|
|
73
|
+
return "The screen could not be captured. Try choosing a different screen, window, or tab.";
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
function describeNotFound(context) {
|
|
78
|
+
const cameras = countDevices(context.devices, "videoinput");
|
|
79
|
+
const microphones = countDevices(context.devices, "audioinput");
|
|
80
|
+
const noCamera = context.camera && cameras === 0;
|
|
81
|
+
const noMicrophone = context.microphone && microphones === 0;
|
|
82
|
+
if (noCamera && noMicrophone) {
|
|
83
|
+
return `No camera or microphone was found. Connect them, then try again. If they are connected, check that they are switched on and that ${PRIVACY_HINT}.`;
|
|
84
|
+
}
|
|
85
|
+
if (noCamera) {
|
|
86
|
+
const fallback = context.microphone ? "Connect a camera, or turn off Camera to record audio only." : "Connect a camera, then try again.";
|
|
87
|
+
return `No camera was found. ${fallback} If a camera is connected, check that it is switched on and that ${PRIVACY_HINT}.`;
|
|
88
|
+
}
|
|
89
|
+
if (noMicrophone) {
|
|
90
|
+
return `No microphone was found. Connect a microphone, then try again. If one is connected, check that it is switched on and that ${PRIVACY_HINT}.`;
|
|
91
|
+
}
|
|
92
|
+
if (context.camera && context.cameraDeviceId) {
|
|
93
|
+
return "The selected camera is not connected. Choose another camera, or System default.";
|
|
94
|
+
}
|
|
95
|
+
if (context.microphone && context.microphoneDeviceId) {
|
|
96
|
+
return "The selected microphone is not connected. Choose another microphone, or System default.";
|
|
97
|
+
}
|
|
98
|
+
const noun = requestedNoun(context);
|
|
99
|
+
return `The ${noun} could not be found. Check that it is connected and switched on, and that ${PRIVACY_HINT}.`;
|
|
100
|
+
}
|
|
101
|
+
function describeOverconstrained(error, context) {
|
|
102
|
+
const constraint = failedConstraint(error);
|
|
103
|
+
if (constraint === "deviceId" || constraint === "groupId") {
|
|
104
|
+
if (context.camera && context.cameraDeviceId) {
|
|
105
|
+
return "The selected camera is not connected. Choose another camera, or System default.";
|
|
106
|
+
}
|
|
107
|
+
if (context.microphone && context.microphoneDeviceId) {
|
|
108
|
+
return "The selected microphone is not connected. Choose another microphone, or System default.";
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const noun = requestedNoun(context);
|
|
112
|
+
const setting = constraint ? ` (${constraint})` : "";
|
|
113
|
+
return `Your ${noun} cannot provide the requested settings${setting}. Clear that value in Advanced device settings, or set Constraint strength to Preferred.`;
|
|
114
|
+
}
|
|
115
|
+
function describeCaptureError(error, context) {
|
|
116
|
+
const name = errorName(error);
|
|
117
|
+
const message = errorMessage(error);
|
|
118
|
+
if (typeof error === "object" && error !== null && displayCaptureErrors.has(error)) {
|
|
119
|
+
const display = describeDisplayError(name, message);
|
|
120
|
+
if (display) return display;
|
|
121
|
+
}
|
|
122
|
+
if (name === "NotFoundError" || /requested device not found/i.test(message)) {
|
|
123
|
+
return describeNotFound(context);
|
|
124
|
+
}
|
|
125
|
+
if (name === "OverconstrainedError") {
|
|
126
|
+
return describeOverconstrained(error, context);
|
|
127
|
+
}
|
|
128
|
+
if (name === "NotAllowedError" || name === "SecurityError") {
|
|
129
|
+
const noun = requestedNoun(context);
|
|
130
|
+
return /system/i.test(message) ? `Your system's privacy settings are blocking the ${noun}. Allow access for this app, then try again.` : `Access to the ${noun} was blocked. Allow it when prompted, or in your browser or system settings, then try again.`;
|
|
131
|
+
}
|
|
132
|
+
if (name === "NotReadableError" || /could not start (video|audio) source/i.test(message)) {
|
|
133
|
+
const noun = /video source/i.test(message) ? "camera" : /audio source/i.test(message) ? "microphone" : requestedNoun(context);
|
|
134
|
+
return `The ${noun} could not start. Another app may be using it: close apps that use the ${noun}, then try again.`;
|
|
135
|
+
}
|
|
136
|
+
if (/getUserMedia is not available/i.test(message)) {
|
|
137
|
+
return "Cameras and microphones are not available here. Recording needs a browser or app that can access them.";
|
|
138
|
+
}
|
|
139
|
+
if (/getDisplayMedia is not available/i.test(message)) {
|
|
140
|
+
return "Screen capture is not available here. Recording the screen needs a browser or app that supports it.";
|
|
141
|
+
}
|
|
142
|
+
return message || "Recording could not start.";
|
|
143
|
+
}
|
|
144
|
+
|
|
36
145
|
// src/recorder/sources/screenRegion.ts
|
|
37
146
|
function validateScreenCaptureRegion(region, width, height) {
|
|
38
147
|
const { x, y, width: w, height: h } = region;
|
|
@@ -186,6 +295,8 @@ async function requestFullScreenStream(options) {
|
|
|
186
295
|
const displayStream = await navigator.mediaDevices.getDisplayMedia({
|
|
187
296
|
video,
|
|
188
297
|
audio: systemAudio ? options?.systemAudioConstraints ?? true : false
|
|
298
|
+
}).catch((err) => {
|
|
299
|
+
throw markDisplayCaptureError(err);
|
|
189
300
|
});
|
|
190
301
|
if (!includeMic) {
|
|
191
302
|
return {
|
|
@@ -249,6 +360,8 @@ async function requestSystemAudioStream(audioConstraints) {
|
|
|
249
360
|
const display = await navigator.mediaDevices.getDisplayMedia({
|
|
250
361
|
video: true,
|
|
251
362
|
audio: audioConstraints ?? true
|
|
363
|
+
}).catch((err) => {
|
|
364
|
+
throw markDisplayCaptureError(err);
|
|
252
365
|
});
|
|
253
366
|
display.getVideoTracks().forEach((t) => t.stop());
|
|
254
367
|
const audio = display.getAudioTracks();
|
|
@@ -2672,20 +2785,6 @@ function captureSummary(micOn, cameraOn, screenOn, systemAudioOn) {
|
|
|
2672
2785
|
}
|
|
2673
2786
|
return "Pick at least one source to record.";
|
|
2674
2787
|
}
|
|
2675
|
-
function recordingFilenameSeed(source, stream, audioRequested) {
|
|
2676
|
-
if (source === "mic") return "audio";
|
|
2677
|
-
const hasAudio = stream ? stream.getAudioTracks().length > 0 : audioRequested;
|
|
2678
|
-
if (source === "camera") return hasAudio ? "camera-audio" : "camera";
|
|
2679
|
-
return hasAudio ? "screen-audio" : "screen";
|
|
2680
|
-
}
|
|
2681
|
-
function dualFilenameSeeds(screenStream, cameraStream, systemAudioRequested, micRequested) {
|
|
2682
|
-
const screenHasAudio = screenStream ? screenStream.getAudioTracks().length > 0 : systemAudioRequested;
|
|
2683
|
-
const cameraHasAudio = cameraStream ? cameraStream.getAudioTracks().length > 0 : micRequested;
|
|
2684
|
-
return {
|
|
2685
|
-
screen: screenHasAudio ? "screen-audio" : "screen",
|
|
2686
|
-
camera: cameraHasAudio ? "camera-audio" : "camera"
|
|
2687
|
-
};
|
|
2688
|
-
}
|
|
2689
2788
|
function RecorderModal({
|
|
2690
2789
|
mediaProvider,
|
|
2691
2790
|
container = null,
|
|
@@ -3370,6 +3469,14 @@ function RecorderModal({
|
|
|
3370
3469
|
const deviceSettingsLocked = narrationRecording ? !narrationRecorderIdle : togglesLocked;
|
|
3371
3470
|
const microphoneEnabled = narrationRecording ? true : micOn;
|
|
3372
3471
|
const cameraEnabled = narrationRecording ? stage.recorder.withCamera : cameraOn;
|
|
3472
|
+
const captureErrorContext = {
|
|
3473
|
+
camera: cameraEnabled,
|
|
3474
|
+
microphone: microphoneEnabled,
|
|
3475
|
+
cameraDeviceId: deviceSettings.camera.deviceId,
|
|
3476
|
+
microphoneDeviceId: deviceSettings.audio.deviceId,
|
|
3477
|
+
devices: mediaDevices
|
|
3478
|
+
};
|
|
3479
|
+
const narrationError = stage.recorder.error ?? stage.controller.mic.error;
|
|
3373
3480
|
const narrationToggleFor = (key) => {
|
|
3374
3481
|
switch (key) {
|
|
3375
3482
|
case "mic":
|
|
@@ -3539,7 +3646,7 @@ function RecorderModal({
|
|
|
3539
3646
|
cameraStream: activeCameraStream
|
|
3540
3647
|
}
|
|
3541
3648
|
),
|
|
3542
|
-
!narrationRecording && recorder.error && /* @__PURE__ */ jsx5("div", { style: errorStyle, children: recorder.error
|
|
3649
|
+
!narrationRecording && recorder.error && /* @__PURE__ */ jsx5("div", { role: "alert", style: errorStyle, "data-testid": "recorder-error", children: describeCaptureError(recorder.error, captureErrorContext) }),
|
|
3543
3650
|
!narrationRecording && saveError && /* @__PURE__ */ jsxs5("div", { role: "alert", style: errorStyle, children: [
|
|
3544
3651
|
saveError,
|
|
3545
3652
|
/* @__PURE__ */ jsx5("p", { style: { marginBottom: 0 }, children: "Your recording is still available below. Download a copy before closing, or try saving again." })
|
|
@@ -3559,7 +3666,7 @@ function RecorderModal({
|
|
|
3559
3666
|
"total. Final encoding may add more data."
|
|
3560
3667
|
] }),
|
|
3561
3668
|
!narrationRecording && recorder.limitReached && /* @__PURE__ */ jsx5("p", { role: "status", style: recordingStatusStyle, children: "Recording stopped at the size limit. Your complete recording is retained; download a copy or save it to the document." }),
|
|
3562
|
-
narrationOn &&
|
|
3669
|
+
narrationOn && narrationError && /* @__PURE__ */ jsx5("div", { role: "alert", style: errorStyle, "data-testid": "recorder-narration-error", children: describeCaptureError(narrationError, captureErrorContext) }),
|
|
3563
3670
|
narrationRecording && narrationCameraStream && /* @__PURE__ */ jsx5("div", { style: previewBoxStyle, children: /* @__PURE__ */ jsx5(
|
|
3564
3671
|
"video",
|
|
3565
3672
|
{
|
|
@@ -70,6 +70,20 @@ function buildFilename(kind, extension, basename, seed) {
|
|
|
70
70
|
const prefix = seed ?? (kind === "audio" ? "narration" : "recording");
|
|
71
71
|
return `${prefix}-${stamp}${extension}`;
|
|
72
72
|
}
|
|
73
|
+
function recordingFilenameSeed(source, stream, audioRequested) {
|
|
74
|
+
if (source === "mic") return "audio";
|
|
75
|
+
const hasAudio = stream ? stream.getAudioTracks().length > 0 : audioRequested;
|
|
76
|
+
if (source === "camera") return hasAudio ? "camera-audio" : "camera";
|
|
77
|
+
return hasAudio ? "screen-audio" : "screen";
|
|
78
|
+
}
|
|
79
|
+
function dualFilenameSeeds(screenStream, cameraStream, systemAudioRequested, micRequested) {
|
|
80
|
+
const screenHasAudio = screenStream ? screenStream.getAudioTracks().length > 0 : systemAudioRequested;
|
|
81
|
+
const cameraHasAudio = cameraStream ? cameraStream.getAudioTracks().length > 0 : micRequested;
|
|
82
|
+
return {
|
|
83
|
+
screen: screenHasAudio ? "screen-audio" : "screen",
|
|
84
|
+
camera: cameraHasAudio ? "camera-audio" : "camera"
|
|
85
|
+
};
|
|
86
|
+
}
|
|
73
87
|
|
|
74
88
|
// src/recorder/sources/micStream.ts
|
|
75
89
|
async function requestMicStream(constraints) {
|
|
@@ -89,5 +103,7 @@ export {
|
|
|
89
103
|
supportsDisplayMedia,
|
|
90
104
|
supportsSystemAudioCapture,
|
|
91
105
|
buildFilename,
|
|
106
|
+
recordingFilenameSeed,
|
|
107
|
+
dualFilenameSeeds,
|
|
92
108
|
requestMicStream
|
|
93
109
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/speech/index.ts
|
|
2
2
|
async function startDictationSession(options) {
|
|
3
|
-
const session = await import("./dictationSession-
|
|
3
|
+
const session = await import("./dictationSession-WEXEY5NS.js");
|
|
4
4
|
if (options.signal.aborted) throw new DOMException("Aborted", "AbortError");
|
|
5
5
|
return session.startDictationSession(options);
|
|
6
6
|
}
|