@ssml-builder-js/ssml-editor-react 2.14.0 → 2.15.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.d.mts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +84 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +84 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/SsmlEditor.tsx +8 -0
- package/src/components/VisualSsmlEditor.tsx +87 -2
- package/test/ui-components.test.tsx +28 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @ssml-builder-js/ssml-editor-react
|
|
2
2
|
|
|
3
|
+
## 2.15.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Add strict audio merge formats, discriminated synthesis errors, per-event source mappings, abortable URL validation, external audio muxers, and live Visual Editor voice capability warnings.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies
|
|
12
|
+
- @ssml-builder-js/ssml-core@2.15.0
|
|
13
|
+
|
|
3
14
|
## 2.14.0
|
|
4
15
|
|
|
5
16
|
### Minor Changes
|
package/dist/index.d.mts
CHANGED
|
@@ -82,6 +82,7 @@ interface VisualVoiceCatalogEntry {
|
|
|
82
82
|
styles?: readonly string[];
|
|
83
83
|
supportedTags?: readonly string[];
|
|
84
84
|
unsupportedTags?: readonly string[];
|
|
85
|
+
models?: readonly string[];
|
|
85
86
|
preview?: boolean;
|
|
86
87
|
status?: "ga" | "preview" | "deprecated";
|
|
87
88
|
}
|
|
@@ -114,8 +115,12 @@ interface VisualSsmlEditorProps {
|
|
|
114
115
|
voiceLocale?: string;
|
|
115
116
|
voiceRegion?: string;
|
|
116
117
|
voiceStyle?: string;
|
|
118
|
+
/** Selected Azure voice model used for live capability validation. */
|
|
119
|
+
voiceModel?: string;
|
|
120
|
+
/** Alias for voiceModel. */
|
|
121
|
+
model?: string;
|
|
117
122
|
}
|
|
118
|
-
declare function VisualSsmlEditor({ document, readOnly, onChange, onPreviewSelection, locale, customInspectors, renderVoiceSelector, voiceCatalog, voiceLocale, voiceRegion, voiceStyle, }: VisualSsmlEditorProps): ReactElement;
|
|
123
|
+
declare function VisualSsmlEditor({ document, readOnly, onChange, onPreviewSelection, locale, customInspectors, renderVoiceSelector, voiceCatalog, voiceLocale, voiceRegion, voiceStyle, voiceModel, model, }: VisualSsmlEditorProps): ReactElement;
|
|
119
124
|
|
|
120
125
|
type SsmlEditorInsertionMode = "insert" | "wrap";
|
|
121
126
|
interface SsmlEditorInsertionOption {
|
|
@@ -301,6 +306,10 @@ interface SsmlEditorProps {
|
|
|
301
306
|
voiceRegion?: string;
|
|
302
307
|
/** Optional style filter for the visual voice selector. */
|
|
303
308
|
voiceStyle?: string;
|
|
309
|
+
/** Selected Azure voice model used for live capability validation. */
|
|
310
|
+
voiceModel?: string;
|
|
311
|
+
/** Alias for voiceModel. */
|
|
312
|
+
model?: string;
|
|
304
313
|
/** Candidate style values shown by the built-in emotion insertion. */
|
|
305
314
|
emotionStyles?: readonly string[];
|
|
306
315
|
/** Class name applied to the editor container. */
|
package/dist/index.d.ts
CHANGED
|
@@ -82,6 +82,7 @@ interface VisualVoiceCatalogEntry {
|
|
|
82
82
|
styles?: readonly string[];
|
|
83
83
|
supportedTags?: readonly string[];
|
|
84
84
|
unsupportedTags?: readonly string[];
|
|
85
|
+
models?: readonly string[];
|
|
85
86
|
preview?: boolean;
|
|
86
87
|
status?: "ga" | "preview" | "deprecated";
|
|
87
88
|
}
|
|
@@ -114,8 +115,12 @@ interface VisualSsmlEditorProps {
|
|
|
114
115
|
voiceLocale?: string;
|
|
115
116
|
voiceRegion?: string;
|
|
116
117
|
voiceStyle?: string;
|
|
118
|
+
/** Selected Azure voice model used for live capability validation. */
|
|
119
|
+
voiceModel?: string;
|
|
120
|
+
/** Alias for voiceModel. */
|
|
121
|
+
model?: string;
|
|
117
122
|
}
|
|
118
|
-
declare function VisualSsmlEditor({ document, readOnly, onChange, onPreviewSelection, locale, customInspectors, renderVoiceSelector, voiceCatalog, voiceLocale, voiceRegion, voiceStyle, }: VisualSsmlEditorProps): ReactElement;
|
|
123
|
+
declare function VisualSsmlEditor({ document, readOnly, onChange, onPreviewSelection, locale, customInspectors, renderVoiceSelector, voiceCatalog, voiceLocale, voiceRegion, voiceStyle, voiceModel, model, }: VisualSsmlEditorProps): ReactElement;
|
|
119
124
|
|
|
120
125
|
type SsmlEditorInsertionMode = "insert" | "wrap";
|
|
121
126
|
interface SsmlEditorInsertionOption {
|
|
@@ -301,6 +306,10 @@ interface SsmlEditorProps {
|
|
|
301
306
|
voiceRegion?: string;
|
|
302
307
|
/** Optional style filter for the visual voice selector. */
|
|
303
308
|
voiceStyle?: string;
|
|
309
|
+
/** Selected Azure voice model used for live capability validation. */
|
|
310
|
+
voiceModel?: string;
|
|
311
|
+
/** Alias for voiceModel. */
|
|
312
|
+
model?: string;
|
|
304
313
|
/** Candidate style values shown by the built-in emotion insertion. */
|
|
305
314
|
emotionStyles?: readonly string[];
|
|
306
315
|
/** Class name applied to the editor container. */
|
package/dist/index.js
CHANGED
|
@@ -5074,6 +5074,40 @@ function VoiceCapabilityMatrix({
|
|
|
5074
5074
|
] })
|
|
5075
5075
|
] });
|
|
5076
5076
|
}
|
|
5077
|
+
function diagnosticMatchesElement(diagnostic, element) {
|
|
5078
|
+
const name = elementLabel(element).toLowerCase().replace("expressas", "mstts:express-as");
|
|
5079
|
+
if (diagnostic.code === "azure-unsupported-style") return name === "mstts:express-as" || name === "express-as";
|
|
5080
|
+
if (diagnostic.code === "azure-unsupported-tag-for-voice") {
|
|
5081
|
+
return diagnostic.message.toLowerCase().includes(`<${name}>`) || diagnostic.message.toLowerCase().includes(`<${name.replace("mstts:", "")}>`);
|
|
5082
|
+
}
|
|
5083
|
+
if (diagnostic.code === "azure-locale-mismatch" || diagnostic.code === "azure-unsupported-model-for-voice")
|
|
5084
|
+
return name === "voice" || name === "mstts:turn";
|
|
5085
|
+
return false;
|
|
5086
|
+
}
|
|
5087
|
+
function elementWarnings(diagnostics, element) {
|
|
5088
|
+
return diagnostics.filter((diagnostic) => diagnosticMatchesElement(diagnostic, element));
|
|
5089
|
+
}
|
|
5090
|
+
function fieldWarnings(diagnostics, element, field) {
|
|
5091
|
+
return elementWarnings(diagnostics, element).filter((diagnostic) => {
|
|
5092
|
+
if (diagnostic.code === "azure-unsupported-style") return field.key === "style";
|
|
5093
|
+
return field.key === "name" || field.key === "voice";
|
|
5094
|
+
});
|
|
5095
|
+
}
|
|
5096
|
+
function WarningBadge({ messages }) {
|
|
5097
|
+
if (messages.length === 0) return null;
|
|
5098
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
5099
|
+
"span",
|
|
5100
|
+
{
|
|
5101
|
+
role: "status",
|
|
5102
|
+
"aria-label": messages.map((message) => message.message).join(" "),
|
|
5103
|
+
title: messages.map((message) => message.message).join(" "),
|
|
5104
|
+
"data-ssml-warning-badge": "",
|
|
5105
|
+
"data-testid": "ssml-editor-warning-badge",
|
|
5106
|
+
className: "ssml-editor-warning-badge",
|
|
5107
|
+
children: "\u26A0"
|
|
5108
|
+
}
|
|
5109
|
+
);
|
|
5110
|
+
}
|
|
5077
5111
|
function DefaultVoiceSelector({
|
|
5078
5112
|
value,
|
|
5079
5113
|
voices,
|
|
@@ -5114,7 +5148,8 @@ function VisualElementInspector({
|
|
|
5114
5148
|
voiceCatalog,
|
|
5115
5149
|
voiceLocale,
|
|
5116
5150
|
voiceRegion,
|
|
5117
|
-
voiceStyle
|
|
5151
|
+
voiceStyle,
|
|
5152
|
+
diagnostics
|
|
5118
5153
|
}) {
|
|
5119
5154
|
if (customInspector) {
|
|
5120
5155
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: customInspector({ document: document2, element, path, readOnly, onChange: commit, locale }) });
|
|
@@ -5126,7 +5161,11 @@ function VisualElementInspector({
|
|
|
5126
5161
|
);
|
|
5127
5162
|
const renderSelector = renderVoiceSelector ?? DefaultVoiceSelector;
|
|
5128
5163
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("fieldset", { children: [
|
|
5129
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.
|
|
5164
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("legend", { children: [
|
|
5165
|
+
`<${elementLabel(element)}>`,
|
|
5166
|
+
" ",
|
|
5167
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(WarningBadge, { messages: elementWarnings(diagnostics, element) })
|
|
5168
|
+
] }),
|
|
5130
5169
|
fields.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: "This element is preserved in the visual tree. Edit its attributes in Code mode." }) : fields.map((field) => {
|
|
5131
5170
|
const value = element[field.key];
|
|
5132
5171
|
const inputValue = value === void 0 ? "" : String(value);
|
|
@@ -5166,6 +5205,7 @@ function VisualElementInspector({
|
|
|
5166
5205
|
url: "\u97F3\u58F0\u5909\u63DB URL",
|
|
5167
5206
|
profile: "\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB"
|
|
5168
5207
|
}[field.key] ?? field.label : field.label,
|
|
5208
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(WarningBadge, { messages: fieldWarnings(diagnostics, element, field) }),
|
|
5169
5209
|
field.key === "name" && elementLabel(element) === "voice" && voiceCatalog ? renderSelector({
|
|
5170
5210
|
value: inputValue,
|
|
5171
5211
|
voices: availableVoices,
|
|
@@ -5246,20 +5286,25 @@ function TreeNode({
|
|
|
5246
5286
|
node,
|
|
5247
5287
|
path,
|
|
5248
5288
|
selectedPath,
|
|
5249
|
-
onSelect
|
|
5289
|
+
onSelect,
|
|
5290
|
+
getWarnings
|
|
5250
5291
|
}) {
|
|
5251
5292
|
if (!isElement(node)) return null;
|
|
5252
5293
|
const name = elementLabel(node);
|
|
5253
5294
|
const isSelected = selectedPath?.join(".") === path.join(".");
|
|
5254
5295
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("li", { children: [
|
|
5255
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.
|
|
5296
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("button", { type: "button", "aria-current": isSelected ? "true" : void 0, onClick: () => onSelect(path), children: [
|
|
5297
|
+
`<${name}>`,
|
|
5298
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(WarningBadge, { messages: getWarnings(node) })
|
|
5299
|
+
] }),
|
|
5256
5300
|
(node.children ?? []).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ul", { children: node.children?.map((child, index) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
5257
5301
|
TreeNode,
|
|
5258
5302
|
{
|
|
5259
5303
|
node: child,
|
|
5260
5304
|
path: [...path, index],
|
|
5261
5305
|
selectedPath,
|
|
5262
|
-
onSelect
|
|
5306
|
+
onSelect,
|
|
5307
|
+
getWarnings
|
|
5263
5308
|
},
|
|
5264
5309
|
`${path.join(".")}/${isElement(child) ? elementLabel(child) : JSON.stringify(child)}`
|
|
5265
5310
|
)) })
|
|
@@ -5276,7 +5321,9 @@ function VisualSsmlEditor({
|
|
|
5276
5321
|
voiceCatalog,
|
|
5277
5322
|
voiceLocale,
|
|
5278
5323
|
voiceRegion,
|
|
5279
|
-
voiceStyle
|
|
5324
|
+
voiceStyle,
|
|
5325
|
+
voiceModel,
|
|
5326
|
+
model
|
|
5280
5327
|
}) {
|
|
5281
5328
|
const [selectedPath, setSelectedPath] = (0, import_react3.useState)(null);
|
|
5282
5329
|
const [selection, setSelection] = (0, import_react3.useState)({ start: 0, end: 0 });
|
|
@@ -5284,7 +5331,27 @@ function VisualSsmlEditor({
|
|
|
5284
5331
|
const selectedLeaf = textLeaves.find((leaf) => leaf.path.join(".") === selectedPath?.join(".")) ?? textLeaves[0];
|
|
5285
5332
|
const selectedElement = selectedPath ? getNodeAtPath(document2.children ?? [], selectedPath) : void 0;
|
|
5286
5333
|
const selectedCustomInspector = selectedElement && isElement(selectedElement) ? customInspectors?.[elementLabel(selectedElement)] ?? customInspectors?.[selectedElement.type] : void 0;
|
|
5287
|
-
const
|
|
5334
|
+
const validationOptions = (0, import_react3.useMemo)(
|
|
5335
|
+
() => ({
|
|
5336
|
+
model: voiceModel ?? model,
|
|
5337
|
+
customVoiceDefinitions: voiceCatalog?.map((voice) => ({
|
|
5338
|
+
name: voice.name,
|
|
5339
|
+
locale: voice.locale,
|
|
5340
|
+
regions: voice.regions ?? (voice.region ? [voice.region] : void 0),
|
|
5341
|
+
styles: voice.styles,
|
|
5342
|
+
supportedTags: voice.supportedTags,
|
|
5343
|
+
unsupportedTags: voice.unsupportedTags,
|
|
5344
|
+
models: voice.models,
|
|
5345
|
+
status: voice.status ?? (voice.preview ? "preview" : "ga")
|
|
5346
|
+
}))
|
|
5347
|
+
}),
|
|
5348
|
+
[model, voiceCatalog, voiceModel]
|
|
5349
|
+
);
|
|
5350
|
+
const diagnostics = (0, import_react3.useMemo)(
|
|
5351
|
+
() => (0, import_ssml_core4.validateAzureSsml)((0, import_ssml_core4.buildSsml)(document2), validationOptions),
|
|
5352
|
+
[document2, validationOptions]
|
|
5353
|
+
);
|
|
5354
|
+
const getWarnings = (element) => elementWarnings(diagnostics, element);
|
|
5288
5355
|
const commit = (nextDocument) => onChange?.(nextDocument);
|
|
5289
5356
|
const applyWrapper = (tag, attributes) => {
|
|
5290
5357
|
if (readOnly || !selectedLeaf) return;
|
|
@@ -5313,7 +5380,8 @@ function VisualSsmlEditor({
|
|
|
5313
5380
|
node,
|
|
5314
5381
|
path: [index],
|
|
5315
5382
|
selectedPath,
|
|
5316
|
-
onSelect: setSelectedPath
|
|
5383
|
+
onSelect: setSelectedPath,
|
|
5384
|
+
getWarnings
|
|
5317
5385
|
},
|
|
5318
5386
|
isElement(node) ? elementLabel(node) : JSON.stringify(node)
|
|
5319
5387
|
)) })
|
|
@@ -5341,6 +5409,7 @@ function VisualSsmlEditor({
|
|
|
5341
5409
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("legend", { children: localizedElementLabel(selectedElement, locale) }),
|
|
5342
5410
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { htmlFor: "ssml-visual-turn-voice", children: [
|
|
5343
5411
|
locale === "ja" ? "\u97F3\u58F0" : "Voice",
|
|
5412
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(WarningBadge, { messages: elementWarnings(diagnostics, selectedElement) }),
|
|
5344
5413
|
voiceCatalog ? (renderVoiceSelector ?? DefaultVoiceSelector)({
|
|
5345
5414
|
value: selectedElement.voice ?? "",
|
|
5346
5415
|
voices: filteredVoices(voiceCatalog, voiceLocale, voiceRegion, voiceStyle),
|
|
@@ -5446,7 +5515,8 @@ function VisualSsmlEditor({
|
|
|
5446
5515
|
voiceCatalog,
|
|
5447
5516
|
voiceLocale,
|
|
5448
5517
|
voiceRegion,
|
|
5449
|
-
voiceStyle
|
|
5518
|
+
voiceStyle,
|
|
5519
|
+
diagnostics
|
|
5450
5520
|
}
|
|
5451
5521
|
) : selectedLeaf ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
5452
5522
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
|
|
@@ -6091,6 +6161,8 @@ var SsmlEditor = (0, import_react4.forwardRef)(function SsmlEditor2({
|
|
|
6091
6161
|
voiceLocale,
|
|
6092
6162
|
voiceRegion,
|
|
6093
6163
|
voiceStyle,
|
|
6164
|
+
voiceModel,
|
|
6165
|
+
model,
|
|
6094
6166
|
emotionStyles,
|
|
6095
6167
|
className,
|
|
6096
6168
|
style,
|
|
@@ -6539,7 +6611,9 @@ var SsmlEditor = (0, import_react4.forwardRef)(function SsmlEditor2({
|
|
|
6539
6611
|
voiceCatalog,
|
|
6540
6612
|
voiceLocale,
|
|
6541
6613
|
voiceRegion,
|
|
6542
|
-
voiceStyle
|
|
6614
|
+
voiceStyle,
|
|
6615
|
+
voiceModel,
|
|
6616
|
+
model
|
|
6543
6617
|
}
|
|
6544
6618
|
) }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { ...editorStyles.editor, minHeight: editorMinHeight }, children: [
|
|
6545
6619
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|