ssml-builder-js 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/README.md +95 -17
- package/dist/{chunk-TKD3PU5Y.mjs → chunk-7LCSH4SZ.mjs} +610 -124
- package/dist/chunk-7LCSH4SZ.mjs.map +1 -0
- package/dist/{chunk-4RQETJUI.mjs → chunk-JNJVTEL6.mjs} +939 -10
- package/dist/chunk-JNJVTEL6.mjs.map +1 -0
- package/dist/core.d.mts +97 -3
- package/dist/core.d.ts +97 -3
- package/dist/core.js +616 -124
- package/dist/core.js.map +1 -1
- package/dist/core.mjs +15 -3
- package/dist/elements.d.mts +10 -1
- package/dist/elements.d.ts +10 -1
- package/dist/elements.js +1245 -15
- package/dist/elements.js.map +1 -1
- package/dist/elements.mjs +316 -9
- package/dist/elements.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +616 -124
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +15 -3
- package/dist/index.mjs.map +1 -1
- package/dist/react.d.mts +47 -6
- package/dist/react.d.ts +47 -6
- package/dist/react.js +1219 -25
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +280 -10
- package/dist/react.mjs.map +1 -1
- package/package.json +4 -3
- package/dist/chunk-4RQETJUI.mjs.map +0 -1
- package/dist/chunk-TKD3PU5Y.mjs.map +0 -1
package/dist/elements.mjs
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
+
AUDIO_DURATION_DESCRIPTIONS,
|
|
3
|
+
AUDIO_DURATION_PRESETS,
|
|
2
4
|
BREAK_TIME_DESCRIPTIONS,
|
|
3
5
|
BREAK_TIME_PRESETS,
|
|
4
6
|
EDITOR_COPY,
|
|
@@ -32,8 +34,9 @@ import {
|
|
|
32
34
|
parseSsml,
|
|
33
35
|
registerSsmlCompletionProvider,
|
|
34
36
|
resolveExpressAsStyles,
|
|
35
|
-
updateEditableText
|
|
36
|
-
|
|
37
|
+
updateEditableText,
|
|
38
|
+
validateAzureSsml
|
|
39
|
+
} from "./chunk-JNJVTEL6.mjs";
|
|
37
40
|
import "./chunk-6S5ODO6A.mjs";
|
|
38
41
|
|
|
39
42
|
// packages/ssml-editor-react/src/ssmlInsertions.ts
|
|
@@ -226,13 +229,34 @@ var SSML_INSERTIONS = [
|
|
|
226
229
|
suffix: "",
|
|
227
230
|
mode: "insert"
|
|
228
231
|
})
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
id: "mstts:audioduration",
|
|
235
|
+
icon: "\u25F7",
|
|
236
|
+
tagName: "mstts:audioduration",
|
|
237
|
+
selfClosing: true,
|
|
238
|
+
labels: { ja: "\u97F3\u58F0\u9577", en: "Audio duration" },
|
|
239
|
+
descriptions: {
|
|
240
|
+
ja: "\u5408\u6210\u97F3\u58F0\u306E\u76EE\u6A19\u6642\u9593\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
|
|
241
|
+
en: "Sets the target duration of synthesized audio."
|
|
242
|
+
},
|
|
243
|
+
parameterDescription: {
|
|
244
|
+
ja: "\u76EE\u6A19\u6642\u9593\u3092\u9078\u629E\u3057\u307E\u3059\u3002",
|
|
245
|
+
en: "Selects the target duration."
|
|
246
|
+
},
|
|
247
|
+
options: createInsertionOptions(AUDIO_DURATION_PRESETS, AUDIO_DURATION_DESCRIPTIONS),
|
|
248
|
+
createTemplate: (value) => ({
|
|
249
|
+
prefix: `<mstts:audioduration value="${value}"/>`,
|
|
250
|
+
suffix: "",
|
|
251
|
+
mode: "insert"
|
|
252
|
+
})
|
|
229
253
|
}
|
|
230
254
|
];
|
|
231
255
|
var DEFAULT_INSERTION_GROUPS = [
|
|
232
256
|
{
|
|
233
257
|
id: "pauses",
|
|
234
258
|
labels: { ja: "\u9593\u30FB\u7121\u97F3", en: "Pauses" },
|
|
235
|
-
insertionIds: ["break", "mstts:silence"]
|
|
259
|
+
insertionIds: ["break", "mstts:silence", "mstts:audioduration"]
|
|
236
260
|
},
|
|
237
261
|
{
|
|
238
262
|
id: "prosody",
|
|
@@ -469,6 +493,60 @@ section[data-ssml-editor] {
|
|
|
469
493
|
border-radius: 0.25rem;
|
|
470
494
|
overflow: visible;
|
|
471
495
|
}
|
|
496
|
+
[data-ssml-editor] .ssml-editor-visual {
|
|
497
|
+
display: grid;
|
|
498
|
+
gap: 0.75rem;
|
|
499
|
+
min-height: 8rem;
|
|
500
|
+
padding: 0.75rem;
|
|
501
|
+
border: 1px solid var(--ssml-editor-control-border);
|
|
502
|
+
border-radius: 0.25rem;
|
|
503
|
+
}
|
|
504
|
+
[data-ssml-editor] .ssml-editor-visual-layout {
|
|
505
|
+
display: grid;
|
|
506
|
+
grid-template-columns: minmax(12rem, 0.35fr) minmax(16rem, 1fr);
|
|
507
|
+
gap: 1rem;
|
|
508
|
+
}
|
|
509
|
+
[data-ssml-editor] .ssml-editor-visual-tree ul {
|
|
510
|
+
margin: 0;
|
|
511
|
+
padding-left: 1.25rem;
|
|
512
|
+
}
|
|
513
|
+
[data-ssml-editor] .ssml-editor-visual button {
|
|
514
|
+
padding: 0.35rem 0.5rem;
|
|
515
|
+
border: 1px solid var(--ssml-editor-control-border);
|
|
516
|
+
border-radius: 0.25rem;
|
|
517
|
+
color: var(--ssml-editor-color);
|
|
518
|
+
background: var(--ssml-editor-control-bg);
|
|
519
|
+
cursor: pointer;
|
|
520
|
+
}
|
|
521
|
+
[data-ssml-editor] .ssml-editor-visual button[data-selected="true"] {
|
|
522
|
+
border-color: var(--ssml-editor-active-border);
|
|
523
|
+
background: var(--ssml-editor-active-bg);
|
|
524
|
+
}
|
|
525
|
+
[data-ssml-editor] .ssml-editor-visual textarea {
|
|
526
|
+
box-sizing: border-box;
|
|
527
|
+
width: 100%;
|
|
528
|
+
min-height: 6rem;
|
|
529
|
+
padding: 0.5rem;
|
|
530
|
+
color: var(--ssml-editor-color);
|
|
531
|
+
background: var(--ssml-editor-control-bg);
|
|
532
|
+
border: 1px solid var(--ssml-editor-control-border);
|
|
533
|
+
border-radius: 0.25rem;
|
|
534
|
+
font: inherit;
|
|
535
|
+
}
|
|
536
|
+
[data-ssml-editor] .ssml-editor-visual-actions,
|
|
537
|
+
[data-ssml-editor] .ssml-editor-visual-breadcrumb {
|
|
538
|
+
display: flex;
|
|
539
|
+
flex-wrap: wrap;
|
|
540
|
+
gap: 0.4rem;
|
|
541
|
+
align-items: center;
|
|
542
|
+
}
|
|
543
|
+
[data-ssml-editor] .ssml-editor-visual-errors {
|
|
544
|
+
padding: 0.5rem;
|
|
545
|
+
color: #b91c1c;
|
|
546
|
+
background: #fef2f2;
|
|
547
|
+
border: 1px solid #b91c1c;
|
|
548
|
+
border-radius: 0.25rem;
|
|
549
|
+
}
|
|
472
550
|
`.trim();
|
|
473
551
|
function injectStyles() {
|
|
474
552
|
if (typeof document === "undefined" || document.getElementById(STYLE_ID)) {
|
|
@@ -482,6 +560,55 @@ function injectStyles() {
|
|
|
482
560
|
function isDarkTheme(theme) {
|
|
483
561
|
return theme === "vs-dark" || theme.toLowerCase().includes("dark");
|
|
484
562
|
}
|
|
563
|
+
function isElement(node) {
|
|
564
|
+
return typeof node !== "string" && node.type !== "text";
|
|
565
|
+
}
|
|
566
|
+
function visualElementName(element) {
|
|
567
|
+
return element.type === "custom" || element.type === "element" ? element.name : element.type;
|
|
568
|
+
}
|
|
569
|
+
function collectVisualTextLeaves(nodes, path = [], ancestors = []) {
|
|
570
|
+
return nodes.flatMap((node, index) => {
|
|
571
|
+
const currentPath = [...path, index];
|
|
572
|
+
if (typeof node === "string") return [{ path: currentPath, value: node, ancestors }];
|
|
573
|
+
if (node.type === "text") return [{ path: currentPath, value: node.value, ancestors }];
|
|
574
|
+
return collectVisualTextLeaves(node.children ?? [], currentPath, [...ancestors, visualElementName(node)]);
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
function updateVisualNodes(nodes, path, update) {
|
|
578
|
+
if (path.length === 0) return nodes;
|
|
579
|
+
const [index, ...rest] = path;
|
|
580
|
+
return nodes.flatMap((node, nodeIndex) => {
|
|
581
|
+
if (nodeIndex !== index) return [node];
|
|
582
|
+
if (rest.length === 0) {
|
|
583
|
+
const next = update(node);
|
|
584
|
+
return Array.isArray(next) ? next : [next];
|
|
585
|
+
}
|
|
586
|
+
if (!isElement(node)) return [node];
|
|
587
|
+
return [{ ...node, children: updateVisualNodes(node.children ?? [], rest, update) }];
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
function updateVisualText(document2, path, value) {
|
|
591
|
+
return { ...document2, children: updateVisualNodes(document2.children ?? [], path, () => value) };
|
|
592
|
+
}
|
|
593
|
+
function wrapVisualText(document2, path, start, end, type, attributes) {
|
|
594
|
+
return {
|
|
595
|
+
...document2,
|
|
596
|
+
children: updateVisualNodes(document2.children ?? [], path, (node) => {
|
|
597
|
+
const value = typeof node === "string" ? node : node.type === "text" ? node.value : "";
|
|
598
|
+
if (!value || start === end) return node;
|
|
599
|
+
if (type === "break") {
|
|
600
|
+
return [value.slice(0, start), { type, attributes, children: [] }, value.slice(start)].filter(
|
|
601
|
+
(part) => typeof part === "string" ? part.length > 0 : true
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
const selected = value.slice(start, end);
|
|
605
|
+
const wrapper = { type, attributes, children: [selected] };
|
|
606
|
+
return [value.slice(0, start), wrapper, value.slice(end)].filter(
|
|
607
|
+
(part) => typeof part === "string" ? part.length > 0 : true
|
|
608
|
+
);
|
|
609
|
+
})
|
|
610
|
+
};
|
|
611
|
+
}
|
|
485
612
|
function getMenuPosition(trigger, menu) {
|
|
486
613
|
const bounds = trigger.getBoundingClientRect();
|
|
487
614
|
const margin = 8;
|
|
@@ -503,6 +630,7 @@ var SsmlEditorElement = class extends HTMLElementBase {
|
|
|
503
630
|
this.toolbarActions = null;
|
|
504
631
|
this.display = null;
|
|
505
632
|
this.editorContainer = null;
|
|
633
|
+
this.visualContainer = null;
|
|
506
634
|
this.helpPanel = null;
|
|
507
635
|
this.openMenu = null;
|
|
508
636
|
this.openMenuTrigger = null;
|
|
@@ -516,6 +644,8 @@ var SsmlEditorElement = class extends HTMLElementBase {
|
|
|
516
644
|
this.documentState = null;
|
|
517
645
|
this.decorationsVisible = false;
|
|
518
646
|
this.helpOpen = false;
|
|
647
|
+
this.visualSelectedPath = null;
|
|
648
|
+
this.visualSelection = { start: 0, end: 0 };
|
|
519
649
|
this.handleDocumentPointerDown = (event) => {
|
|
520
650
|
const target = event.target;
|
|
521
651
|
if (this.openMenu && target instanceof Node && !this.openMenu.contains(target) && !this.openMenuTrigger?.contains(target)) {
|
|
@@ -557,6 +687,12 @@ var SsmlEditorElement = class extends HTMLElementBase {
|
|
|
557
687
|
set locale(locale) {
|
|
558
688
|
this.setAttribute("locale", locale);
|
|
559
689
|
}
|
|
690
|
+
get editMode() {
|
|
691
|
+
return this.getAttribute("edit-mode") === "visual" ? "visual" : "code";
|
|
692
|
+
}
|
|
693
|
+
set editMode(mode) {
|
|
694
|
+
this.setAttribute("edit-mode", mode);
|
|
695
|
+
}
|
|
560
696
|
prepareDocument(value) {
|
|
561
697
|
try {
|
|
562
698
|
this.documentState = parseSsml(value);
|
|
@@ -599,6 +735,7 @@ var SsmlEditorElement = class extends HTMLElementBase {
|
|
|
599
735
|
}
|
|
600
736
|
}
|
|
601
737
|
}
|
|
738
|
+
this.renderVisualEditor();
|
|
602
739
|
return;
|
|
603
740
|
}
|
|
604
741
|
if (name === "theme" && this.monaco) {
|
|
@@ -616,6 +753,9 @@ var SsmlEditorElement = class extends HTMLElementBase {
|
|
|
616
753
|
this.renderHelp();
|
|
617
754
|
return;
|
|
618
755
|
}
|
|
756
|
+
if (name === "edit-mode") {
|
|
757
|
+
this.updateEditMode();
|
|
758
|
+
}
|
|
619
759
|
if (name === "show-decorations") {
|
|
620
760
|
this.decorationsVisible = newValue !== null;
|
|
621
761
|
this.updateDecorations();
|
|
@@ -640,7 +780,9 @@ var SsmlEditorElement = class extends HTMLElementBase {
|
|
|
640
780
|
display.dataset.ssmlEditorDisplay = "";
|
|
641
781
|
const editorContainer = document.createElement("div");
|
|
642
782
|
editorContainer.className = "ssml-editor-editor";
|
|
643
|
-
|
|
783
|
+
const visualContainer = document.createElement("div");
|
|
784
|
+
visualContainer.className = "ssml-editor-visual";
|
|
785
|
+
display.append(editorContainer, visualContainer);
|
|
644
786
|
root.append(toolbar, display);
|
|
645
787
|
this.replaceChildren(root);
|
|
646
788
|
this.root = root;
|
|
@@ -648,8 +790,11 @@ var SsmlEditorElement = class extends HTMLElementBase {
|
|
|
648
790
|
this.toolbarActions = toolbarActions;
|
|
649
791
|
this.display = display;
|
|
650
792
|
this.editorContainer = editorContainer;
|
|
793
|
+
this.visualContainer = visualContainer;
|
|
651
794
|
this.renderToolbar();
|
|
652
795
|
this.renderHelp();
|
|
796
|
+
this.updateEditMode();
|
|
797
|
+
this.renderVisualEditor();
|
|
653
798
|
}
|
|
654
799
|
renderToolbar() {
|
|
655
800
|
const toolbar = this.toolbar;
|
|
@@ -693,10 +838,10 @@ var SsmlEditorElement = class extends HTMLElementBase {
|
|
|
693
838
|
for (const id of toolbarIds) {
|
|
694
839
|
const group = groupByButtonId.get(id);
|
|
695
840
|
if (previousGroup !== void 0 && group !== previousGroup) {
|
|
696
|
-
const
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
toolbarActions.append(
|
|
841
|
+
const separator2 = document.createElement("span");
|
|
842
|
+
separator2.className = "ssml-editor-toolbar-separator";
|
|
843
|
+
separator2.setAttribute("aria-hidden", "true");
|
|
844
|
+
toolbarActions.append(separator2);
|
|
700
845
|
}
|
|
701
846
|
previousGroup = group;
|
|
702
847
|
const insertion = insertionById.get(id);
|
|
@@ -708,8 +853,24 @@ var SsmlEditorElement = class extends HTMLElementBase {
|
|
|
708
853
|
toolbarActions.append(this.createActionButton(id));
|
|
709
854
|
}
|
|
710
855
|
}
|
|
856
|
+
const separator = document.createElement("span");
|
|
857
|
+
separator.className = "ssml-editor-toolbar-separator";
|
|
858
|
+
separator.setAttribute("aria-hidden", "true");
|
|
859
|
+
toolbarActions.append(separator, this.createModeButton("visual", "Visual"), this.createModeButton("code", "Code"));
|
|
711
860
|
this.updateActiveButtons();
|
|
712
861
|
}
|
|
862
|
+
createModeButton(mode, label) {
|
|
863
|
+
const button = document.createElement("button");
|
|
864
|
+
button.type = "button";
|
|
865
|
+
button.className = "ssml-editor-toolbar-button";
|
|
866
|
+
button.dataset.ssmlEditorButton = `edit-mode-${mode}`;
|
|
867
|
+
button.setAttribute("aria-pressed", String(this.editMode === mode));
|
|
868
|
+
button.textContent = label;
|
|
869
|
+
button.addEventListener("click", () => {
|
|
870
|
+
this.editMode = mode;
|
|
871
|
+
});
|
|
872
|
+
return button;
|
|
873
|
+
}
|
|
713
874
|
createActionButton(id) {
|
|
714
875
|
const copy = EDITOR_COPY[this.locale];
|
|
715
876
|
const labels = {
|
|
@@ -1089,6 +1250,150 @@ var SsmlEditorElement = class extends HTMLElementBase {
|
|
|
1089
1250
|
this.root.dataset.theme = isDarkTheme(this.theme) ? "dark" : "light";
|
|
1090
1251
|
}
|
|
1091
1252
|
}
|
|
1253
|
+
updateEditMode() {
|
|
1254
|
+
if (this.editorContainer && this.visualContainer) {
|
|
1255
|
+
const visual = this.editMode === "visual";
|
|
1256
|
+
this.editorContainer.hidden = visual;
|
|
1257
|
+
this.visualContainer.hidden = !visual;
|
|
1258
|
+
}
|
|
1259
|
+
for (const mode of ["visual", "code"]) {
|
|
1260
|
+
const button = this.toolbarActions?.querySelector(
|
|
1261
|
+
`[data-ssml-editor-button="edit-mode-${mode}"]`
|
|
1262
|
+
);
|
|
1263
|
+
button?.setAttribute("aria-pressed", String(this.editMode === mode));
|
|
1264
|
+
button?.toggleAttribute("data-active", this.editMode === mode);
|
|
1265
|
+
}
|
|
1266
|
+
this.renderVisualEditor();
|
|
1267
|
+
}
|
|
1268
|
+
renderVisualEditor() {
|
|
1269
|
+
const container = this.visualContainer;
|
|
1270
|
+
if (!container) return;
|
|
1271
|
+
container.replaceChildren();
|
|
1272
|
+
if (!this.documentState) {
|
|
1273
|
+
const error = document.createElement("p");
|
|
1274
|
+
error.className = "ssml-editor-visual-errors";
|
|
1275
|
+
error.textContent = "SSML syntax must be valid before visual editing is available.";
|
|
1276
|
+
container.append(error);
|
|
1277
|
+
return;
|
|
1278
|
+
}
|
|
1279
|
+
const documentState = this.documentState;
|
|
1280
|
+
const leaves = collectVisualTextLeaves(documentState.children ?? []);
|
|
1281
|
+
const selectedLeaf = leaves.find((leaf) => leaf.path.join(".") === this.visualSelectedPath?.join(".")) ?? leaves[0];
|
|
1282
|
+
const breadcrumb = document.createElement("div");
|
|
1283
|
+
breadcrumb.className = "ssml-editor-visual-breadcrumb";
|
|
1284
|
+
breadcrumb.textContent = `<speak>${selectedLeaf ? ` / ${selectedLeaf.ancestors.map((name) => `<${name}>`).join(" / ")}` : ""}`;
|
|
1285
|
+
const clear = document.createElement("button");
|
|
1286
|
+
clear.type = "button";
|
|
1287
|
+
clear.textContent = "Clear parent";
|
|
1288
|
+
clear.disabled = !this.visualSelectedPath;
|
|
1289
|
+
clear.addEventListener("click", () => {
|
|
1290
|
+
this.visualSelectedPath = null;
|
|
1291
|
+
this.renderVisualEditor();
|
|
1292
|
+
});
|
|
1293
|
+
breadcrumb.append(clear);
|
|
1294
|
+
container.append(breadcrumb);
|
|
1295
|
+
const diagnostics = validateAzureSsml(buildSsml(documentState));
|
|
1296
|
+
if (diagnostics.length > 0) {
|
|
1297
|
+
const errors = document.createElement("div");
|
|
1298
|
+
errors.className = "ssml-editor-visual-errors";
|
|
1299
|
+
errors.setAttribute("role", "alert");
|
|
1300
|
+
for (const diagnostic of diagnostics) {
|
|
1301
|
+
const message = document.createElement("div");
|
|
1302
|
+
message.textContent = diagnostic.message;
|
|
1303
|
+
errors.append(message);
|
|
1304
|
+
}
|
|
1305
|
+
container.append(errors);
|
|
1306
|
+
}
|
|
1307
|
+
const layout = document.createElement("div");
|
|
1308
|
+
layout.className = "ssml-editor-visual-layout";
|
|
1309
|
+
const tree = document.createElement("nav");
|
|
1310
|
+
tree.className = "ssml-editor-visual-tree";
|
|
1311
|
+
tree.setAttribute("aria-label", "SSML structure tree");
|
|
1312
|
+
tree.append(document.createTextNode("Structure"));
|
|
1313
|
+
const treeList = document.createElement("ul");
|
|
1314
|
+
const renderTree = (nodes, parent, parentPath = []) => {
|
|
1315
|
+
nodes.forEach((node, index) => {
|
|
1316
|
+
if (!isElement(node)) return;
|
|
1317
|
+
const path = [...parentPath, index];
|
|
1318
|
+
const item = document.createElement("li");
|
|
1319
|
+
const button = document.createElement("button");
|
|
1320
|
+
button.type = "button";
|
|
1321
|
+
button.textContent = `<${visualElementName(node)}>`;
|
|
1322
|
+
button.dataset.selected = String(path.join(".") === this.visualSelectedPath?.join("."));
|
|
1323
|
+
button.addEventListener("click", () => {
|
|
1324
|
+
this.visualSelectedPath = path;
|
|
1325
|
+
this.renderVisualEditor();
|
|
1326
|
+
});
|
|
1327
|
+
item.append(button);
|
|
1328
|
+
if ((node.children ?? []).some(isElement)) {
|
|
1329
|
+
const childList = document.createElement("ul");
|
|
1330
|
+
renderTree(node.children ?? [], childList, path);
|
|
1331
|
+
item.append(childList);
|
|
1332
|
+
}
|
|
1333
|
+
parent.append(item);
|
|
1334
|
+
});
|
|
1335
|
+
};
|
|
1336
|
+
renderTree(documentState.children ?? [], treeList);
|
|
1337
|
+
tree.append(treeList);
|
|
1338
|
+
layout.append(tree);
|
|
1339
|
+
const form = document.createElement("div");
|
|
1340
|
+
form.className = "ssml-editor-visual-form";
|
|
1341
|
+
if (selectedLeaf) {
|
|
1342
|
+
const label = document.createElement("label");
|
|
1343
|
+
label.append(document.createTextNode("Text"));
|
|
1344
|
+
const textarea = document.createElement("textarea");
|
|
1345
|
+
textarea.value = selectedLeaf.value;
|
|
1346
|
+
textarea.readOnly = this.readonly;
|
|
1347
|
+
textarea.addEventListener("select", () => {
|
|
1348
|
+
this.visualSelection = { start: textarea.selectionStart, end: textarea.selectionEnd };
|
|
1349
|
+
});
|
|
1350
|
+
textarea.addEventListener("input", () => {
|
|
1351
|
+
if (!this.readonly) this.replaceDocument(updateVisualText(documentState, selectedLeaf.path, textarea.value));
|
|
1352
|
+
});
|
|
1353
|
+
label.append(textarea);
|
|
1354
|
+
form.append(label);
|
|
1355
|
+
const actions = document.createElement("div");
|
|
1356
|
+
actions.className = "ssml-editor-visual-actions";
|
|
1357
|
+
const wrappers = [
|
|
1358
|
+
["Rate", "prosody", { rate: "slow" }],
|
|
1359
|
+
["Pitch", "prosody", { pitch: "high" }],
|
|
1360
|
+
["Emotion", "mstts:express-as", { style: "cheerful" }],
|
|
1361
|
+
["Pause", "break", { time: "500ms" }],
|
|
1362
|
+
["Pronunciation", "phoneme", { alphabet: "ipa", ph: selectedLeaf.value }]
|
|
1363
|
+
];
|
|
1364
|
+
for (const [labelText, type, attributes] of wrappers) {
|
|
1365
|
+
const button = document.createElement("button");
|
|
1366
|
+
button.type = "button";
|
|
1367
|
+
button.textContent = labelText;
|
|
1368
|
+
button.disabled = this.readonly;
|
|
1369
|
+
button.addEventListener("click", () => {
|
|
1370
|
+
const start = this.visualSelection.start === this.visualSelection.end ? 0 : this.visualSelection.start;
|
|
1371
|
+
const end = this.visualSelection.start === this.visualSelection.end ? selectedLeaf.value.length : this.visualSelection.end;
|
|
1372
|
+
this.replaceDocument(wrapVisualText(documentState, selectedLeaf.path, start, end, type, attributes));
|
|
1373
|
+
this.renderVisualEditor();
|
|
1374
|
+
});
|
|
1375
|
+
actions.append(button);
|
|
1376
|
+
}
|
|
1377
|
+
const preview = document.createElement("button");
|
|
1378
|
+
preview.type = "button";
|
|
1379
|
+
preview.textContent = "Preview selection";
|
|
1380
|
+
preview.addEventListener("click", () => {
|
|
1381
|
+
this.dispatchEvent(
|
|
1382
|
+
new CustomEvent("preview-selection", {
|
|
1383
|
+
detail: { ssml: buildSsml({ ...documentState, children: [selectedLeaf.value] }) },
|
|
1384
|
+
bubbles: true,
|
|
1385
|
+
composed: true
|
|
1386
|
+
})
|
|
1387
|
+
);
|
|
1388
|
+
});
|
|
1389
|
+
actions.append(preview);
|
|
1390
|
+
form.append(actions);
|
|
1391
|
+
} else {
|
|
1392
|
+
form.textContent = "Select an element or text node to edit it.";
|
|
1393
|
+
}
|
|
1394
|
+
layout.append(form);
|
|
1395
|
+
container.append(layout);
|
|
1396
|
+
}
|
|
1092
1397
|
updateActiveButtons() {
|
|
1093
1398
|
const editor = this.editor;
|
|
1094
1399
|
const model = this.model;
|
|
@@ -1116,6 +1421,7 @@ var SsmlEditorElement = class extends HTMLElementBase {
|
|
|
1116
1421
|
return;
|
|
1117
1422
|
}
|
|
1118
1423
|
const model = monacoModule.editor.createModel(this.prepareDocument(this.value), "xml");
|
|
1424
|
+
this.renderVisualEditor();
|
|
1119
1425
|
const editor = monacoModule.editor.create(container, {
|
|
1120
1426
|
model,
|
|
1121
1427
|
theme: this.theme,
|
|
@@ -1214,7 +1520,8 @@ SsmlEditorElement.observedAttributes = [
|
|
|
1214
1520
|
"locale",
|
|
1215
1521
|
"show-toolbar",
|
|
1216
1522
|
"show-toolbar-labels",
|
|
1217
|
-
"show-decorations"
|
|
1523
|
+
"show-decorations",
|
|
1524
|
+
"edit-mode"
|
|
1218
1525
|
];
|
|
1219
1526
|
|
|
1220
1527
|
// packages/ssml-editor-elements/src/index.ts
|