ssml-builder-js 2.14.0 → 2.16.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/dist/react.mjs CHANGED
@@ -42,13 +42,13 @@ import {
42
42
  resolveExpressAsStyles,
43
43
  updateEditableText,
44
44
  updateTagAttribute
45
- } from "./chunk-UZSCXPC5.mjs";
45
+ } from "./chunk-F2EMU3HM.mjs";
46
46
  import {
47
47
  buildPartialSsml,
48
48
  buildSsml,
49
49
  validateAzureSsml,
50
50
  validateSsml
51
- } from "./chunk-QFIBPCO4.mjs";
51
+ } from "./chunk-HI74FTKY.mjs";
52
52
  import "./chunk-6S5ODO6A.mjs";
53
53
 
54
54
  // packages/ssml-editor-react/src/SsmlEditor.tsx
@@ -1976,6 +1976,40 @@ function VoiceCapabilityMatrix({
1976
1976
  const status = voice.status ?? (voice.preview ? "preview" : void 0);
1977
1977
  return /* @__PURE__ */ React.createElement("div", { className: "ssml-editor-voice-capabilities", "data-voice-capabilities": "" }, /* @__PURE__ */ React.createElement("div", null, locale === "ja" ? "\u30B9\u30C6\u30FC\u30BF\u30B9" : "Status", ": ", status?.toUpperCase() ?? (locale === "ja" ? "GA" : "GA"), status === "preview" || status === "deprecated" ? /* @__PURE__ */ React.createElement("span", { role: "status", "aria-label": status, className: "ssml-editor-voice-warning-badge" }, status === "preview" ? "\u26A0 Preview" : "\u26A0 Deprecated") : null), (voice.regions?.length || voice.region) && /* @__PURE__ */ React.createElement("div", null, locale === "ja" ? "\u30EA\u30FC\u30B8\u30E7\u30F3" : "Regions", ":", " ", [...voice.regions ?? [], ...voice.region ? [voice.region] : []].filter((region, index, all) => all.indexOf(region) === index).join(", ")), voice.supportedTags && voice.supportedTags.length > 0 && /* @__PURE__ */ React.createElement("div", null, locale === "ja" ? "\u5BFE\u5FDC\u30BF\u30B0" : "Supported tags", ": ", voice.supportedTags.join(", ")), voice.unsupportedTags && voice.unsupportedTags.length > 0 && /* @__PURE__ */ React.createElement("div", { className: "ssml-editor-voice-unsupported" }, locale === "ja" ? "\u975E\u5BFE\u5FDC\u30BF\u30B0" : "Unsupported tags", ": ", voice.unsupportedTags.join(", ")));
1978
1978
  }
1979
+ function diagnosticMatchesElement(diagnostic, element) {
1980
+ const name = elementLabel(element).toLowerCase().replace("expressas", "mstts:express-as");
1981
+ if (diagnostic.code === "azure-unsupported-style") return name === "mstts:express-as" || name === "express-as";
1982
+ if (diagnostic.code === "azure-unsupported-tag-for-voice") {
1983
+ return diagnostic.message.toLowerCase().includes(`<${name}>`) || diagnostic.message.toLowerCase().includes(`<${name.replace("mstts:", "")}>`);
1984
+ }
1985
+ if (diagnostic.code === "azure-locale-mismatch" || diagnostic.code === "azure-unsupported-model-for-voice")
1986
+ return name === "voice" || name === "mstts:turn";
1987
+ return false;
1988
+ }
1989
+ function elementWarnings(diagnostics, element) {
1990
+ return diagnostics.filter((diagnostic) => diagnosticMatchesElement(diagnostic, element));
1991
+ }
1992
+ function fieldWarnings(diagnostics, element, field) {
1993
+ return elementWarnings(diagnostics, element).filter((diagnostic) => {
1994
+ if (diagnostic.code === "azure-unsupported-style") return field.key === "style";
1995
+ return field.key === "name" || field.key === "voice";
1996
+ });
1997
+ }
1998
+ function WarningBadge({ messages }) {
1999
+ if (messages.length === 0) return null;
2000
+ return /* @__PURE__ */ React.createElement(
2001
+ "span",
2002
+ {
2003
+ role: "status",
2004
+ "aria-label": messages.map((message) => message.message).join(" "),
2005
+ title: messages.map((message) => message.message).join(" "),
2006
+ "data-ssml-warning-badge": "",
2007
+ "data-testid": "ssml-editor-warning-badge",
2008
+ className: "ssml-editor-warning-badge"
2009
+ },
2010
+ "\u26A0"
2011
+ );
2012
+ }
1979
2013
  function DefaultVoiceSelector({
1980
2014
  value,
1981
2015
  voices,
@@ -2008,7 +2042,8 @@ function VisualElementInspector({
2008
2042
  voiceCatalog,
2009
2043
  voiceLocale,
2010
2044
  voiceRegion,
2011
- voiceStyle
2045
+ voiceStyle,
2046
+ diagnostics
2012
2047
  }) {
2013
2048
  if (customInspector) {
2014
2049
  return /* @__PURE__ */ React.createElement(React.Fragment, null, customInspector({ document: document2, element, path, readOnly, onChange: commit, locale }));
@@ -2019,7 +2054,7 @@ function VisualElementInspector({
2019
2054
  (voice) => voice.name === (element.type === "voice" ? element.name : void 0)
2020
2055
  );
2021
2056
  const renderSelector = renderVoiceSelector ?? DefaultVoiceSelector;
2022
- 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) => {
2057
+ return /* @__PURE__ */ React.createElement("fieldset", null, /* @__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) => {
2023
2058
  const value = element[field.key];
2024
2059
  const inputValue = value === void 0 ? "" : String(value);
2025
2060
  const inputId = `ssml-visual-${field.key}`;
@@ -2056,7 +2091,7 @@ function VisualElementInspector({
2056
2091
  speakerProfileId: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID",
2057
2092
  url: "\u97F3\u58F0\u5909\u63DB URL",
2058
2093
  profile: "\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB"
2059
- }[field.key] ?? field.label : field.label, field.key === "name" && elementLabel(element) === "voice" && voiceCatalog ? renderSelector({
2094
+ }[field.key] ?? field.label : field.label, /* @__PURE__ */ React.createElement(WarningBadge, { messages: fieldWarnings(diagnostics, element, field) }), field.key === "name" && elementLabel(element) === "voice" && voiceCatalog ? renderSelector({
2060
2095
  value: inputValue,
2061
2096
  voices: availableVoices,
2062
2097
  selectedVoice,
@@ -2134,19 +2169,21 @@ function TreeNode({
2134
2169
  node,
2135
2170
  path,
2136
2171
  selectedPath,
2137
- onSelect
2172
+ onSelect,
2173
+ getWarnings
2138
2174
  }) {
2139
2175
  if (!isElement(node)) return null;
2140
2176
  const name = elementLabel(node);
2141
2177
  const isSelected = selectedPath?.join(".") === path.join(".");
2142
- return /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement("button", { type: "button", "aria-current": isSelected ? "true" : void 0, onClick: () => onSelect(path) }, `<${name}>`), (node.children ?? []).length > 0 && /* @__PURE__ */ React.createElement("ul", null, node.children?.map((child, index) => /* @__PURE__ */ React.createElement(
2178
+ return /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement("button", { type: "button", "aria-current": isSelected ? "true" : void 0, onClick: () => onSelect(path) }, `<${name}>`, /* @__PURE__ */ React.createElement(WarningBadge, { messages: getWarnings(node) })), (node.children ?? []).length > 0 && /* @__PURE__ */ React.createElement("ul", null, node.children?.map((child, index) => /* @__PURE__ */ React.createElement(
2143
2179
  TreeNode,
2144
2180
  {
2145
2181
  key: `${path.join(".")}/${isElement(child) ? elementLabel(child) : JSON.stringify(child)}`,
2146
2182
  node: child,
2147
2183
  path: [...path, index],
2148
2184
  selectedPath,
2149
- onSelect
2185
+ onSelect,
2186
+ getWarnings
2150
2187
  }
2151
2188
  ))));
2152
2189
  }
@@ -2161,7 +2198,9 @@ function VisualSsmlEditor({
2161
2198
  voiceCatalog,
2162
2199
  voiceLocale,
2163
2200
  voiceRegion,
2164
- voiceStyle
2201
+ voiceStyle,
2202
+ voiceModel,
2203
+ model
2165
2204
  }) {
2166
2205
  const [selectedPath, setSelectedPath] = useState2(null);
2167
2206
  const [selection, setSelection] = useState2({ start: 0, end: 0 });
@@ -2169,7 +2208,27 @@ function VisualSsmlEditor({
2169
2208
  const selectedLeaf = textLeaves.find((leaf) => leaf.path.join(".") === selectedPath?.join(".")) ?? textLeaves[0];
2170
2209
  const selectedElement = selectedPath ? getNodeAtPath(document2.children ?? [], selectedPath) : void 0;
2171
2210
  const selectedCustomInspector = selectedElement && isElement(selectedElement) ? customInspectors?.[elementLabel(selectedElement)] ?? customInspectors?.[selectedElement.type] : void 0;
2172
- const diagnostics = useMemo(() => validateAzureSsml(buildSsml(document2)), [document2]);
2211
+ const validationOptions = useMemo(
2212
+ () => ({
2213
+ model: voiceModel ?? model,
2214
+ customVoiceDefinitions: voiceCatalog?.map((voice) => ({
2215
+ name: voice.name,
2216
+ locale: voice.locale,
2217
+ regions: voice.regions ?? (voice.region ? [voice.region] : void 0),
2218
+ styles: voice.styles,
2219
+ supportedTags: voice.supportedTags,
2220
+ unsupportedTags: voice.unsupportedTags,
2221
+ models: voice.models,
2222
+ status: voice.status ?? (voice.preview ? "preview" : "ga")
2223
+ }))
2224
+ }),
2225
+ [model, voiceCatalog, voiceModel]
2226
+ );
2227
+ const diagnostics = useMemo(
2228
+ () => validateAzureSsml(buildSsml(document2), validationOptions),
2229
+ [document2, validationOptions]
2230
+ );
2231
+ const getWarnings = (element) => elementWarnings(diagnostics, element);
2173
2232
  const commit = (nextDocument) => onChange?.(nextDocument);
2174
2233
  const applyWrapper = (tag, attributes) => {
2175
2234
  if (readOnly || !selectedLeaf) return;
@@ -2184,7 +2243,8 @@ function VisualSsmlEditor({
2184
2243
  node,
2185
2244
  path: [index],
2186
2245
  selectedPath,
2187
- onSelect: setSelectedPath
2246
+ onSelect: setSelectedPath,
2247
+ getWarnings
2188
2248
  }
2189
2249
  )))), /* @__PURE__ */ React.createElement("div", { className: "ssml-editor-visual-form" }, selectedCustomInspector && selectedElement && isElement(selectedElement) ? selectedCustomInspector({
2190
2250
  document: document2,
@@ -2201,7 +2261,7 @@ function VisualSsmlEditor({
2201
2261
  onClick: () => commit(addDialogTurn(document2, selectedPath ?? []))
2202
2262
  },
2203
2263
  locale === "ja" ? "\u30BF\u30FC\u30F3\u3092\u8FFD\u52A0" : "Add turn"
2204
- )) : selectedElement && isElement(selectedElement) && selectedElement.type === "mstts:turn" ? /* @__PURE__ */ React.createElement("fieldset", null, /* @__PURE__ */ React.createElement("legend", null, localizedElementLabel(selectedElement, locale)), /* @__PURE__ */ React.createElement("label", { htmlFor: "ssml-visual-turn-voice" }, locale === "ja" ? "\u97F3\u58F0" : "Voice", voiceCatalog ? (renderVoiceSelector ?? DefaultVoiceSelector)({
2264
+ )) : selectedElement && isElement(selectedElement) && selectedElement.type === "mstts:turn" ? /* @__PURE__ */ React.createElement("fieldset", null, /* @__PURE__ */ React.createElement("legend", null, localizedElementLabel(selectedElement, locale)), /* @__PURE__ */ React.createElement("label", { htmlFor: "ssml-visual-turn-voice" }, locale === "ja" ? "\u97F3\u58F0" : "Voice", /* @__PURE__ */ React.createElement(WarningBadge, { messages: elementWarnings(diagnostics, selectedElement) }), voiceCatalog ? (renderVoiceSelector ?? DefaultVoiceSelector)({
2205
2265
  value: selectedElement.voice ?? "",
2206
2266
  voices: filteredVoices(voiceCatalog, voiceLocale, voiceRegion, voiceStyle),
2207
2267
  selectedVoice: voiceCatalog?.find((voice) => voice.name === selectedElement.voice),
@@ -2278,7 +2338,8 @@ function VisualSsmlEditor({
2278
2338
  voiceCatalog,
2279
2339
  voiceLocale,
2280
2340
  voiceRegion,
2281
- voiceStyle
2341
+ voiceStyle,
2342
+ diagnostics
2282
2343
  }
2283
2344
  ) : selectedLeaf ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("label", null, "Text", /* @__PURE__ */ React.createElement(
2284
2345
  "textarea",
@@ -2902,6 +2963,8 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
2902
2963
  voiceLocale,
2903
2964
  voiceRegion,
2904
2965
  voiceStyle,
2966
+ voiceModel,
2967
+ model,
2905
2968
  emotionStyles,
2906
2969
  className,
2907
2970
  style,
@@ -3300,7 +3363,9 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
3300
3363
  voiceCatalog,
3301
3364
  voiceLocale,
3302
3365
  voiceRegion,
3303
- voiceStyle
3366
+ voiceStyle,
3367
+ voiceModel,
3368
+ model
3304
3369
  }
3305
3370
  )) : /* @__PURE__ */ React.createElement("div", { style: { ...editorStyles.editor, minHeight: editorMinHeight } }, /* @__PURE__ */ React.createElement(
3306
3371
  Editor,