ssml-builder-js 2.12.0 → 2.13.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
@@ -26,9 +26,8 @@ import {
26
26
  SILENCE_VALUE_DESCRIPTIONS,
27
27
  SILENCE_VALUE_PRESETS,
28
28
  SSML_ATTRIBUTE_PRESETS,
29
+ SSML_ELEMENT_COPY,
29
30
  SSML_HOVER_COPY,
30
- buildPartialSsml,
31
- buildSsml,
32
31
  clearSsmlDocument,
33
32
  createSsmlInsertionEdit,
34
33
  findActiveSsmlTags,
@@ -42,10 +41,14 @@ import {
42
41
  registerSsmlCompletionProvider,
43
42
  resolveExpressAsStyles,
44
43
  updateEditableText,
45
- updateTagAttribute,
44
+ updateTagAttribute
45
+ } from "./chunk-LCTE26LS.mjs";
46
+ import {
47
+ buildPartialSsml,
48
+ buildSsml,
46
49
  validateAzureSsml,
47
50
  validateSsml
48
- } from "./chunk-FHDFRM2F.mjs";
51
+ } from "./chunk-CZ2F3TET.mjs";
49
52
  import "./chunk-6S5ODO6A.mjs";
50
53
 
51
54
  // packages/ssml-editor-react/src/SsmlEditor.tsx
@@ -1951,19 +1954,94 @@ var VISUAL_ELEMENT_FIELDS = {
1951
1954
  function getElementFields(element) {
1952
1955
  return VISUAL_ELEMENT_FIELDS[elementLabel(element)] ?? [];
1953
1956
  }
1957
+ function localizedElementLabel(element, locale) {
1958
+ return SSML_ELEMENT_COPY[locale][elementLabel(element)]?.label ?? elementLabel(element);
1959
+ }
1960
+ function filteredVoices(voiceCatalog, locale, region, style) {
1961
+ return (voiceCatalog ?? []).filter((voice) => {
1962
+ if (locale && voice.locale.toLowerCase() !== locale.toLowerCase()) return false;
1963
+ if (region && voice.region && voice.region.toLowerCase() !== region.toLowerCase()) return false;
1964
+ if (style && !voice.styles?.some((candidate) => candidate.toLowerCase() === style.toLowerCase())) return false;
1965
+ return true;
1966
+ });
1967
+ }
1968
+ function DefaultVoiceSelector({ value, voices, readOnly, locale, onChange }) {
1969
+ return /* @__PURE__ */ React.createElement(
1970
+ "select",
1971
+ {
1972
+ "aria-label": locale === "ja" ? "\u97F3\u58F0" : "Voice",
1973
+ value,
1974
+ disabled: readOnly,
1975
+ onChange: (event) => onChange(event.target.value)
1976
+ },
1977
+ /* @__PURE__ */ React.createElement("option", { value: "" }, locale === "ja" ? "\u97F3\u58F0\u3092\u9078\u629E" : "Select a voice"),
1978
+ voices.map((voice) => /* @__PURE__ */ React.createElement("option", { key: voice.name, value: voice.name }, voice.name, voice.preview || voice.status === "preview" ? " (preview)" : ""))
1979
+ );
1980
+ }
1954
1981
  function VisualElementInspector({
1955
1982
  document: document2,
1956
1983
  element,
1957
1984
  path,
1958
1985
  readOnly,
1959
- commit
1986
+ commit,
1987
+ locale,
1988
+ customInspector,
1989
+ renderVoiceSelector,
1990
+ voiceCatalog,
1991
+ voiceLocale,
1992
+ voiceRegion,
1993
+ voiceStyle
1960
1994
  }) {
1995
+ if (customInspector) {
1996
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, customInspector({ document: document2, element, path, readOnly, onChange: commit, locale }));
1997
+ }
1961
1998
  const fields = getElementFields(element);
1999
+ const availableVoices = filteredVoices(voiceCatalog, voiceLocale, voiceRegion, voiceStyle);
2000
+ const renderSelector = renderVoiceSelector ?? DefaultVoiceSelector;
1962
2001
  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
2002
  const value = element[field.key];
1964
2003
  const inputValue = value === void 0 ? "" : String(value);
1965
2004
  const inputId = `ssml-visual-${field.key}`;
1966
- return /* @__PURE__ */ React.createElement("label", { key: field.key, htmlFor: inputId }, field.label, field.multiline ? /* @__PURE__ */ React.createElement(
2005
+ return /* @__PURE__ */ React.createElement("label", { key: field.key, htmlFor: inputId }, locale === "ja" ? {
2006
+ name: "\u540D\u524D",
2007
+ effect: "\u52B9\u679C",
2008
+ rate: "\u901F\u5EA6",
2009
+ pitch: "\u30D4\u30C3\u30C1",
2010
+ volume: "\u97F3\u91CF",
2011
+ contour: "\u30D4\u30C3\u30C1\u66F2\u7DDA",
2012
+ range: "\u7BC4\u56F2",
2013
+ style: "\u30B9\u30BF\u30A4\u30EB",
2014
+ styleDegree: "\u30B9\u30BF\u30A4\u30EB\u5F37\u5EA6",
2015
+ role: "\u5F79\u5272",
2016
+ interpretAs: "\u89E3\u91C8",
2017
+ alphabet: "\u30A2\u30EB\u30D5\u30A1\u30D9\u30C3\u30C8",
2018
+ ph: "\u767A\u97F3",
2019
+ level: "\u5F37\u8ABF\u30EC\u30D9\u30EB",
2020
+ src: "\u97F3\u58F0 URL",
2021
+ desc: "\u8AAC\u660E",
2022
+ clipBegin: "\u958B\u59CB\u4F4D\u7F6E",
2023
+ clipEnd: "\u7D42\u4E86\u4F4D\u7F6E",
2024
+ speed: "\u901F\u5EA6",
2025
+ repeatCount: "\u7E70\u308A\u8FD4\u3057\u56DE\u6570",
2026
+ repeatDuration: "\u7E70\u308A\u8FD4\u3057\u6642\u9593",
2027
+ soundLevel: "\u97F3\u91CF\u30EC\u30D9\u30EB",
2028
+ mark: "\u30D6\u30C3\u30AF\u30DE\u30FC\u30AF\u540D",
2029
+ alias: "\u5225\u540D",
2030
+ lang: "\u8A00\u8A9E",
2031
+ uri: "\u8F9E\u66F8 URI",
2032
+ typeValue: "\u7121\u97F3\u306E\u7A2E\u985E",
2033
+ value: "\u5024",
2034
+ id: "\u57CB\u3081\u8FBC\u307F ID",
2035
+ speakerProfileId: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID",
2036
+ url: "\u97F3\u58F0\u5909\u63DB URL",
2037
+ profile: "\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB"
2038
+ }[field.key] ?? field.label : field.label, field.key === "name" && elementLabel(element) === "voice" && voiceCatalog ? renderSelector({
2039
+ value: inputValue,
2040
+ voices: availableVoices,
2041
+ readOnly,
2042
+ locale,
2043
+ onChange: (value2) => commit(updateOptionalElementProperty(document2, path, field.key, value2))
2044
+ }) : field.multiline ? /* @__PURE__ */ React.createElement(
1967
2045
  "textarea",
1968
2046
  {
1969
2047
  id: inputId,
@@ -2054,13 +2132,21 @@ function VisualSsmlEditor({
2054
2132
  document: document2,
2055
2133
  readOnly = false,
2056
2134
  onChange,
2057
- onPreviewSelection
2135
+ onPreviewSelection,
2136
+ locale = "en",
2137
+ customInspectors,
2138
+ renderVoiceSelector,
2139
+ voiceCatalog,
2140
+ voiceLocale,
2141
+ voiceRegion,
2142
+ voiceStyle
2058
2143
  }) {
2059
2144
  const [selectedPath, setSelectedPath] = useState2(null);
2060
2145
  const [selection, setSelection] = useState2({ start: 0, end: 0 });
2061
2146
  const textLeaves = useMemo(() => collectTextLeaves(document2.children ?? []), [document2]);
2062
2147
  const selectedLeaf = textLeaves.find((leaf) => leaf.path.join(".") === selectedPath?.join(".")) ?? textLeaves[0];
2063
2148
  const selectedElement = selectedPath ? getNodeAtPath(document2.children ?? [], selectedPath) : void 0;
2149
+ const selectedCustomInspector = selectedElement && isElement(selectedElement) ? customInspectors?.[elementLabel(selectedElement)] ?? customInspectors?.[selectedElement.type] : void 0;
2064
2150
  const diagnostics = useMemo(() => validateAzureSsml(buildSsml(document2)), [document2]);
2065
2151
  const commit = (nextDocument) => onChange?.(nextDocument);
2066
2152
  const applyWrapper = (tag, attributes) => {
@@ -2078,29 +2164,43 @@ function VisualSsmlEditor({
2078
2164
  selectedPath,
2079
2165
  onSelect: setSelectedPath
2080
2166
  }
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(
2167
+ )))), /* @__PURE__ */ React.createElement("div", { className: "ssml-editor-visual-form" }, selectedCustomInspector && selectedElement && isElement(selectedElement) ? selectedCustomInspector({
2168
+ document: document2,
2169
+ element: selectedElement,
2170
+ path: selectedPath ?? [],
2171
+ readOnly,
2172
+ onChange: commit,
2173
+ locale
2174
+ }) : 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(
2082
2175
  "button",
2083
2176
  {
2084
2177
  type: "button",
2085
2178
  disabled: readOnly,
2086
2179
  onClick: () => commit(addDialogTurn(document2, selectedPath ?? []))
2087
2180
  },
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(
2181
+ locale === "ja" ? "\u30BF\u30FC\u30F3\u3092\u8FFD\u52A0" : "Add turn"
2182
+ )) : 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)({
2183
+ value: selectedElement.voice ?? "",
2184
+ voices: filteredVoices(voiceCatalog, voiceLocale, voiceRegion, voiceStyle),
2185
+ readOnly,
2186
+ locale,
2187
+ onChange: (value) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "voice", value))
2188
+ }) : /* @__PURE__ */ React.createElement(
2090
2189
  "input",
2091
2190
  {
2191
+ id: "ssml-visual-turn-voice",
2092
2192
  value: selectedElement.voice ?? "",
2093
2193
  readOnly,
2094
2194
  onChange: (event) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "voice", event.target.value))
2095
2195
  }
2096
- )), /* @__PURE__ */ React.createElement("label", null, "Speaker", /* @__PURE__ */ React.createElement(
2196
+ )), /* @__PURE__ */ React.createElement("label", null, locale === "ja" ? "\u8A71\u8005" : "Speaker", /* @__PURE__ */ React.createElement(
2097
2197
  "input",
2098
2198
  {
2099
2199
  value: selectedElement.speaker ?? "",
2100
2200
  readOnly,
2101
2201
  onChange: (event) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "speaker", event.target.value))
2102
2202
  }
2103
- )), /* @__PURE__ */ React.createElement("label", null, "Turn text", /* @__PURE__ */ React.createElement(
2203
+ )), /* @__PURE__ */ React.createElement("label", null, locale === "ja" ? "\u767A\u8A71\u30C6\u30AD\u30B9\u30C8" : "Turn text", /* @__PURE__ */ React.createElement(
2104
2204
  "textarea",
2105
2205
  {
2106
2206
  value: selectedElement.children?.filter((child) => typeof child === "string").join("") ?? "",
@@ -2113,28 +2213,28 @@ function VisualSsmlEditor({
2113
2213
  )
2114
2214
  )
2115
2215
  }
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(
2216
+ ))) : selectedElement && isElement(selectedElement) && selectedElement.type === "mstts:backgroundaudio" ? /* @__PURE__ */ React.createElement("fieldset", null, /* @__PURE__ */ React.createElement("legend", null, localizedElementLabel(selectedElement, locale)), /* @__PURE__ */ React.createElement("label", null, locale === "ja" ? "\u97F3\u58F0 URL" : "Source URL", /* @__PURE__ */ React.createElement(
2117
2217
  "input",
2118
2218
  {
2119
2219
  value: selectedElement.src ?? "",
2120
2220
  readOnly,
2121
2221
  onChange: (event) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "src", event.target.value))
2122
2222
  }
2123
- )), /* @__PURE__ */ React.createElement("label", null, "Volume", /* @__PURE__ */ React.createElement(
2223
+ )), /* @__PURE__ */ React.createElement("label", null, locale === "ja" ? "\u97F3\u91CF" : "Volume", /* @__PURE__ */ React.createElement(
2124
2224
  "input",
2125
2225
  {
2126
2226
  value: selectedElement.volume === void 0 ? "" : String(selectedElement.volume),
2127
2227
  readOnly,
2128
2228
  onChange: (event) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "volume", event.target.value))
2129
2229
  }
2130
- )), /* @__PURE__ */ React.createElement("label", null, "Fade in", /* @__PURE__ */ React.createElement(
2230
+ )), /* @__PURE__ */ React.createElement("label", null, locale === "ja" ? "\u30D5\u30A7\u30FC\u30C9\u30A4\u30F3" : "Fade in", /* @__PURE__ */ React.createElement(
2131
2231
  "input",
2132
2232
  {
2133
2233
  value: selectedElement.fadeIn === void 0 ? "" : String(selectedElement.fadeIn),
2134
2234
  readOnly,
2135
2235
  onChange: (event) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "fadeIn", event.target.value))
2136
2236
  }
2137
- )), /* @__PURE__ */ React.createElement("label", null, "Fade out", /* @__PURE__ */ React.createElement(
2237
+ )), /* @__PURE__ */ React.createElement("label", null, locale === "ja" ? "\u30D5\u30A7\u30FC\u30C9\u30A2\u30A6\u30C8" : "Fade out", /* @__PURE__ */ React.createElement(
2138
2238
  "input",
2139
2239
  {
2140
2240
  value: selectedElement.fadeOut === void 0 ? "" : String(selectedElement.fadeOut),
@@ -2148,7 +2248,14 @@ function VisualSsmlEditor({
2148
2248
  element: selectedElement,
2149
2249
  path: selectedPath ?? [],
2150
2250
  readOnly,
2151
- commit
2251
+ commit,
2252
+ locale,
2253
+ customInspector: customInspectors?.[elementLabel(selectedElement)] ?? customInspectors?.[selectedElement.type],
2254
+ renderVoiceSelector,
2255
+ voiceCatalog,
2256
+ voiceLocale,
2257
+ voiceRegion,
2258
+ voiceStyle
2152
2259
  }
2153
2260
  ) : selectedLeaf ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("label", null, "Text", /* @__PURE__ */ React.createElement(
2154
2261
  "textarea",
@@ -2766,6 +2873,12 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
2766
2873
  insertionGroups,
2767
2874
  customInsertions,
2768
2875
  additionalInsertions,
2876
+ customInspectors,
2877
+ renderVoiceSelector,
2878
+ voiceCatalog,
2879
+ voiceLocale,
2880
+ voiceRegion,
2881
+ voiceStyle,
2769
2882
  emotionStyles,
2770
2883
  className,
2771
2884
  style,
@@ -3157,7 +3270,14 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
3157
3270
  document: draftDocument,
3158
3271
  readOnly: isReadOnly,
3159
3272
  onChange: commit,
3160
- onPreviewSelection
3273
+ onPreviewSelection,
3274
+ locale: localeProp ?? languageProp,
3275
+ customInspectors,
3276
+ renderVoiceSelector,
3277
+ voiceCatalog,
3278
+ voiceLocale,
3279
+ voiceRegion,
3280
+ voiceStyle
3161
3281
  }
3162
3282
  )) : /* @__PURE__ */ React.createElement("div", { style: { ...editorStyles.editor, minHeight: editorMinHeight } }, /* @__PURE__ */ React.createElement(
3163
3283
  Editor,
@@ -3217,6 +3337,7 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
3217
3337
  export {
3218
3338
  EDITOR_COPY,
3219
3339
  INLINE_BADGE_COPY,
3340
+ SSML_ELEMENT_COPY,
3220
3341
  SSML_HOVER_COPY,
3221
3342
  SSML_INSERTIONS,
3222
3343
  SsmlEditor,