ssml-builder-js 2.15.0 → 2.17.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 +11 -3
- package/dist/{chunk-FXUM45ZY.mjs → chunk-2SYLELUT.mjs} +53 -6
- package/dist/chunk-2SYLELUT.mjs.map +1 -0
- package/dist/{chunk-WXFLUCLR.mjs → chunk-5AZWURHW.mjs} +37 -6
- package/dist/chunk-5AZWURHW.mjs.map +1 -0
- package/dist/{chunk-BDY2Q2JL.mjs → chunk-SLZ7PE6W.mjs} +2 -2
- package/dist/core.d.mts +24 -1
- package/dist/core.d.ts +24 -1
- package/dist/core.js +53 -5
- package/dist/core.js.map +1 -1
- package/dist/core.mjs +3 -1
- package/dist/elements.js +35 -5
- package/dist/elements.js.map +1 -1
- package/dist/elements.mjs +2 -2
- package/dist/{index.d-8BvkB9gz.d.mts → index.d-tpKoP1jl.d.mts} +27 -1
- package/dist/{index.d-8BvkB9gz.d.ts → index.d-tpKoP1jl.d.ts} +27 -1
- package/dist/index.d.mts +123 -9
- package/dist/index.d.ts +123 -9
- package/dist/index.js +874 -180
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +787 -172
- package/dist/index.mjs.map +1 -1
- package/dist/react.d.mts +5 -2
- package/dist/react.d.ts +5 -2
- package/dist/react.js +143 -29
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +110 -26
- package/dist/react.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-FXUM45ZY.mjs.map +0 -1
- package/dist/chunk-WXFLUCLR.mjs.map +0 -1
- /package/dist/{chunk-BDY2Q2JL.mjs.map → chunk-SLZ7PE6W.mjs.map} +0 -0
package/dist/react.js
CHANGED
|
@@ -1743,6 +1743,8 @@ function InsertionPopover({
|
|
|
1743
1743
|
toolbarButtonStyle,
|
|
1744
1744
|
emptyOptionsMessage,
|
|
1745
1745
|
isReadOnly,
|
|
1746
|
+
disabled = false,
|
|
1747
|
+
disabledReason,
|
|
1746
1748
|
isOpen,
|
|
1747
1749
|
menuPosition,
|
|
1748
1750
|
menuRef,
|
|
@@ -1760,7 +1762,7 @@ function InsertionPopover({
|
|
|
1760
1762
|
role: "menuitem",
|
|
1761
1763
|
style: editorStyles.toolbarOption,
|
|
1762
1764
|
title: option.descriptions?.[language] ?? insertion.descriptions[language],
|
|
1763
|
-
disabled: isReadOnly,
|
|
1765
|
+
disabled: isReadOnly || disabled,
|
|
1764
1766
|
onMouseDown: (event) => event.preventDefault(),
|
|
1765
1767
|
onClick: () => {
|
|
1766
1768
|
if (!isReadOnly) {
|
|
@@ -1789,11 +1791,12 @@ function InsertionPopover({
|
|
|
1789
1791
|
{
|
|
1790
1792
|
type: "button",
|
|
1791
1793
|
style: toolbarButtonStyle,
|
|
1792
|
-
title: getInsertionTitle(insertion, language),
|
|
1794
|
+
title: disabledReason ?? getInsertionTitle(insertion, language),
|
|
1793
1795
|
"aria-label": insertion.labels[language],
|
|
1794
1796
|
"aria-haspopup": "menu",
|
|
1795
1797
|
"aria-expanded": isOpen,
|
|
1796
1798
|
"aria-controls": isOpen ? `ssml-editor-popover-${insertion.id}` : void 0,
|
|
1799
|
+
disabled: isReadOnly || disabled,
|
|
1797
1800
|
onClick: (event) => onToggle(event.currentTarget)
|
|
1798
1801
|
},
|
|
1799
1802
|
showToolbarIcons && /* @__PURE__ */ React.createElement("span", { style: editorStyles.toolbarIcon, "aria-hidden": "true" }, insertion.icon),
|
|
@@ -3144,6 +3147,7 @@ function tokenizeElements(source) {
|
|
|
3144
3147
|
if (parent) parent.childElementCount += 1;
|
|
3145
3148
|
const parentVoiceName = [...openElements].reverse().find((element) => element.voiceName)?.voiceName;
|
|
3146
3149
|
const tokenName = nameMatch[1];
|
|
3150
|
+
const path = parent ? [...parent.path, `${tokenName}[${childElementIndex ?? 0}]`] : [tokenName];
|
|
3147
3151
|
const tokenVoiceName = tokenName.toLowerCase() === "voice" ? attributes.get("name") : tokenName.toLowerCase() === "mstts:turn" ? attributes.get("voice") ?? parentVoiceName : parentVoiceName;
|
|
3148
3152
|
tokens.push({
|
|
3149
3153
|
attributes,
|
|
@@ -3154,12 +3158,14 @@ function tokenizeElements(source) {
|
|
|
3154
3158
|
parentName: parent?.name,
|
|
3155
3159
|
parentVoiceName,
|
|
3156
3160
|
selfClosing,
|
|
3157
|
-
start
|
|
3161
|
+
start,
|
|
3162
|
+
path
|
|
3158
3163
|
});
|
|
3159
3164
|
if (!selfClosing) {
|
|
3160
3165
|
openElements.push({
|
|
3161
3166
|
childElementCount: 0,
|
|
3162
3167
|
name: tokenName,
|
|
3168
|
+
path,
|
|
3163
3169
|
voiceName: tokenVoiceName
|
|
3164
3170
|
});
|
|
3165
3171
|
}
|
|
@@ -3172,13 +3178,14 @@ function location(source, offset) {
|
|
|
3172
3178
|
const line = before.split("\n").length;
|
|
3173
3179
|
return { line, column: before.length - (before.lastIndexOf("\n") + 1) + 1 };
|
|
3174
3180
|
}
|
|
3175
|
-
function addDiagnostic(diagnostics, source, offset, message, severity = "error", code2) {
|
|
3181
|
+
function addDiagnostic(diagnostics, source, offset, message, severity = "error", code2, metadata = {}) {
|
|
3176
3182
|
diagnostics.push({
|
|
3177
3183
|
...location(source, offset),
|
|
3178
3184
|
message,
|
|
3179
3185
|
severity,
|
|
3180
3186
|
source: "ssml-static-validator",
|
|
3181
|
-
...code2 ? { code: code2 } : {}
|
|
3187
|
+
...code2 ? { code: code2 } : {},
|
|
3188
|
+
...metadata
|
|
3182
3189
|
});
|
|
3183
3190
|
}
|
|
3184
3191
|
function isSupportedProsodyRate(value) {
|
|
@@ -3641,9 +3648,11 @@ function validateAzureSsmlStatic(ssml, options = {}) {
|
|
|
3641
3648
|
for (const token of tokens) {
|
|
3642
3649
|
const tokenName = token.name.toLowerCase();
|
|
3643
3650
|
const tokenVoiceName = tokenName === "voice" ? attr(token, "name")?.trim() : tokenName === "mstts:turn" ? attr(token, "voice")?.trim() || token.parentVoiceName : options.validateNestedVoices === false ? voiceName : token.parentVoiceName;
|
|
3651
|
+
const tokenDiagnosticStart = diagnostics.length;
|
|
3644
3652
|
validateElement(token, ssml, diagnostics, tokenVoiceName, options, voiceCatalog);
|
|
3645
3653
|
const definition = tokenVoiceName ? voiceCatalog.get(tokenVoiceName.toLowerCase()) : void 0;
|
|
3646
3654
|
validateVoiceFeatureMatrix(token, ssml, diagnostics, tokenVoiceName, definition);
|
|
3655
|
+
annotateTokenDiagnostics(diagnostics, tokenDiagnosticStart, token, options, tokenVoiceName);
|
|
3647
3656
|
if (tokenName === "voice" && options.model && definition?.models && !definition.models.some((model) => model.toLowerCase() === options.model?.toLowerCase())) {
|
|
3648
3657
|
addDiagnostic(
|
|
3649
3658
|
diagnostics,
|
|
@@ -3665,12 +3674,30 @@ function urlAttributes(token) {
|
|
|
3665
3674
|
return value === void 0 ? [] : [{ attribute, value }];
|
|
3666
3675
|
});
|
|
3667
3676
|
}
|
|
3677
|
+
function annotateTokenDiagnostics(diagnostics, startIndex, token, options, voiceName) {
|
|
3678
|
+
const attributes = [...token.attributes.keys()];
|
|
3679
|
+
for (const diagnostic of diagnostics.slice(startIndex)) {
|
|
3680
|
+
const attributeName = attributes.find(
|
|
3681
|
+
(attribute) => new RegExp(`(?:<[^> ]+\\s+|")${attribute}(?:"|>|\\s)`, "i").test(diagnostic.message)
|
|
3682
|
+
);
|
|
3683
|
+
const nodePath = options.sourceNodePath ? [...options.sourceNodePath] : [...token.path];
|
|
3684
|
+
Object.assign(diagnostic, {
|
|
3685
|
+
range: { start: token.start, end: token.end + 1 },
|
|
3686
|
+
tagName: token.name,
|
|
3687
|
+
...attributeName ? { attributeName } : {},
|
|
3688
|
+
...voiceName ? { voiceName } : {},
|
|
3689
|
+
...options.chunkIndex !== void 0 ? { chunkIndex: options.chunkIndex } : {},
|
|
3690
|
+
nodePath,
|
|
3691
|
+
targetNodePath: [...token.path]
|
|
3692
|
+
});
|
|
3693
|
+
}
|
|
3694
|
+
}
|
|
3668
3695
|
function validateAzureSsml(ssml, options = {}) {
|
|
3669
3696
|
const diagnostics = validateAzureSsmlStatic(ssml, options);
|
|
3670
3697
|
const validator = options.urlValidator ?? options.customUrlValidator;
|
|
3671
3698
|
if (!validator || typeof ssml !== "string") return diagnostics;
|
|
3672
3699
|
const runnerOptions = options.urlValidation ?? {};
|
|
3673
|
-
const boundedValidator = createAzureUrlValidatorRunner(validator, {
|
|
3700
|
+
const boundedValidator = options.urlValidatorRunner ?? createAzureUrlValidatorRunner(validator, {
|
|
3674
3701
|
...runnerOptions,
|
|
3675
3702
|
...options.urlValidatorConcurrency !== void 0 ? { concurrency: options.urlValidatorConcurrency } : {},
|
|
3676
3703
|
...options.urlValidatorTimeoutMs !== void 0 ? { timeoutMs: options.urlValidatorTimeoutMs } : {},
|
|
@@ -3695,21 +3722,25 @@ function validateAzureSsml(ssml, options = {}) {
|
|
|
3695
3722
|
const valid = typeof result === "boolean" ? result : result.valid;
|
|
3696
3723
|
if (!valid) {
|
|
3697
3724
|
const reason = typeof result === "boolean" ? void 0 : result.reason;
|
|
3725
|
+
const diagnosticStart = diagnostics.length;
|
|
3698
3726
|
addDiagnostic(
|
|
3699
3727
|
diagnostics,
|
|
3700
3728
|
ssml,
|
|
3701
3729
|
token.start,
|
|
3702
3730
|
`<${token.name} ${attribute}> was rejected by the custom URL validator${reason ? `: ${reason}` : "."}`
|
|
3703
3731
|
);
|
|
3732
|
+
annotateTokenDiagnostics(diagnostics, diagnosticStart, token, options, token.parentVoiceName);
|
|
3704
3733
|
}
|
|
3705
3734
|
} catch (error) {
|
|
3706
3735
|
const reason = error instanceof Error ? error.message : String(error);
|
|
3736
|
+
const diagnosticStart = diagnostics.length;
|
|
3707
3737
|
addDiagnostic(
|
|
3708
3738
|
diagnostics,
|
|
3709
3739
|
ssml,
|
|
3710
3740
|
token.start,
|
|
3711
3741
|
`<${token.name} ${attribute}> could not be validated by the custom URL validator: ${reason}`
|
|
3712
3742
|
);
|
|
3743
|
+
annotateTokenDiagnostics(diagnostics, diagnosticStart, token, options, token.parentVoiceName);
|
|
3713
3744
|
}
|
|
3714
3745
|
})
|
|
3715
3746
|
);
|
|
@@ -3719,7 +3750,9 @@ var AZURE_VOICE_CATALOG_METADATA = {
|
|
|
3719
3750
|
apiVersion: "2025-10-01",
|
|
3720
3751
|
generatedAt: "2026-08-28T00:00:00.000Z",
|
|
3721
3752
|
regions: [],
|
|
3722
|
-
voiceCount: AZURE_VOICE_DEFINITIONS.length
|
|
3753
|
+
voiceCount: AZURE_VOICE_DEFINITIONS.length,
|
|
3754
|
+
expiresAt: "2026-09-04T00:00:00.000Z",
|
|
3755
|
+
regionDiffs: {}
|
|
3723
3756
|
};
|
|
3724
3757
|
|
|
3725
3758
|
// packages/ssml-editor-react/src/clearSsmlDocument.ts
|
|
@@ -6856,6 +6889,21 @@ function filteredVoices(voiceCatalog, locale, region, style) {
|
|
|
6856
6889
|
return true;
|
|
6857
6890
|
});
|
|
6858
6891
|
}
|
|
6892
|
+
function voiceForPath(document2, path, voiceCatalog) {
|
|
6893
|
+
if (!path || !voiceCatalog) return void 0;
|
|
6894
|
+
const ancestors = getElementAncestors(document2.children ?? [], path);
|
|
6895
|
+
const node = getNodeAtPath(document2.children ?? [], path);
|
|
6896
|
+
const candidates = [...ancestors, ...node && isElement(node) ? [node] : []].reverse();
|
|
6897
|
+
const voiceName = candidates.find((element) => element.type === "voice")?.name ?? candidates.find((element) => element.type === "mstts:turn")?.voice;
|
|
6898
|
+
return voiceName ? voiceCatalog.find((voice) => voice.name.toLowerCase() === voiceName.toLowerCase()) : void 0;
|
|
6899
|
+
}
|
|
6900
|
+
function isVisualTagSupported(tagName, voice) {
|
|
6901
|
+
if (!voice) return true;
|
|
6902
|
+
const tag = tagName.toLowerCase();
|
|
6903
|
+
const unsupported = new Set((voice.unsupportedTags ?? []).map((candidate) => candidate.toLowerCase()));
|
|
6904
|
+
const supported = voice.supportedTags?.map((candidate) => candidate.toLowerCase());
|
|
6905
|
+
return !unsupported.has(tag) && (supported === void 0 || supported.includes(tag));
|
|
6906
|
+
}
|
|
6859
6907
|
function VoiceCapabilityMatrix({
|
|
6860
6908
|
voice,
|
|
6861
6909
|
locale
|
|
@@ -6930,10 +6978,12 @@ function VisualElementInspector({
|
|
|
6930
6978
|
voiceLocale,
|
|
6931
6979
|
voiceRegion,
|
|
6932
6980
|
voiceStyle,
|
|
6933
|
-
diagnostics
|
|
6981
|
+
diagnostics,
|
|
6982
|
+
voiceDefinition
|
|
6934
6983
|
}) {
|
|
6984
|
+
const tagSupported = isVisualTagSupported(elementLabel(element), voiceDefinition);
|
|
6935
6985
|
if (customInspector) {
|
|
6936
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, customInspector({ document: document2, element, path, readOnly, onChange: commit, locale }));
|
|
6986
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, customInspector({ document: document2, element, path, readOnly: readOnly || !tagSupported, onChange: commit, locale }));
|
|
6937
6987
|
}
|
|
6938
6988
|
const fields = getElementFields(element);
|
|
6939
6989
|
const availableVoices = filteredVoices(voiceCatalog, voiceLocale, voiceRegion, voiceStyle);
|
|
@@ -6941,7 +6991,7 @@ function VisualElementInspector({
|
|
|
6941
6991
|
(voice) => voice.name === (element.type === "voice" ? element.name : void 0)
|
|
6942
6992
|
);
|
|
6943
6993
|
const renderSelector = renderVoiceSelector ?? DefaultVoiceSelector;
|
|
6944
|
-
return /* @__PURE__ */ React.createElement("fieldset",
|
|
6994
|
+
return /* @__PURE__ */ React.createElement("fieldset", { disabled: readOnly || !tagSupported, style: !tagSupported ? { border: "1px solid #c0392b" } : void 0 }, /* @__PURE__ */ React.createElement("legend", null, `<${elementLabel(element)}>`, " ", /* @__PURE__ */ React.createElement(WarningBadge, { messages: elementWarnings(diagnostics, 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) => {
|
|
6945
6995
|
const value = element[field.key];
|
|
6946
6996
|
const inputValue = value === void 0 ? "" : String(value);
|
|
6947
6997
|
const inputId = `ssml-visual-${field.key}`;
|
|
@@ -6990,6 +7040,7 @@ function VisualElementInspector({
|
|
|
6990
7040
|
{
|
|
6991
7041
|
id: inputId,
|
|
6992
7042
|
value: inputValue,
|
|
7043
|
+
disabled: readOnly || !tagSupported,
|
|
6993
7044
|
readOnly,
|
|
6994
7045
|
onChange: (event) => commit(updateOptionalElementProperty(document2, path, field.key, event.target.value))
|
|
6995
7046
|
}
|
|
@@ -6998,6 +7049,7 @@ function VisualElementInspector({
|
|
|
6998
7049
|
{
|
|
6999
7050
|
id: inputId,
|
|
7000
7051
|
value: inputValue,
|
|
7052
|
+
disabled: readOnly || !tagSupported,
|
|
7001
7053
|
readOnly,
|
|
7002
7054
|
onChange: (event) => commit(updateOptionalElementProperty(document2, path, field.key, event.target.value))
|
|
7003
7055
|
}
|
|
@@ -7061,18 +7113,27 @@ function TreeNode({
|
|
|
7061
7113
|
}) {
|
|
7062
7114
|
if (!isElement(node)) return null;
|
|
7063
7115
|
const name = elementLabel(node);
|
|
7116
|
+
const messages = getWarnings(node);
|
|
7064
7117
|
const isSelected = selectedPath?.join(".") === path.join(".");
|
|
7065
|
-
return /* @__PURE__ */ React.createElement(
|
|
7066
|
-
|
|
7118
|
+
return /* @__PURE__ */ React.createElement(
|
|
7119
|
+
"li",
|
|
7067
7120
|
{
|
|
7068
|
-
|
|
7069
|
-
|
|
7070
|
-
|
|
7071
|
-
|
|
7072
|
-
|
|
7073
|
-
|
|
7074
|
-
|
|
7075
|
-
|
|
7121
|
+
"data-ssml-diagnostic": messages.length > 0 ? "error" : void 0,
|
|
7122
|
+
style: messages.length > 0 ? { border: "1px solid #c0392b", borderRadius: "0.25rem" } : void 0
|
|
7123
|
+
},
|
|
7124
|
+
/* @__PURE__ */ React.createElement("button", { type: "button", "aria-current": isSelected ? "true" : void 0, onClick: () => onSelect(path) }, `<${name}>`, /* @__PURE__ */ React.createElement(WarningBadge, { messages: getWarnings(node) })),
|
|
7125
|
+
(node.children ?? []).length > 0 && /* @__PURE__ */ React.createElement("ul", null, node.children?.map((child, index) => /* @__PURE__ */ React.createElement(
|
|
7126
|
+
TreeNode,
|
|
7127
|
+
{
|
|
7128
|
+
key: `${path.join(".")}/${isElement(child) ? elementLabel(child) : JSON.stringify(child)}`,
|
|
7129
|
+
node: child,
|
|
7130
|
+
path: [...path, index],
|
|
7131
|
+
selectedPath,
|
|
7132
|
+
onSelect,
|
|
7133
|
+
getWarnings
|
|
7134
|
+
}
|
|
7135
|
+
)))
|
|
7136
|
+
);
|
|
7076
7137
|
}
|
|
7077
7138
|
function VisualSsmlEditor({
|
|
7078
7139
|
document: document2,
|
|
@@ -7087,13 +7148,16 @@ function VisualSsmlEditor({
|
|
|
7087
7148
|
voiceRegion,
|
|
7088
7149
|
voiceStyle,
|
|
7089
7150
|
voiceModel,
|
|
7090
|
-
model
|
|
7151
|
+
model,
|
|
7152
|
+
voiceCatalogMetadata
|
|
7091
7153
|
}) {
|
|
7092
7154
|
const [selectedPath, setSelectedPath] = (0, import_react3.useState)(null);
|
|
7093
7155
|
const [selection, setSelection] = (0, import_react3.useState)({ start: 0, end: 0 });
|
|
7094
7156
|
const textLeaves = (0, import_react3.useMemo)(() => collectTextLeaves(document2.children ?? []), [document2]);
|
|
7095
7157
|
const selectedLeaf = textLeaves.find((leaf) => leaf.path.join(".") === selectedPath?.join(".")) ?? textLeaves[0];
|
|
7096
7158
|
const selectedElement = selectedPath ? getNodeAtPath(document2.children ?? [], selectedPath) : void 0;
|
|
7159
|
+
const activeVoice = voiceForPath(document2, selectedPath ?? selectedLeaf?.path, voiceCatalog);
|
|
7160
|
+
const staleCatalog = voiceCatalogMetadata?.expiresAt ? Date.parse(voiceCatalogMetadata.expiresAt) <= Date.now() : false;
|
|
7097
7161
|
const selectedCustomInspector = selectedElement && isElement(selectedElement) ? customInspectors?.[elementLabel(selectedElement)] ?? customInspectors?.[selectedElement.type] : void 0;
|
|
7098
7162
|
const validationOptions = (0, import_react3.useMemo)(
|
|
7099
7163
|
() => ({
|
|
@@ -7123,7 +7187,7 @@ function VisualSsmlEditor({
|
|
|
7123
7187
|
const end = selection.start === selection.end ? selectedLeaf.value.length : selection.end;
|
|
7124
7188
|
commit(wrapTextAtPath(document2, selectedLeaf.path, start, end, tag, attributes));
|
|
7125
7189
|
};
|
|
7126
|
-
return /* @__PURE__ */ React.createElement("div", { className: "ssml-editor-visual", "data-ssml-editor-visual": "" }, /* @__PURE__ */ React.createElement("fieldset", { className: "ssml-editor-visual-breadcrumb" }, /* @__PURE__ */ React.createElement("legend", null, "SSML structure breadcrumb"), /* @__PURE__ */ React.createElement("span", null, "<speak>"), selectedLeaf?.ancestors.map((ancestor, index) => /* @__PURE__ */ React.createElement("span", { key: selectedLeaf?.ancestors.slice(0, index + 1).join("/") }, " / <", ancestor, ">")), selectedPath && /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => setSelectedPath(null) }, "Clear parent")), diagnostics.length > 0 && /* @__PURE__ */ React.createElement("div", { role: "alert", "aria-invalid": "true", className: "ssml-editor-visual-errors" }, diagnostics.map((diagnostic) => /* @__PURE__ */ React.createElement("div", { key: `${diagnostic.code}-${diagnostic.message}` }, diagnostic.message))), /* @__PURE__ */ React.createElement("div", { className: "ssml-editor-visual-layout" }, /* @__PURE__ */ React.createElement("nav", { "aria-label": "SSML structure tree", className: "ssml-editor-visual-tree" }, /* @__PURE__ */ React.createElement("strong", null, "Structure"), /* @__PURE__ */ React.createElement("ul", null, document2.children?.map((node, index) => /* @__PURE__ */ React.createElement(
|
|
7190
|
+
return /* @__PURE__ */ React.createElement("div", { className: "ssml-editor-visual", "data-ssml-editor-visual": "" }, /* @__PURE__ */ React.createElement("fieldset", { className: "ssml-editor-visual-breadcrumb" }, /* @__PURE__ */ React.createElement("legend", null, "SSML structure breadcrumb"), /* @__PURE__ */ React.createElement("span", null, "<speak>"), selectedLeaf?.ancestors.map((ancestor, index) => /* @__PURE__ */ React.createElement("span", { key: selectedLeaf?.ancestors.slice(0, index + 1).join("/") }, " / <", ancestor, ">")), selectedPath && /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => setSelectedPath(null) }, "Clear parent")), diagnostics.length > 0 && /* @__PURE__ */ React.createElement("div", { role: "alert", "aria-invalid": "true", className: "ssml-editor-visual-errors" }, diagnostics.map((diagnostic) => /* @__PURE__ */ React.createElement("div", { key: `${diagnostic.code}-${diagnostic.message}` }, diagnostic.message))), staleCatalog && /* @__PURE__ */ React.createElement("div", { role: "status", className: "ssml-editor-visual-catalog-warning" }, locale === "ja" ? "\u97F3\u58F0\u30AB\u30BF\u30ED\u30B0\u304C\u53E4\u304F\u306A\u3063\u3066\u3044\u307E\u3059\u3002\u66F4\u65B0\u3057\u3066\u304F\u3060\u3055\u3044\u3002" : "The voice catalog is stale. Refresh it before synthesizing."), /* @__PURE__ */ React.createElement("div", { className: "ssml-editor-visual-layout" }, /* @__PURE__ */ React.createElement("nav", { "aria-label": "SSML structure tree", className: "ssml-editor-visual-tree" }, /* @__PURE__ */ React.createElement("strong", null, "Structure"), /* @__PURE__ */ React.createElement("ul", null, document2.children?.map((node, index) => /* @__PURE__ */ React.createElement(
|
|
7127
7191
|
TreeNode,
|
|
7128
7192
|
{
|
|
7129
7193
|
key: isElement(node) ? elementLabel(node) : JSON.stringify(node),
|
|
@@ -7137,14 +7201,14 @@ function VisualSsmlEditor({
|
|
|
7137
7201
|
document: document2,
|
|
7138
7202
|
element: selectedElement,
|
|
7139
7203
|
path: selectedPath ?? [],
|
|
7140
|
-
readOnly,
|
|
7204
|
+
readOnly: readOnly || !isVisualTagSupported(elementLabel(selectedElement), activeVoice),
|
|
7141
7205
|
onChange: commit,
|
|
7142
7206
|
locale
|
|
7143
7207
|
}) : selectedElement && isElement(selectedElement) && selectedElement.type === "mstts:dialog" ? /* @__PURE__ */ React.createElement("fieldset", null, /* @__PURE__ */ React.createElement("legend", null, localizedElementLabel(selectedElement, locale)), /* @__PURE__ */ React.createElement("p", null, SSML_ELEMENT_COPY[locale][elementLabel(selectedElement)]?.description), /* @__PURE__ */ React.createElement(
|
|
7144
7208
|
"button",
|
|
7145
7209
|
{
|
|
7146
7210
|
type: "button",
|
|
7147
|
-
disabled: readOnly,
|
|
7211
|
+
disabled: readOnly || !isVisualTagSupported("mstts:dialog", activeVoice),
|
|
7148
7212
|
onClick: () => commit(addDialogTurn(document2, selectedPath ?? []))
|
|
7149
7213
|
},
|
|
7150
7214
|
locale === "ja" ? "\u30BF\u30FC\u30F3\u3092\u8FFD\u52A0" : "Add turn"
|
|
@@ -7226,7 +7290,8 @@ function VisualSsmlEditor({
|
|
|
7226
7290
|
voiceLocale,
|
|
7227
7291
|
voiceRegion,
|
|
7228
7292
|
voiceStyle,
|
|
7229
|
-
diagnostics
|
|
7293
|
+
diagnostics,
|
|
7294
|
+
voiceDefinition: activeVoice
|
|
7230
7295
|
}
|
|
7231
7296
|
) : selectedLeaf ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("label", null, "Text", /* @__PURE__ */ React.createElement(
|
|
7232
7297
|
"textarea",
|
|
@@ -7236,19 +7301,43 @@ function VisualSsmlEditor({
|
|
|
7236
7301
|
onSelect: (event) => setSelection({ start: event.currentTarget.selectionStart, end: event.currentTarget.selectionEnd }),
|
|
7237
7302
|
onChange: (event) => commit(updateTextAtPath(document2, selectedLeaf.path, event.target.value))
|
|
7238
7303
|
}
|
|
7239
|
-
)), /* @__PURE__ */ React.createElement("fieldset", { className: "ssml-editor-visual-actions" }, /* @__PURE__ */ React.createElement("legend", null, "Apply SSML formatting"), /* @__PURE__ */ React.createElement(
|
|
7304
|
+
)), /* @__PURE__ */ React.createElement("fieldset", { className: "ssml-editor-visual-actions" }, /* @__PURE__ */ React.createElement("legend", null, "Apply SSML formatting"), /* @__PURE__ */ React.createElement(
|
|
7240
7305
|
"button",
|
|
7241
7306
|
{
|
|
7242
7307
|
type: "button",
|
|
7243
|
-
disabled: readOnly,
|
|
7308
|
+
disabled: readOnly || !isVisualTagSupported("prosody", activeVoice),
|
|
7309
|
+
onClick: () => applyWrapper("prosody", { rate: "slow" })
|
|
7310
|
+
},
|
|
7311
|
+
"Rate"
|
|
7312
|
+
), /* @__PURE__ */ React.createElement(
|
|
7313
|
+
"button",
|
|
7314
|
+
{
|
|
7315
|
+
type: "button",
|
|
7316
|
+
disabled: readOnly || !isVisualTagSupported("prosody", activeVoice),
|
|
7317
|
+
onClick: () => applyWrapper("prosody", { pitch: "high" })
|
|
7318
|
+
},
|
|
7319
|
+
"Pitch"
|
|
7320
|
+
), /* @__PURE__ */ React.createElement(
|
|
7321
|
+
"button",
|
|
7322
|
+
{
|
|
7323
|
+
type: "button",
|
|
7324
|
+
disabled: readOnly || !isVisualTagSupported("mstts:express-as", activeVoice),
|
|
7244
7325
|
onClick: () => applyWrapper("mstts:express-as", { style: "cheerful" })
|
|
7245
7326
|
},
|
|
7246
7327
|
"Emotion"
|
|
7247
|
-
), /* @__PURE__ */ React.createElement(
|
|
7328
|
+
), /* @__PURE__ */ React.createElement(
|
|
7248
7329
|
"button",
|
|
7249
7330
|
{
|
|
7250
7331
|
type: "button",
|
|
7251
|
-
disabled: readOnly,
|
|
7332
|
+
disabled: readOnly || !isVisualTagSupported("break", activeVoice),
|
|
7333
|
+
onClick: () => applyWrapper("break", { time: "500ms" })
|
|
7334
|
+
},
|
|
7335
|
+
"Pause"
|
|
7336
|
+
), /* @__PURE__ */ React.createElement(
|
|
7337
|
+
"button",
|
|
7338
|
+
{
|
|
7339
|
+
type: "button",
|
|
7340
|
+
disabled: readOnly || !isVisualTagSupported("phoneme", activeVoice),
|
|
7252
7341
|
onClick: () => applyWrapper("phoneme", { alphabet: "ipa", ph: selectedLeaf.value })
|
|
7253
7342
|
},
|
|
7254
7343
|
"Pronunciation"
|
|
@@ -7267,6 +7356,23 @@ var UNGROUPED_TOOLBAR_GROUP = "__ssml-editor-ungrouped__";
|
|
|
7267
7356
|
var TIMING_POPOVER_TAGS = /* @__PURE__ */ new Set(["break", "mstts:silence", "mstts:audioduration"]);
|
|
7268
7357
|
var PROSODY_POPOVER_TAGS = /* @__PURE__ */ new Set(["prosody", "mstts:express-as", "voice", "emphasis"]);
|
|
7269
7358
|
var TEXT_POPOVER_TAGS = /* @__PURE__ */ new Set(["sub", "say-as", "phoneme", "w", "lang"]);
|
|
7359
|
+
function isVoiceTagSupported(tagName, voice) {
|
|
7360
|
+
if (!tagName || !voice) return true;
|
|
7361
|
+
const tag = tagName.toLowerCase();
|
|
7362
|
+
const unsupported = new Set((voice.unsupportedTags ?? []).map((candidate) => candidate.toLowerCase()));
|
|
7363
|
+
const supported = voice.supportedTags?.map((candidate) => candidate.toLowerCase());
|
|
7364
|
+
return !unsupported.has(tag) && (supported === void 0 || supported.includes(tag));
|
|
7365
|
+
}
|
|
7366
|
+
function findFirstVoiceName(nodes) {
|
|
7367
|
+
for (const node of nodes) {
|
|
7368
|
+
if (typeof node === "string" || node.type === "text") continue;
|
|
7369
|
+
if (node.type === "voice" && node.name) return node.name;
|
|
7370
|
+
if (node.type === "mstts:turn" && node.voice) return node.voice;
|
|
7371
|
+
const nested = findFirstVoiceName(node.children ?? []);
|
|
7372
|
+
if (nested) return nested;
|
|
7373
|
+
}
|
|
7374
|
+
return void 0;
|
|
7375
|
+
}
|
|
7270
7376
|
function localizedText(value) {
|
|
7271
7377
|
return { ja: value, en: value };
|
|
7272
7378
|
}
|
|
@@ -7852,6 +7958,7 @@ var SsmlEditor = (0, import_react4.forwardRef)(function SsmlEditor2({
|
|
|
7852
7958
|
voiceStyle,
|
|
7853
7959
|
voiceModel,
|
|
7854
7960
|
model,
|
|
7961
|
+
voiceCatalogMetadata,
|
|
7855
7962
|
emotionStyles,
|
|
7856
7963
|
className,
|
|
7857
7964
|
style,
|
|
@@ -8015,6 +8122,10 @@ var SsmlEditor = (0, import_react4.forwardRef)(function SsmlEditor2({
|
|
|
8015
8122
|
const isProsodyPopover = insertion.tagName !== void 0 && PROSODY_POPOVER_TAGS.has(insertion.tagName);
|
|
8016
8123
|
const isTextPopover = insertion.tagName !== void 0 && TEXT_POPOVER_TAGS.has(insertion.tagName);
|
|
8017
8124
|
const insertionButtonStyle = insertion.tagName && activeTags.has(insertion.tagName.toLowerCase()) ? { ...toolbarButtonStyle, ...editorStyles.toolbarButtonActive } : toolbarButtonStyle;
|
|
8125
|
+
const activeVoiceName = popoverVoiceName ?? findFirstVoiceName(draftDocument.children ?? []);
|
|
8126
|
+
const activeVoice = voiceCatalog?.find((voice) => voice.name.toLowerCase() === activeVoiceName?.toLowerCase());
|
|
8127
|
+
const insertionDisabled = !isVoiceTagSupported(insertion.tagName, activeVoice);
|
|
8128
|
+
const disabledReason = insertionDisabled ? language === "ja" ? `\u9078\u629E\u4E2D\u306E\u97F3\u58F0\u306F <${insertion.tagName}> \u306B\u5BFE\u5FDC\u3057\u3066\u3044\u307E\u305B\u3093\u3002` : `The selected voice does not support <${insertion.tagName}>.` : void 0;
|
|
8018
8129
|
const props = {
|
|
8019
8130
|
language,
|
|
8020
8131
|
isDarkTheme,
|
|
@@ -8023,6 +8134,8 @@ var SsmlEditor = (0, import_react4.forwardRef)(function SsmlEditor2({
|
|
|
8023
8134
|
toolbarButtonStyle: insertionButtonStyle,
|
|
8024
8135
|
emptyOptionsMessage: copy.noAvailableOptions,
|
|
8025
8136
|
isReadOnly,
|
|
8137
|
+
disabled: insertionDisabled,
|
|
8138
|
+
disabledReason,
|
|
8026
8139
|
openPopoverId,
|
|
8027
8140
|
menuPosition: popoverPosition,
|
|
8028
8141
|
menuRef: setPopoverMenuRef,
|
|
@@ -8251,6 +8364,7 @@ var SsmlEditor = (0, import_react4.forwardRef)(function SsmlEditor2({
|
|
|
8251
8364
|
voiceLocale,
|
|
8252
8365
|
voiceRegion,
|
|
8253
8366
|
voiceStyle,
|
|
8367
|
+
voiceCatalogMetadata,
|
|
8254
8368
|
voiceModel,
|
|
8255
8369
|
model
|
|
8256
8370
|
}
|