@ssml-builder-js/ssml-editor-react 2.8.1 → 2.10.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
@@ -436,6 +436,11 @@ var SSML_ATTRIBUTE_PRESETS = {
436
436
  "mstts:audioduration": {
437
437
  value: AUDIO_DURATION_PRESETS
438
438
  },
439
+ "mstts:backgroundaudio": {
440
+ volume: PROSODY_VOLUME_PRESETS,
441
+ fadein: AUDIO_DURATION_PRESETS,
442
+ fadeout: AUDIO_DURATION_PRESETS
443
+ },
439
444
  silence: {
440
445
  type: SILENCE_TYPE_PRESETS,
441
446
  value: SILENCE_VALUE_PRESETS
@@ -1252,6 +1257,14 @@ var editorStyles = {
1252
1257
  display: "grid",
1253
1258
  gap: "0.5rem"
1254
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
+ },
1255
1268
  toolbarIconOnly: {
1256
1269
  justifyContent: "center",
1257
1270
  minWidth: "2.25rem",
@@ -3263,6 +3276,24 @@ var SSML_COMPLETION_SNIPPETS = [
3263
3276
  label: "mstts:audioduration",
3264
3277
  insertText: '<mstts:audioduration value="10s" />'
3265
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
+ },
3266
3297
  {
3267
3298
  label: "sub",
3268
3299
  insertText: `<sub alias="\${1:\u8AAD\u307F}">\${2:\u6F22\u5B57}</sub>`
@@ -3930,6 +3961,63 @@ var SSML_TAG_DEFINITIONS = [
3930
3961
  example: "10s"
3931
3962
  }
3932
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: []
3933
4021
  }
3934
4022
  ];
3935
4023
  var definitionsByName = /* @__PURE__ */ new Map();
@@ -4534,8 +4622,183 @@ function useSsmlMonaco({
4534
4622
  return { editorRef, onMount };
4535
4623
  }
4536
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
+
4537
4800
  // src/SsmlEditor.tsx
4538
- 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";
4539
4802
  var UNGROUPED_TOOLBAR_GROUP = "__ssml-editor-ungrouped__";
4540
4803
  var TIMING_POPOVER_TAGS = /* @__PURE__ */ new Set(["break", "mstts:silence", "mstts:audioduration"]);
4541
4804
  var PROSODY_POPOVER_TAGS = /* @__PURE__ */ new Set(["prosody", "mstts:express-as", "voice", "emphasis"]);
@@ -5001,6 +5264,60 @@ var STYLE_CSS = `
5001
5264
  > .ssml-editor-help-settings-summary::before {
5002
5265
  content: "\u25BE";
5003
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
+ }
5004
5321
  `.trim();
5005
5322
  function injectEditorTheme() {
5006
5323
  if (typeof document !== "undefined" && !document.getElementById(STYLE_ID)) {
@@ -5017,10 +5334,10 @@ function ToolbarDecorationsSwitch({
5017
5334
  decorationsVisible,
5018
5335
  onVisibilityChange
5019
5336
  }) {
5020
- return /* @__PURE__ */ jsxs2("div", { style: editorStyles.toolbarSwitch, children: [
5021
- showToolbarIcons && /* @__PURE__ */ jsx6("span", { style: editorStyles.toolbarIcon, "aria-hidden": "true", children: "\u2606" }),
5022
- showToolbarText && /* @__PURE__ */ jsx6("span", { children: copy.decorations }),
5023
- /* @__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(
5024
5341
  "button",
5025
5342
  {
5026
5343
  type: "button",
@@ -5031,7 +5348,7 @@ function ToolbarDecorationsSwitch({
5031
5348
  "aria-label": copy.decorations,
5032
5349
  title: decorationsVisible ? copy.decorationsHideTitle : copy.decorationsShowTitle,
5033
5350
  onClick: () => onVisibilityChange(!decorationsVisible),
5034
- 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" })
5035
5352
  }
5036
5353
  )
5037
5354
  ] });
@@ -5074,8 +5391,11 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5074
5391
  toolbarStyle,
5075
5392
  displayClassName,
5076
5393
  displayStyle,
5077
- loadingFallback
5394
+ loadingFallback,
5395
+ editMode: editModeProp = "code"
5078
5396
  }, ref) {
5397
+ const [editMode, setEditMode] = useState3(editModeProp);
5398
+ useEffect3(() => setEditMode(editModeProp), [editModeProp]);
5079
5399
  const resolvedEditorOptions = {
5080
5400
  ...settings,
5081
5401
  ...editorOptions,
@@ -5153,6 +5473,7 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5153
5473
  );
5154
5474
  const {
5155
5475
  editorRef,
5476
+ draftDocument,
5156
5477
  text,
5157
5478
  selectionOverlay,
5158
5479
  activeTags,
@@ -5164,6 +5485,7 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5164
5485
  syntaxError,
5165
5486
  setSyntaxError,
5166
5487
  helpPanelId,
5488
+ commit,
5167
5489
  handleTextChange,
5168
5490
  handleInsert,
5169
5491
  handleInsertBreak,
@@ -5241,15 +5563,15 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5241
5563
  onApply: isTimingPopover ? handleInsertBreak : isProsodyPopover ? handleInsertProsody : isTextPopover ? handleInsertText : handleInsert
5242
5564
  };
5243
5565
  if (isTimingPopover) {
5244
- return /* @__PURE__ */ jsx6(TimingPopovers, { ...props, insertions: [insertion] });
5566
+ return /* @__PURE__ */ jsx7(TimingPopovers, { ...props, insertions: [insertion] });
5245
5567
  }
5246
5568
  if (isProsodyPopover) {
5247
- return /* @__PURE__ */ jsx6(ProsodyPopovers, { ...props, insertions: [insertion], voiceName: popoverVoiceName });
5569
+ return /* @__PURE__ */ jsx7(ProsodyPopovers, { ...props, insertions: [insertion], voiceName: popoverVoiceName });
5248
5570
  }
5249
5571
  if (isTextPopover) {
5250
- return /* @__PURE__ */ jsx6(TextPopovers, { ...props, insertions: [insertion] });
5572
+ return /* @__PURE__ */ jsx7(TextPopovers, { ...props, insertions: [insertion] });
5251
5573
  }
5252
- return /* @__PURE__ */ jsx6(
5574
+ return /* @__PURE__ */ jsx7(
5253
5575
  InsertionPopover,
5254
5576
  {
5255
5577
  ...props,
@@ -5262,7 +5584,7 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5262
5584
  const toolbarItemRenderers = /* @__PURE__ */ new Map([
5263
5585
  [
5264
5586
  "undo",
5265
- () => /* @__PURE__ */ jsxs2(
5587
+ () => /* @__PURE__ */ jsxs3(
5266
5588
  "button",
5267
5589
  {
5268
5590
  type: "button",
@@ -5276,8 +5598,8 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5276
5598
  }
5277
5599
  },
5278
5600
  children: [
5279
- showToolbarIcons && /* @__PURE__ */ jsx6("span", { style: editorStyles.toolbarIcon, "aria-hidden": "true", children: "\u21A9" }),
5280
- 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 })
5281
5603
  ]
5282
5604
  },
5283
5605
  "undo"
@@ -5285,7 +5607,7 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5285
5607
  ],
5286
5608
  [
5287
5609
  "redo",
5288
- () => /* @__PURE__ */ jsxs2(
5610
+ () => /* @__PURE__ */ jsxs3(
5289
5611
  "button",
5290
5612
  {
5291
5613
  type: "button",
@@ -5299,8 +5621,8 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5299
5621
  }
5300
5622
  },
5301
5623
  children: [
5302
- showToolbarIcons && /* @__PURE__ */ jsx6("span", { style: editorStyles.toolbarIcon, "aria-hidden": "true", children: "\u21AA" }),
5303
- 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 })
5304
5626
  ]
5305
5627
  },
5306
5628
  "redo"
@@ -5308,7 +5630,7 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5308
5630
  ],
5309
5631
  [
5310
5632
  "clearAll",
5311
- () => /* @__PURE__ */ jsxs2(
5633
+ () => /* @__PURE__ */ jsxs3(
5312
5634
  "button",
5313
5635
  {
5314
5636
  type: "button",
@@ -5322,8 +5644,8 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5322
5644
  }
5323
5645
  },
5324
5646
  children: [
5325
- showToolbarIcons && /* @__PURE__ */ jsx6("span", { style: editorStyles.toolbarIcon, "aria-hidden": "true", children: "\xD7" }),
5326
- 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 })
5327
5649
  ]
5328
5650
  },
5329
5651
  "clearAll"
@@ -5331,7 +5653,7 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5331
5653
  ],
5332
5654
  [
5333
5655
  "format",
5334
- () => /* @__PURE__ */ jsxs2(
5656
+ () => /* @__PURE__ */ jsxs3(
5335
5657
  "button",
5336
5658
  {
5337
5659
  type: "button",
@@ -5345,8 +5667,8 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5345
5667
  }
5346
5668
  },
5347
5669
  children: [
5348
- showToolbarIcons && /* @__PURE__ */ jsx6("span", { style: editorStyles.toolbarIcon, "aria-hidden": "true", children: "\u2261" }),
5349
- 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 })
5350
5672
  ]
5351
5673
  },
5352
5674
  "format"
@@ -5354,7 +5676,7 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5354
5676
  ],
5355
5677
  [
5356
5678
  "decorations",
5357
- () => /* @__PURE__ */ jsx6(
5679
+ () => /* @__PURE__ */ jsx7(
5358
5680
  ToolbarDecorationsSwitch,
5359
5681
  {
5360
5682
  copy,
@@ -5368,7 +5690,7 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5368
5690
  ],
5369
5691
  [
5370
5692
  "help",
5371
- () => /* @__PURE__ */ jsxs2(
5693
+ () => /* @__PURE__ */ jsxs3(
5372
5694
  "button",
5373
5695
  {
5374
5696
  type: "button",
@@ -5379,8 +5701,8 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5379
5701
  "aria-controls": helpPanelId,
5380
5702
  onClick: () => setIsHelpOpen((open) => !open),
5381
5703
  children: [
5382
- showToolbarIcons && /* @__PURE__ */ jsx6("span", { style: editorStyles.toolbarIcon, "aria-hidden": "true", children: "?" }),
5383
- 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 })
5384
5706
  ]
5385
5707
  },
5386
5708
  "help"
@@ -5404,15 +5726,15 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5404
5726
  previousRenderedGroupId = groupId;
5405
5727
  hasRenderedToolbarItem = true;
5406
5728
  renderedItems.push(
5407
- /* @__PURE__ */ jsxs2(Fragment3, { children: [
5408
- 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" }),
5409
5731
  render()
5410
5732
  ] }, id)
5411
5733
  );
5412
5734
  }
5413
5735
  return renderedItems;
5414
5736
  };
5415
- return /* @__PURE__ */ jsxs2(
5737
+ return /* @__PURE__ */ jsxs3(
5416
5738
  "section",
5417
5739
  {
5418
5740
  className,
@@ -5421,61 +5743,92 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5421
5743
  "data-ssml-editor": "",
5422
5744
  "data-theme": isDarkTheme ? "dark" : "light",
5423
5745
  children: [
5424
- showToolbar && /* @__PURE__ */ jsx6(
5746
+ showToolbar && /* @__PURE__ */ jsx7(
5425
5747
  "div",
5426
5748
  {
5427
5749
  className: toolbarClassName,
5428
5750
  style: { ...editorStyles.toolbarContainer, ...toolbarStyle },
5429
5751
  "data-ssml-editor-toolbar": "",
5430
- children: /* @__PURE__ */ jsx6(
5752
+ children: /* @__PURE__ */ jsxs3(
5431
5753
  "div",
5432
5754
  {
5433
5755
  style: editorStyles.toolbarActions,
5434
5756
  role: "toolbar",
5435
5757
  "aria-label": copy.toolbarAriaLabel,
5436
5758
  "data-ssml-editor-toolbar-actions": "",
5437
- 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
+ ]
5438
5783
  }
5439
5784
  )
5440
5785
  }
5441
5786
  ),
5442
- /* @__PURE__ */ jsxs2("div", { className: displayClassName, style: { ...editorStyles.display, ...displayStyle }, "data-ssml-editor-display": "", children: [
5443
- isHelpOpen && isSsmlEditorButtonVisible(buttonVisibility, "help") && /* @__PURE__ */ jsxs2("section", { id: helpPanelId, style: editorStyles.helpPanel, "aria-label": copy.helpHeading, children: [
5444
- /* @__PURE__ */ jsx6("h3", { style: editorStyles.helpHeading, children: copy.helpHeading }),
5445
- /* @__PURE__ */ jsx6("p", { style: editorStyles.helpDescription, children: copy.helpDescription }),
5446
- 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: [
5447
- /* @__PURE__ */ jsxs2("summary", { className: "ssml-editor-help-settings-summary", style: editorStyles.helpSettingsSummary, children: [
5448
- /* @__PURE__ */ jsx6("span", { style: editorStyles.helpIcon, "aria-hidden": "true", children: insertion.icon }),
5449
- /* @__PURE__ */ jsxs2("span", { style: editorStyles.helpSettingsSummaryContent, children: [
5450
- /* @__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] }),
5451
5796
  " ",
5452
- insertion.tagName && /* @__PURE__ */ jsxs2(Fragment4, { children: [
5453
- /* @__PURE__ */ jsx6("code", { children: `<${insertion.tagName}${insertion.selfClosing ? "/>" : ">"}` }),
5797
+ insertion.tagName && /* @__PURE__ */ jsxs3(Fragment5, { children: [
5798
+ /* @__PURE__ */ jsx7("code", { children: `<${insertion.tagName}${insertion.selfClosing ? "/>" : ">"}` }),
5454
5799
  " \u2014",
5455
5800
  " "
5456
5801
  ] }),
5457
5802
  insertion.descriptions[language]
5458
5803
  ] })
5459
5804
  ] }),
5460
- /* @__PURE__ */ jsxs2("p", { style: editorStyles.helpSettingsDescription, children: [
5461
- /* @__PURE__ */ jsxs2("strong", { children: [
5805
+ /* @__PURE__ */ jsxs3("p", { style: editorStyles.helpSettingsDescription, children: [
5806
+ /* @__PURE__ */ jsxs3("strong", { children: [
5462
5807
  copy.parameters,
5463
5808
  ":"
5464
5809
  ] }),
5465
5810
  " ",
5466
5811
  insertion.parameterDescription[language]
5467
5812
  ] }),
5468
- /* @__PURE__ */ jsx6("ul", { style: editorStyles.helpSettingsList, children: insertion.options.map((option) => /* @__PURE__ */ jsxs2("li", { children: [
5469
- /* @__PURE__ */ jsx6("strong", { children: option.labels[language] }),
5470
- 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: [
5471
5816
  " \u2014 ",
5472
5817
  option.descriptions[language]
5473
5818
  ] })
5474
5819
  ] }, option.value)) })
5475
5820
  ] }) }, insertion.id)) })
5476
5821
  ] }),
5477
- /* @__PURE__ */ jsxs2("div", { style: { ...editorStyles.editor, minHeight: editorMinHeight }, children: [
5478
- /* @__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(
5479
5832
  Editor,
5480
5833
  {
5481
5834
  height: editorHeight,
@@ -5500,7 +5853,7 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5500
5853
  onChange: (value) => handleTextChange(value ?? "")
5501
5854
  }
5502
5855
  ),
5503
- selectionOverlay.hasSelection && selectionOverlay.position && /* @__PURE__ */ jsxs2(
5856
+ selectionOverlay.hasSelection && selectionOverlay.position && /* @__PURE__ */ jsxs3(
5504
5857
  "div",
5505
5858
  {
5506
5859
  role: "toolbar",
@@ -5514,12 +5867,12 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5514
5867
  },
5515
5868
  onMouseDown: (event) => event.preventDefault(),
5516
5869
  children: [
5517
- /* @__PURE__ */ jsxs2("span", { style: editorStyles.selectionCount, "aria-live": "polite", children: [
5870
+ /* @__PURE__ */ jsxs3("span", { style: editorStyles.selectionCount, "aria-live": "polite", children: [
5518
5871
  selectionOverlay.characterCount,
5519
5872
  copy.selectionCountSuffix
5520
5873
  ] }),
5521
- /* @__PURE__ */ jsx6("span", { style: editorStyles.selectionActionsDivider, "aria-hidden": "true" }),
5522
- /* @__PURE__ */ jsxs2(
5874
+ /* @__PURE__ */ jsx7("span", { style: editorStyles.selectionActionsDivider, "aria-hidden": "true" }),
5875
+ /* @__PURE__ */ jsxs3(
5523
5876
  "button",
5524
5877
  {
5525
5878
  type: "button",
@@ -5528,7 +5881,7 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5528
5881
  disabled: !canPreviewSelection,
5529
5882
  onClick: previewSelection,
5530
5883
  children: [
5531
- /* @__PURE__ */ jsx6("span", { style: editorStyles.selectionActionIcon, "aria-hidden": "true", children: "\u25B6" }),
5884
+ /* @__PURE__ */ jsx7("span", { style: editorStyles.selectionActionIcon, "aria-hidden": "true", children: "\u25B6" }),
5532
5885
  copy.previewSelection
5533
5886
  ]
5534
5887
  }
@@ -5537,7 +5890,7 @@ var SsmlEditor = forwardRef(function SsmlEditor2({
5537
5890
  }
5538
5891
  )
5539
5892
  ] }),
5540
- syntaxError && /* @__PURE__ */ jsxs2("p", { style: editorStyles.error, role: "alert", children: [
5893
+ syntaxError && /* @__PURE__ */ jsxs3("p", { style: editorStyles.error, role: "alert", children: [
5541
5894
  copy.syntaxError,
5542
5895
  ": ",
5543
5896
  syntaxError.message
@@ -5553,6 +5906,7 @@ export {
5553
5906
  SSML_HOVER_COPY,
5554
5907
  SSML_INSERTIONS,
5555
5908
  SsmlEditor,
5909
+ VisualSsmlEditor,
5556
5910
  createSsmlEditorInsertionDefinition,
5557
5911
  registerSsmlCodeLens,
5558
5912
  updateTagAttribute