@ssml-builder-js/ssml-editor-react 2.11.0 → 2.12.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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @ssml-builder-js/ssml-editor-react
2
2
 
3
+ ## 2.12.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Add Visual Editor forms for the supported SSML and Azure extension elements and preserve parent context during selection preview.
8
+
3
9
  ## 2.11.0
4
10
 
5
11
  ### Minor Changes
package/dist/index.js CHANGED
@@ -4730,6 +4730,141 @@ function updateOptionalElementProperty(document2, path, property, value) {
4730
4730
  return next;
4731
4731
  });
4732
4732
  }
4733
+ var VISUAL_ELEMENT_FIELDS = {
4734
+ voice: [
4735
+ { key: "name", label: "Voice name" },
4736
+ { key: "effect", label: "Effect" }
4737
+ ],
4738
+ prosody: [
4739
+ { key: "rate", label: "Rate" },
4740
+ { key: "pitch", label: "Pitch" },
4741
+ { key: "volume", label: "Volume" },
4742
+ { key: "contour", label: "Contour" },
4743
+ { key: "range", label: "Range" }
4744
+ ],
4745
+ "express-as": [
4746
+ { key: "style", label: "Style" },
4747
+ { key: "styleDegree", label: "Style degree" },
4748
+ { key: "role", label: "Role" }
4749
+ ],
4750
+ expressAs: [
4751
+ { key: "style", label: "Style" },
4752
+ { key: "styleDegree", label: "Style degree" },
4753
+ { key: "role", label: "Role" }
4754
+ ],
4755
+ "mstts:express-as": [
4756
+ { key: "style", label: "Style" },
4757
+ { key: "styleDegree", label: "Style degree" },
4758
+ { key: "role", label: "Role" }
4759
+ ],
4760
+ "say-as": [
4761
+ { key: "interpretAs", label: "Interpret as" },
4762
+ { key: "format", label: "Format" },
4763
+ { key: "detail", label: "Detail" }
4764
+ ],
4765
+ sayAs: [
4766
+ { key: "interpretAs", label: "Interpret as" },
4767
+ { key: "format", label: "Format" },
4768
+ { key: "detail", label: "Detail" }
4769
+ ],
4770
+ phoneme: [
4771
+ { key: "alphabet", label: "Alphabet" },
4772
+ { key: "ph", label: "Pronunciation" }
4773
+ ],
4774
+ emphasis: [{ key: "level", label: "Level" }],
4775
+ audio: [
4776
+ { key: "src", label: "Source URL" },
4777
+ { key: "desc", label: "Description" },
4778
+ { key: "clipBegin", label: "Clip begin" },
4779
+ { key: "clipEnd", label: "Clip end" },
4780
+ { key: "speed", label: "Speed" },
4781
+ { key: "repeatCount", label: "Repeat count" },
4782
+ { key: "repeatDuration", label: "Repeat duration" },
4783
+ { key: "soundLevel", label: "Sound level" }
4784
+ ],
4785
+ mark: [{ key: "name", label: "Mark name" }],
4786
+ bookmark: [{ key: "mark", label: "Bookmark name" }],
4787
+ sub: [{ key: "alias", label: "Alias" }],
4788
+ lang: [{ key: "lang", label: "Language" }],
4789
+ lexicon: [{ key: "uri", label: "Lexicon URI" }],
4790
+ "mstts:silence": [
4791
+ { key: "typeValue", label: "Silence type" },
4792
+ { key: "value", label: "Value" }
4793
+ ],
4794
+ silence: [
4795
+ { key: "typeValue", label: "Silence type" },
4796
+ { key: "value", label: "Value" }
4797
+ ],
4798
+ "mstts:audioduration": [{ key: "value", label: "Duration" }],
4799
+ "mstts:ttsembedding": [{ key: "speakerProfileId", label: "Speaker profile ID" }],
4800
+ "mstts:embedding": [
4801
+ { key: "id", label: "Embedding ID" },
4802
+ { key: "speakerProfileId", label: "Speaker profile ID" }
4803
+ ],
4804
+ "mstts:voiceconversion": [
4805
+ { key: "url", label: "Source URL" },
4806
+ { key: "profile", label: "Profile" },
4807
+ { key: "speakerProfileId", label: "Speaker profile ID" }
4808
+ ]
4809
+ };
4810
+ function getElementFields(element) {
4811
+ return VISUAL_ELEMENT_FIELDS[elementLabel(element)] ?? [];
4812
+ }
4813
+ function VisualElementInspector({
4814
+ document: document2,
4815
+ element,
4816
+ path,
4817
+ readOnly,
4818
+ commit
4819
+ }) {
4820
+ const fields = getElementFields(element);
4821
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("fieldset", { children: [
4822
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("legend", { children: `<${elementLabel(element)}>` }),
4823
+ fields.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: "This element is preserved in the visual tree. Edit its attributes in Code mode." }) : fields.map((field) => {
4824
+ const value = element[field.key];
4825
+ const inputValue = value === void 0 ? "" : String(value);
4826
+ const inputId = `ssml-visual-${field.key}`;
4827
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { htmlFor: inputId, children: [
4828
+ field.label,
4829
+ field.multiline ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4830
+ "textarea",
4831
+ {
4832
+ id: inputId,
4833
+ value: inputValue,
4834
+ readOnly,
4835
+ onChange: (event) => commit(updateOptionalElementProperty(document2, path, field.key, event.target.value))
4836
+ }
4837
+ ) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4838
+ "input",
4839
+ {
4840
+ id: inputId,
4841
+ value: inputValue,
4842
+ readOnly,
4843
+ onChange: (event) => commit(updateOptionalElementProperty(document2, path, field.key, event.target.value))
4844
+ }
4845
+ )
4846
+ ] }, field.key);
4847
+ })
4848
+ ] });
4849
+ }
4850
+ function getElementAncestors(nodes, path, ancestors = []) {
4851
+ const [index, ...rest] = path;
4852
+ const node = nodes[index];
4853
+ if (!node || !isElement(node)) return ancestors;
4854
+ if (rest.length === 0) return ancestors;
4855
+ return getElementAncestors(node.children ?? [], rest, [...ancestors, node]);
4856
+ }
4857
+ function buildVisualPreview(document2, leaf, selection) {
4858
+ const start = Math.max(0, Math.min(selection.start, leaf.value.length));
4859
+ const end = Math.max(start, Math.min(selection.end, leaf.value.length));
4860
+ const value = start === end ? leaf.value : leaf.value.slice(start, end);
4861
+ const ancestors = getElementAncestors(document2.children ?? [], leaf.path);
4862
+ const children = ancestors.reduceRight(
4863
+ (current, ancestor) => [{ ...ancestor, children: current }],
4864
+ [value]
4865
+ );
4866
+ return (0, import_ssml_core4.buildSsml)({ ...document2, children });
4867
+ }
4733
4868
  function addDialogTurn(document2, path) {
4734
4869
  return updateElementAtPath(document2, path, (element) => ({
4735
4870
  ...element,
@@ -4927,7 +5062,16 @@ function VisualSsmlEditor({
4927
5062
  }
4928
5063
  )
4929
5064
  ] })
4930
- ] }) : selectedLeaf ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5065
+ ] }) : selectedElement && isElement(selectedElement) ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
5066
+ VisualElementInspector,
5067
+ {
5068
+ document: document2,
5069
+ element: selectedElement,
5070
+ path: selectedPath ?? [],
5071
+ readOnly,
5072
+ commit
5073
+ }
5074
+ ) : selectedLeaf ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
4931
5075
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
4932
5076
  "Text",
4933
5077
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
@@ -4967,7 +5111,7 @@ function VisualSsmlEditor({
4967
5111
  "button",
4968
5112
  {
4969
5113
  type: "button",
4970
- onClick: () => onPreviewSelection((0, import_ssml_core4.buildSsml)({ ...document2, children: [selectedLeaf.value] })),
5114
+ onClick: () => onPreviewSelection(buildVisualPreview(document2, selectedLeaf, selection)),
4971
5115
  children: "Preview selection"
4972
5116
  }
4973
5117
  )