@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
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The speech-input (dictation) capability contract between an editor host and
|
|
3
|
+
* the shell.
|
|
4
|
+
*
|
|
5
|
+
* A host that can turn speech into text passes a {@link SpeechInputProvider}
|
|
6
|
+
* (or a factory) to `EditorShell`'s `speechInput` prop — the same
|
|
7
|
+
* capability-injection semantics as `proofing`: absent means the feature is
|
|
8
|
+
* off, no dictation code loads, and no microphone button renders.
|
|
9
|
+
*
|
|
10
|
+
* The editor owns everything up to the recognizer: microphone capture, phrase
|
|
11
|
+
* segmentation, WAV encoding, the toolbar button, the caret indicator and the
|
|
12
|
+
* literal-text insertion. The provider owns only recognition. Audio crosses
|
|
13
|
+
* the boundary as one self-contained mono 16-bit PCM WAV at 16 kHz per take,
|
|
14
|
+
* which every whisper.cpp build accepts and which keeps IPC payloads small.
|
|
15
|
+
* The surface is plain data end to end, so a provider can be a local engine
|
|
16
|
+
* behind IPC, a call to a service, or a test fake.
|
|
17
|
+
*/
|
|
18
|
+
/** Sample rate (Hz) of every WAV take handed to {@link SpeechInputProvider.transcribe}. */
|
|
19
|
+
declare const SPEECH_INPUT_SAMPLE_RATE = 16000;
|
|
20
|
+
/** Coarse readiness of a provider, as the editor's button presents it. */
|
|
21
|
+
type SpeechInputReadinessState =
|
|
22
|
+
/** Dictation can start now. */
|
|
23
|
+
'ready'
|
|
24
|
+
/**
|
|
25
|
+
* The host needs a one-time setup (typically a model download) first.
|
|
26
|
+
* Clicking the microphone calls {@link SpeechInputProvider.requestSetup}
|
|
27
|
+
* instead of starting.
|
|
28
|
+
*/
|
|
29
|
+
| 'download-required'
|
|
30
|
+
/** Dictation can never work in this host/session. The button is hidden. */
|
|
31
|
+
| 'unavailable'
|
|
32
|
+
/**
|
|
33
|
+
* The host still needs a permission (e.g. the OS microphone grant).
|
|
34
|
+
* Starting proceeds — the microphone request is what prompts for it.
|
|
35
|
+
*/
|
|
36
|
+
| 'permission-required';
|
|
37
|
+
interface SpeechInputReadiness {
|
|
38
|
+
readonly state: SpeechInputReadinessState;
|
|
39
|
+
/** Optional human-readable detail, shown in the button's tooltip/errors. */
|
|
40
|
+
readonly reason?: string;
|
|
41
|
+
}
|
|
42
|
+
interface SpeechInputTranscribeOptions {
|
|
43
|
+
/**
|
|
44
|
+
* The recent dictated text of this session (a word-aligned tail of at most
|
|
45
|
+
* 1,000 characters), for engines that accept a priming prompt — Whisper's
|
|
46
|
+
* `prompt`. Absent on the first take.
|
|
47
|
+
*/
|
|
48
|
+
readonly prompt?: string;
|
|
49
|
+
/** BCP-47-ish language hint. The editor does not currently set it. */
|
|
50
|
+
readonly language?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Aborted when the user cancels dictation or the editor unmounts. A
|
|
53
|
+
* provider should stop promptly; a rejection after abort is ignored.
|
|
54
|
+
*/
|
|
55
|
+
readonly signal: AbortSignal;
|
|
56
|
+
}
|
|
57
|
+
interface SpeechInputTranscript {
|
|
58
|
+
/** Recognized text. Blank (or Whisper's `[BLANK_AUDIO]`) means silence. */
|
|
59
|
+
readonly text: string;
|
|
60
|
+
}
|
|
61
|
+
interface SpeechInputProvider {
|
|
62
|
+
/** Stable identifier, e.g. `whisper-local`. */
|
|
63
|
+
readonly id: string;
|
|
64
|
+
/** Human-readable name, e.g. `On-device speech recognition`. */
|
|
65
|
+
readonly label: string;
|
|
66
|
+
/**
|
|
67
|
+
* Current readiness. Called once when the editor mounts with the
|
|
68
|
+
* capability (to decide whether to render the microphone at all) and again
|
|
69
|
+
* each time the user starts dictation. Must be cheap.
|
|
70
|
+
*/
|
|
71
|
+
status(): Promise<SpeechInputReadiness>;
|
|
72
|
+
/** Optional readiness push (e.g. a model finished downloading). Returns unsubscribe. */
|
|
73
|
+
onStatus?(listener: (readiness: SpeechInputReadiness) => void): () => void;
|
|
74
|
+
/** Called when the user starts dictation so the host can warm its engine; may reject. */
|
|
75
|
+
prepare?(signal: AbortSignal): Promise<void>;
|
|
76
|
+
/** Called when the user clicks the mic while readiness is 'download-required'; the host shows its own setup UI. */
|
|
77
|
+
requestSetup?(): void;
|
|
78
|
+
/** Transcribe one self-contained take: mono 16-bit PCM WAV at 16 kHz. */
|
|
79
|
+
transcribe(wav: ArrayBuffer, options: SpeechInputTranscribeOptions): Promise<SpeechInputTranscript>;
|
|
80
|
+
/** Release resources. Called by the editor only for a provider it created from a factory. */
|
|
81
|
+
dispose?(): void;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Deferred construction. The shell creates the provider when it mounts with
|
|
85
|
+
* the capability (it needs `status()` to decide whether to render the
|
|
86
|
+
* microphone) and disposes it on unmount or when the capability changes. A
|
|
87
|
+
* host that passes an instance owns its lifetime — which is what lets one warm
|
|
88
|
+
* engine survive shell remounts.
|
|
89
|
+
*/
|
|
90
|
+
type SpeechInputProviderFactory = () => SpeechInputProvider;
|
|
91
|
+
/** What the `speechInput` shell prop accepts. */
|
|
92
|
+
type SpeechInputCapability = SpeechInputProvider | SpeechInputProviderFactory;
|
|
93
|
+
/**
|
|
94
|
+
* Resolve a capability to a provider instance. A factory is invoked (and the
|
|
95
|
+
* caller then owns — must dispose — what it returns); an instance is returned
|
|
96
|
+
* as-is and stays host-owned.
|
|
97
|
+
*/
|
|
98
|
+
declare function resolveSpeechInputProvider(capability: SpeechInputCapability): SpeechInputProvider;
|
|
99
|
+
/**
|
|
100
|
+
* Whether a capability is a factory, i.e. whether the provider resolved from
|
|
101
|
+
* it is owned (and must be disposed) by the resolver's caller.
|
|
102
|
+
*/
|
|
103
|
+
declare function isSpeechInputProviderFactory(capability: SpeechInputCapability): capability is SpeechInputProviderFactory;
|
|
104
|
+
/**
|
|
105
|
+
* Imperative dictation control on the editor context
|
|
106
|
+
* (`useEditorContext().dictation`), for a host component rendered inside the
|
|
107
|
+
* shell's slots — e.g. to drive dictation from a native menu command. `null`
|
|
108
|
+
* when the host injected no `speechInput` capability.
|
|
109
|
+
*/
|
|
110
|
+
interface DictationControl {
|
|
111
|
+
/** True from the moment dictation starts until its last take is inserted. */
|
|
112
|
+
readonly active: boolean;
|
|
113
|
+
/**
|
|
114
|
+
* True when `toggle()` would do something: the provider is not
|
|
115
|
+
* `unavailable` and a text-editing view (Write or Source) is showing.
|
|
116
|
+
*/
|
|
117
|
+
readonly available: boolean;
|
|
118
|
+
/**
|
|
119
|
+
* Start dictation when idle (or ask the host to set up when readiness is
|
|
120
|
+
* `download-required`); finish and stop when listening; cancel while the
|
|
121
|
+
* microphone is still starting.
|
|
122
|
+
*/
|
|
123
|
+
toggle(): void;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export { type DictationControl, SPEECH_INPUT_SAMPLE_RATE, type SpeechInputCapability, type SpeechInputProvider, type SpeechInputProviderFactory, type SpeechInputReadiness, type SpeechInputReadinessState, type SpeechInputTranscribeOptions, type SpeechInputTranscript, isSpeechInputProviderFactory, resolveSpeechInputProvider };
|
package/dist/styles/index.css
CHANGED
|
@@ -7866,8 +7866,9 @@
|
|
|
7866
7866
|
.squisq-toolbar-contextual--clipped {
|
|
7867
7867
|
visibility: hidden;
|
|
7868
7868
|
}
|
|
7869
|
-
.squisq-toolbar-button--overflowed
|
|
7870
|
-
|
|
7869
|
+
.squisq-toolbar-button.squisq-toolbar-button--overflowed,
|
|
7870
|
+
.squisq-toolbar-group.squisq-toolbar-group--collapsed {
|
|
7871
|
+
display: none;
|
|
7871
7872
|
}
|
|
7872
7873
|
.squisq-toolbar-view-tab {
|
|
7873
7874
|
padding: 8px 14px 4px;
|
|
@@ -8145,6 +8146,131 @@
|
|
|
8145
8146
|
text-overflow: ellipsis;
|
|
8146
8147
|
white-space: nowrap;
|
|
8147
8148
|
}
|
|
8149
|
+
.squisq-dictation {
|
|
8150
|
+
position: relative;
|
|
8151
|
+
display: inline-flex;
|
|
8152
|
+
align-items: center;
|
|
8153
|
+
flex-shrink: 0;
|
|
8154
|
+
}
|
|
8155
|
+
.squisq-dictation-button--active,
|
|
8156
|
+
.squisq-dictation-button--active:hover {
|
|
8157
|
+
background: var(--squisq-danger-soft);
|
|
8158
|
+
color: var(--squisq-danger-strong);
|
|
8159
|
+
}
|
|
8160
|
+
.squisq-dictation-button--listening {
|
|
8161
|
+
width: auto;
|
|
8162
|
+
gap: 6px;
|
|
8163
|
+
padding: 2px 8px 0;
|
|
8164
|
+
}
|
|
8165
|
+
.squisq-dictation-button[aria-busy=true] > i {
|
|
8166
|
+
animation: squisq-dictation-pulse 1.2s ease-in-out infinite;
|
|
8167
|
+
}
|
|
8168
|
+
.squisq-dictation-meter {
|
|
8169
|
+
display: inline-flex;
|
|
8170
|
+
align-items: center;
|
|
8171
|
+
gap: 1px;
|
|
8172
|
+
height: 16px;
|
|
8173
|
+
}
|
|
8174
|
+
.squisq-dictation-meter-bar {
|
|
8175
|
+
width: 2px;
|
|
8176
|
+
min-height: 2px;
|
|
8177
|
+
border-radius: 1px;
|
|
8178
|
+
background: currentColor;
|
|
8179
|
+
transition: height 0.05s linear;
|
|
8180
|
+
}
|
|
8181
|
+
.squisq-dictation-error {
|
|
8182
|
+
position: absolute;
|
|
8183
|
+
top: calc(100% + 6px);
|
|
8184
|
+
right: 0;
|
|
8185
|
+
z-index: 110;
|
|
8186
|
+
display: flex;
|
|
8187
|
+
align-items: flex-start;
|
|
8188
|
+
gap: 8px;
|
|
8189
|
+
width: max-content;
|
|
8190
|
+
max-width: min(320px, calc(100vw - 24px));
|
|
8191
|
+
padding: 8px 8px 8px 12px;
|
|
8192
|
+
border: 1px solid var(--squisq-danger-border);
|
|
8193
|
+
border-radius: 8px;
|
|
8194
|
+
background: var(--squisq-surface);
|
|
8195
|
+
box-shadow: var(--squisq-shadow-md);
|
|
8196
|
+
color: var(--squisq-danger-text);
|
|
8197
|
+
font-size: 12px;
|
|
8198
|
+
font-weight: 500;
|
|
8199
|
+
line-height: 1.4;
|
|
8200
|
+
white-space: normal;
|
|
8201
|
+
}
|
|
8202
|
+
.squisq-dictation-error-text {
|
|
8203
|
+
flex: 1;
|
|
8204
|
+
}
|
|
8205
|
+
.squisq-dictation-error-dismiss {
|
|
8206
|
+
flex-shrink: 0;
|
|
8207
|
+
padding: 0 2px;
|
|
8208
|
+
border: 0;
|
|
8209
|
+
background: transparent;
|
|
8210
|
+
color: var(--squisq-text-muted);
|
|
8211
|
+
cursor: pointer;
|
|
8212
|
+
font-size: 12px;
|
|
8213
|
+
line-height: 1.4;
|
|
8214
|
+
}
|
|
8215
|
+
.squisq-dictation-error-dismiss:hover {
|
|
8216
|
+
color: var(--squisq-text-strong);
|
|
8217
|
+
}
|
|
8218
|
+
.squisq-dictation-interim {
|
|
8219
|
+
display: inline-block;
|
|
8220
|
+
margin-left: 2px;
|
|
8221
|
+
padding: 0 6px;
|
|
8222
|
+
border-radius: 999px;
|
|
8223
|
+
background: var(--squisq-danger-soft);
|
|
8224
|
+
color: var(--squisq-text-muted);
|
|
8225
|
+
font-size: 0.78em;
|
|
8226
|
+
font-style: italic;
|
|
8227
|
+
font-weight: 400;
|
|
8228
|
+
line-height: 1.5;
|
|
8229
|
+
vertical-align: baseline;
|
|
8230
|
+
white-space: nowrap;
|
|
8231
|
+
user-select: none;
|
|
8232
|
+
pointer-events: none;
|
|
8233
|
+
}
|
|
8234
|
+
.squisq-dictation-interim::before {
|
|
8235
|
+
content: "";
|
|
8236
|
+
display: inline-block;
|
|
8237
|
+
width: 6px;
|
|
8238
|
+
height: 6px;
|
|
8239
|
+
margin-right: 5px;
|
|
8240
|
+
border-radius: 50%;
|
|
8241
|
+
background: var(--squisq-danger);
|
|
8242
|
+
vertical-align: 0.1em;
|
|
8243
|
+
animation: squisq-dictation-pulse 1.2s ease-in-out infinite;
|
|
8244
|
+
}
|
|
8245
|
+
.squisq-dictation-interim--source {
|
|
8246
|
+
display: inline;
|
|
8247
|
+
margin-left: 0.5ch;
|
|
8248
|
+
padding: 0;
|
|
8249
|
+
background: transparent;
|
|
8250
|
+
font-size: inherit;
|
|
8251
|
+
line-height: inherit;
|
|
8252
|
+
opacity: 0.8;
|
|
8253
|
+
}
|
|
8254
|
+
.squisq-dictation-interim--source::before {
|
|
8255
|
+
content: none;
|
|
8256
|
+
}
|
|
8257
|
+
@keyframes squisq-dictation-pulse {
|
|
8258
|
+
0%, 100% {
|
|
8259
|
+
opacity: 1;
|
|
8260
|
+
}
|
|
8261
|
+
50% {
|
|
8262
|
+
opacity: 0.35;
|
|
8263
|
+
}
|
|
8264
|
+
}
|
|
8265
|
+
@media (prefers-reduced-motion: reduce) {
|
|
8266
|
+
.squisq-dictation-interim::before,
|
|
8267
|
+
.squisq-dictation-button[aria-busy=true] > i {
|
|
8268
|
+
animation: none;
|
|
8269
|
+
}
|
|
8270
|
+
.squisq-dictation-meter-bar {
|
|
8271
|
+
transition: none;
|
|
8272
|
+
}
|
|
8273
|
+
}
|
|
8148
8274
|
|
|
8149
8275
|
/* src/styles/template-picker.css */
|
|
8150
8276
|
.squisq-mermaid-type-menu {
|
|
@@ -9134,6 +9260,18 @@
|
|
|
9134
9260
|
color: var(--squisq-text-muted);
|
|
9135
9261
|
flex-shrink: 0;
|
|
9136
9262
|
gap: 12px;
|
|
9263
|
+
container: squisq-status-bar / inline-size;
|
|
9264
|
+
}
|
|
9265
|
+
@container squisq-status-bar (max-width: 520px) {
|
|
9266
|
+
.squisq-status-chars,
|
|
9267
|
+
.squisq-status-lines {
|
|
9268
|
+
display: none;
|
|
9269
|
+
}
|
|
9270
|
+
}
|
|
9271
|
+
@container squisq-status-bar (max-width: 300px) {
|
|
9272
|
+
.squisq-status-blocks {
|
|
9273
|
+
display: none;
|
|
9274
|
+
}
|
|
9137
9275
|
}
|
|
9138
9276
|
.squisq-status-item {
|
|
9139
9277
|
white-space: nowrap;
|
|
@@ -11323,7 +11461,8 @@
|
|
|
11323
11461
|
color: var(--squisq-danger-strong);
|
|
11324
11462
|
background: var(--squisq-danger-soft);
|
|
11325
11463
|
}
|
|
11326
|
-
.squisq-version-history-empty
|
|
11464
|
+
.squisq-version-history-empty,
|
|
11465
|
+
.squisq-version-history-note {
|
|
11327
11466
|
padding: 16px 12px;
|
|
11328
11467
|
font-size: 12px;
|
|
11329
11468
|
color: var(--squisq-text-muted);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { Doc, Theme, MediaProvider } from '@bendyline/squisq/schemas';
|
|
3
3
|
import { ContentContainer } from '@bendyline/squisq/storage';
|
|
4
|
-
import { o as TeleprompterRecordingDeps, g as NarrationStageHandle, T as TeleprompterController, c as FloatingWindowHandle } from '../useNarrationStage-
|
|
5
|
-
export { C as CanvasSink, D as DEFAULT_TELEPROMPTER_PREFS, F as FloatOpenOptions, b as FloatTier, d as FloatingWindowManager, M as MicAnalysisHandle, e as MicAnalysisStatus, N as NarrationRecorderController, f as NarrationRecorderState, h as NarrationTake, P as PrompterTransport, J as TELEPROMPTER_PREF_LIMITS, n as TeleprompterPrefs, q as UseNarrationStageOptions, s as createFloatingWindowManager, t as detectFloatTiers, K as normalizeTeleprompterPrefs, z as useFloatingWindow, B as useMicAnalysis, E as useNarrationRecorder, G as useNarrationStage, H as useTeleprompter, I as vadConfigForSensitivity } from '../useNarrationStage-
|
|
6
|
-
import { NarrationTimingJsonV3, NarrationScript, NarrationAlignment } from '@bendyline/squisq/narration';
|
|
4
|
+
import { o as TeleprompterRecordingDeps, g as NarrationStageHandle, T as TeleprompterController, c as FloatingWindowHandle } from '../useNarrationStage-CB8dynUg.js';
|
|
5
|
+
export { C as CanvasSink, D as DEFAULT_TELEPROMPTER_PREFS, F as FloatOpenOptions, b as FloatTier, d as FloatingWindowManager, M as MicAnalysisHandle, e as MicAnalysisStatus, N as NarrationRecorderController, f as NarrationRecorderState, h as NarrationTake, P as PrompterTransport, J as TELEPROMPTER_PREF_LIMITS, n as TeleprompterPrefs, q as UseNarrationStageOptions, s as createFloatingWindowManager, t as detectFloatTiers, K as normalizeTeleprompterPrefs, z as useFloatingWindow, B as useMicAnalysis, E as useNarrationRecorder, G as useNarrationStage, H as useTeleprompter, I as vadConfigForSensitivity } from '../useNarrationStage-CB8dynUg.js';
|
|
6
|
+
import { NarrationTimingJsonV3, NarrationScript, NarrationAlignment, NarrationTimingGenerator } from '@bendyline/squisq/narration';
|
|
7
7
|
import { ReactNode } from 'react';
|
|
8
8
|
|
|
9
9
|
interface TeleprompterViewProps {
|
|
@@ -88,10 +88,18 @@ interface NarrationSavePlanArgs {
|
|
|
88
88
|
durationSec: number;
|
|
89
89
|
audioExt: string;
|
|
90
90
|
cameraExt: string | null;
|
|
91
|
-
baseWpm
|
|
91
|
+
/** Speaking rate the take was paced at; recorded as `generator.baseWpm`. */
|
|
92
|
+
baseWpm?: number;
|
|
92
93
|
cameraOffsetSec?: number;
|
|
93
94
|
/** User-chosen filename base for the audio file; default timestamped `narration-…`. */
|
|
94
95
|
audioBasename?: string;
|
|
96
|
+
/**
|
|
97
|
+
* Provenance written to the sidecar's `generator`. Defaults to the
|
|
98
|
+
* teleprompter aligner (`squisq-teleprompter` / `dsp-align`); a host that
|
|
99
|
+
* produced the timings itself names itself here — e.g.
|
|
100
|
+
* `{ name: 'my-tts', method: 'tts' }` for generated narration.
|
|
101
|
+
*/
|
|
102
|
+
generator?: NarrationTimingGenerator;
|
|
95
103
|
}
|
|
96
104
|
interface NarrationSavePlan {
|
|
97
105
|
audioRelativeName: string;
|
|
@@ -305,4 +313,4 @@ declare function prompterVarsFromTheme(theme: Theme): Record<string, string>;
|
|
|
305
313
|
declare function ensureTeleprompterStyles(doc: Document): void;
|
|
306
314
|
declare const TELEPROMPTER_CSS = "\n.squisq-teleprompter-root {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-height: 0;\n background: var(--squisq-bg, #f5f5f5);\n outline: none;\n}\n.squisq-teleprompter-stage {\n position: relative;\n flex: 1;\n min-height: 0;\n display: flex;\n}\n\n.squisq-teleprompter-surface {\n position: relative;\n flex: 1;\n min-height: 0;\n overflow: hidden;\n background: var(--squisq-prompter-bg, #101014);\n color: var(--squisq-prompter-text, #f5f5f2);\n font-family: var(--squisq-prompter-font, system-ui, sans-serif);\n user-select: none;\n}\n.squisq-teleprompter-flip {\n position: absolute;\n inset: 0;\n}\n.squisq-teleprompter-surface--mirrored .squisq-teleprompter-flip {\n transform: scaleX(-1);\n}\n.squisq-teleprompter-scroll {\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n will-change: transform;\n padding: 40vh 8% 60vh;\n box-sizing: border-box;\n}\n.squisq-teleprompter-line-guide {\n position: absolute;\n left: 0;\n right: 0;\n pointer-events: none;\n z-index: 2;\n}\n.squisq-teleprompter-line-guide::before,\n.squisq-teleprompter-line-guide::after {\n content: '';\n position: absolute;\n top: 50%;\n border: 9px solid transparent;\n transform: translateY(-50%);\n}\n.squisq-teleprompter-line-guide::before {\n left: 6px;\n border-left-color: var(--squisq-prompter-accent, #e8b64c);\n}\n.squisq-teleprompter-line-guide::after {\n right: 6px;\n border-right-color: var(--squisq-prompter-accent, #e8b64c);\n}\n.squisq-teleprompter-guide-band {\n position: absolute;\n left: 0;\n right: 0;\n background: color-mix(in srgb, var(--squisq-prompter-accent, #e8b64c) 9%, transparent);\n pointer-events: none;\n z-index: 1;\n}\n\n.squisq-teleprompter-block-marker {\n display: block;\n margin: 1.1em 0 0.35em;\n font-size: 0.38em;\n font-weight: 600;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: var(--squisq-prompter-muted, #9a9aa0);\n border-top: 1px solid color-mix(in srgb, var(--squisq-prompter-muted, #9a9aa0) 35%, transparent);\n padding-top: 0.6em;\n}\n.squisq-teleprompter-para {\n margin: 0 0 0.55em;\n line-height: 1.4;\n font-weight: 500;\n}\n.squisq-teleprompter-word {\n opacity: 0.45;\n transition: color 0.1s linear, opacity 0.1s linear;\n}\n.squisq-teleprompter-word--read {\n opacity: 0.9;\n}\n.squisq-teleprompter-word--active {\n opacity: 1;\n font-weight: 800;\n color: var(--squisq-prompter-accent, #e8b64c);\n}\n\n.squisq-teleprompter-countdown {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 3;\n background: color-mix(in srgb, var(--squisq-prompter-bg, #101014) 72%, transparent);\n}\n.squisq-teleprompter-countdown-digit {\n font-size: 18vmin;\n font-weight: 800;\n color: var(--squisq-prompter-accent, #e8b64c);\n animation: squisq-prompter-pulse 1s ease-in-out infinite;\n}\n.squisq-teleprompter-recdot {\n position: absolute;\n top: 14px;\n right: 16px;\n z-index: 4;\n width: 14px;\n height: 14px;\n border-radius: 50%;\n background: #e5484d;\n box-shadow: 0 0 0 3px color-mix(in srgb, #e5484d 30%, transparent);\n animation: squisq-prompter-pulse 1.4s ease-in-out infinite;\n}\n/* Counter-flip overlays so they stay readable in mirror mode. */\n.squisq-teleprompter-surface--mirrored .squisq-teleprompter-countdown,\n.squisq-teleprompter-surface--mirrored .squisq-teleprompter-recdot {\n transform: scaleX(-1);\n}\n\n.squisq-teleprompter-selfview {\n position: absolute;\n right: 14px;\n bottom: 14px;\n width: 160px;\n border-radius: 8px;\n border: 2px solid color-mix(in srgb, #e5484d 60%, transparent);\n z-index: 5;\n background: #000;\n}\n\n.squisq-teleprompter-review {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 10px;\n padding: 8px 12px;\n border-top: 1px solid var(--squisq-border, #e5e7eb);\n background: var(--squisq-surface, var(--squisq-bg, #fff));\n color: var(--squisq-text, #111827);\n font-size: 12.5px;\n}\n.squisq-teleprompter-review audio {\n height: 28px;\n max-width: 260px;\n}\n.squisq-teleprompter-review button {\n font: inherit;\n color: inherit;\n background: var(--squisq-input-bg, #fff);\n border: 1px solid var(--squisq-border, #d1d5db);\n border-radius: 6px;\n padding: 4px 10px;\n cursor: pointer;\n}\n\n.squisq-teleprompter-float-note {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n color: var(--squisq-text-muted, #4b5563);\n font-size: 14px;\n}\n\n.squisq-teleprompter-controls {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 10px;\n padding: 8px 12px;\n border-top: 1px solid var(--squisq-border, #e5e7eb);\n background: var(--squisq-surface, var(--squisq-bg, #fff));\n color: var(--squisq-text, #111827);\n font-size: 12.5px;\n}\n.squisq-teleprompter-controls .squisq-teleprompter-group {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n white-space: nowrap;\n}\n.squisq-teleprompter-controls button {\n font: inherit;\n color: inherit;\n background: var(--squisq-input-bg, #fff);\n border: 1px solid var(--squisq-border, #d1d5db);\n border-radius: 6px;\n padding: 4px 10px;\n cursor: pointer;\n}\n.squisq-teleprompter-controls button:hover {\n background: var(--squisq-surface-hover, #f3f4f6);\n}\n.squisq-teleprompter-controls button[aria-pressed='true'] {\n background: var(--squisq-text, #111827);\n color: var(--squisq-bg, #fff);\n border-color: var(--squisq-text, #111827);\n}\n.squisq-teleprompter-controls select,\n.squisq-teleprompter-controls input[type='range'] {\n font: inherit;\n color: inherit;\n background: var(--squisq-input-bg, #fff);\n border: 1px solid var(--squisq-border, #d1d5db);\n border-radius: 6px;\n max-width: 170px;\n}\n.squisq-teleprompter-controls input[type='range'] {\n border: none;\n background: transparent;\n width: 110px;\n}\n.squisq-teleprompter-meter {\n position: relative;\n width: 64px;\n height: 8px;\n border-radius: 4px;\n overflow: hidden;\n background: var(--squisq-border, #e5e7eb);\n}\n.squisq-teleprompter-meter-fill {\n position: absolute;\n inset: 0 auto 0 0;\n background: #9ca3af;\n transition: width 0.08s linear;\n}\n.squisq-teleprompter-meter--voice .squisq-teleprompter-meter-fill {\n background: #30a46c;\n}\n\n@keyframes squisq-prompter-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.45; }\n}\n@media (prefers-reduced-motion: reduce) {\n .squisq-teleprompter-countdown-digit,\n .squisq-teleprompter-recdot {\n animation: none;\n }\n .squisq-teleprompter-word {\n transition: none;\n }\n}\n";
|
|
307
315
|
|
|
308
|
-
export { EYE_LINE_FRACTION, FloatingWindowHandle, type NarrationSavePlan, type NarrationSaveResult, NarrationStage, NarrationStageHandle, type NarrationStageProps, PCM_WORKLET_NAME, PCM_WORKLET_SOURCE, TELEPROMPTER_CSS, TeleprompterController, TeleprompterControls, type TeleprompterControlsProps, TeleprompterRecordingDeps, TeleprompterSurface, type TeleprompterSurfaceProps, TeleprompterView, type TeleprompterViewProps, type TokenLineMap, buildNarrationSavePlan, cameraVideoLine, ensureTeleprompterStyles, executeNarrationSave, insertNarrationPreamble, measureTokenLines, narrationAnnotationLine, prompterVarsFromTheme, registerPcmWorklet, stepScroll, targetOffsetFor };
|
|
316
|
+
export { EYE_LINE_FRACTION, type ExecuteNarrationSaveDeps, FloatingWindowHandle, type NarrationSavePlan, type NarrationSavePlanArgs, type NarrationSaveProgress, type NarrationSaveResult, NarrationStage, NarrationStageHandle, type NarrationStageProps, PCM_WORKLET_NAME, PCM_WORKLET_SOURCE, TELEPROMPTER_CSS, TeleprompterController, TeleprompterControls, type TeleprompterControlsProps, TeleprompterRecordingDeps, TeleprompterSurface, type TeleprompterSurfaceProps, TeleprompterView, type TeleprompterViewProps, type TokenLineMap, buildNarrationSavePlan, cameraVideoLine, ensureTeleprompterStyles, executeNarrationSave, insertNarrationPreamble, measureTokenLines, narrationAnnotationLine, prompterVarsFromTheme, registerPcmWorklet, stepScroll, targetOffsetFor };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
TeleprompterView
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-DJS5I5NO.js";
|
|
4
4
|
import {
|
|
5
5
|
DEFAULT_TELEPROMPTER_PREFS,
|
|
6
6
|
EYE_LINE_FRACTION,
|
|
@@ -31,7 +31,8 @@ import {
|
|
|
31
31
|
useNarrationStage,
|
|
32
32
|
useTeleprompter,
|
|
33
33
|
vadConfigForSensitivity
|
|
34
|
-
} from "../chunk-
|
|
34
|
+
} from "../chunk-ZS6G2P57.js";
|
|
35
|
+
import "../chunk-DNJ6HOES.js";
|
|
35
36
|
export {
|
|
36
37
|
DEFAULT_TELEPROMPTER_PREFS,
|
|
37
38
|
EYE_LINE_FRACTION,
|
|
@@ -380,6 +380,13 @@ interface TeleprompterController {
|
|
|
380
380
|
prefs: TeleprompterPrefs;
|
|
381
381
|
setPrefs: (patch: Partial<TeleprompterPrefs>) => void;
|
|
382
382
|
play: () => void;
|
|
383
|
+
/**
|
|
384
|
+
* `play()`, resolving when the prompter actually rolls: true once the
|
|
385
|
+
* countdown finishes (at once if there is none or it is already rolling),
|
|
386
|
+
* false if pause/restart/unmount cancels the countdown first. Capture
|
|
387
|
+
* awaits this so a take opens at "go", not on "3, 2, 1".
|
|
388
|
+
*/
|
|
389
|
+
countIn: () => Promise<boolean>;
|
|
383
390
|
pause: () => void;
|
|
384
391
|
restart: () => void;
|
|
385
392
|
/** Move by spoken words and re-anchor voice tracking. */
|
|
@@ -519,8 +526,15 @@ interface UseNarrationRecorderOptions {
|
|
|
519
526
|
cameraRecorderOptions?: NarrationMediaRecorderOptions;
|
|
520
527
|
/** Combined audio/camera soft stop threshold; defaults to 90 MiB. Final chunks are retained. */
|
|
521
528
|
maxRecordingBytes?: number;
|
|
522
|
-
/**
|
|
529
|
+
/**
|
|
530
|
+
* Awaited once every device is live, immediately before the first byte is
|
|
531
|
+
* captured — the prompter's countdown. Resolving false cancels the start
|
|
532
|
+
* with nothing recorded.
|
|
533
|
+
*/
|
|
534
|
+
countIn?: () => Promise<boolean>;
|
|
535
|
+
/** Fired once capture is running. */
|
|
523
536
|
onRecordingStart?: () => void;
|
|
537
|
+
/** Fired on stop, including a stop while the start (or its count-in) is pending. */
|
|
524
538
|
onRecordingStop?: () => void;
|
|
525
539
|
}
|
|
526
540
|
interface NarrationRecorderController {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bendyline/squisq-editor-react",
|
|
3
|
-
"version": "2.11.
|
|
3
|
+
"version": "2.11.19",
|
|
4
4
|
"description": "React editor shell with raw/WYSIWYG/preview modes for Squisq documents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Bendyline",
|
|
@@ -76,6 +76,10 @@
|
|
|
76
76
|
"types": "./dist/review/index.d.ts",
|
|
77
77
|
"import": "./dist/review/index.js"
|
|
78
78
|
},
|
|
79
|
+
"./speech": {
|
|
80
|
+
"types": "./dist/speech/index.d.ts",
|
|
81
|
+
"import": "./dist/speech/index.js"
|
|
82
|
+
},
|
|
79
83
|
"./styles": {
|
|
80
84
|
"types": "./dist/styles/index.d.ts",
|
|
81
85
|
"default": "./dist/styles/index.css"
|
|
@@ -98,12 +102,12 @@
|
|
|
98
102
|
}
|
|
99
103
|
},
|
|
100
104
|
"dependencies": {
|
|
101
|
-
"@bendyline/squisq": "2.11.
|
|
105
|
+
"@bendyline/squisq": "2.11.16",
|
|
102
106
|
"@bendyline/squisq-calc": "2.11.1",
|
|
103
|
-
"@bendyline/squisq-formats": "2.6.
|
|
104
|
-
"@bendyline/squisq-grid-react": "2.11.
|
|
105
|
-
"@bendyline/squisq-react": "2.11.
|
|
106
|
-
"@bendyline/squisq-video-react": "2.4.
|
|
107
|
+
"@bendyline/squisq-formats": "2.6.16",
|
|
108
|
+
"@bendyline/squisq-grid-react": "2.11.17",
|
|
109
|
+
"@bendyline/squisq-react": "2.11.18",
|
|
110
|
+
"@bendyline/squisq-video-react": "2.4.22",
|
|
107
111
|
"@fortawesome/fontawesome-free": "7.2.0",
|
|
108
112
|
"@tiptap/core": "2.27.3",
|
|
109
113
|
"@tiptap/extension-heading": "2.27.3",
|