ssml-builder-js 2.10.0 → 2.12.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 +100 -5
- package/bin/sync-voices.js +62 -0
- package/dist/{chunk-GW6CKHXF.mjs → chunk-4BVNAUVR.mjs} +177 -5
- package/dist/chunk-4BVNAUVR.mjs.map +1 -0
- package/dist/{chunk-BYIZQL2W.mjs → chunk-FHDFRM2F.mjs} +92 -14
- package/dist/chunk-FHDFRM2F.mjs.map +1 -0
- package/dist/core.d.mts +21 -3
- package/dist/core.d.ts +21 -3
- package/dist/core.js +177 -4
- package/dist/core.js.map +1 -1
- package/dist/core.mjs +3 -1
- package/dist/elements.js +91 -13
- package/dist/elements.js.map +1 -1
- package/dist/elements.mjs +1 -1
- package/dist/index.d.mts +55 -2
- package/dist/index.d.ts +55 -2
- package/dist/index.js +300 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +124 -3
- package/dist/index.mjs.map +1 -1
- package/dist/react.js +335 -15
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +245 -3
- package/dist/react.mjs.map +1 -1
- package/package.json +5 -1
- package/dist/chunk-BYIZQL2W.mjs.map +0 -1
- package/dist/chunk-GW6CKHXF.mjs.map +0 -1
package/dist/react.mjs
CHANGED
|
@@ -45,7 +45,7 @@ import {
|
|
|
45
45
|
updateTagAttribute,
|
|
46
46
|
validateAzureSsml,
|
|
47
47
|
validateSsml
|
|
48
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-FHDFRM2F.mjs";
|
|
49
49
|
import "./chunk-6S5ODO6A.mjs";
|
|
50
50
|
|
|
51
51
|
// packages/ssml-editor-react/src/SsmlEditor.tsx
|
|
@@ -1840,6 +1840,175 @@ function updateNodesAtPath(nodes, path, update) {
|
|
|
1840
1840
|
function updateTextAtPath(document2, path, value) {
|
|
1841
1841
|
return { ...document2, children: updateNodesAtPath(document2.children ?? [], path, () => value) };
|
|
1842
1842
|
}
|
|
1843
|
+
function getNodeAtPath(nodes, path) {
|
|
1844
|
+
let current;
|
|
1845
|
+
let currentNodes = nodes;
|
|
1846
|
+
for (const index of path) {
|
|
1847
|
+
current = currentNodes[index];
|
|
1848
|
+
if (current === void 0) return void 0;
|
|
1849
|
+
if (!isElement(current)) {
|
|
1850
|
+
currentNodes = [];
|
|
1851
|
+
} else {
|
|
1852
|
+
currentNodes = current.children ?? [];
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
return current;
|
|
1856
|
+
}
|
|
1857
|
+
function updateElementAtPath(document2, path, update) {
|
|
1858
|
+
return {
|
|
1859
|
+
...document2,
|
|
1860
|
+
children: updateNodesAtPath(document2.children ?? [], path, (node) => isElement(node) ? update(node) : node)
|
|
1861
|
+
};
|
|
1862
|
+
}
|
|
1863
|
+
function updateElementText(element, value) {
|
|
1864
|
+
return { ...element, children: [value] };
|
|
1865
|
+
}
|
|
1866
|
+
function updateOptionalElementProperty(document2, path, property, value) {
|
|
1867
|
+
return updateElementAtPath(document2, path, (element) => {
|
|
1868
|
+
const next = { ...element };
|
|
1869
|
+
if (value.trim()) next[property] = value;
|
|
1870
|
+
else delete next[property];
|
|
1871
|
+
return next;
|
|
1872
|
+
});
|
|
1873
|
+
}
|
|
1874
|
+
var VISUAL_ELEMENT_FIELDS = {
|
|
1875
|
+
voice: [
|
|
1876
|
+
{ key: "name", label: "Voice name" },
|
|
1877
|
+
{ key: "effect", label: "Effect" }
|
|
1878
|
+
],
|
|
1879
|
+
prosody: [
|
|
1880
|
+
{ key: "rate", label: "Rate" },
|
|
1881
|
+
{ key: "pitch", label: "Pitch" },
|
|
1882
|
+
{ key: "volume", label: "Volume" },
|
|
1883
|
+
{ key: "contour", label: "Contour" },
|
|
1884
|
+
{ key: "range", label: "Range" }
|
|
1885
|
+
],
|
|
1886
|
+
"express-as": [
|
|
1887
|
+
{ key: "style", label: "Style" },
|
|
1888
|
+
{ key: "styleDegree", label: "Style degree" },
|
|
1889
|
+
{ key: "role", label: "Role" }
|
|
1890
|
+
],
|
|
1891
|
+
expressAs: [
|
|
1892
|
+
{ key: "style", label: "Style" },
|
|
1893
|
+
{ key: "styleDegree", label: "Style degree" },
|
|
1894
|
+
{ key: "role", label: "Role" }
|
|
1895
|
+
],
|
|
1896
|
+
"mstts:express-as": [
|
|
1897
|
+
{ key: "style", label: "Style" },
|
|
1898
|
+
{ key: "styleDegree", label: "Style degree" },
|
|
1899
|
+
{ key: "role", label: "Role" }
|
|
1900
|
+
],
|
|
1901
|
+
"say-as": [
|
|
1902
|
+
{ key: "interpretAs", label: "Interpret as" },
|
|
1903
|
+
{ key: "format", label: "Format" },
|
|
1904
|
+
{ key: "detail", label: "Detail" }
|
|
1905
|
+
],
|
|
1906
|
+
sayAs: [
|
|
1907
|
+
{ key: "interpretAs", label: "Interpret as" },
|
|
1908
|
+
{ key: "format", label: "Format" },
|
|
1909
|
+
{ key: "detail", label: "Detail" }
|
|
1910
|
+
],
|
|
1911
|
+
phoneme: [
|
|
1912
|
+
{ key: "alphabet", label: "Alphabet" },
|
|
1913
|
+
{ key: "ph", label: "Pronunciation" }
|
|
1914
|
+
],
|
|
1915
|
+
emphasis: [{ key: "level", label: "Level" }],
|
|
1916
|
+
audio: [
|
|
1917
|
+
{ key: "src", label: "Source URL" },
|
|
1918
|
+
{ key: "desc", label: "Description" },
|
|
1919
|
+
{ key: "clipBegin", label: "Clip begin" },
|
|
1920
|
+
{ key: "clipEnd", label: "Clip end" },
|
|
1921
|
+
{ key: "speed", label: "Speed" },
|
|
1922
|
+
{ key: "repeatCount", label: "Repeat count" },
|
|
1923
|
+
{ key: "repeatDuration", label: "Repeat duration" },
|
|
1924
|
+
{ key: "soundLevel", label: "Sound level" }
|
|
1925
|
+
],
|
|
1926
|
+
mark: [{ key: "name", label: "Mark name" }],
|
|
1927
|
+
bookmark: [{ key: "mark", label: "Bookmark name" }],
|
|
1928
|
+
sub: [{ key: "alias", label: "Alias" }],
|
|
1929
|
+
lang: [{ key: "lang", label: "Language" }],
|
|
1930
|
+
lexicon: [{ key: "uri", label: "Lexicon URI" }],
|
|
1931
|
+
"mstts:silence": [
|
|
1932
|
+
{ key: "typeValue", label: "Silence type" },
|
|
1933
|
+
{ key: "value", label: "Value" }
|
|
1934
|
+
],
|
|
1935
|
+
silence: [
|
|
1936
|
+
{ key: "typeValue", label: "Silence type" },
|
|
1937
|
+
{ key: "value", label: "Value" }
|
|
1938
|
+
],
|
|
1939
|
+
"mstts:audioduration": [{ key: "value", label: "Duration" }],
|
|
1940
|
+
"mstts:ttsembedding": [{ key: "speakerProfileId", label: "Speaker profile ID" }],
|
|
1941
|
+
"mstts:embedding": [
|
|
1942
|
+
{ key: "id", label: "Embedding ID" },
|
|
1943
|
+
{ key: "speakerProfileId", label: "Speaker profile ID" }
|
|
1944
|
+
],
|
|
1945
|
+
"mstts:voiceconversion": [
|
|
1946
|
+
{ key: "url", label: "Source URL" },
|
|
1947
|
+
{ key: "profile", label: "Profile" },
|
|
1948
|
+
{ key: "speakerProfileId", label: "Speaker profile ID" }
|
|
1949
|
+
]
|
|
1950
|
+
};
|
|
1951
|
+
function getElementFields(element) {
|
|
1952
|
+
return VISUAL_ELEMENT_FIELDS[elementLabel(element)] ?? [];
|
|
1953
|
+
}
|
|
1954
|
+
function VisualElementInspector({
|
|
1955
|
+
document: document2,
|
|
1956
|
+
element,
|
|
1957
|
+
path,
|
|
1958
|
+
readOnly,
|
|
1959
|
+
commit
|
|
1960
|
+
}) {
|
|
1961
|
+
const fields = getElementFields(element);
|
|
1962
|
+
return /* @__PURE__ */ React.createElement("fieldset", null, /* @__PURE__ */ React.createElement("legend", null, `<${elementLabel(element)}>`), fields.length === 0 ? /* @__PURE__ */ React.createElement("p", null, "This element is preserved in the visual tree. Edit its attributes in Code mode.") : fields.map((field) => {
|
|
1963
|
+
const value = element[field.key];
|
|
1964
|
+
const inputValue = value === void 0 ? "" : String(value);
|
|
1965
|
+
const inputId = `ssml-visual-${field.key}`;
|
|
1966
|
+
return /* @__PURE__ */ React.createElement("label", { key: field.key, htmlFor: inputId }, field.label, field.multiline ? /* @__PURE__ */ React.createElement(
|
|
1967
|
+
"textarea",
|
|
1968
|
+
{
|
|
1969
|
+
id: inputId,
|
|
1970
|
+
value: inputValue,
|
|
1971
|
+
readOnly,
|
|
1972
|
+
onChange: (event) => commit(updateOptionalElementProperty(document2, path, field.key, event.target.value))
|
|
1973
|
+
}
|
|
1974
|
+
) : /* @__PURE__ */ React.createElement(
|
|
1975
|
+
"input",
|
|
1976
|
+
{
|
|
1977
|
+
id: inputId,
|
|
1978
|
+
value: inputValue,
|
|
1979
|
+
readOnly,
|
|
1980
|
+
onChange: (event) => commit(updateOptionalElementProperty(document2, path, field.key, event.target.value))
|
|
1981
|
+
}
|
|
1982
|
+
));
|
|
1983
|
+
}));
|
|
1984
|
+
}
|
|
1985
|
+
function getElementAncestors(nodes, path, ancestors = []) {
|
|
1986
|
+
const [index, ...rest] = path;
|
|
1987
|
+
const node = nodes[index];
|
|
1988
|
+
if (!node || !isElement(node)) return ancestors;
|
|
1989
|
+
if (rest.length === 0) return ancestors;
|
|
1990
|
+
return getElementAncestors(node.children ?? [], rest, [...ancestors, node]);
|
|
1991
|
+
}
|
|
1992
|
+
function buildVisualPreview(document2, leaf, selection) {
|
|
1993
|
+
const start = Math.max(0, Math.min(selection.start, leaf.value.length));
|
|
1994
|
+
const end = Math.max(start, Math.min(selection.end, leaf.value.length));
|
|
1995
|
+
const value = start === end ? leaf.value : leaf.value.slice(start, end);
|
|
1996
|
+
const ancestors = getElementAncestors(document2.children ?? [], leaf.path);
|
|
1997
|
+
const children = ancestors.reduceRight(
|
|
1998
|
+
(current, ancestor) => [{ ...ancestor, children: current }],
|
|
1999
|
+
[value]
|
|
2000
|
+
);
|
|
2001
|
+
return buildSsml({ ...document2, children });
|
|
2002
|
+
}
|
|
2003
|
+
function addDialogTurn(document2, path) {
|
|
2004
|
+
return updateElementAtPath(document2, path, (element) => ({
|
|
2005
|
+
...element,
|
|
2006
|
+
children: [
|
|
2007
|
+
...element.children ?? [],
|
|
2008
|
+
{ type: "mstts:turn", voice: "en-US-JennyNeural", children: ["New dialog turn"] }
|
|
2009
|
+
]
|
|
2010
|
+
}));
|
|
2011
|
+
}
|
|
1843
2012
|
function wrapTextAtPath(document2, path, start, end, tag, attributes) {
|
|
1844
2013
|
return {
|
|
1845
2014
|
...document2,
|
|
@@ -1891,6 +2060,7 @@ function VisualSsmlEditor({
|
|
|
1891
2060
|
const [selection, setSelection] = useState2({ start: 0, end: 0 });
|
|
1892
2061
|
const textLeaves = useMemo(() => collectTextLeaves(document2.children ?? []), [document2]);
|
|
1893
2062
|
const selectedLeaf = textLeaves.find((leaf) => leaf.path.join(".") === selectedPath?.join(".")) ?? textLeaves[0];
|
|
2063
|
+
const selectedElement = selectedPath ? getNodeAtPath(document2.children ?? [], selectedPath) : void 0;
|
|
1894
2064
|
const diagnostics = useMemo(() => validateAzureSsml(buildSsml(document2)), [document2]);
|
|
1895
2065
|
const commit = (nextDocument) => onChange?.(nextDocument);
|
|
1896
2066
|
const applyWrapper = (tag, attributes) => {
|
|
@@ -1908,7 +2078,79 @@ function VisualSsmlEditor({
|
|
|
1908
2078
|
selectedPath,
|
|
1909
2079
|
onSelect: setSelectedPath
|
|
1910
2080
|
}
|
|
1911
|
-
)))), /* @__PURE__ */ React.createElement("div", { className: "ssml-editor-visual-form" },
|
|
2081
|
+
)))), /* @__PURE__ */ React.createElement("div", { className: "ssml-editor-visual-form" }, selectedElement && isElement(selectedElement) && selectedElement.type === "mstts:dialog" ? /* @__PURE__ */ React.createElement("fieldset", null, /* @__PURE__ */ React.createElement("legend", null, "Dialog"), /* @__PURE__ */ React.createElement("p", null, "Add and edit speaker turns in this dialog."), /* @__PURE__ */ React.createElement(
|
|
2082
|
+
"button",
|
|
2083
|
+
{
|
|
2084
|
+
type: "button",
|
|
2085
|
+
disabled: readOnly,
|
|
2086
|
+
onClick: () => commit(addDialogTurn(document2, selectedPath ?? []))
|
|
2087
|
+
},
|
|
2088
|
+
"Add turn"
|
|
2089
|
+
)) : selectedElement && isElement(selectedElement) && selectedElement.type === "mstts:turn" ? /* @__PURE__ */ React.createElement("fieldset", null, /* @__PURE__ */ React.createElement("legend", null, "Dialog turn"), /* @__PURE__ */ React.createElement("label", null, "Voice", /* @__PURE__ */ React.createElement(
|
|
2090
|
+
"input",
|
|
2091
|
+
{
|
|
2092
|
+
value: selectedElement.voice ?? "",
|
|
2093
|
+
readOnly,
|
|
2094
|
+
onChange: (event) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "voice", event.target.value))
|
|
2095
|
+
}
|
|
2096
|
+
)), /* @__PURE__ */ React.createElement("label", null, "Speaker", /* @__PURE__ */ React.createElement(
|
|
2097
|
+
"input",
|
|
2098
|
+
{
|
|
2099
|
+
value: selectedElement.speaker ?? "",
|
|
2100
|
+
readOnly,
|
|
2101
|
+
onChange: (event) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "speaker", event.target.value))
|
|
2102
|
+
}
|
|
2103
|
+
)), /* @__PURE__ */ React.createElement("label", null, "Turn text", /* @__PURE__ */ React.createElement(
|
|
2104
|
+
"textarea",
|
|
2105
|
+
{
|
|
2106
|
+
value: selectedElement.children?.filter((child) => typeof child === "string").join("") ?? "",
|
|
2107
|
+
readOnly,
|
|
2108
|
+
onChange: (event) => commit(
|
|
2109
|
+
updateElementAtPath(
|
|
2110
|
+
document2,
|
|
2111
|
+
selectedPath ?? [],
|
|
2112
|
+
(element) => updateElementText(element, event.target.value)
|
|
2113
|
+
)
|
|
2114
|
+
)
|
|
2115
|
+
}
|
|
2116
|
+
))) : selectedElement && isElement(selectedElement) && selectedElement.type === "mstts:backgroundaudio" ? /* @__PURE__ */ React.createElement("fieldset", null, /* @__PURE__ */ React.createElement("legend", null, "Background audio"), /* @__PURE__ */ React.createElement("label", null, "Source URL", /* @__PURE__ */ React.createElement(
|
|
2117
|
+
"input",
|
|
2118
|
+
{
|
|
2119
|
+
value: selectedElement.src ?? "",
|
|
2120
|
+
readOnly,
|
|
2121
|
+
onChange: (event) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "src", event.target.value))
|
|
2122
|
+
}
|
|
2123
|
+
)), /* @__PURE__ */ React.createElement("label", null, "Volume", /* @__PURE__ */ React.createElement(
|
|
2124
|
+
"input",
|
|
2125
|
+
{
|
|
2126
|
+
value: selectedElement.volume === void 0 ? "" : String(selectedElement.volume),
|
|
2127
|
+
readOnly,
|
|
2128
|
+
onChange: (event) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "volume", event.target.value))
|
|
2129
|
+
}
|
|
2130
|
+
)), /* @__PURE__ */ React.createElement("label", null, "Fade in", /* @__PURE__ */ React.createElement(
|
|
2131
|
+
"input",
|
|
2132
|
+
{
|
|
2133
|
+
value: selectedElement.fadeIn === void 0 ? "" : String(selectedElement.fadeIn),
|
|
2134
|
+
readOnly,
|
|
2135
|
+
onChange: (event) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "fadeIn", event.target.value))
|
|
2136
|
+
}
|
|
2137
|
+
)), /* @__PURE__ */ React.createElement("label", null, "Fade out", /* @__PURE__ */ React.createElement(
|
|
2138
|
+
"input",
|
|
2139
|
+
{
|
|
2140
|
+
value: selectedElement.fadeOut === void 0 ? "" : String(selectedElement.fadeOut),
|
|
2141
|
+
readOnly,
|
|
2142
|
+
onChange: (event) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "fadeOut", event.target.value))
|
|
2143
|
+
}
|
|
2144
|
+
))) : selectedElement && isElement(selectedElement) ? /* @__PURE__ */ React.createElement(
|
|
2145
|
+
VisualElementInspector,
|
|
2146
|
+
{
|
|
2147
|
+
document: document2,
|
|
2148
|
+
element: selectedElement,
|
|
2149
|
+
path: selectedPath ?? [],
|
|
2150
|
+
readOnly,
|
|
2151
|
+
commit
|
|
2152
|
+
}
|
|
2153
|
+
) : selectedLeaf ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("label", null, "Text", /* @__PURE__ */ React.createElement(
|
|
1912
2154
|
"textarea",
|
|
1913
2155
|
{
|
|
1914
2156
|
value: selectedLeaf.value,
|
|
@@ -1936,7 +2178,7 @@ function VisualSsmlEditor({
|
|
|
1936
2178
|
"button",
|
|
1937
2179
|
{
|
|
1938
2180
|
type: "button",
|
|
1939
|
-
onClick: () => onPreviewSelection(
|
|
2181
|
+
onClick: () => onPreviewSelection(buildVisualPreview(document2, selectedLeaf, selection))
|
|
1940
2182
|
},
|
|
1941
2183
|
"Preview selection"
|
|
1942
2184
|
))) : /* @__PURE__ */ React.createElement("p", null, "Select an element or text node to edit it."))));
|