@bendyline/squisq-editor-react 2.11.16 → 2.11.18
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-ZDWZRVAY.js → chunk-2XXBDECJ.js} +1 -1
- package/dist/{chunk-DNB32SU2.js → chunk-45NQUR62.js} +1105 -130
- package/dist/{chunk-34FRFDQ6.js → chunk-CGENKHWY.js} +58 -102
- package/dist/chunk-DNEVUOBZ.js +14 -0
- package/dist/chunk-DNJ6HOES.js +93 -0
- package/dist/{chunk-EN5BYZOE.js → chunk-DPAMJV6A.js} +3 -2
- package/dist/{chunk-5TYCTE33.js → chunk-GIXXD2O3.js} +2189 -1834
- package/dist/{chunk-ENDRPV6T.js → chunk-H7YTL6SL.js} +1 -1
- package/dist/chunk-LRZJD5YD.js +107 -0
- package/dist/{chunk-4D7QMRTZ.js → chunk-U6NVT3BY.js} +466 -540
- package/dist/{chunk-WDQFWD7Z.js → chunk-UBFNLWCN.js} +2 -2
- package/dist/{chunk-27WOOJXR.js → chunk-WKZLBGPX.js} +1 -1
- package/dist/dictationSession-PN3OT5LJ.js +521 -0
- package/dist/index.d.ts +36 -21
- package/dist/index.js +54 -22
- 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-C6ArYQBa.d.ts → recorder-CRsw0eC7.d.ts} +6 -5
- 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-Cb0P_ln8.d.ts} +170 -5
- package/dist/speech/index.d.ts +126 -0
- package/dist/speech/index.js +10 -0
- package/dist/styles/index.css +162 -29
- package/dist/teleprompter/index.d.ts +13 -5
- package/dist/teleprompter/index.js +3 -2
- package/dist/{useNarrationStage-DopDr_P5.d.ts → useNarrationStage-vKtk6H-V.d.ts} +7 -1
- package/package.json +10 -6
|
@@ -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) {
|
|
@@ -178,7 +101,9 @@ async function registerPcmWorklet(ctx) {
|
|
|
178
101
|
|
|
179
102
|
// src/teleprompter/useMicAnalysis.ts
|
|
180
103
|
import { useCallback, useEffect as useEffect2, useRef, useState } from "react";
|
|
181
|
-
function useMicAnalysis(constraints) {
|
|
104
|
+
function useMicAnalysis(constraints, externalStream) {
|
|
105
|
+
const externalStreamRef = useRef(externalStream);
|
|
106
|
+
externalStreamRef.current = externalStream;
|
|
182
107
|
const [status, setStatus] = useState("idle");
|
|
183
108
|
const [error, setError] = useState(null);
|
|
184
109
|
const [devices, setDevices] = useState([]);
|
|
@@ -220,7 +145,7 @@ function useMicAnalysis(constraints) {
|
|
|
220
145
|
graph.sink?.disconnect();
|
|
221
146
|
} catch {
|
|
222
147
|
}
|
|
223
|
-
for (const track of graph.stream.getTracks()) track.stop();
|
|
148
|
+
if (graph.ownsStream) for (const track of graph.stream.getTracks()) track.stop();
|
|
224
149
|
void graph.context.close().catch(() => void 0);
|
|
225
150
|
}, []);
|
|
226
151
|
const stop = useCallback(() => {
|
|
@@ -238,17 +163,26 @@ function useMicAnalysis(constraints) {
|
|
|
238
163
|
teardown();
|
|
239
164
|
setStatus("starting");
|
|
240
165
|
setError(null);
|
|
166
|
+
const ownsStream = externalStreamRef.current === void 0;
|
|
167
|
+
let acquiredStream = null;
|
|
168
|
+
let acquiredContext = null;
|
|
241
169
|
try {
|
|
170
|
+
if (!ownsStream && !externalStreamRef.current?.getAudioTracks().length) {
|
|
171
|
+
setStatus("idle");
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
242
174
|
const baseConstraints = constraintsRef.current;
|
|
243
|
-
const micStream = await requestMicStream({
|
|
175
|
+
const micStream = externalStreamRef.current ?? await requestMicStream({
|
|
244
176
|
...baseConstraints ?? {},
|
|
245
177
|
...deviceId ? { deviceId: { exact: deviceId } } : {}
|
|
246
178
|
});
|
|
179
|
+
acquiredStream = micStream;
|
|
247
180
|
if (generation !== generationRef.current) {
|
|
248
|
-
for (const track of micStream.getTracks()) track.stop();
|
|
181
|
+
if (ownsStream) for (const track of micStream.getTracks()) track.stop();
|
|
249
182
|
return null;
|
|
250
183
|
}
|
|
251
184
|
const context = new AudioContext();
|
|
185
|
+
acquiredContext = context;
|
|
252
186
|
if (context.state === "suspended") {
|
|
253
187
|
await context.resume().catch(() => void 0);
|
|
254
188
|
}
|
|
@@ -297,17 +231,19 @@ function useMicAnalysis(constraints) {
|
|
|
297
231
|
node = processor;
|
|
298
232
|
}
|
|
299
233
|
if (generation !== generationRef.current) {
|
|
300
|
-
for (const track of micStream.getTracks()) track.stop();
|
|
234
|
+
if (ownsStream) for (const track of micStream.getTracks()) track.stop();
|
|
301
235
|
void context.close().catch(() => void 0);
|
|
302
236
|
return null;
|
|
303
237
|
}
|
|
304
|
-
graphRef.current = { stream: micStream, context, source, node, sink };
|
|
238
|
+
graphRef.current = { stream: micStream, ownsStream, context, source, node, sink };
|
|
305
239
|
setStream(micStream);
|
|
306
240
|
setSampleRate(context.sampleRate);
|
|
307
241
|
setStatus("live");
|
|
308
242
|
void refreshDevices();
|
|
309
243
|
return micStream;
|
|
310
244
|
} catch (err) {
|
|
245
|
+
if (ownsStream) for (const track of acquiredStream?.getTracks() ?? []) track.stop();
|
|
246
|
+
if (acquiredContext) void acquiredContext.close().catch(() => void 0);
|
|
311
247
|
if (generation !== generationRef.current) return null;
|
|
312
248
|
setError(err instanceof Error ? err : new Error(String(err)));
|
|
313
249
|
setStatus("error");
|
|
@@ -316,6 +252,12 @@ function useMicAnalysis(constraints) {
|
|
|
316
252
|
},
|
|
317
253
|
[refreshDevices, teardown]
|
|
318
254
|
);
|
|
255
|
+
useEffect2(() => {
|
|
256
|
+
if (externalStream === void 0) return;
|
|
257
|
+
if (externalStream?.getAudioTracks().length) void start(null);
|
|
258
|
+
else stop();
|
|
259
|
+
return stop;
|
|
260
|
+
}, [externalStream, start, stop]);
|
|
319
261
|
const subscribeHop = useCallback((listener) => {
|
|
320
262
|
listenersRef.current.add(listener);
|
|
321
263
|
return () => {
|
|
@@ -333,7 +275,17 @@ function useMicAnalysis(constraints) {
|
|
|
333
275
|
teardown();
|
|
334
276
|
};
|
|
335
277
|
}, [teardown]);
|
|
336
|
-
return {
|
|
278
|
+
return {
|
|
279
|
+
status,
|
|
280
|
+
error,
|
|
281
|
+
stream,
|
|
282
|
+
sampleRate,
|
|
283
|
+
devices,
|
|
284
|
+
sourceManaged: externalStream !== void 0,
|
|
285
|
+
start,
|
|
286
|
+
stop,
|
|
287
|
+
subscribeHop
|
|
288
|
+
};
|
|
337
289
|
}
|
|
338
290
|
|
|
339
291
|
// src/teleprompter/types.ts
|
|
@@ -465,7 +417,7 @@ function useTeleprompter(opts) {
|
|
|
465
417
|
const [transport, setTransport] = useState2("stopped");
|
|
466
418
|
const [countdownRemaining, setCountdownRemaining] = useState2(null);
|
|
467
419
|
const [view, setView] = useState2({ wordPos: 0, micLevel: 0, voiceActive: false });
|
|
468
|
-
const mic = useMicAnalysis(opts.micConstraints);
|
|
420
|
+
const mic = useMicAnalysis(opts.micConstraints, opts.analysisStream);
|
|
469
421
|
const scriptRef = useRef2(script);
|
|
470
422
|
const prefsRef = useRef2(prefs);
|
|
471
423
|
const transportRef = useRef2(transport);
|
|
@@ -1764,11 +1716,16 @@ function insertNarrationPreamble(source, audioPath, cameraPath) {
|
|
|
1764
1716
|
|
|
1765
1717
|
// src/teleprompter/recording/narrationSave.ts
|
|
1766
1718
|
import {
|
|
1767
|
-
buildNarrationTimingJson
|
|
1719
|
+
buildNarrationTimingJson,
|
|
1720
|
+
resolveNarrationTimingGenerator
|
|
1768
1721
|
} from "@bendyline/squisq/narration";
|
|
1769
1722
|
function buildNarrationSavePlan(args) {
|
|
1723
|
+
const timingOptions = {
|
|
1724
|
+
...args.baseWpm !== void 0 ? { baseWpm: args.baseWpm } : {},
|
|
1725
|
+
...args.generator ? { generator: args.generator } : {}
|
|
1726
|
+
};
|
|
1770
1727
|
const sidecarPayload = args.alignment ? buildNarrationTimingJson(args.script, args.alignment, args.durationSec, {
|
|
1771
|
-
|
|
1728
|
+
...timingOptions,
|
|
1772
1729
|
...args.cameraOffsetSec !== void 0 ? { cameraOffsetSec: args.cameraOffsetSec } : {}
|
|
1773
1730
|
}) : {
|
|
1774
1731
|
version: 3,
|
|
@@ -1776,7 +1733,7 @@ function buildNarrationSavePlan(args) {
|
|
|
1776
1733
|
duration: args.durationSec,
|
|
1777
1734
|
bookmarks: [],
|
|
1778
1735
|
blocks: [],
|
|
1779
|
-
generator:
|
|
1736
|
+
generator: resolveNarrationTimingGenerator(timingOptions)
|
|
1780
1737
|
};
|
|
1781
1738
|
return {
|
|
1782
1739
|
audioRelativeName: `audio/${buildFilename("audio", args.audioExt, args.audioBasename)}`,
|
|
@@ -1851,7 +1808,11 @@ import { useCallback as useCallback5, useEffect as useEffect6, useMemo as useMem
|
|
|
1851
1808
|
import { wordIndexAtTime } from "@bendyline/squisq/narration";
|
|
1852
1809
|
function useNarrationStage(opts) {
|
|
1853
1810
|
const { doc, recording = null, getAudioBasename } = opts;
|
|
1854
|
-
const controller = useTeleprompter({
|
|
1811
|
+
const controller = useTeleprompter({
|
|
1812
|
+
doc,
|
|
1813
|
+
micConstraints: opts.micConstraints,
|
|
1814
|
+
analysisStream: opts.analysisStream
|
|
1815
|
+
});
|
|
1855
1816
|
const float = useFloatingWindow(TELEPROMPTER_CSS);
|
|
1856
1817
|
const controllerRef = useRef5(controller);
|
|
1857
1818
|
controllerRef.current = controller;
|
|
@@ -2325,6 +2286,8 @@ function TeleprompterControls({
|
|
|
2325
2286
|
{
|
|
2326
2287
|
"aria-label": "Microphone",
|
|
2327
2288
|
value: prefs.micDeviceId ?? "",
|
|
2289
|
+
disabled: mic.sourceManaged,
|
|
2290
|
+
title: mic.sourceManaged ? "Microphone selected in recording settings" : void 0,
|
|
2328
2291
|
onChange: (e) => setPrefs({ micDeviceId: e.target.value || null }),
|
|
2329
2292
|
children: [
|
|
2330
2293
|
/* @__PURE__ */ jsx2("option", { value: "", children: "Default mic" }),
|
|
@@ -2839,13 +2802,6 @@ function NarrationStage(props) {
|
|
|
2839
2802
|
export {
|
|
2840
2803
|
resolveMaxRecordingBytes,
|
|
2841
2804
|
formatRecordingBytes,
|
|
2842
|
-
resolveFormat,
|
|
2843
|
-
supportsMediaRecorder,
|
|
2844
|
-
supportsUserMedia,
|
|
2845
|
-
supportsDisplayMedia,
|
|
2846
|
-
supportsSystemAudioCapture,
|
|
2847
|
-
buildFilename,
|
|
2848
|
-
requestMicStream,
|
|
2849
2805
|
requestCameraStream,
|
|
2850
2806
|
useStreamPreview,
|
|
2851
2807
|
buildTimingJson,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// src/speech/types.ts
|
|
2
|
+
var SPEECH_INPUT_SAMPLE_RATE = 16e3;
|
|
3
|
+
function resolveSpeechInputProvider(capability) {
|
|
4
|
+
return typeof capability === "function" ? capability() : capability;
|
|
5
|
+
}
|
|
6
|
+
function isSpeechInputProviderFactory(capability) {
|
|
7
|
+
return typeof capability === "function";
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
SPEECH_INPUT_SAMPLE_RATE,
|
|
12
|
+
resolveSpeechInputProvider,
|
|
13
|
+
isSpeechInputProviderFactory
|
|
14
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// src/recorder/formats.ts
|
|
2
|
+
var AUDIO_CANDIDATES = [
|
|
3
|
+
"audio/webm;codecs=opus",
|
|
4
|
+
"audio/webm",
|
|
5
|
+
"audio/mp4;codecs=mp4a.40.2",
|
|
6
|
+
"audio/mp4",
|
|
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
|
+
}
|
|
84
|
+
|
|
85
|
+
export {
|
|
86
|
+
resolveFormat,
|
|
87
|
+
supportsMediaRecorder,
|
|
88
|
+
supportsUserMedia,
|
|
89
|
+
supportsDisplayMedia,
|
|
90
|
+
supportsSystemAudioCapture,
|
|
91
|
+
buildFilename,
|
|
92
|
+
requestMicStream
|
|
93
|
+
};
|
|
@@ -588,7 +588,7 @@ function tiptapToMarkdown(html) {
|
|
|
588
588
|
const imgMatch = remaining.match(/^<img\b([^>]*)>/);
|
|
589
589
|
if (imgMatch) {
|
|
590
590
|
const attrs = imgMatch[1] ?? "";
|
|
591
|
-
const src =
|
|
591
|
+
const src = /(?:^|\s)(?:data-squisq-image-)?src="([^"]*)"/i.exec(attrs)?.[1];
|
|
592
592
|
if (src) {
|
|
593
593
|
const alt = /\balt="([^"]*)"/i.exec(attrs)?.[1] ?? "";
|
|
594
594
|
const serialized = serializeImage(src, alt, attrs);
|
|
@@ -903,6 +903,7 @@ function serializeImage(src, alt, attrs) {
|
|
|
903
903
|
const width = /\bwidth="([^"]*)"/i.exec(attrs)?.[1];
|
|
904
904
|
const height = /\bheight="([^"]*)"/i.exec(attrs)?.[1];
|
|
905
905
|
const title = /\btitle="([^"]*)"/i.exec(attrs)?.[1];
|
|
906
|
+
if (title?.startsWith("squisq-upload:")) return "";
|
|
906
907
|
if (!width && !height) {
|
|
907
908
|
return `})`;
|
|
908
909
|
}
|
|
@@ -1188,7 +1189,7 @@ function htmlToInline(html) {
|
|
|
1188
1189
|
return `[${text}](${escapedDestination(href)}${titlePart})`;
|
|
1189
1190
|
});
|
|
1190
1191
|
result = result.replace(RE_IMG_TAG, (match, attrs) => {
|
|
1191
|
-
const src =
|
|
1192
|
+
const src = /(?:^|\s)(?:data-squisq-image-)?src="([^"]*)"/i.exec(attrs)?.[1];
|
|
1192
1193
|
if (!src) return match;
|
|
1193
1194
|
const alt = /\balt="([^"]*)"/i.exec(attrs)?.[1] ?? "";
|
|
1194
1195
|
const serialized = serializeImage(src, alt, attrs);
|