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.js CHANGED
@@ -33,6 +33,7 @@ var react_exports = {};
33
33
  __export(react_exports, {
34
34
  EDITOR_COPY: () => EDITOR_COPY,
35
35
  INLINE_BADGE_COPY: () => INLINE_BADGE_COPY,
36
+ SSML_ELEMENT_COPY: () => SSML_ELEMENT_COPY,
36
37
  SSML_HOVER_COPY: () => SSML_HOVER_COPY,
37
38
  SSML_INSERTIONS: () => SSML_INSERTIONS,
38
39
  SsmlEditor: () => SsmlEditor,
@@ -820,6 +821,152 @@ var SILENCE_VALUE_DESCRIPTIONS = {
820
821
  };
821
822
 
822
823
  // packages/ssml-editor-react/src/locales.ts
824
+ var SSML_ELEMENT_COPY = {
825
+ ja: {
826
+ dialog: {
827
+ label: "\u30C0\u30A4\u30A2\u30ED\u30B0",
828
+ description: "\u8907\u6570\u306E\u8A71\u8005\u30BF\u30FC\u30F3\u3092\u307E\u3068\u3081\u307E\u3059\u3002",
829
+ validationErrors: { turn: "turn \u306F dialog \u306E\u76F4\u4E0B\u306B\u914D\u7F6E\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
830
+ },
831
+ "mstts:dialog": {
832
+ label: "\u30C0\u30A4\u30A2\u30ED\u30B0",
833
+ description: "\u8907\u6570\u306E\u8A71\u8005\u30BF\u30FC\u30F3\u3092\u307E\u3068\u3081\u307E\u3059\u3002",
834
+ validationErrors: { turn: "turn \u306F dialog \u306E\u76F4\u4E0B\u306B\u914D\u7F6E\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
835
+ },
836
+ turn: {
837
+ label: "\u8A71\u8005\u30BF\u30FC\u30F3",
838
+ description: "\u30C0\u30A4\u30A2\u30ED\u30B0\u5185\u306E\u8A71\u8005\u3068\u767A\u8A71\u5185\u5BB9\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
839
+ validationErrors: { voice: "voice \u307E\u305F\u306F speaker \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
840
+ },
841
+ "mstts:turn": {
842
+ label: "\u8A71\u8005\u30BF\u30FC\u30F3",
843
+ description: "\u30C0\u30A4\u30A2\u30ED\u30B0\u5185\u306E\u8A71\u8005\u3068\u767A\u8A71\u5185\u5BB9\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
844
+ validationErrors: { voice: "voice \u307E\u305F\u306F speaker \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
845
+ },
846
+ backgroundaudio: {
847
+ label: "\u80CC\u666F\u97F3\u58F0",
848
+ description: "\u5408\u6210\u97F3\u58F0\u306E\u80CC\u5F8C\u3067\u518D\u751F\u3059\u308B\u97F3\u58F0\u30D5\u30A1\u30A4\u30EB\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
849
+ validationErrors: { src: "\u80CC\u666F\u97F3\u58F0\u306B\u306F URL \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
850
+ },
851
+ "mstts:backgroundaudio": {
852
+ label: "\u80CC\u666F\u97F3\u58F0",
853
+ description: "\u5408\u6210\u97F3\u58F0\u306E\u80CC\u5F8C\u3067\u518D\u751F\u3059\u308B\u97F3\u58F0\u30D5\u30A1\u30A4\u30EB\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
854
+ validationErrors: { src: "\u80CC\u666F\u97F3\u58F0\u306B\u306F URL \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
855
+ },
856
+ ttsembedding: {
857
+ label: "\u97F3\u58F0\u57CB\u3081\u8FBC\u307F",
858
+ description: "\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0\u307E\u305F\u306F\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u57CB\u3081\u8FBC\u307F\u307E\u3059\u3002",
859
+ validationErrors: { speakerProfileId: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
860
+ },
861
+ "mstts:ttsembedding": {
862
+ label: "\u97F3\u58F0\u57CB\u3081\u8FBC\u307F",
863
+ description: "\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0\u307E\u305F\u306F\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u57CB\u3081\u8FBC\u307F\u307E\u3059\u3002",
864
+ validationErrors: { speakerProfileId: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
865
+ },
866
+ embedding: {
867
+ label: "\u57CB\u3081\u8FBC\u307F",
868
+ description: "\u8A71\u8005\u57CB\u3081\u8FBC\u307F\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
869
+ validationErrors: {}
870
+ },
871
+ "mstts:embedding": {
872
+ label: "\u57CB\u3081\u8FBC\u307F",
873
+ description: "\u8A71\u8005\u57CB\u3081\u8FBC\u307F\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
874
+ validationErrors: {}
875
+ },
876
+ silence: {
877
+ label: "\u7121\u97F3",
878
+ description: "\u767A\u8A71\u524D\u5F8C\u307E\u305F\u306F\u53E5\u8AAD\u70B9\u306E\u5883\u754C\u306B\u7121\u97F3\u3092\u8FFD\u52A0\u3057\u307E\u3059\u3002",
879
+ validationErrors: { value: "\u7121\u97F3\u306E\u9577\u3055\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
880
+ },
881
+ "mstts:silence": {
882
+ label: "\u7121\u97F3",
883
+ description: "\u767A\u8A71\u524D\u5F8C\u307E\u305F\u306F\u53E5\u8AAD\u70B9\u306E\u5883\u754C\u306B\u7121\u97F3\u3092\u8FFD\u52A0\u3057\u307E\u3059\u3002",
884
+ validationErrors: { value: "\u7121\u97F3\u306E\u9577\u3055\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
885
+ },
886
+ voiceconversion: {
887
+ label: "\u97F3\u58F0\u5909\u63DB",
888
+ description: "\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0\u306E\u5909\u63DB\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
889
+ validationErrors: {}
890
+ },
891
+ "mstts:voiceconversion": {
892
+ label: "\u97F3\u58F0\u5909\u63DB",
893
+ description: "\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0\u306E\u5909\u63DB\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
894
+ validationErrors: {}
895
+ }
896
+ },
897
+ en: {
898
+ dialog: {
899
+ label: "Dialog",
900
+ description: "Groups multiple speaker turns.",
901
+ validationErrors: { turn: "Place turns directly inside a dialog." }
902
+ },
903
+ "mstts:dialog": {
904
+ label: "Dialog",
905
+ description: "Groups multiple speaker turns.",
906
+ validationErrors: { turn: "Place turns directly inside a dialog." }
907
+ },
908
+ turn: {
909
+ label: "Dialog turn",
910
+ description: "Specifies a speaker and utterance within a dialog.",
911
+ validationErrors: { voice: "Provide a voice or speaker." }
912
+ },
913
+ "mstts:turn": {
914
+ label: "Dialog turn",
915
+ description: "Specifies a speaker and utterance within a dialog.",
916
+ validationErrors: { voice: "Provide a voice or speaker." }
917
+ },
918
+ backgroundaudio: {
919
+ label: "Background audio",
920
+ description: "Specifies an audio file played behind synthesized speech.",
921
+ validationErrors: { src: "Provide a background audio URL." }
922
+ },
923
+ "mstts:backgroundaudio": {
924
+ label: "Background audio",
925
+ description: "Specifies an audio file played behind synthesized speech.",
926
+ validationErrors: { src: "Provide a background audio URL." }
927
+ },
928
+ ttsembedding: {
929
+ label: "TTS embedding",
930
+ description: "Embeds custom voice or speaker profile metadata.",
931
+ validationErrors: { speakerProfileId: "Provide a speaker profile ID." }
932
+ },
933
+ "mstts:ttsembedding": {
934
+ label: "TTS embedding",
935
+ description: "Embeds custom voice or speaker profile metadata.",
936
+ validationErrors: { speakerProfileId: "Provide a speaker profile ID." }
937
+ },
938
+ embedding: {
939
+ label: "Embedding",
940
+ description: "Specifies speaker embedding metadata.",
941
+ validationErrors: {}
942
+ },
943
+ "mstts:embedding": {
944
+ label: "Embedding",
945
+ description: "Specifies speaker embedding metadata.",
946
+ validationErrors: {}
947
+ },
948
+ silence: {
949
+ label: "Silence",
950
+ description: "Adds silence before, after, or around punctuation.",
951
+ validationErrors: { value: "Provide a silence duration." }
952
+ },
953
+ "mstts:silence": {
954
+ label: "Silence",
955
+ description: "Adds silence before, after, or around punctuation.",
956
+ validationErrors: { value: "Provide a silence duration." }
957
+ },
958
+ voiceconversion: {
959
+ label: "Voice conversion",
960
+ description: "Specifies custom voice conversion metadata.",
961
+ validationErrors: {}
962
+ },
963
+ "mstts:voiceconversion": {
964
+ label: "Voice conversion",
965
+ description: "Specifies custom voice conversion metadata.",
966
+ validationErrors: {}
967
+ }
968
+ }
969
+ };
823
970
  var EDITOR_COPY = {
824
971
  ja: {
825
972
  editorAriaLabel: "SSML\u30A8\u30C7\u30A3\u30BF\u30FC",
@@ -1037,6 +1184,42 @@ var SSML_HOVER_COPY = {
1037
1184
  parameters: {
1038
1185
  value: { title: "value", description: "\u76EE\u6A19\u6642\u9593\u3002\u4F8B: `10s`\u3001`5000ms`\u3001`00:00:10`\u3002" }
1039
1186
  }
1187
+ },
1188
+ "mstts:dialog": { title: "\u30C0\u30A4\u30A2\u30ED\u30B0", description: "\u8907\u6570\u306E\u8A71\u8005\u30BF\u30FC\u30F3\u3092\u307E\u3068\u3081\u307E\u3059\u3002", parameters: {} },
1189
+ "mstts:turn": {
1190
+ title: "\u8A71\u8005\u30BF\u30FC\u30F3",
1191
+ description: "\u30C0\u30A4\u30A2\u30ED\u30B0\u5185\u306E\u8A71\u8005\u3068\u767A\u8A71\u5185\u5BB9\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
1192
+ parameters: {
1193
+ voice: { title: "voice", description: "\u3053\u306E\u30BF\u30FC\u30F3\u3067\u4F7F\u7528\u3059\u308B Azure \u97F3\u58F0\u540D\u3002" },
1194
+ speaker: { title: "speaker", description: "\u8A71\u8005\u8B58\u5225\u5B50\u3002" }
1195
+ }
1196
+ },
1197
+ "mstts:backgroundaudio": {
1198
+ title: "\u80CC\u666F\u97F3\u58F0",
1199
+ description: "\u5408\u6210\u97F3\u58F0\u306E\u80CC\u5F8C\u3067\u518D\u751F\u3059\u308B\u97F3\u58F0\u30D5\u30A1\u30A4\u30EB\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
1200
+ parameters: { src: { title: "src", description: "\u97F3\u58F0\u30D5\u30A1\u30A4\u30EB\u306E HTTPS URL\u3002" } }
1201
+ },
1202
+ "mstts:ttsembedding": {
1203
+ title: "\u97F3\u58F0\u57CB\u3081\u8FBC\u307F",
1204
+ description: "\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0\u307E\u305F\u306F\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u57CB\u3081\u8FBC\u307F\u307E\u3059\u3002",
1205
+ parameters: { speakerProfileId: { title: "speakerProfileId", description: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID\u3002" } }
1206
+ },
1207
+ "mstts:embedding": {
1208
+ title: "\u57CB\u3081\u8FBC\u307F",
1209
+ description: "\u8A71\u8005\u57CB\u3081\u8FBC\u307F\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
1210
+ parameters: {
1211
+ id: { title: "id", description: "\u57CB\u3081\u8FBC\u307F ID\u3002" },
1212
+ speakerProfileId: { title: "speakerProfileId", description: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID\u3002" }
1213
+ }
1214
+ },
1215
+ "mstts:voiceconversion": {
1216
+ title: "\u97F3\u58F0\u5909\u63DB",
1217
+ description: "\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0\u306E\u5909\u63DB\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
1218
+ parameters: {
1219
+ url: { title: "url", description: "\u97F3\u58F0\u5909\u63DB\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB URL\u3002" },
1220
+ profile: { title: "profile", description: "\u5909\u63DB\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB\u3002" },
1221
+ speakerProfileId: { title: "speakerProfileId", description: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID\u3002" }
1222
+ }
1040
1223
  }
1041
1224
  }
1042
1225
  },
@@ -1201,6 +1384,42 @@ var SSML_HOVER_COPY = {
1201
1384
  parameters: {
1202
1385
  value: { title: "value", description: "The target duration, such as `10s`, `5000ms`, or `00:00:10`." }
1203
1386
  }
1387
+ },
1388
+ "mstts:dialog": { title: "Dialog", description: "Groups multiple speaker turns.", parameters: {} },
1389
+ "mstts:turn": {
1390
+ title: "Dialog turn",
1391
+ description: "Specifies a speaker and utterance within a dialog.",
1392
+ parameters: {
1393
+ voice: { title: "voice", description: "The Azure voice used by this turn." },
1394
+ speaker: { title: "speaker", description: "The speaker identifier." }
1395
+ }
1396
+ },
1397
+ "mstts:backgroundaudio": {
1398
+ title: "Background audio",
1399
+ description: "Specifies an audio file played behind synthesized speech.",
1400
+ parameters: { src: { title: "src", description: "The HTTPS URL of the audio file." } }
1401
+ },
1402
+ "mstts:ttsembedding": {
1403
+ title: "TTS embedding",
1404
+ description: "Embeds custom voice or speaker profile metadata.",
1405
+ parameters: { speakerProfileId: { title: "speakerProfileId", description: "The speaker profile ID." } }
1406
+ },
1407
+ "mstts:embedding": {
1408
+ title: "Embedding",
1409
+ description: "Specifies speaker embedding metadata.",
1410
+ parameters: {
1411
+ id: { title: "id", description: "The embedding ID." },
1412
+ speakerProfileId: { title: "speakerProfileId", description: "The speaker profile ID." }
1413
+ }
1414
+ },
1415
+ "mstts:voiceconversion": {
1416
+ title: "Voice conversion",
1417
+ description: "Specifies custom voice conversion metadata.",
1418
+ parameters: {
1419
+ url: { title: "url", description: "The voice conversion profile URL." },
1420
+ profile: { title: "profile", description: "The conversion profile." },
1421
+ speakerProfileId: { title: "speakerProfileId", description: "The speaker profile ID." }
1422
+ }
1204
1423
  }
1205
1424
  }
1206
1425
  }
@@ -3234,7 +3453,7 @@ function validateElement(token, source, diagnostics, voiceName, options, voiceCa
3234
3453
  addDiagnostic(diagnostics, source, token.start, "<mstts:backgroundaudio> must be self-closing.");
3235
3454
  }
3236
3455
  }
3237
- function validateAzureSsml(ssml, options = {}) {
3456
+ function validateAzureSsmlStatic(ssml, options = {}) {
3238
3457
  const diagnostics = [];
3239
3458
  if (typeof ssml !== "string") {
3240
3459
  return [
@@ -3358,6 +3577,51 @@ function validateAzureSsml(ssml, options = {}) {
3358
3577
  }
3359
3578
  return diagnostics;
3360
3579
  }
3580
+ function urlAttributes(token) {
3581
+ const tag = canonicalTagName(token.name);
3582
+ const attributes = tag === "audio" || tag === "mstts:backgroundaudio" ? ["src"] : tag === "lexicon" ? ["uri"] : tag === "mstts:voiceconversion" ? ["url"] : [];
3583
+ return attributes.flatMap((attribute) => {
3584
+ const value = attr(token, attribute);
3585
+ return value === void 0 ? [] : [{ attribute, value }];
3586
+ });
3587
+ }
3588
+ function validateAzureSsml(ssml, options = {}) {
3589
+ const diagnostics = validateAzureSsmlStatic(ssml, options);
3590
+ const validator = options.urlValidator ?? options.customUrlValidator;
3591
+ if (!validator || typeof ssml !== "string") return diagnostics;
3592
+ let tokens;
3593
+ try {
3594
+ tokens = tokenizeElements(ssml);
3595
+ } catch {
3596
+ return diagnostics;
3597
+ }
3598
+ const checks = tokens.flatMap(
3599
+ (token) => urlAttributes(token).map(async ({ attribute, value }) => {
3600
+ try {
3601
+ const result = await validator(value, { tag: token.name, attribute });
3602
+ const valid = typeof result === "boolean" ? result : result.valid;
3603
+ if (!valid) {
3604
+ const reason = typeof result === "boolean" ? void 0 : result.reason;
3605
+ addDiagnostic(
3606
+ diagnostics,
3607
+ ssml,
3608
+ token.start,
3609
+ `<${token.name} ${attribute}> was rejected by the custom URL validator${reason ? `: ${reason}` : "."}`
3610
+ );
3611
+ }
3612
+ } catch (error) {
3613
+ const reason = error instanceof Error ? error.message : String(error);
3614
+ addDiagnostic(
3615
+ diagnostics,
3616
+ ssml,
3617
+ token.start,
3618
+ `<${token.name} ${attribute}> could not be validated by the custom URL validator: ${reason}`
3619
+ );
3620
+ }
3621
+ })
3622
+ );
3623
+ return Promise.all(checks).then(() => diagnostics);
3624
+ }
3361
3625
  var AZURE_VOICE_CATALOG_METADATA = {
3362
3626
  apiVersion: "2025-10-01",
3363
3627
  generatedAt: "2026-08-28T00:00:00.000Z",
@@ -6484,19 +6748,94 @@ var VISUAL_ELEMENT_FIELDS = {
6484
6748
  function getElementFields(element) {
6485
6749
  return VISUAL_ELEMENT_FIELDS[elementLabel(element)] ?? [];
6486
6750
  }
6751
+ function localizedElementLabel(element, locale) {
6752
+ return SSML_ELEMENT_COPY[locale][elementLabel(element)]?.label ?? elementLabel(element);
6753
+ }
6754
+ function filteredVoices(voiceCatalog, locale, region, style) {
6755
+ return (voiceCatalog ?? []).filter((voice) => {
6756
+ if (locale && voice.locale.toLowerCase() !== locale.toLowerCase()) return false;
6757
+ if (region && voice.region && voice.region.toLowerCase() !== region.toLowerCase()) return false;
6758
+ if (style && !voice.styles?.some((candidate) => candidate.toLowerCase() === style.toLowerCase())) return false;
6759
+ return true;
6760
+ });
6761
+ }
6762
+ function DefaultVoiceSelector({ value, voices, readOnly, locale, onChange }) {
6763
+ return /* @__PURE__ */ React.createElement(
6764
+ "select",
6765
+ {
6766
+ "aria-label": locale === "ja" ? "\u97F3\u58F0" : "Voice",
6767
+ value,
6768
+ disabled: readOnly,
6769
+ onChange: (event) => onChange(event.target.value)
6770
+ },
6771
+ /* @__PURE__ */ React.createElement("option", { value: "" }, locale === "ja" ? "\u97F3\u58F0\u3092\u9078\u629E" : "Select a voice"),
6772
+ voices.map((voice) => /* @__PURE__ */ React.createElement("option", { key: voice.name, value: voice.name }, voice.name, voice.preview || voice.status === "preview" ? " (preview)" : ""))
6773
+ );
6774
+ }
6487
6775
  function VisualElementInspector({
6488
6776
  document: document2,
6489
6777
  element,
6490
6778
  path,
6491
6779
  readOnly,
6492
- commit
6780
+ commit,
6781
+ locale,
6782
+ customInspector,
6783
+ renderVoiceSelector,
6784
+ voiceCatalog,
6785
+ voiceLocale,
6786
+ voiceRegion,
6787
+ voiceStyle
6493
6788
  }) {
6789
+ if (customInspector) {
6790
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, customInspector({ document: document2, element, path, readOnly, onChange: commit, locale }));
6791
+ }
6494
6792
  const fields = getElementFields(element);
6793
+ const availableVoices = filteredVoices(voiceCatalog, voiceLocale, voiceRegion, voiceStyle);
6794
+ const renderSelector = renderVoiceSelector ?? DefaultVoiceSelector;
6495
6795
  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) => {
6496
6796
  const value = element[field.key];
6497
6797
  const inputValue = value === void 0 ? "" : String(value);
6498
6798
  const inputId = `ssml-visual-${field.key}`;
6499
- return /* @__PURE__ */ React.createElement("label", { key: field.key, htmlFor: inputId }, field.label, field.multiline ? /* @__PURE__ */ React.createElement(
6799
+ return /* @__PURE__ */ React.createElement("label", { key: field.key, htmlFor: inputId }, locale === "ja" ? {
6800
+ name: "\u540D\u524D",
6801
+ effect: "\u52B9\u679C",
6802
+ rate: "\u901F\u5EA6",
6803
+ pitch: "\u30D4\u30C3\u30C1",
6804
+ volume: "\u97F3\u91CF",
6805
+ contour: "\u30D4\u30C3\u30C1\u66F2\u7DDA",
6806
+ range: "\u7BC4\u56F2",
6807
+ style: "\u30B9\u30BF\u30A4\u30EB",
6808
+ styleDegree: "\u30B9\u30BF\u30A4\u30EB\u5F37\u5EA6",
6809
+ role: "\u5F79\u5272",
6810
+ interpretAs: "\u89E3\u91C8",
6811
+ alphabet: "\u30A2\u30EB\u30D5\u30A1\u30D9\u30C3\u30C8",
6812
+ ph: "\u767A\u97F3",
6813
+ level: "\u5F37\u8ABF\u30EC\u30D9\u30EB",
6814
+ src: "\u97F3\u58F0 URL",
6815
+ desc: "\u8AAC\u660E",
6816
+ clipBegin: "\u958B\u59CB\u4F4D\u7F6E",
6817
+ clipEnd: "\u7D42\u4E86\u4F4D\u7F6E",
6818
+ speed: "\u901F\u5EA6",
6819
+ repeatCount: "\u7E70\u308A\u8FD4\u3057\u56DE\u6570",
6820
+ repeatDuration: "\u7E70\u308A\u8FD4\u3057\u6642\u9593",
6821
+ soundLevel: "\u97F3\u91CF\u30EC\u30D9\u30EB",
6822
+ mark: "\u30D6\u30C3\u30AF\u30DE\u30FC\u30AF\u540D",
6823
+ alias: "\u5225\u540D",
6824
+ lang: "\u8A00\u8A9E",
6825
+ uri: "\u8F9E\u66F8 URI",
6826
+ typeValue: "\u7121\u97F3\u306E\u7A2E\u985E",
6827
+ value: "\u5024",
6828
+ id: "\u57CB\u3081\u8FBC\u307F ID",
6829
+ speakerProfileId: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID",
6830
+ url: "\u97F3\u58F0\u5909\u63DB URL",
6831
+ profile: "\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB"
6832
+ }[field.key] ?? field.label : field.label, field.key === "name" && elementLabel(element) === "voice" && voiceCatalog ? renderSelector({
6833
+ value: inputValue,
6834
+ voices: availableVoices,
6835
+ readOnly,
6836
+ locale,
6837
+ onChange: (value2) => commit(updateOptionalElementProperty(document2, path, field.key, value2))
6838
+ }) : field.multiline ? /* @__PURE__ */ React.createElement(
6500
6839
  "textarea",
6501
6840
  {
6502
6841
  id: inputId,
@@ -6587,13 +6926,21 @@ function VisualSsmlEditor({
6587
6926
  document: document2,
6588
6927
  readOnly = false,
6589
6928
  onChange,
6590
- onPreviewSelection
6929
+ onPreviewSelection,
6930
+ locale = "en",
6931
+ customInspectors,
6932
+ renderVoiceSelector,
6933
+ voiceCatalog,
6934
+ voiceLocale,
6935
+ voiceRegion,
6936
+ voiceStyle
6591
6937
  }) {
6592
6938
  const [selectedPath, setSelectedPath] = (0, import_react3.useState)(null);
6593
6939
  const [selection, setSelection] = (0, import_react3.useState)({ start: 0, end: 0 });
6594
6940
  const textLeaves = (0, import_react3.useMemo)(() => collectTextLeaves(document2.children ?? []), [document2]);
6595
6941
  const selectedLeaf = textLeaves.find((leaf) => leaf.path.join(".") === selectedPath?.join(".")) ?? textLeaves[0];
6596
6942
  const selectedElement = selectedPath ? getNodeAtPath(document2.children ?? [], selectedPath) : void 0;
6943
+ const selectedCustomInspector = selectedElement && isElement(selectedElement) ? customInspectors?.[elementLabel(selectedElement)] ?? customInspectors?.[selectedElement.type] : void 0;
6597
6944
  const diagnostics = (0, import_react3.useMemo)(() => validateAzureSsml(buildSsml(document2)), [document2]);
6598
6945
  const commit = (nextDocument) => onChange?.(nextDocument);
6599
6946
  const applyWrapper = (tag, attributes) => {
@@ -6611,29 +6958,43 @@ function VisualSsmlEditor({
6611
6958
  selectedPath,
6612
6959
  onSelect: setSelectedPath
6613
6960
  }
6614
- )))), /* @__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(
6961
+ )))), /* @__PURE__ */ React.createElement("div", { className: "ssml-editor-visual-form" }, selectedCustomInspector && selectedElement && isElement(selectedElement) ? selectedCustomInspector({
6962
+ document: document2,
6963
+ element: selectedElement,
6964
+ path: selectedPath ?? [],
6965
+ readOnly,
6966
+ onChange: commit,
6967
+ locale
6968
+ }) : 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(
6615
6969
  "button",
6616
6970
  {
6617
6971
  type: "button",
6618
6972
  disabled: readOnly,
6619
6973
  onClick: () => commit(addDialogTurn(document2, selectedPath ?? []))
6620
6974
  },
6621
- "Add turn"
6622
- )) : 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(
6975
+ locale === "ja" ? "\u30BF\u30FC\u30F3\u3092\u8FFD\u52A0" : "Add turn"
6976
+ )) : 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)({
6977
+ value: selectedElement.voice ?? "",
6978
+ voices: filteredVoices(voiceCatalog, voiceLocale, voiceRegion, voiceStyle),
6979
+ readOnly,
6980
+ locale,
6981
+ onChange: (value) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "voice", value))
6982
+ }) : /* @__PURE__ */ React.createElement(
6623
6983
  "input",
6624
6984
  {
6985
+ id: "ssml-visual-turn-voice",
6625
6986
  value: selectedElement.voice ?? "",
6626
6987
  readOnly,
6627
6988
  onChange: (event) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "voice", event.target.value))
6628
6989
  }
6629
- )), /* @__PURE__ */ React.createElement("label", null, "Speaker", /* @__PURE__ */ React.createElement(
6990
+ )), /* @__PURE__ */ React.createElement("label", null, locale === "ja" ? "\u8A71\u8005" : "Speaker", /* @__PURE__ */ React.createElement(
6630
6991
  "input",
6631
6992
  {
6632
6993
  value: selectedElement.speaker ?? "",
6633
6994
  readOnly,
6634
6995
  onChange: (event) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "speaker", event.target.value))
6635
6996
  }
6636
- )), /* @__PURE__ */ React.createElement("label", null, "Turn text", /* @__PURE__ */ React.createElement(
6997
+ )), /* @__PURE__ */ React.createElement("label", null, locale === "ja" ? "\u767A\u8A71\u30C6\u30AD\u30B9\u30C8" : "Turn text", /* @__PURE__ */ React.createElement(
6637
6998
  "textarea",
6638
6999
  {
6639
7000
  value: selectedElement.children?.filter((child) => typeof child === "string").join("") ?? "",
@@ -6646,28 +7007,28 @@ function VisualSsmlEditor({
6646
7007
  )
6647
7008
  )
6648
7009
  }
6649
- ))) : 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(
7010
+ ))) : 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(
6650
7011
  "input",
6651
7012
  {
6652
7013
  value: selectedElement.src ?? "",
6653
7014
  readOnly,
6654
7015
  onChange: (event) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "src", event.target.value))
6655
7016
  }
6656
- )), /* @__PURE__ */ React.createElement("label", null, "Volume", /* @__PURE__ */ React.createElement(
7017
+ )), /* @__PURE__ */ React.createElement("label", null, locale === "ja" ? "\u97F3\u91CF" : "Volume", /* @__PURE__ */ React.createElement(
6657
7018
  "input",
6658
7019
  {
6659
7020
  value: selectedElement.volume === void 0 ? "" : String(selectedElement.volume),
6660
7021
  readOnly,
6661
7022
  onChange: (event) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "volume", event.target.value))
6662
7023
  }
6663
- )), /* @__PURE__ */ React.createElement("label", null, "Fade in", /* @__PURE__ */ React.createElement(
7024
+ )), /* @__PURE__ */ React.createElement("label", null, locale === "ja" ? "\u30D5\u30A7\u30FC\u30C9\u30A4\u30F3" : "Fade in", /* @__PURE__ */ React.createElement(
6664
7025
  "input",
6665
7026
  {
6666
7027
  value: selectedElement.fadeIn === void 0 ? "" : String(selectedElement.fadeIn),
6667
7028
  readOnly,
6668
7029
  onChange: (event) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "fadeIn", event.target.value))
6669
7030
  }
6670
- )), /* @__PURE__ */ React.createElement("label", null, "Fade out", /* @__PURE__ */ React.createElement(
7031
+ )), /* @__PURE__ */ React.createElement("label", null, locale === "ja" ? "\u30D5\u30A7\u30FC\u30C9\u30A2\u30A6\u30C8" : "Fade out", /* @__PURE__ */ React.createElement(
6671
7032
  "input",
6672
7033
  {
6673
7034
  value: selectedElement.fadeOut === void 0 ? "" : String(selectedElement.fadeOut),
@@ -6681,7 +7042,14 @@ function VisualSsmlEditor({
6681
7042
  element: selectedElement,
6682
7043
  path: selectedPath ?? [],
6683
7044
  readOnly,
6684
- commit
7045
+ commit,
7046
+ locale,
7047
+ customInspector: customInspectors?.[elementLabel(selectedElement)] ?? customInspectors?.[selectedElement.type],
7048
+ renderVoiceSelector,
7049
+ voiceCatalog,
7050
+ voiceLocale,
7051
+ voiceRegion,
7052
+ voiceStyle
6685
7053
  }
6686
7054
  ) : selectedLeaf ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("label", null, "Text", /* @__PURE__ */ React.createElement(
6687
7055
  "textarea",
@@ -7299,6 +7667,12 @@ var SsmlEditor = (0, import_react4.forwardRef)(function SsmlEditor2({
7299
7667
  insertionGroups,
7300
7668
  customInsertions,
7301
7669
  additionalInsertions,
7670
+ customInspectors,
7671
+ renderVoiceSelector,
7672
+ voiceCatalog,
7673
+ voiceLocale,
7674
+ voiceRegion,
7675
+ voiceStyle,
7302
7676
  emotionStyles,
7303
7677
  className,
7304
7678
  style,
@@ -7690,7 +8064,14 @@ var SsmlEditor = (0, import_react4.forwardRef)(function SsmlEditor2({
7690
8064
  document: draftDocument,
7691
8065
  readOnly: isReadOnly,
7692
8066
  onChange: commit,
7693
- onPreviewSelection
8067
+ onPreviewSelection,
8068
+ locale: localeProp ?? languageProp,
8069
+ customInspectors,
8070
+ renderVoiceSelector,
8071
+ voiceCatalog,
8072
+ voiceLocale,
8073
+ voiceRegion,
8074
+ voiceStyle
7694
8075
  }
7695
8076
  )) : /* @__PURE__ */ React.createElement("div", { style: { ...editorStyles.editor, minHeight: editorMinHeight } }, /* @__PURE__ */ React.createElement(
7696
8077
  import_react5.default,
@@ -7751,6 +8132,7 @@ var SsmlEditor = (0, import_react4.forwardRef)(function SsmlEditor2({
7751
8132
  0 && (module.exports = {
7752
8133
  EDITOR_COPY,
7753
8134
  INLINE_BADGE_COPY,
8135
+ SSML_ELEMENT_COPY,
7754
8136
  SSML_HOVER_COPY,
7755
8137
  SSML_INSERTIONS,
7756
8138
  SsmlEditor,