@ssml-builder-js/ssml-editor-react 2.11.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/index.js CHANGED
@@ -33,6 +33,7 @@ var index_exports = {};
33
33
  __export(index_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
  // 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
  }
@@ -4730,6 +4949,221 @@ function updateOptionalElementProperty(document2, path, property, value) {
4730
4949
  return next;
4731
4950
  });
4732
4951
  }
4952
+ var VISUAL_ELEMENT_FIELDS = {
4953
+ voice: [
4954
+ { key: "name", label: "Voice name" },
4955
+ { key: "effect", label: "Effect" }
4956
+ ],
4957
+ prosody: [
4958
+ { key: "rate", label: "Rate" },
4959
+ { key: "pitch", label: "Pitch" },
4960
+ { key: "volume", label: "Volume" },
4961
+ { key: "contour", label: "Contour" },
4962
+ { key: "range", label: "Range" }
4963
+ ],
4964
+ "express-as": [
4965
+ { key: "style", label: "Style" },
4966
+ { key: "styleDegree", label: "Style degree" },
4967
+ { key: "role", label: "Role" }
4968
+ ],
4969
+ expressAs: [
4970
+ { key: "style", label: "Style" },
4971
+ { key: "styleDegree", label: "Style degree" },
4972
+ { key: "role", label: "Role" }
4973
+ ],
4974
+ "mstts:express-as": [
4975
+ { key: "style", label: "Style" },
4976
+ { key: "styleDegree", label: "Style degree" },
4977
+ { key: "role", label: "Role" }
4978
+ ],
4979
+ "say-as": [
4980
+ { key: "interpretAs", label: "Interpret as" },
4981
+ { key: "format", label: "Format" },
4982
+ { key: "detail", label: "Detail" }
4983
+ ],
4984
+ sayAs: [
4985
+ { key: "interpretAs", label: "Interpret as" },
4986
+ { key: "format", label: "Format" },
4987
+ { key: "detail", label: "Detail" }
4988
+ ],
4989
+ phoneme: [
4990
+ { key: "alphabet", label: "Alphabet" },
4991
+ { key: "ph", label: "Pronunciation" }
4992
+ ],
4993
+ emphasis: [{ key: "level", label: "Level" }],
4994
+ audio: [
4995
+ { key: "src", label: "Source URL" },
4996
+ { key: "desc", label: "Description" },
4997
+ { key: "clipBegin", label: "Clip begin" },
4998
+ { key: "clipEnd", label: "Clip end" },
4999
+ { key: "speed", label: "Speed" },
5000
+ { key: "repeatCount", label: "Repeat count" },
5001
+ { key: "repeatDuration", label: "Repeat duration" },
5002
+ { key: "soundLevel", label: "Sound level" }
5003
+ ],
5004
+ mark: [{ key: "name", label: "Mark name" }],
5005
+ bookmark: [{ key: "mark", label: "Bookmark name" }],
5006
+ sub: [{ key: "alias", label: "Alias" }],
5007
+ lang: [{ key: "lang", label: "Language" }],
5008
+ lexicon: [{ key: "uri", label: "Lexicon URI" }],
5009
+ "mstts:silence": [
5010
+ { key: "typeValue", label: "Silence type" },
5011
+ { key: "value", label: "Value" }
5012
+ ],
5013
+ silence: [
5014
+ { key: "typeValue", label: "Silence type" },
5015
+ { key: "value", label: "Value" }
5016
+ ],
5017
+ "mstts:audioduration": [{ key: "value", label: "Duration" }],
5018
+ "mstts:ttsembedding": [{ key: "speakerProfileId", label: "Speaker profile ID" }],
5019
+ "mstts:embedding": [
5020
+ { key: "id", label: "Embedding ID" },
5021
+ { key: "speakerProfileId", label: "Speaker profile ID" }
5022
+ ],
5023
+ "mstts:voiceconversion": [
5024
+ { key: "url", label: "Source URL" },
5025
+ { key: "profile", label: "Profile" },
5026
+ { key: "speakerProfileId", label: "Speaker profile ID" }
5027
+ ]
5028
+ };
5029
+ function getElementFields(element) {
5030
+ return VISUAL_ELEMENT_FIELDS[elementLabel(element)] ?? [];
5031
+ }
5032
+ function localizedElementLabel(element, locale) {
5033
+ return SSML_ELEMENT_COPY[locale][elementLabel(element)]?.label ?? elementLabel(element);
5034
+ }
5035
+ function filteredVoices(voiceCatalog, locale, region, style) {
5036
+ return (voiceCatalog ?? []).filter((voice) => {
5037
+ if (locale && voice.locale.toLowerCase() !== locale.toLowerCase()) return false;
5038
+ if (region && voice.region && voice.region.toLowerCase() !== region.toLowerCase()) return false;
5039
+ if (style && !voice.styles?.some((candidate) => candidate.toLowerCase() === style.toLowerCase())) return false;
5040
+ return true;
5041
+ });
5042
+ }
5043
+ function DefaultVoiceSelector({ value, voices, readOnly, locale, onChange }) {
5044
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
5045
+ "select",
5046
+ {
5047
+ "aria-label": locale === "ja" ? "\u97F3\u58F0" : "Voice",
5048
+ value,
5049
+ disabled: readOnly,
5050
+ onChange: (event) => onChange(event.target.value),
5051
+ children: [
5052
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "", children: locale === "ja" ? "\u97F3\u58F0\u3092\u9078\u629E" : "Select a voice" }),
5053
+ voices.map((voice) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("option", { value: voice.name, children: [
5054
+ voice.name,
5055
+ voice.preview || voice.status === "preview" ? " (preview)" : ""
5056
+ ] }, voice.name))
5057
+ ]
5058
+ }
5059
+ );
5060
+ }
5061
+ function VisualElementInspector({
5062
+ document: document2,
5063
+ element,
5064
+ path,
5065
+ readOnly,
5066
+ commit,
5067
+ locale,
5068
+ customInspector,
5069
+ renderVoiceSelector,
5070
+ voiceCatalog,
5071
+ voiceLocale,
5072
+ voiceRegion,
5073
+ voiceStyle
5074
+ }) {
5075
+ if (customInspector) {
5076
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: customInspector({ document: document2, element, path, readOnly, onChange: commit, locale }) });
5077
+ }
5078
+ const fields = getElementFields(element);
5079
+ const availableVoices = filteredVoices(voiceCatalog, voiceLocale, voiceRegion, voiceStyle);
5080
+ const renderSelector = renderVoiceSelector ?? DefaultVoiceSelector;
5081
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("fieldset", { children: [
5082
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("legend", { children: `<${elementLabel(element)}>` }),
5083
+ 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) => {
5084
+ const value = element[field.key];
5085
+ const inputValue = value === void 0 ? "" : String(value);
5086
+ const inputId = `ssml-visual-${field.key}`;
5087
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { htmlFor: inputId, children: [
5088
+ locale === "ja" ? {
5089
+ name: "\u540D\u524D",
5090
+ effect: "\u52B9\u679C",
5091
+ rate: "\u901F\u5EA6",
5092
+ pitch: "\u30D4\u30C3\u30C1",
5093
+ volume: "\u97F3\u91CF",
5094
+ contour: "\u30D4\u30C3\u30C1\u66F2\u7DDA",
5095
+ range: "\u7BC4\u56F2",
5096
+ style: "\u30B9\u30BF\u30A4\u30EB",
5097
+ styleDegree: "\u30B9\u30BF\u30A4\u30EB\u5F37\u5EA6",
5098
+ role: "\u5F79\u5272",
5099
+ interpretAs: "\u89E3\u91C8",
5100
+ alphabet: "\u30A2\u30EB\u30D5\u30A1\u30D9\u30C3\u30C8",
5101
+ ph: "\u767A\u97F3",
5102
+ level: "\u5F37\u8ABF\u30EC\u30D9\u30EB",
5103
+ src: "\u97F3\u58F0 URL",
5104
+ desc: "\u8AAC\u660E",
5105
+ clipBegin: "\u958B\u59CB\u4F4D\u7F6E",
5106
+ clipEnd: "\u7D42\u4E86\u4F4D\u7F6E",
5107
+ speed: "\u901F\u5EA6",
5108
+ repeatCount: "\u7E70\u308A\u8FD4\u3057\u56DE\u6570",
5109
+ repeatDuration: "\u7E70\u308A\u8FD4\u3057\u6642\u9593",
5110
+ soundLevel: "\u97F3\u91CF\u30EC\u30D9\u30EB",
5111
+ mark: "\u30D6\u30C3\u30AF\u30DE\u30FC\u30AF\u540D",
5112
+ alias: "\u5225\u540D",
5113
+ lang: "\u8A00\u8A9E",
5114
+ uri: "\u8F9E\u66F8 URI",
5115
+ typeValue: "\u7121\u97F3\u306E\u7A2E\u985E",
5116
+ value: "\u5024",
5117
+ id: "\u57CB\u3081\u8FBC\u307F ID",
5118
+ speakerProfileId: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID",
5119
+ url: "\u97F3\u58F0\u5909\u63DB URL",
5120
+ profile: "\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB"
5121
+ }[field.key] ?? field.label : field.label,
5122
+ field.key === "name" && elementLabel(element) === "voice" && voiceCatalog ? renderSelector({
5123
+ value: inputValue,
5124
+ voices: availableVoices,
5125
+ readOnly,
5126
+ locale,
5127
+ onChange: (value2) => commit(updateOptionalElementProperty(document2, path, field.key, value2))
5128
+ }) : field.multiline ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
5129
+ "textarea",
5130
+ {
5131
+ id: inputId,
5132
+ value: inputValue,
5133
+ readOnly,
5134
+ onChange: (event) => commit(updateOptionalElementProperty(document2, path, field.key, event.target.value))
5135
+ }
5136
+ ) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
5137
+ "input",
5138
+ {
5139
+ id: inputId,
5140
+ value: inputValue,
5141
+ readOnly,
5142
+ onChange: (event) => commit(updateOptionalElementProperty(document2, path, field.key, event.target.value))
5143
+ }
5144
+ )
5145
+ ] }, field.key);
5146
+ })
5147
+ ] });
5148
+ }
5149
+ function getElementAncestors(nodes, path, ancestors = []) {
5150
+ const [index, ...rest] = path;
5151
+ const node = nodes[index];
5152
+ if (!node || !isElement(node)) return ancestors;
5153
+ if (rest.length === 0) return ancestors;
5154
+ return getElementAncestors(node.children ?? [], rest, [...ancestors, node]);
5155
+ }
5156
+ function buildVisualPreview(document2, leaf, selection) {
5157
+ const start = Math.max(0, Math.min(selection.start, leaf.value.length));
5158
+ const end = Math.max(start, Math.min(selection.end, leaf.value.length));
5159
+ const value = start === end ? leaf.value : leaf.value.slice(start, end);
5160
+ const ancestors = getElementAncestors(document2.children ?? [], leaf.path);
5161
+ const children = ancestors.reduceRight(
5162
+ (current, ancestor) => [{ ...ancestor, children: current }],
5163
+ [value]
5164
+ );
5165
+ return (0, import_ssml_core4.buildSsml)({ ...document2, children });
5166
+ }
4733
5167
  function addDialogTurn(document2, path) {
4734
5168
  return updateElementAtPath(document2, path, (element) => ({
4735
5169
  ...element,
@@ -4787,13 +5221,21 @@ function VisualSsmlEditor({
4787
5221
  document: document2,
4788
5222
  readOnly = false,
4789
5223
  onChange,
4790
- onPreviewSelection
5224
+ onPreviewSelection,
5225
+ locale = "en",
5226
+ customInspectors,
5227
+ renderVoiceSelector,
5228
+ voiceCatalog,
5229
+ voiceLocale,
5230
+ voiceRegion,
5231
+ voiceStyle
4791
5232
  }) {
4792
5233
  const [selectedPath, setSelectedPath] = (0, import_react3.useState)(null);
4793
5234
  const [selection, setSelection] = (0, import_react3.useState)({ start: 0, end: 0 });
4794
5235
  const textLeaves = (0, import_react3.useMemo)(() => collectTextLeaves(document2.children ?? []), [document2]);
4795
5236
  const selectedLeaf = textLeaves.find((leaf) => leaf.path.join(".") === selectedPath?.join(".")) ?? textLeaves[0];
4796
5237
  const selectedElement = selectedPath ? getNodeAtPath(document2.children ?? [], selectedPath) : void 0;
5238
+ const selectedCustomInspector = selectedElement && isElement(selectedElement) ? customInspectors?.[elementLabel(selectedElement)] ?? customInspectors?.[selectedElement.type] : void 0;
4797
5239
  const diagnostics = (0, import_react3.useMemo)(() => (0, import_ssml_core4.validateAzureSsml)((0, import_ssml_core4.buildSsml)(document2)), [document2]);
4798
5240
  const commit = (nextDocument) => onChange?.(nextDocument);
4799
5241
  const applyWrapper = (tag, attributes) => {
@@ -4828,25 +5270,39 @@ function VisualSsmlEditor({
4828
5270
  isElement(node) ? elementLabel(node) : JSON.stringify(node)
4829
5271
  )) })
4830
5272
  ] }),
4831
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "ssml-editor-visual-form", children: selectedElement && isElement(selectedElement) && selectedElement.type === "mstts:dialog" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("fieldset", { children: [
4832
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("legend", { children: "Dialog" }),
4833
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: "Add and edit speaker turns in this dialog." }),
5273
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "ssml-editor-visual-form", children: selectedCustomInspector && selectedElement && isElement(selectedElement) ? selectedCustomInspector({
5274
+ document: document2,
5275
+ element: selectedElement,
5276
+ path: selectedPath ?? [],
5277
+ readOnly,
5278
+ onChange: commit,
5279
+ locale
5280
+ }) : selectedElement && isElement(selectedElement) && selectedElement.type === "mstts:dialog" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("fieldset", { children: [
5281
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("legend", { children: localizedElementLabel(selectedElement, locale) }),
5282
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: SSML_ELEMENT_COPY[locale][elementLabel(selectedElement)]?.description }),
4834
5283
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4835
5284
  "button",
4836
5285
  {
4837
5286
  type: "button",
4838
5287
  disabled: readOnly,
4839
5288
  onClick: () => commit(addDialogTurn(document2, selectedPath ?? [])),
4840
- children: "Add turn"
5289
+ children: locale === "ja" ? "\u30BF\u30FC\u30F3\u3092\u8FFD\u52A0" : "Add turn"
4841
5290
  }
4842
5291
  )
4843
5292
  ] }) : selectedElement && isElement(selectedElement) && selectedElement.type === "mstts:turn" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("fieldset", { children: [
4844
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("legend", { children: "Dialog turn" }),
4845
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
4846
- "Voice",
4847
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
5293
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("legend", { children: localizedElementLabel(selectedElement, locale) }),
5294
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { htmlFor: "ssml-visual-turn-voice", children: [
5295
+ locale === "ja" ? "\u97F3\u58F0" : "Voice",
5296
+ voiceCatalog ? (renderVoiceSelector ?? DefaultVoiceSelector)({
5297
+ value: selectedElement.voice ?? "",
5298
+ voices: filteredVoices(voiceCatalog, voiceLocale, voiceRegion, voiceStyle),
5299
+ readOnly,
5300
+ locale,
5301
+ onChange: (value) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "voice", value))
5302
+ }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4848
5303
  "input",
4849
5304
  {
5305
+ id: "ssml-visual-turn-voice",
4850
5306
  value: selectedElement.voice ?? "",
4851
5307
  readOnly,
4852
5308
  onChange: (event) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "voice", event.target.value))
@@ -4854,7 +5310,7 @@ function VisualSsmlEditor({
4854
5310
  )
4855
5311
  ] }),
4856
5312
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
4857
- "Speaker",
5313
+ locale === "ja" ? "\u8A71\u8005" : "Speaker",
4858
5314
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4859
5315
  "input",
4860
5316
  {
@@ -4865,7 +5321,7 @@ function VisualSsmlEditor({
4865
5321
  )
4866
5322
  ] }),
4867
5323
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
4868
- "Turn text",
5324
+ locale === "ja" ? "\u767A\u8A71\u30C6\u30AD\u30B9\u30C8" : "Turn text",
4869
5325
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4870
5326
  "textarea",
4871
5327
  {
@@ -4882,9 +5338,9 @@ function VisualSsmlEditor({
4882
5338
  )
4883
5339
  ] })
4884
5340
  ] }) : selectedElement && isElement(selectedElement) && selectedElement.type === "mstts:backgroundaudio" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("fieldset", { children: [
4885
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("legend", { children: "Background audio" }),
5341
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("legend", { children: localizedElementLabel(selectedElement, locale) }),
4886
5342
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
4887
- "Source URL",
5343
+ locale === "ja" ? "\u97F3\u58F0 URL" : "Source URL",
4888
5344
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4889
5345
  "input",
4890
5346
  {
@@ -4895,7 +5351,7 @@ function VisualSsmlEditor({
4895
5351
  )
4896
5352
  ] }),
4897
5353
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
4898
- "Volume",
5354
+ locale === "ja" ? "\u97F3\u91CF" : "Volume",
4899
5355
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4900
5356
  "input",
4901
5357
  {
@@ -4906,7 +5362,7 @@ function VisualSsmlEditor({
4906
5362
  )
4907
5363
  ] }),
4908
5364
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
4909
- "Fade in",
5365
+ locale === "ja" ? "\u30D5\u30A7\u30FC\u30C9\u30A4\u30F3" : "Fade in",
4910
5366
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4911
5367
  "input",
4912
5368
  {
@@ -4917,7 +5373,7 @@ function VisualSsmlEditor({
4917
5373
  )
4918
5374
  ] }),
4919
5375
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
4920
- "Fade out",
5376
+ locale === "ja" ? "\u30D5\u30A7\u30FC\u30C9\u30A2\u30A6\u30C8" : "Fade out",
4921
5377
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4922
5378
  "input",
4923
5379
  {
@@ -4927,7 +5383,23 @@ function VisualSsmlEditor({
4927
5383
  }
4928
5384
  )
4929
5385
  ] })
4930
- ] }) : selectedLeaf ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5386
+ ] }) : selectedElement && isElement(selectedElement) ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
5387
+ VisualElementInspector,
5388
+ {
5389
+ document: document2,
5390
+ element: selectedElement,
5391
+ path: selectedPath ?? [],
5392
+ readOnly,
5393
+ commit,
5394
+ locale,
5395
+ customInspector: customInspectors?.[elementLabel(selectedElement)] ?? customInspectors?.[selectedElement.type],
5396
+ renderVoiceSelector,
5397
+ voiceCatalog,
5398
+ voiceLocale,
5399
+ voiceRegion,
5400
+ voiceStyle
5401
+ }
5402
+ ) : selectedLeaf ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
4931
5403
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
4932
5404
  "Text",
4933
5405
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
@@ -4967,7 +5439,7 @@ function VisualSsmlEditor({
4967
5439
  "button",
4968
5440
  {
4969
5441
  type: "button",
4970
- onClick: () => onPreviewSelection((0, import_ssml_core4.buildSsml)({ ...document2, children: [selectedLeaf.value] })),
5442
+ onClick: () => onPreviewSelection(buildVisualPreview(document2, selectedLeaf, selection)),
4971
5443
  children: "Preview selection"
4972
5444
  }
4973
5445
  )
@@ -5564,6 +6036,12 @@ var SsmlEditor = (0, import_react4.forwardRef)(function SsmlEditor2({
5564
6036
  insertionGroups,
5565
6037
  customInsertions,
5566
6038
  additionalInsertions,
6039
+ customInspectors,
6040
+ renderVoiceSelector,
6041
+ voiceCatalog,
6042
+ voiceLocale,
6043
+ voiceRegion,
6044
+ voiceStyle,
5567
6045
  emotionStyles,
5568
6046
  className,
5569
6047
  style,
@@ -6005,7 +6483,14 @@ var SsmlEditor = (0, import_react4.forwardRef)(function SsmlEditor2({
6005
6483
  document: draftDocument,
6006
6484
  readOnly: isReadOnly,
6007
6485
  onChange: commit,
6008
- onPreviewSelection
6486
+ onPreviewSelection,
6487
+ locale: localeProp ?? languageProp,
6488
+ customInspectors,
6489
+ renderVoiceSelector,
6490
+ voiceCatalog,
6491
+ voiceLocale,
6492
+ voiceRegion,
6493
+ voiceStyle
6009
6494
  }
6010
6495
  ) }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { ...editorStyles.editor, minHeight: editorMinHeight }, children: [
6011
6496
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
@@ -6084,6 +6569,7 @@ var SsmlEditor = (0, import_react4.forwardRef)(function SsmlEditor2({
6084
6569
  0 && (module.exports = {
6085
6570
  EDITOR_COPY,
6086
6571
  INLINE_BADGE_COPY,
6572
+ SSML_ELEMENT_COPY,
6087
6573
  SSML_HOVER_COPY,
6088
6574
  SSML_INSERTIONS,
6089
6575
  SsmlEditor,