@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.mjs CHANGED
@@ -782,6 +782,152 @@ var SILENCE_VALUE_DESCRIPTIONS = {
782
782
  };
783
783
 
784
784
  // src/locales.ts
785
+ var SSML_ELEMENT_COPY = {
786
+ ja: {
787
+ dialog: {
788
+ label: "\u30C0\u30A4\u30A2\u30ED\u30B0",
789
+ description: "\u8907\u6570\u306E\u8A71\u8005\u30BF\u30FC\u30F3\u3092\u307E\u3068\u3081\u307E\u3059\u3002",
790
+ validationErrors: { turn: "turn \u306F dialog \u306E\u76F4\u4E0B\u306B\u914D\u7F6E\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
791
+ },
792
+ "mstts:dialog": {
793
+ label: "\u30C0\u30A4\u30A2\u30ED\u30B0",
794
+ description: "\u8907\u6570\u306E\u8A71\u8005\u30BF\u30FC\u30F3\u3092\u307E\u3068\u3081\u307E\u3059\u3002",
795
+ validationErrors: { turn: "turn \u306F dialog \u306E\u76F4\u4E0B\u306B\u914D\u7F6E\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
796
+ },
797
+ turn: {
798
+ label: "\u8A71\u8005\u30BF\u30FC\u30F3",
799
+ description: "\u30C0\u30A4\u30A2\u30ED\u30B0\u5185\u306E\u8A71\u8005\u3068\u767A\u8A71\u5185\u5BB9\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
800
+ validationErrors: { voice: "voice \u307E\u305F\u306F speaker \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
801
+ },
802
+ "mstts:turn": {
803
+ label: "\u8A71\u8005\u30BF\u30FC\u30F3",
804
+ description: "\u30C0\u30A4\u30A2\u30ED\u30B0\u5185\u306E\u8A71\u8005\u3068\u767A\u8A71\u5185\u5BB9\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
805
+ validationErrors: { voice: "voice \u307E\u305F\u306F speaker \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
806
+ },
807
+ backgroundaudio: {
808
+ label: "\u80CC\u666F\u97F3\u58F0",
809
+ 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",
810
+ validationErrors: { src: "\u80CC\u666F\u97F3\u58F0\u306B\u306F URL \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
811
+ },
812
+ "mstts:backgroundaudio": {
813
+ label: "\u80CC\u666F\u97F3\u58F0",
814
+ 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",
815
+ validationErrors: { src: "\u80CC\u666F\u97F3\u58F0\u306B\u306F URL \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
816
+ },
817
+ ttsembedding: {
818
+ label: "\u97F3\u58F0\u57CB\u3081\u8FBC\u307F",
819
+ 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",
820
+ validationErrors: { speakerProfileId: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
821
+ },
822
+ "mstts:ttsembedding": {
823
+ label: "\u97F3\u58F0\u57CB\u3081\u8FBC\u307F",
824
+ 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",
825
+ validationErrors: { speakerProfileId: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
826
+ },
827
+ embedding: {
828
+ label: "\u57CB\u3081\u8FBC\u307F",
829
+ description: "\u8A71\u8005\u57CB\u3081\u8FBC\u307F\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
830
+ validationErrors: {}
831
+ },
832
+ "mstts:embedding": {
833
+ label: "\u57CB\u3081\u8FBC\u307F",
834
+ description: "\u8A71\u8005\u57CB\u3081\u8FBC\u307F\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
835
+ validationErrors: {}
836
+ },
837
+ silence: {
838
+ label: "\u7121\u97F3",
839
+ description: "\u767A\u8A71\u524D\u5F8C\u307E\u305F\u306F\u53E5\u8AAD\u70B9\u306E\u5883\u754C\u306B\u7121\u97F3\u3092\u8FFD\u52A0\u3057\u307E\u3059\u3002",
840
+ validationErrors: { value: "\u7121\u97F3\u306E\u9577\u3055\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
841
+ },
842
+ "mstts:silence": {
843
+ label: "\u7121\u97F3",
844
+ description: "\u767A\u8A71\u524D\u5F8C\u307E\u305F\u306F\u53E5\u8AAD\u70B9\u306E\u5883\u754C\u306B\u7121\u97F3\u3092\u8FFD\u52A0\u3057\u307E\u3059\u3002",
845
+ validationErrors: { value: "\u7121\u97F3\u306E\u9577\u3055\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
846
+ },
847
+ voiceconversion: {
848
+ label: "\u97F3\u58F0\u5909\u63DB",
849
+ description: "\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0\u306E\u5909\u63DB\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
850
+ validationErrors: {}
851
+ },
852
+ "mstts:voiceconversion": {
853
+ label: "\u97F3\u58F0\u5909\u63DB",
854
+ description: "\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0\u306E\u5909\u63DB\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
855
+ validationErrors: {}
856
+ }
857
+ },
858
+ en: {
859
+ dialog: {
860
+ label: "Dialog",
861
+ description: "Groups multiple speaker turns.",
862
+ validationErrors: { turn: "Place turns directly inside a dialog." }
863
+ },
864
+ "mstts:dialog": {
865
+ label: "Dialog",
866
+ description: "Groups multiple speaker turns.",
867
+ validationErrors: { turn: "Place turns directly inside a dialog." }
868
+ },
869
+ turn: {
870
+ label: "Dialog turn",
871
+ description: "Specifies a speaker and utterance within a dialog.",
872
+ validationErrors: { voice: "Provide a voice or speaker." }
873
+ },
874
+ "mstts:turn": {
875
+ label: "Dialog turn",
876
+ description: "Specifies a speaker and utterance within a dialog.",
877
+ validationErrors: { voice: "Provide a voice or speaker." }
878
+ },
879
+ backgroundaudio: {
880
+ label: "Background audio",
881
+ description: "Specifies an audio file played behind synthesized speech.",
882
+ validationErrors: { src: "Provide a background audio URL." }
883
+ },
884
+ "mstts:backgroundaudio": {
885
+ label: "Background audio",
886
+ description: "Specifies an audio file played behind synthesized speech.",
887
+ validationErrors: { src: "Provide a background audio URL." }
888
+ },
889
+ ttsembedding: {
890
+ label: "TTS embedding",
891
+ description: "Embeds custom voice or speaker profile metadata.",
892
+ validationErrors: { speakerProfileId: "Provide a speaker profile ID." }
893
+ },
894
+ "mstts:ttsembedding": {
895
+ label: "TTS embedding",
896
+ description: "Embeds custom voice or speaker profile metadata.",
897
+ validationErrors: { speakerProfileId: "Provide a speaker profile ID." }
898
+ },
899
+ embedding: {
900
+ label: "Embedding",
901
+ description: "Specifies speaker embedding metadata.",
902
+ validationErrors: {}
903
+ },
904
+ "mstts:embedding": {
905
+ label: "Embedding",
906
+ description: "Specifies speaker embedding metadata.",
907
+ validationErrors: {}
908
+ },
909
+ silence: {
910
+ label: "Silence",
911
+ description: "Adds silence before, after, or around punctuation.",
912
+ validationErrors: { value: "Provide a silence duration." }
913
+ },
914
+ "mstts:silence": {
915
+ label: "Silence",
916
+ description: "Adds silence before, after, or around punctuation.",
917
+ validationErrors: { value: "Provide a silence duration." }
918
+ },
919
+ voiceconversion: {
920
+ label: "Voice conversion",
921
+ description: "Specifies custom voice conversion metadata.",
922
+ validationErrors: {}
923
+ },
924
+ "mstts:voiceconversion": {
925
+ label: "Voice conversion",
926
+ description: "Specifies custom voice conversion metadata.",
927
+ validationErrors: {}
928
+ }
929
+ }
930
+ };
785
931
  var EDITOR_COPY = {
786
932
  ja: {
787
933
  editorAriaLabel: "SSML\u30A8\u30C7\u30A3\u30BF\u30FC",
@@ -999,6 +1145,42 @@ var SSML_HOVER_COPY = {
999
1145
  parameters: {
1000
1146
  value: { title: "value", description: "\u76EE\u6A19\u6642\u9593\u3002\u4F8B: `10s`\u3001`5000ms`\u3001`00:00:10`\u3002" }
1001
1147
  }
1148
+ },
1149
+ "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: {} },
1150
+ "mstts:turn": {
1151
+ title: "\u8A71\u8005\u30BF\u30FC\u30F3",
1152
+ description: "\u30C0\u30A4\u30A2\u30ED\u30B0\u5185\u306E\u8A71\u8005\u3068\u767A\u8A71\u5185\u5BB9\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
1153
+ parameters: {
1154
+ voice: { title: "voice", description: "\u3053\u306E\u30BF\u30FC\u30F3\u3067\u4F7F\u7528\u3059\u308B Azure \u97F3\u58F0\u540D\u3002" },
1155
+ speaker: { title: "speaker", description: "\u8A71\u8005\u8B58\u5225\u5B50\u3002" }
1156
+ }
1157
+ },
1158
+ "mstts:backgroundaudio": {
1159
+ title: "\u80CC\u666F\u97F3\u58F0",
1160
+ 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",
1161
+ parameters: { src: { title: "src", description: "\u97F3\u58F0\u30D5\u30A1\u30A4\u30EB\u306E HTTPS URL\u3002" } }
1162
+ },
1163
+ "mstts:ttsembedding": {
1164
+ title: "\u97F3\u58F0\u57CB\u3081\u8FBC\u307F",
1165
+ 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",
1166
+ parameters: { speakerProfileId: { title: "speakerProfileId", description: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID\u3002" } }
1167
+ },
1168
+ "mstts:embedding": {
1169
+ title: "\u57CB\u3081\u8FBC\u307F",
1170
+ description: "\u8A71\u8005\u57CB\u3081\u8FBC\u307F\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
1171
+ parameters: {
1172
+ id: { title: "id", description: "\u57CB\u3081\u8FBC\u307F ID\u3002" },
1173
+ speakerProfileId: { title: "speakerProfileId", description: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID\u3002" }
1174
+ }
1175
+ },
1176
+ "mstts:voiceconversion": {
1177
+ title: "\u97F3\u58F0\u5909\u63DB",
1178
+ description: "\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0\u306E\u5909\u63DB\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
1179
+ parameters: {
1180
+ url: { title: "url", description: "\u97F3\u58F0\u5909\u63DB\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB URL\u3002" },
1181
+ profile: { title: "profile", description: "\u5909\u63DB\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB\u3002" },
1182
+ speakerProfileId: { title: "speakerProfileId", description: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID\u3002" }
1183
+ }
1002
1184
  }
1003
1185
  }
1004
1186
  },
@@ -1163,6 +1345,42 @@ var SSML_HOVER_COPY = {
1163
1345
  parameters: {
1164
1346
  value: { title: "value", description: "The target duration, such as `10s`, `5000ms`, or `00:00:10`." }
1165
1347
  }
1348
+ },
1349
+ "mstts:dialog": { title: "Dialog", description: "Groups multiple speaker turns.", parameters: {} },
1350
+ "mstts:turn": {
1351
+ title: "Dialog turn",
1352
+ description: "Specifies a speaker and utterance within a dialog.",
1353
+ parameters: {
1354
+ voice: { title: "voice", description: "The Azure voice used by this turn." },
1355
+ speaker: { title: "speaker", description: "The speaker identifier." }
1356
+ }
1357
+ },
1358
+ "mstts:backgroundaudio": {
1359
+ title: "Background audio",
1360
+ description: "Specifies an audio file played behind synthesized speech.",
1361
+ parameters: { src: { title: "src", description: "The HTTPS URL of the audio file." } }
1362
+ },
1363
+ "mstts:ttsembedding": {
1364
+ title: "TTS embedding",
1365
+ description: "Embeds custom voice or speaker profile metadata.",
1366
+ parameters: { speakerProfileId: { title: "speakerProfileId", description: "The speaker profile ID." } }
1367
+ },
1368
+ "mstts:embedding": {
1369
+ title: "Embedding",
1370
+ description: "Specifies speaker embedding metadata.",
1371
+ parameters: {
1372
+ id: { title: "id", description: "The embedding ID." },
1373
+ speakerProfileId: { title: "speakerProfileId", description: "The speaker profile ID." }
1374
+ }
1375
+ },
1376
+ "mstts:voiceconversion": {
1377
+ title: "Voice conversion",
1378
+ description: "Specifies custom voice conversion metadata.",
1379
+ parameters: {
1380
+ url: { title: "url", description: "The voice conversion profile URL." },
1381
+ profile: { title: "profile", description: "The conversion profile." },
1382
+ speakerProfileId: { title: "speakerProfileId", description: "The speaker profile ID." }
1383
+ }
1166
1384
  }
1167
1385
  }
1168
1386
  }
@@ -4695,6 +4913,221 @@ function updateOptionalElementProperty(document2, path, property, value) {
4695
4913
  return next;
4696
4914
  });
4697
4915
  }
4916
+ var VISUAL_ELEMENT_FIELDS = {
4917
+ voice: [
4918
+ { key: "name", label: "Voice name" },
4919
+ { key: "effect", label: "Effect" }
4920
+ ],
4921
+ prosody: [
4922
+ { key: "rate", label: "Rate" },
4923
+ { key: "pitch", label: "Pitch" },
4924
+ { key: "volume", label: "Volume" },
4925
+ { key: "contour", label: "Contour" },
4926
+ { key: "range", label: "Range" }
4927
+ ],
4928
+ "express-as": [
4929
+ { key: "style", label: "Style" },
4930
+ { key: "styleDegree", label: "Style degree" },
4931
+ { key: "role", label: "Role" }
4932
+ ],
4933
+ expressAs: [
4934
+ { key: "style", label: "Style" },
4935
+ { key: "styleDegree", label: "Style degree" },
4936
+ { key: "role", label: "Role" }
4937
+ ],
4938
+ "mstts:express-as": [
4939
+ { key: "style", label: "Style" },
4940
+ { key: "styleDegree", label: "Style degree" },
4941
+ { key: "role", label: "Role" }
4942
+ ],
4943
+ "say-as": [
4944
+ { key: "interpretAs", label: "Interpret as" },
4945
+ { key: "format", label: "Format" },
4946
+ { key: "detail", label: "Detail" }
4947
+ ],
4948
+ sayAs: [
4949
+ { key: "interpretAs", label: "Interpret as" },
4950
+ { key: "format", label: "Format" },
4951
+ { key: "detail", label: "Detail" }
4952
+ ],
4953
+ phoneme: [
4954
+ { key: "alphabet", label: "Alphabet" },
4955
+ { key: "ph", label: "Pronunciation" }
4956
+ ],
4957
+ emphasis: [{ key: "level", label: "Level" }],
4958
+ audio: [
4959
+ { key: "src", label: "Source URL" },
4960
+ { key: "desc", label: "Description" },
4961
+ { key: "clipBegin", label: "Clip begin" },
4962
+ { key: "clipEnd", label: "Clip end" },
4963
+ { key: "speed", label: "Speed" },
4964
+ { key: "repeatCount", label: "Repeat count" },
4965
+ { key: "repeatDuration", label: "Repeat duration" },
4966
+ { key: "soundLevel", label: "Sound level" }
4967
+ ],
4968
+ mark: [{ key: "name", label: "Mark name" }],
4969
+ bookmark: [{ key: "mark", label: "Bookmark name" }],
4970
+ sub: [{ key: "alias", label: "Alias" }],
4971
+ lang: [{ key: "lang", label: "Language" }],
4972
+ lexicon: [{ key: "uri", label: "Lexicon URI" }],
4973
+ "mstts:silence": [
4974
+ { key: "typeValue", label: "Silence type" },
4975
+ { key: "value", label: "Value" }
4976
+ ],
4977
+ silence: [
4978
+ { key: "typeValue", label: "Silence type" },
4979
+ { key: "value", label: "Value" }
4980
+ ],
4981
+ "mstts:audioduration": [{ key: "value", label: "Duration" }],
4982
+ "mstts:ttsembedding": [{ key: "speakerProfileId", label: "Speaker profile ID" }],
4983
+ "mstts:embedding": [
4984
+ { key: "id", label: "Embedding ID" },
4985
+ { key: "speakerProfileId", label: "Speaker profile ID" }
4986
+ ],
4987
+ "mstts:voiceconversion": [
4988
+ { key: "url", label: "Source URL" },
4989
+ { key: "profile", label: "Profile" },
4990
+ { key: "speakerProfileId", label: "Speaker profile ID" }
4991
+ ]
4992
+ };
4993
+ function getElementFields(element) {
4994
+ return VISUAL_ELEMENT_FIELDS[elementLabel(element)] ?? [];
4995
+ }
4996
+ function localizedElementLabel(element, locale) {
4997
+ return SSML_ELEMENT_COPY[locale][elementLabel(element)]?.label ?? elementLabel(element);
4998
+ }
4999
+ function filteredVoices(voiceCatalog, locale, region, style) {
5000
+ return (voiceCatalog ?? []).filter((voice) => {
5001
+ if (locale && voice.locale.toLowerCase() !== locale.toLowerCase()) return false;
5002
+ if (region && voice.region && voice.region.toLowerCase() !== region.toLowerCase()) return false;
5003
+ if (style && !voice.styles?.some((candidate) => candidate.toLowerCase() === style.toLowerCase())) return false;
5004
+ return true;
5005
+ });
5006
+ }
5007
+ function DefaultVoiceSelector({ value, voices, readOnly, locale, onChange }) {
5008
+ return /* @__PURE__ */ jsxs2(
5009
+ "select",
5010
+ {
5011
+ "aria-label": locale === "ja" ? "\u97F3\u58F0" : "Voice",
5012
+ value,
5013
+ disabled: readOnly,
5014
+ onChange: (event) => onChange(event.target.value),
5015
+ children: [
5016
+ /* @__PURE__ */ jsx6("option", { value: "", children: locale === "ja" ? "\u97F3\u58F0\u3092\u9078\u629E" : "Select a voice" }),
5017
+ voices.map((voice) => /* @__PURE__ */ jsxs2("option", { value: voice.name, children: [
5018
+ voice.name,
5019
+ voice.preview || voice.status === "preview" ? " (preview)" : ""
5020
+ ] }, voice.name))
5021
+ ]
5022
+ }
5023
+ );
5024
+ }
5025
+ function VisualElementInspector({
5026
+ document: document2,
5027
+ element,
5028
+ path,
5029
+ readOnly,
5030
+ commit,
5031
+ locale,
5032
+ customInspector,
5033
+ renderVoiceSelector,
5034
+ voiceCatalog,
5035
+ voiceLocale,
5036
+ voiceRegion,
5037
+ voiceStyle
5038
+ }) {
5039
+ if (customInspector) {
5040
+ return /* @__PURE__ */ jsx6(Fragment3, { children: customInspector({ document: document2, element, path, readOnly, onChange: commit, locale }) });
5041
+ }
5042
+ const fields = getElementFields(element);
5043
+ const availableVoices = filteredVoices(voiceCatalog, voiceLocale, voiceRegion, voiceStyle);
5044
+ const renderSelector = renderVoiceSelector ?? DefaultVoiceSelector;
5045
+ return /* @__PURE__ */ jsxs2("fieldset", { children: [
5046
+ /* @__PURE__ */ jsx6("legend", { children: `<${elementLabel(element)}>` }),
5047
+ fields.length === 0 ? /* @__PURE__ */ jsx6("p", { children: "This element is preserved in the visual tree. Edit its attributes in Code mode." }) : fields.map((field) => {
5048
+ const value = element[field.key];
5049
+ const inputValue = value === void 0 ? "" : String(value);
5050
+ const inputId = `ssml-visual-${field.key}`;
5051
+ return /* @__PURE__ */ jsxs2("label", { htmlFor: inputId, children: [
5052
+ locale === "ja" ? {
5053
+ name: "\u540D\u524D",
5054
+ effect: "\u52B9\u679C",
5055
+ rate: "\u901F\u5EA6",
5056
+ pitch: "\u30D4\u30C3\u30C1",
5057
+ volume: "\u97F3\u91CF",
5058
+ contour: "\u30D4\u30C3\u30C1\u66F2\u7DDA",
5059
+ range: "\u7BC4\u56F2",
5060
+ style: "\u30B9\u30BF\u30A4\u30EB",
5061
+ styleDegree: "\u30B9\u30BF\u30A4\u30EB\u5F37\u5EA6",
5062
+ role: "\u5F79\u5272",
5063
+ interpretAs: "\u89E3\u91C8",
5064
+ alphabet: "\u30A2\u30EB\u30D5\u30A1\u30D9\u30C3\u30C8",
5065
+ ph: "\u767A\u97F3",
5066
+ level: "\u5F37\u8ABF\u30EC\u30D9\u30EB",
5067
+ src: "\u97F3\u58F0 URL",
5068
+ desc: "\u8AAC\u660E",
5069
+ clipBegin: "\u958B\u59CB\u4F4D\u7F6E",
5070
+ clipEnd: "\u7D42\u4E86\u4F4D\u7F6E",
5071
+ speed: "\u901F\u5EA6",
5072
+ repeatCount: "\u7E70\u308A\u8FD4\u3057\u56DE\u6570",
5073
+ repeatDuration: "\u7E70\u308A\u8FD4\u3057\u6642\u9593",
5074
+ soundLevel: "\u97F3\u91CF\u30EC\u30D9\u30EB",
5075
+ mark: "\u30D6\u30C3\u30AF\u30DE\u30FC\u30AF\u540D",
5076
+ alias: "\u5225\u540D",
5077
+ lang: "\u8A00\u8A9E",
5078
+ uri: "\u8F9E\u66F8 URI",
5079
+ typeValue: "\u7121\u97F3\u306E\u7A2E\u985E",
5080
+ value: "\u5024",
5081
+ id: "\u57CB\u3081\u8FBC\u307F ID",
5082
+ speakerProfileId: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID",
5083
+ url: "\u97F3\u58F0\u5909\u63DB URL",
5084
+ profile: "\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB"
5085
+ }[field.key] ?? field.label : field.label,
5086
+ field.key === "name" && elementLabel(element) === "voice" && voiceCatalog ? renderSelector({
5087
+ value: inputValue,
5088
+ voices: availableVoices,
5089
+ readOnly,
5090
+ locale,
5091
+ onChange: (value2) => commit(updateOptionalElementProperty(document2, path, field.key, value2))
5092
+ }) : field.multiline ? /* @__PURE__ */ jsx6(
5093
+ "textarea",
5094
+ {
5095
+ id: inputId,
5096
+ value: inputValue,
5097
+ readOnly,
5098
+ onChange: (event) => commit(updateOptionalElementProperty(document2, path, field.key, event.target.value))
5099
+ }
5100
+ ) : /* @__PURE__ */ jsx6(
5101
+ "input",
5102
+ {
5103
+ id: inputId,
5104
+ value: inputValue,
5105
+ readOnly,
5106
+ onChange: (event) => commit(updateOptionalElementProperty(document2, path, field.key, event.target.value))
5107
+ }
5108
+ )
5109
+ ] }, field.key);
5110
+ })
5111
+ ] });
5112
+ }
5113
+ function getElementAncestors(nodes, path, ancestors = []) {
5114
+ const [index, ...rest] = path;
5115
+ const node = nodes[index];
5116
+ if (!node || !isElement(node)) return ancestors;
5117
+ if (rest.length === 0) return ancestors;
5118
+ return getElementAncestors(node.children ?? [], rest, [...ancestors, node]);
5119
+ }
5120
+ function buildVisualPreview(document2, leaf, selection) {
5121
+ const start = Math.max(0, Math.min(selection.start, leaf.value.length));
5122
+ const end = Math.max(start, Math.min(selection.end, leaf.value.length));
5123
+ const value = start === end ? leaf.value : leaf.value.slice(start, end);
5124
+ const ancestors = getElementAncestors(document2.children ?? [], leaf.path);
5125
+ const children = ancestors.reduceRight(
5126
+ (current, ancestor) => [{ ...ancestor, children: current }],
5127
+ [value]
5128
+ );
5129
+ return buildSsml3({ ...document2, children });
5130
+ }
4698
5131
  function addDialogTurn(document2, path) {
4699
5132
  return updateElementAtPath(document2, path, (element) => ({
4700
5133
  ...element,
@@ -4752,13 +5185,21 @@ function VisualSsmlEditor({
4752
5185
  document: document2,
4753
5186
  readOnly = false,
4754
5187
  onChange,
4755
- onPreviewSelection
5188
+ onPreviewSelection,
5189
+ locale = "en",
5190
+ customInspectors,
5191
+ renderVoiceSelector,
5192
+ voiceCatalog,
5193
+ voiceLocale,
5194
+ voiceRegion,
5195
+ voiceStyle
4756
5196
  }) {
4757
5197
  const [selectedPath, setSelectedPath] = useState2(null);
4758
5198
  const [selection, setSelection] = useState2({ start: 0, end: 0 });
4759
5199
  const textLeaves = useMemo(() => collectTextLeaves(document2.children ?? []), [document2]);
4760
5200
  const selectedLeaf = textLeaves.find((leaf) => leaf.path.join(".") === selectedPath?.join(".")) ?? textLeaves[0];
4761
5201
  const selectedElement = selectedPath ? getNodeAtPath(document2.children ?? [], selectedPath) : void 0;
5202
+ const selectedCustomInspector = selectedElement && isElement(selectedElement) ? customInspectors?.[elementLabel(selectedElement)] ?? customInspectors?.[selectedElement.type] : void 0;
4762
5203
  const diagnostics = useMemo(() => validateAzureSsml(buildSsml3(document2)), [document2]);
4763
5204
  const commit = (nextDocument) => onChange?.(nextDocument);
4764
5205
  const applyWrapper = (tag, attributes) => {
@@ -4793,25 +5234,39 @@ function VisualSsmlEditor({
4793
5234
  isElement(node) ? elementLabel(node) : JSON.stringify(node)
4794
5235
  )) })
4795
5236
  ] }),
4796
- /* @__PURE__ */ jsx6("div", { className: "ssml-editor-visual-form", children: selectedElement && isElement(selectedElement) && selectedElement.type === "mstts:dialog" ? /* @__PURE__ */ jsxs2("fieldset", { children: [
4797
- /* @__PURE__ */ jsx6("legend", { children: "Dialog" }),
4798
- /* @__PURE__ */ jsx6("p", { children: "Add and edit speaker turns in this dialog." }),
5237
+ /* @__PURE__ */ jsx6("div", { className: "ssml-editor-visual-form", children: selectedCustomInspector && selectedElement && isElement(selectedElement) ? selectedCustomInspector({
5238
+ document: document2,
5239
+ element: selectedElement,
5240
+ path: selectedPath ?? [],
5241
+ readOnly,
5242
+ onChange: commit,
5243
+ locale
5244
+ }) : selectedElement && isElement(selectedElement) && selectedElement.type === "mstts:dialog" ? /* @__PURE__ */ jsxs2("fieldset", { children: [
5245
+ /* @__PURE__ */ jsx6("legend", { children: localizedElementLabel(selectedElement, locale) }),
5246
+ /* @__PURE__ */ jsx6("p", { children: SSML_ELEMENT_COPY[locale][elementLabel(selectedElement)]?.description }),
4799
5247
  /* @__PURE__ */ jsx6(
4800
5248
  "button",
4801
5249
  {
4802
5250
  type: "button",
4803
5251
  disabled: readOnly,
4804
5252
  onClick: () => commit(addDialogTurn(document2, selectedPath ?? [])),
4805
- children: "Add turn"
5253
+ children: locale === "ja" ? "\u30BF\u30FC\u30F3\u3092\u8FFD\u52A0" : "Add turn"
4806
5254
  }
4807
5255
  )
4808
5256
  ] }) : selectedElement && isElement(selectedElement) && selectedElement.type === "mstts:turn" ? /* @__PURE__ */ jsxs2("fieldset", { children: [
4809
- /* @__PURE__ */ jsx6("legend", { children: "Dialog turn" }),
4810
- /* @__PURE__ */ jsxs2("label", { children: [
4811
- "Voice",
4812
- /* @__PURE__ */ jsx6(
5257
+ /* @__PURE__ */ jsx6("legend", { children: localizedElementLabel(selectedElement, locale) }),
5258
+ /* @__PURE__ */ jsxs2("label", { htmlFor: "ssml-visual-turn-voice", children: [
5259
+ locale === "ja" ? "\u97F3\u58F0" : "Voice",
5260
+ voiceCatalog ? (renderVoiceSelector ?? DefaultVoiceSelector)({
5261
+ value: selectedElement.voice ?? "",
5262
+ voices: filteredVoices(voiceCatalog, voiceLocale, voiceRegion, voiceStyle),
5263
+ readOnly,
5264
+ locale,
5265
+ onChange: (value) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "voice", value))
5266
+ }) : /* @__PURE__ */ jsx6(
4813
5267
  "input",
4814
5268
  {
5269
+ id: "ssml-visual-turn-voice",
4815
5270
  value: selectedElement.voice ?? "",
4816
5271
  readOnly,
4817
5272
  onChange: (event) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "voice", event.target.value))
@@ -4819,7 +5274,7 @@ function VisualSsmlEditor({
4819
5274
  )
4820
5275
  ] }),
4821
5276
  /* @__PURE__ */ jsxs2("label", { children: [
4822
- "Speaker",
5277
+ locale === "ja" ? "\u8A71\u8005" : "Speaker",
4823
5278
  /* @__PURE__ */ jsx6(
4824
5279
  "input",
4825
5280
  {
@@ -4830,7 +5285,7 @@ function VisualSsmlEditor({
4830
5285
  )
4831
5286
  ] }),
4832
5287
  /* @__PURE__ */ jsxs2("label", { children: [
4833
- "Turn text",
5288
+ locale === "ja" ? "\u767A\u8A71\u30C6\u30AD\u30B9\u30C8" : "Turn text",
4834
5289
  /* @__PURE__ */ jsx6(
4835
5290
  "textarea",
4836
5291
  {
@@ -4847,9 +5302,9 @@ function VisualSsmlEditor({
4847
5302
  )
4848
5303
  ] })
4849
5304
  ] }) : selectedElement && isElement(selectedElement) && selectedElement.type === "mstts:backgroundaudio" ? /* @__PURE__ */ jsxs2("fieldset", { children: [
4850
- /* @__PURE__ */ jsx6("legend", { children: "Background audio" }),
5305
+ /* @__PURE__ */ jsx6("legend", { children: localizedElementLabel(selectedElement, locale) }),
4851
5306
  /* @__PURE__ */ jsxs2("label", { children: [
4852
- "Source URL",
5307
+ locale === "ja" ? "\u97F3\u58F0 URL" : "Source URL",
4853
5308
  /* @__PURE__ */ jsx6(
4854
5309
  "input",
4855
5310
  {
@@ -4860,7 +5315,7 @@ function VisualSsmlEditor({
4860
5315
  )
4861
5316
  ] }),
4862
5317
  /* @__PURE__ */ jsxs2("label", { children: [
4863
- "Volume",
5318
+ locale === "ja" ? "\u97F3\u91CF" : "Volume",
4864
5319
  /* @__PURE__ */ jsx6(
4865
5320
  "input",
4866
5321
  {
@@ -4871,7 +5326,7 @@ function VisualSsmlEditor({
4871
5326
  )
4872
5327
  ] }),
4873
5328
  /* @__PURE__ */ jsxs2("label", { children: [
4874
- "Fade in",
5329
+ locale === "ja" ? "\u30D5\u30A7\u30FC\u30C9\u30A4\u30F3" : "Fade in",
4875
5330
  /* @__PURE__ */ jsx6(
4876
5331
  "input",
4877
5332
  {
@@ -4882,7 +5337,7 @@ function VisualSsmlEditor({
4882
5337
  )
4883
5338
  ] }),
4884
5339
  /* @__PURE__ */ jsxs2("label", { children: [
4885
- "Fade out",
5340
+ locale === "ja" ? "\u30D5\u30A7\u30FC\u30C9\u30A2\u30A6\u30C8" : "Fade out",
4886
5341
  /* @__PURE__ */ jsx6(
4887
5342
  "input",
4888
5343
  {
@@ -4892,7 +5347,23 @@ function VisualSsmlEditor({
4892
5347
  }
4893
5348
  )
4894
5349
  ] })
4895
- ] }) : selectedLeaf ? /* @__PURE__ */ jsxs2(Fragment3, { children: [
5350
+ ] }) : selectedElement && isElement(selectedElement) ? /* @__PURE__ */ jsx6(
5351
+ VisualElementInspector,
5352
+ {
5353
+ document: document2,
5354
+ element: selectedElement,
5355
+ path: selectedPath ?? [],
5356
+ readOnly,
5357
+ commit,
5358
+ locale,
5359
+ customInspector: customInspectors?.[elementLabel(selectedElement)] ?? customInspectors?.[selectedElement.type],
5360
+ renderVoiceSelector,
5361
+ voiceCatalog,
5362
+ voiceLocale,
5363
+ voiceRegion,
5364
+ voiceStyle
5365
+ }
5366
+ ) : selectedLeaf ? /* @__PURE__ */ jsxs2(Fragment3, { children: [
4896
5367
  /* @__PURE__ */ jsxs2("label", { children: [
4897
5368
  "Text",
4898
5369
  /* @__PURE__ */ jsx6(
@@ -4932,7 +5403,7 @@ function VisualSsmlEditor({
4932
5403
  "button",
4933
5404
  {
4934
5405
  type: "button",
4935
- onClick: () => onPreviewSelection(buildSsml3({ ...document2, children: [selectedLeaf.value] })),
5406
+ onClick: () => onPreviewSelection(buildVisualPreview(document2, selectedLeaf, selection)),
4936
5407
  children: "Preview selection"
4937
5408
  }
4938
5409
  )
@@ -5529,6 +6000,12 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5529
6000
  insertionGroups,
5530
6001
  customInsertions,
5531
6002
  additionalInsertions,
6003
+ customInspectors,
6004
+ renderVoiceSelector,
6005
+ voiceCatalog,
6006
+ voiceLocale,
6007
+ voiceRegion,
6008
+ voiceStyle,
5532
6009
  emotionStyles,
5533
6010
  className,
5534
6011
  style,
@@ -5970,7 +6447,14 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5970
6447
  document: draftDocument,
5971
6448
  readOnly: isReadOnly,
5972
6449
  onChange: commit,
5973
- onPreviewSelection
6450
+ onPreviewSelection,
6451
+ locale: localeProp ?? languageProp,
6452
+ customInspectors,
6453
+ renderVoiceSelector,
6454
+ voiceCatalog,
6455
+ voiceLocale,
6456
+ voiceRegion,
6457
+ voiceStyle
5974
6458
  }
5975
6459
  ) }) : /* @__PURE__ */ jsxs3("div", { style: { ...editorStyles.editor, minHeight: editorMinHeight }, children: [
5976
6460
  /* @__PURE__ */ jsx7(
@@ -6048,6 +6532,7 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
6048
6532
  export {
6049
6533
  EDITOR_COPY,
6050
6534
  INLINE_BADGE_COPY,
6535
+ SSML_ELEMENT_COPY,
6051
6536
  SSML_HOVER_COPY,
6052
6537
  SSML_INSERTIONS,
6053
6538
  SsmlEditor,