@ssml-builder-js/ssml-editor-react 2.8.0 → 2.9.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
@@ -6,7 +6,7 @@ var __export = (target, all) => {
6
6
  };
7
7
 
8
8
  // src/SsmlEditor.tsx
9
- import { Fragment as Fragment3, forwardRef, useImperativeHandle } from "react";
9
+ import { Fragment as Fragment4, forwardRef, useEffect as useEffect3, useImperativeHandle, useState as useState3 } from "react";
10
10
  import Editor from "@monaco-editor/react";
11
11
 
12
12
  // src/buttonVisibility.ts
@@ -17,6 +17,8 @@ function isSsmlEditorButtonVisible(buttonVisibility, button) {
17
17
  // src/constants/ssmlPresets.ts
18
18
  var ssmlPresets_exports = {};
19
19
  __export(ssmlPresets_exports, {
20
+ AUDIO_DURATION_DESCRIPTIONS: () => AUDIO_DURATION_DESCRIPTIONS,
21
+ AUDIO_DURATION_PRESETS: () => AUDIO_DURATION_PRESETS,
20
22
  BREAK_STRENGTH_PRESETS: () => BREAK_STRENGTH_PRESETS,
21
23
  BREAK_TIME_DESCRIPTIONS: () => BREAK_TIME_DESCRIPTIONS,
22
24
  BREAK_TIME_PRESETS: () => BREAK_TIME_PRESETS,
@@ -49,6 +51,103 @@ __export(ssmlPresets_exports, {
49
51
  getExpressAsStyleCategory: () => getExpressAsStyleCategory,
50
52
  resolveExpressAsStyles: () => resolveExpressAsStyles
51
53
  });
54
+
55
+ // src/constants/azureVoiceStyleMap.generated.ts
56
+ var AZURE_VOICE_STYLE_MAP = {
57
+ "de-DE-ConradNeural": ["cheerful", "sad"],
58
+ "de-DE-KatjaNeural": ["cheerful", "sad"],
59
+ "en-US-AndrewNeural": ["empathetic", "relieved"],
60
+ "en-US-GuyNeural": [
61
+ "angry",
62
+ "cheerful",
63
+ "excited",
64
+ "friendly",
65
+ "hopeful",
66
+ "newscast",
67
+ "sad",
68
+ "shouting",
69
+ "terrified",
70
+ "unfriendly",
71
+ "whispering"
72
+ ],
73
+ "en-US-JennyMultilingualNeural": [
74
+ "cheerful",
75
+ "empathetic",
76
+ "excited",
77
+ "friendly",
78
+ "hopeful",
79
+ "sad",
80
+ "shouting",
81
+ "terrified",
82
+ "unfriendly",
83
+ "whispering"
84
+ ],
85
+ "en-US-JennyNeural": [
86
+ "assistant",
87
+ "chat",
88
+ "customerservice",
89
+ "newscast",
90
+ "cheerful",
91
+ "empathetic",
92
+ "excited",
93
+ "friendly",
94
+ "hopeful",
95
+ "sad",
96
+ "shouting",
97
+ "terrified",
98
+ "unfriendly",
99
+ "whispering"
100
+ ],
101
+ "es-ES-ElviraNeural": [],
102
+ "fil-PH-AngeloNeural": [],
103
+ "fr-FR-DeniseNeural": ["cheerful", "sad"],
104
+ "fr-FR-HenriNeural": ["cheerful", "sad"],
105
+ "id-ID-GadisNeural": [],
106
+ "it-IT-ElsaNeural": ["cheerful", "sad"],
107
+ "ja-JP-KeitaNeural": ["chat"],
108
+ "ja-JP-MayuNeural": ["calm", "cheerful", "sad"],
109
+ "ja-JP-NanamiNeural": ["chat", "customerservice", "cheerful", "whispering", "sad"],
110
+ "ko-KR-SunHiNeural": ["cheerful", "sad"],
111
+ "ms-MY-YasminNeural": [],
112
+ "pt-BR-FranciscaNeural": ["calm"],
113
+ "ru-RU-SvetlanaNeural": ["cheerful", "sad", "angry", "disgruntled", "embarrassed", "fearful"],
114
+ "th-TH-PremwadeeNeural": [],
115
+ "vi-VN-HoaiMyNeural": [],
116
+ "zh-CN-XiaoxiaoNeural": [
117
+ "assistant",
118
+ "chat",
119
+ "customerservice",
120
+ "newscast",
121
+ "cheerful",
122
+ "empathetic",
123
+ "excited",
124
+ "friendly",
125
+ "hopeful",
126
+ "sad",
127
+ "terrified",
128
+ "whispering",
129
+ "poetry-reading",
130
+ "sports_commentary",
131
+ "sports_commentary_excited",
132
+ "story"
133
+ ],
134
+ "zh-CN-YunxiNeural": [
135
+ "narration-relaxed",
136
+ "embarrassed",
137
+ "fearful",
138
+ "sad",
139
+ "disgruntled",
140
+ "serious",
141
+ "angry",
142
+ "depressed",
143
+ "chat",
144
+ "cheerful",
145
+ "assistant"
146
+ ],
147
+ "zh-TW-HsiaoChenNeural": []
148
+ };
149
+
150
+ // src/constants/ssmlPresets.ts
52
151
  var SSML_PRESETS = [
53
152
  {
54
153
  id: "basic",
@@ -148,7 +247,7 @@ function getExpressAsStyleCategory(style) {
148
247
  }
149
248
  return "other";
150
249
  }
151
- var VOICE_STYLE_MAP = {
250
+ var LEGACY_VOICE_STYLE_MAP = {
152
251
  "ja-JP-MayuNeural": ["calm", "cheerful", "sad"],
153
252
  "ja-JP-KeitaNeural": ["chat"],
154
253
  "ja-JP-NanamiNeural": ["chat", "customerservice", "cheerful", "whispering", "sad"],
@@ -234,6 +333,7 @@ var VOICE_STYLE_MAP = {
234
333
  "de-DE-ConradNeural": ["cheerful", "sad"],
235
334
  "ru-RU-SvetlanaNeural": ["cheerful", "sad", "angry", "disgruntled", "embarrassed", "fearful"]
236
335
  };
336
+ var VOICE_STYLE_MAP = Object.keys(AZURE_VOICE_STYLE_MAP).length > 0 ? AZURE_VOICE_STYLE_MAP : LEGACY_VOICE_STYLE_MAP;
237
337
  var VOICE_STYLE_MAP_BY_NORMALIZED_NAME = new Map(
238
338
  Object.entries(VOICE_STYLE_MAP).map(([voiceName, styles]) => [voiceName.toLowerCase(), styles])
239
339
  );
@@ -276,6 +376,7 @@ var SAY_AS_PRESETS = [
276
376
  ];
277
377
  var LANGUAGE_PRESETS = ["ja-JP", "en-US", "de-DE", "fr-FR"];
278
378
  var SILENCE_VALUE_PRESETS = ["300ms", "500ms", "1s"];
379
+ var AUDIO_DURATION_PRESETS = ["5s", "10s", "30s"];
279
380
  var SILENCE_TYPE_PRESETS = [
280
381
  "Leading",
281
382
  "Tailing",
@@ -284,6 +385,11 @@ var SILENCE_TYPE_PRESETS = [
284
385
  "Semicolon",
285
386
  "Enumerationcomma"
286
387
  ];
388
+ var AUDIO_DURATION_DESCRIPTIONS = {
389
+ "5s": { ja: "5\u79D2", en: "5 seconds" },
390
+ "10s": { ja: "10\u79D2", en: "10 seconds" },
391
+ "30s": { ja: "30\u79D2", en: "30 seconds" }
392
+ };
287
393
  var PHONEME_ALPHABET_PRESETS = ["ipa", "sapi", "ups", "x-sampa"];
288
394
  var VISEME_TYPE_PRESETS = ["redlips_front", "FacialExpression"];
289
395
  var SSML_ATTRIBUTE_PRESETS = {
@@ -327,6 +433,14 @@ var SSML_ATTRIBUTE_PRESETS = {
327
433
  type: SILENCE_TYPE_PRESETS,
328
434
  value: SILENCE_VALUE_PRESETS
329
435
  },
436
+ "mstts:audioduration": {
437
+ value: AUDIO_DURATION_PRESETS
438
+ },
439
+ "mstts:backgroundaudio": {
440
+ volume: PROSODY_VOLUME_PRESETS,
441
+ fadein: AUDIO_DURATION_PRESETS,
442
+ fadeout: AUDIO_DURATION_PRESETS
443
+ },
330
444
  silence: {
331
445
  type: SILENCE_TYPE_PRESETS,
332
446
  value: SILENCE_VALUE_PRESETS
@@ -873,6 +987,13 @@ var SSML_HOVER_COPY = {
873
987
  parameters: {
874
988
  type: { title: "type", description: "\u30D3\u30BC\u30FC\u30E0\u30A4\u30D9\u30F3\u30C8\u306E\u5F62\u5F0F\u3002" }
875
989
  }
990
+ },
991
+ "mstts:audioduration": {
992
+ title: "\u97F3\u58F0\u9577",
993
+ description: "\u5408\u6210\u97F3\u58F0\u306E\u76EE\u6A19\u6642\u9593\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
994
+ parameters: {
995
+ value: { title: "value", description: "\u76EE\u6A19\u6642\u9593\u3002\u4F8B: `10s`\u3001`5000ms`\u3001`00:00:10`\u3002" }
996
+ }
876
997
  }
877
998
  }
878
999
  },
@@ -1030,6 +1151,13 @@ var SSML_HOVER_COPY = {
1030
1151
  parameters: {
1031
1152
  type: { title: "type", description: "The viseme event format." }
1032
1153
  }
1154
+ },
1155
+ "mstts:audioduration": {
1156
+ title: "Audio duration",
1157
+ description: "Sets the target duration of synthesized audio.",
1158
+ parameters: {
1159
+ value: { title: "value", description: "The target duration, such as `10s`, `5000ms`, or `00:00:10`." }
1160
+ }
1033
1161
  }
1034
1162
  }
1035
1163
  }
@@ -1129,6 +1257,14 @@ var editorStyles = {
1129
1257
  display: "grid",
1130
1258
  gap: "0.5rem"
1131
1259
  },
1260
+ visual: {
1261
+ display: "grid",
1262
+ gap: "0.75rem",
1263
+ minHeight: "8rem",
1264
+ padding: "0.75rem",
1265
+ border: "1px solid var(--ssml-editor-control-border)",
1266
+ borderRadius: "0.25rem"
1267
+ },
1132
1268
  toolbarIconOnly: {
1133
1269
  justifyContent: "center",
1134
1270
  minWidth: "2.25rem",
@@ -1567,6 +1703,7 @@ var INTRINSICALLY_EMPTY_ELEMENTS = /* @__PURE__ */ new Set([
1567
1703
  "lexicon",
1568
1704
  "mark",
1569
1705
  "mstts:silence",
1706
+ "mstts:audioduration",
1570
1707
  "mstts:viseme",
1571
1708
  "silence",
1572
1709
  "viseme"
@@ -2413,7 +2550,7 @@ function findSsmlVoiceContext(source, offset) {
2413
2550
  }
2414
2551
 
2415
2552
  // src/hooks/useSsmlEditorState.ts
2416
- var TIMING_INSERTION_TAGS = /* @__PURE__ */ new Set(["break", "mstts:silence"]);
2553
+ var TIMING_INSERTION_TAGS = /* @__PURE__ */ new Set(["break", "mstts:silence", "mstts:audioduration"]);
2417
2554
  var PROSODY_INSERTION_TAGS = /* @__PURE__ */ new Set(["prosody", "mstts:express-as", "voice", "emphasis"]);
2418
2555
  var TEXT_INSERTION_TAGS = /* @__PURE__ */ new Set(["sub", "say-as", "phoneme", "w", "lang"]);
2419
2556
  var EMPTY_SELECTION_OVERLAY = {
@@ -3135,6 +3272,28 @@ var SSML_COMPLETION_SNIPPETS = [
3135
3272
  label: "mstts:express-as",
3136
3273
  insertText: `<mstts:express-as style="cheerful">\${1:text}</mstts:express-as>`
3137
3274
  },
3275
+ {
3276
+ label: "mstts:audioduration",
3277
+ insertText: '<mstts:audioduration value="10s" />'
3278
+ },
3279
+ {
3280
+ label: "mstts:dialog",
3281
+ insertText: `<mstts:dialog>
3282
+ <mstts:turn voice="\${1:en-US-JennyNeural}">\${2:text}</mstts:turn>
3283
+ </mstts:dialog>`
3284
+ },
3285
+ {
3286
+ label: "mstts:turn",
3287
+ insertText: `<mstts:turn voice="\${1:en-US-JennyNeural}">\${2:text}</mstts:turn>`
3288
+ },
3289
+ {
3290
+ label: "mstts:backgroundaudio",
3291
+ insertText: `<mstts:backgroundaudio src="\${1:https://example.com/audio.mp3}" volume="\${2:-3dB}" />`
3292
+ },
3293
+ {
3294
+ label: "mstts:ttsembedding",
3295
+ insertText: `<mstts:ttsembedding>\${1:text}</mstts:ttsembedding>`
3296
+ },
3138
3297
  {
3139
3298
  label: "sub",
3140
3299
  insertText: `<sub alias="\${1:\u8AAD\u307F}">\${2:\u6F22\u5B57}</sub>`
@@ -3410,7 +3569,7 @@ function registerSsmlCodeLens(monaco, editor, onOpenPopover) {
3410
3569
  break;
3411
3570
  }
3412
3571
  const tag = source.slice(tagStart, tagEnd + 1);
3413
- const tagName = tag.match(/^<\s*(prosody|break)\b/i)?.[1]?.toLowerCase();
3572
+ const tagName = tag.match(/^<\s*(prosody|break|mstts:audioduration)\b/i)?.[1]?.toLowerCase();
3414
3573
  index = tagEnd + 1;
3415
3574
  if (!tagName) {
3416
3575
  continue;
@@ -3440,7 +3599,22 @@ function registerSsmlCodeLens(monaco, editor, onOpenPopover) {
3440
3599
  elementRange
3441
3600
  })
3442
3601
  );
3443
- } else if (/\/\s*>$/.test(tag)) {
3602
+ } else if (tagName === "mstts:audioduration" && /\/\s*>$/.test(tag)) {
3603
+ lenses.push(
3604
+ createLens(
3605
+ model,
3606
+ tagStart,
3607
+ tagEnd + 1,
3608
+ `\u26A1 Duration: ${getAttributeValue(tag, "value") ?? "default"} (Click to edit)`,
3609
+ { type: "attribute", insertionId: "mstts:audioduration", attributeName: "value", tagRange }
3610
+ ),
3611
+ createLens(model, tagStart, tagEnd + 1, "Delete", {
3612
+ type: "delete",
3613
+ tagRange,
3614
+ elementRange
3615
+ })
3616
+ );
3617
+ } else if (tagName === "break" && /\/\s*>$/.test(tag)) {
3444
3618
  lenses.push(
3445
3619
  createLens(
3446
3620
  model,
@@ -3776,6 +3950,74 @@ var SSML_TAG_DEFINITIONS = [
3776
3950
  values: VISEME_TYPE_PRESETS2
3777
3951
  }
3778
3952
  ]
3953
+ },
3954
+ {
3955
+ name: "mstts:audioduration",
3956
+ description: "Sets the target duration of synthesized audio.",
3957
+ parameters: [
3958
+ {
3959
+ name: "value",
3960
+ description: "The target duration, such as `10s`, `5000ms`, or `00:00:10`.",
3961
+ example: "10s"
3962
+ }
3963
+ ]
3964
+ },
3965
+ {
3966
+ name: "mstts:dialog",
3967
+ description: "Groups multiple Azure dialog turns that can use different voices.",
3968
+ parameters: []
3969
+ },
3970
+ {
3971
+ name: "mstts:turn",
3972
+ description: "Adds one dialog turn using the required Azure voice name.",
3973
+ parameters: [
3974
+ {
3975
+ name: "voice",
3976
+ description: "The Azure voice used for this turn, such as `en-US-JennyNeural`.",
3977
+ example: "en-US-JennyNeural"
3978
+ }
3979
+ ]
3980
+ },
3981
+ {
3982
+ name: "mstts:backgroundaudio",
3983
+ description: "Plays background audio while speech is synthesized.",
3984
+ parameters: [
3985
+ {
3986
+ name: "src",
3987
+ description: "An absolute HTTP(S) URL for the background audio file.",
3988
+ example: "https://example.com/music.mp3"
3989
+ },
3990
+ {
3991
+ name: "volume",
3992
+ description: "The background audio volume, for example `-3dB` or `medium`.",
3993
+ example: "-3dB"
3994
+ },
3995
+ {
3996
+ name: "fadein",
3997
+ description: "The fade-in duration, for example `1s`.",
3998
+ example: "1s"
3999
+ },
4000
+ {
4001
+ name: "fadeout",
4002
+ description: "The fade-out duration, for example `500ms`.",
4003
+ example: "500ms"
4004
+ }
4005
+ ]
4006
+ },
4007
+ {
4008
+ name: "mstts:ttsembedding",
4009
+ description: "Embeds custom voice or speaker profile metadata for Azure Speech.",
4010
+ parameters: []
4011
+ },
4012
+ {
4013
+ name: "mstts:embedding",
4014
+ description: "Specifies embedding metadata for custom voice scenarios.",
4015
+ parameters: []
4016
+ },
4017
+ {
4018
+ name: "mstts:voiceconversion",
4019
+ description: "Specifies voice conversion metadata for custom voice scenarios.",
4020
+ parameters: []
3779
4021
  }
3780
4022
  ];
3781
4023
  var definitionsByName = /* @__PURE__ */ new Map();
@@ -4380,10 +4622,185 @@ function useSsmlMonaco({
4380
4622
  return { editorRef, onMount };
4381
4623
  }
4382
4624
 
4625
+ // src/components/VisualSsmlEditor.tsx
4626
+ import { useMemo, useState as useState2 } from "react";
4627
+ import {
4628
+ buildSsml as buildSsml3,
4629
+ validateAzureSsml
4630
+ } from "@ssml-builder-js/ssml-core";
4631
+ import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
4632
+ function isElement(node) {
4633
+ return typeof node !== "string" && node.type !== "text";
4634
+ }
4635
+ function elementLabel(element) {
4636
+ return element.type === "custom" || element.type === "element" ? element.name : element.type;
4637
+ }
4638
+ function collectTextLeaves(nodes, path = [], ancestors = []) {
4639
+ return nodes.flatMap((node, index) => {
4640
+ const currentPath = [...path, index];
4641
+ if (typeof node === "string") return [{ ancestors, path: currentPath, value: node }];
4642
+ if (node.type === "text") return [{ ancestors, path: currentPath, value: node.value }];
4643
+ return collectTextLeaves(node.children ?? [], currentPath, [...ancestors, elementLabel(node)]);
4644
+ });
4645
+ }
4646
+ function updateNodesAtPath(nodes, path, update) {
4647
+ if (path.length === 0) return nodes;
4648
+ const [index, ...rest] = path;
4649
+ return nodes.flatMap((node, nodeIndex) => {
4650
+ if (nodeIndex !== index) return [node];
4651
+ if (rest.length === 0) {
4652
+ const next = update(node);
4653
+ return Array.isArray(next) ? next : [next];
4654
+ }
4655
+ if (!isElement(node)) return [node];
4656
+ return [{ ...node, children: updateNodesAtPath(node.children ?? [], rest, update) }];
4657
+ });
4658
+ }
4659
+ function updateTextAtPath(document2, path, value) {
4660
+ return { ...document2, children: updateNodesAtPath(document2.children ?? [], path, () => value) };
4661
+ }
4662
+ function wrapTextAtPath(document2, path, start, end, tag, attributes) {
4663
+ return {
4664
+ ...document2,
4665
+ children: updateNodesAtPath(document2.children ?? [], path, (node) => {
4666
+ const value = typeof node === "string" ? node : node.type === "text" ? node.value : "";
4667
+ if (!value || start === end) return node;
4668
+ if (tag === "break") {
4669
+ return [
4670
+ value.slice(0, start),
4671
+ { type: tag, attributes, children: [] },
4672
+ value.slice(start)
4673
+ ].filter((part) => typeof part === "string" ? part.length > 0 : true);
4674
+ }
4675
+ const selected = value.slice(start, end);
4676
+ const wrapped = { type: tag, attributes, children: [selected] };
4677
+ return [value.slice(0, start), wrapped, value.slice(end)].filter(
4678
+ (part) => typeof part === "string" ? part.length > 0 : true
4679
+ );
4680
+ })
4681
+ };
4682
+ }
4683
+ function TreeNode({
4684
+ node,
4685
+ path,
4686
+ selectedPath,
4687
+ onSelect
4688
+ }) {
4689
+ if (!isElement(node)) return null;
4690
+ const name = elementLabel(node);
4691
+ const isSelected = selectedPath?.join(".") === path.join(".");
4692
+ return /* @__PURE__ */ jsxs2("li", { children: [
4693
+ /* @__PURE__ */ jsx6("button", { type: "button", "aria-current": isSelected ? "true" : void 0, onClick: () => onSelect(path), children: `<${name}>` }),
4694
+ (node.children ?? []).length > 0 && /* @__PURE__ */ jsx6("ul", { children: node.children?.map((child, index) => /* @__PURE__ */ jsx6(
4695
+ TreeNode,
4696
+ {
4697
+ node: child,
4698
+ path: [...path, index],
4699
+ selectedPath,
4700
+ onSelect
4701
+ },
4702
+ `${path.join(".")}/${isElement(child) ? elementLabel(child) : JSON.stringify(child)}`
4703
+ )) })
4704
+ ] });
4705
+ }
4706
+ function VisualSsmlEditor({
4707
+ document: document2,
4708
+ readOnly = false,
4709
+ onChange,
4710
+ onPreviewSelection
4711
+ }) {
4712
+ const [selectedPath, setSelectedPath] = useState2(null);
4713
+ const [selection, setSelection] = useState2({ start: 0, end: 0 });
4714
+ const textLeaves = useMemo(() => collectTextLeaves(document2.children ?? []), [document2]);
4715
+ const selectedLeaf = textLeaves.find((leaf) => leaf.path.join(".") === selectedPath?.join(".")) ?? textLeaves[0];
4716
+ const diagnostics = useMemo(() => validateAzureSsml(buildSsml3(document2)), [document2]);
4717
+ const commit = (nextDocument) => onChange?.(nextDocument);
4718
+ const applyWrapper = (tag, attributes) => {
4719
+ if (readOnly || !selectedLeaf) return;
4720
+ const start = selection.start === selection.end ? 0 : selection.start;
4721
+ const end = selection.start === selection.end ? selectedLeaf.value.length : selection.end;
4722
+ commit(wrapTextAtPath(document2, selectedLeaf.path, start, end, tag, attributes));
4723
+ };
4724
+ return /* @__PURE__ */ jsxs2("div", { className: "ssml-editor-visual", "data-ssml-editor-visual": "", children: [
4725
+ /* @__PURE__ */ jsxs2("fieldset", { className: "ssml-editor-visual-breadcrumb", children: [
4726
+ /* @__PURE__ */ jsx6("legend", { children: "SSML structure breadcrumb" }),
4727
+ /* @__PURE__ */ jsx6("span", { children: "<speak>" }),
4728
+ selectedLeaf?.ancestors.map((ancestor, index) => /* @__PURE__ */ jsxs2("span", { children: [
4729
+ " / <",
4730
+ ancestor,
4731
+ ">"
4732
+ ] }, selectedLeaf?.ancestors.slice(0, index + 1).join("/"))),
4733
+ selectedPath && /* @__PURE__ */ jsx6("button", { type: "button", onClick: () => setSelectedPath(null), children: "Clear parent" })
4734
+ ] }),
4735
+ diagnostics.length > 0 && /* @__PURE__ */ jsx6("div", { role: "alert", "aria-invalid": "true", className: "ssml-editor-visual-errors", children: diagnostics.map((diagnostic) => /* @__PURE__ */ jsx6("div", { children: diagnostic.message }, `${diagnostic.code}-${diagnostic.message}`)) }),
4736
+ /* @__PURE__ */ jsxs2("div", { className: "ssml-editor-visual-layout", children: [
4737
+ /* @__PURE__ */ jsxs2("nav", { "aria-label": "SSML structure tree", className: "ssml-editor-visual-tree", children: [
4738
+ /* @__PURE__ */ jsx6("strong", { children: "Structure" }),
4739
+ /* @__PURE__ */ jsx6("ul", { children: document2.children?.map((node, index) => /* @__PURE__ */ jsx6(
4740
+ TreeNode,
4741
+ {
4742
+ node,
4743
+ path: [index],
4744
+ selectedPath,
4745
+ onSelect: setSelectedPath
4746
+ },
4747
+ isElement(node) ? elementLabel(node) : JSON.stringify(node)
4748
+ )) })
4749
+ ] }),
4750
+ /* @__PURE__ */ jsx6("div", { className: "ssml-editor-visual-form", children: selectedLeaf ? /* @__PURE__ */ jsxs2(Fragment3, { children: [
4751
+ /* @__PURE__ */ jsxs2("label", { children: [
4752
+ "Text",
4753
+ /* @__PURE__ */ jsx6(
4754
+ "textarea",
4755
+ {
4756
+ value: selectedLeaf.value,
4757
+ readOnly,
4758
+ onSelect: (event) => setSelection({ start: event.currentTarget.selectionStart, end: event.currentTarget.selectionEnd }),
4759
+ onChange: (event) => commit(updateTextAtPath(document2, selectedLeaf.path, event.target.value))
4760
+ }
4761
+ )
4762
+ ] }),
4763
+ /* @__PURE__ */ jsxs2("fieldset", { className: "ssml-editor-visual-actions", children: [
4764
+ /* @__PURE__ */ jsx6("legend", { children: "Apply SSML formatting" }),
4765
+ /* @__PURE__ */ jsx6("button", { type: "button", disabled: readOnly, onClick: () => applyWrapper("prosody", { rate: "slow" }), children: "Rate" }),
4766
+ /* @__PURE__ */ jsx6("button", { type: "button", disabled: readOnly, onClick: () => applyWrapper("prosody", { pitch: "high" }), children: "Pitch" }),
4767
+ /* @__PURE__ */ jsx6(
4768
+ "button",
4769
+ {
4770
+ type: "button",
4771
+ disabled: readOnly,
4772
+ onClick: () => applyWrapper("mstts:express-as", { style: "cheerful" }),
4773
+ children: "Emotion"
4774
+ }
4775
+ ),
4776
+ /* @__PURE__ */ jsx6("button", { type: "button", disabled: readOnly, onClick: () => applyWrapper("break", { time: "500ms" }), children: "Pause" }),
4777
+ /* @__PURE__ */ jsx6(
4778
+ "button",
4779
+ {
4780
+ type: "button",
4781
+ disabled: readOnly,
4782
+ onClick: () => applyWrapper("phoneme", { alphabet: "ipa", ph: selectedLeaf.value }),
4783
+ children: "Pronunciation"
4784
+ }
4785
+ ),
4786
+ onPreviewSelection && /* @__PURE__ */ jsx6(
4787
+ "button",
4788
+ {
4789
+ type: "button",
4790
+ onClick: () => onPreviewSelection(buildSsml3({ ...document2, children: [selectedLeaf.value] })),
4791
+ children: "Preview selection"
4792
+ }
4793
+ )
4794
+ ] })
4795
+ ] }) : /* @__PURE__ */ jsx6("p", { children: "Select an element or text node to edit it." }) })
4796
+ ] })
4797
+ ] });
4798
+ }
4799
+
4383
4800
  // src/SsmlEditor.tsx
4384
- import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
4801
+ import { Fragment as Fragment5, jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
4385
4802
  var UNGROUPED_TOOLBAR_GROUP = "__ssml-editor-ungrouped__";
4386
- var TIMING_POPOVER_TAGS = /* @__PURE__ */ new Set(["break", "mstts:silence"]);
4803
+ var TIMING_POPOVER_TAGS = /* @__PURE__ */ new Set(["break", "mstts:silence", "mstts:audioduration"]);
4387
4804
  var PROSODY_POPOVER_TAGS = /* @__PURE__ */ new Set(["prosody", "mstts:express-as", "voice", "emphasis"]);
4388
4805
  var TEXT_POPOVER_TAGS = /* @__PURE__ */ new Set(["sub", "say-as", "phoneme", "w", "lang"]);
4389
4806
  function localizedText(value) {
@@ -4611,13 +5028,34 @@ var SSML_INSERTIONS = [
4611
5028
  suffix: "",
4612
5029
  mode: "insert"
4613
5030
  })
5031
+ },
5032
+ {
5033
+ id: "mstts:audioduration",
5034
+ icon: "\u25F7",
5035
+ tagName: "mstts:audioduration",
5036
+ selfClosing: true,
5037
+ labels: { ja: "\u97F3\u58F0\u9577", en: "Audio duration" },
5038
+ descriptions: {
5039
+ ja: "\u5408\u6210\u97F3\u58F0\u306E\u76EE\u6A19\u6642\u9593\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
5040
+ en: "Sets the target duration of synthesized audio."
5041
+ },
5042
+ parameterDescription: {
5043
+ ja: "\u76EE\u6A19\u6642\u9593\u3092\u9078\u629E\u3057\u307E\u3059\u3002",
5044
+ en: "Selects the target duration."
5045
+ },
5046
+ options: createInsertionOptions(AUDIO_DURATION_PRESETS, AUDIO_DURATION_DESCRIPTIONS),
5047
+ createTemplate: (value) => ({
5048
+ prefix: `<mstts:audioduration value="${value}"/>`,
5049
+ suffix: "",
5050
+ mode: "insert"
5051
+ })
4614
5052
  }
4615
5053
  ];
4616
5054
  var DEFAULT_INSERTION_GROUPS = [
4617
5055
  {
4618
5056
  id: "pauses",
4619
5057
  labels: { ja: "\u9593\u30FB\u7121\u97F3", en: "Pauses" },
4620
- insertionIds: ["break", "mstts:silence"]
5058
+ insertionIds: ["break", "mstts:silence", "mstts:audioduration"]
4621
5059
  },
4622
5060
  {
4623
5061
  id: "prosody",
@@ -4826,6 +5264,60 @@ var STYLE_CSS = `
4826
5264
  > .ssml-editor-help-settings-summary::before {
4827
5265
  content: "\u25BE";
4828
5266
  }
5267
+ [data-ssml-editor] .ssml-editor-visual-layout {
5268
+ display: grid;
5269
+ grid-template-columns: minmax(12rem, 0.35fr) minmax(16rem, 1fr);
5270
+ gap: 1rem;
5271
+ }
5272
+ [data-ssml-editor] .ssml-editor-visual-tree,
5273
+ [data-ssml-editor] .ssml-editor-visual-form {
5274
+ display: grid;
5275
+ gap: 0.5rem;
5276
+ align-content: start;
5277
+ }
5278
+ [data-ssml-editor] .ssml-editor-visual-tree ul {
5279
+ margin: 0;
5280
+ padding-left: 1.25rem;
5281
+ }
5282
+ [data-ssml-editor] .ssml-editor-visual-tree button,
5283
+ [data-ssml-editor] .ssml-editor-visual-breadcrumb button,
5284
+ [data-ssml-editor] .ssml-editor-visual-actions button {
5285
+ padding: 0.35rem 0.5rem;
5286
+ border: 1px solid var(--ssml-editor-control-border);
5287
+ border-radius: 0.25rem;
5288
+ color: var(--ssml-editor-color);
5289
+ background: var(--ssml-editor-control-bg);
5290
+ cursor: pointer;
5291
+ }
5292
+ [data-ssml-editor] .ssml-editor-visual-tree button[aria-current] {
5293
+ border-color: var(--ssml-editor-active-border);
5294
+ background: var(--ssml-editor-active-bg);
5295
+ }
5296
+ [data-ssml-editor] .ssml-editor-visual-breadcrumb,
5297
+ [data-ssml-editor] .ssml-editor-visual-actions {
5298
+ display: flex;
5299
+ flex-wrap: wrap;
5300
+ gap: 0.4rem;
5301
+ align-items: center;
5302
+ }
5303
+ [data-ssml-editor] .ssml-editor-visual-form textarea {
5304
+ box-sizing: border-box;
5305
+ width: 100%;
5306
+ min-height: 6rem;
5307
+ padding: 0.5rem;
5308
+ color: var(--ssml-editor-color);
5309
+ background: var(--ssml-editor-control-bg);
5310
+ border: 1px solid var(--ssml-editor-control-border);
5311
+ border-radius: 0.25rem;
5312
+ font: inherit;
5313
+ }
5314
+ [data-ssml-editor] .ssml-editor-visual-errors {
5315
+ padding: 0.5rem;
5316
+ color: var(--ssml-editor-error);
5317
+ background: var(--ssml-editor-error-bg);
5318
+ border: 1px solid var(--ssml-editor-error);
5319
+ border-radius: 0.25rem;
5320
+ }
4829
5321
  `.trim();
4830
5322
  function injectEditorTheme() {
4831
5323
  if (typeof document !== "undefined" && !document.getElementById(STYLE_ID)) {
@@ -4842,10 +5334,10 @@ function ToolbarDecorationsSwitch({
4842
5334
  decorationsVisible,
4843
5335
  onVisibilityChange
4844
5336
  }) {
4845
- return /* @__PURE__ */ jsxs2("div", { style: editorStyles.toolbarSwitch, children: [
4846
- showToolbarIcons && /* @__PURE__ */ jsx6("span", { style: editorStyles.toolbarIcon, "aria-hidden": "true", children: "\u2606" }),
4847
- showToolbarText && /* @__PURE__ */ jsx6("span", { children: copy.decorations }),
4848
- /* @__PURE__ */ jsx6(
5337
+ return /* @__PURE__ */ jsxs3("div", { style: editorStyles.toolbarSwitch, children: [
5338
+ showToolbarIcons && /* @__PURE__ */ jsx7("span", { style: editorStyles.toolbarIcon, "aria-hidden": "true", children: "\u2606" }),
5339
+ showToolbarText && /* @__PURE__ */ jsx7("span", { children: copy.decorations }),
5340
+ /* @__PURE__ */ jsx7(
4849
5341
  "button",
4850
5342
  {
4851
5343
  type: "button",
@@ -4856,7 +5348,7 @@ function ToolbarDecorationsSwitch({
4856
5348
  "aria-label": copy.decorations,
4857
5349
  title: decorationsVisible ? copy.decorationsHideTitle : copy.decorationsShowTitle,
4858
5350
  onClick: () => onVisibilityChange(!decorationsVisible),
4859
- children: /* @__PURE__ */ jsx6("span", { className: "ssml-editor-switch-thumb", style: editorStyles.toolbarSwitchThumb, "aria-hidden": "true" })
5351
+ children: /* @__PURE__ */ jsx7("span", { className: "ssml-editor-switch-thumb", style: editorStyles.toolbarSwitchThumb, "aria-hidden": "true" })
4860
5352
  }
4861
5353
  )
4862
5354
  ] });
@@ -4899,8 +5391,11 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
4899
5391
  toolbarStyle,
4900
5392
  displayClassName,
4901
5393
  displayStyle,
4902
- loadingFallback
5394
+ loadingFallback,
5395
+ editMode: editModeProp = "code"
4903
5396
  }, ref) {
5397
+ const [editMode, setEditMode] = useState3(editModeProp);
5398
+ useEffect3(() => setEditMode(editModeProp), [editModeProp]);
4904
5399
  const resolvedEditorOptions = {
4905
5400
  ...settings,
4906
5401
  ...editorOptions,
@@ -4978,6 +5473,7 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
4978
5473
  );
4979
5474
  const {
4980
5475
  editorRef,
5476
+ draftDocument,
4981
5477
  text,
4982
5478
  selectionOverlay,
4983
5479
  activeTags,
@@ -4989,6 +5485,7 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
4989
5485
  syntaxError,
4990
5486
  setSyntaxError,
4991
5487
  helpPanelId,
5488
+ commit,
4992
5489
  handleTextChange,
4993
5490
  handleInsert,
4994
5491
  handleInsertBreak,
@@ -5066,15 +5563,15 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5066
5563
  onApply: isTimingPopover ? handleInsertBreak : isProsodyPopover ? handleInsertProsody : isTextPopover ? handleInsertText : handleInsert
5067
5564
  };
5068
5565
  if (isTimingPopover) {
5069
- return /* @__PURE__ */ jsx6(TimingPopovers, { ...props, insertions: [insertion] });
5566
+ return /* @__PURE__ */ jsx7(TimingPopovers, { ...props, insertions: [insertion] });
5070
5567
  }
5071
5568
  if (isProsodyPopover) {
5072
- return /* @__PURE__ */ jsx6(ProsodyPopovers, { ...props, insertions: [insertion], voiceName: popoverVoiceName });
5569
+ return /* @__PURE__ */ jsx7(ProsodyPopovers, { ...props, insertions: [insertion], voiceName: popoverVoiceName });
5073
5570
  }
5074
5571
  if (isTextPopover) {
5075
- return /* @__PURE__ */ jsx6(TextPopovers, { ...props, insertions: [insertion] });
5572
+ return /* @__PURE__ */ jsx7(TextPopovers, { ...props, insertions: [insertion] });
5076
5573
  }
5077
- return /* @__PURE__ */ jsx6(
5574
+ return /* @__PURE__ */ jsx7(
5078
5575
  InsertionPopover,
5079
5576
  {
5080
5577
  ...props,
@@ -5087,7 +5584,7 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5087
5584
  const toolbarItemRenderers = /* @__PURE__ */ new Map([
5088
5585
  [
5089
5586
  "undo",
5090
- () => /* @__PURE__ */ jsxs2(
5587
+ () => /* @__PURE__ */ jsxs3(
5091
5588
  "button",
5092
5589
  {
5093
5590
  type: "button",
@@ -5101,8 +5598,8 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5101
5598
  }
5102
5599
  },
5103
5600
  children: [
5104
- showToolbarIcons && /* @__PURE__ */ jsx6("span", { style: editorStyles.toolbarIcon, "aria-hidden": "true", children: "\u21A9" }),
5105
- showToolbarText && /* @__PURE__ */ jsx6("span", { children: copy.undo })
5601
+ showToolbarIcons && /* @__PURE__ */ jsx7("span", { style: editorStyles.toolbarIcon, "aria-hidden": "true", children: "\u21A9" }),
5602
+ showToolbarText && /* @__PURE__ */ jsx7("span", { children: copy.undo })
5106
5603
  ]
5107
5604
  },
5108
5605
  "undo"
@@ -5110,7 +5607,7 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5110
5607
  ],
5111
5608
  [
5112
5609
  "redo",
5113
- () => /* @__PURE__ */ jsxs2(
5610
+ () => /* @__PURE__ */ jsxs3(
5114
5611
  "button",
5115
5612
  {
5116
5613
  type: "button",
@@ -5124,8 +5621,8 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5124
5621
  }
5125
5622
  },
5126
5623
  children: [
5127
- showToolbarIcons && /* @__PURE__ */ jsx6("span", { style: editorStyles.toolbarIcon, "aria-hidden": "true", children: "\u21AA" }),
5128
- showToolbarText && /* @__PURE__ */ jsx6("span", { children: copy.redo })
5624
+ showToolbarIcons && /* @__PURE__ */ jsx7("span", { style: editorStyles.toolbarIcon, "aria-hidden": "true", children: "\u21AA" }),
5625
+ showToolbarText && /* @__PURE__ */ jsx7("span", { children: copy.redo })
5129
5626
  ]
5130
5627
  },
5131
5628
  "redo"
@@ -5133,7 +5630,7 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5133
5630
  ],
5134
5631
  [
5135
5632
  "clearAll",
5136
- () => /* @__PURE__ */ jsxs2(
5633
+ () => /* @__PURE__ */ jsxs3(
5137
5634
  "button",
5138
5635
  {
5139
5636
  type: "button",
@@ -5147,8 +5644,8 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5147
5644
  }
5148
5645
  },
5149
5646
  children: [
5150
- showToolbarIcons && /* @__PURE__ */ jsx6("span", { style: editorStyles.toolbarIcon, "aria-hidden": "true", children: "\xD7" }),
5151
- showToolbarText && /* @__PURE__ */ jsx6("span", { children: copy.clearAll })
5647
+ showToolbarIcons && /* @__PURE__ */ jsx7("span", { style: editorStyles.toolbarIcon, "aria-hidden": "true", children: "\xD7" }),
5648
+ showToolbarText && /* @__PURE__ */ jsx7("span", { children: copy.clearAll })
5152
5649
  ]
5153
5650
  },
5154
5651
  "clearAll"
@@ -5156,7 +5653,7 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5156
5653
  ],
5157
5654
  [
5158
5655
  "format",
5159
- () => /* @__PURE__ */ jsxs2(
5656
+ () => /* @__PURE__ */ jsxs3(
5160
5657
  "button",
5161
5658
  {
5162
5659
  type: "button",
@@ -5170,8 +5667,8 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5170
5667
  }
5171
5668
  },
5172
5669
  children: [
5173
- showToolbarIcons && /* @__PURE__ */ jsx6("span", { style: editorStyles.toolbarIcon, "aria-hidden": "true", children: "\u2261" }),
5174
- showToolbarText && /* @__PURE__ */ jsx6("span", { children: copy.format })
5670
+ showToolbarIcons && /* @__PURE__ */ jsx7("span", { style: editorStyles.toolbarIcon, "aria-hidden": "true", children: "\u2261" }),
5671
+ showToolbarText && /* @__PURE__ */ jsx7("span", { children: copy.format })
5175
5672
  ]
5176
5673
  },
5177
5674
  "format"
@@ -5179,7 +5676,7 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5179
5676
  ],
5180
5677
  [
5181
5678
  "decorations",
5182
- () => /* @__PURE__ */ jsx6(
5679
+ () => /* @__PURE__ */ jsx7(
5183
5680
  ToolbarDecorationsSwitch,
5184
5681
  {
5185
5682
  copy,
@@ -5193,7 +5690,7 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5193
5690
  ],
5194
5691
  [
5195
5692
  "help",
5196
- () => /* @__PURE__ */ jsxs2(
5693
+ () => /* @__PURE__ */ jsxs3(
5197
5694
  "button",
5198
5695
  {
5199
5696
  type: "button",
@@ -5204,8 +5701,8 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5204
5701
  "aria-controls": helpPanelId,
5205
5702
  onClick: () => setIsHelpOpen((open) => !open),
5206
5703
  children: [
5207
- showToolbarIcons && /* @__PURE__ */ jsx6("span", { style: editorStyles.toolbarIcon, "aria-hidden": "true", children: "?" }),
5208
- showToolbarText && /* @__PURE__ */ jsx6("span", { children: copy.help })
5704
+ showToolbarIcons && /* @__PURE__ */ jsx7("span", { style: editorStyles.toolbarIcon, "aria-hidden": "true", children: "?" }),
5705
+ showToolbarText && /* @__PURE__ */ jsx7("span", { children: copy.help })
5209
5706
  ]
5210
5707
  },
5211
5708
  "help"
@@ -5229,15 +5726,15 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5229
5726
  previousRenderedGroupId = groupId;
5230
5727
  hasRenderedToolbarItem = true;
5231
5728
  renderedItems.push(
5232
- /* @__PURE__ */ jsxs2(Fragment3, { children: [
5233
- showSeparator && /* @__PURE__ */ jsx6("span", { style: editorStyles.toolbarSeparator, "aria-hidden": "true" }),
5729
+ /* @__PURE__ */ jsxs3(Fragment4, { children: [
5730
+ showSeparator && /* @__PURE__ */ jsx7("span", { style: editorStyles.toolbarSeparator, "aria-hidden": "true" }),
5234
5731
  render()
5235
5732
  ] }, id)
5236
5733
  );
5237
5734
  }
5238
5735
  return renderedItems;
5239
5736
  };
5240
- return /* @__PURE__ */ jsxs2(
5737
+ return /* @__PURE__ */ jsxs3(
5241
5738
  "section",
5242
5739
  {
5243
5740
  className,
@@ -5246,61 +5743,92 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5246
5743
  "data-ssml-editor": "",
5247
5744
  "data-theme": isDarkTheme ? "dark" : "light",
5248
5745
  children: [
5249
- showToolbar && /* @__PURE__ */ jsx6(
5746
+ showToolbar && /* @__PURE__ */ jsx7(
5250
5747
  "div",
5251
5748
  {
5252
5749
  className: toolbarClassName,
5253
5750
  style: { ...editorStyles.toolbarContainer, ...toolbarStyle },
5254
5751
  "data-ssml-editor-toolbar": "",
5255
- children: /* @__PURE__ */ jsx6(
5752
+ children: /* @__PURE__ */ jsxs3(
5256
5753
  "div",
5257
5754
  {
5258
5755
  style: editorStyles.toolbarActions,
5259
5756
  role: "toolbar",
5260
5757
  "aria-label": copy.toolbarAriaLabel,
5261
5758
  "data-ssml-editor-toolbar-actions": "",
5262
- children: renderToolbarItems()
5759
+ children: [
5760
+ renderToolbarItems(),
5761
+ /* @__PURE__ */ jsx7("span", { style: editorStyles.toolbarSeparator, "aria-hidden": "true" }),
5762
+ /* @__PURE__ */ jsx7(
5763
+ "button",
5764
+ {
5765
+ type: "button",
5766
+ style: editMode === "visual" ? { ...toolbarButtonStyle, ...editorStyles.toolbarButtonActive } : toolbarButtonStyle,
5767
+ "aria-pressed": editMode === "visual",
5768
+ onClick: () => setEditMode("visual"),
5769
+ children: "Visual"
5770
+ }
5771
+ ),
5772
+ /* @__PURE__ */ jsx7(
5773
+ "button",
5774
+ {
5775
+ type: "button",
5776
+ style: editMode === "code" ? { ...toolbarButtonStyle, ...editorStyles.toolbarButtonActive } : toolbarButtonStyle,
5777
+ "aria-pressed": editMode === "code",
5778
+ onClick: () => setEditMode("code"),
5779
+ children: "Code"
5780
+ }
5781
+ )
5782
+ ]
5263
5783
  }
5264
5784
  )
5265
5785
  }
5266
5786
  ),
5267
- /* @__PURE__ */ jsxs2("div", { className: displayClassName, style: { ...editorStyles.display, ...displayStyle }, "data-ssml-editor-display": "", children: [
5268
- isHelpOpen && isSsmlEditorButtonVisible(buttonVisibility, "help") && /* @__PURE__ */ jsxs2("section", { id: helpPanelId, style: editorStyles.helpPanel, "aria-label": copy.helpHeading, children: [
5269
- /* @__PURE__ */ jsx6("h3", { style: editorStyles.helpHeading, children: copy.helpHeading }),
5270
- /* @__PURE__ */ jsx6("p", { style: editorStyles.helpDescription, children: copy.helpDescription }),
5271
- helpInsertions.length > 0 && /* @__PURE__ */ jsx6("ul", { style: editorStyles.helpList, children: helpInsertions.map((insertion) => /* @__PURE__ */ jsx6("li", { style: editorStyles.helpItem, children: /* @__PURE__ */ jsxs2("details", { className: "ssml-editor-help-settings-accordion", style: editorStyles.helpSettingsAccordion, children: [
5272
- /* @__PURE__ */ jsxs2("summary", { className: "ssml-editor-help-settings-summary", style: editorStyles.helpSettingsSummary, children: [
5273
- /* @__PURE__ */ jsx6("span", { style: editorStyles.helpIcon, "aria-hidden": "true", children: insertion.icon }),
5274
- /* @__PURE__ */ jsxs2("span", { style: editorStyles.helpSettingsSummaryContent, children: [
5275
- /* @__PURE__ */ jsx6("strong", { children: insertion.labels[language] }),
5787
+ /* @__PURE__ */ jsxs3("div", { className: displayClassName, style: { ...editorStyles.display, ...displayStyle }, "data-ssml-editor-display": "", children: [
5788
+ isHelpOpen && isSsmlEditorButtonVisible(buttonVisibility, "help") && /* @__PURE__ */ jsxs3("section", { id: helpPanelId, style: editorStyles.helpPanel, "aria-label": copy.helpHeading, children: [
5789
+ /* @__PURE__ */ jsx7("h3", { style: editorStyles.helpHeading, children: copy.helpHeading }),
5790
+ /* @__PURE__ */ jsx7("p", { style: editorStyles.helpDescription, children: copy.helpDescription }),
5791
+ helpInsertions.length > 0 && /* @__PURE__ */ jsx7("ul", { style: editorStyles.helpList, children: helpInsertions.map((insertion) => /* @__PURE__ */ jsx7("li", { style: editorStyles.helpItem, children: /* @__PURE__ */ jsxs3("details", { className: "ssml-editor-help-settings-accordion", style: editorStyles.helpSettingsAccordion, children: [
5792
+ /* @__PURE__ */ jsxs3("summary", { className: "ssml-editor-help-settings-summary", style: editorStyles.helpSettingsSummary, children: [
5793
+ /* @__PURE__ */ jsx7("span", { style: editorStyles.helpIcon, "aria-hidden": "true", children: insertion.icon }),
5794
+ /* @__PURE__ */ jsxs3("span", { style: editorStyles.helpSettingsSummaryContent, children: [
5795
+ /* @__PURE__ */ jsx7("strong", { children: insertion.labels[language] }),
5276
5796
  " ",
5277
- insertion.tagName && /* @__PURE__ */ jsxs2(Fragment4, { children: [
5278
- /* @__PURE__ */ jsx6("code", { children: `<${insertion.tagName}${insertion.selfClosing ? "/>" : ">"}` }),
5797
+ insertion.tagName && /* @__PURE__ */ jsxs3(Fragment5, { children: [
5798
+ /* @__PURE__ */ jsx7("code", { children: `<${insertion.tagName}${insertion.selfClosing ? "/>" : ">"}` }),
5279
5799
  " \u2014",
5280
5800
  " "
5281
5801
  ] }),
5282
5802
  insertion.descriptions[language]
5283
5803
  ] })
5284
5804
  ] }),
5285
- /* @__PURE__ */ jsxs2("p", { style: editorStyles.helpSettingsDescription, children: [
5286
- /* @__PURE__ */ jsxs2("strong", { children: [
5805
+ /* @__PURE__ */ jsxs3("p", { style: editorStyles.helpSettingsDescription, children: [
5806
+ /* @__PURE__ */ jsxs3("strong", { children: [
5287
5807
  copy.parameters,
5288
5808
  ":"
5289
5809
  ] }),
5290
5810
  " ",
5291
5811
  insertion.parameterDescription[language]
5292
5812
  ] }),
5293
- /* @__PURE__ */ jsx6("ul", { style: editorStyles.helpSettingsList, children: insertion.options.map((option) => /* @__PURE__ */ jsxs2("li", { children: [
5294
- /* @__PURE__ */ jsx6("strong", { children: option.labels[language] }),
5295
- option.descriptions && /* @__PURE__ */ jsxs2(Fragment4, { children: [
5813
+ /* @__PURE__ */ jsx7("ul", { style: editorStyles.helpSettingsList, children: insertion.options.map((option) => /* @__PURE__ */ jsxs3("li", { children: [
5814
+ /* @__PURE__ */ jsx7("strong", { children: option.labels[language] }),
5815
+ option.descriptions && /* @__PURE__ */ jsxs3(Fragment5, { children: [
5296
5816
  " \u2014 ",
5297
5817
  option.descriptions[language]
5298
5818
  ] })
5299
5819
  ] }, option.value)) })
5300
5820
  ] }) }, insertion.id)) })
5301
5821
  ] }),
5302
- /* @__PURE__ */ jsxs2("div", { style: { ...editorStyles.editor, minHeight: editorMinHeight }, children: [
5303
- /* @__PURE__ */ jsx6(
5822
+ editMode === "visual" ? /* @__PURE__ */ jsx7("div", { style: editorStyles.visual, children: /* @__PURE__ */ jsx7(
5823
+ VisualSsmlEditor,
5824
+ {
5825
+ document: draftDocument,
5826
+ readOnly: isReadOnly,
5827
+ onChange: commit,
5828
+ onPreviewSelection
5829
+ }
5830
+ ) }) : /* @__PURE__ */ jsxs3("div", { style: { ...editorStyles.editor, minHeight: editorMinHeight }, children: [
5831
+ /* @__PURE__ */ jsx7(
5304
5832
  Editor,
5305
5833
  {
5306
5834
  height: editorHeight,
@@ -5325,7 +5853,7 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5325
5853
  onChange: (value) => handleTextChange(value ?? "")
5326
5854
  }
5327
5855
  ),
5328
- selectionOverlay.hasSelection && selectionOverlay.position && /* @__PURE__ */ jsxs2(
5856
+ selectionOverlay.hasSelection && selectionOverlay.position && /* @__PURE__ */ jsxs3(
5329
5857
  "div",
5330
5858
  {
5331
5859
  role: "toolbar",
@@ -5339,12 +5867,12 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5339
5867
  },
5340
5868
  onMouseDown: (event) => event.preventDefault(),
5341
5869
  children: [
5342
- /* @__PURE__ */ jsxs2("span", { style: editorStyles.selectionCount, "aria-live": "polite", children: [
5870
+ /* @__PURE__ */ jsxs3("span", { style: editorStyles.selectionCount, "aria-live": "polite", children: [
5343
5871
  selectionOverlay.characterCount,
5344
5872
  copy.selectionCountSuffix
5345
5873
  ] }),
5346
- /* @__PURE__ */ jsx6("span", { style: editorStyles.selectionActionsDivider, "aria-hidden": "true" }),
5347
- /* @__PURE__ */ jsxs2(
5874
+ /* @__PURE__ */ jsx7("span", { style: editorStyles.selectionActionsDivider, "aria-hidden": "true" }),
5875
+ /* @__PURE__ */ jsxs3(
5348
5876
  "button",
5349
5877
  {
5350
5878
  type: "button",
@@ -5353,7 +5881,7 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5353
5881
  disabled: !canPreviewSelection,
5354
5882
  onClick: previewSelection,
5355
5883
  children: [
5356
- /* @__PURE__ */ jsx6("span", { style: editorStyles.selectionActionIcon, "aria-hidden": "true", children: "\u25B6" }),
5884
+ /* @__PURE__ */ jsx7("span", { style: editorStyles.selectionActionIcon, "aria-hidden": "true", children: "\u25B6" }),
5357
5885
  copy.previewSelection
5358
5886
  ]
5359
5887
  }
@@ -5362,7 +5890,7 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5362
5890
  }
5363
5891
  )
5364
5892
  ] }),
5365
- syntaxError && /* @__PURE__ */ jsxs2("p", { style: editorStyles.error, role: "alert", children: [
5893
+ syntaxError && /* @__PURE__ */ jsxs3("p", { style: editorStyles.error, role: "alert", children: [
5366
5894
  copy.syntaxError,
5367
5895
  ": ",
5368
5896
  syntaxError.message
@@ -5378,6 +5906,7 @@ export {
5378
5906
  SSML_HOVER_COPY,
5379
5907
  SSML_INSERTIONS,
5380
5908
  SsmlEditor,
5909
+ VisualSsmlEditor,
5381
5910
  createSsmlEditorInsertionDefinition,
5382
5911
  registerSsmlCodeLens,
5383
5912
  updateTagAttribute