@ssml-builder-js/ssml-editor-react 2.4.0
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/CHANGELOG.md +91 -0
- package/dist/index.d.mts +288 -0
- package/dist/index.d.ts +288 -0
- package/dist/index.js +5423 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +5385 -0
- package/dist/index.mjs.map +1 -0
- package/e2e/monaco-editor.spec.ts +126 -0
- package/package.json +57 -0
- package/src/SsmlEditor.tsx +1302 -0
- package/src/buttonVisibility.ts +36 -0
- package/src/clearSsmlDocument.ts +57 -0
- package/src/components/popovers/InsertionPopover.tsx +136 -0
- package/src/components/popovers/InsertionPopovers.tsx +47 -0
- package/src/components/popovers/ProsodyPopovers.tsx +78 -0
- package/src/components/popovers/TextPopovers.tsx +8 -0
- package/src/components/popovers/TimingPopovers.tsx +8 -0
- package/src/constants/ssmlPresets.ts +660 -0
- package/src/constants/ui.ts +11 -0
- package/src/editableSsml.ts +251 -0
- package/src/formatXml.ts +600 -0
- package/src/hooks/useSsmlEditorState.ts +704 -0
- package/src/hooks/useSsmlMonaco.ts +393 -0
- package/src/index.tsx +50 -0
- package/src/locales.ts +435 -0
- package/src/ssmlCodeAction.ts +144 -0
- package/src/ssmlCodeLens.ts +226 -0
- package/src/ssmlCompletion.ts +129 -0
- package/src/ssmlContext.ts +196 -0
- package/src/ssmlDiagnostics.ts +191 -0
- package/src/ssmlHover.ts +703 -0
- package/src/ssmlInsertion.ts +75 -0
- package/src/ssmlInsertions.ts +471 -0
- package/src/styles/editorStyles.ts +282 -0
- package/test/format-xml-edge-cases.test.ts +107 -0
- package/test/index.test.ts +597 -0
- package/test/randomized-editor-invariants.test.ts +520 -0
- package/test/ui-components.test.tsx +854 -0
- package/tsconfig.json +10 -0
- package/tsup.config.ts +17 -0
- package/vitest.config.mjs +10 -0
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
2
|
+
import type { Monaco, OnMount } from "@monaco-editor/react";
|
|
3
|
+
import { INLINE_BADGE_COPY, type SsmlEditorLanguage, type SsmlEditorLocale } from "../locales";
|
|
4
|
+
import {
|
|
5
|
+
clearSsmlDiagnostics,
|
|
6
|
+
type MonacoEditor,
|
|
7
|
+
type MonacoModel,
|
|
8
|
+
type SsmlSyntaxError,
|
|
9
|
+
updateSsmlDiagnostics,
|
|
10
|
+
} from "../ssmlDiagnostics";
|
|
11
|
+
import { registerSsmlCompletionProvider } from "../ssmlCompletion";
|
|
12
|
+
import { registerSsmlCodeActions } from "../ssmlCodeAction";
|
|
13
|
+
import { registerSsmlCodeLens, type SsmlCodeLensCallback } from "../ssmlCodeLens";
|
|
14
|
+
import { findSsmlHoverTarget, formatSsmlHover } from "../ssmlHover";
|
|
15
|
+
import { findActiveSsmlTags } from "../ssmlContext";
|
|
16
|
+
import type { MonacoEditorRef } from "./useSsmlEditorState";
|
|
17
|
+
|
|
18
|
+
type MonacoLanguages = Monaco["languages"];
|
|
19
|
+
type MonacoDisposable = ReturnType<MonacoEditor["onDidChangeCursorSelection"]>;
|
|
20
|
+
type MonacoContentDisposable = ReturnType<MonacoEditor["onDidChangeModelContent"]>;
|
|
21
|
+
type MonacoCursorPositionDisposable = ReturnType<MonacoEditor["onDidChangeCursorPosition"]>;
|
|
22
|
+
type MonacoCompletionDisposable = ReturnType<MonacoLanguages["registerCompletionItemProvider"]>;
|
|
23
|
+
type MonacoCodeActionDisposable = ReturnType<MonacoLanguages["registerCodeActionProvider"]>;
|
|
24
|
+
type MonacoCodeLensDisposable = ReturnType<MonacoLanguages["registerCodeLensProvider"]>;
|
|
25
|
+
type MonacoHoverProvider = Parameters<MonacoLanguages["registerHoverProvider"]>[1];
|
|
26
|
+
type MonacoHoverModel = Parameters<MonacoHoverProvider["provideHover"]>[0];
|
|
27
|
+
type MonacoHoverPosition = Parameters<MonacoHoverProvider["provideHover"]>[1];
|
|
28
|
+
type MonacoDecoration = Parameters<MonacoModel["deltaDecorations"]>[1][number];
|
|
29
|
+
|
|
30
|
+
const SSML_DIAGNOSTICS_DEBOUNCE_MS = 300;
|
|
31
|
+
|
|
32
|
+
interface HoverProviderRegistration {
|
|
33
|
+
disposable: ReturnType<MonacoLanguages["registerHoverProvider"]>;
|
|
34
|
+
references: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const hoverProviderRegistrations = new WeakMap<MonacoLanguages, Map<SsmlEditorLocale, HoverProviderRegistration>>();
|
|
38
|
+
|
|
39
|
+
export interface UseSsmlMonacoOptions {
|
|
40
|
+
editorRef?: MonacoEditorRef;
|
|
41
|
+
language: SsmlEditorLanguage;
|
|
42
|
+
text: string;
|
|
43
|
+
decorationsVisible: boolean;
|
|
44
|
+
enableCodeLens: boolean;
|
|
45
|
+
getOuterVoiceName: () => string | undefined;
|
|
46
|
+
onOpenPopover: SsmlCodeLensCallback;
|
|
47
|
+
onSelectionOverlayChange: (editor: MonacoEditor, notify: boolean) => void;
|
|
48
|
+
onActiveTagsChange: (tags: ReadonlySet<string>) => void;
|
|
49
|
+
onSyntaxErrorChange: (error: SsmlSyntaxError | null) => void;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface UseSsmlMonacoResult {
|
|
53
|
+
editorRef: { current: MonacoEditor | null };
|
|
54
|
+
onMount: OnMount;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function acquireSsmlHoverProvider(monaco: Monaco, locale: SsmlEditorLocale): () => void {
|
|
58
|
+
const languages = monaco.languages;
|
|
59
|
+
let registrations = hoverProviderRegistrations.get(languages);
|
|
60
|
+
if (!registrations) {
|
|
61
|
+
registrations = new Map();
|
|
62
|
+
hoverProviderRegistrations.set(languages, registrations);
|
|
63
|
+
}
|
|
64
|
+
let registration = registrations.get(locale);
|
|
65
|
+
|
|
66
|
+
if (!registration) {
|
|
67
|
+
const provider: MonacoHoverProvider = {
|
|
68
|
+
provideHover(model: MonacoHoverModel, position: MonacoHoverPosition) {
|
|
69
|
+
const target = findSsmlHoverTarget(model.getValue(), position.lineNumber, position.column);
|
|
70
|
+
if (!target) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
contents: [
|
|
76
|
+
{
|
|
77
|
+
isTrusted: false,
|
|
78
|
+
supportHtml: false,
|
|
79
|
+
value: formatSsmlHover(target, locale),
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
range: target.range,
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
const disposable = languages.registerHoverProvider("xml", provider);
|
|
87
|
+
registration = { disposable, references: 0 };
|
|
88
|
+
registrations.set(locale, registration);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
registration.references += 1;
|
|
92
|
+
let released = false;
|
|
93
|
+
return () => {
|
|
94
|
+
if (released) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
released = true;
|
|
98
|
+
|
|
99
|
+
const currentRegistrations = hoverProviderRegistrations.get(languages);
|
|
100
|
+
const current = currentRegistrations?.get(locale);
|
|
101
|
+
if (!current) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
current.references -= 1;
|
|
106
|
+
if (current.references === 0) {
|
|
107
|
+
current.disposable.dispose();
|
|
108
|
+
currentRegistrations?.delete(locale);
|
|
109
|
+
if (currentRegistrations?.size === 0) {
|
|
110
|
+
hoverProviderRegistrations.delete(languages);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function getSsmlAttributeValue(tag: string, attributeName: string): string | undefined {
|
|
117
|
+
const escapedAttributeName = attributeName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
118
|
+
const match = tag.match(new RegExp(`\\b${escapedAttributeName}\\s*=\\s*("|')([^"']*)\\1`, "i"));
|
|
119
|
+
return match?.[2];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function getSsmlInlineDecorations(model: MonacoModel, value: string, language: SsmlEditorLanguage): MonacoDecoration[] {
|
|
123
|
+
const decorations: MonacoDecoration[] = [];
|
|
124
|
+
const tagPattern = /<(break|prosody)\b[^>]*>/gi;
|
|
125
|
+
const badgeCopy = INLINE_BADGE_COPY[language];
|
|
126
|
+
|
|
127
|
+
for (const match of value.matchAll(tagPattern)) {
|
|
128
|
+
const tag = match[0];
|
|
129
|
+
const tagName = match[1].toLowerCase();
|
|
130
|
+
const startOffset = match.index ?? 0;
|
|
131
|
+
const endOffset = startOffset + tag.length;
|
|
132
|
+
const start = model.getPositionAt(startOffset);
|
|
133
|
+
const end = model.getPositionAt(endOffset);
|
|
134
|
+
const pauseValue = getSsmlAttributeValue(tag, "time") ?? getSsmlAttributeValue(tag, "strength");
|
|
135
|
+
const pitchValue =
|
|
136
|
+
getSsmlAttributeValue(tag, "pitch") ??
|
|
137
|
+
getSsmlAttributeValue(tag, "contour") ??
|
|
138
|
+
getSsmlAttributeValue(tag, "range");
|
|
139
|
+
const prosodyValue = pitchValue ?? getSsmlAttributeValue(tag, "rate") ?? getSsmlAttributeValue(tag, "volume");
|
|
140
|
+
|
|
141
|
+
const badge =
|
|
142
|
+
tagName === "break"
|
|
143
|
+
? `${badgeCopy.pause}${pauseValue ? ` ${pauseValue}` : ""}`
|
|
144
|
+
: `${pitchValue ? badgeCopy.pitch : badgeCopy.prosody}${prosodyValue ? ` ${prosodyValue}` : ""}`;
|
|
145
|
+
|
|
146
|
+
decorations.push({
|
|
147
|
+
range: {
|
|
148
|
+
startLineNumber: start.lineNumber,
|
|
149
|
+
startColumn: start.column,
|
|
150
|
+
endLineNumber: end.lineNumber,
|
|
151
|
+
endColumn: end.column,
|
|
152
|
+
},
|
|
153
|
+
options: {
|
|
154
|
+
after: {
|
|
155
|
+
content: ` ${badge}`,
|
|
156
|
+
inlineClassName: `ssml-editor-inline-badge ssml-editor-inline-badge-${tagName === "break" ? "pause" : "prosody"}`,
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return decorations;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function syncSsmlInlineDecorations(
|
|
166
|
+
model: MonacoModel,
|
|
167
|
+
value: string,
|
|
168
|
+
language: SsmlEditorLanguage,
|
|
169
|
+
showDecorations: boolean,
|
|
170
|
+
decorationIds: string[],
|
|
171
|
+
): string[] {
|
|
172
|
+
return model.deltaDecorations(decorationIds, showDecorations ? getSsmlInlineDecorations(model, value, language) : []);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function useSsmlMonaco({
|
|
176
|
+
editorRef: externalEditorRef,
|
|
177
|
+
language,
|
|
178
|
+
text,
|
|
179
|
+
decorationsVisible,
|
|
180
|
+
enableCodeLens,
|
|
181
|
+
getOuterVoiceName,
|
|
182
|
+
onOpenPopover,
|
|
183
|
+
onSelectionOverlayChange,
|
|
184
|
+
onActiveTagsChange,
|
|
185
|
+
onSyntaxErrorChange,
|
|
186
|
+
}: UseSsmlMonacoOptions): UseSsmlMonacoResult {
|
|
187
|
+
const internalEditorRef = useRef<MonacoEditor | null>(null);
|
|
188
|
+
const editorRef = useRef(externalEditorRef ?? internalEditorRef).current;
|
|
189
|
+
const monacoRef = useRef<Monaco | null>(null);
|
|
190
|
+
const completionProviderRef = useRef<MonacoCompletionDisposable | null>(null);
|
|
191
|
+
const codeActionProviderRef = useRef<MonacoCodeActionDisposable | null>(null);
|
|
192
|
+
const codeLensProviderRef = useRef<MonacoCodeLensDisposable | null>(null);
|
|
193
|
+
const releaseHoverProviderRef = useRef<(() => void) | null>(null);
|
|
194
|
+
const selectionChangeRef = useRef<MonacoDisposable | null>(null);
|
|
195
|
+
const cursorPositionChangeRef = useRef<MonacoCursorPositionDisposable | null>(null);
|
|
196
|
+
const diagnosticsChangeRef = useRef<MonacoContentDisposable | null>(null);
|
|
197
|
+
const diagnosticsTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
198
|
+
const selectionLayoutDisposablesRef = useRef<MonacoDisposable[]>([]);
|
|
199
|
+
const inlineDecorationIdsRef = useRef<string[]>([]);
|
|
200
|
+
const languageRef = useRef(language);
|
|
201
|
+
const decorationsVisibleRef = useRef(decorationsVisible);
|
|
202
|
+
const enableCodeLensRef = useRef(enableCodeLens);
|
|
203
|
+
const getOuterVoiceNameRef = useRef(getOuterVoiceName);
|
|
204
|
+
const onSelectionOverlayChangeRef = useRef(onSelectionOverlayChange);
|
|
205
|
+
const onActiveTagsChangeRef = useRef(onActiveTagsChange);
|
|
206
|
+
const onSyntaxErrorChangeRef = useRef(onSyntaxErrorChange);
|
|
207
|
+
const onOpenPopoverRef = useRef(onOpenPopover);
|
|
208
|
+
|
|
209
|
+
languageRef.current = language;
|
|
210
|
+
decorationsVisibleRef.current = decorationsVisible;
|
|
211
|
+
enableCodeLensRef.current = enableCodeLens;
|
|
212
|
+
getOuterVoiceNameRef.current = getOuterVoiceName;
|
|
213
|
+
onSelectionOverlayChangeRef.current = onSelectionOverlayChange;
|
|
214
|
+
onActiveTagsChangeRef.current = onActiveTagsChange;
|
|
215
|
+
onSyntaxErrorChangeRef.current = onSyntaxErrorChange;
|
|
216
|
+
onOpenPopoverRef.current = onOpenPopover;
|
|
217
|
+
|
|
218
|
+
const runSsmlDiagnostics = useCallback((editor: MonacoEditor, monaco: Monaco): void => {
|
|
219
|
+
const model = editor.getModel();
|
|
220
|
+
if (!model) {
|
|
221
|
+
onSyntaxErrorChangeRef.current(null);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
onSyntaxErrorChangeRef.current(updateSsmlDiagnostics(monaco, model));
|
|
226
|
+
}, []);
|
|
227
|
+
|
|
228
|
+
const scheduleSsmlDiagnostics = useCallback(
|
|
229
|
+
(editor: MonacoEditor, monaco: Monaco): void => {
|
|
230
|
+
if (diagnosticsTimeoutRef.current !== null) {
|
|
231
|
+
clearTimeout(diagnosticsTimeoutRef.current);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
diagnosticsTimeoutRef.current = setTimeout(() => {
|
|
235
|
+
diagnosticsTimeoutRef.current = null;
|
|
236
|
+
runSsmlDiagnostics(editor, monaco);
|
|
237
|
+
}, SSML_DIAGNOSTICS_DEBOUNCE_MS);
|
|
238
|
+
},
|
|
239
|
+
[runSsmlDiagnostics],
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
const clearSsmlDiagnosticsResources = useCallback((): void => {
|
|
243
|
+
if (diagnosticsTimeoutRef.current !== null) {
|
|
244
|
+
clearTimeout(diagnosticsTimeoutRef.current);
|
|
245
|
+
diagnosticsTimeoutRef.current = null;
|
|
246
|
+
}
|
|
247
|
+
diagnosticsChangeRef.current?.dispose();
|
|
248
|
+
diagnosticsChangeRef.current = null;
|
|
249
|
+
}, []);
|
|
250
|
+
|
|
251
|
+
const syncActiveTags = useCallback((editor: MonacoEditor): void => {
|
|
252
|
+
const model = editor.getModel();
|
|
253
|
+
const position = editor.getPosition();
|
|
254
|
+
if (!model || !position) {
|
|
255
|
+
onActiveTagsChangeRef.current(new Set());
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
onActiveTagsChangeRef.current(findActiveSsmlTags(model.getValue(), model.getOffsetAt(position)));
|
|
259
|
+
}, []);
|
|
260
|
+
|
|
261
|
+
const disposeMonacoResources = useCallback((): void => {
|
|
262
|
+
clearSsmlDiagnosticsResources();
|
|
263
|
+
const model = editorRef.current?.getModel();
|
|
264
|
+
if (model && monacoRef.current) {
|
|
265
|
+
clearSsmlDiagnostics(monacoRef.current, model);
|
|
266
|
+
inlineDecorationIdsRef.current = model.deltaDecorations(inlineDecorationIdsRef.current, []);
|
|
267
|
+
}
|
|
268
|
+
selectionChangeRef.current?.dispose();
|
|
269
|
+
selectionChangeRef.current = null;
|
|
270
|
+
cursorPositionChangeRef.current?.dispose();
|
|
271
|
+
cursorPositionChangeRef.current = null;
|
|
272
|
+
completionProviderRef.current?.dispose();
|
|
273
|
+
completionProviderRef.current = null;
|
|
274
|
+
codeActionProviderRef.current?.dispose();
|
|
275
|
+
codeActionProviderRef.current = null;
|
|
276
|
+
codeLensProviderRef.current?.dispose();
|
|
277
|
+
codeLensProviderRef.current = null;
|
|
278
|
+
for (const disposable of selectionLayoutDisposablesRef.current) {
|
|
279
|
+
disposable.dispose();
|
|
280
|
+
}
|
|
281
|
+
selectionLayoutDisposablesRef.current = [];
|
|
282
|
+
releaseHoverProviderRef.current?.();
|
|
283
|
+
releaseHoverProviderRef.current = null;
|
|
284
|
+
editorRef.current = null;
|
|
285
|
+
monacoRef.current = null;
|
|
286
|
+
}, [clearSsmlDiagnosticsResources, editorRef]);
|
|
287
|
+
|
|
288
|
+
const onMount = useCallback<OnMount>(
|
|
289
|
+
(editor, monaco) => {
|
|
290
|
+
disposeMonacoResources();
|
|
291
|
+
editorRef.current = editor;
|
|
292
|
+
monacoRef.current = monaco;
|
|
293
|
+
selectionChangeRef.current = editor.onDidChangeCursorSelection(() => {
|
|
294
|
+
onSelectionOverlayChangeRef.current(editor, true);
|
|
295
|
+
});
|
|
296
|
+
cursorPositionChangeRef.current = editor.onDidChangeCursorPosition(() => {
|
|
297
|
+
syncActiveTags(editor);
|
|
298
|
+
});
|
|
299
|
+
diagnosticsChangeRef.current = editor.onDidChangeModelContent(() => {
|
|
300
|
+
const model = editor.getModel();
|
|
301
|
+
if (model) {
|
|
302
|
+
inlineDecorationIdsRef.current = syncSsmlInlineDecorations(
|
|
303
|
+
model,
|
|
304
|
+
model.getValue(),
|
|
305
|
+
languageRef.current,
|
|
306
|
+
decorationsVisibleRef.current,
|
|
307
|
+
inlineDecorationIdsRef.current,
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
syncActiveTags(editor);
|
|
311
|
+
scheduleSsmlDiagnostics(editor, monaco);
|
|
312
|
+
});
|
|
313
|
+
selectionLayoutDisposablesRef.current = [
|
|
314
|
+
editor.onDidScrollChange(() => onSelectionOverlayChangeRef.current(editor, false)),
|
|
315
|
+
editor.onDidLayoutChange(() => onSelectionOverlayChangeRef.current(editor, false)),
|
|
316
|
+
editor.onDidContentSizeChange(() => onSelectionOverlayChangeRef.current(editor, false)),
|
|
317
|
+
];
|
|
318
|
+
completionProviderRef.current = registerSsmlCompletionProvider(monaco, {
|
|
319
|
+
getOuterVoiceName: () => getOuterVoiceNameRef.current(),
|
|
320
|
+
model: editor.getModel(),
|
|
321
|
+
});
|
|
322
|
+
codeActionProviderRef.current = registerSsmlCodeActions(monaco);
|
|
323
|
+
if (enableCodeLensRef.current) {
|
|
324
|
+
codeLensProviderRef.current = registerSsmlCodeLens(monaco, editor, (action) =>
|
|
325
|
+
onOpenPopoverRef.current(action),
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
releaseHoverProviderRef.current = acquireSsmlHoverProvider(monaco, languageRef.current);
|
|
329
|
+
runSsmlDiagnostics(editor, monaco);
|
|
330
|
+
const model = editor.getModel();
|
|
331
|
+
if (model) {
|
|
332
|
+
inlineDecorationIdsRef.current = syncSsmlInlineDecorations(
|
|
333
|
+
model,
|
|
334
|
+
editor.getValue(),
|
|
335
|
+
languageRef.current,
|
|
336
|
+
decorationsVisibleRef.current,
|
|
337
|
+
inlineDecorationIdsRef.current,
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
onSelectionOverlayChangeRef.current(editor, true);
|
|
341
|
+
syncActiveTags(editor);
|
|
342
|
+
},
|
|
343
|
+
[disposeMonacoResources, editorRef, runSsmlDiagnostics, scheduleSsmlDiagnostics, syncActiveTags],
|
|
344
|
+
);
|
|
345
|
+
|
|
346
|
+
useEffect(() => {
|
|
347
|
+
const editor = editorRef.current;
|
|
348
|
+
const monaco = monacoRef.current;
|
|
349
|
+
if (!editor || !monaco) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
codeLensProviderRef.current?.dispose();
|
|
354
|
+
codeLensProviderRef.current = enableCodeLens
|
|
355
|
+
? registerSsmlCodeLens(monaco, editor, (action) => onOpenPopoverRef.current(action))
|
|
356
|
+
: null;
|
|
357
|
+
}, [editorRef, enableCodeLens]);
|
|
358
|
+
|
|
359
|
+
useEffect(() => {
|
|
360
|
+
const monaco = monacoRef.current;
|
|
361
|
+
if (!monaco) {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
releaseHoverProviderRef.current?.();
|
|
366
|
+
releaseHoverProviderRef.current = acquireSsmlHoverProvider(monaco, language);
|
|
367
|
+
}, [language]);
|
|
368
|
+
|
|
369
|
+
const previousTextRef = useRef<string | null>(null);
|
|
370
|
+
useEffect(() => {
|
|
371
|
+
const editor = editorRef.current;
|
|
372
|
+
const monaco = monacoRef.current;
|
|
373
|
+
const model = editor?.getModel();
|
|
374
|
+
if (editor && model && monaco) {
|
|
375
|
+
const previousText = previousTextRef.current;
|
|
376
|
+
previousTextRef.current = text;
|
|
377
|
+
inlineDecorationIdsRef.current = syncSsmlInlineDecorations(
|
|
378
|
+
model,
|
|
379
|
+
text,
|
|
380
|
+
language,
|
|
381
|
+
decorationsVisible,
|
|
382
|
+
inlineDecorationIdsRef.current,
|
|
383
|
+
);
|
|
384
|
+
if (previousText === null || previousText !== text) {
|
|
385
|
+
scheduleSsmlDiagnostics(editor, monaco);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}, [decorationsVisible, editorRef, language, scheduleSsmlDiagnostics, text]);
|
|
389
|
+
|
|
390
|
+
useEffect(() => disposeMonacoResources, [disposeMonacoResources]);
|
|
391
|
+
|
|
392
|
+
return { editorRef, onMount };
|
|
393
|
+
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ssml-editor-react: React-based SSML editor component.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { SsmlEditor } from "./SsmlEditor";
|
|
6
|
+
export type {
|
|
7
|
+
SsmlEditorButton,
|
|
8
|
+
SsmlEditorButtonVisibility,
|
|
9
|
+
SsmlEditorInsertionButton,
|
|
10
|
+
} from "./buttonVisibility";
|
|
11
|
+
export type {
|
|
12
|
+
SsmlEditorLineNumbers,
|
|
13
|
+
SsmlEditorCustomInsertion,
|
|
14
|
+
SsmlEditorCustomInsertionCollection,
|
|
15
|
+
SsmlEditorCustomInsertionDefinition,
|
|
16
|
+
SsmlEditorInsertionDefinition,
|
|
17
|
+
SsmlEditorInsertionGroup,
|
|
18
|
+
SsmlEditorInsertionMode,
|
|
19
|
+
SsmlEditorInsertionOption,
|
|
20
|
+
SsmlEditorInsertionTemplate,
|
|
21
|
+
SsmlEditorOptions,
|
|
22
|
+
SsmlEditorRef,
|
|
23
|
+
SsmlEditorProps,
|
|
24
|
+
SsmlEditorToolbarGroup,
|
|
25
|
+
SelectionInfo,
|
|
26
|
+
SsmlEditorTheme,
|
|
27
|
+
SsmlEditorWordWrap,
|
|
28
|
+
SsmlInsertionDefinition,
|
|
29
|
+
SsmlInsertionOption,
|
|
30
|
+
SsmlInsertionTemplate,
|
|
31
|
+
} from "./SsmlEditor";
|
|
32
|
+
export { EDITOR_COPY, INLINE_BADGE_COPY, SSML_HOVER_COPY } from "./locales";
|
|
33
|
+
export type {
|
|
34
|
+
EditorCopy,
|
|
35
|
+
InlineBadgeCopy,
|
|
36
|
+
SsmlEditorLanguage,
|
|
37
|
+
SsmlEditorLocale,
|
|
38
|
+
SsmlEditorLocalizedText,
|
|
39
|
+
SsmlHoverLocale,
|
|
40
|
+
SsmlHoverParameterCopy,
|
|
41
|
+
SsmlHoverTagCopy,
|
|
42
|
+
} from "./locales";
|
|
43
|
+
export {
|
|
44
|
+
createSsmlEditorInsertionDefinition,
|
|
45
|
+
SSML_INSERTIONS,
|
|
46
|
+
} from "./SsmlEditor";
|
|
47
|
+
export { registerSsmlCodeLens } from "./ssmlCodeLens";
|
|
48
|
+
export type { SsmlCodeLensAction, SsmlCodeLensCallback } from "./ssmlCodeLens";
|
|
49
|
+
export { updateTagAttribute } from "./ssmlContext";
|
|
50
|
+
export type { SsmlTagRange } from "./ssmlContext";
|