ssml-builder-js 2.1.0 → 2.2.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/dist/react.mjs CHANGED
@@ -1,7 +1,48 @@
1
1
  "use client";
2
2
  import {
3
- __export
4
- } from "./chunk-6S5ODO6A.mjs";
3
+ BREAK_TIME_DESCRIPTIONS,
4
+ BREAK_TIME_PRESETS,
5
+ DEFAULT_LOCALE,
6
+ EDITOR_COPY,
7
+ EMPHASIS_LEVEL_DESCRIPTIONS,
8
+ EMPHASIS_LEVEL_PRESETS,
9
+ EXPRESS_AS_STYLE_DESCRIPTIONS,
10
+ EXPRESS_AS_STYLE_PRESETS,
11
+ INLINE_BADGE_COPY,
12
+ LANGUAGE_DESCRIPTIONS,
13
+ LANGUAGE_PRESETS,
14
+ OVERLAY_Z_INDEX,
15
+ PROSODY_PITCH_DESCRIPTIONS,
16
+ PROSODY_PITCH_PRESETS,
17
+ PROSODY_RATE_DESCRIPTIONS,
18
+ PROSODY_RATE_PRESETS,
19
+ PROSODY_VOLUME_DESCRIPTIONS,
20
+ PROSODY_VOLUME_PRESETS,
21
+ SAY_AS_DESCRIPTIONS,
22
+ SAY_AS_PRESETS,
23
+ SELECTION_OVERLAY_ABOVE_THRESHOLD_LINES,
24
+ SILENCE_VALUE_DESCRIPTIONS,
25
+ SILENCE_VALUE_PRESETS,
26
+ SSML_ATTRIBUTE_PRESETS,
27
+ SSML_HOVER_COPY,
28
+ buildPartialSsml,
29
+ buildSsml,
30
+ clearSsmlDocument,
31
+ createSsmlInsertionEdit,
32
+ findActiveSsmlTags,
33
+ findSsmlHoverTarget,
34
+ findSsmlVoiceContext,
35
+ formatSsmlHover,
36
+ formatXmlFragment,
37
+ getEditableRegion,
38
+ getEditableText,
39
+ getExpressAsStyleCategory,
40
+ registerSsmlCompletionProvider,
41
+ resolveExpressAsStyles,
42
+ updateEditableText,
43
+ validateSsml
44
+ } from "./chunk-2XJER2BG.mjs";
45
+ import "./chunk-6S5ODO6A.mjs";
5
46
 
6
47
  // packages/ssml-editor-react/src/SsmlEditor.tsx
7
48
  import { Fragment, forwardRef, useImperativeHandle } from "react";
@@ -12,871 +53,6 @@ function isSsmlEditorButtonVisible(buttonVisibility, button) {
12
53
  return buttonVisibility?.[button] !== false;
13
54
  }
14
55
 
15
- // packages/ssml-editor-react/src/constants/ssmlPresets.ts
16
- var ssmlPresets_exports = {};
17
- __export(ssmlPresets_exports, {
18
- BREAK_STRENGTH_PRESETS: () => BREAK_STRENGTH_PRESETS,
19
- BREAK_TIME_DESCRIPTIONS: () => BREAK_TIME_DESCRIPTIONS,
20
- BREAK_TIME_PRESETS: () => BREAK_TIME_PRESETS,
21
- EMPHASIS_LEVEL_DESCRIPTIONS: () => EMPHASIS_LEVEL_DESCRIPTIONS,
22
- EMPHASIS_LEVEL_PRESETS: () => EMPHASIS_LEVEL_PRESETS,
23
- EXPRESS_AS_ROLE_PRESETS: () => EXPRESS_AS_ROLE_PRESETS,
24
- EXPRESS_AS_STYLES_BY_VOICE: () => EXPRESS_AS_STYLES_BY_VOICE,
25
- EXPRESS_AS_STYLE_DESCRIPTIONS: () => EXPRESS_AS_STYLE_DESCRIPTIONS,
26
- EXPRESS_AS_STYLE_PRESETS: () => EXPRESS_AS_STYLE_PRESETS,
27
- LANGUAGE_DESCRIPTIONS: () => LANGUAGE_DESCRIPTIONS,
28
- LANGUAGE_PRESETS: () => LANGUAGE_PRESETS,
29
- PHONEME_ALPHABET_PRESETS: () => PHONEME_ALPHABET_PRESETS,
30
- PROSODY_PITCH_DESCRIPTIONS: () => PROSODY_PITCH_DESCRIPTIONS,
31
- PROSODY_PITCH_PRESETS: () => PROSODY_PITCH_PRESETS,
32
- PROSODY_RATE_DESCRIPTIONS: () => PROSODY_RATE_DESCRIPTIONS,
33
- PROSODY_RATE_PRESETS: () => PROSODY_RATE_PRESETS,
34
- PROSODY_RATE_VALUES: () => PROSODY_RATE_VALUES,
35
- PROSODY_VOLUME_DESCRIPTIONS: () => PROSODY_VOLUME_DESCRIPTIONS,
36
- PROSODY_VOLUME_PRESETS: () => PROSODY_VOLUME_PRESETS,
37
- SAY_AS_DESCRIPTIONS: () => SAY_AS_DESCRIPTIONS,
38
- SAY_AS_PRESETS: () => SAY_AS_PRESETS,
39
- SILENCE_TYPE_PRESETS: () => SILENCE_TYPE_PRESETS,
40
- SILENCE_VALUE_DESCRIPTIONS: () => SILENCE_VALUE_DESCRIPTIONS,
41
- SILENCE_VALUE_PRESETS: () => SILENCE_VALUE_PRESETS,
42
- SSML_ATTRIBUTE_PRESETS: () => SSML_ATTRIBUTE_PRESETS,
43
- SSML_PRESETS: () => SSML_PRESETS,
44
- SSML_PRESET_EXAMPLES: () => SSML_PRESET_EXAMPLES,
45
- VISEME_TYPE_PRESETS: () => VISEME_TYPE_PRESETS,
46
- resolveExpressAsStyles: () => resolveExpressAsStyles
47
- });
48
- var SSML_PRESETS = [
49
- {
50
- id: "basic",
51
- label: "Basic speech",
52
- ssml: '<speak version="1.0" xml:lang="en-US">Hello, world!</speak>',
53
- description: "A minimal SSML document."
54
- },
55
- {
56
- id: "voice",
57
- label: "Voice selection",
58
- ssml: '<speak version="1.0" xml:lang="en-US"><voice name="en-US-JennyNeural">Hello, world!</voice></speak>',
59
- description: "Speaks text with a selected voice."
60
- },
61
- {
62
- id: "prosody",
63
- label: "Prosody",
64
- ssml: '<speak version="1.0" xml:lang="en-US"><prosody rate="fast" pitch="+2st">Hello, world!</prosody></speak>',
65
- description: "Adjusts the speech rate and pitch."
66
- }
67
- ];
68
- var BREAK_TIME_PRESETS = ["500ms", "1s", "2s", "3s"];
69
- var BREAK_STRENGTH_PRESETS = ["none", "x-weak", "weak", "medium", "strong", "x-strong"];
70
- var PROSODY_RATE_PRESETS = ["x-slow", "slow", "medium", "fast", "x-fast"];
71
- var PROSODY_RATE_VALUES = [...PROSODY_RATE_PRESETS, "percentage"];
72
- var PROSODY_PITCH_PRESETS = ["+2st", "-2st", "0st", "+4st", "-4st", "+8st", "-8st", "+12st", "-12st"];
73
- var PROSODY_VOLUME_PRESETS = ["silent", "x-soft", "soft", "medium", "loud", "x-loud"];
74
- var EXPRESS_AS_STYLE_PRESETS = [
75
- "cheerful",
76
- "friendly",
77
- "calm",
78
- "sad",
79
- "angry",
80
- "excited",
81
- "serious",
82
- "assistant",
83
- "chat",
84
- "customerservice",
85
- "hopeful",
86
- "newscast",
87
- "shouting",
88
- "terrified",
89
- "unfriendly",
90
- "whispering"
91
- ];
92
- var EXPRESS_AS_STYLES_BY_VOICE = {
93
- "ja-JP-NanamiNeural": ["chat", "cheerful", "customerservice"],
94
- "ja-JP-KeitaNeural": [],
95
- "en-US-JennyNeural": [
96
- "angry",
97
- "assistant",
98
- "chat",
99
- "cheerful",
100
- "customerservice",
101
- "excited",
102
- "friendly",
103
- "hopeful",
104
- "newscast",
105
- "sad",
106
- "shouting",
107
- "terrified",
108
- "unfriendly",
109
- "whispering"
110
- ],
111
- "en-US-GuyNeural": [
112
- "angry",
113
- "cheerful",
114
- "excited",
115
- "friendly",
116
- "hopeful",
117
- "newscast",
118
- "sad",
119
- "shouting",
120
- "terrified",
121
- "unfriendly",
122
- "whispering"
123
- ]
124
- };
125
- var EXPRESS_AS_STYLES_BY_NORMALIZED_VOICE = new Map(
126
- Object.entries(EXPRESS_AS_STYLES_BY_VOICE).map(([voiceName, styles]) => [voiceName.toLowerCase(), styles])
127
- );
128
- function resolveExpressAsStyles(voiceName, candidates = EXPRESS_AS_STYLE_PRESETS) {
129
- if (voiceName === void 0) {
130
- return candidates;
131
- }
132
- const supportedStyles = EXPRESS_AS_STYLES_BY_NORMALIZED_VOICE.get(voiceName.trim().toLowerCase());
133
- if (supportedStyles === void 0) {
134
- return candidates;
135
- }
136
- const supportedStyleSet = new Set(supportedStyles);
137
- return candidates.filter((style) => supportedStyleSet.has(style));
138
- }
139
- var EXPRESS_AS_ROLE_PRESETS = [
140
- "Girl",
141
- "Boy",
142
- "YoungAdultFemale",
143
- "YoungAdultMale",
144
- "OlderAdultFemale",
145
- "OlderAdultMale",
146
- "SeniorFemale",
147
- "SeniorMale"
148
- ];
149
- var EMPHASIS_LEVEL_PRESETS = ["strong", "moderate", "reduced", "none"];
150
- var SAY_AS_PRESETS = [
151
- "characters",
152
- "spell-out",
153
- "cardinal",
154
- "ordinal",
155
- "number",
156
- "date",
157
- "time",
158
- "telephone",
159
- "fraction",
160
- "address",
161
- "name",
162
- "currency"
163
- ];
164
- var LANGUAGE_PRESETS = ["ja-JP", "en-US", "de-DE", "fr-FR"];
165
- var SILENCE_VALUE_PRESETS = ["300ms", "500ms", "1s"];
166
- var SILENCE_TYPE_PRESETS = [
167
- "Leading",
168
- "Tailing",
169
- "Sentenceboundary",
170
- "Comma",
171
- "Semicolon",
172
- "Enumerationcomma"
173
- ];
174
- var PHONEME_ALPHABET_PRESETS = ["ipa", "sapi", "ups", "x-sampa"];
175
- var VISEME_TYPE_PRESETS = ["redlips_front", "FacialExpression"];
176
- var SSML_ATTRIBUTE_PRESETS = {
177
- break: {
178
- strength: BREAK_STRENGTH_PRESETS,
179
- time: BREAK_TIME_PRESETS
180
- },
181
- prosody: {
182
- rate: PROSODY_RATE_PRESETS,
183
- pitch: PROSODY_PITCH_PRESETS,
184
- volume: PROSODY_VOLUME_PRESETS
185
- },
186
- "mstts:express-as": {
187
- style: EXPRESS_AS_STYLE_PRESETS,
188
- role: EXPRESS_AS_ROLE_PRESETS
189
- },
190
- "express-as": {
191
- style: EXPRESS_AS_STYLE_PRESETS,
192
- role: EXPRESS_AS_ROLE_PRESETS
193
- },
194
- expressAs: {
195
- style: EXPRESS_AS_STYLE_PRESETS,
196
- role: EXPRESS_AS_ROLE_PRESETS
197
- },
198
- "say-as": {
199
- "interpret-as": SAY_AS_PRESETS
200
- },
201
- sayAs: {
202
- "interpret-as": SAY_AS_PRESETS
203
- },
204
- emphasis: {
205
- level: EMPHASIS_LEVEL_PRESETS
206
- },
207
- lang: {
208
- "xml:lang": LANGUAGE_PRESETS
209
- },
210
- phoneme: {
211
- alphabet: PHONEME_ALPHABET_PRESETS
212
- },
213
- "mstts:silence": {
214
- type: SILENCE_TYPE_PRESETS,
215
- value: SILENCE_VALUE_PRESETS
216
- },
217
- silence: {
218
- type: SILENCE_TYPE_PRESETS,
219
- value: SILENCE_VALUE_PRESETS
220
- },
221
- "mstts:viseme": {
222
- type: VISEME_TYPE_PRESETS
223
- },
224
- viseme: {
225
- type: VISEME_TYPE_PRESETS
226
- }
227
- };
228
- var SSML_PRESET_EXAMPLES = {
229
- breakTime: "500ms",
230
- prosodyRate: "fast",
231
- prosodyPitch: "+2st",
232
- prosodyVolume: "loud",
233
- expressAsStyle: "cheerful",
234
- phonemeAlphabet: "ipa",
235
- silenceValue: "300ms"
236
- };
237
- var BREAK_TIME_DESCRIPTIONS = {
238
- "500ms": {
239
- ja: "500\u30DF\u30EA\u79D2\u306E\u7121\u97F3",
240
- en: "Inserts 500 milliseconds of silence."
241
- },
242
- "1s": {
243
- ja: "1\u79D2\u306E\u7121\u97F3",
244
- en: "Inserts one second of silence."
245
- },
246
- "2s": {
247
- ja: "2\u79D2\u306E\u7121\u97F3",
248
- en: "Inserts two seconds of silence."
249
- },
250
- "3s": {
251
- ja: "3\u79D2\u306E\u7121\u97F3",
252
- en: "Inserts three seconds of silence."
253
- }
254
- };
255
- var EMPHASIS_LEVEL_DESCRIPTIONS = {
256
- strong: {
257
- ja: "\u5F37\u3044\u5F37\u8ABF",
258
- en: "Applies strong emphasis."
259
- },
260
- moderate: {
261
- ja: "\u4E2D\u7A0B\u5EA6\u306E\u5F37\u8ABF",
262
- en: "Applies moderate emphasis."
263
- },
264
- reduced: {
265
- ja: "\u5F31\u3081\u306E\u5F37\u8ABF",
266
- en: "Applies reduced emphasis."
267
- },
268
- none: {
269
- ja: "\u5F37\u8ABF\u306A\u3057",
270
- en: "Applies no emphasis."
271
- }
272
- };
273
- var PROSODY_RATE_DESCRIPTIONS = {
274
- "x-slow": {
275
- ja: "\u6700\u3082\u9045\u3044\u901F\u5EA6",
276
- en: "Uses the slowest speech rate."
277
- },
278
- slow: {
279
- ja: "\u9045\u3044\u901F\u5EA6",
280
- en: "Uses a slow speech rate."
281
- },
282
- medium: {
283
- ja: "\u6A19\u6E96\u7684\u306A\u901F\u5EA6",
284
- en: "Uses the standard speech rate."
285
- },
286
- fast: {
287
- ja: "\u901F\u3044\u901F\u5EA6",
288
- en: "Uses a fast speech rate."
289
- },
290
- "x-fast": {
291
- ja: "\u6700\u3082\u901F\u3044\u901F\u5EA6",
292
- en: "Uses the fastest speech rate."
293
- }
294
- };
295
- var PROSODY_PITCH_DESCRIPTIONS = {
296
- "+2st": {
297
- ja: "\u57FA\u6E96\u306E\u58F0\u306E\u9AD8\u3055\u3088\u308A2\u534A\u97F3\u4E0A",
298
- en: "Raises the pitch by two semitones."
299
- },
300
- "-2st": {
301
- ja: "\u57FA\u6E96\u306E\u58F0\u306E\u9AD8\u3055\u3088\u308A2\u534A\u97F3\u4E0B",
302
- en: "Lowers the pitch by two semitones."
303
- },
304
- "0st": {
305
- ja: "\u57FA\u6E96\u306E\u58F0\u306E\u9AD8\u3055",
306
- en: "Keeps the baseline pitch."
307
- },
308
- "+4st": {
309
- ja: "\u57FA\u6E96\u306E\u58F0\u306E\u9AD8\u3055\u3088\u308A4\u534A\u97F3\u4E0A",
310
- en: "Raises the pitch by four semitones."
311
- },
312
- "-4st": {
313
- ja: "\u57FA\u6E96\u306E\u58F0\u306E\u9AD8\u3055\u3088\u308A4\u534A\u97F3\u4E0B",
314
- en: "Lowers the pitch by four semitones."
315
- },
316
- "+8st": {
317
- ja: "\u57FA\u6E96\u306E\u58F0\u306E\u9AD8\u3055\u3088\u308A8\u534A\u97F3\u4E0A",
318
- en: "Raises the pitch by eight semitones."
319
- },
320
- "-8st": {
321
- ja: "\u57FA\u6E96\u306E\u58F0\u306E\u9AD8\u3055\u3088\u308A8\u534A\u97F3\u4E0B",
322
- en: "Lowers the pitch by eight semitones."
323
- },
324
- "+12st": {
325
- ja: "\u57FA\u6E96\u306E\u58F0\u306E\u9AD8\u3055\u3088\u308A12\u534A\u97F3\u4E0A",
326
- en: "Raises the pitch by twelve semitones."
327
- },
328
- "-12st": {
329
- ja: "\u57FA\u6E96\u306E\u58F0\u306E\u9AD8\u3055\u3088\u308A12\u534A\u97F3\u4E0B",
330
- en: "Lowers the pitch by twelve semitones."
331
- }
332
- };
333
- var PROSODY_VOLUME_DESCRIPTIONS = {
334
- silent: {
335
- ja: "\u7121\u97F3",
336
- en: "Makes the selected text silent."
337
- },
338
- "x-soft": {
339
- ja: "\u6700\u3082\u5C0F\u3055\u3044\u97F3\u91CF",
340
- en: "Uses the quietest volume."
341
- },
342
- soft: {
343
- ja: "\u5C0F\u3055\u3044\u97F3\u91CF",
344
- en: "Uses a soft volume."
345
- },
346
- medium: {
347
- ja: "\u6A19\u6E96\u7684\u306A\u97F3\u91CF",
348
- en: "Uses the standard volume."
349
- },
350
- loud: {
351
- ja: "\u5927\u304D\u3044\u97F3\u91CF",
352
- en: "Uses a loud volume."
353
- },
354
- "x-loud": {
355
- ja: "\u6700\u3082\u5927\u304D\u3044\u97F3\u91CF",
356
- en: "Uses the loudest volume."
357
- }
358
- };
359
- var EXPRESS_AS_STYLE_DESCRIPTIONS = {
360
- cheerful: {
361
- ja: "\u660E\u308B\u304F\u5143\u6C17\u306A\u30B9\u30BF\u30A4\u30EB",
362
- en: "Uses a cheerful style."
363
- },
364
- friendly: {
365
- ja: "\u89AA\u3057\u307F\u3084\u3059\u3044\u30B9\u30BF\u30A4\u30EB",
366
- en: "Uses a friendly style."
367
- },
368
- calm: {
369
- ja: "\u7A4F\u3084\u304B\u306A\u30B9\u30BF\u30A4\u30EB",
370
- en: "Uses a calm style."
371
- },
372
- sad: {
373
- ja: "\u60B2\u3057\u3052\u306A\u30B9\u30BF\u30A4\u30EB",
374
- en: "Uses a sad style."
375
- },
376
- angry: {
377
- ja: "\u6012\u3063\u305F\u3088\u3046\u306A\u30B9\u30BF\u30A4\u30EB",
378
- en: "Uses an angry style."
379
- },
380
- excited: {
381
- ja: "\u8208\u596E\u3057\u305F\u30B9\u30BF\u30A4\u30EB",
382
- en: "Uses an excited style."
383
- },
384
- serious: {
385
- ja: "\u771F\u5263\u306A\u30B9\u30BF\u30A4\u30EB",
386
- en: "Uses a serious style."
387
- },
388
- assistant: {
389
- ja: "\u30C7\u30B8\u30BF\u30EB\u30A2\u30B7\u30B9\u30BF\u30F3\u30C8\u5411\u3051\u306E\u30B9\u30BF\u30A4\u30EB",
390
- en: "Uses a digital assistant style."
391
- },
392
- chat: {
393
- ja: "\u4F1A\u8A71\u5411\u3051\u306E\u30B9\u30BF\u30A4\u30EB",
394
- en: "Uses a conversational style."
395
- },
396
- customerservice: {
397
- ja: "\u30AB\u30B9\u30BF\u30DE\u30FC\u30B5\u30FC\u30D3\u30B9\u5411\u3051\u306E\u30B9\u30BF\u30A4\u30EB",
398
- en: "Uses a customer service style."
399
- },
400
- hopeful: {
401
- ja: "\u5E0C\u671B\u306B\u6E80\u3061\u305F\u30B9\u30BF\u30A4\u30EB",
402
- en: "Uses a hopeful style."
403
- },
404
- newscast: {
405
- ja: "\u30CB\u30E5\u30FC\u30B9\u8AAD\u307F\u4E0A\u3052\u5411\u3051\u306E\u30B9\u30BF\u30A4\u30EB",
406
- en: "Uses a newscast style."
407
- },
408
- shouting: {
409
- ja: "\u53EB\u3076\u3088\u3046\u306A\u30B9\u30BF\u30A4\u30EB",
410
- en: "Uses a shouting style."
411
- },
412
- terrified: {
413
- ja: "\u6050\u6016\u306B\u6E80\u3061\u305F\u30B9\u30BF\u30A4\u30EB",
414
- en: "Uses a terrified style."
415
- },
416
- unfriendly: {
417
- ja: "\u7121\u611B\u60F3\u306A\u30B9\u30BF\u30A4\u30EB",
418
- en: "Uses an unfriendly style."
419
- },
420
- whispering: {
421
- ja: "\u3055\u3055\u3084\u304F\u3088\u3046\u306A\u30B9\u30BF\u30A4\u30EB",
422
- en: "Uses a whispering style."
423
- }
424
- };
425
- var SAY_AS_DESCRIPTIONS = {
426
- characters: {
427
- ja: "1\u6587\u5B57\u305A\u3064\u306E\u8AAD\u307F\u4E0A\u3052",
428
- en: "Speaks the characters one by one."
429
- },
430
- "spell-out": {
431
- ja: "\u7DB4\u308A\u306E\u8AAD\u307F\u4E0A\u3052\uFF081\u6587\u5B57\u305A\u3064\uFF09",
432
- en: "Spells out the text character by character."
433
- },
434
- cardinal: {
435
- ja: "\u57FA\u6570\u3068\u3057\u3066\u306E\u8AAD\u307F\u4E0A\u3052",
436
- en: "Speaks the value as a cardinal number."
437
- },
438
- ordinal: {
439
- ja: "\u5E8F\u6570\u3068\u3057\u3066\u306E\u8AAD\u307F\u4E0A\u3052",
440
- en: "Speaks the value as an ordinal number."
441
- },
442
- number: {
443
- ja: "\u6570\u5024\u3068\u3057\u3066\u306E\u8AAD\u307F\u4E0A\u3052",
444
- en: "Speaks the value as a number."
445
- },
446
- date: {
447
- ja: "\u65E5\u4ED8\u3068\u3057\u3066\u306E\u8AAD\u307F\u4E0A\u3052",
448
- en: "Speaks the value as a date."
449
- },
450
- time: {
451
- ja: "\u6642\u523B\u3068\u3057\u3066\u306E\u8AAD\u307F\u4E0A\u3052",
452
- en: "Speaks the value as a time."
453
- },
454
- telephone: {
455
- ja: "\u96FB\u8A71\u756A\u53F7\u3068\u3057\u3066\u306E\u8AAD\u307F\u4E0A\u3052",
456
- en: "Speaks the value as a telephone number."
457
- },
458
- fraction: {
459
- ja: "\u5206\u6570\u3068\u3057\u3066\u306E\u8AAD\u307F\u4E0A\u3052",
460
- en: "Speaks the value as a fraction."
461
- },
462
- address: {
463
- ja: "\u4F4F\u6240\u3068\u3057\u3066\u306E\u8AAD\u307F\u4E0A\u3052",
464
- en: "Speaks the value as an address."
465
- },
466
- name: {
467
- ja: "\u540D\u524D\u3068\u3057\u3066\u306E\u8AAD\u307F\u4E0A\u3052",
468
- en: "Speaks the value as a name."
469
- },
470
- currency: {
471
- ja: "\u901A\u8CA8\u3068\u3057\u3066\u306E\u8AAD\u307F\u4E0A\u3052",
472
- en: "Speaks the value as currency."
473
- }
474
- };
475
- var LANGUAGE_DESCRIPTIONS = {
476
- "ja-JP": {
477
- ja: "\u65E5\u672C\u8A9E\uFF08\u65E5\u672C\uFF09\u3067\u8AAD\u307F\u4E0A\u3052\u307E\u3059\u3002",
478
- en: "Speaks the text in Japanese (Japan)."
479
- },
480
- "en-US": {
481
- ja: "\u82F1\u8A9E\uFF08\u7C73\u56FD\uFF09\u3067\u8AAD\u307F\u4E0A\u3052\u307E\u3059\u3002",
482
- en: "Speaks the text in English (United States)."
483
- },
484
- "de-DE": {
485
- ja: "\u30C9\u30A4\u30C4\u8A9E\uFF08\u30C9\u30A4\u30C4\uFF09\u3067\u8AAD\u307F\u4E0A\u3052\u307E\u3059\u3002",
486
- en: "Speaks the text in German (Germany)."
487
- },
488
- "fr-FR": {
489
- ja: "\u30D5\u30E9\u30F3\u30B9\u8A9E\uFF08\u30D5\u30E9\u30F3\u30B9\uFF09\u3067\u8AAD\u307F\u4E0A\u3052\u307E\u3059\u3002",
490
- en: "Speaks the text in French (France)."
491
- }
492
- };
493
- var SILENCE_VALUE_DESCRIPTIONS = {
494
- "300ms": {
495
- ja: "\u5148\u982D\u306B300\u30DF\u30EA\u79D2\u306E\u7121\u97F3\u3092\u633F\u5165\u3057\u307E\u3059\u3002",
496
- en: "Inserts 300 milliseconds of leading silence."
497
- },
498
- "500ms": {
499
- ja: "\u5148\u982D\u306B500\u30DF\u30EA\u79D2\u306E\u7121\u97F3\u3092\u633F\u5165\u3057\u307E\u3059\u3002",
500
- en: "Inserts 500 milliseconds of leading silence."
501
- },
502
- "1s": {
503
- ja: "\u5148\u982D\u306B1\u79D2\u306E\u7121\u97F3\u3092\u633F\u5165\u3057\u307E\u3059\u3002",
504
- en: "Inserts one second of leading silence."
505
- }
506
- };
507
-
508
- // packages/ssml-editor-react/src/locales.ts
509
- var EDITOR_COPY = {
510
- ja: {
511
- editorAriaLabel: "SSML\u30A8\u30C7\u30A3\u30BF\u30FC",
512
- toolbarAriaLabel: "SSML\u30C4\u30FC\u30EB\u30D0\u30FC",
513
- clearAll: "\u5168\u3066\u30AF\u30EA\u30A2",
514
- clearAllTitle: "\u97F3\u58F0\u8A2D\u5B9A\u3092\u4FDD\u6301\u3057\u3066XML\u8981\u7D20\u3092\u524A\u9664\u3057\u672C\u6587\u3092\u6B8B\u3059",
515
- undo: "\u5143\u306B\u623B\u3059",
516
- undoTitle: "\u76F4\u524D\u306E\u5909\u66F4\u3092\u5143\u306B\u623B\u3059",
517
- redo: "\u3084\u308A\u76F4\u3059",
518
- redoTitle: "\u5143\u306B\u623B\u3057\u305F\u5909\u66F4\u3092\u3084\u308A\u76F4\u3059",
519
- help: "\u8AAC\u660E",
520
- helpTitle: "\u30DC\u30BF\u30F3\u3068\u30D1\u30E9\u30E1\u30FC\u30BF\u306E\u8AAC\u660E\u3092\u8868\u793A",
521
- helpHeading: "\u30DC\u30BF\u30F3\u3068\u30D1\u30E9\u30E1\u30FC\u30BF\u306E\u8AAC\u660E",
522
- helpDescription: "\u5404\u30B3\u30F3\u30C8\u30ED\u30FC\u30EB\u306E\u6A5F\u80FD\u3068\u30D1\u30E9\u30E1\u30FC\u30BF\u3092\u78BA\u8A8D\u3067\u304D\u307E\u3059\u3002",
523
- parameters: "\u30D1\u30E9\u30E1\u30FC\u30BF",
524
- format: "\u30D5\u30A9\u30FC\u30DE\u30C3\u30C8",
525
- formatTitle: "\u672C\u6587\u306EXML\u3092\u6539\u884C\u3057\u3066\u898B\u3084\u3059\u304F\u8868\u793A",
526
- decorations: "\u88C5\u98FE",
527
- decorationsShowTitle: "\u30A4\u30F3\u30E9\u30A4\u30F3\u88C5\u98FE\u3092\u8868\u793A",
528
- decorationsHideTitle: "\u30A4\u30F3\u30E9\u30A4\u30F3\u88C5\u98FE\u3092\u975E\u8868\u793A",
529
- syntaxError: "\u69CB\u6587\u30A8\u30E9\u30FC",
530
- selectionActions: "\u9078\u629E\u7BC4\u56F2\u306E\u64CD\u4F5C",
531
- selectionCountSuffix: "\u6587\u5B57",
532
- previewSelection: "\u9078\u629E\u90E8\u5206\u3092\u8A66\u8074",
533
- previewSelectionTitle: "\u9078\u629E\u90E8\u5206\u306ESSML\u3092\u8A66\u8074",
534
- noAvailableOptions: "\u5229\u7528\u53EF\u80FD\u306A\u9078\u629E\u80A2\u304C\u3042\u308A\u307E\u305B\u3093\u3002"
535
- },
536
- en: {
537
- editorAriaLabel: "SSML editor",
538
- toolbarAriaLabel: "SSML toolbar",
539
- clearAll: "Clear all",
540
- clearAllTitle: "Remove non-voice XML elements and keep the text and voice settings",
541
- undo: "Undo",
542
- undoTitle: "Undo the last change",
543
- redo: "Redo",
544
- redoTitle: "Redo the last undone change",
545
- help: "Help",
546
- helpTitle: "Show button and parameter descriptions",
547
- helpHeading: "Button and parameter descriptions",
548
- helpDescription: "Review what each control does and its parameters.",
549
- parameters: "Parameters",
550
- format: "Format",
551
- formatTitle: "Format the XML in the editor",
552
- decorations: "Decorations",
553
- decorationsShowTitle: "Show inline decorations",
554
- decorationsHideTitle: "Hide inline decorations",
555
- syntaxError: "Syntax error",
556
- selectionActions: "Selection actions",
557
- selectionCountSuffix: " characters",
558
- previewSelection: "Preview selection",
559
- previewSelectionTitle: "Preview the selected SSML",
560
- noAvailableOptions: "No options are available."
561
- }
562
- };
563
- var INLINE_BADGE_COPY = {
564
- ja: {
565
- pause: "\u9593",
566
- pitch: "\u30D4\u30C3\u30C1\u5909\u5316",
567
- prosody: "\u58F0\u306E\u8ABF\u6574"
568
- },
569
- en: {
570
- pause: "Pause",
571
- pitch: "Pitch change",
572
- prosody: "Prosody"
573
- }
574
- };
575
- var SSML_HOVER_COPY = {
576
- ja: {
577
- parameterHeading: "\u30D1\u30E9\u30E1\u30FC\u30BF",
578
- parametersHeading: "\u30D1\u30E9\u30E1\u30FC\u30BF",
579
- allowedValues: "\u4F7F\u7528\u3067\u304D\u308B\u5024",
580
- example: "\u4F8B",
581
- noParameters: "\u3053\u306E\u8981\u7D20\u306B\u30D1\u30E9\u30E1\u30FC\u30BF\u306F\u3042\u308A\u307E\u305B\u3093\u3002",
582
- tags: {
583
- voice: {
584
- title: "\u97F3\u58F0",
585
- description: "\u56F2\u307E\u308C\u305F\u30C6\u30AD\u30B9\u30C8\u306E\u5408\u6210\u306B\u4F7F\u7528\u3059\u308B\u97F3\u58F0\u3068\u3001\u4EFB\u610F\u306E\u97F3\u58F0\u52B9\u679C\u3092\u9078\u629E\u3057\u307E\u3059\u3002",
586
- parameters: {
587
- name: { title: "name", description: "\u97F3\u58F0\u540D\u3002\u4F8B: `en-US-JennyNeural`\u3002" },
588
- effect: { title: "effect", description: "\u4EFB\u610F\u306E\u97F3\u58F0\u52B9\u679C\u3002\u4F8B: `eq_car`\u3002" }
589
- }
590
- },
591
- prosody: {
592
- title: "\u97FB\u5F8B",
593
- description: "\u56F2\u307E\u308C\u305F\u30C6\u30AD\u30B9\u30C8\u306E\u901F\u5EA6\u3001\u30D4\u30C3\u30C1\u3001\u97F3\u91CF\u3001\u307E\u305F\u306F\u30D4\u30C3\u30C1\u66F2\u7DDA\u3092\u5909\u66F4\u3057\u307E\u3059\u3002",
594
- parameters: {
595
- rate: { title: "rate", description: "\u767A\u8A71\u901F\u5EA6\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002" },
596
- pitch: { title: "pitch", description: "\u540D\u524D\u4ED8\u304D\u306E\u5024\u3001\u5272\u5408\u3001\u5468\u6CE2\u6570\u3001\u307E\u305F\u306F\u534A\u97F3\u3067\u30D4\u30C3\u30C1\u3092\u8ABF\u6574\u3057\u307E\u3059\u3002" },
597
- volume: { title: "volume", description: "\u540D\u524D\u4ED8\u304D\u306E\u5024\u3001\u5272\u5408\u3001\u307E\u305F\u306F\u30C7\u30B7\u30D9\u30EB\u5024\u3067\u97F3\u91CF\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002" },
598
- contour: { title: "contour", description: "\u30C6\u30AD\u30B9\u30C8\u5185\u306E\u4F4D\u7F6E\u3054\u3068\u306E\u76F8\u5BFE\u7684\u306A\u30D4\u30C3\u30C1\u5909\u5316\u3092\u5B9A\u7FA9\u3057\u307E\u3059\u3002" },
599
- range: { title: "range", description: "\u97F3\u58F0\u306E\u30D4\u30C3\u30C1\u7BC4\u56F2\u3092\u8ABF\u6574\u3057\u307E\u3059\u3002" }
600
- }
601
- },
602
- break: {
603
- title: "\u9593",
604
- description: "\u5358\u8A9E\u3084\u305D\u306E\u4ED6\u306E\u97F3\u58F0\u30B3\u30F3\u30C6\u30F3\u30C4\u306E\u9593\u306B\u30DD\u30FC\u30BA\u3092\u633F\u5165\u3057\u307E\u3059\u3002",
605
- parameters: {
606
- time: { title: "time", description: "\u30DD\u30FC\u30BA\u306E\u9577\u3055\u3002\u4F8B: `500ms` \u307E\u305F\u306F `1s`\u3002" },
607
- strength: { title: "strength", description: "\u76F8\u5BFE\u7684\u306A\u30DD\u30FC\u30BA\u306E\u5F37\u3055\u3002" }
608
- }
609
- },
610
- "mstts:express-as": {
611
- title: "\u8868\u73FE",
612
- description: "\u56F2\u307E\u308C\u305F\u30C6\u30AD\u30B9\u30C8\u306B\u97F3\u58F0\u30B9\u30BF\u30A4\u30EB\u3001\u30B9\u30BF\u30A4\u30EB\u306E\u5F37\u3055\u3001\u307E\u305F\u306F\u5F79\u5272\u3092\u9069\u7528\u3057\u307E\u3059\u3002",
613
- parameters: {
614
- style: { title: "style", description: "\u9078\u629E\u3057\u305F\u97F3\u58F0\u304C\u5BFE\u5FDC\u3059\u308B\u97F3\u58F0\u30B9\u30BF\u30A4\u30EB\u3002\u4F8B: `cheerful`\u3002" },
615
- styledegree: { title: "styledegree", description: "\u9078\u629E\u3057\u305F\u97F3\u58F0\u30B9\u30BF\u30A4\u30EB\u306E\u5F37\u3055\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002" },
616
- role: { title: "role", description: "\u5BFE\u5FDC\u3057\u3066\u3044\u308B\u5834\u5408\u306B\u97F3\u58F0\u306E\u5F79\u5272\u3092\u5909\u66F4\u3057\u307E\u3059\u3002" }
617
- }
618
- },
619
- "say-as": {
620
- title: "\u8AAD\u307F\u4E0A\u3052\u65B9",
621
- description: "\u56F2\u307E\u308C\u305F\u30C6\u30AD\u30B9\u30C8\u306E\u89E3\u91C8\u65B9\u6CD5\u3068\u8AAD\u307F\u4E0A\u3052\u65B9\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
622
- parameters: {
623
- "interpret-as": { title: "interpret-as", description: "\u6587\u5B57\u3001\u6570\u5B57\u3001\u65E5\u4ED8\u3001\u6642\u523B\u306A\u3069\u306E\u89E3\u91C8\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002" },
624
- format: { title: "format", description: "\u9078\u629E\u3057\u305F\u89E3\u91C8\u306E\u5F62\u5F0F\u3092\u88DC\u8DB3\u3057\u307E\u3059\u3002" },
625
- detail: { title: "detail", description: "\u9078\u629E\u3057\u305F\u89E3\u91C8\u306E\u8A73\u7D30\u3092\u88DC\u8DB3\u3057\u307E\u3059\u3002" }
626
- }
627
- },
628
- phoneme: {
629
- title: "\u97F3\u7D20",
630
- description: "\u6307\u5B9A\u3057\u305F\u97F3\u7D20\u8868\u8A18\u3067\u901A\u5E38\u306E\u767A\u97F3\u3092\u7F6E\u304D\u63DB\u3048\u307E\u3059\u3002",
631
- parameters: {
632
- alphabet: { title: "alphabet", description: "`ph` \u5024\u306B\u4F7F\u7528\u3059\u308B\u97F3\u7D20\u30A2\u30EB\u30D5\u30A1\u30D9\u30C3\u30C8\u3002" },
633
- ph: { title: "ph", description: "\u56F2\u307E\u308C\u305F\u30C6\u30AD\u30B9\u30C8\u306E\u97F3\u7D20\u8868\u8A18\u3002" }
634
- }
635
- },
636
- emphasis: {
637
- title: "\u5F37\u8ABF",
638
- description: "\u56F2\u307E\u308C\u305F\u30C6\u30AD\u30B9\u30C8\u3092\u5F37\u8ABF\u3057\u307E\u3059\u3002",
639
- parameters: {
640
- level: { title: "level", description: "\u5F37\u8ABF\u306E\u5EA6\u5408\u3044\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002" }
641
- }
642
- },
643
- audio: {
644
- title: "\u97F3\u58F0\u30D5\u30A1\u30A4\u30EB",
645
- description: "\u5408\u6210\u7D50\u679C\u306E\u4E00\u90E8\u3068\u3057\u3066\u97F3\u58F0\u30D5\u30A1\u30A4\u30EB\u3092\u518D\u751F\u3057\u307E\u3059\u3002",
646
- parameters: {
647
- src: { title: "src", description: "\u97F3\u58F0\u30D5\u30A1\u30A4\u30EB\u306E URI\u3002" },
648
- desc: { title: "desc", description: "\u97F3\u58F0\u3092\u518D\u751F\u3067\u304D\u306A\u3044\u5834\u5408\u306B\u4F7F\u7528\u3059\u308B\u4EE3\u66FF\u30C6\u30AD\u30B9\u30C8\u3002" },
649
- clipBegin: { title: "clipBegin", description: "\u97F3\u58F0\u30D5\u30A1\u30A4\u30EB\u5185\u306E\u958B\u59CB\u4F4D\u7F6E\u3002\u4F8B: `0s`\u3002" },
650
- clipEnd: { title: "clipEnd", description: "\u97F3\u58F0\u30D5\u30A1\u30A4\u30EB\u5185\u306E\u7D42\u4E86\u4F4D\u7F6E\u3002\u4F8B: `5s`\u3002" },
651
- speed: { title: "speed", description: "\u97F3\u58F0\u30D5\u30A1\u30A4\u30EB\u306E\u518D\u751F\u901F\u5EA6\u3002\u4F8B: `1.0`\u3002" },
652
- repeatCount: { title: "repeatCount", description: "\u97F3\u58F0\u3092\u7E70\u308A\u8FD4\u3059\u56DE\u6570\u3002\u4F8B: `2`\u3002" },
653
- repeatDuration: { title: "repeatDuration", description: "\u97F3\u58F0\u3092\u7E70\u308A\u8FD4\u3059\u5408\u8A08\u6642\u9593\u3002\u4F8B: `10s`\u3002" },
654
- soundLevel: { title: "soundLevel", description: "\u30C7\u30B7\u30D9\u30EB\u5358\u4F4D\u306E\u97F3\u91CF\u8ABF\u6574\u3002\u4F8B: `-3dB`\u3002" }
655
- }
656
- },
657
- sub: {
658
- title: "\u7F6E\u63DB",
659
- description: "\u56F2\u307E\u308C\u305F\u30C6\u30AD\u30B9\u30C8\u306E\u4EE3\u308F\u308A\u306B\u5225\u540D\u30C6\u30AD\u30B9\u30C8\u3092\u8AAD\u307F\u4E0A\u3052\u307E\u3059\u3002",
660
- parameters: {
661
- alias: { title: "alias", description: "\u5143\u306E\u30C6\u30AD\u30B9\u30C8\u306E\u4EE3\u308F\u308A\u306B\u8AAD\u307F\u4E0A\u3052\u308B\u30C6\u30AD\u30B9\u30C8\u3002\u4F8B: `World Wide Web`\u3002" }
662
- }
663
- },
664
- lang: {
665
- title: "\u8A00\u8A9E",
666
- description: "\u56F2\u307E\u308C\u305F\u30C6\u30AD\u30B9\u30C8\u306E\u8AAD\u307F\u4E0A\u3052\u8A00\u8A9E\u3092\u5909\u66F4\u3057\u307E\u3059\u3002",
667
- parameters: {
668
- "xml:lang": { title: "xml:lang", description: "BCP-47 \u8A00\u8A9E\u30BF\u30B0\u3002\u4F8B: `ja-JP`\u3002" }
669
- }
670
- },
671
- mark: {
672
- title: "\u30DE\u30FC\u30AB\u30FC",
673
- description: "\u5408\u6210\u97F3\u58F0\u30B9\u30C8\u30EA\u30FC\u30E0\u306B\u30AB\u30B9\u30BF\u30E0\u30DE\u30FC\u30AB\u30FC\u3092\u633F\u5165\u3057\u307E\u3059\u3002",
674
- parameters: {
675
- name: { title: "name", description: "\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u3067\u5B9A\u7FA9\u3057\u305F\u30DE\u30FC\u30AB\u30FC\u540D\u3002\u4F8B: `chapter-1`\u3002" }
676
- }
677
- },
678
- bookmark: {
679
- title: "\u30D6\u30C3\u30AF\u30DE\u30FC\u30AF",
680
- description: "\u5408\u6210\u97F3\u58F0\u30B9\u30C8\u30EA\u30FC\u30E0\u306B\u30D6\u30C3\u30AF\u30DE\u30FC\u30AF\u30DE\u30FC\u30AB\u30FC\u3092\u633F\u5165\u3057\u307E\u3059\u3002",
681
- parameters: {
682
- mark: { title: "mark", description: "\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u3067\u5B9A\u7FA9\u3057\u305F\u30D6\u30C3\u30AF\u30DE\u30FC\u30AF\u540D\u3002\u4F8B: `chapter-1`\u3002" }
683
- }
684
- },
685
- lexicon: {
686
- title: "\u767A\u97F3\u8F9E\u66F8",
687
- description: "\u5408\u6210\u6587\u66F8\u306B\u767A\u97F3\u8F9E\u66F8\u3092\u95A2\u9023\u4ED8\u3051\u307E\u3059\u3002",
688
- parameters: {
689
- uri: { title: "uri", description: "\u767A\u97F3\u8F9E\u66F8\u306E URI\u3002" }
690
- }
691
- },
692
- p: { title: "\u6BB5\u843D", description: "\u30C6\u30AD\u30B9\u30C8\u3092\u6BB5\u843D\u3068\u3057\u3066\u307E\u3068\u3081\u307E\u3059\u3002", parameters: {} },
693
- s: { title: "\u6587", description: "\u30C6\u30AD\u30B9\u30C8\u3092\u6587\u3068\u3057\u3066\u307E\u3068\u3081\u307E\u3059\u3002", parameters: {} },
694
- w: { title: "\u5358\u8A9E", description: "\u30C6\u30AD\u30B9\u30C8\u3092\u5358\u8A9E\u3068\u3057\u3066\u307E\u3068\u3081\u307E\u3059\u3002", parameters: {} },
695
- "mstts:silence": {
696
- title: "\u7121\u97F3",
697
- description: "\u30C6\u30AD\u30B9\u30C8\u306E\u524D\u5F8C\u3001\u307E\u305F\u306F\u53E5\u8AAD\u70B9\u306E\u5883\u754C\u306B\u6307\u5B9A\u3057\u305F\u7121\u97F3\u3092\u8FFD\u52A0\u3057\u307E\u3059\u3002",
698
- parameters: {
699
- type: { title: "type", description: "\u7121\u97F3\u306E\u4F4D\u7F6E\u307E\u305F\u306F\u53E5\u8AAD\u70B9\u306E\u5883\u754C\u3002" },
700
- value: { title: "value", description: "\u7121\u97F3\u306E\u9577\u3055\u3002\u4F8B: `300ms`\u3002" }
701
- }
702
- },
703
- "mstts:viseme": {
704
- title: "\u30D3\u30BC\u30FC\u30E0",
705
- description: "\u5408\u6210\u97F3\u58F0\u306E\u30D3\u30BC\u30FC\u30E0\u30A4\u30D9\u30F3\u30C8\u3092\u8981\u6C42\u3057\u307E\u3059\u3002",
706
- parameters: {
707
- type: { title: "type", description: "\u30D3\u30BC\u30FC\u30E0\u30A4\u30D9\u30F3\u30C8\u306E\u5F62\u5F0F\u3002" }
708
- }
709
- }
710
- }
711
- },
712
- en: {
713
- parameterHeading: "Parameter",
714
- parametersHeading: "Parameters",
715
- allowedValues: "Allowed values",
716
- example: "Example",
717
- noParameters: "This element has no parameters.",
718
- tags: {
719
- voice: {
720
- title: "Voice",
721
- description: "Selects the voice and optional voice effect used to synthesize the enclosed text.",
722
- parameters: {
723
- name: { title: "name", description: "The voice name, such as `en-US-JennyNeural`." },
724
- effect: { title: "effect", description: "An optional voice effect, such as `eq_car`." }
725
- }
726
- },
727
- prosody: {
728
- title: "Prosody",
729
- description: "Changes the speaking rate, pitch, volume, or pitch contour of the enclosed text.",
730
- parameters: {
731
- rate: { title: "rate", description: "Controls speaking speed." },
732
- pitch: {
733
- title: "pitch",
734
- description: "Adjusts pitch using a named value, percentage, frequency, or semitone value."
735
- },
736
- volume: {
737
- title: "volume",
738
- description: "Controls loudness using a named value, percentage, or decibel value."
739
- },
740
- contour: {
741
- title: "contour",
742
- description: "Defines a sequence of relative pitch changes at positions in the text."
743
- },
744
- range: { title: "range", description: "Adjusts the pitch range of the voice." }
745
- }
746
- },
747
- break: {
748
- title: "Break",
749
- description: "Inserts a pause between words or other spoken content.",
750
- parameters: {
751
- time: { title: "time", description: "The pause duration, for example `500ms` or `1s`." },
752
- strength: { title: "strength", description: "The relative pause strength." }
753
- }
754
- },
755
- "mstts:express-as": {
756
- title: "Express-as",
757
- description: "Applies a speaking style, style degree, or role to the enclosed text.",
758
- parameters: {
759
- style: {
760
- title: "style",
761
- description: "The speaking style supported by the selected voice, such as `cheerful`."
762
- },
763
- styledegree: { title: "styledegree", description: "Controls the intensity of the selected speaking style." },
764
- role: { title: "role", description: "Changes the speaking role when supported by the selected voice." }
765
- }
766
- },
767
- "say-as": {
768
- title: "Say-as",
769
- description: "Controls how the enclosed text is interpreted and spoken.",
770
- parameters: {
771
- "interpret-as": {
772
- title: "interpret-as",
773
- description: "Specifies the interpretation, such as characters, digits, date, or time."
774
- },
775
- format: { title: "format", description: "Provides a format hint for the selected interpretation." },
776
- detail: {
777
- title: "detail",
778
- description: "Provides an additional detail hint for the selected interpretation."
779
- }
780
- }
781
- },
782
- phoneme: {
783
- title: "Phoneme",
784
- description: "Replaces normal pronunciation with the supplied phonetic pronunciation.",
785
- parameters: {
786
- alphabet: { title: "alphabet", description: "The phonetic alphabet used by the `ph` value." },
787
- ph: { title: "ph", description: "The phonetic pronunciation for the enclosed text." }
788
- }
789
- },
790
- emphasis: {
791
- title: "Emphasis",
792
- description: "Adds emphasis to the enclosed text.",
793
- parameters: {
794
- level: { title: "level", description: "Controls the amount of emphasis." }
795
- }
796
- },
797
- audio: {
798
- title: "Audio",
799
- description: "Plays an audio file as part of the synthesized output.",
800
- parameters: {
801
- src: { title: "src", description: "The URI of the audio file." },
802
- desc: { title: "desc", description: "Alternative text to use if the audio cannot be played." },
803
- clipBegin: { title: "clipBegin", description: "The starting offset within the audio file." },
804
- clipEnd: { title: "clipEnd", description: "The ending offset within the audio file." },
805
- speed: { title: "speed", description: "The playback speed of the audio file." },
806
- repeatCount: { title: "repeatCount", description: "The number of times to repeat the audio." },
807
- repeatDuration: {
808
- title: "repeatDuration",
809
- description: "The total duration for which the audio may repeat."
810
- },
811
- soundLevel: { title: "soundLevel", description: "The audio volume adjustment in decibels." }
812
- }
813
- },
814
- sub: {
815
- title: "Substitution",
816
- description: "Substitutes the alias text when speaking the enclosed text.",
817
- parameters: {
818
- alias: { title: "alias", description: "The text to speak instead of the enclosed text." }
819
- }
820
- },
821
- lang: {
822
- title: "Language",
823
- description: "Changes the language used for the enclosed text.",
824
- parameters: {
825
- "xml:lang": { title: "xml:lang", description: "The BCP-47 language tag." }
826
- }
827
- },
828
- mark: {
829
- title: "Mark",
830
- description: "Inserts a custom marker into the synthesized audio stream.",
831
- parameters: {
832
- name: { title: "name", description: "The application-defined marker name." }
833
- }
834
- },
835
- bookmark: {
836
- title: "Bookmark",
837
- description: "Inserts a bookmark marker into the synthesized audio stream.",
838
- parameters: {
839
- mark: { title: "mark", description: "The application-defined bookmark name." }
840
- }
841
- },
842
- lexicon: {
843
- title: "Lexicon",
844
- description: "Associates a pronunciation lexicon with the synthesized document.",
845
- parameters: {
846
- uri: { title: "uri", description: "The URI of the pronunciation lexicon." }
847
- }
848
- },
849
- p: { title: "Paragraph", description: "Groups text into a paragraph.", parameters: {} },
850
- s: { title: "Sentence", description: "Groups text into a sentence.", parameters: {} },
851
- w: { title: "Word", description: "Groups text into a word.", parameters: {} },
852
- "mstts:silence": {
853
- title: "Silence",
854
- description: "Adds a specified silence before or after text or at a punctuation boundary.",
855
- parameters: {
856
- type: { title: "type", description: "The silence position or punctuation boundary." },
857
- value: { title: "value", description: "The silence duration, for example `300ms`." }
858
- }
859
- },
860
- "mstts:viseme": {
861
- title: "Viseme",
862
- description: "Requests viseme events for the synthesized audio.",
863
- parameters: {
864
- type: { title: "type", description: "The viseme event format." }
865
- }
866
- }
867
- }
868
- }
869
- };
870
-
871
- // packages/ssml-editor-react/src/constants/ui.ts
872
- var DEFAULT_LOCALE = "ja";
873
- var OVERLAY_Z_INDEX = 9999;
874
- var SELECTION_OVERLAY_ABOVE_THRESHOLD_LINES = 4;
875
- var SSML_INSERTION_MODES = {
876
- insert: "insert",
877
- wrap: "wrap"
878
- };
879
-
880
56
  // packages/ssml-editor-react/src/components/popovers/InsertionPopover.tsx
881
57
  import { createPortal } from "react-dom";
882
58
 
@@ -954,6 +130,10 @@ var editorStyles = {
954
130
  font: "inherit",
955
131
  cursor: "pointer"
956
132
  },
133
+ toolbarButtonActive: {
134
+ border: "1px solid var(--ssml-editor-active-border)",
135
+ backgroundColor: "var(--ssml-editor-active-bg)"
136
+ },
957
137
  display: {
958
138
  display: "grid",
959
139
  gap: "0.5rem"
@@ -999,12 +179,35 @@ var editorStyles = {
999
179
  whiteSpace: "nowrap",
1000
180
  cursor: "pointer"
1001
181
  },
182
+ toolbarOptionGroup: {
183
+ display: "grid",
184
+ gap: "0.125rem",
185
+ margin: 0,
186
+ padding: 0,
187
+ border: 0
188
+ },
189
+ toolbarOptionGroupLabel: {
190
+ padding: "0.375rem 0.5rem 0.125rem",
191
+ color: "var(--ssml-editor-color)",
192
+ fontSize: "0.875rem",
193
+ fontWeight: 600,
194
+ whiteSpace: "nowrap"
195
+ },
1002
196
  toolbarEmpty: {
1003
197
  margin: 0,
1004
198
  padding: "0.375rem 0.5rem",
1005
199
  color: "var(--ssml-editor-color)",
1006
200
  whiteSpace: "nowrap"
1007
201
  },
202
+ toolbarEmptySelect: {
203
+ padding: "0.375rem 0.5rem",
204
+ border: "1px solid var(--ssml-editor-control-border)",
205
+ borderRadius: "0.125rem",
206
+ color: "var(--ssml-editor-color)",
207
+ backgroundColor: "var(--ssml-editor-control-bg)",
208
+ font: "inherit",
209
+ textAlign: "left"
210
+ },
1008
211
  helpPanel: {
1009
212
  display: "grid",
1010
213
  gap: "0.5rem",
@@ -1156,8 +359,29 @@ function InsertionPopover({
1156
359
  menuRef,
1157
360
  onToggle,
1158
361
  onClose,
1159
- onApply
362
+ onApply,
363
+ optionGroups
1160
364
  }) {
365
+ const hasOptions = optionGroups ? optionGroups.some((group) => group.options.length > 0) : insertion.options.length > 0;
366
+ const renderOption = (option) => /* @__PURE__ */ React.createElement(
367
+ "button",
368
+ {
369
+ key: option.value,
370
+ type: "button",
371
+ role: "menuitem",
372
+ style: editorStyles.toolbarOption,
373
+ title: option.descriptions?.[language] ?? insertion.descriptions[language],
374
+ disabled: isReadOnly,
375
+ onMouseDown: (event) => event.preventDefault(),
376
+ onClick: () => {
377
+ if (!isReadOnly) {
378
+ onApply(insertion, option);
379
+ }
380
+ onClose();
381
+ }
382
+ },
383
+ option.labels[language]
384
+ );
1161
385
  const menu = isOpen && menuPosition && typeof document !== "undefined" ? /* @__PURE__ */ React.createElement(
1162
386
  "div",
1163
387
  {
@@ -1169,25 +393,7 @@ function InsertionPopover({
1169
393
  role: "menu",
1170
394
  "aria-label": insertion.labels[language]
1171
395
  },
1172
- insertion.options.length === 0 ? /* @__PURE__ */ React.createElement("p", { role: "status", style: editorStyles.toolbarEmpty }, emptyOptionsMessage) : insertion.options.map((option) => /* @__PURE__ */ React.createElement(
1173
- "button",
1174
- {
1175
- key: option.value,
1176
- type: "button",
1177
- role: "menuitem",
1178
- style: editorStyles.toolbarOption,
1179
- title: option.descriptions?.[language] ?? insertion.descriptions[language],
1180
- disabled: isReadOnly,
1181
- onMouseDown: (event) => event.preventDefault(),
1182
- onClick: () => {
1183
- if (!isReadOnly) {
1184
- onApply(insertion, option);
1185
- }
1186
- onClose();
1187
- }
1188
- },
1189
- option.labels[language]
1190
- ))
396
+ !hasOptions ? /* @__PURE__ */ React.createElement("div", { role: "presentation" }, /* @__PURE__ */ React.createElement("select", { disabled: true, value: "", style: editorStyles.toolbarEmptySelect }, /* @__PURE__ */ React.createElement("option", { value: "", disabled: true }, emptyOptionsMessage))) : optionGroups ? optionGroups.map((group) => /* @__PURE__ */ React.createElement("fieldset", { key: group.label, style: editorStyles.toolbarOptionGroup }, /* @__PURE__ */ React.createElement("legend", { style: editorStyles.toolbarOptionGroupLabel }, group.label), group.options.map(renderOption))) : insertion.options.map(renderOption)
1191
397
  ) : null;
1192
398
  return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { style: editorStyles.toolbarDropdown }, /* @__PURE__ */ React.createElement(
1193
399
  "button",
@@ -1212,6 +418,8 @@ function InsertionPopovers({
1212
418
  insertions,
1213
419
  openPopoverId,
1214
420
  onToggle,
421
+ emptyOptionsMessages,
422
+ optionGroups,
1215
423
  ...props
1216
424
  }) {
1217
425
  return /* @__PURE__ */ React.createElement(React.Fragment, null, insertions.map((insertion) => /* @__PURE__ */ React.createElement(
@@ -1220,6 +428,8 @@ function InsertionPopovers({
1220
428
  key: insertion.id,
1221
429
  ...props,
1222
430
  insertion,
431
+ emptyOptionsMessage: emptyOptionsMessages?.[insertion.id] ?? props.emptyOptionsMessage,
432
+ optionGroups: optionGroups?.[insertion.id],
1223
433
  isOpen: openPopoverId === insertion.id,
1224
434
  onToggle: (trigger) => onToggle(insertion.id, trigger)
1225
435
  }
@@ -1227,23 +437,57 @@ function InsertionPopovers({
1227
437
  }
1228
438
 
1229
439
  // packages/ssml-editor-react/src/components/popovers/ProsodyPopovers.tsx
1230
- function ProsodyPopovers({ insertions, voiceName, ...props }) {
440
+ var CATEGORY_LABEL_KEYS = {
441
+ emotions: "categoryEmotions",
442
+ scenarios: "categoryScenarios",
443
+ media: "categoryMedia",
444
+ other: "categoryOther"
445
+ };
446
+ var CATEGORY_ORDER = ["emotions", "scenarios", "media", "other"];
447
+ function isExpressAsInsertion(insertion) {
448
+ return insertion.id === "emotion" && insertion.tagName?.toLowerCase() === "mstts:express-as";
449
+ }
450
+ function createOptionGroups(options, language) {
451
+ const t = (key) => EDITOR_COPY[language][key];
452
+ return CATEGORY_ORDER.map((category) => ({
453
+ label: t(CATEGORY_LABEL_KEYS[category]),
454
+ options: options.filter((option) => getExpressAsStyleCategory(option.value) === category)
455
+ })).filter((group) => group.options.length > 0);
456
+ }
457
+ function ProsodyPopovers({ insertions, voiceName, language, ...props }) {
458
+ const emptyOptionsMessages = {};
1231
459
  const filteredInsertions = insertions.map((insertion) => {
1232
- if (insertion.id !== "emotion" || insertion.tagName?.toLowerCase() !== "mstts:express-as") {
460
+ if (!isExpressAsInsertion(insertion)) {
1233
461
  return insertion;
1234
462
  }
1235
- const styles = new Set(
463
+ const availableStyles = new Set(
1236
464
  resolveExpressAsStyles(
1237
465
  voiceName,
1238
466
  insertion.options.map((option) => option.value)
1239
467
  )
1240
468
  );
469
+ if (availableStyles.size === 0) {
470
+ emptyOptionsMessages[insertion.id] = EDITOR_COPY[language].styleNotSupported;
471
+ }
472
+ const availableOptions = insertion.options.filter((option) => availableStyles.has(option.value));
1241
473
  return {
1242
474
  ...insertion,
1243
- options: insertion.options.filter((option) => styles.has(option.value))
475
+ options: availableOptions
1244
476
  };
1245
477
  });
1246
- return /* @__PURE__ */ React.createElement(InsertionPopovers, { ...props, insertions: filteredInsertions });
478
+ const optionGroups = Object.fromEntries(
479
+ filteredInsertions.filter(isExpressAsInsertion).map((insertion) => [insertion.id, createOptionGroups(insertion.options, language)])
480
+ );
481
+ return /* @__PURE__ */ React.createElement(
482
+ InsertionPopovers,
483
+ {
484
+ ...props,
485
+ emptyOptionsMessages,
486
+ language,
487
+ insertions: filteredInsertions,
488
+ optionGroups
489
+ }
490
+ );
1247
491
  }
1248
492
 
1249
493
  // packages/ssml-editor-react/src/components/popovers/TextPopovers.tsx
@@ -1258,1597 +502,30 @@ function TimingPopovers(props) {
1258
502
 
1259
503
  // packages/ssml-editor-react/src/hooks/useSsmlEditorState.ts
1260
504
  import { useCallback, useEffect, useId, useRef, useState } from "react";
1261
-
1262
- // packages/ssml-core/dist/index.mjs
1263
- var __typeError = (msg) => {
1264
- throw TypeError(msg);
1265
- };
1266
- var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
1267
- var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
1268
- var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
1269
- var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
1270
- var SYNTHESIS_NAMESPACE = "http://www.w3.org/2001/10/synthesis";
1271
- var MSTTS_NAMESPACE = "http://www.w3.org/2001/mstts";
1272
- var DEFAULT_SSML_VERSION = "1.0";
1273
- var DEFAULT_SSML_LANGUAGE = "en-US";
1274
- var MAX_NESTING_DEPTH = 1e3;
1275
- var XML_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_.:-]*$/;
1276
- var MSTTS_TAG_PREFIX = "mstts:";
1277
- var SSML_TAGS = {
1278
- SPEAK: "speak",
1279
- VOICE: "voice",
1280
- PROSODY: "prosody",
1281
- BREAK: "break",
1282
- EXPRESS_AS: "express-as",
1283
- EXPRESS_AS_CAMEL: "expressAs",
1284
- MSTTS_EXPRESS_AS: "mstts:express-as",
1285
- SAY_AS: "say-as",
1286
- SAY_AS_CAMEL: "sayAs",
1287
- PHONEME: "phoneme",
1288
- EMPHASIS: "emphasis",
1289
- AUDIO: "audio",
1290
- SUB: "sub",
1291
- LANG: "lang",
1292
- MARK: "mark",
1293
- BOOKMARK: "bookmark",
1294
- LEXICON: "lexicon",
1295
- PARAGRAPH: "p",
1296
- SENTENCE: "s",
1297
- WORD: "w",
1298
- MSTTS_SILENCE: "mstts:silence",
1299
- SILENCE: "silence",
1300
- MSTTS_VISEME: "mstts:viseme",
1301
- VISEME: "viseme"
1302
- };
1303
- var SSML_ATTRS = {
1304
- VERSION: "version",
1305
- XMLNS: "xmlns",
1306
- XML_LANG: "xml:lang",
1307
- LANG: "lang",
1308
- MSTTS_XMLNS: "xmlns:mstts",
1309
- NAME: "name",
1310
- EFFECT: "effect",
1311
- RATE: "rate",
1312
- PITCH: "pitch",
1313
- VOLUME: "volume",
1314
- CONTOUR: "contour",
1315
- RANGE: "range",
1316
- TIME: "time",
1317
- STRENGTH: "strength",
1318
- STYLE: "style",
1319
- STYLE_DEGREE: "styledegree",
1320
- STYLE_DEGREE_CAMEL: "styleDegree",
1321
- ROLE: "role",
1322
- INTERPRET_AS: "interpret-as",
1323
- FORMAT: "format",
1324
- DETAIL: "detail",
1325
- ALPHABET: "alphabet",
1326
- PH: "ph",
1327
- LEVEL: "level",
1328
- SRC: "src",
1329
- DESC: "desc",
1330
- CLIP_BEGIN: "clipBegin",
1331
- CLIP_END: "clipEnd",
1332
- SPEED: "speed",
1333
- REPEAT_COUNT: "repeatCount",
1334
- REPEAT_DURATION: "repeatDuration",
1335
- SOUND_LEVEL: "soundLevel",
1336
- ALIAS: "alias",
1337
- MARK: "mark",
1338
- URI: "uri",
1339
- TYPE: "type",
1340
- VALUE: "value"
505
+ var TIMING_INSERTION_TAGS = /* @__PURE__ */ new Set(["break", "mstts:silence"]);
506
+ var PROSODY_INSERTION_TAGS = /* @__PURE__ */ new Set(["prosody", "mstts:express-as", "voice", "emphasis"]);
507
+ var TEXT_INSERTION_TAGS = /* @__PURE__ */ new Set(["sub", "say-as", "phoneme", "w", "lang"]);
508
+ var EMPTY_SELECTION_OVERLAY = {
509
+ selectedText: "",
510
+ characterCount: 0,
511
+ hasSelection: false,
512
+ position: null,
513
+ placement: "above"
1341
514
  };
1342
- function escapeText(value) {
1343
- return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1344
- }
1345
- function escapeAttribute(value) {
1346
- return escapeText(value).replace(/"/g, "&quot;").replace(/'/g, "&apos;");
1347
- }
1348
- function addAttribute(attributes, name, value) {
1349
- if (value !== void 0) {
1350
- attributes[name] = value;
1351
- }
1352
- }
1353
- function getAttributes(element) {
1354
- const attributes = {
1355
- ...element.attributes ?? {}
1356
- };
1357
- switch (element.type) {
1358
- case SSML_TAGS.VOICE:
1359
- addAttribute(attributes, SSML_ATTRS.NAME, element.name);
1360
- addAttribute(attributes, SSML_ATTRS.EFFECT, element.effect);
1361
- break;
1362
- case SSML_TAGS.PROSODY:
1363
- addAttribute(attributes, SSML_ATTRS.RATE, element.rate);
1364
- addAttribute(attributes, SSML_ATTRS.PITCH, element.pitch);
1365
- addAttribute(attributes, SSML_ATTRS.VOLUME, element.volume);
1366
- addAttribute(attributes, SSML_ATTRS.CONTOUR, element.contour);
1367
- addAttribute(attributes, SSML_ATTRS.RANGE, element.range);
1368
- break;
1369
- case SSML_TAGS.BREAK:
1370
- addAttribute(attributes, SSML_ATTRS.TIME, element.time);
1371
- addAttribute(attributes, SSML_ATTRS.STRENGTH, element.strength);
1372
- break;
1373
- case SSML_TAGS.EXPRESS_AS:
1374
- case SSML_TAGS.EXPRESS_AS_CAMEL:
1375
- case SSML_TAGS.MSTTS_EXPRESS_AS:
1376
- addAttribute(attributes, SSML_ATTRS.STYLE, element.style);
1377
- addAttribute(attributes, SSML_ATTRS.STYLE_DEGREE, element.styleDegree);
1378
- addAttribute(attributes, SSML_ATTRS.ROLE, element.role);
1379
- break;
1380
- case SSML_TAGS.SAY_AS:
1381
- case SSML_TAGS.SAY_AS_CAMEL:
1382
- addAttribute(attributes, SSML_ATTRS.INTERPRET_AS, element.interpretAs);
1383
- addAttribute(attributes, SSML_ATTRS.FORMAT, element.format);
1384
- addAttribute(attributes, SSML_ATTRS.DETAIL, element.detail);
1385
- break;
1386
- case SSML_TAGS.PHONEME:
1387
- addAttribute(attributes, SSML_ATTRS.ALPHABET, element.alphabet);
1388
- addAttribute(attributes, SSML_ATTRS.PH, element.ph);
1389
- break;
1390
- case SSML_TAGS.EMPHASIS:
1391
- addAttribute(attributes, SSML_ATTRS.LEVEL, element.level);
1392
- break;
1393
- case SSML_TAGS.AUDIO:
1394
- addAttribute(attributes, SSML_ATTRS.SRC, element.src);
1395
- addAttribute(attributes, SSML_ATTRS.DESC, element.desc);
1396
- addAttribute(attributes, SSML_ATTRS.CLIP_BEGIN, element.clipBegin);
1397
- addAttribute(attributes, SSML_ATTRS.CLIP_END, element.clipEnd);
1398
- addAttribute(attributes, SSML_ATTRS.SPEED, element.speed);
1399
- addAttribute(attributes, SSML_ATTRS.REPEAT_COUNT, element.repeatCount);
1400
- addAttribute(attributes, SSML_ATTRS.REPEAT_DURATION, element.repeatDuration);
1401
- addAttribute(attributes, SSML_ATTRS.SOUND_LEVEL, element.soundLevel);
1402
- break;
1403
- case SSML_TAGS.SUB:
1404
- addAttribute(attributes, SSML_ATTRS.ALIAS, element.alias);
1405
- break;
1406
- case SSML_TAGS.LANG:
1407
- addAttribute(attributes, SSML_ATTRS.XML_LANG, element.lang);
1408
- break;
1409
- case SSML_TAGS.MARK:
1410
- addAttribute(attributes, SSML_ATTRS.NAME, element.name);
1411
- break;
1412
- case SSML_TAGS.BOOKMARK:
1413
- addAttribute(attributes, SSML_ATTRS.MARK, element.mark);
1414
- break;
1415
- case SSML_TAGS.LEXICON:
1416
- addAttribute(attributes, SSML_ATTRS.URI, element.uri);
1417
- break;
1418
- case SSML_TAGS.MSTTS_SILENCE:
1419
- case SSML_TAGS.SILENCE:
1420
- addAttribute(attributes, SSML_ATTRS.TYPE, element.typeValue ?? element.silenceType);
1421
- addAttribute(attributes, SSML_ATTRS.VALUE, element.value);
1422
- break;
1423
- case SSML_TAGS.MSTTS_VISEME:
1424
- case SSML_TAGS.VISEME:
1425
- addAttribute(attributes, SSML_ATTRS.TYPE, element.typeValue ?? element.visemeType);
1426
- break;
1427
- case SSML_TAGS.PARAGRAPH:
1428
- case SSML_TAGS.SENTENCE:
1429
- case SSML_TAGS.WORD:
1430
- case "element":
1431
- case "custom":
1432
- break;
1433
- }
1434
- return attributes;
1435
- }
1436
- function getTagName(element) {
1437
- switch (element.type) {
1438
- case SSML_TAGS.EXPRESS_AS:
1439
- case SSML_TAGS.EXPRESS_AS_CAMEL:
1440
- case SSML_TAGS.MSTTS_EXPRESS_AS:
1441
- return SSML_TAGS.MSTTS_EXPRESS_AS;
1442
- case SSML_TAGS.SAY_AS:
1443
- case SSML_TAGS.SAY_AS_CAMEL:
1444
- return SSML_TAGS.SAY_AS;
1445
- case SSML_TAGS.SILENCE:
1446
- case SSML_TAGS.MSTTS_SILENCE:
1447
- return SSML_TAGS.MSTTS_SILENCE;
1448
- case SSML_TAGS.VISEME:
1449
- case SSML_TAGS.MSTTS_VISEME:
1450
- return SSML_TAGS.MSTTS_VISEME;
1451
- case "element":
1452
- case "custom":
1453
- return element.name;
1454
- default:
1455
- return element.type;
1456
- }
515
+ function isSsmlElement(node) {
516
+ return typeof node !== "string" && node.type !== "text";
1457
517
  }
1458
- function getChildren(element) {
1459
- return element.children ?? [];
518
+ function isVoice(element) {
519
+ return element.type === "voice";
1460
520
  }
1461
- function validateName(name, kind) {
1462
- if (!XML_NAME_PATTERN.test(name)) {
1463
- throw new Error(`Invalid XML ${kind} name: ${name}`);
1464
- }
521
+ function isProsody(element) {
522
+ return element.type === "prosody";
1465
523
  }
1466
- function serializeAttributes(attributes) {
1467
- return Object.entries(attributes).map(([name, value]) => {
1468
- validateName(name, "attribute");
1469
- return ` ${name}="${escapeAttribute(String(value))}"`;
1470
- }).join("");
1471
- }
1472
- function serializeNode(node) {
1473
- if (typeof node === "string") {
1474
- return escapeText(node);
1475
- }
1476
- if (node.type === "text") {
1477
- return escapeText(node.value);
1478
- }
1479
- const tagName = getTagName(node);
1480
- validateName(tagName, "element");
1481
- const attributes = serializeAttributes(getAttributes(node));
1482
- const children = getChildren(node);
1483
- if (children.length === 0) {
1484
- return `<${tagName}${attributes}/>`;
1485
- }
1486
- return `<${tagName}${attributes}>${children.map(serializeNode).join("")}</${tagName}>`;
1487
- }
1488
- function usesMsttsNamespace(nodes) {
1489
- return nodes.some((node) => {
1490
- if (typeof node === "string" || node.type === "text") {
1491
- return false;
1492
- }
1493
- const tagName = getTagName(node);
1494
- return tagName.startsWith(MSTTS_TAG_PREFIX) || usesMsttsNamespace(getChildren(node));
1495
- });
1496
- }
1497
- function serializeDocument(document2) {
1498
- const children = document2.children ?? (document2.content === void 0 ? [] : [document2.content]);
1499
- const attributes = {
1500
- ...document2.attributes ?? {},
1501
- [SSML_ATTRS.VERSION]: document2.version,
1502
- [SSML_ATTRS.XMLNS]: SYNTHESIS_NAMESPACE,
1503
- [SSML_ATTRS.XML_LANG]: document2.lang
1504
- };
1505
- if (usesMsttsNamespace(children) && attributes[SSML_ATTRS.MSTTS_XMLNS] === void 0) {
1506
- attributes[SSML_ATTRS.MSTTS_XMLNS] = MSTTS_NAMESPACE;
1507
- }
1508
- return `<${SSML_TAGS.SPEAK}${serializeAttributes(attributes)}>${children.map(serializeNode).join("")}</${SSML_TAGS.SPEAK}>`;
1509
- }
1510
- function buildSsml(documentOrContent, lang = DEFAULT_SSML_LANGUAGE) {
1511
- if (typeof documentOrContent === "string") {
1512
- return {
1513
- version: DEFAULT_SSML_VERSION,
1514
- lang,
1515
- content: documentOrContent
1516
- };
1517
- }
1518
- return serializeDocument(documentOrContent);
1519
- }
1520
- var XML_ENTITIES = {
1521
- amp: "&",
1522
- apos: "'",
1523
- gt: ">",
1524
- lt: "<",
1525
- quot: '"'
1526
- };
1527
- function hasOwn(object, property) {
1528
- return Object.getOwnPropertyDescriptor(object, property) !== void 0;
1529
- }
1530
- function setAttribute(attributes, name, value) {
1531
- Object.defineProperty(attributes, name, {
1532
- configurable: true,
1533
- enumerable: true,
1534
- value,
1535
- writable: true
1536
- });
1537
- }
1538
- function decodeEntity(entity) {
1539
- const namedValue = hasOwn(XML_ENTITIES, entity) ? XML_ENTITIES[entity] : void 0;
1540
- if (namedValue !== void 0) {
1541
- return namedValue;
1542
- }
1543
- const isHexadecimal = entity.startsWith("#x") || entity.startsWith("#X");
1544
- const isDecimal = entity.startsWith("#");
1545
- if (!isHexadecimal && !isDecimal) {
1546
- throw new Error(`Unknown XML entity: &${entity};`);
1547
- }
1548
- const digits = entity.slice(isHexadecimal ? 2 : 1);
1549
- const codePoint = Number.parseInt(digits, isHexadecimal ? 16 : 10);
1550
- if (!digits || !Number.isInteger(codePoint) || codePoint < 0 || codePoint > 1114111 || codePoint >= 55296 && codePoint <= 57343 || codePoint < 32 && ![9, 10, 13].includes(codePoint)) {
1551
- throw new Error(`Invalid XML character reference: &${entity};`);
1552
- }
1553
- return String.fromCodePoint(codePoint);
1554
- }
1555
- function decodeXmlEntities(value) {
1556
- let result = "";
1557
- let start = 0;
1558
- while (true) {
1559
- const ampersand = value.indexOf("&", start);
1560
- if (ampersand === -1) {
1561
- return result + value.slice(start);
1562
- }
1563
- result += value.slice(start, ampersand);
1564
- const semicolon = value.indexOf(";", ampersand + 1);
1565
- if (semicolon === -1) {
1566
- throw new Error("Unterminated XML entity reference");
1567
- }
1568
- result += decodeEntity(value.slice(ampersand + 1, semicolon));
1569
- start = semicolon + 1;
1570
- }
1571
- }
1572
- function isXmlNameStart(value) {
1573
- return value !== void 0 && /[A-Za-z_]/.test(value);
1574
- }
1575
- function isXmlNameCharacter(value) {
1576
- return value !== void 0 && /[A-Za-z0-9_.:-]/.test(value);
1577
- }
1578
- function isXmlWhitespace(value) {
1579
- return value === " " || value === " " || value === "\r" || value === "\n";
1580
- }
1581
- function removeStandardNamespaceAttributes(attributes) {
1582
- if (attributes[SSML_ATTRS.XMLNS] === SYNTHESIS_NAMESPACE) {
1583
- delete attributes[SSML_ATTRS.XMLNS];
1584
- }
1585
- if (attributes[SSML_ATTRS.MSTTS_XMLNS] === MSTTS_NAMESPACE) {
1586
- delete attributes[SSML_ATTRS.MSTTS_XMLNS];
1587
- }
1588
- }
1589
- var _index;
1590
- var XmlParser = class {
1591
- constructor(source) {
1592
- __privateAdd(this, _index, 0);
1593
- this.source = source;
1594
- }
1595
- parse() {
1596
- if (this.source.charCodeAt(0) === 65279) {
1597
- __privateSet(this, _index, __privateGet(this, _index) + 1);
1598
- }
1599
- this.skipMisc();
1600
- if (__privateGet(this, _index) >= this.source.length) {
1601
- this.fail("SSML input is empty");
1602
- }
1603
- if (this.source[__privateGet(this, _index)] !== "<") {
1604
- this.fail("SSML input must start with an XML element");
1605
- }
1606
- const root = this.parseElement(0);
1607
- this.skipMisc();
1608
- if (__privateGet(this, _index) !== this.source.length) {
1609
- this.fail("Unexpected content after the root XML element");
1610
- }
1611
- return root;
1612
- }
1613
- parseElement(depth) {
1614
- if (depth > MAX_NESTING_DEPTH) {
1615
- this.fail("XML nesting depth exceeds the supported limit");
1616
- }
1617
- this.expect("<");
1618
- if (this.source[__privateGet(this, _index)] === "/") {
1619
- this.fail("Unexpected closing XML element");
1620
- }
1621
- const name = this.parseName();
1622
- const { attributes, selfClosing } = this.parseStartTag();
1623
- if (selfClosing) {
1624
- return { name, attributes, children: [] };
1625
- }
1626
- const children = [];
1627
- while (__privateGet(this, _index) < this.source.length) {
1628
- if (this.source.startsWith("</", __privateGet(this, _index))) {
1629
- __privateSet(this, _index, __privateGet(this, _index) + 2);
1630
- const closingName = this.parseName();
1631
- this.skipWhitespace();
1632
- this.expect(">");
1633
- if (closingName !== name) {
1634
- this.fail(`Mismatched closing element: expected </${name}> but found </${closingName}>`);
1635
- }
1636
- return { name, attributes, children };
1637
- }
1638
- if (this.source.startsWith("<!--", __privateGet(this, _index))) {
1639
- this.skipComment();
1640
- continue;
1641
- }
1642
- if (this.source.startsWith("<![CDATA[", __privateGet(this, _index))) {
1643
- this.appendText(children, this.parseCdata());
1644
- continue;
1645
- }
1646
- if (this.source.startsWith("<?", __privateGet(this, _index))) {
1647
- this.skipProcessingInstruction();
1648
- continue;
1649
- }
1650
- if (this.source.startsWith("<!", __privateGet(this, _index))) {
1651
- this.fail("Unsupported XML declaration inside an element");
1652
- }
1653
- if (this.source[__privateGet(this, _index)] === "<") {
1654
- children.push(this.parseElement(depth + 1));
1655
- } else {
1656
- this.appendText(children, this.parseText());
1657
- }
1658
- }
1659
- this.fail(`Unclosed XML element: <${name}>`);
1660
- }
1661
- parseStartTag() {
1662
- const attributes = {};
1663
- while (__privateGet(this, _index) < this.source.length) {
1664
- this.skipWhitespace();
1665
- if (this.source.startsWith("/>", __privateGet(this, _index))) {
1666
- __privateSet(this, _index, __privateGet(this, _index) + 2);
1667
- return { attributes, selfClosing: true };
1668
- }
1669
- if (this.source[__privateGet(this, _index)] === ">") {
1670
- __privateSet(this, _index, __privateGet(this, _index) + 1);
1671
- return { attributes, selfClosing: false };
1672
- }
1673
- const name = this.parseName();
1674
- this.skipWhitespace();
1675
- this.expect("=");
1676
- this.skipWhitespace();
1677
- const quote = this.source[__privateGet(this, _index)];
1678
- if (quote !== '"' && quote !== "'") {
1679
- this.fail(`XML attribute ${name} must use a quoted value`);
1680
- }
1681
- __privateSet(this, _index, __privateGet(this, _index) + 1);
1682
- const valueStart = __privateGet(this, _index);
1683
- while (__privateGet(this, _index) < this.source.length && this.source[__privateGet(this, _index)] !== quote) {
1684
- if (this.source[__privateGet(this, _index)] === "<") {
1685
- this.fail(`Invalid "<" in XML attribute ${name}`);
1686
- }
1687
- __privateSet(this, _index, __privateGet(this, _index) + 1);
1688
- }
1689
- if (__privateGet(this, _index) >= this.source.length) {
1690
- this.fail(`Unclosed XML attribute ${name}`);
1691
- }
1692
- const value = decodeXmlEntities(this.source.slice(valueStart, __privateGet(this, _index)));
1693
- __privateSet(this, _index, __privateGet(this, _index) + 1);
1694
- if (hasOwn(attributes, name)) {
1695
- this.fail(`Duplicate XML attribute: ${name}`);
1696
- }
1697
- setAttribute(attributes, name, value);
1698
- }
1699
- this.fail("Unclosed XML start tag");
1700
- }
1701
- parseText() {
1702
- const start = __privateGet(this, _index);
1703
- while (__privateGet(this, _index) < this.source.length && this.source[__privateGet(this, _index)] !== "<") {
1704
- __privateSet(this, _index, __privateGet(this, _index) + 1);
1705
- }
1706
- const value = this.source.slice(start, __privateGet(this, _index));
1707
- if (value.includes("]]>")) {
1708
- this.fail("CDATA termination is not valid in ordinary XML text");
1709
- }
1710
- return decodeXmlEntities(value);
1711
- }
1712
- parseCdata() {
1713
- __privateSet(this, _index, __privateGet(this, _index) + "<![CDATA[".length);
1714
- const end = this.source.indexOf("]]>", __privateGet(this, _index));
1715
- if (end === -1) {
1716
- this.fail("Unclosed XML CDATA section");
1717
- }
1718
- const value = this.source.slice(__privateGet(this, _index), end);
1719
- __privateSet(this, _index, end + 3);
1720
- return value;
1721
- }
1722
- skipComment() {
1723
- __privateSet(this, _index, __privateGet(this, _index) + "<!--".length);
1724
- const end = this.source.indexOf("-->", __privateGet(this, _index));
1725
- if (end === -1) {
1726
- this.fail("Unclosed XML comment");
1727
- }
1728
- if (this.source.slice(__privateGet(this, _index), end).includes("--")) {
1729
- this.fail("XML comments cannot contain consecutive hyphens");
1730
- }
1731
- __privateSet(this, _index, end + 3);
1732
- }
1733
- skipProcessingInstruction() {
1734
- __privateSet(this, _index, __privateGet(this, _index) + "<?".length);
1735
- this.parseName();
1736
- const end = this.source.indexOf("?>", __privateGet(this, _index));
1737
- if (end === -1) {
1738
- this.fail("Unclosed XML processing instruction");
1739
- }
1740
- __privateSet(this, _index, end + 2);
1741
- }
1742
- skipMisc() {
1743
- while (__privateGet(this, _index) < this.source.length) {
1744
- this.skipWhitespace();
1745
- if (this.source.startsWith("<!--", __privateGet(this, _index))) {
1746
- this.skipComment();
1747
- continue;
1748
- }
1749
- if (this.source.startsWith("<?", __privateGet(this, _index))) {
1750
- this.skipProcessingInstruction();
1751
- continue;
1752
- }
1753
- if (this.source.startsWith("<!DOCTYPE", __privateGet(this, _index))) {
1754
- this.fail("DOCTYPE declarations are not supported");
1755
- }
1756
- break;
1757
- }
1758
- }
1759
- parseName() {
1760
- const first = this.source[__privateGet(this, _index)];
1761
- if (!isXmlNameStart(first)) {
1762
- this.fail("Invalid XML name");
1763
- }
1764
- const start = __privateGet(this, _index);
1765
- __privateSet(this, _index, __privateGet(this, _index) + 1);
1766
- while (isXmlNameCharacter(this.source[__privateGet(this, _index)])) {
1767
- __privateSet(this, _index, __privateGet(this, _index) + 1);
1768
- }
1769
- return this.source.slice(start, __privateGet(this, _index));
1770
- }
1771
- appendText(children, value) {
1772
- if (!value) {
1773
- return;
1774
- }
1775
- const previous = children[children.length - 1];
1776
- if (typeof previous === "string") {
1777
- children[children.length - 1] = previous + value;
1778
- } else {
1779
- children.push(value);
1780
- }
1781
- }
1782
- skipWhitespace() {
1783
- while (isXmlWhitespace(this.source[__privateGet(this, _index)])) {
1784
- __privateSet(this, _index, __privateGet(this, _index) + 1);
1785
- }
1786
- }
1787
- expect(value) {
1788
- if (!this.source.startsWith(value, __privateGet(this, _index))) {
1789
- this.fail(`Expected "${value}"`);
1790
- }
1791
- __privateSet(this, _index, __privateGet(this, _index) + value.length);
1792
- }
1793
- fail(message) {
1794
- throw new Error(`${message} at position ${__privateGet(this, _index)}`);
1795
- }
1796
- };
1797
- _index = /* @__PURE__ */ new WeakMap();
1798
- function readAttribute(attributes, ...names) {
1799
- let found = false;
1800
- let value;
1801
- for (const name of names) {
1802
- if (hasOwn(attributes, name)) {
1803
- if (!found) {
1804
- value = String(attributes[name]);
1805
- found = true;
1806
- }
1807
- delete attributes[name];
1808
- }
1809
- }
1810
- return value;
1811
- }
1812
- function getElementAttributes(node) {
1813
- const attributes = { ...node.attributes };
1814
- removeStandardNamespaceAttributes(attributes);
1815
- return attributes;
1816
- }
1817
- function finishElement(element, node, attributes) {
1818
- if (node.children.length > 0) {
1819
- element.children = node.children.map(convertNode);
1820
- }
1821
- if (Object.keys(attributes).length > 0) {
1822
- element.attributes = attributes;
1823
- }
1824
- return element;
1825
- }
1826
- function convertElement(node) {
1827
- const attributes = getElementAttributes(node);
1828
- switch (node.name) {
1829
- case SSML_TAGS.VOICE: {
1830
- const element = { type: SSML_TAGS.VOICE };
1831
- const name = readAttribute(attributes, SSML_ATTRS.NAME);
1832
- const effect = readAttribute(attributes, SSML_ATTRS.EFFECT);
1833
- if (name !== void 0) element.name = name;
1834
- if (effect !== void 0) element.effect = effect;
1835
- return finishElement(element, node, attributes);
1836
- }
1837
- case SSML_TAGS.PROSODY: {
1838
- const element = { type: SSML_TAGS.PROSODY };
1839
- const rate = readAttribute(attributes, SSML_ATTRS.RATE);
1840
- const pitch = readAttribute(attributes, SSML_ATTRS.PITCH);
1841
- const volume = readAttribute(attributes, SSML_ATTRS.VOLUME);
1842
- const contour = readAttribute(attributes, SSML_ATTRS.CONTOUR);
1843
- const range = readAttribute(attributes, SSML_ATTRS.RANGE);
1844
- if (rate !== void 0) element.rate = rate;
1845
- if (pitch !== void 0) element.pitch = pitch;
1846
- if (volume !== void 0) element.volume = volume;
1847
- if (contour !== void 0) element.contour = contour;
1848
- if (range !== void 0) element.range = range;
1849
- return finishElement(element, node, attributes);
1850
- }
1851
- case SSML_TAGS.BREAK: {
1852
- const element = { type: SSML_TAGS.BREAK };
1853
- const time = readAttribute(attributes, SSML_ATTRS.TIME);
1854
- const strength = readAttribute(attributes, SSML_ATTRS.STRENGTH);
1855
- if (time !== void 0) element.time = time;
1856
- if (strength !== void 0) element.strength = strength;
1857
- return finishElement(element, node, attributes);
1858
- }
1859
- case SSML_TAGS.EXPRESS_AS:
1860
- case SSML_TAGS.EXPRESS_AS_CAMEL:
1861
- case SSML_TAGS.MSTTS_EXPRESS_AS: {
1862
- const element = { type: node.name };
1863
- const style = readAttribute(attributes, SSML_ATTRS.STYLE);
1864
- const styleDegree = readAttribute(attributes, SSML_ATTRS.STYLE_DEGREE, SSML_ATTRS.STYLE_DEGREE_CAMEL);
1865
- const role = readAttribute(attributes, SSML_ATTRS.ROLE);
1866
- if (style !== void 0) element.style = style;
1867
- if (styleDegree !== void 0) element.styleDegree = styleDegree;
1868
- if (role !== void 0) element.role = role;
1869
- return finishElement(element, node, attributes);
1870
- }
1871
- case SSML_TAGS.SAY_AS:
1872
- case SSML_TAGS.SAY_AS_CAMEL: {
1873
- const element = { type: node.name };
1874
- const interpretAs = readAttribute(attributes, SSML_ATTRS.INTERPRET_AS);
1875
- const format = readAttribute(attributes, SSML_ATTRS.FORMAT);
1876
- const detail = readAttribute(attributes, SSML_ATTRS.DETAIL);
1877
- if (interpretAs !== void 0) element.interpretAs = interpretAs;
1878
- if (format !== void 0) element.format = format;
1879
- if (detail !== void 0) element.detail = detail;
1880
- return finishElement(element, node, attributes);
1881
- }
1882
- case SSML_TAGS.PHONEME: {
1883
- const element = { type: SSML_TAGS.PHONEME };
1884
- const alphabet = readAttribute(attributes, SSML_ATTRS.ALPHABET);
1885
- const ph = readAttribute(attributes, SSML_ATTRS.PH);
1886
- if (alphabet !== void 0) element.alphabet = alphabet;
1887
- if (ph !== void 0) element.ph = ph;
1888
- return finishElement(element, node, attributes);
1889
- }
1890
- case SSML_TAGS.EMPHASIS: {
1891
- const element = { type: SSML_TAGS.EMPHASIS };
1892
- const level = readAttribute(attributes, SSML_ATTRS.LEVEL);
1893
- if (level !== void 0) element.level = level;
1894
- return finishElement(element, node, attributes);
1895
- }
1896
- case SSML_TAGS.AUDIO: {
1897
- const element = { type: SSML_TAGS.AUDIO };
1898
- const src = readAttribute(attributes, SSML_ATTRS.SRC);
1899
- const desc = readAttribute(attributes, SSML_ATTRS.DESC);
1900
- const clipBegin = readAttribute(attributes, SSML_ATTRS.CLIP_BEGIN);
1901
- const clipEnd = readAttribute(attributes, SSML_ATTRS.CLIP_END);
1902
- const speed = readAttribute(attributes, SSML_ATTRS.SPEED);
1903
- const repeatCount = readAttribute(attributes, SSML_ATTRS.REPEAT_COUNT);
1904
- const repeatDuration = readAttribute(attributes, SSML_ATTRS.REPEAT_DURATION);
1905
- const soundLevel = readAttribute(attributes, SSML_ATTRS.SOUND_LEVEL);
1906
- if (src !== void 0) element.src = src;
1907
- if (desc !== void 0) element.desc = desc;
1908
- if (clipBegin !== void 0) element.clipBegin = clipBegin;
1909
- if (clipEnd !== void 0) element.clipEnd = clipEnd;
1910
- if (speed !== void 0) element.speed = speed;
1911
- if (repeatCount !== void 0) element.repeatCount = repeatCount;
1912
- if (repeatDuration !== void 0) element.repeatDuration = repeatDuration;
1913
- if (soundLevel !== void 0) element.soundLevel = soundLevel;
1914
- return finishElement(element, node, attributes);
1915
- }
1916
- case SSML_TAGS.SUB: {
1917
- const element = { type: SSML_TAGS.SUB };
1918
- const alias = readAttribute(attributes, SSML_ATTRS.ALIAS);
1919
- if (alias !== void 0) element.alias = alias;
1920
- return finishElement(element, node, attributes);
1921
- }
1922
- case SSML_TAGS.LANG: {
1923
- const element = { type: SSML_TAGS.LANG };
1924
- const lang = readAttribute(attributes, SSML_ATTRS.XML_LANG, SSML_ATTRS.LANG);
1925
- if (lang !== void 0) element.lang = lang;
1926
- return finishElement(element, node, attributes);
1927
- }
1928
- case SSML_TAGS.MARK: {
1929
- const element = { type: SSML_TAGS.MARK };
1930
- const name = readAttribute(attributes, SSML_ATTRS.NAME);
1931
- if (name !== void 0) element.name = name;
1932
- return finishElement(element, node, attributes);
1933
- }
1934
- case SSML_TAGS.BOOKMARK: {
1935
- const element = { type: SSML_TAGS.BOOKMARK };
1936
- const mark = readAttribute(attributes, SSML_ATTRS.MARK);
1937
- if (mark !== void 0) element.mark = mark;
1938
- return finishElement(element, node, attributes);
1939
- }
1940
- case SSML_TAGS.LEXICON: {
1941
- const element = { type: SSML_TAGS.LEXICON };
1942
- const uri = readAttribute(attributes, SSML_ATTRS.URI);
1943
- if (uri !== void 0) element.uri = uri;
1944
- return finishElement(element, node, attributes);
1945
- }
1946
- case SSML_TAGS.PARAGRAPH: {
1947
- const element = { type: SSML_TAGS.PARAGRAPH };
1948
- return finishElement(element, node, attributes);
1949
- }
1950
- case SSML_TAGS.SENTENCE: {
1951
- const element = { type: SSML_TAGS.SENTENCE };
1952
- return finishElement(element, node, attributes);
1953
- }
1954
- case SSML_TAGS.WORD: {
1955
- const element = { type: SSML_TAGS.WORD };
1956
- return finishElement(element, node, attributes);
1957
- }
1958
- case SSML_TAGS.MSTTS_SILENCE:
1959
- case SSML_TAGS.SILENCE: {
1960
- const element = {
1961
- type: node.name === SSML_TAGS.MSTTS_SILENCE ? SSML_TAGS.MSTTS_SILENCE : SSML_TAGS.SILENCE
1962
- };
1963
- const typeValue = readAttribute(attributes, SSML_ATTRS.TYPE);
1964
- const value = readAttribute(attributes, SSML_ATTRS.VALUE);
1965
- if (typeValue !== void 0) element.typeValue = typeValue;
1966
- if (value !== void 0) element.value = value;
1967
- return finishElement(element, node, attributes);
1968
- }
1969
- case SSML_TAGS.MSTTS_VISEME:
1970
- case SSML_TAGS.VISEME: {
1971
- const element = {
1972
- type: node.name === SSML_TAGS.MSTTS_VISEME ? SSML_TAGS.MSTTS_VISEME : SSML_TAGS.VISEME
1973
- };
1974
- const typeValue = readAttribute(attributes, SSML_ATTRS.TYPE);
1975
- if (typeValue !== void 0) element.typeValue = typeValue;
1976
- return finishElement(element, node, attributes);
1977
- }
1978
- default: {
1979
- const element = {
1980
- name: node.name,
1981
- type: "custom"
1982
- };
1983
- return finishElement(element, node, attributes);
1984
- }
1985
- }
1986
- }
1987
- function convertNode(node) {
1988
- return typeof node === "string" ? node : convertElement(node);
1989
- }
1990
- function parseSsml(xmlString) {
1991
- if (typeof xmlString !== "string") {
1992
- throw new TypeError("SSML input must be a string");
1993
- }
1994
- const root = new XmlParser(xmlString).parse();
1995
- if (root.name !== SSML_TAGS.SPEAK) {
1996
- throw new Error(`SSML root element must be <${SSML_TAGS.SPEAK}>, found <${root.name}>`);
1997
- }
1998
- const attributes = { ...root.attributes };
1999
- const version = readAttribute(attributes, SSML_ATTRS.VERSION);
2000
- const lang = readAttribute(attributes, SSML_ATTRS.XML_LANG, SSML_ATTRS.LANG);
2001
- if (version === void 0) {
2002
- throw new Error(`SSML <${SSML_TAGS.SPEAK}> element is missing the "${SSML_ATTRS.VERSION}" attribute`);
2003
- }
2004
- if (lang === void 0) {
2005
- throw new Error(`SSML <${SSML_TAGS.SPEAK}> element is missing the "${SSML_ATTRS.XML_LANG}" attribute`);
2006
- }
2007
- removeStandardNamespaceAttributes(attributes);
2008
- const document2 = {
2009
- children: root.children.map(convertNode),
2010
- lang,
2011
- type: SSML_TAGS.SPEAK,
2012
- version
2013
- };
2014
- if (Object.keys(attributes).length > 0) {
2015
- document2.attributes = attributes;
2016
- }
2017
- return document2;
2018
- }
2019
- function escapeAttribute2(value) {
2020
- return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/'/g, "&apos;");
2021
- }
2022
- function getPartialTextNodes(text, version, lang) {
2023
- if (!text.includes("<")) {
2024
- return [text];
2025
- }
2026
- try {
2027
- const openingTag = `<${SSML_TAGS.SPEAK} ${SSML_ATTRS.VERSION}="${escapeAttribute2(version)}" ${SSML_ATTRS.XMLNS}="${SYNTHESIS_NAMESPACE}" ${SSML_ATTRS.XML_LANG}="${escapeAttribute2(lang)}">`;
2028
- return parseSsml(`${openingTag}${text}</${SSML_TAGS.SPEAK}>`).children ?? [];
2029
- } catch {
2030
- return [{ type: "text", value: text }];
2031
- }
2032
- }
2033
- function getVoiceContext(context) {
2034
- const voice = context.voice;
2035
- if (typeof voice === "object" && voice !== null) {
2036
- return voice;
2037
- }
2038
- if (context.voiceName === void 0 && context.voiceEffect === void 0 && typeof voice !== "string") {
2039
- return void 0;
2040
- }
2041
- return {
2042
- name: context.voiceName ?? voice,
2043
- effect: context.voiceEffect
2044
- };
2045
- }
2046
- function serializePartialSsml(text, context) {
2047
- const version = context.version ?? DEFAULT_SSML_VERSION;
2048
- const lang = context.lang ?? DEFAULT_SSML_LANGUAGE;
2049
- let children = getPartialTextNodes(text, version, lang);
2050
- if (context.prosody) {
2051
- children = [
2052
- {
2053
- type: SSML_TAGS.PROSODY,
2054
- ...context.prosody,
2055
- children
2056
- }
2057
- ];
2058
- }
2059
- const voice = getVoiceContext(context);
2060
- if (voice) {
2061
- children = [
2062
- {
2063
- type: SSML_TAGS.VOICE,
2064
- ...voice,
2065
- children
2066
- }
2067
- ];
2068
- }
2069
- const document2 = {
2070
- type: SSML_TAGS.SPEAK,
2071
- version,
2072
- lang,
2073
- attributes: context.attributes,
2074
- children
2075
- };
2076
- return buildSsml(document2);
2077
- }
2078
- function buildPartialSsml(textOrOptions, context) {
2079
- if (typeof textOrOptions === "string") {
2080
- return serializePartialSsml(textOrOptions, context ?? {});
2081
- }
2082
- return serializePartialSsml(textOrOptions.text, textOrOptions);
2083
- }
2084
- var PARSER_POSITION_SUFFIX = / at position (\d+)$/;
2085
- function validateSsml(xmlString) {
2086
- try {
2087
- parseSsml(xmlString);
2088
- return null;
2089
- } catch (error) {
2090
- const rawMessage = error instanceof Error ? error.message : String(error);
2091
- const positionMatch = PARSER_POSITION_SUFFIX.exec(rawMessage);
2092
- return {
2093
- message: positionMatch ? rawMessage.slice(0, positionMatch.index) : rawMessage,
2094
- position: positionMatch ? Number.parseInt(positionMatch[1], 10) : 0
2095
- };
2096
- }
2097
- }
2098
-
2099
- // packages/ssml-editor-react/src/clearSsmlDocument.ts
2100
- function getDocumentChildren(document2) {
2101
- return document2.children ?? (document2.content === void 0 ? [] : [document2.content]);
2102
- }
2103
- function appendNode(nodes, node) {
2104
- if (typeof node === "string") {
2105
- const lastNode = nodes[nodes.length - 1];
2106
- if (typeof lastNode === "string") {
2107
- nodes[nodes.length - 1] = lastNode + node;
2108
- } else if (node !== "") {
2109
- nodes.push(node);
2110
- }
2111
- return;
2112
- }
2113
- nodes.push(node);
2114
- }
2115
- function clearNodes(nodes) {
2116
- const clearedNodes = [];
2117
- for (const node of nodes) {
2118
- if (typeof node === "string") {
2119
- appendNode(clearedNodes, node);
2120
- continue;
2121
- }
2122
- if (node.type === "text") {
2123
- appendNode(clearedNodes, node.value);
2124
- continue;
2125
- }
2126
- const children = clearNodes(node.children ?? []);
2127
- if (node.type === "voice") {
2128
- appendNode(clearedNodes, { ...node, children });
2129
- continue;
2130
- }
2131
- for (const child of children) {
2132
- appendNode(clearedNodes, child);
2133
- }
2134
- }
2135
- return clearedNodes;
2136
- }
2137
- function clearSsmlDocument(document2) {
2138
- const nextDocument = {
2139
- ...document2,
2140
- children: clearNodes(getDocumentChildren(document2))
2141
- };
2142
- if (nextDocument.content !== void 0) {
2143
- delete nextDocument.content;
2144
- }
2145
- return nextDocument;
2146
- }
2147
-
2148
- // packages/ssml-editor-react/src/formatXml.ts
2149
- var INDENT = " ";
2150
- var FRAGMENT_ROOT = "ssml-builder-fragment";
2151
- var XML_ENTITY_NAMES = /* @__PURE__ */ new Set(["amp", "apos", "gt", "lt", "quot"]);
2152
- var INTRINSICALLY_EMPTY_ELEMENTS = /* @__PURE__ */ new Set([
2153
- "break",
2154
- "bookmark",
2155
- "lexicon",
2156
- "mark",
2157
- "mstts:silence",
2158
- "mstts:viseme",
2159
- "silence",
2160
- "viseme"
2161
- ]);
2162
- function isXmlNameStart2(value) {
2163
- return value !== void 0 && /[A-Za-z_]/.test(value);
2164
- }
2165
- function isXmlNameCharacter2(value) {
2166
- return value !== void 0 && /[A-Za-z0-9_.:-]/.test(value);
2167
- }
2168
- function isXmlWhitespace2(value) {
2169
- return value === " " || value === " " || value === "\r" || value === "\n";
2170
- }
2171
- function fail(message) {
2172
- throw new Error(message);
2173
- }
2174
- function skipWhitespace(source, index) {
2175
- while (isXmlWhitespace2(source[index])) {
2176
- index += 1;
2177
- }
2178
- return index;
2179
- }
2180
- function readName(source, start, end) {
2181
- if (!isXmlNameStart2(source[start])) {
2182
- fail("Invalid XML name");
2183
- }
2184
- let index = start + 1;
2185
- while (index < end && isXmlNameCharacter2(source[index])) {
2186
- index += 1;
2187
- }
2188
- return { name: source.slice(start, index), index };
2189
- }
2190
- function validateCharacterReference(entity) {
2191
- const isHexadecimal = entity.startsWith("#x") || entity.startsWith("#X");
2192
- const isDecimal = entity.startsWith("#");
2193
- if (!isHexadecimal && !isDecimal) {
2194
- if (!XML_ENTITY_NAMES.has(entity)) {
2195
- fail(`Unknown XML entity: &${entity};`);
2196
- }
2197
- return;
2198
- }
2199
- const digits = entity.slice(isHexadecimal ? 2 : 1);
2200
- const validDigits = isHexadecimal ? /^[0-9A-Fa-f]+$/.test(digits) : /^[0-9]+$/.test(digits);
2201
- const codePoint = Number.parseInt(digits, isHexadecimal ? 16 : 10);
2202
- if (!validDigits || !Number.isInteger(codePoint) || codePoint < 0 || codePoint > 1114111 || codePoint >= 55296 && codePoint <= 57343 || codePoint < 32 && ![9, 10, 13].includes(codePoint)) {
2203
- fail(`Invalid XML character reference: &${entity};`);
2204
- }
2205
- }
2206
- function validateEntityReferences(value) {
2207
- let index = 0;
2208
- while (true) {
2209
- const ampersand = value.indexOf("&", index);
2210
- if (ampersand === -1) {
2211
- return;
2212
- }
2213
- const semicolon = value.indexOf(";", ampersand + 1);
2214
- if (semicolon === -1) {
2215
- fail("Unterminated XML entity reference");
2216
- }
2217
- validateCharacterReference(value.slice(ampersand + 1, semicolon));
2218
- index = semicolon + 1;
2219
- }
2220
- }
2221
- function findTagEnd(source, start, hasInternalSubset = false) {
2222
- let quote;
2223
- let subsetDepth = 0;
2224
- for (let index = start + 1; index < source.length; index += 1) {
2225
- const character = source[index];
2226
- if (quote !== void 0) {
2227
- if (character === quote) {
2228
- quote = void 0;
2229
- }
2230
- continue;
2231
- }
2232
- if (character === '"' || character === "'") {
2233
- quote = character;
2234
- continue;
2235
- }
2236
- if (hasInternalSubset) {
2237
- if (character === "[") {
2238
- subsetDepth += 1;
2239
- continue;
2240
- }
2241
- if (character === "]" && subsetDepth > 0) {
2242
- subsetDepth -= 1;
2243
- continue;
2244
- }
2245
- }
2246
- if (character === ">" && subsetDepth === 0) {
2247
- return index;
2248
- }
2249
- }
2250
- fail("Unclosed XML markup");
2251
- }
2252
- function parseStartTag(source, start, end) {
2253
- const nameResult = readName(source, start + 1, end);
2254
- let index = nameResult.index;
2255
- let hasAttribute = false;
2256
- const attributeNames = /* @__PURE__ */ new Set();
2257
- while (index < end) {
2258
- const beforeWhitespace = index;
2259
- index = skipWhitespace(source, index);
2260
- if (index === end) {
2261
- break;
2262
- }
2263
- if (source[index] === "/") {
2264
- if (index + 1 !== end) {
2265
- fail("Invalid XML self-closing tag");
2266
- }
2267
- return { name: nameResult.name, selfClosing: true };
2268
- }
2269
- if (hasAttribute && beforeWhitespace === index) {
2270
- fail("XML attributes must be separated by whitespace");
2271
- }
2272
- const attribute = readName(source, index, end);
2273
- index = skipWhitespace(source, attribute.index);
2274
- if (source[index] !== "=") {
2275
- fail(`XML attribute ${attribute.name} must have a value`);
2276
- }
2277
- index = skipWhitespace(source, index + 1);
2278
- const quote = source[index];
2279
- if (quote !== '"' && quote !== "'") {
2280
- fail(`XML attribute ${attribute.name} must use a quoted value`);
2281
- }
2282
- index += 1;
2283
- const valueStart = index;
2284
- while (index < end && source[index] !== quote) {
2285
- if (source[index] === "<") {
2286
- fail(`Invalid "<" in XML attribute ${attribute.name}`);
2287
- }
2288
- index += 1;
2289
- }
2290
- if (index === end) {
2291
- fail(`Unclosed XML attribute ${attribute.name}`);
2292
- }
2293
- if (attributeNames.has(attribute.name)) {
2294
- fail(`Duplicate XML attribute: ${attribute.name}`);
2295
- }
2296
- attributeNames.add(attribute.name);
2297
- validateEntityReferences(source.slice(valueStart, index));
2298
- index += 1;
2299
- hasAttribute = true;
2300
- }
2301
- return { name: nameResult.name, selfClosing: false };
2302
- }
2303
- function parseClosingTag(source, start, end) {
2304
- const nameResult = readName(source, start + 2, end);
2305
- if (skipWhitespace(source, nameResult.index) !== end) {
2306
- fail("Invalid XML closing tag");
2307
- }
2308
- return nameResult.name;
2309
- }
2310
- function parseProcessingInstruction(source, start, end) {
2311
- readName(source, start + 2, end);
2312
- }
2313
- function parseDeclaration(source, start, end) {
2314
- readName(source, start + 2, end);
2315
- }
2316
- function appendNode2(nodes, node) {
2317
- const previous = nodes[nodes.length - 1];
2318
- if (node.kind === "text" && previous?.kind === "text") {
2319
- previous.value += node.value;
2320
- return;
2321
- }
2322
- nodes.push(node);
2323
- }
2324
- function currentElement(stack) {
2325
- return stack[stack.length - 1];
2326
- }
2327
- function parseXml(source) {
2328
- const children = [];
2329
- const stack = [];
2330
- let root;
2331
- let index = 0;
2332
- while (index < source.length) {
2333
- if (source[index] !== "<") {
2334
- const textStart = index;
2335
- while (index < source.length && source[index] !== "<") {
2336
- index += 1;
2337
- }
2338
- const value = source.slice(textStart, index);
2339
- if (value.includes("]]>")) {
2340
- fail("CDATA termination is not valid in ordinary XML text");
2341
- }
2342
- validateEntityReferences(value);
2343
- if (stack.length === 0 && value.trim() !== "") {
2344
- fail("Unexpected text outside the root XML element");
2345
- }
2346
- appendNode2(currentElement(stack)?.children ?? children, { kind: "text", value });
2347
- continue;
2348
- }
2349
- if (source.startsWith("<!--", index)) {
2350
- const end2 = source.indexOf("-->", index + 4);
2351
- if (end2 === -1) {
2352
- fail("Unclosed XML comment");
2353
- }
2354
- const content = source.slice(index + 4, end2);
2355
- if (content.includes("--") || content.endsWith("-")) {
2356
- fail("Invalid XML comment");
2357
- }
2358
- appendNode2(currentElement(stack)?.children ?? children, {
2359
- kind: "comment",
2360
- raw: source.slice(index, end2 + 3)
2361
- });
2362
- index = end2 + 3;
2363
- continue;
2364
- }
2365
- if (source.startsWith("<![CDATA[", index)) {
2366
- const end2 = source.indexOf("]]>", index + "<![CDATA[".length);
2367
- if (end2 === -1) {
2368
- fail("Unclosed XML CDATA section");
2369
- }
2370
- if (stack.length === 0) {
2371
- fail("CDATA is not allowed outside the root XML element");
2372
- }
2373
- appendNode2(currentElement(stack)?.children ?? children, {
2374
- kind: "cdata",
2375
- raw: source.slice(index, end2 + 3)
2376
- });
2377
- index = end2 + 3;
2378
- continue;
2379
- }
2380
- if (source.startsWith("<?", index)) {
2381
- const end2 = source.indexOf("?>", index + 2);
2382
- if (end2 === -1) {
2383
- fail("Unclosed XML processing instruction");
2384
- }
2385
- parseProcessingInstruction(source, index, end2);
2386
- appendNode2(currentElement(stack)?.children ?? children, {
2387
- kind: "processing-instruction",
2388
- raw: source.slice(index, end2 + 2)
2389
- });
2390
- index = end2 + 2;
2391
- continue;
2392
- }
2393
- if (source.startsWith("</", index)) {
2394
- const end2 = findTagEnd(source, index);
2395
- const name2 = parseClosingTag(source, index, end2);
2396
- const element = currentElement(stack);
2397
- if (element === void 0 || element.name !== name2) {
2398
- fail(`Mismatched closing XML element: </${name2}>`);
2399
- }
2400
- element.close = source.slice(index, end2 + 1);
2401
- stack.pop();
2402
- index = end2 + 1;
2403
- continue;
2404
- }
2405
- if (source.startsWith("<!", index)) {
2406
- const end2 = findTagEnd(source, index, true);
2407
- parseDeclaration(source, index, end2);
2408
- appendNode2(currentElement(stack)?.children ?? children, {
2409
- kind: "declaration",
2410
- raw: source.slice(index, end2 + 1)
2411
- });
2412
- index = end2 + 1;
2413
- continue;
2414
- }
2415
- const end = findTagEnd(source, index);
2416
- const { name, selfClosing } = parseStartTag(source, index, end);
2417
- if (stack.length === 0) {
2418
- if (root !== void 0) {
2419
- fail("Multiple root XML elements are not allowed");
2420
- }
2421
- root = {
2422
- kind: "element",
2423
- name,
2424
- open: source.slice(index, end + 1),
2425
- selfClosing,
2426
- children: []
2427
- };
2428
- children.push(root);
2429
- } else {
2430
- const element = {
2431
- kind: "element",
2432
- name,
2433
- open: source.slice(index, end + 1),
2434
- selfClosing,
2435
- children: []
2436
- };
2437
- currentElement(stack)?.children.push(element);
2438
- if (!selfClosing) {
2439
- stack.push(element);
2440
- }
2441
- index = end + 1;
2442
- continue;
2443
- }
2444
- if (!selfClosing) {
2445
- stack.push(root);
2446
- }
2447
- index = end + 1;
2448
- }
2449
- if (stack.length > 0) {
2450
- fail(`Unclosed XML element: <${currentElement(stack)?.name}>`);
2451
- }
2452
- if (root === void 0) {
2453
- fail("XML input does not contain a root element");
2454
- }
2455
- return { children, root };
2456
- }
2457
- function indentation(depth) {
2458
- return INDENT.repeat(depth);
2459
- }
2460
- function renderInline(node) {
2461
- const content = node.children.map((child) => {
2462
- if (child.kind === "element") {
2463
- return renderInline(child);
2464
- }
2465
- return child.kind === "text" ? child.value : child.raw;
2466
- }).join("");
2467
- return `${node.open}${content}${node.close ?? ""}`;
2468
- }
2469
- function hasOnlyTextChildren(node) {
2470
- return node.children.every((child) => child.kind === "text");
2471
- }
2472
- function hasSignificantText(node) {
2473
- return node.children.some((child) => child.kind === "text" && child.value.trim() !== "");
2474
- }
2475
- function hasOnlyWhitespaceText(node) {
2476
- return node.children.every((child) => child.kind === "text" && child.value.trim() === "");
2477
- }
2478
- function toSelfClosingTag(open) {
2479
- return `${open.slice(0, -1).trimEnd()}/>`;
2480
- }
2481
- function formatElement(node, depth, isRoot) {
2482
- const prefix = indentation(depth);
2483
- if (node.selfClosing) {
2484
- return [`${prefix}${node.open}`];
2485
- }
2486
- if (node.close === void 0) {
2487
- fail(`Unclosed XML element: <${node.name}>`);
2488
- }
2489
- if (INTRINSICALLY_EMPTY_ELEMENTS.has(node.name) && hasOnlyWhitespaceText(node)) {
2490
- return [`${prefix}${toSelfClosingTag(node.open)}`];
2491
- }
2492
- if (isRoot && hasOnlyTextChildren(node)) {
2493
- const text = node.children.filter((child) => child.kind === "text").map((child) => child.value).join("");
2494
- const trimmedText = text.trim();
2495
- if (trimmedText === "") {
2496
- return [`${prefix}${node.open}`, `${prefix}${node.close}`];
2497
- }
2498
- if (text.includes("\n") || text.includes("\r") || text !== trimmedText) {
2499
- return [`${prefix}${renderInline(node)}`];
2500
- }
2501
- return [`${prefix}${node.open}`, `${indentation(depth + 1)}${trimmedText}`, `${prefix}${node.close}`];
2502
- }
2503
- if (hasSignificantText(node)) {
2504
- return [`${prefix}${renderInline(node)}`];
2505
- }
2506
- const lines = [`${prefix}${node.open}`];
2507
- for (const child of node.children) {
2508
- if (child.kind === "text") {
2509
- continue;
2510
- }
2511
- if (child.kind === "element") {
2512
- lines.push(...formatElement(child, depth + 1, false));
2513
- } else {
2514
- lines.push(`${indentation(depth + 1)}${child.raw}`);
2515
- }
2516
- }
2517
- lines.push(`${prefix}${node.close}`);
2518
- return lines;
2519
- }
2520
- function stripTrailingWhitespace(value) {
2521
- return value.replace(/[ \t]+$/gm, "");
2522
- }
2523
- function getTrailingLineBreaks(value) {
2524
- let start = value.length;
2525
- while (start > 0) {
2526
- if (value[start - 1] === "\n") {
2527
- start -= 1;
2528
- if (start > 0 && value[start - 1] === "\r") {
2529
- start -= 1;
2530
- }
2531
- continue;
2532
- }
2533
- if (value[start - 1] === "\r") {
2534
- start -= 1;
2535
- continue;
2536
- }
2537
- break;
2538
- }
2539
- return value.slice(start);
2540
- }
2541
- function stripTrailingLineBreaks(value) {
2542
- let end = value.length;
2543
- while (end > 0) {
2544
- if (value[end - 1] === "\n") {
2545
- end -= 1;
2546
- if (end > 0 && value[end - 1] === "\r") {
2547
- end -= 1;
2548
- }
2549
- continue;
2550
- }
2551
- if (value[end - 1] === "\r") {
2552
- end -= 1;
2553
- continue;
2554
- }
2555
- break;
2556
- }
2557
- return value.slice(0, end);
2558
- }
2559
- function preserveTrailingLineBreak(formatted, trailingLineBreaks) {
2560
- if (trailingLineBreaks === "") {
2561
- return formatted;
2562
- }
2563
- return `${stripTrailingLineBreaks(formatted)}${trailingLineBreaks}`;
2564
- }
2565
- function renderDocument(document2) {
2566
- const lines = [];
2567
- for (const child of document2.children) {
2568
- if (child.kind === "text") {
2569
- continue;
2570
- }
2571
- if (child.kind === "element") {
2572
- lines.push(...formatElement(child, 0, child === document2.root));
2573
- } else {
2574
- lines.push(child.raw);
2575
- }
2576
- }
2577
- return stripTrailingWhitespace(lines.join("\n")).trim();
2578
- }
2579
- function unwrapFormattedFragment(formatted) {
2580
- const opening = `<${FRAGMENT_ROOT}>`;
2581
- const closing = `</${FRAGMENT_ROOT}>`;
2582
- if (!formatted.startsWith(opening) || !formatted.endsWith(closing)) {
2583
- return void 0;
2584
- }
2585
- if (formatted === `${opening}${closing}`) {
2586
- return "";
2587
- }
2588
- const multilineOpening = `${opening}
2589
- `;
2590
- const multilineClosing = `
2591
- ${closing}`;
2592
- if (formatted.startsWith(multilineOpening) && formatted.endsWith(multilineClosing)) {
2593
- const content = formatted.slice(multilineOpening.length, -multilineClosing.length);
2594
- return content.split("\n").map((line) => line.startsWith(INDENT) ? line.slice(INDENT.length) : line).join("\n");
2595
- }
2596
- return formatted.slice(opening.length, -closing.length);
2597
- }
2598
- function formatXmlFragment(xml) {
2599
- const trailingLineBreaks = getTrailingLineBreaks(xml);
2600
- const source = xml.trim();
2601
- if (source === "") {
2602
- return "";
2603
- }
2604
- const wrapped = `<${FRAGMENT_ROOT}>${source}</${FRAGMENT_ROOT}>`;
2605
- let formatted;
2606
- try {
2607
- formatted = renderDocument(parseXml(wrapped));
2608
- } catch {
2609
- return xml;
2610
- }
2611
- return preserveTrailingLineBreak(unwrapFormattedFragment(formatted) ?? xml, trailingLineBreaks);
2612
- }
2613
-
2614
- // packages/ssml-editor-react/src/ssmlInsertion.ts
2615
- function isLineStart(value, offset) {
2616
- return offset === 0 || value[offset - 1] === "\n" || value[offset - 1] === "\r";
2617
- }
2618
- function isLineEnd(value, offset) {
2619
- return offset === value.length || value[offset] === "\n" || value[offset] === "\r";
2620
- }
2621
- function getLineBreakAt(value, offset) {
2622
- if (value.startsWith("\r\n", offset)) {
2623
- return "\r\n";
2624
- }
2625
- if (value[offset] === "\n" || value[offset] === "\r") {
2626
- return value[offset];
2627
- }
2628
- return "";
2629
- }
2630
- function startsWithLineBreak(value) {
2631
- return value.startsWith("\n") || value.startsWith("\r");
2632
- }
2633
- function endsWithLineBreak(value) {
2634
- return value.endsWith("\n") || value.endsWith("\r");
2635
- }
2636
- function createSsmlInsertionEdit(source, startOffset, endOffset, template, eol = "\n", selectedText = startOffset === endOffset ? "" : source.slice(startOffset, endOffset)) {
2637
- if (template.mode === SSML_INSERTION_MODES.wrap) {
2638
- const trailingLineBreak2 = getLineBreakAt(source, endOffset) === "" ? eol : "";
2639
- return {
2640
- replacement: `${template.prefix}${selectedText}${template.suffix}${trailingLineBreak2}`,
2641
- selectionOffset: template.prefix.length
2642
- };
2643
- }
2644
- const followingLineBreak = getLineBreakAt(source, endOffset);
2645
- const leadingLineBreak = !isLineStart(source, startOffset) && !startsWithLineBreak(template.prefix) ? eol : "";
2646
- const needsTrailingLineBreak = selectedText.length > 0 ? !selectedText.startsWith("\n") && !selectedText.startsWith("\r") : !isLineEnd(source, endOffset) || followingLineBreak === "";
2647
- const trailingLineBreak = needsTrailingLineBreak && !endsWithLineBreak(template.prefix) ? eol : "";
2648
- const insertionPrefix = `${leadingLineBreak}${template.prefix}`;
2649
- return {
2650
- replacement: `${insertionPrefix}${trailingLineBreak}${selectedText}`,
2651
- selectionOffset: insertionPrefix.length + trailingLineBreak.length + (selectedText.length === 0 && trailingLineBreak === "" ? followingLineBreak.length : 0)
2652
- };
2653
- }
2654
-
2655
- // packages/ssml-editor-react/src/ssmlContext.ts
2656
- function findTagEnd2(source, start, limit) {
2657
- let quote;
2658
- for (let index = start + 1; index < limit; index += 1) {
2659
- const character = source[index];
2660
- if (quote !== void 0) {
2661
- if (character === quote) {
2662
- quote = void 0;
2663
- }
2664
- continue;
2665
- }
2666
- if (character === '"' || character === "'") {
2667
- quote = character;
2668
- continue;
2669
- }
2670
- if (character === ">") {
2671
- return index;
2672
- }
2673
- }
2674
- return -1;
2675
- }
2676
- function getVoiceName(tag) {
2677
- return tag.match(/\bname\s*=\s*(["'])([\s\S]*?)\1/i)?.[2];
2678
- }
2679
- function closeElement(stack, name) {
2680
- for (let index = stack.length - 1; index >= 0; index -= 1) {
2681
- if (stack[index]?.name === name) {
2682
- stack.splice(index);
2683
- return;
2684
- }
2685
- }
2686
- }
2687
- function findSsmlVoiceContext(source, offset) {
2688
- const limit = Math.max(0, Math.min(offset, source.length));
2689
- const stack = [];
2690
- let index = 0;
2691
- while (index < limit) {
2692
- const tagStart = source.indexOf("<", index);
2693
- if (tagStart === -1 || tagStart >= limit) {
2694
- break;
2695
- }
2696
- if (source.startsWith("<!--", tagStart)) {
2697
- const end = source.indexOf("-->", tagStart + 4);
2698
- index = end === -1 || end + 3 > limit ? limit : end + 3;
2699
- continue;
2700
- }
2701
- if (source.startsWith("<![CDATA[", tagStart)) {
2702
- const end = source.indexOf("]]>", tagStart + 9);
2703
- index = end === -1 || end + 3 > limit ? limit : end + 3;
2704
- continue;
2705
- }
2706
- if (source.startsWith("<?", tagStart)) {
2707
- const end = source.indexOf("?>", tagStart + 2);
2708
- index = end === -1 || end + 2 > limit ? limit : end + 2;
2709
- continue;
2710
- }
2711
- const tagEnd = findTagEnd2(source, tagStart, limit);
2712
- if (tagEnd === -1) {
2713
- break;
2714
- }
2715
- const tag = source.slice(tagStart, tagEnd + 1);
2716
- if (tag.startsWith("<!")) {
2717
- index = tagEnd + 1;
2718
- continue;
2719
- }
2720
- const closingMatch = tag.match(/^<\s*\/\s*([A-Za-z_][A-Za-z0-9_.:-]*)/);
2721
- if (closingMatch?.[1]) {
2722
- closeElement(stack, closingMatch[1].toLowerCase());
2723
- index = tagEnd + 1;
2724
- continue;
2725
- }
2726
- const openingMatch = tag.match(/^<\s*([A-Za-z_][A-Za-z0-9_.:-]*)/);
2727
- if (openingMatch?.[1] && !/\/\s*>$/.test(tag)) {
2728
- const name = openingMatch[1].toLowerCase();
2729
- stack.push({
2730
- name,
2731
- ...name === "voice" ? { voiceName: getVoiceName(tag) } : {}
2732
- });
2733
- }
2734
- index = tagEnd + 1;
2735
- }
2736
- for (let stackIndex = stack.length - 1; stackIndex >= 0; stackIndex -= 1) {
2737
- const element = stack[stackIndex];
2738
- if (element?.name === "voice") {
2739
- return element.voiceName === void 0 ? {} : { voiceName: element.voiceName };
2740
- }
2741
- }
2742
- return void 0;
2743
- }
2744
-
2745
- // packages/ssml-editor-react/src/hooks/useSsmlEditorState.ts
2746
- var TIMING_INSERTION_TAGS = /* @__PURE__ */ new Set(["break", "mstts:silence"]);
2747
- var PROSODY_INSERTION_TAGS = /* @__PURE__ */ new Set(["prosody", "mstts:express-as", "voice", "emphasis"]);
2748
- var TEXT_INSERTION_TAGS = /* @__PURE__ */ new Set(["sub", "say-as", "phoneme", "w", "lang"]);
2749
- var EMPTY_SELECTION_OVERLAY = {
2750
- selectedText: "",
2751
- characterCount: 0,
2752
- hasSelection: false,
2753
- position: null,
2754
- placement: "above"
2755
- };
2756
- function isSsmlElement(node) {
2757
- return typeof node !== "string" && node.type !== "text";
2758
- }
2759
- function isVoice(element) {
2760
- return element.type === "voice";
2761
- }
2762
- function isProsody(element) {
2763
- return element.type === "prosody";
2764
- }
2765
- function getSsmlElementName(element) {
2766
- return element.type === "custom" || element.type === "element" ? element.name : element.type;
2767
- }
2768
- function findEditableTagEnd(source, start) {
2769
- let quote;
2770
- for (let index = start + 1; index < source.length; index += 1) {
2771
- const character = source[index];
2772
- if (quote !== void 0) {
2773
- if (character === quote) {
2774
- quote = void 0;
2775
- }
2776
- continue;
2777
- }
2778
- if (character === '"' || character === "'") {
2779
- quote = character;
2780
- continue;
2781
- }
2782
- if (character === ">") {
2783
- return index;
2784
- }
2785
- }
2786
- return source.length;
2787
- }
2788
- function collectEditableStartTags(source) {
2789
- const tags = [];
2790
- let index = 0;
2791
- while (index < source.length) {
2792
- if (source[index] !== "<") {
2793
- index += 1;
2794
- continue;
2795
- }
2796
- if (source.startsWith("<!--", index)) {
2797
- const end2 = source.indexOf("-->", index + 4);
2798
- index = end2 === -1 ? source.length : end2 + 3;
2799
- continue;
2800
- }
2801
- if (source.startsWith("<![CDATA[", index)) {
2802
- const end2 = source.indexOf("]]>", index + 9);
2803
- index = end2 === -1 ? source.length : end2 + 3;
2804
- continue;
2805
- }
2806
- if (source.startsWith("<?", index)) {
2807
- const end2 = source.indexOf("?>", index + 2);
2808
- index = end2 === -1 ? source.length : end2 + 2;
2809
- continue;
2810
- }
2811
- if (source.startsWith("</", index) || source.startsWith("<!", index)) {
2812
- const end2 = findEditableTagEnd(source, index);
2813
- index = end2 === source.length ? source.length : end2 + 1;
2814
- continue;
2815
- }
2816
- const end = findEditableTagEnd(source, index);
2817
- if (end === source.length) {
2818
- break;
2819
- }
2820
- const raw = source.slice(index, end + 1);
2821
- const match = raw.match(/^<([A-Za-z_][A-Za-z0-9_.:-]*)/);
2822
- if (match) {
2823
- tags.push({ name: match[1], selfClosing: /\/\s*>$/.test(raw) });
2824
- }
2825
- index = end + 1;
2826
- }
2827
- return tags;
2828
- }
2829
- function preserveEmptyPairElements(nodes, startTags, startTagIndex) {
2830
- return nodes.map((node) => {
2831
- if (!isSsmlElement(node)) {
2832
- return node;
2833
- }
2834
- const elementName = getSsmlElementName(node);
2835
- const startTag = startTags[startTagIndex.value];
2836
- startTagIndex.value += 1;
2837
- if (node.children === void 0 || node.children.length === 0) {
2838
- return startTag?.name === elementName && !startTag.selfClosing && !INTRINSICALLY_EMPTY_ELEMENTS.has(elementName) ? { ...node, children: [""] } : node;
2839
- }
2840
- const children = preserveEmptyPairElements(node.children, startTags, startTagIndex);
2841
- if (children.every((child, index) => child === node.children?.[index])) {
2842
- return node;
2843
- }
2844
- return { ...node, children };
2845
- });
2846
- }
2847
- function getDocumentChildren2(document2) {
2848
- return document2.children ?? (document2.content === void 0 ? [] : [document2.content]);
524
+ function getDocumentChildren(document2) {
525
+ return document2.children ?? (document2.content === void 0 ? [] : [document2.content]);
2849
526
  }
2850
527
  function getPartialContext(document2) {
2851
- const children = getDocumentChildren2(document2);
528
+ const children = getDocumentChildren(document2);
2852
529
  const voice = findFirstElement(children, isVoice);
2853
530
  const prosody = findFirstElement(children, isProsody);
2854
531
  const context = {
@@ -2873,127 +550,21 @@ function getPartialContext(document2) {
2873
550
  };
2874
551
  }
2875
552
  return context;
2876
- }
2877
- function findFirstElement(nodes, predicate) {
2878
- for (const node of nodes) {
2879
- if (!isSsmlElement(node)) {
2880
- continue;
2881
- }
2882
- if (predicate(node)) {
2883
- return node;
2884
- }
2885
- const child = findFirstElement(node.children ?? [], predicate);
2886
- if (child) {
2887
- return child;
2888
- }
2889
- }
2890
- return void 0;
2891
- }
2892
- function findFirstElementPath(nodes, predicate, ancestors = []) {
2893
- for (const node of nodes) {
2894
- if (!isSsmlElement(node)) {
2895
- continue;
2896
- }
2897
- const path = [...ancestors, node];
2898
- if (predicate(node)) {
2899
- return path;
2900
- }
2901
- const childPath = findFirstElementPath(node.children ?? [], predicate, path);
2902
- if (childPath) {
2903
- return childPath;
2904
- }
2905
- }
2906
- return void 0;
2907
- }
2908
- function updateFirstElement(nodes, predicate, update) {
2909
- let updated = false;
2910
- const nextNodes = nodes.map((node) => {
2911
- if (updated || !isSsmlElement(node)) {
2912
- return node;
2913
- }
2914
- if (predicate(node)) {
2915
- updated = true;
2916
- return update(node);
2917
- }
2918
- if (node.children) {
2919
- const result = updateFirstElement(node.children, predicate, update);
2920
- if (result.updated) {
2921
- updated = true;
2922
- return { ...node, children: result.nodes };
2923
- }
2924
- }
2925
- return node;
2926
- });
2927
- return { nodes: nextNodes, updated };
2928
- }
2929
- function withChildren(document2, children) {
2930
- const nextDocument = { ...document2, children };
2931
- if (nextDocument.content !== void 0) {
2932
- delete nextDocument.content;
2933
- }
2934
- return nextDocument;
2935
- }
2936
- function parseEditableText(value, lang) {
2937
- try {
2938
- const wrapper = buildSsml({
2939
- version: "1.0",
2940
- lang,
2941
- children: []
2942
- });
2943
- const openingTagEnd = wrapper.indexOf(">") + 1;
2944
- const children = parseSsml(`${wrapper.slice(0, openingTagEnd)}${value}</speak>`).children ?? [];
2945
- return children.some(isSsmlElement) ? preserveEmptyPairElements(children, collectEditableStartTags(value), { value: 0 }) : [value];
2946
- } catch {
2947
- return [value];
2948
- }
2949
- }
2950
- function serializeEditableText(nodes, lang) {
2951
- if (nodes.length === 1 && typeof nodes[0] === "string") {
2952
- return nodes[0];
2953
- }
2954
- const xml = buildSsml({
2955
- version: "1.0",
2956
- lang,
2957
- children: nodes
2958
- });
2959
- const contentStart = xml.indexOf(">") + 1;
2960
- return xml.slice(contentStart, -"</speak>".length);
2961
- }
2962
- function getEditableRegion(document2) {
2963
- const children = getDocumentChildren2(document2);
2964
- const path = findFirstElementPath(children, isProsody) ?? findFirstElementPath(children, isVoice);
2965
- const element = path ? path[path.length - 1] : void 0;
2966
- const voice = path ? [...path].reverse().find(isVoice) : void 0;
2967
- return {
2968
- children: element?.children ?? children,
2969
- ...voice ? { voiceName: voice.name } : {}
2970
- };
2971
- }
2972
- function getEditableChildren(document2) {
2973
- return getEditableRegion(document2).children;
2974
- }
2975
- function getEditableText(document2) {
2976
- return serializeEditableText(getEditableChildren(document2), document2.lang);
2977
- }
2978
- function updateText(document2, value) {
2979
- const nextChildren = parseEditableText(value, document2.lang);
2980
- const editableChildren = nextChildren.length > 0 ? nextChildren : [value];
2981
- const children = getDocumentChildren2(document2);
2982
- const prosodyResult = updateFirstElement(children, isProsody, (prosody) => ({
2983
- ...prosody,
2984
- children: editableChildren
2985
- }));
2986
- if (prosodyResult.updated) {
2987
- return withChildren(document2, prosodyResult.nodes);
2988
- }
2989
- const voiceResult = updateFirstElement(children, isVoice, (voice) => ({
2990
- ...voice,
2991
- children: editableChildren
2992
- }));
2993
- if (voiceResult.updated) {
2994
- return withChildren(document2, voiceResult.nodes);
553
+ }
554
+ function findFirstElement(nodes, predicate) {
555
+ for (const node of nodes) {
556
+ if (!isSsmlElement(node)) {
557
+ continue;
558
+ }
559
+ if (predicate(node)) {
560
+ return node;
561
+ }
562
+ const child = findFirstElement(node.children ?? [], predicate);
563
+ if (child) {
564
+ return child;
565
+ }
2995
566
  }
2996
- return withChildren(document2, editableChildren);
567
+ return void 0;
2997
568
  }
2998
569
  function getSelectionInfo(editor) {
2999
570
  const model = editor.getModel();
@@ -3130,6 +701,7 @@ function useSsmlEditorState({
3130
701
  const [decorationsVisible, setDecorationsVisible] = useState(showDecorations);
3131
702
  const [isHelpOpen, setIsHelpOpen] = useState(false);
3132
703
  const [syntaxError, setSyntaxError] = useState(null);
704
+ const [activeTags, setActiveTags] = useState(() => /* @__PURE__ */ new Set());
3133
705
  const [openPopoverId, setOpenPopoverId] = useState(null);
3134
706
  const [popoverVoiceName, setPopoverVoiceName] = useState(void 0);
3135
707
  const [popoverPosition, setPopoverPosition] = useState(null);
@@ -3232,6 +804,14 @@ function useSsmlEditorState({
3232
804
  });
3233
805
  }
3234
806
  }, []);
807
+ const updateActiveTags = useCallback((nextTags) => {
808
+ setActiveTags((currentTags) => {
809
+ if (currentTags.size === nextTags.size && [...currentTags].every((tag) => nextTags.has(tag))) {
810
+ return currentTags;
811
+ }
812
+ return new Set(nextTags);
813
+ });
814
+ }, []);
3235
815
  const commit = useCallback(
3236
816
  (nextDocument) => {
3237
817
  draftDocumentRef.current = nextDocument;
@@ -3243,7 +823,7 @@ function useSsmlEditorState({
3243
823
  );
3244
824
  const handleTextChange = useCallback(
3245
825
  (value) => {
3246
- commit(updateText(draftDocumentRef.current, value));
826
+ commit(updateEditableText(draftDocumentRef.current, value));
3247
827
  if (editorRef.current) {
3248
828
  refreshSelectionOverlay(editorRef.current, false);
3249
829
  }
@@ -3287,7 +867,7 @@ function useSsmlEditorState({
3287
867
  }, [commit]);
3288
868
  const handleFormat = useCallback(() => {
3289
869
  const value = editorRef.current?.getValue() ?? getEditableText(draftDocumentRef.current);
3290
- commit(updateText(draftDocumentRef.current, formatXmlFragment(value)));
870
+ commit(updateEditableText(draftDocumentRef.current, formatXmlFragment(value)));
3291
871
  }, [commit]);
3292
872
  const handleUndo = useCallback(() => {
3293
873
  editorRef.current?.trigger("toolbar", "undo", null);
@@ -3317,7 +897,7 @@ function useSsmlEditorState({
3317
897
  );
3318
898
  const getFullSsml = useCallback(
3319
899
  () => buildSsml(
3320
- editorRef.current ? updateText(draftDocumentRef.current, editorRef.current.getValue()) : draftDocumentRef.current
900
+ editorRef.current ? updateEditableText(draftDocumentRef.current, editorRef.current.getValue()) : draftDocumentRef.current
3321
901
  ),
3322
902
  []
3323
903
  );
@@ -3327,6 +907,7 @@ function useSsmlEditorState({
3327
907
  draftDocument,
3328
908
  text: getEditableText(draftDocument),
3329
909
  selectionOverlay,
910
+ activeTags,
3330
911
  isDarkTheme: resolvedTheme === "dark" || resolvedTheme === "system" && isDark,
3331
912
  decorationsVisible,
3332
913
  setDecorationsVisible,
@@ -3348,6 +929,7 @@ function useSsmlEditorState({
3348
929
  previewSelection,
3349
930
  canPreviewSelection: onPreviewSelection !== void 0,
3350
931
  refreshSelectionOverlay,
932
+ updateActiveTags,
3351
933
  getSelectedSsml: getSelectedSsmlValue,
3352
934
  getCurrentLineSsml: getCurrentLineSsmlValue,
3353
935
  getFullSsml,
@@ -3500,97 +1082,6 @@ function updateSsmlDiagnostics(monaco, model) {
3500
1082
  return syntaxError;
3501
1083
  }
3502
1084
 
3503
- // packages/ssml-editor-react/src/ssmlCompletion.ts
3504
- var SSML_ATTRIBUTE_VALUE_PATTERN = /<([\w:-]+)\s+[^>]*?\b([\w:-]+)=["']([^"']*)$/i;
3505
- var EXPRESS_AS_TAG_NAMES = /* @__PURE__ */ new Set(["mstts:express-as", "express-as", "expressas"]);
3506
- function findSsmlAttributePresets(tagName, attributeName) {
3507
- const tagPresets = Object.entries(SSML_ATTRIBUTE_PRESETS).find(
3508
- ([presetTagName]) => presetTagName.toLowerCase() === tagName.toLowerCase()
3509
- )?.[1];
3510
- return Object.entries(tagPresets ?? {}).find(
3511
- ([presetAttributeName]) => presetAttributeName.toLowerCase() === attributeName.toLowerCase()
3512
- )?.[1];
3513
- }
3514
- var SSML_COMPLETION_SNIPPETS = [
3515
- {
3516
- label: "break",
3517
- insertText: '<break time="500ms" />'
3518
- },
3519
- {
3520
- label: "prosody",
3521
- insertText: `<prosody rate="medium" pitch="medium">\${1:text}</prosody>`
3522
- },
3523
- {
3524
- label: "mstts:express-as",
3525
- insertText: `<mstts:express-as style="cheerful">\${1:text}</mstts:express-as>`
3526
- },
3527
- {
3528
- label: "sub",
3529
- insertText: `<sub alias="\${1:\u8AAD\u307F}">\${2:\u6F22\u5B57}</sub>`
3530
- }
3531
- ];
3532
- function registerSsmlCompletionProvider(monaco, options = {}) {
3533
- const provider = {
3534
- provideCompletionItems(model, position) {
3535
- if (options.model && options.model !== model) {
3536
- return { suggestions: [] };
3537
- }
3538
- const value = model.getValue();
3539
- const offset = model.getOffsetAt(position);
3540
- const textUntilPosition = value.slice(0, offset);
3541
- const isClosingTag = /<\/[a-zA-Z0-9:-]*$/.test(textUntilPosition);
3542
- if (isClosingTag) {
3543
- return { suggestions: [] };
3544
- }
3545
- const attributeMatch = SSML_ATTRIBUTE_VALUE_PATTERN.exec(textUntilPosition);
3546
- let attributeValues = attributeMatch ? findSsmlAttributePresets(attributeMatch[1], attributeMatch[2]) : void 0;
3547
- if (attributeMatch && attributeValues && EXPRESS_AS_TAG_NAMES.has(attributeMatch[1].toLowerCase()) && attributeMatch[2].toLowerCase() === "style") {
3548
- const voiceContext = findSsmlVoiceContext(value, offset);
3549
- const voiceName = voiceContext === void 0 ? options.getOuterVoiceName?.() : voiceContext.voiceName;
3550
- attributeValues = resolveExpressAsStyles(voiceName, attributeValues);
3551
- }
3552
- const openTagMatch = textUntilPosition.match(/<(?!\/)[a-zA-Z0-9:-]*$/);
3553
- const openTagLength = openTagMatch?.[0].length ?? 0;
3554
- const isAfterBracket = model.getValueInRange({
3555
- startLineNumber: position.lineNumber,
3556
- startColumn: position.column - 1,
3557
- endLineNumber: position.lineNumber,
3558
- endColumn: position.column
3559
- }) === "<";
3560
- const hasClosingBracket = isAfterBracket && model.getValueInRange({
3561
- startLineNumber: position.lineNumber,
3562
- startColumn: position.column,
3563
- endLineNumber: position.lineNumber,
3564
- endColumn: position.column + 1
3565
- }) === ">";
3566
- const range = {
3567
- startLineNumber: position.lineNumber,
3568
- startColumn: openTagLength > 0 ? position.column - openTagLength : position.column,
3569
- endLineNumber: position.lineNumber,
3570
- endColumn: hasClosingBracket ? position.column + 1 : position.column
3571
- };
3572
- return {
3573
- suggestions: [
3574
- ...attributeMatch ? [] : SSML_COMPLETION_SNIPPETS.map(({ label, insertText }) => ({
3575
- label,
3576
- kind: monaco.languages.CompletionItemKind.Snippet,
3577
- insertText,
3578
- insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
3579
- range
3580
- })),
3581
- ...attributeValues?.map((value2) => ({
3582
- label: value2,
3583
- kind: monaco.languages.CompletionItemKind.Value,
3584
- insertText: value2
3585
- })) ?? []
3586
- ]
3587
- };
3588
- },
3589
- triggerCharacters: ["<", '"', "'"]
3590
- };
3591
- return monaco.languages.registerCompletionItemProvider("xml", provider);
3592
- }
3593
-
3594
1085
  // packages/ssml-editor-react/src/ssmlCodeAction.ts
3595
1086
  var MISSING_TIME_UNIT_ACTION_TITLE = '\u5358\u4F4D "ms" \u3092\u4ED8\u4E0E\u3057\u3066\u4FEE\u5FA9';
3596
1087
  function getStructuredMarkerCode(marker) {
@@ -3699,601 +1190,6 @@ function registerSsmlCodeActions(monaco) {
3699
1190
  });
3700
1191
  }
3701
1192
 
3702
- // packages/ssml-editor-react/src/ssmlHover.ts
3703
- var {
3704
- BREAK_STRENGTH_PRESETS: BREAK_STRENGTH_PRESETS2,
3705
- EMPHASIS_LEVEL_PRESETS: EMPHASIS_LEVEL_PRESETS2,
3706
- PHONEME_ALPHABET_PRESETS: PHONEME_ALPHABET_PRESETS2,
3707
- PROSODY_RATE_VALUES: PROSODY_RATE_VALUES2,
3708
- PROSODY_VOLUME_PRESETS: PROSODY_VOLUME_PRESETS2,
3709
- SILENCE_TYPE_PRESETS: SILENCE_TYPE_PRESETS2,
3710
- SSML_PRESET_EXAMPLES: SSML_PRESET_EXAMPLES2,
3711
- VISEME_TYPE_PRESETS: VISEME_TYPE_PRESETS2
3712
- } = ssmlPresets_exports;
3713
- var SSML_TAG_DEFINITIONS = [
3714
- {
3715
- name: "voice",
3716
- description: "Selects the voice and optional voice effect used to synthesize the enclosed text.",
3717
- parameters: [
3718
- {
3719
- name: "name",
3720
- description: "The voice name, such as `en-US-JennyNeural`.",
3721
- example: "en-US-JennyNeural"
3722
- },
3723
- {
3724
- name: "effect",
3725
- description: "An optional voice effect, such as `eq_car`."
3726
- }
3727
- ]
3728
- },
3729
- {
3730
- name: "prosody",
3731
- description: "Changes the speaking rate, pitch, volume, or pitch contour of the enclosed text.",
3732
- parameters: [
3733
- {
3734
- name: "rate",
3735
- description: "Controls speaking speed.",
3736
- values: PROSODY_RATE_VALUES2,
3737
- example: SSML_PRESET_EXAMPLES2.prosodyRate
3738
- },
3739
- {
3740
- name: "pitch",
3741
- description: "Adjusts pitch using a named value, percentage, frequency, or semitone value.",
3742
- example: SSML_PRESET_EXAMPLES2.prosodyPitch
3743
- },
3744
- {
3745
- name: "volume",
3746
- description: "Controls loudness using a named value, percentage, or decibel value.",
3747
- values: PROSODY_VOLUME_PRESETS2,
3748
- example: SSML_PRESET_EXAMPLES2.prosodyVolume
3749
- },
3750
- {
3751
- name: "contour",
3752
- description: "Defines a sequence of relative pitch changes at positions in the text.",
3753
- example: "(0%,+0st) (100%,+2st)"
3754
- },
3755
- {
3756
- name: "range",
3757
- description: "Adjusts the pitch range of the voice.",
3758
- example: "+2st"
3759
- }
3760
- ]
3761
- },
3762
- {
3763
- name: "break",
3764
- description: "Inserts a pause between words or other spoken content.",
3765
- parameters: [
3766
- {
3767
- name: "time",
3768
- description: "The pause duration, for example `500ms` or `1s`.",
3769
- example: SSML_PRESET_EXAMPLES2.breakTime
3770
- },
3771
- {
3772
- name: "strength",
3773
- description: "The relative pause strength.",
3774
- values: BREAK_STRENGTH_PRESETS2
3775
- }
3776
- ]
3777
- },
3778
- {
3779
- name: "mstts:express-as",
3780
- aliases: ["express-as", "expressAs"],
3781
- description: "Applies a speaking style, style degree, or role to the enclosed text.",
3782
- parameters: [
3783
- {
3784
- name: "style",
3785
- description: "The speaking style supported by the selected voice, such as `cheerful`.",
3786
- example: SSML_PRESET_EXAMPLES2.expressAsStyle
3787
- },
3788
- {
3789
- name: "styledegree",
3790
- aliases: ["style-degree", "styleDegree"],
3791
- description: "Controls the intensity of the selected speaking style.",
3792
- example: "1.5"
3793
- },
3794
- {
3795
- name: "role",
3796
- description: "Changes the speaking role when supported by the selected voice.",
3797
- example: "YoungAdultFemale"
3798
- }
3799
- ]
3800
- },
3801
- {
3802
- name: "say-as",
3803
- aliases: ["sayAs"],
3804
- description: "Controls how the enclosed text is interpreted and spoken.",
3805
- parameters: [
3806
- {
3807
- name: "interpret-as",
3808
- description: "Specifies the interpretation, such as characters, digits, date, or time.",
3809
- example: "characters"
3810
- },
3811
- {
3812
- name: "format",
3813
- description: "Provides a format hint for the selected interpretation."
3814
- },
3815
- {
3816
- name: "detail",
3817
- description: "Provides an additional detail hint for the selected interpretation."
3818
- }
3819
- ]
3820
- },
3821
- {
3822
- name: "phoneme",
3823
- description: "Replaces normal pronunciation with the supplied phonetic pronunciation.",
3824
- parameters: [
3825
- {
3826
- name: "alphabet",
3827
- description: "The phonetic alphabet used by the `ph` value.",
3828
- values: PHONEME_ALPHABET_PRESETS2,
3829
- example: SSML_PRESET_EXAMPLES2.phonemeAlphabet
3830
- },
3831
- {
3832
- name: "ph",
3833
- description: "The phonetic pronunciation for the enclosed text.",
3834
- example: "h\u0259\u02C8lo\u028A"
3835
- }
3836
- ]
3837
- },
3838
- {
3839
- name: "emphasis",
3840
- description: "Adds emphasis to the enclosed text.",
3841
- parameters: [
3842
- {
3843
- name: "level",
3844
- description: "Controls the amount of emphasis.",
3845
- values: EMPHASIS_LEVEL_PRESETS2
3846
- }
3847
- ]
3848
- },
3849
- {
3850
- name: "audio",
3851
- description: "Plays an audio file as part of the synthesized output.",
3852
- parameters: [
3853
- {
3854
- name: "src",
3855
- description: "The URI of the audio file.",
3856
- example: "https://example.com/intro.wav"
3857
- },
3858
- {
3859
- name: "desc",
3860
- description: "Alternative text to use if the audio cannot be played."
3861
- },
3862
- {
3863
- name: "clipBegin",
3864
- description: "The starting offset within the audio file.",
3865
- example: "0s"
3866
- },
3867
- {
3868
- name: "clipEnd",
3869
- description: "The ending offset within the audio file.",
3870
- example: "5s"
3871
- },
3872
- {
3873
- name: "speed",
3874
- description: "The playback speed of the audio file.",
3875
- example: "1.0"
3876
- },
3877
- {
3878
- name: "repeatCount",
3879
- description: "The number of times to repeat the audio.",
3880
- example: "2"
3881
- },
3882
- {
3883
- name: "repeatDuration",
3884
- description: "The total duration for which the audio may repeat.",
3885
- example: "10s"
3886
- },
3887
- {
3888
- name: "soundLevel",
3889
- description: "The audio volume adjustment in decibels.",
3890
- example: "-3dB"
3891
- }
3892
- ]
3893
- },
3894
- {
3895
- name: "sub",
3896
- description: "Substitutes the alias text when speaking the enclosed text.",
3897
- parameters: [
3898
- {
3899
- name: "alias",
3900
- description: "The text to speak instead of the enclosed text.",
3901
- example: "World Wide Web"
3902
- }
3903
- ]
3904
- },
3905
- {
3906
- name: "lang",
3907
- description: "Changes the language used for the enclosed text.",
3908
- parameters: [
3909
- {
3910
- name: "xml:lang",
3911
- aliases: ["lang"],
3912
- description: "The BCP-47 language tag.",
3913
- example: "ja-JP"
3914
- }
3915
- ]
3916
- },
3917
- {
3918
- name: "mark",
3919
- description: "Inserts a custom marker into the synthesized audio stream.",
3920
- parameters: [
3921
- {
3922
- name: "name",
3923
- description: "The application-defined marker name.",
3924
- example: "chapter-1"
3925
- }
3926
- ]
3927
- },
3928
- {
3929
- name: "bookmark",
3930
- description: "Inserts a bookmark marker into the synthesized audio stream.",
3931
- parameters: [
3932
- {
3933
- name: "mark",
3934
- description: "The application-defined bookmark name.",
3935
- example: "chapter-1"
3936
- }
3937
- ]
3938
- },
3939
- {
3940
- name: "lexicon",
3941
- description: "Associates a pronunciation lexicon with the synthesized document.",
3942
- parameters: [
3943
- {
3944
- name: "uri",
3945
- description: "The URI of the pronunciation lexicon.",
3946
- example: "https://example.com/lexicon.pls"
3947
- }
3948
- ]
3949
- },
3950
- {
3951
- name: "p",
3952
- description: "Groups text into a paragraph.",
3953
- parameters: []
3954
- },
3955
- {
3956
- name: "s",
3957
- description: "Groups text into a sentence.",
3958
- parameters: []
3959
- },
3960
- {
3961
- name: "w",
3962
- description: "Groups text into a word.",
3963
- parameters: []
3964
- },
3965
- {
3966
- name: "mstts:silence",
3967
- aliases: ["silence"],
3968
- description: "Adds a specified silence before or after text or at a punctuation boundary.",
3969
- parameters: [
3970
- {
3971
- name: "type",
3972
- description: "The silence position or punctuation boundary.",
3973
- values: SILENCE_TYPE_PRESETS2
3974
- },
3975
- {
3976
- name: "value",
3977
- description: "The silence duration, for example `300ms`.",
3978
- example: SSML_PRESET_EXAMPLES2.silenceValue
3979
- }
3980
- ]
3981
- },
3982
- {
3983
- name: "mstts:viseme",
3984
- aliases: ["viseme"],
3985
- description: "Requests viseme events for the synthesized audio.",
3986
- parameters: [
3987
- {
3988
- name: "type",
3989
- description: "The viseme event format.",
3990
- values: VISEME_TYPE_PRESETS2
3991
- }
3992
- ]
3993
- }
3994
- ];
3995
- var definitionsByName = /* @__PURE__ */ new Map();
3996
- for (const definition of SSML_TAG_DEFINITIONS) {
3997
- definitionsByName.set(definition.name, definition);
3998
- for (const alias of definition.aliases ?? []) {
3999
- definitionsByName.set(alias, definition);
4000
- }
4001
- }
4002
- function isXmlNameStart3(value) {
4003
- return value !== void 0 && /[A-Za-z_]/.test(value);
4004
- }
4005
- function isXmlNameCharacter3(value) {
4006
- return value !== void 0 && /[A-Za-z0-9_.:-]/.test(value);
4007
- }
4008
- function isXmlWhitespace3(value) {
4009
- return value === " " || value === " " || value === "\r" || value === "\n";
4010
- }
4011
- function positionToOffset(source, lineNumber, column) {
4012
- if (!Number.isInteger(lineNumber) || !Number.isInteger(column) || lineNumber < 1 || column < 1) {
4013
- return void 0;
4014
- }
4015
- let lineStart = 0;
4016
- for (let line = 1; line < lineNumber; line += 1) {
4017
- const newline = source.indexOf("\n", lineStart);
4018
- if (newline === -1) {
4019
- return void 0;
4020
- }
4021
- lineStart = newline + 1;
4022
- }
4023
- const lineEnd = source.indexOf("\n", lineStart);
4024
- const lineLength = lineEnd === -1 ? source.length - lineStart : lineEnd - lineStart;
4025
- if (column > lineLength + 1) {
4026
- return void 0;
4027
- }
4028
- return lineStart + column - 1;
4029
- }
4030
- function offsetToPosition(source, offset) {
4031
- const lastNewline = source.lastIndexOf("\n", offset - 1);
4032
- const lineNumber = source.slice(0, offset).split("\n").length;
4033
- return {
4034
- lineNumber,
4035
- column: offset - lastNewline
4036
- };
4037
- }
4038
- function toRange(source, token) {
4039
- const start = offsetToPosition(source, token.start);
4040
- const end = offsetToPosition(source, token.end);
4041
- return {
4042
- startLineNumber: start.lineNumber,
4043
- startColumn: start.column,
4044
- endLineNumber: end.lineNumber,
4045
- endColumn: end.column
4046
- };
4047
- }
4048
- function containsOffset(token, offset) {
4049
- return offset >= token.start && offset < token.end;
4050
- }
4051
- function findTagEnd3(source, start) {
4052
- let quote;
4053
- for (let index = start; index < source.length; index += 1) {
4054
- const character = source[index];
4055
- if (quote !== void 0) {
4056
- if (character === quote) {
4057
- quote = void 0;
4058
- }
4059
- } else if (character === '"' || character === "'") {
4060
- quote = character;
4061
- } else if (character === ">") {
4062
- return index;
4063
- }
4064
- }
4065
- return void 0;
4066
- }
4067
- function parseTag(source, start, contentEnd, tokenEnd) {
4068
- let index = start + 1;
4069
- let closing = false;
4070
- if (source[index] === "/") {
4071
- closing = true;
4072
- index += 1;
4073
- }
4074
- while (index < contentEnd && isXmlWhitespace3(source[index])) {
4075
- index += 1;
4076
- }
4077
- if (!isXmlNameStart3(source[index])) {
4078
- return void 0;
4079
- }
4080
- const nameStart = index;
4081
- index += 1;
4082
- while (index < contentEnd && isXmlNameCharacter3(source[index])) {
4083
- index += 1;
4084
- }
4085
- const nameEnd = index;
4086
- const attributes = [];
4087
- if (!closing) {
4088
- while (index < contentEnd) {
4089
- while (index < contentEnd && isXmlWhitespace3(source[index])) {
4090
- index += 1;
4091
- }
4092
- if (index >= contentEnd || source[index] === "/") {
4093
- break;
4094
- }
4095
- if (!isXmlNameStart3(source[index])) {
4096
- index += 1;
4097
- continue;
4098
- }
4099
- const attributeStart = index;
4100
- index += 1;
4101
- while (index < contentEnd && isXmlNameCharacter3(source[index])) {
4102
- index += 1;
4103
- }
4104
- const attributeEnd = index;
4105
- while (index < contentEnd && isXmlWhitespace3(source[index])) {
4106
- index += 1;
4107
- }
4108
- let value;
4109
- if (source[index] === "=") {
4110
- index += 1;
4111
- while (index < contentEnd && isXmlWhitespace3(source[index])) {
4112
- index += 1;
4113
- }
4114
- const quote = source[index];
4115
- if (quote === '"' || quote === "'") {
4116
- index += 1;
4117
- const valueStart = index;
4118
- while (index < contentEnd && source[index] !== quote) {
4119
- index += 1;
4120
- }
4121
- value = { start: valueStart, end: index };
4122
- if (index < contentEnd) {
4123
- index += 1;
4124
- }
4125
- } else {
4126
- const valueStart = index;
4127
- while (index < contentEnd && !isXmlWhitespace3(source[index]) && source[index] !== "/") {
4128
- index += 1;
4129
- }
4130
- value = { start: valueStart, end: index };
4131
- }
4132
- }
4133
- attributes.push({
4134
- name: { start: attributeStart, end: attributeEnd },
4135
- value
4136
- });
4137
- }
4138
- }
4139
- return {
4140
- name: source.slice(nameStart, nameEnd),
4141
- nameRange: { start: nameStart, end: nameEnd },
4142
- start,
4143
- end: tokenEnd,
4144
- closing,
4145
- attributes
4146
- };
4147
- }
4148
- function findTagAtOffset(source, offset) {
4149
- let searchStart = 0;
4150
- while (searchStart < source.length) {
4151
- const start = source.indexOf("<", searchStart);
4152
- if (start === -1 || start > offset) {
4153
- return void 0;
4154
- }
4155
- if (source.startsWith("<!--", start)) {
4156
- const commentEnd = source.indexOf("-->", start + 4);
4157
- const tokenEnd2 = commentEnd === -1 ? source.length : commentEnd + 3;
4158
- if (offset < tokenEnd2) {
4159
- return void 0;
4160
- }
4161
- searchStart = tokenEnd2;
4162
- continue;
4163
- }
4164
- if (source.startsWith("<![CDATA[", start)) {
4165
- const cdataEnd = source.indexOf("]]>", start + 9);
4166
- const tokenEnd2 = cdataEnd === -1 ? source.length : cdataEnd + 3;
4167
- if (offset < tokenEnd2) {
4168
- return void 0;
4169
- }
4170
- searchStart = tokenEnd2;
4171
- continue;
4172
- }
4173
- if (source.startsWith("<?", start)) {
4174
- const processingEnd = source.indexOf("?>", start + 2);
4175
- const tokenEnd2 = processingEnd === -1 ? source.length : processingEnd + 2;
4176
- if (offset < tokenEnd2) {
4177
- return void 0;
4178
- }
4179
- searchStart = tokenEnd2;
4180
- continue;
4181
- }
4182
- const tagEnd = findTagEnd3(source, start + 1);
4183
- const contentEnd = tagEnd ?? source.length;
4184
- const tokenEnd = tagEnd === void 0 ? source.length : tagEnd + 1;
4185
- if (offset < tokenEnd) {
4186
- return parseTag(source, start, contentEnd, tokenEnd);
4187
- }
4188
- if (tagEnd === void 0) {
4189
- return void 0;
4190
- }
4191
- searchStart = tokenEnd;
4192
- }
4193
- return void 0;
4194
- }
4195
- function findParameter(definition, name) {
4196
- return definition.parameters.find(
4197
- (parameter) => parameter.name === name || parameter.aliases?.includes(name) === true
4198
- );
4199
- }
4200
- function getSsmlTagDefinition(name) {
4201
- return definitionsByName.get(name);
4202
- }
4203
- function findSsmlHoverTarget(source, lineNumber, column) {
4204
- const offset = positionToOffset(source, lineNumber, column);
4205
- if (offset === void 0) {
4206
- return void 0;
4207
- }
4208
- const tag = findTagAtOffset(source, offset);
4209
- if (!tag) {
4210
- return void 0;
4211
- }
4212
- const definition = getSsmlTagDefinition(tag.name);
4213
- if (!definition) {
4214
- return void 0;
4215
- }
4216
- if (containsOffset(tag.nameRange, offset)) {
4217
- return {
4218
- kind: "tag",
4219
- tagName: tag.name,
4220
- isClosingTag: tag.closing,
4221
- definition,
4222
- range: toRange(source, tag.nameRange)
4223
- };
4224
- }
4225
- for (const attribute of tag.attributes) {
4226
- if (containsOffset(attribute.name, offset)) {
4227
- const parameter = findParameter(definition, source.slice(attribute.name.start, attribute.name.end));
4228
- if (!parameter) {
4229
- return void 0;
4230
- }
4231
- return {
4232
- kind: "parameter",
4233
- tagName: tag.name,
4234
- isClosingTag: tag.closing,
4235
- definition,
4236
- parameter,
4237
- range: toRange(source, attribute.name)
4238
- };
4239
- }
4240
- if (attribute.value && containsOffset(attribute.value, offset)) {
4241
- const parameter = findParameter(definition, source.slice(attribute.name.start, attribute.name.end));
4242
- if (!parameter) {
4243
- return void 0;
4244
- }
4245
- return {
4246
- kind: "parameter-value",
4247
- tagName: tag.name,
4248
- isClosingTag: tag.closing,
4249
- definition,
4250
- parameter,
4251
- range: toRange(source, attribute.value)
4252
- };
4253
- }
4254
- }
4255
- return void 0;
4256
- }
4257
- function code(value) {
4258
- return `\`${value.replace(/\\/g, "\\\\").replace(/`/g, "\\`")}\``;
4259
- }
4260
- function formatParameter(parameter, tagName, locale) {
4261
- const localizedParameter = SSML_HOVER_COPY[locale].tags[tagName]?.parameters[parameter.name];
4262
- const description = localizedParameter?.description ?? parameter.description;
4263
- const values = parameter.values && parameter.values.length > 0 ? ` ${SSML_HOVER_COPY[locale].allowedValues}: ${parameter.values.map(code).join(", ")}.` : "";
4264
- const example = parameter.example ? ` ${SSML_HOVER_COPY[locale].example}: ${code(parameter.example)}.` : "";
4265
- return `- ${code(parameter.name)}: ${description}${values}${example}`;
4266
- }
4267
- function formatSsmlHover(target, locale = "en") {
4268
- const localizedTag = SSML_HOVER_COPY[locale].tags[target.definition.name];
4269
- const tagTitle = localizedTag?.title ?? target.definition.name;
4270
- const tagDescription = localizedTag?.description ?? target.definition.description;
4271
- const tagSyntax = target.isClosingTag ? `</${target.tagName}>` : `<${target.tagName}>`;
4272
- const lines = [`### ${code(tagSyntax)}`, "", `**${tagTitle}**`, "", tagDescription];
4273
- if (target.parameter) {
4274
- const localizedParameter = localizedTag?.parameters[target.parameter.name];
4275
- const parameterTitle = localizedParameter?.title ?? target.parameter.name;
4276
- const parameterDescription = localizedParameter?.description ?? target.parameter.description;
4277
- lines.push("", `**${SSML_HOVER_COPY[locale].parameterHeading} ${code(parameterTitle)}**`, "", parameterDescription);
4278
- if (target.parameter.values && target.parameter.values.length > 0) {
4279
- lines.push("", `${SSML_HOVER_COPY[locale].allowedValues}: ${target.parameter.values.map(code).join(", ")}.`);
4280
- }
4281
- if (target.parameter.example) {
4282
- lines.push("", `${SSML_HOVER_COPY[locale].example}: ${code(target.parameter.example)}.`);
4283
- }
4284
- } else if (target.definition.parameters.length > 0) {
4285
- lines.push(
4286
- "",
4287
- `**${SSML_HOVER_COPY[locale].parametersHeading}**`,
4288
- "",
4289
- ...target.definition.parameters.map((parameter) => formatParameter(parameter, target.definition.name, locale))
4290
- );
4291
- } else {
4292
- lines.push("", SSML_HOVER_COPY[locale].noParameters);
4293
- }
4294
- return lines.join("\n");
4295
- }
4296
-
4297
1193
  // packages/ssml-editor-react/src/hooks/useSsmlMonaco.ts
4298
1194
  var SSML_DIAGNOSTICS_DEBOUNCE_MS = 300;
4299
1195
  var hoverProviderRegistrations = /* @__PURE__ */ new WeakMap();
@@ -4397,6 +1293,7 @@ function useSsmlMonaco({
4397
1293
  decorationsVisible,
4398
1294
  getOuterVoiceName,
4399
1295
  onSelectionOverlayChange,
1296
+ onActiveTagsChange,
4400
1297
  onSyntaxErrorChange
4401
1298
  }) {
4402
1299
  const internalEditorRef = useRef2(null);
@@ -4406,6 +1303,7 @@ function useSsmlMonaco({
4406
1303
  const codeActionProviderRef = useRef2(null);
4407
1304
  const releaseHoverProviderRef = useRef2(null);
4408
1305
  const selectionChangeRef = useRef2(null);
1306
+ const cursorPositionChangeRef = useRef2(null);
4409
1307
  const diagnosticsChangeRef = useRef2(null);
4410
1308
  const diagnosticsTimeoutRef = useRef2(null);
4411
1309
  const selectionLayoutDisposablesRef = useRef2([]);
@@ -4414,11 +1312,13 @@ function useSsmlMonaco({
4414
1312
  const decorationsVisibleRef = useRef2(decorationsVisible);
4415
1313
  const getOuterVoiceNameRef = useRef2(getOuterVoiceName);
4416
1314
  const onSelectionOverlayChangeRef = useRef2(onSelectionOverlayChange);
1315
+ const onActiveTagsChangeRef = useRef2(onActiveTagsChange);
4417
1316
  const onSyntaxErrorChangeRef = useRef2(onSyntaxErrorChange);
4418
1317
  languageRef.current = language;
4419
1318
  decorationsVisibleRef.current = decorationsVisible;
4420
1319
  getOuterVoiceNameRef.current = getOuterVoiceName;
4421
1320
  onSelectionOverlayChangeRef.current = onSelectionOverlayChange;
1321
+ onActiveTagsChangeRef.current = onActiveTagsChange;
4422
1322
  onSyntaxErrorChangeRef.current = onSyntaxErrorChange;
4423
1323
  const runSsmlDiagnostics = useCallback2((editor, monaco) => {
4424
1324
  const model = editor.getModel();
@@ -4448,6 +1348,15 @@ function useSsmlMonaco({
4448
1348
  diagnosticsChangeRef.current?.dispose();
4449
1349
  diagnosticsChangeRef.current = null;
4450
1350
  }, []);
1351
+ const syncActiveTags = useCallback2((editor) => {
1352
+ const model = editor.getModel();
1353
+ const position = editor.getPosition();
1354
+ if (!model || !position) {
1355
+ onActiveTagsChangeRef.current(/* @__PURE__ */ new Set());
1356
+ return;
1357
+ }
1358
+ onActiveTagsChangeRef.current(findActiveSsmlTags(model.getValue(), model.getOffsetAt(position)));
1359
+ }, []);
4451
1360
  const disposeMonacoResources = useCallback2(() => {
4452
1361
  clearSsmlDiagnosticsResources();
4453
1362
  const model = editorRef.current?.getModel();
@@ -4457,6 +1366,8 @@ function useSsmlMonaco({
4457
1366
  }
4458
1367
  selectionChangeRef.current?.dispose();
4459
1368
  selectionChangeRef.current = null;
1369
+ cursorPositionChangeRef.current?.dispose();
1370
+ cursorPositionChangeRef.current = null;
4460
1371
  completionProviderRef.current?.dispose();
4461
1372
  completionProviderRef.current = null;
4462
1373
  codeActionProviderRef.current?.dispose();
@@ -4478,6 +1389,9 @@ function useSsmlMonaco({
4478
1389
  selectionChangeRef.current = editor.onDidChangeCursorSelection(() => {
4479
1390
  onSelectionOverlayChangeRef.current(editor, true);
4480
1391
  });
1392
+ cursorPositionChangeRef.current = editor.onDidChangeCursorPosition(() => {
1393
+ syncActiveTags(editor);
1394
+ });
4481
1395
  diagnosticsChangeRef.current = editor.onDidChangeModelContent(() => {
4482
1396
  const model2 = editor.getModel();
4483
1397
  if (model2) {
@@ -4489,6 +1403,7 @@ function useSsmlMonaco({
4489
1403
  inlineDecorationIdsRef.current
4490
1404
  );
4491
1405
  }
1406
+ syncActiveTags(editor);
4492
1407
  scheduleSsmlDiagnostics(editor, monaco);
4493
1408
  });
4494
1409
  selectionLayoutDisposablesRef.current = [
@@ -4514,8 +1429,9 @@ function useSsmlMonaco({
4514
1429
  );
4515
1430
  }
4516
1431
  onSelectionOverlayChangeRef.current(editor, true);
1432
+ syncActiveTags(editor);
4517
1433
  },
4518
- [disposeMonacoResources, editorRef, runSsmlDiagnostics, scheduleSsmlDiagnostics]
1434
+ [disposeMonacoResources, editorRef, runSsmlDiagnostics, scheduleSsmlDiagnostics, syncActiveTags]
4519
1435
  );
4520
1436
  useEffect2(() => {
4521
1437
  const monaco = monacoRef.current;
@@ -4903,6 +1819,8 @@ var STYLE_CSS = `
4903
1819
  --ssml-editor-border: #d1d5db;
4904
1820
  --ssml-editor-control-bg: #f9fafb;
4905
1821
  --ssml-editor-control-border: #9ca3af;
1822
+ --ssml-editor-active-bg: #dbeafe;
1823
+ --ssml-editor-active-border: #2563eb;
4906
1824
  --ssml-editor-preview-bg: #f3f4f6;
4907
1825
  --ssml-editor-error: #b91c1c;
4908
1826
  --ssml-editor-error-bg: #fef2f2;
@@ -4920,6 +1838,8 @@ var STYLE_CSS = `
4920
1838
  --ssml-editor-border: #374151;
4921
1839
  --ssml-editor-control-bg: #111827;
4922
1840
  --ssml-editor-control-border: #4b5563;
1841
+ --ssml-editor-active-bg: #1e3a8a;
1842
+ --ssml-editor-active-border: #60a5fa;
4923
1843
  --ssml-editor-preview-bg: #111827;
4924
1844
  --ssml-editor-error: #fca5a5;
4925
1845
  --ssml-editor-error-bg: #450a0a;
@@ -5139,6 +2059,7 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5139
2059
  editorRef,
5140
2060
  text,
5141
2061
  selectionOverlay,
2062
+ activeTags,
5142
2063
  isDarkTheme,
5143
2064
  decorationsVisible,
5144
2065
  setDecorationsVisible,
@@ -5159,6 +2080,7 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5159
2080
  previewSelection,
5160
2081
  canPreviewSelection,
5161
2082
  refreshSelectionOverlay,
2083
+ updateActiveTags,
5162
2084
  getSelectedSsml: getSelectedSsml2,
5163
2085
  getCurrentLineSsml: getCurrentLineSsml2,
5164
2086
  getFullSsml,
@@ -5187,6 +2109,7 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5187
2109
  decorationsVisible,
5188
2110
  getOuterVoiceName,
5189
2111
  onSelectionOverlayChange: refreshSelectionOverlay,
2112
+ onActiveTagsChange: updateActiveTags,
5190
2113
  onSyntaxErrorChange: setSyntaxError
5191
2114
  });
5192
2115
  useImperativeHandle(
@@ -5202,12 +2125,13 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5202
2125
  const isTimingPopover = insertion.tagName !== void 0 && TIMING_POPOVER_TAGS.has(insertion.tagName);
5203
2126
  const isProsodyPopover = insertion.tagName !== void 0 && PROSODY_POPOVER_TAGS.has(insertion.tagName);
5204
2127
  const isTextPopover = insertion.tagName !== void 0 && TEXT_POPOVER_TAGS.has(insertion.tagName);
2128
+ const insertionButtonStyle = insertion.tagName && activeTags.has(insertion.tagName.toLowerCase()) ? { ...toolbarButtonStyle, ...editorStyles.toolbarButtonActive } : toolbarButtonStyle;
5205
2129
  const props = {
5206
2130
  language,
5207
2131
  isDarkTheme,
5208
2132
  showToolbarIcons,
5209
2133
  showToolbarText,
5210
- toolbarButtonStyle,
2134
+ toolbarButtonStyle: insertionButtonStyle,
5211
2135
  emptyOptionsMessage: copy.noAvailableOptions,
5212
2136
  isReadOnly,
5213
2137
  openPopoverId,