@ssml-builder-js/ssml-editor-react 2.10.0 → 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/dist/index.js +155 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +155 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/components/VisualSsmlEditor.tsx +153 -1
- package/src/constants/azureVoiceStyleMap.generated.ts +3 -0
- package/src/constants/ssmlPresets.ts +3 -2
- package/src/ssmlCompletion.ts +1 -1
- package/src/ssmlHover.ts +6 -6
- package/test/ui-components.test.tsx +62 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @ssml-builder-js/ssml-editor-react
|
|
2
2
|
|
|
3
|
+
## 2.11.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Release v2.11.0 with Azure voice catalog APIs and synchronization tools, expanded editor voice support, and stricter background audio validation.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies
|
|
12
|
+
- @ssml-builder-js/ssml-core@2.11.0
|
|
13
|
+
|
|
3
14
|
## 2.10.0
|
|
4
15
|
|
|
5
16
|
### Minor Changes
|
package/dist/index.js
CHANGED
|
@@ -57,6 +57,7 @@ var ssmlPresets_exports = {};
|
|
|
57
57
|
__export(ssmlPresets_exports, {
|
|
58
58
|
AUDIO_DURATION_DESCRIPTIONS: () => AUDIO_DURATION_DESCRIPTIONS,
|
|
59
59
|
AUDIO_DURATION_PRESETS: () => AUDIO_DURATION_PRESETS,
|
|
60
|
+
BACKGROUND_AUDIO_DURATION_PRESETS: () => BACKGROUND_AUDIO_DURATION_PRESETS,
|
|
60
61
|
BREAK_STRENGTH_PRESETS: () => BREAK_STRENGTH_PRESETS,
|
|
61
62
|
BREAK_TIME_DESCRIPTIONS: () => BREAK_TIME_DESCRIPTIONS,
|
|
62
63
|
BREAK_TIME_PRESETS: () => BREAK_TIME_PRESETS,
|
|
@@ -138,6 +139,9 @@ var AZURE_VOICE_STYLE_MAP = {
|
|
|
138
139
|
],
|
|
139
140
|
"es-ES-ElviraNeural": [],
|
|
140
141
|
"fil-PH-AngeloNeural": [],
|
|
142
|
+
"fil-PH-Angelo:DragonHDLatestNeural": [],
|
|
143
|
+
"fil-PH-BlessicaNeural": [],
|
|
144
|
+
"fil-PH-Blessica:DragonHDLatestNeural": [],
|
|
141
145
|
"fr-FR-DeniseNeural": ["cheerful", "sad"],
|
|
142
146
|
"fr-FR-HenriNeural": ["cheerful", "sad"],
|
|
143
147
|
"id-ID-GadisNeural": [],
|
|
@@ -415,6 +419,7 @@ var SAY_AS_PRESETS = [
|
|
|
415
419
|
var LANGUAGE_PRESETS = ["ja-JP", "en-US", "de-DE", "fr-FR"];
|
|
416
420
|
var SILENCE_VALUE_PRESETS = ["300ms", "500ms", "1s"];
|
|
417
421
|
var AUDIO_DURATION_PRESETS = ["5s", "10s", "30s"];
|
|
422
|
+
var BACKGROUND_AUDIO_DURATION_PRESETS = ["0", "500", "1000", "3000", "10000"];
|
|
418
423
|
var SILENCE_TYPE_PRESETS = [
|
|
419
424
|
"Leading",
|
|
420
425
|
"Tailing",
|
|
@@ -476,8 +481,8 @@ var SSML_ATTRIBUTE_PRESETS = {
|
|
|
476
481
|
},
|
|
477
482
|
"mstts:backgroundaudio": {
|
|
478
483
|
volume: PROSODY_VOLUME_PRESETS,
|
|
479
|
-
fadein:
|
|
480
|
-
fadeout:
|
|
484
|
+
fadein: BACKGROUND_AUDIO_DURATION_PRESETS,
|
|
485
|
+
fadeout: BACKGROUND_AUDIO_DURATION_PRESETS
|
|
481
486
|
},
|
|
482
487
|
silence: {
|
|
483
488
|
type: SILENCE_TYPE_PRESETS,
|
|
@@ -3326,7 +3331,7 @@ var SSML_COMPLETION_SNIPPETS = [
|
|
|
3326
3331
|
},
|
|
3327
3332
|
{
|
|
3328
3333
|
label: "mstts:backgroundaudio",
|
|
3329
|
-
insertText: `<mstts:backgroundaudio src="\${1:https://example.com/audio.mp3}" volume="\${2
|
|
3334
|
+
insertText: `<mstts:backgroundaudio src="\${1:https://example.com/audio.mp3}" volume="\${2:70}" fadein="\${3:1000}" fadeout="\${4:1000}" />`
|
|
3330
3335
|
},
|
|
3331
3336
|
{
|
|
3332
3337
|
label: "mstts:ttsembedding",
|
|
@@ -4027,18 +4032,18 @@ var SSML_TAG_DEFINITIONS = [
|
|
|
4027
4032
|
},
|
|
4028
4033
|
{
|
|
4029
4034
|
name: "volume",
|
|
4030
|
-
description: "The background audio volume
|
|
4031
|
-
example: "
|
|
4035
|
+
description: "The background audio volume from 0 to 100.",
|
|
4036
|
+
example: "70"
|
|
4032
4037
|
},
|
|
4033
4038
|
{
|
|
4034
4039
|
name: "fadein",
|
|
4035
|
-
description: "The fade-in duration
|
|
4036
|
-
example: "
|
|
4040
|
+
description: "The fade-in duration in milliseconds from 0 to 10000.",
|
|
4041
|
+
example: "1000"
|
|
4037
4042
|
},
|
|
4038
4043
|
{
|
|
4039
4044
|
name: "fadeout",
|
|
4040
|
-
description: "The fade-out duration
|
|
4041
|
-
example: "
|
|
4045
|
+
description: "The fade-out duration in milliseconds from 0 to 10000.",
|
|
4046
|
+
example: "1000"
|
|
4042
4047
|
}
|
|
4043
4048
|
]
|
|
4044
4049
|
},
|
|
@@ -4694,6 +4699,46 @@ function updateNodesAtPath(nodes, path, update) {
|
|
|
4694
4699
|
function updateTextAtPath(document2, path, value) {
|
|
4695
4700
|
return { ...document2, children: updateNodesAtPath(document2.children ?? [], path, () => value) };
|
|
4696
4701
|
}
|
|
4702
|
+
function getNodeAtPath(nodes, path) {
|
|
4703
|
+
let current;
|
|
4704
|
+
let currentNodes = nodes;
|
|
4705
|
+
for (const index of path) {
|
|
4706
|
+
current = currentNodes[index];
|
|
4707
|
+
if (current === void 0) return void 0;
|
|
4708
|
+
if (!isElement(current)) {
|
|
4709
|
+
currentNodes = [];
|
|
4710
|
+
} else {
|
|
4711
|
+
currentNodes = current.children ?? [];
|
|
4712
|
+
}
|
|
4713
|
+
}
|
|
4714
|
+
return current;
|
|
4715
|
+
}
|
|
4716
|
+
function updateElementAtPath(document2, path, update) {
|
|
4717
|
+
return {
|
|
4718
|
+
...document2,
|
|
4719
|
+
children: updateNodesAtPath(document2.children ?? [], path, (node) => isElement(node) ? update(node) : node)
|
|
4720
|
+
};
|
|
4721
|
+
}
|
|
4722
|
+
function updateElementText(element, value) {
|
|
4723
|
+
return { ...element, children: [value] };
|
|
4724
|
+
}
|
|
4725
|
+
function updateOptionalElementProperty(document2, path, property, value) {
|
|
4726
|
+
return updateElementAtPath(document2, path, (element) => {
|
|
4727
|
+
const next = { ...element };
|
|
4728
|
+
if (value.trim()) next[property] = value;
|
|
4729
|
+
else delete next[property];
|
|
4730
|
+
return next;
|
|
4731
|
+
});
|
|
4732
|
+
}
|
|
4733
|
+
function addDialogTurn(document2, path) {
|
|
4734
|
+
return updateElementAtPath(document2, path, (element) => ({
|
|
4735
|
+
...element,
|
|
4736
|
+
children: [
|
|
4737
|
+
...element.children ?? [],
|
|
4738
|
+
{ type: "mstts:turn", voice: "en-US-JennyNeural", children: ["New dialog turn"] }
|
|
4739
|
+
]
|
|
4740
|
+
}));
|
|
4741
|
+
}
|
|
4697
4742
|
function wrapTextAtPath(document2, path, start, end, tag, attributes) {
|
|
4698
4743
|
return {
|
|
4699
4744
|
...document2,
|
|
@@ -4748,6 +4793,7 @@ function VisualSsmlEditor({
|
|
|
4748
4793
|
const [selection, setSelection] = (0, import_react3.useState)({ start: 0, end: 0 });
|
|
4749
4794
|
const textLeaves = (0, import_react3.useMemo)(() => collectTextLeaves(document2.children ?? []), [document2]);
|
|
4750
4795
|
const selectedLeaf = textLeaves.find((leaf) => leaf.path.join(".") === selectedPath?.join(".")) ?? textLeaves[0];
|
|
4796
|
+
const selectedElement = selectedPath ? getNodeAtPath(document2.children ?? [], selectedPath) : void 0;
|
|
4751
4797
|
const diagnostics = (0, import_react3.useMemo)(() => (0, import_ssml_core4.validateAzureSsml)((0, import_ssml_core4.buildSsml)(document2)), [document2]);
|
|
4752
4798
|
const commit = (nextDocument) => onChange?.(nextDocument);
|
|
4753
4799
|
const applyWrapper = (tag, attributes) => {
|
|
@@ -4782,7 +4828,106 @@ function VisualSsmlEditor({
|
|
|
4782
4828
|
isElement(node) ? elementLabel(node) : JSON.stringify(node)
|
|
4783
4829
|
)) })
|
|
4784
4830
|
] }),
|
|
4785
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "ssml-editor-visual-form", children:
|
|
4831
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "ssml-editor-visual-form", children: selectedElement && isElement(selectedElement) && selectedElement.type === "mstts:dialog" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("fieldset", { children: [
|
|
4832
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("legend", { children: "Dialog" }),
|
|
4833
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: "Add and edit speaker turns in this dialog." }),
|
|
4834
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4835
|
+
"button",
|
|
4836
|
+
{
|
|
4837
|
+
type: "button",
|
|
4838
|
+
disabled: readOnly,
|
|
4839
|
+
onClick: () => commit(addDialogTurn(document2, selectedPath ?? [])),
|
|
4840
|
+
children: "Add turn"
|
|
4841
|
+
}
|
|
4842
|
+
)
|
|
4843
|
+
] }) : selectedElement && isElement(selectedElement) && selectedElement.type === "mstts:turn" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("fieldset", { children: [
|
|
4844
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("legend", { children: "Dialog turn" }),
|
|
4845
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
|
|
4846
|
+
"Voice",
|
|
4847
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4848
|
+
"input",
|
|
4849
|
+
{
|
|
4850
|
+
value: selectedElement.voice ?? "",
|
|
4851
|
+
readOnly,
|
|
4852
|
+
onChange: (event) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "voice", event.target.value))
|
|
4853
|
+
}
|
|
4854
|
+
)
|
|
4855
|
+
] }),
|
|
4856
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
|
|
4857
|
+
"Speaker",
|
|
4858
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4859
|
+
"input",
|
|
4860
|
+
{
|
|
4861
|
+
value: selectedElement.speaker ?? "",
|
|
4862
|
+
readOnly,
|
|
4863
|
+
onChange: (event) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "speaker", event.target.value))
|
|
4864
|
+
}
|
|
4865
|
+
)
|
|
4866
|
+
] }),
|
|
4867
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
|
|
4868
|
+
"Turn text",
|
|
4869
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4870
|
+
"textarea",
|
|
4871
|
+
{
|
|
4872
|
+
value: selectedElement.children?.filter((child) => typeof child === "string").join("") ?? "",
|
|
4873
|
+
readOnly,
|
|
4874
|
+
onChange: (event) => commit(
|
|
4875
|
+
updateElementAtPath(
|
|
4876
|
+
document2,
|
|
4877
|
+
selectedPath ?? [],
|
|
4878
|
+
(element) => updateElementText(element, event.target.value)
|
|
4879
|
+
)
|
|
4880
|
+
)
|
|
4881
|
+
}
|
|
4882
|
+
)
|
|
4883
|
+
] })
|
|
4884
|
+
] }) : selectedElement && isElement(selectedElement) && selectedElement.type === "mstts:backgroundaudio" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("fieldset", { children: [
|
|
4885
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("legend", { children: "Background audio" }),
|
|
4886
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
|
|
4887
|
+
"Source URL",
|
|
4888
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4889
|
+
"input",
|
|
4890
|
+
{
|
|
4891
|
+
value: selectedElement.src ?? "",
|
|
4892
|
+
readOnly,
|
|
4893
|
+
onChange: (event) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "src", event.target.value))
|
|
4894
|
+
}
|
|
4895
|
+
)
|
|
4896
|
+
] }),
|
|
4897
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
|
|
4898
|
+
"Volume",
|
|
4899
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4900
|
+
"input",
|
|
4901
|
+
{
|
|
4902
|
+
value: selectedElement.volume === void 0 ? "" : String(selectedElement.volume),
|
|
4903
|
+
readOnly,
|
|
4904
|
+
onChange: (event) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "volume", event.target.value))
|
|
4905
|
+
}
|
|
4906
|
+
)
|
|
4907
|
+
] }),
|
|
4908
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
|
|
4909
|
+
"Fade in",
|
|
4910
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4911
|
+
"input",
|
|
4912
|
+
{
|
|
4913
|
+
value: selectedElement.fadeIn === void 0 ? "" : String(selectedElement.fadeIn),
|
|
4914
|
+
readOnly,
|
|
4915
|
+
onChange: (event) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "fadeIn", event.target.value))
|
|
4916
|
+
}
|
|
4917
|
+
)
|
|
4918
|
+
] }),
|
|
4919
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
|
|
4920
|
+
"Fade out",
|
|
4921
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4922
|
+
"input",
|
|
4923
|
+
{
|
|
4924
|
+
value: selectedElement.fadeOut === void 0 ? "" : String(selectedElement.fadeOut),
|
|
4925
|
+
readOnly,
|
|
4926
|
+
onChange: (event) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "fadeOut", event.target.value))
|
|
4927
|
+
}
|
|
4928
|
+
)
|
|
4929
|
+
] })
|
|
4930
|
+
] }) : selectedLeaf ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
4786
4931
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
|
|
4787
4932
|
"Text",
|
|
4788
4933
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|