@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 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.jsx)("legend", { children: `<${elementLabel(element)}>` }),
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.jsx)("button", { type: "button", "aria-current": isSelected ? "true" : void 0, onClick: () => onSelect(path), children: `<${name}>` }),
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 diagnostics = (0, import_react3.useMemo)(() => (0, import_ssml_core4.validateAzureSsml)((0, import_ssml_core4.buildSsml)(document2)), [document2]);
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)(