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/README.md +77 -8
- package/dist/chunk-2XJER2BG.mjs +3486 -0
- package/dist/chunk-2XJER2BG.mjs.map +1 -0
- package/dist/elements.d.mts +72 -0
- package/dist/elements.d.ts +72 -0
- package/dist/elements.js +4577 -0
- package/dist/elements.js.map +1 -0
- package/dist/elements.mjs +1234 -0
- package/dist/elements.mjs.map +1 -0
- package/dist/react.d.mts +5 -0
- package/dist/react.d.ts +5 -0
- package/dist/react.js +558 -236
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +212 -3288
- package/dist/react.mjs.map +1 -1
- package/package.json +11 -2
package/dist/react.js
CHANGED
|
@@ -58,7 +58,7 @@ __export(ssmlPresets_exports, {
|
|
|
58
58
|
EMPHASIS_LEVEL_DESCRIPTIONS: () => EMPHASIS_LEVEL_DESCRIPTIONS,
|
|
59
59
|
EMPHASIS_LEVEL_PRESETS: () => EMPHASIS_LEVEL_PRESETS,
|
|
60
60
|
EXPRESS_AS_ROLE_PRESETS: () => EXPRESS_AS_ROLE_PRESETS,
|
|
61
|
-
|
|
61
|
+
EXPRESS_AS_STYLE_CATEGORIES: () => EXPRESS_AS_STYLE_CATEGORIES,
|
|
62
62
|
EXPRESS_AS_STYLE_DESCRIPTIONS: () => EXPRESS_AS_STYLE_DESCRIPTIONS,
|
|
63
63
|
EXPRESS_AS_STYLE_PRESETS: () => EXPRESS_AS_STYLE_PRESETS,
|
|
64
64
|
LANGUAGE_DESCRIPTIONS: () => LANGUAGE_DESCRIPTIONS,
|
|
@@ -80,6 +80,8 @@ __export(ssmlPresets_exports, {
|
|
|
80
80
|
SSML_PRESETS: () => SSML_PRESETS,
|
|
81
81
|
SSML_PRESET_EXAMPLES: () => SSML_PRESET_EXAMPLES,
|
|
82
82
|
VISEME_TYPE_PRESETS: () => VISEME_TYPE_PRESETS,
|
|
83
|
+
VOICE_STYLE_MAP: () => VOICE_STYLE_MAP,
|
|
84
|
+
getExpressAsStyleCategory: () => getExpressAsStyleCategory,
|
|
83
85
|
resolveExpressAsStyles: () => resolveExpressAsStyles
|
|
84
86
|
});
|
|
85
87
|
var SSML_PRESETS = [
|
|
@@ -124,21 +126,90 @@ var EXPRESS_AS_STYLE_PRESETS = [
|
|
|
124
126
|
"shouting",
|
|
125
127
|
"terrified",
|
|
126
128
|
"unfriendly",
|
|
127
|
-
"whispering"
|
|
129
|
+
"whispering",
|
|
130
|
+
"empathetic",
|
|
131
|
+
"relieved",
|
|
132
|
+
"fearful",
|
|
133
|
+
"depressed",
|
|
134
|
+
"disgruntled",
|
|
135
|
+
"embarrassed",
|
|
136
|
+
"narration-relaxed",
|
|
137
|
+
"poetry-reading",
|
|
138
|
+
"sports_commentary",
|
|
139
|
+
"sports_commentary_excited",
|
|
140
|
+
"story"
|
|
128
141
|
];
|
|
129
|
-
var
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
142
|
+
var EXPRESS_AS_STYLE_CATEGORIES = {
|
|
143
|
+
emotions: [
|
|
144
|
+
"cheerful",
|
|
145
|
+
"sad",
|
|
133
146
|
"angry",
|
|
147
|
+
"calm",
|
|
148
|
+
"fearful",
|
|
149
|
+
"depressed",
|
|
150
|
+
"disgruntled",
|
|
151
|
+
"embarrassed",
|
|
152
|
+
"empathetic",
|
|
153
|
+
"envious",
|
|
154
|
+
"excited",
|
|
155
|
+
"friendly",
|
|
156
|
+
"gentle",
|
|
157
|
+
"hopeful",
|
|
158
|
+
"relieved",
|
|
159
|
+
"serious",
|
|
160
|
+
"shouting",
|
|
161
|
+
"terrified",
|
|
162
|
+
"unfriendly",
|
|
163
|
+
"whispering"
|
|
164
|
+
],
|
|
165
|
+
scenarios: ["chat", "customerservice", "assistant", "livecommercial", "poetry-reading", "story"],
|
|
166
|
+
media: [
|
|
167
|
+
"newscast",
|
|
168
|
+
"newscast-casual",
|
|
169
|
+
"newscast-formal",
|
|
170
|
+
"narration-professional",
|
|
171
|
+
"narration-relaxed",
|
|
172
|
+
"documentary-narration",
|
|
173
|
+
"advertisement_upbeat",
|
|
174
|
+
"sports_commentary",
|
|
175
|
+
"sports_commentary_excited"
|
|
176
|
+
]
|
|
177
|
+
};
|
|
178
|
+
function getExpressAsStyleCategory(style) {
|
|
179
|
+
for (const [category, styles] of Object.entries(EXPRESS_AS_STYLE_CATEGORIES)) {
|
|
180
|
+
if (styles.includes(style)) {
|
|
181
|
+
return category;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return "other";
|
|
185
|
+
}
|
|
186
|
+
var VOICE_STYLE_MAP = {
|
|
187
|
+
"ja-JP-MayuNeural": ["calm", "cheerful", "sad"],
|
|
188
|
+
"ja-JP-KeitaNeural": ["chat"],
|
|
189
|
+
"ja-JP-NanamiNeural": ["chat", "customerservice", "cheerful", "whispering", "sad"],
|
|
190
|
+
"en-US-JennyMultilingualNeural": [
|
|
191
|
+
"cheerful",
|
|
192
|
+
"empathetic",
|
|
193
|
+
"excited",
|
|
194
|
+
"friendly",
|
|
195
|
+
"hopeful",
|
|
196
|
+
"sad",
|
|
197
|
+
"shouting",
|
|
198
|
+
"terrified",
|
|
199
|
+
"unfriendly",
|
|
200
|
+
"whispering"
|
|
201
|
+
],
|
|
202
|
+
"en-US-AndrewNeural": ["empathetic", "relieved"],
|
|
203
|
+
"en-US-JennyNeural": [
|
|
134
204
|
"assistant",
|
|
135
205
|
"chat",
|
|
136
|
-
"cheerful",
|
|
137
206
|
"customerservice",
|
|
207
|
+
"newscast",
|
|
208
|
+
"cheerful",
|
|
209
|
+
"empathetic",
|
|
138
210
|
"excited",
|
|
139
211
|
"friendly",
|
|
140
212
|
"hopeful",
|
|
141
|
-
"newscast",
|
|
142
213
|
"sad",
|
|
143
214
|
"shouting",
|
|
144
215
|
"terrified",
|
|
@@ -157,18 +228,58 @@ var EXPRESS_AS_STYLES_BY_VOICE = {
|
|
|
157
228
|
"terrified",
|
|
158
229
|
"unfriendly",
|
|
159
230
|
"whispering"
|
|
160
|
-
]
|
|
231
|
+
],
|
|
232
|
+
"ko-KR-SunHiNeural": ["cheerful", "sad"],
|
|
233
|
+
"zh-CN-XiaoxiaoNeural": [
|
|
234
|
+
"assistant",
|
|
235
|
+
"chat",
|
|
236
|
+
"customerservice",
|
|
237
|
+
"newscast",
|
|
238
|
+
"cheerful",
|
|
239
|
+
"empathetic",
|
|
240
|
+
"excited",
|
|
241
|
+
"friendly",
|
|
242
|
+
"hopeful",
|
|
243
|
+
"sad",
|
|
244
|
+
"terrified",
|
|
245
|
+
"whispering",
|
|
246
|
+
"poetry-reading",
|
|
247
|
+
"sports_commentary",
|
|
248
|
+
"sports_commentary_excited",
|
|
249
|
+
"story"
|
|
250
|
+
],
|
|
251
|
+
"zh-CN-YunxiNeural": [
|
|
252
|
+
"narration-relaxed",
|
|
253
|
+
"embarrassed",
|
|
254
|
+
"fearful",
|
|
255
|
+
"sad",
|
|
256
|
+
"disgruntled",
|
|
257
|
+
"serious",
|
|
258
|
+
"angry",
|
|
259
|
+
"depressed",
|
|
260
|
+
"chat",
|
|
261
|
+
"cheerful",
|
|
262
|
+
"assistant"
|
|
263
|
+
],
|
|
264
|
+
"fr-FR-DeniseNeural": ["cheerful", "sad"],
|
|
265
|
+
"fr-FR-HenriNeural": ["cheerful", "sad"],
|
|
266
|
+
"pt-BR-FranciscaNeural": ["calm"],
|
|
267
|
+
"it-IT-ElsaNeural": ["cheerful", "sad"],
|
|
268
|
+
"de-DE-KatjaNeural": ["cheerful", "sad"],
|
|
269
|
+
"de-DE-ConradNeural": ["cheerful", "sad"],
|
|
270
|
+
"ru-RU-SvetlanaNeural": ["cheerful", "sad", "angry", "disgruntled", "embarrassed", "fearful"]
|
|
161
271
|
};
|
|
162
|
-
var
|
|
163
|
-
Object.entries(
|
|
272
|
+
var VOICE_STYLE_MAP_BY_NORMALIZED_NAME = new Map(
|
|
273
|
+
Object.entries(VOICE_STYLE_MAP).map(([voiceName, styles]) => [voiceName.toLowerCase(), styles])
|
|
164
274
|
);
|
|
165
275
|
function resolveExpressAsStyles(voiceName, candidates = EXPRESS_AS_STYLE_PRESETS) {
|
|
166
|
-
|
|
276
|
+
const normalizedVoiceName = voiceName?.trim().toLowerCase();
|
|
277
|
+
if (!normalizedVoiceName) {
|
|
167
278
|
return candidates;
|
|
168
279
|
}
|
|
169
|
-
const supportedStyles =
|
|
280
|
+
const supportedStyles = VOICE_STYLE_MAP_BY_NORMALIZED_NAME.get(normalizedVoiceName);
|
|
170
281
|
if (supportedStyles === void 0) {
|
|
171
|
-
return
|
|
282
|
+
return [];
|
|
172
283
|
}
|
|
173
284
|
const supportedStyleSet = new Set(supportedStyles);
|
|
174
285
|
return candidates.filter((style) => supportedStyleSet.has(style));
|
|
@@ -418,6 +529,30 @@ var EXPRESS_AS_STYLE_DESCRIPTIONS = {
|
|
|
418
529
|
ja: "\u8208\u596E\u3057\u305F\u30B9\u30BF\u30A4\u30EB",
|
|
419
530
|
en: "Uses an excited style."
|
|
420
531
|
},
|
|
532
|
+
empathetic: {
|
|
533
|
+
ja: "\u5171\u611F\u3092\u793A\u3059\u30B9\u30BF\u30A4\u30EB",
|
|
534
|
+
en: "Uses an empathetic style."
|
|
535
|
+
},
|
|
536
|
+
relieved: {
|
|
537
|
+
ja: "\u5B89\u5FC3\u3057\u305F\u30B9\u30BF\u30A4\u30EB",
|
|
538
|
+
en: "Uses a relieved style."
|
|
539
|
+
},
|
|
540
|
+
fearful: {
|
|
541
|
+
ja: "\u6050\u308C\u3092\u611F\u3058\u3055\u305B\u308B\u30B9\u30BF\u30A4\u30EB",
|
|
542
|
+
en: "Uses a fearful style."
|
|
543
|
+
},
|
|
544
|
+
depressed: {
|
|
545
|
+
ja: "\u843D\u3061\u8FBC\u3093\u3060\u30B9\u30BF\u30A4\u30EB",
|
|
546
|
+
en: "Uses a depressed style."
|
|
547
|
+
},
|
|
548
|
+
disgruntled: {
|
|
549
|
+
ja: "\u4E0D\u6E80\u3092\u611F\u3058\u3055\u305B\u308B\u30B9\u30BF\u30A4\u30EB",
|
|
550
|
+
en: "Uses a disgruntled style."
|
|
551
|
+
},
|
|
552
|
+
embarrassed: {
|
|
553
|
+
ja: "\u6065\u305A\u304B\u3057\u305D\u3046\u306A\u30B9\u30BF\u30A4\u30EB",
|
|
554
|
+
en: "Uses an embarrassed style."
|
|
555
|
+
},
|
|
421
556
|
serious: {
|
|
422
557
|
ja: "\u771F\u5263\u306A\u30B9\u30BF\u30A4\u30EB",
|
|
423
558
|
en: "Uses a serious style."
|
|
@@ -457,6 +592,26 @@ var EXPRESS_AS_STYLE_DESCRIPTIONS = {
|
|
|
457
592
|
whispering: {
|
|
458
593
|
ja: "\u3055\u3055\u3084\u304F\u3088\u3046\u306A\u30B9\u30BF\u30A4\u30EB",
|
|
459
594
|
en: "Uses a whispering style."
|
|
595
|
+
},
|
|
596
|
+
"narration-relaxed": {
|
|
597
|
+
ja: "\u30EA\u30E9\u30C3\u30AF\u30B9\u3057\u305F\u30CA\u30EC\u30FC\u30B7\u30E7\u30F3\u5411\u3051\u306E\u30B9\u30BF\u30A4\u30EB",
|
|
598
|
+
en: "Uses a relaxed narration style."
|
|
599
|
+
},
|
|
600
|
+
"poetry-reading": {
|
|
601
|
+
ja: "\u8A69\u306E\u6717\u8AAD\u5411\u3051\u306E\u30B9\u30BF\u30A4\u30EB",
|
|
602
|
+
en: "Uses a poetry-reading style."
|
|
603
|
+
},
|
|
604
|
+
sports_commentary: {
|
|
605
|
+
ja: "\u30B9\u30DD\u30FC\u30C4\u5B9F\u6CC1\u5411\u3051\u306E\u30B9\u30BF\u30A4\u30EB",
|
|
606
|
+
en: "Uses a sports commentary style."
|
|
607
|
+
},
|
|
608
|
+
sports_commentary_excited: {
|
|
609
|
+
ja: "\u8208\u596E\u3057\u305F\u30B9\u30DD\u30FC\u30C4\u5B9F\u6CC1\u5411\u3051\u306E\u30B9\u30BF\u30A4\u30EB",
|
|
610
|
+
en: "Uses an excited sports commentary style."
|
|
611
|
+
},
|
|
612
|
+
story: {
|
|
613
|
+
ja: "\u7269\u8A9E\u306E\u6717\u8AAD\u5411\u3051\u306E\u30B9\u30BF\u30A4\u30EB",
|
|
614
|
+
en: "Uses a storytelling style."
|
|
460
615
|
}
|
|
461
616
|
};
|
|
462
617
|
var SAY_AS_DESCRIPTIONS = {
|
|
@@ -568,7 +723,12 @@ var EDITOR_COPY = {
|
|
|
568
723
|
selectionCountSuffix: "\u6587\u5B57",
|
|
569
724
|
previewSelection: "\u9078\u629E\u90E8\u5206\u3092\u8A66\u8074",
|
|
570
725
|
previewSelectionTitle: "\u9078\u629E\u90E8\u5206\u306ESSML\u3092\u8A66\u8074",
|
|
571
|
-
noAvailableOptions: "\u5229\u7528\u53EF\u80FD\u306A\u9078\u629E\u80A2\u304C\u3042\u308A\u307E\u305B\u3093\u3002"
|
|
726
|
+
noAvailableOptions: "\u5229\u7528\u53EF\u80FD\u306A\u9078\u629E\u80A2\u304C\u3042\u308A\u307E\u305B\u3093\u3002",
|
|
727
|
+
styleNotSupported: "\u3053\u306E\u97F3\u58F0\u306F\u30B9\u30BF\u30A4\u30EB\u6307\u5B9A\u306B\u5BFE\u5FDC\u3057\u3066\u3044\u307E\u305B\u3093",
|
|
728
|
+
categoryEmotions: "\u611F\u60C5\u30FB\u30C8\u30FC\u30F3",
|
|
729
|
+
categoryScenarios: "\u4F1A\u8A71\u30FB\u30B7\u30CA\u30EA\u30AA",
|
|
730
|
+
categoryMedia: "\u30E1\u30C7\u30A3\u30A2\u30FB\u30CA\u30EC\u30FC\u30B7\u30E7\u30F3",
|
|
731
|
+
categoryOther: "\u305D\u306E\u4ED6"
|
|
572
732
|
},
|
|
573
733
|
en: {
|
|
574
734
|
editorAriaLabel: "SSML editor",
|
|
@@ -594,7 +754,12 @@ var EDITOR_COPY = {
|
|
|
594
754
|
selectionCountSuffix: " characters",
|
|
595
755
|
previewSelection: "Preview selection",
|
|
596
756
|
previewSelectionTitle: "Preview the selected SSML",
|
|
597
|
-
noAvailableOptions: "No options are available."
|
|
757
|
+
noAvailableOptions: "No options are available.",
|
|
758
|
+
styleNotSupported: "This voice does not support style selection.",
|
|
759
|
+
categoryEmotions: "Emotions / Tone",
|
|
760
|
+
categoryScenarios: "Conversations / Scenarios",
|
|
761
|
+
categoryMedia: "Media / Broadcast",
|
|
762
|
+
categoryOther: "Other"
|
|
598
763
|
}
|
|
599
764
|
};
|
|
600
765
|
var INLINE_BADGE_COPY = {
|
|
@@ -991,6 +1156,10 @@ var editorStyles = {
|
|
|
991
1156
|
font: "inherit",
|
|
992
1157
|
cursor: "pointer"
|
|
993
1158
|
},
|
|
1159
|
+
toolbarButtonActive: {
|
|
1160
|
+
border: "1px solid var(--ssml-editor-active-border)",
|
|
1161
|
+
backgroundColor: "var(--ssml-editor-active-bg)"
|
|
1162
|
+
},
|
|
994
1163
|
display: {
|
|
995
1164
|
display: "grid",
|
|
996
1165
|
gap: "0.5rem"
|
|
@@ -1036,12 +1205,35 @@ var editorStyles = {
|
|
|
1036
1205
|
whiteSpace: "nowrap",
|
|
1037
1206
|
cursor: "pointer"
|
|
1038
1207
|
},
|
|
1208
|
+
toolbarOptionGroup: {
|
|
1209
|
+
display: "grid",
|
|
1210
|
+
gap: "0.125rem",
|
|
1211
|
+
margin: 0,
|
|
1212
|
+
padding: 0,
|
|
1213
|
+
border: 0
|
|
1214
|
+
},
|
|
1215
|
+
toolbarOptionGroupLabel: {
|
|
1216
|
+
padding: "0.375rem 0.5rem 0.125rem",
|
|
1217
|
+
color: "var(--ssml-editor-color)",
|
|
1218
|
+
fontSize: "0.875rem",
|
|
1219
|
+
fontWeight: 600,
|
|
1220
|
+
whiteSpace: "nowrap"
|
|
1221
|
+
},
|
|
1039
1222
|
toolbarEmpty: {
|
|
1040
1223
|
margin: 0,
|
|
1041
1224
|
padding: "0.375rem 0.5rem",
|
|
1042
1225
|
color: "var(--ssml-editor-color)",
|
|
1043
1226
|
whiteSpace: "nowrap"
|
|
1044
1227
|
},
|
|
1228
|
+
toolbarEmptySelect: {
|
|
1229
|
+
padding: "0.375rem 0.5rem",
|
|
1230
|
+
border: "1px solid var(--ssml-editor-control-border)",
|
|
1231
|
+
borderRadius: "0.125rem",
|
|
1232
|
+
color: "var(--ssml-editor-color)",
|
|
1233
|
+
backgroundColor: "var(--ssml-editor-control-bg)",
|
|
1234
|
+
font: "inherit",
|
|
1235
|
+
textAlign: "left"
|
|
1236
|
+
},
|
|
1045
1237
|
helpPanel: {
|
|
1046
1238
|
display: "grid",
|
|
1047
1239
|
gap: "0.5rem",
|
|
@@ -1193,8 +1385,29 @@ function InsertionPopover({
|
|
|
1193
1385
|
menuRef,
|
|
1194
1386
|
onToggle,
|
|
1195
1387
|
onClose,
|
|
1196
|
-
onApply
|
|
1388
|
+
onApply,
|
|
1389
|
+
optionGroups
|
|
1197
1390
|
}) {
|
|
1391
|
+
const hasOptions = optionGroups ? optionGroups.some((group) => group.options.length > 0) : insertion.options.length > 0;
|
|
1392
|
+
const renderOption = (option) => /* @__PURE__ */ React.createElement(
|
|
1393
|
+
"button",
|
|
1394
|
+
{
|
|
1395
|
+
key: option.value,
|
|
1396
|
+
type: "button",
|
|
1397
|
+
role: "menuitem",
|
|
1398
|
+
style: editorStyles.toolbarOption,
|
|
1399
|
+
title: option.descriptions?.[language] ?? insertion.descriptions[language],
|
|
1400
|
+
disabled: isReadOnly,
|
|
1401
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
1402
|
+
onClick: () => {
|
|
1403
|
+
if (!isReadOnly) {
|
|
1404
|
+
onApply(insertion, option);
|
|
1405
|
+
}
|
|
1406
|
+
onClose();
|
|
1407
|
+
}
|
|
1408
|
+
},
|
|
1409
|
+
option.labels[language]
|
|
1410
|
+
);
|
|
1198
1411
|
const menu = isOpen && menuPosition && typeof document !== "undefined" ? /* @__PURE__ */ React.createElement(
|
|
1199
1412
|
"div",
|
|
1200
1413
|
{
|
|
@@ -1206,25 +1419,7 @@ function InsertionPopover({
|
|
|
1206
1419
|
role: "menu",
|
|
1207
1420
|
"aria-label": insertion.labels[language]
|
|
1208
1421
|
},
|
|
1209
|
-
|
|
1210
|
-
"button",
|
|
1211
|
-
{
|
|
1212
|
-
key: option.value,
|
|
1213
|
-
type: "button",
|
|
1214
|
-
role: "menuitem",
|
|
1215
|
-
style: editorStyles.toolbarOption,
|
|
1216
|
-
title: option.descriptions?.[language] ?? insertion.descriptions[language],
|
|
1217
|
-
disabled: isReadOnly,
|
|
1218
|
-
onMouseDown: (event) => event.preventDefault(),
|
|
1219
|
-
onClick: () => {
|
|
1220
|
-
if (!isReadOnly) {
|
|
1221
|
-
onApply(insertion, option);
|
|
1222
|
-
}
|
|
1223
|
-
onClose();
|
|
1224
|
-
}
|
|
1225
|
-
},
|
|
1226
|
-
option.labels[language]
|
|
1227
|
-
))
|
|
1422
|
+
!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)
|
|
1228
1423
|
) : null;
|
|
1229
1424
|
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { style: editorStyles.toolbarDropdown }, /* @__PURE__ */ React.createElement(
|
|
1230
1425
|
"button",
|
|
@@ -1249,6 +1444,8 @@ function InsertionPopovers({
|
|
|
1249
1444
|
insertions,
|
|
1250
1445
|
openPopoverId,
|
|
1251
1446
|
onToggle,
|
|
1447
|
+
emptyOptionsMessages,
|
|
1448
|
+
optionGroups,
|
|
1252
1449
|
...props
|
|
1253
1450
|
}) {
|
|
1254
1451
|
return /* @__PURE__ */ React.createElement(React.Fragment, null, insertions.map((insertion) => /* @__PURE__ */ React.createElement(
|
|
@@ -1257,6 +1454,8 @@ function InsertionPopovers({
|
|
|
1257
1454
|
key: insertion.id,
|
|
1258
1455
|
...props,
|
|
1259
1456
|
insertion,
|
|
1457
|
+
emptyOptionsMessage: emptyOptionsMessages?.[insertion.id] ?? props.emptyOptionsMessage,
|
|
1458
|
+
optionGroups: optionGroups?.[insertion.id],
|
|
1260
1459
|
isOpen: openPopoverId === insertion.id,
|
|
1261
1460
|
onToggle: (trigger) => onToggle(insertion.id, trigger)
|
|
1262
1461
|
}
|
|
@@ -1264,23 +1463,57 @@ function InsertionPopovers({
|
|
|
1264
1463
|
}
|
|
1265
1464
|
|
|
1266
1465
|
// packages/ssml-editor-react/src/components/popovers/ProsodyPopovers.tsx
|
|
1267
|
-
|
|
1466
|
+
var CATEGORY_LABEL_KEYS = {
|
|
1467
|
+
emotions: "categoryEmotions",
|
|
1468
|
+
scenarios: "categoryScenarios",
|
|
1469
|
+
media: "categoryMedia",
|
|
1470
|
+
other: "categoryOther"
|
|
1471
|
+
};
|
|
1472
|
+
var CATEGORY_ORDER = ["emotions", "scenarios", "media", "other"];
|
|
1473
|
+
function isExpressAsInsertion(insertion) {
|
|
1474
|
+
return insertion.id === "emotion" && insertion.tagName?.toLowerCase() === "mstts:express-as";
|
|
1475
|
+
}
|
|
1476
|
+
function createOptionGroups(options, language) {
|
|
1477
|
+
const t = (key) => EDITOR_COPY[language][key];
|
|
1478
|
+
return CATEGORY_ORDER.map((category) => ({
|
|
1479
|
+
label: t(CATEGORY_LABEL_KEYS[category]),
|
|
1480
|
+
options: options.filter((option) => getExpressAsStyleCategory(option.value) === category)
|
|
1481
|
+
})).filter((group) => group.options.length > 0);
|
|
1482
|
+
}
|
|
1483
|
+
function ProsodyPopovers({ insertions, voiceName, language, ...props }) {
|
|
1484
|
+
const emptyOptionsMessages = {};
|
|
1268
1485
|
const filteredInsertions = insertions.map((insertion) => {
|
|
1269
|
-
if (insertion
|
|
1486
|
+
if (!isExpressAsInsertion(insertion)) {
|
|
1270
1487
|
return insertion;
|
|
1271
1488
|
}
|
|
1272
|
-
const
|
|
1489
|
+
const availableStyles = new Set(
|
|
1273
1490
|
resolveExpressAsStyles(
|
|
1274
1491
|
voiceName,
|
|
1275
1492
|
insertion.options.map((option) => option.value)
|
|
1276
1493
|
)
|
|
1277
1494
|
);
|
|
1495
|
+
if (availableStyles.size === 0) {
|
|
1496
|
+
emptyOptionsMessages[insertion.id] = EDITOR_COPY[language].styleNotSupported;
|
|
1497
|
+
}
|
|
1498
|
+
const availableOptions = insertion.options.filter((option) => availableStyles.has(option.value));
|
|
1278
1499
|
return {
|
|
1279
1500
|
...insertion,
|
|
1280
|
-
options:
|
|
1501
|
+
options: availableOptions
|
|
1281
1502
|
};
|
|
1282
1503
|
});
|
|
1283
|
-
|
|
1504
|
+
const optionGroups = Object.fromEntries(
|
|
1505
|
+
filteredInsertions.filter(isExpressAsInsertion).map((insertion) => [insertion.id, createOptionGroups(insertion.options, language)])
|
|
1506
|
+
);
|
|
1507
|
+
return /* @__PURE__ */ React.createElement(
|
|
1508
|
+
InsertionPopovers,
|
|
1509
|
+
{
|
|
1510
|
+
...props,
|
|
1511
|
+
emptyOptionsMessages,
|
|
1512
|
+
language,
|
|
1513
|
+
insertions: filteredInsertions,
|
|
1514
|
+
optionGroups
|
|
1515
|
+
}
|
|
1516
|
+
);
|
|
1284
1517
|
}
|
|
1285
1518
|
|
|
1286
1519
|
// packages/ssml-editor-react/src/components/popovers/TextPopovers.tsx
|
|
@@ -2648,148 +2881,7 @@ function formatXmlFragment(xml) {
|
|
|
2648
2881
|
return preserveTrailingLineBreak(unwrapFormattedFragment(formatted) ?? xml, trailingLineBreaks);
|
|
2649
2882
|
}
|
|
2650
2883
|
|
|
2651
|
-
// packages/ssml-editor-react/src/
|
|
2652
|
-
function isLineStart(value, offset) {
|
|
2653
|
-
return offset === 0 || value[offset - 1] === "\n" || value[offset - 1] === "\r";
|
|
2654
|
-
}
|
|
2655
|
-
function isLineEnd(value, offset) {
|
|
2656
|
-
return offset === value.length || value[offset] === "\n" || value[offset] === "\r";
|
|
2657
|
-
}
|
|
2658
|
-
function getLineBreakAt(value, offset) {
|
|
2659
|
-
if (value.startsWith("\r\n", offset)) {
|
|
2660
|
-
return "\r\n";
|
|
2661
|
-
}
|
|
2662
|
-
if (value[offset] === "\n" || value[offset] === "\r") {
|
|
2663
|
-
return value[offset];
|
|
2664
|
-
}
|
|
2665
|
-
return "";
|
|
2666
|
-
}
|
|
2667
|
-
function startsWithLineBreak(value) {
|
|
2668
|
-
return value.startsWith("\n") || value.startsWith("\r");
|
|
2669
|
-
}
|
|
2670
|
-
function endsWithLineBreak(value) {
|
|
2671
|
-
return value.endsWith("\n") || value.endsWith("\r");
|
|
2672
|
-
}
|
|
2673
|
-
function createSsmlInsertionEdit(source, startOffset, endOffset, template, eol = "\n", selectedText = startOffset === endOffset ? "" : source.slice(startOffset, endOffset)) {
|
|
2674
|
-
if (template.mode === SSML_INSERTION_MODES.wrap) {
|
|
2675
|
-
const trailingLineBreak2 = getLineBreakAt(source, endOffset) === "" ? eol : "";
|
|
2676
|
-
return {
|
|
2677
|
-
replacement: `${template.prefix}${selectedText}${template.suffix}${trailingLineBreak2}`,
|
|
2678
|
-
selectionOffset: template.prefix.length
|
|
2679
|
-
};
|
|
2680
|
-
}
|
|
2681
|
-
const followingLineBreak = getLineBreakAt(source, endOffset);
|
|
2682
|
-
const leadingLineBreak = !isLineStart(source, startOffset) && !startsWithLineBreak(template.prefix) ? eol : "";
|
|
2683
|
-
const needsTrailingLineBreak = selectedText.length > 0 ? !selectedText.startsWith("\n") && !selectedText.startsWith("\r") : !isLineEnd(source, endOffset) || followingLineBreak === "";
|
|
2684
|
-
const trailingLineBreak = needsTrailingLineBreak && !endsWithLineBreak(template.prefix) ? eol : "";
|
|
2685
|
-
const insertionPrefix = `${leadingLineBreak}${template.prefix}`;
|
|
2686
|
-
return {
|
|
2687
|
-
replacement: `${insertionPrefix}${trailingLineBreak}${selectedText}`,
|
|
2688
|
-
selectionOffset: insertionPrefix.length + trailingLineBreak.length + (selectedText.length === 0 && trailingLineBreak === "" ? followingLineBreak.length : 0)
|
|
2689
|
-
};
|
|
2690
|
-
}
|
|
2691
|
-
|
|
2692
|
-
// packages/ssml-editor-react/src/ssmlContext.ts
|
|
2693
|
-
function findTagEnd2(source, start, limit) {
|
|
2694
|
-
let quote;
|
|
2695
|
-
for (let index = start + 1; index < limit; index += 1) {
|
|
2696
|
-
const character = source[index];
|
|
2697
|
-
if (quote !== void 0) {
|
|
2698
|
-
if (character === quote) {
|
|
2699
|
-
quote = void 0;
|
|
2700
|
-
}
|
|
2701
|
-
continue;
|
|
2702
|
-
}
|
|
2703
|
-
if (character === '"' || character === "'") {
|
|
2704
|
-
quote = character;
|
|
2705
|
-
continue;
|
|
2706
|
-
}
|
|
2707
|
-
if (character === ">") {
|
|
2708
|
-
return index;
|
|
2709
|
-
}
|
|
2710
|
-
}
|
|
2711
|
-
return -1;
|
|
2712
|
-
}
|
|
2713
|
-
function getVoiceName(tag) {
|
|
2714
|
-
return tag.match(/\bname\s*=\s*(["'])([\s\S]*?)\1/i)?.[2];
|
|
2715
|
-
}
|
|
2716
|
-
function closeElement(stack, name) {
|
|
2717
|
-
for (let index = stack.length - 1; index >= 0; index -= 1) {
|
|
2718
|
-
if (stack[index]?.name === name) {
|
|
2719
|
-
stack.splice(index);
|
|
2720
|
-
return;
|
|
2721
|
-
}
|
|
2722
|
-
}
|
|
2723
|
-
}
|
|
2724
|
-
function findSsmlVoiceContext(source, offset) {
|
|
2725
|
-
const limit = Math.max(0, Math.min(offset, source.length));
|
|
2726
|
-
const stack = [];
|
|
2727
|
-
let index = 0;
|
|
2728
|
-
while (index < limit) {
|
|
2729
|
-
const tagStart = source.indexOf("<", index);
|
|
2730
|
-
if (tagStart === -1 || tagStart >= limit) {
|
|
2731
|
-
break;
|
|
2732
|
-
}
|
|
2733
|
-
if (source.startsWith("<!--", tagStart)) {
|
|
2734
|
-
const end = source.indexOf("-->", tagStart + 4);
|
|
2735
|
-
index = end === -1 || end + 3 > limit ? limit : end + 3;
|
|
2736
|
-
continue;
|
|
2737
|
-
}
|
|
2738
|
-
if (source.startsWith("<![CDATA[", tagStart)) {
|
|
2739
|
-
const end = source.indexOf("]]>", tagStart + 9);
|
|
2740
|
-
index = end === -1 || end + 3 > limit ? limit : end + 3;
|
|
2741
|
-
continue;
|
|
2742
|
-
}
|
|
2743
|
-
if (source.startsWith("<?", tagStart)) {
|
|
2744
|
-
const end = source.indexOf("?>", tagStart + 2);
|
|
2745
|
-
index = end === -1 || end + 2 > limit ? limit : end + 2;
|
|
2746
|
-
continue;
|
|
2747
|
-
}
|
|
2748
|
-
const tagEnd = findTagEnd2(source, tagStart, limit);
|
|
2749
|
-
if (tagEnd === -1) {
|
|
2750
|
-
break;
|
|
2751
|
-
}
|
|
2752
|
-
const tag = source.slice(tagStart, tagEnd + 1);
|
|
2753
|
-
if (tag.startsWith("<!")) {
|
|
2754
|
-
index = tagEnd + 1;
|
|
2755
|
-
continue;
|
|
2756
|
-
}
|
|
2757
|
-
const closingMatch = tag.match(/^<\s*\/\s*([A-Za-z_][A-Za-z0-9_.:-]*)/);
|
|
2758
|
-
if (closingMatch?.[1]) {
|
|
2759
|
-
closeElement(stack, closingMatch[1].toLowerCase());
|
|
2760
|
-
index = tagEnd + 1;
|
|
2761
|
-
continue;
|
|
2762
|
-
}
|
|
2763
|
-
const openingMatch = tag.match(/^<\s*([A-Za-z_][A-Za-z0-9_.:-]*)/);
|
|
2764
|
-
if (openingMatch?.[1] && !/\/\s*>$/.test(tag)) {
|
|
2765
|
-
const name = openingMatch[1].toLowerCase();
|
|
2766
|
-
stack.push({
|
|
2767
|
-
name,
|
|
2768
|
-
...name === "voice" ? { voiceName: getVoiceName(tag) } : {}
|
|
2769
|
-
});
|
|
2770
|
-
}
|
|
2771
|
-
index = tagEnd + 1;
|
|
2772
|
-
}
|
|
2773
|
-
for (let stackIndex = stack.length - 1; stackIndex >= 0; stackIndex -= 1) {
|
|
2774
|
-
const element = stack[stackIndex];
|
|
2775
|
-
if (element?.name === "voice") {
|
|
2776
|
-
return element.voiceName === void 0 ? {} : { voiceName: element.voiceName };
|
|
2777
|
-
}
|
|
2778
|
-
}
|
|
2779
|
-
return void 0;
|
|
2780
|
-
}
|
|
2781
|
-
|
|
2782
|
-
// packages/ssml-editor-react/src/hooks/useSsmlEditorState.ts
|
|
2783
|
-
var TIMING_INSERTION_TAGS = /* @__PURE__ */ new Set(["break", "mstts:silence"]);
|
|
2784
|
-
var PROSODY_INSERTION_TAGS = /* @__PURE__ */ new Set(["prosody", "mstts:express-as", "voice", "emphasis"]);
|
|
2785
|
-
var TEXT_INSERTION_TAGS = /* @__PURE__ */ new Set(["sub", "say-as", "phoneme", "w", "lang"]);
|
|
2786
|
-
var EMPTY_SELECTION_OVERLAY = {
|
|
2787
|
-
selectedText: "",
|
|
2788
|
-
characterCount: 0,
|
|
2789
|
-
hasSelection: false,
|
|
2790
|
-
position: null,
|
|
2791
|
-
placement: "above"
|
|
2792
|
-
};
|
|
2884
|
+
// packages/ssml-editor-react/src/editableSsml.ts
|
|
2793
2885
|
function isSsmlElement(node) {
|
|
2794
2886
|
return typeof node !== "string" && node.type !== "text";
|
|
2795
2887
|
}
|
|
@@ -2884,48 +2976,6 @@ function preserveEmptyPairElements(nodes, startTags, startTagIndex) {
|
|
|
2884
2976
|
function getDocumentChildren2(document2) {
|
|
2885
2977
|
return document2.children ?? (document2.content === void 0 ? [] : [document2.content]);
|
|
2886
2978
|
}
|
|
2887
|
-
function getPartialContext(document2) {
|
|
2888
|
-
const children = getDocumentChildren2(document2);
|
|
2889
|
-
const voice = findFirstElement(children, isVoice);
|
|
2890
|
-
const prosody = findFirstElement(children, isProsody);
|
|
2891
|
-
const context = {
|
|
2892
|
-
version: document2.version,
|
|
2893
|
-
lang: document2.lang
|
|
2894
|
-
};
|
|
2895
|
-
if (voice) {
|
|
2896
|
-
context.voice = {
|
|
2897
|
-
name: voice.name,
|
|
2898
|
-
effect: voice.effect,
|
|
2899
|
-
attributes: voice.attributes
|
|
2900
|
-
};
|
|
2901
|
-
}
|
|
2902
|
-
if (prosody) {
|
|
2903
|
-
context.prosody = {
|
|
2904
|
-
rate: prosody.rate,
|
|
2905
|
-
pitch: prosody.pitch,
|
|
2906
|
-
volume: prosody.volume,
|
|
2907
|
-
contour: prosody.contour,
|
|
2908
|
-
range: prosody.range,
|
|
2909
|
-
attributes: prosody.attributes
|
|
2910
|
-
};
|
|
2911
|
-
}
|
|
2912
|
-
return context;
|
|
2913
|
-
}
|
|
2914
|
-
function findFirstElement(nodes, predicate) {
|
|
2915
|
-
for (const node of nodes) {
|
|
2916
|
-
if (!isSsmlElement(node)) {
|
|
2917
|
-
continue;
|
|
2918
|
-
}
|
|
2919
|
-
if (predicate(node)) {
|
|
2920
|
-
return node;
|
|
2921
|
-
}
|
|
2922
|
-
const child = findFirstElement(node.children ?? [], predicate);
|
|
2923
|
-
if (child) {
|
|
2924
|
-
return child;
|
|
2925
|
-
}
|
|
2926
|
-
}
|
|
2927
|
-
return void 0;
|
|
2928
|
-
}
|
|
2929
2979
|
function findFirstElementPath(nodes, predicate, ancestors = []) {
|
|
2930
2980
|
for (const node of nodes) {
|
|
2931
2981
|
if (!isSsmlElement(node)) {
|
|
@@ -3006,13 +3056,10 @@ function getEditableRegion(document2) {
|
|
|
3006
3056
|
...voice ? { voiceName: voice.name } : {}
|
|
3007
3057
|
};
|
|
3008
3058
|
}
|
|
3009
|
-
function getEditableChildren(document2) {
|
|
3010
|
-
return getEditableRegion(document2).children;
|
|
3011
|
-
}
|
|
3012
3059
|
function getEditableText(document2) {
|
|
3013
|
-
return serializeEditableText(
|
|
3060
|
+
return serializeEditableText(getEditableRegion(document2).children, document2.lang);
|
|
3014
3061
|
}
|
|
3015
|
-
function
|
|
3062
|
+
function updateEditableText(document2, value) {
|
|
3016
3063
|
const nextChildren = parseEditableText(value, document2.lang);
|
|
3017
3064
|
const editableChildren = nextChildren.length > 0 ? nextChildren : [value];
|
|
3018
3065
|
const children = getDocumentChildren2(document2);
|
|
@@ -3032,6 +3079,241 @@ function updateText(document2, value) {
|
|
|
3032
3079
|
}
|
|
3033
3080
|
return withChildren(document2, editableChildren);
|
|
3034
3081
|
}
|
|
3082
|
+
|
|
3083
|
+
// packages/ssml-editor-react/src/ssmlInsertion.ts
|
|
3084
|
+
function isLineStart(value, offset) {
|
|
3085
|
+
return offset === 0 || value[offset - 1] === "\n" || value[offset - 1] === "\r";
|
|
3086
|
+
}
|
|
3087
|
+
function isLineEnd(value, offset) {
|
|
3088
|
+
return offset === value.length || value[offset] === "\n" || value[offset] === "\r";
|
|
3089
|
+
}
|
|
3090
|
+
function getLineBreakAt(value, offset) {
|
|
3091
|
+
if (value.startsWith("\r\n", offset)) {
|
|
3092
|
+
return "\r\n";
|
|
3093
|
+
}
|
|
3094
|
+
if (value[offset] === "\n" || value[offset] === "\r") {
|
|
3095
|
+
return value[offset];
|
|
3096
|
+
}
|
|
3097
|
+
return "";
|
|
3098
|
+
}
|
|
3099
|
+
function startsWithLineBreak(value) {
|
|
3100
|
+
return value.startsWith("\n") || value.startsWith("\r");
|
|
3101
|
+
}
|
|
3102
|
+
function endsWithLineBreak(value) {
|
|
3103
|
+
return value.endsWith("\n") || value.endsWith("\r");
|
|
3104
|
+
}
|
|
3105
|
+
function createSsmlInsertionEdit(source, startOffset, endOffset, template, eol = "\n", selectedText = startOffset === endOffset ? "" : source.slice(startOffset, endOffset)) {
|
|
3106
|
+
if (template.mode === SSML_INSERTION_MODES.wrap) {
|
|
3107
|
+
const trailingLineBreak2 = getLineBreakAt(source, endOffset) === "" ? eol : "";
|
|
3108
|
+
return {
|
|
3109
|
+
replacement: `${template.prefix}${selectedText}${template.suffix}${trailingLineBreak2}`,
|
|
3110
|
+
selectionOffset: template.prefix.length
|
|
3111
|
+
};
|
|
3112
|
+
}
|
|
3113
|
+
const followingLineBreak = getLineBreakAt(source, endOffset);
|
|
3114
|
+
const leadingLineBreak = !isLineStart(source, startOffset) && !startsWithLineBreak(template.prefix) ? eol : "";
|
|
3115
|
+
const needsTrailingLineBreak = selectedText.length > 0 ? !selectedText.startsWith("\n") && !selectedText.startsWith("\r") : !isLineEnd(source, endOffset) || followingLineBreak === "";
|
|
3116
|
+
const trailingLineBreak = needsTrailingLineBreak && !endsWithLineBreak(template.prefix) ? eol : "";
|
|
3117
|
+
const insertionPrefix = `${leadingLineBreak}${template.prefix}`;
|
|
3118
|
+
return {
|
|
3119
|
+
replacement: `${insertionPrefix}${trailingLineBreak}${selectedText}`,
|
|
3120
|
+
selectionOffset: insertionPrefix.length + trailingLineBreak.length + (selectedText.length === 0 && trailingLineBreak === "" ? followingLineBreak.length : 0)
|
|
3121
|
+
};
|
|
3122
|
+
}
|
|
3123
|
+
|
|
3124
|
+
// packages/ssml-editor-react/src/ssmlContext.ts
|
|
3125
|
+
function findTagEnd2(source, start, limit) {
|
|
3126
|
+
let quote;
|
|
3127
|
+
for (let index = start + 1; index < limit; index += 1) {
|
|
3128
|
+
const character = source[index];
|
|
3129
|
+
if (quote !== void 0) {
|
|
3130
|
+
if (character === quote) {
|
|
3131
|
+
quote = void 0;
|
|
3132
|
+
}
|
|
3133
|
+
continue;
|
|
3134
|
+
}
|
|
3135
|
+
if (character === '"' || character === "'") {
|
|
3136
|
+
quote = character;
|
|
3137
|
+
continue;
|
|
3138
|
+
}
|
|
3139
|
+
if (character === ">") {
|
|
3140
|
+
return index;
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
3143
|
+
return -1;
|
|
3144
|
+
}
|
|
3145
|
+
function getVoiceName(tag) {
|
|
3146
|
+
return tag.match(/\bname\s*=\s*(["'])([\s\S]*?)\1/i)?.[2];
|
|
3147
|
+
}
|
|
3148
|
+
function closeElement(stack, name) {
|
|
3149
|
+
for (let index = stack.length - 1; index >= 0; index -= 1) {
|
|
3150
|
+
if (stack[index]?.name === name) {
|
|
3151
|
+
stack.splice(index);
|
|
3152
|
+
return;
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
function findActiveSsmlTags(source, offset) {
|
|
3157
|
+
const limit = Math.max(0, Math.min(offset, source.length));
|
|
3158
|
+
const stack = [];
|
|
3159
|
+
let index = 0;
|
|
3160
|
+
while (index < source.length) {
|
|
3161
|
+
const tagStart = source.indexOf("<", index);
|
|
3162
|
+
if (tagStart === -1 || tagStart > limit) {
|
|
3163
|
+
break;
|
|
3164
|
+
}
|
|
3165
|
+
const nonContentEnd = source.startsWith("<!--", tagStart) ? source.indexOf("-->", tagStart + 4) : source.startsWith("<![CDATA[", tagStart) ? source.indexOf("]]>", tagStart + 9) : source.startsWith("<?", tagStart) ? source.indexOf("?>", tagStart + 2) : void 0;
|
|
3166
|
+
if (nonContentEnd !== void 0) {
|
|
3167
|
+
const delimiterLength = source.startsWith("<?", tagStart) ? 2 : 3;
|
|
3168
|
+
const end = nonContentEnd === -1 ? source.length : nonContentEnd + delimiterLength;
|
|
3169
|
+
if (limit < end) {
|
|
3170
|
+
break;
|
|
3171
|
+
}
|
|
3172
|
+
index = end;
|
|
3173
|
+
continue;
|
|
3174
|
+
}
|
|
3175
|
+
const tagEnd = findTagEnd2(source, tagStart, source.length);
|
|
3176
|
+
const tag = source.slice(tagStart, tagEnd === -1 ? source.length : tagEnd + 1);
|
|
3177
|
+
const closingMatch = tag.match(/^<\s*\/\s*([A-Za-z_][A-Za-z0-9_.:-]*)/);
|
|
3178
|
+
const openingMatch = tag.match(/^<\s*([A-Za-z_][A-Za-z0-9_.:-]*)/);
|
|
3179
|
+
if (tagEnd === -1 || limit <= tagEnd) {
|
|
3180
|
+
if (openingMatch?.[1]) {
|
|
3181
|
+
stack.push({ name: openingMatch[1].toLowerCase() });
|
|
3182
|
+
}
|
|
3183
|
+
break;
|
|
3184
|
+
}
|
|
3185
|
+
if (closingMatch?.[1]) {
|
|
3186
|
+
closeElement(stack, closingMatch[1].toLowerCase());
|
|
3187
|
+
} else if (openingMatch?.[1] && !/\/\s*>$/.test(tag)) {
|
|
3188
|
+
stack.push({ name: openingMatch[1].toLowerCase() });
|
|
3189
|
+
}
|
|
3190
|
+
index = tagEnd + 1;
|
|
3191
|
+
}
|
|
3192
|
+
return new Set(stack.map(({ name }) => name));
|
|
3193
|
+
}
|
|
3194
|
+
function findSsmlVoiceContext(source, offset) {
|
|
3195
|
+
const limit = Math.max(0, Math.min(offset, source.length));
|
|
3196
|
+
const stack = [];
|
|
3197
|
+
let index = 0;
|
|
3198
|
+
while (index < limit) {
|
|
3199
|
+
const tagStart = source.indexOf("<", index);
|
|
3200
|
+
if (tagStart === -1 || tagStart >= limit) {
|
|
3201
|
+
break;
|
|
3202
|
+
}
|
|
3203
|
+
if (source.startsWith("<!--", tagStart)) {
|
|
3204
|
+
const end = source.indexOf("-->", tagStart + 4);
|
|
3205
|
+
index = end === -1 || end + 3 > limit ? limit : end + 3;
|
|
3206
|
+
continue;
|
|
3207
|
+
}
|
|
3208
|
+
if (source.startsWith("<![CDATA[", tagStart)) {
|
|
3209
|
+
const end = source.indexOf("]]>", tagStart + 9);
|
|
3210
|
+
index = end === -1 || end + 3 > limit ? limit : end + 3;
|
|
3211
|
+
continue;
|
|
3212
|
+
}
|
|
3213
|
+
if (source.startsWith("<?", tagStart)) {
|
|
3214
|
+
const end = source.indexOf("?>", tagStart + 2);
|
|
3215
|
+
index = end === -1 || end + 2 > limit ? limit : end + 2;
|
|
3216
|
+
continue;
|
|
3217
|
+
}
|
|
3218
|
+
const tagEnd = findTagEnd2(source, tagStart, limit);
|
|
3219
|
+
if (tagEnd === -1) {
|
|
3220
|
+
break;
|
|
3221
|
+
}
|
|
3222
|
+
const tag = source.slice(tagStart, tagEnd + 1);
|
|
3223
|
+
if (tag.startsWith("<!")) {
|
|
3224
|
+
index = tagEnd + 1;
|
|
3225
|
+
continue;
|
|
3226
|
+
}
|
|
3227
|
+
const closingMatch = tag.match(/^<\s*\/\s*([A-Za-z_][A-Za-z0-9_.:-]*)/);
|
|
3228
|
+
if (closingMatch?.[1]) {
|
|
3229
|
+
closeElement(stack, closingMatch[1].toLowerCase());
|
|
3230
|
+
index = tagEnd + 1;
|
|
3231
|
+
continue;
|
|
3232
|
+
}
|
|
3233
|
+
const openingMatch = tag.match(/^<\s*([A-Za-z_][A-Za-z0-9_.:-]*)/);
|
|
3234
|
+
if (openingMatch?.[1] && !/\/\s*>$/.test(tag)) {
|
|
3235
|
+
const name = openingMatch[1].toLowerCase();
|
|
3236
|
+
stack.push({
|
|
3237
|
+
name,
|
|
3238
|
+
...name === "voice" ? { voiceName: getVoiceName(tag) } : {}
|
|
3239
|
+
});
|
|
3240
|
+
}
|
|
3241
|
+
index = tagEnd + 1;
|
|
3242
|
+
}
|
|
3243
|
+
for (let stackIndex = stack.length - 1; stackIndex >= 0; stackIndex -= 1) {
|
|
3244
|
+
const element = stack[stackIndex];
|
|
3245
|
+
if (element?.name === "voice") {
|
|
3246
|
+
return element.voiceName === void 0 ? {} : { voiceName: element.voiceName };
|
|
3247
|
+
}
|
|
3248
|
+
}
|
|
3249
|
+
return void 0;
|
|
3250
|
+
}
|
|
3251
|
+
|
|
3252
|
+
// packages/ssml-editor-react/src/hooks/useSsmlEditorState.ts
|
|
3253
|
+
var TIMING_INSERTION_TAGS = /* @__PURE__ */ new Set(["break", "mstts:silence"]);
|
|
3254
|
+
var PROSODY_INSERTION_TAGS = /* @__PURE__ */ new Set(["prosody", "mstts:express-as", "voice", "emphasis"]);
|
|
3255
|
+
var TEXT_INSERTION_TAGS = /* @__PURE__ */ new Set(["sub", "say-as", "phoneme", "w", "lang"]);
|
|
3256
|
+
var EMPTY_SELECTION_OVERLAY = {
|
|
3257
|
+
selectedText: "",
|
|
3258
|
+
characterCount: 0,
|
|
3259
|
+
hasSelection: false,
|
|
3260
|
+
position: null,
|
|
3261
|
+
placement: "above"
|
|
3262
|
+
};
|
|
3263
|
+
function isSsmlElement2(node) {
|
|
3264
|
+
return typeof node !== "string" && node.type !== "text";
|
|
3265
|
+
}
|
|
3266
|
+
function isVoice2(element) {
|
|
3267
|
+
return element.type === "voice";
|
|
3268
|
+
}
|
|
3269
|
+
function isProsody2(element) {
|
|
3270
|
+
return element.type === "prosody";
|
|
3271
|
+
}
|
|
3272
|
+
function getDocumentChildren3(document2) {
|
|
3273
|
+
return document2.children ?? (document2.content === void 0 ? [] : [document2.content]);
|
|
3274
|
+
}
|
|
3275
|
+
function getPartialContext(document2) {
|
|
3276
|
+
const children = getDocumentChildren3(document2);
|
|
3277
|
+
const voice = findFirstElement(children, isVoice2);
|
|
3278
|
+
const prosody = findFirstElement(children, isProsody2);
|
|
3279
|
+
const context = {
|
|
3280
|
+
version: document2.version,
|
|
3281
|
+
lang: document2.lang
|
|
3282
|
+
};
|
|
3283
|
+
if (voice) {
|
|
3284
|
+
context.voice = {
|
|
3285
|
+
name: voice.name,
|
|
3286
|
+
effect: voice.effect,
|
|
3287
|
+
attributes: voice.attributes
|
|
3288
|
+
};
|
|
3289
|
+
}
|
|
3290
|
+
if (prosody) {
|
|
3291
|
+
context.prosody = {
|
|
3292
|
+
rate: prosody.rate,
|
|
3293
|
+
pitch: prosody.pitch,
|
|
3294
|
+
volume: prosody.volume,
|
|
3295
|
+
contour: prosody.contour,
|
|
3296
|
+
range: prosody.range,
|
|
3297
|
+
attributes: prosody.attributes
|
|
3298
|
+
};
|
|
3299
|
+
}
|
|
3300
|
+
return context;
|
|
3301
|
+
}
|
|
3302
|
+
function findFirstElement(nodes, predicate) {
|
|
3303
|
+
for (const node of nodes) {
|
|
3304
|
+
if (!isSsmlElement2(node)) {
|
|
3305
|
+
continue;
|
|
3306
|
+
}
|
|
3307
|
+
if (predicate(node)) {
|
|
3308
|
+
return node;
|
|
3309
|
+
}
|
|
3310
|
+
const child = findFirstElement(node.children ?? [], predicate);
|
|
3311
|
+
if (child) {
|
|
3312
|
+
return child;
|
|
3313
|
+
}
|
|
3314
|
+
}
|
|
3315
|
+
return void 0;
|
|
3316
|
+
}
|
|
3035
3317
|
function getSelectionInfo(editor) {
|
|
3036
3318
|
const model = editor.getModel();
|
|
3037
3319
|
const selection = editor.getSelection();
|
|
@@ -3167,6 +3449,7 @@ function useSsmlEditorState({
|
|
|
3167
3449
|
const [decorationsVisible, setDecorationsVisible] = (0, import_react.useState)(showDecorations);
|
|
3168
3450
|
const [isHelpOpen, setIsHelpOpen] = (0, import_react.useState)(false);
|
|
3169
3451
|
const [syntaxError, setSyntaxError] = (0, import_react.useState)(null);
|
|
3452
|
+
const [activeTags, setActiveTags] = (0, import_react.useState)(() => /* @__PURE__ */ new Set());
|
|
3170
3453
|
const [openPopoverId, setOpenPopoverId] = (0, import_react.useState)(null);
|
|
3171
3454
|
const [popoverVoiceName, setPopoverVoiceName] = (0, import_react.useState)(void 0);
|
|
3172
3455
|
const [popoverPosition, setPopoverPosition] = (0, import_react.useState)(null);
|
|
@@ -3269,6 +3552,14 @@ function useSsmlEditorState({
|
|
|
3269
3552
|
});
|
|
3270
3553
|
}
|
|
3271
3554
|
}, []);
|
|
3555
|
+
const updateActiveTags = (0, import_react.useCallback)((nextTags) => {
|
|
3556
|
+
setActiveTags((currentTags) => {
|
|
3557
|
+
if (currentTags.size === nextTags.size && [...currentTags].every((tag) => nextTags.has(tag))) {
|
|
3558
|
+
return currentTags;
|
|
3559
|
+
}
|
|
3560
|
+
return new Set(nextTags);
|
|
3561
|
+
});
|
|
3562
|
+
}, []);
|
|
3272
3563
|
const commit = (0, import_react.useCallback)(
|
|
3273
3564
|
(nextDocument) => {
|
|
3274
3565
|
draftDocumentRef.current = nextDocument;
|
|
@@ -3280,7 +3571,7 @@ function useSsmlEditorState({
|
|
|
3280
3571
|
);
|
|
3281
3572
|
const handleTextChange = (0, import_react.useCallback)(
|
|
3282
3573
|
(value) => {
|
|
3283
|
-
commit(
|
|
3574
|
+
commit(updateEditableText(draftDocumentRef.current, value));
|
|
3284
3575
|
if (editorRef.current) {
|
|
3285
3576
|
refreshSelectionOverlay(editorRef.current, false);
|
|
3286
3577
|
}
|
|
@@ -3324,7 +3615,7 @@ function useSsmlEditorState({
|
|
|
3324
3615
|
}, [commit]);
|
|
3325
3616
|
const handleFormat = (0, import_react.useCallback)(() => {
|
|
3326
3617
|
const value = editorRef.current?.getValue() ?? getEditableText(draftDocumentRef.current);
|
|
3327
|
-
commit(
|
|
3618
|
+
commit(updateEditableText(draftDocumentRef.current, formatXmlFragment(value)));
|
|
3328
3619
|
}, [commit]);
|
|
3329
3620
|
const handleUndo = (0, import_react.useCallback)(() => {
|
|
3330
3621
|
editorRef.current?.trigger("toolbar", "undo", null);
|
|
@@ -3354,7 +3645,7 @@ function useSsmlEditorState({
|
|
|
3354
3645
|
);
|
|
3355
3646
|
const getFullSsml = (0, import_react.useCallback)(
|
|
3356
3647
|
() => buildSsml(
|
|
3357
|
-
editorRef.current ?
|
|
3648
|
+
editorRef.current ? updateEditableText(draftDocumentRef.current, editorRef.current.getValue()) : draftDocumentRef.current
|
|
3358
3649
|
),
|
|
3359
3650
|
[]
|
|
3360
3651
|
);
|
|
@@ -3364,6 +3655,7 @@ function useSsmlEditorState({
|
|
|
3364
3655
|
draftDocument,
|
|
3365
3656
|
text: getEditableText(draftDocument),
|
|
3366
3657
|
selectionOverlay,
|
|
3658
|
+
activeTags,
|
|
3367
3659
|
isDarkTheme: resolvedTheme === "dark" || resolvedTheme === "system" && isDark,
|
|
3368
3660
|
decorationsVisible,
|
|
3369
3661
|
setDecorationsVisible,
|
|
@@ -3385,6 +3677,7 @@ function useSsmlEditorState({
|
|
|
3385
3677
|
previewSelection,
|
|
3386
3678
|
canPreviewSelection: onPreviewSelection !== void 0,
|
|
3387
3679
|
refreshSelectionOverlay,
|
|
3680
|
+
updateActiveTags,
|
|
3388
3681
|
getSelectedSsml: getSelectedSsmlValue,
|
|
3389
3682
|
getCurrentLineSsml: getCurrentLineSsmlValue,
|
|
3390
3683
|
getFullSsml,
|
|
@@ -3618,7 +3911,8 @@ function registerSsmlCompletionProvider(monaco, options = {}) {
|
|
|
3618
3911
|
...attributeValues?.map((value2) => ({
|
|
3619
3912
|
label: value2,
|
|
3620
3913
|
kind: monaco.languages.CompletionItemKind.Value,
|
|
3621
|
-
insertText: value2
|
|
3914
|
+
insertText: value2,
|
|
3915
|
+
range
|
|
3622
3916
|
})) ?? []
|
|
3623
3917
|
]
|
|
3624
3918
|
};
|
|
@@ -4434,6 +4728,7 @@ function useSsmlMonaco({
|
|
|
4434
4728
|
decorationsVisible,
|
|
4435
4729
|
getOuterVoiceName,
|
|
4436
4730
|
onSelectionOverlayChange,
|
|
4731
|
+
onActiveTagsChange,
|
|
4437
4732
|
onSyntaxErrorChange
|
|
4438
4733
|
}) {
|
|
4439
4734
|
const internalEditorRef = (0, import_react2.useRef)(null);
|
|
@@ -4443,6 +4738,7 @@ function useSsmlMonaco({
|
|
|
4443
4738
|
const codeActionProviderRef = (0, import_react2.useRef)(null);
|
|
4444
4739
|
const releaseHoverProviderRef = (0, import_react2.useRef)(null);
|
|
4445
4740
|
const selectionChangeRef = (0, import_react2.useRef)(null);
|
|
4741
|
+
const cursorPositionChangeRef = (0, import_react2.useRef)(null);
|
|
4446
4742
|
const diagnosticsChangeRef = (0, import_react2.useRef)(null);
|
|
4447
4743
|
const diagnosticsTimeoutRef = (0, import_react2.useRef)(null);
|
|
4448
4744
|
const selectionLayoutDisposablesRef = (0, import_react2.useRef)([]);
|
|
@@ -4451,11 +4747,13 @@ function useSsmlMonaco({
|
|
|
4451
4747
|
const decorationsVisibleRef = (0, import_react2.useRef)(decorationsVisible);
|
|
4452
4748
|
const getOuterVoiceNameRef = (0, import_react2.useRef)(getOuterVoiceName);
|
|
4453
4749
|
const onSelectionOverlayChangeRef = (0, import_react2.useRef)(onSelectionOverlayChange);
|
|
4750
|
+
const onActiveTagsChangeRef = (0, import_react2.useRef)(onActiveTagsChange);
|
|
4454
4751
|
const onSyntaxErrorChangeRef = (0, import_react2.useRef)(onSyntaxErrorChange);
|
|
4455
4752
|
languageRef.current = language;
|
|
4456
4753
|
decorationsVisibleRef.current = decorationsVisible;
|
|
4457
4754
|
getOuterVoiceNameRef.current = getOuterVoiceName;
|
|
4458
4755
|
onSelectionOverlayChangeRef.current = onSelectionOverlayChange;
|
|
4756
|
+
onActiveTagsChangeRef.current = onActiveTagsChange;
|
|
4459
4757
|
onSyntaxErrorChangeRef.current = onSyntaxErrorChange;
|
|
4460
4758
|
const runSsmlDiagnostics = (0, import_react2.useCallback)((editor, monaco) => {
|
|
4461
4759
|
const model = editor.getModel();
|
|
@@ -4485,6 +4783,15 @@ function useSsmlMonaco({
|
|
|
4485
4783
|
diagnosticsChangeRef.current?.dispose();
|
|
4486
4784
|
diagnosticsChangeRef.current = null;
|
|
4487
4785
|
}, []);
|
|
4786
|
+
const syncActiveTags = (0, import_react2.useCallback)((editor) => {
|
|
4787
|
+
const model = editor.getModel();
|
|
4788
|
+
const position = editor.getPosition();
|
|
4789
|
+
if (!model || !position) {
|
|
4790
|
+
onActiveTagsChangeRef.current(/* @__PURE__ */ new Set());
|
|
4791
|
+
return;
|
|
4792
|
+
}
|
|
4793
|
+
onActiveTagsChangeRef.current(findActiveSsmlTags(model.getValue(), model.getOffsetAt(position)));
|
|
4794
|
+
}, []);
|
|
4488
4795
|
const disposeMonacoResources = (0, import_react2.useCallback)(() => {
|
|
4489
4796
|
clearSsmlDiagnosticsResources();
|
|
4490
4797
|
const model = editorRef.current?.getModel();
|
|
@@ -4494,6 +4801,8 @@ function useSsmlMonaco({
|
|
|
4494
4801
|
}
|
|
4495
4802
|
selectionChangeRef.current?.dispose();
|
|
4496
4803
|
selectionChangeRef.current = null;
|
|
4804
|
+
cursorPositionChangeRef.current?.dispose();
|
|
4805
|
+
cursorPositionChangeRef.current = null;
|
|
4497
4806
|
completionProviderRef.current?.dispose();
|
|
4498
4807
|
completionProviderRef.current = null;
|
|
4499
4808
|
codeActionProviderRef.current?.dispose();
|
|
@@ -4515,6 +4824,9 @@ function useSsmlMonaco({
|
|
|
4515
4824
|
selectionChangeRef.current = editor.onDidChangeCursorSelection(() => {
|
|
4516
4825
|
onSelectionOverlayChangeRef.current(editor, true);
|
|
4517
4826
|
});
|
|
4827
|
+
cursorPositionChangeRef.current = editor.onDidChangeCursorPosition(() => {
|
|
4828
|
+
syncActiveTags(editor);
|
|
4829
|
+
});
|
|
4518
4830
|
diagnosticsChangeRef.current = editor.onDidChangeModelContent(() => {
|
|
4519
4831
|
const model2 = editor.getModel();
|
|
4520
4832
|
if (model2) {
|
|
@@ -4526,6 +4838,7 @@ function useSsmlMonaco({
|
|
|
4526
4838
|
inlineDecorationIdsRef.current
|
|
4527
4839
|
);
|
|
4528
4840
|
}
|
|
4841
|
+
syncActiveTags(editor);
|
|
4529
4842
|
scheduleSsmlDiagnostics(editor, monaco);
|
|
4530
4843
|
});
|
|
4531
4844
|
selectionLayoutDisposablesRef.current = [
|
|
@@ -4551,8 +4864,9 @@ function useSsmlMonaco({
|
|
|
4551
4864
|
);
|
|
4552
4865
|
}
|
|
4553
4866
|
onSelectionOverlayChangeRef.current(editor, true);
|
|
4867
|
+
syncActiveTags(editor);
|
|
4554
4868
|
},
|
|
4555
|
-
[disposeMonacoResources, editorRef, runSsmlDiagnostics, scheduleSsmlDiagnostics]
|
|
4869
|
+
[disposeMonacoResources, editorRef, runSsmlDiagnostics, scheduleSsmlDiagnostics, syncActiveTags]
|
|
4556
4870
|
);
|
|
4557
4871
|
(0, import_react2.useEffect)(() => {
|
|
4558
4872
|
const monaco = monacoRef.current;
|
|
@@ -4940,6 +5254,8 @@ var STYLE_CSS = `
|
|
|
4940
5254
|
--ssml-editor-border: #d1d5db;
|
|
4941
5255
|
--ssml-editor-control-bg: #f9fafb;
|
|
4942
5256
|
--ssml-editor-control-border: #9ca3af;
|
|
5257
|
+
--ssml-editor-active-bg: #dbeafe;
|
|
5258
|
+
--ssml-editor-active-border: #2563eb;
|
|
4943
5259
|
--ssml-editor-preview-bg: #f3f4f6;
|
|
4944
5260
|
--ssml-editor-error: #b91c1c;
|
|
4945
5261
|
--ssml-editor-error-bg: #fef2f2;
|
|
@@ -4957,6 +5273,8 @@ var STYLE_CSS = `
|
|
|
4957
5273
|
--ssml-editor-border: #374151;
|
|
4958
5274
|
--ssml-editor-control-bg: #111827;
|
|
4959
5275
|
--ssml-editor-control-border: #4b5563;
|
|
5276
|
+
--ssml-editor-active-bg: #1e3a8a;
|
|
5277
|
+
--ssml-editor-active-border: #60a5fa;
|
|
4960
5278
|
--ssml-editor-preview-bg: #111827;
|
|
4961
5279
|
--ssml-editor-error: #fca5a5;
|
|
4962
5280
|
--ssml-editor-error-bg: #450a0a;
|
|
@@ -5176,6 +5494,7 @@ var SsmlEditor = (0, import_react3.forwardRef)(function SsmlEditor2({
|
|
|
5176
5494
|
editorRef,
|
|
5177
5495
|
text,
|
|
5178
5496
|
selectionOverlay,
|
|
5497
|
+
activeTags,
|
|
5179
5498
|
isDarkTheme,
|
|
5180
5499
|
decorationsVisible,
|
|
5181
5500
|
setDecorationsVisible,
|
|
@@ -5196,6 +5515,7 @@ var SsmlEditor = (0, import_react3.forwardRef)(function SsmlEditor2({
|
|
|
5196
5515
|
previewSelection,
|
|
5197
5516
|
canPreviewSelection,
|
|
5198
5517
|
refreshSelectionOverlay,
|
|
5518
|
+
updateActiveTags,
|
|
5199
5519
|
getSelectedSsml: getSelectedSsml2,
|
|
5200
5520
|
getCurrentLineSsml: getCurrentLineSsml2,
|
|
5201
5521
|
getFullSsml,
|
|
@@ -5224,6 +5544,7 @@ var SsmlEditor = (0, import_react3.forwardRef)(function SsmlEditor2({
|
|
|
5224
5544
|
decorationsVisible,
|
|
5225
5545
|
getOuterVoiceName,
|
|
5226
5546
|
onSelectionOverlayChange: refreshSelectionOverlay,
|
|
5547
|
+
onActiveTagsChange: updateActiveTags,
|
|
5227
5548
|
onSyntaxErrorChange: setSyntaxError
|
|
5228
5549
|
});
|
|
5229
5550
|
(0, import_react3.useImperativeHandle)(
|
|
@@ -5239,12 +5560,13 @@ var SsmlEditor = (0, import_react3.forwardRef)(function SsmlEditor2({
|
|
|
5239
5560
|
const isTimingPopover = insertion.tagName !== void 0 && TIMING_POPOVER_TAGS.has(insertion.tagName);
|
|
5240
5561
|
const isProsodyPopover = insertion.tagName !== void 0 && PROSODY_POPOVER_TAGS.has(insertion.tagName);
|
|
5241
5562
|
const isTextPopover = insertion.tagName !== void 0 && TEXT_POPOVER_TAGS.has(insertion.tagName);
|
|
5563
|
+
const insertionButtonStyle = insertion.tagName && activeTags.has(insertion.tagName.toLowerCase()) ? { ...toolbarButtonStyle, ...editorStyles.toolbarButtonActive } : toolbarButtonStyle;
|
|
5242
5564
|
const props = {
|
|
5243
5565
|
language,
|
|
5244
5566
|
isDarkTheme,
|
|
5245
5567
|
showToolbarIcons,
|
|
5246
5568
|
showToolbarText,
|
|
5247
|
-
toolbarButtonStyle,
|
|
5569
|
+
toolbarButtonStyle: insertionButtonStyle,
|
|
5248
5570
|
emptyOptionsMessage: copy.noAvailableOptions,
|
|
5249
5571
|
isReadOnly,
|
|
5250
5572
|
openPopoverId,
|