@ssml-builder-js/ssml-editor-elements 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/CHANGELOG.md +19 -0
- package/dist/index.d.mts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +531 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +532 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/SsmlEditorElement.ts +322 -3
- package/src/index.ts +1 -0
- package/tsconfig.json +1 -0
package/dist/index.js
CHANGED
|
@@ -100,6 +100,7 @@ var INTRINSICALLY_EMPTY_ELEMENTS = /* @__PURE__ */ new Set([
|
|
|
100
100
|
"lexicon",
|
|
101
101
|
"mark",
|
|
102
102
|
"mstts:silence",
|
|
103
|
+
"mstts:audioduration",
|
|
103
104
|
"mstts:viseme",
|
|
104
105
|
"silence",
|
|
105
106
|
"viseme"
|
|
@@ -758,6 +759,8 @@ function updateEditableText(document2, value) {
|
|
|
758
759
|
// ../ssml-editor-react/src/constants/ssmlPresets.ts
|
|
759
760
|
var ssmlPresets_exports = {};
|
|
760
761
|
__export(ssmlPresets_exports, {
|
|
762
|
+
AUDIO_DURATION_DESCRIPTIONS: () => AUDIO_DURATION_DESCRIPTIONS,
|
|
763
|
+
AUDIO_DURATION_PRESETS: () => AUDIO_DURATION_PRESETS,
|
|
761
764
|
BREAK_STRENGTH_PRESETS: () => BREAK_STRENGTH_PRESETS,
|
|
762
765
|
BREAK_TIME_DESCRIPTIONS: () => BREAK_TIME_DESCRIPTIONS,
|
|
763
766
|
BREAK_TIME_PRESETS: () => BREAK_TIME_PRESETS,
|
|
@@ -790,6 +793,103 @@ __export(ssmlPresets_exports, {
|
|
|
790
793
|
getExpressAsStyleCategory: () => getExpressAsStyleCategory,
|
|
791
794
|
resolveExpressAsStyles: () => resolveExpressAsStyles
|
|
792
795
|
});
|
|
796
|
+
|
|
797
|
+
// ../ssml-editor-react/src/constants/azureVoiceStyleMap.generated.ts
|
|
798
|
+
var AZURE_VOICE_STYLE_MAP = {
|
|
799
|
+
"de-DE-ConradNeural": ["cheerful", "sad"],
|
|
800
|
+
"de-DE-KatjaNeural": ["cheerful", "sad"],
|
|
801
|
+
"en-US-AndrewNeural": ["empathetic", "relieved"],
|
|
802
|
+
"en-US-GuyNeural": [
|
|
803
|
+
"angry",
|
|
804
|
+
"cheerful",
|
|
805
|
+
"excited",
|
|
806
|
+
"friendly",
|
|
807
|
+
"hopeful",
|
|
808
|
+
"newscast",
|
|
809
|
+
"sad",
|
|
810
|
+
"shouting",
|
|
811
|
+
"terrified",
|
|
812
|
+
"unfriendly",
|
|
813
|
+
"whispering"
|
|
814
|
+
],
|
|
815
|
+
"en-US-JennyMultilingualNeural": [
|
|
816
|
+
"cheerful",
|
|
817
|
+
"empathetic",
|
|
818
|
+
"excited",
|
|
819
|
+
"friendly",
|
|
820
|
+
"hopeful",
|
|
821
|
+
"sad",
|
|
822
|
+
"shouting",
|
|
823
|
+
"terrified",
|
|
824
|
+
"unfriendly",
|
|
825
|
+
"whispering"
|
|
826
|
+
],
|
|
827
|
+
"en-US-JennyNeural": [
|
|
828
|
+
"assistant",
|
|
829
|
+
"chat",
|
|
830
|
+
"customerservice",
|
|
831
|
+
"newscast",
|
|
832
|
+
"cheerful",
|
|
833
|
+
"empathetic",
|
|
834
|
+
"excited",
|
|
835
|
+
"friendly",
|
|
836
|
+
"hopeful",
|
|
837
|
+
"sad",
|
|
838
|
+
"shouting",
|
|
839
|
+
"terrified",
|
|
840
|
+
"unfriendly",
|
|
841
|
+
"whispering"
|
|
842
|
+
],
|
|
843
|
+
"es-ES-ElviraNeural": [],
|
|
844
|
+
"fil-PH-AngeloNeural": [],
|
|
845
|
+
"fr-FR-DeniseNeural": ["cheerful", "sad"],
|
|
846
|
+
"fr-FR-HenriNeural": ["cheerful", "sad"],
|
|
847
|
+
"id-ID-GadisNeural": [],
|
|
848
|
+
"it-IT-ElsaNeural": ["cheerful", "sad"],
|
|
849
|
+
"ja-JP-KeitaNeural": ["chat"],
|
|
850
|
+
"ja-JP-MayuNeural": ["calm", "cheerful", "sad"],
|
|
851
|
+
"ja-JP-NanamiNeural": ["chat", "customerservice", "cheerful", "whispering", "sad"],
|
|
852
|
+
"ko-KR-SunHiNeural": ["cheerful", "sad"],
|
|
853
|
+
"ms-MY-YasminNeural": [],
|
|
854
|
+
"pt-BR-FranciscaNeural": ["calm"],
|
|
855
|
+
"ru-RU-SvetlanaNeural": ["cheerful", "sad", "angry", "disgruntled", "embarrassed", "fearful"],
|
|
856
|
+
"th-TH-PremwadeeNeural": [],
|
|
857
|
+
"vi-VN-HoaiMyNeural": [],
|
|
858
|
+
"zh-CN-XiaoxiaoNeural": [
|
|
859
|
+
"assistant",
|
|
860
|
+
"chat",
|
|
861
|
+
"customerservice",
|
|
862
|
+
"newscast",
|
|
863
|
+
"cheerful",
|
|
864
|
+
"empathetic",
|
|
865
|
+
"excited",
|
|
866
|
+
"friendly",
|
|
867
|
+
"hopeful",
|
|
868
|
+
"sad",
|
|
869
|
+
"terrified",
|
|
870
|
+
"whispering",
|
|
871
|
+
"poetry-reading",
|
|
872
|
+
"sports_commentary",
|
|
873
|
+
"sports_commentary_excited",
|
|
874
|
+
"story"
|
|
875
|
+
],
|
|
876
|
+
"zh-CN-YunxiNeural": [
|
|
877
|
+
"narration-relaxed",
|
|
878
|
+
"embarrassed",
|
|
879
|
+
"fearful",
|
|
880
|
+
"sad",
|
|
881
|
+
"disgruntled",
|
|
882
|
+
"serious",
|
|
883
|
+
"angry",
|
|
884
|
+
"depressed",
|
|
885
|
+
"chat",
|
|
886
|
+
"cheerful",
|
|
887
|
+
"assistant"
|
|
888
|
+
],
|
|
889
|
+
"zh-TW-HsiaoChenNeural": []
|
|
890
|
+
};
|
|
891
|
+
|
|
892
|
+
// ../ssml-editor-react/src/constants/ssmlPresets.ts
|
|
793
893
|
var SSML_PRESETS = [
|
|
794
894
|
{
|
|
795
895
|
id: "basic",
|
|
@@ -889,7 +989,7 @@ function getExpressAsStyleCategory(style) {
|
|
|
889
989
|
}
|
|
890
990
|
return "other";
|
|
891
991
|
}
|
|
892
|
-
var
|
|
992
|
+
var LEGACY_VOICE_STYLE_MAP = {
|
|
893
993
|
"ja-JP-MayuNeural": ["calm", "cheerful", "sad"],
|
|
894
994
|
"ja-JP-KeitaNeural": ["chat"],
|
|
895
995
|
"ja-JP-NanamiNeural": ["chat", "customerservice", "cheerful", "whispering", "sad"],
|
|
@@ -975,6 +1075,7 @@ var VOICE_STYLE_MAP = {
|
|
|
975
1075
|
"de-DE-ConradNeural": ["cheerful", "sad"],
|
|
976
1076
|
"ru-RU-SvetlanaNeural": ["cheerful", "sad", "angry", "disgruntled", "embarrassed", "fearful"]
|
|
977
1077
|
};
|
|
1078
|
+
var VOICE_STYLE_MAP = Object.keys(AZURE_VOICE_STYLE_MAP).length > 0 ? AZURE_VOICE_STYLE_MAP : LEGACY_VOICE_STYLE_MAP;
|
|
978
1079
|
var VOICE_STYLE_MAP_BY_NORMALIZED_NAME = new Map(
|
|
979
1080
|
Object.entries(VOICE_STYLE_MAP).map(([voiceName, styles]) => [voiceName.toLowerCase(), styles])
|
|
980
1081
|
);
|
|
@@ -1017,6 +1118,7 @@ var SAY_AS_PRESETS = [
|
|
|
1017
1118
|
];
|
|
1018
1119
|
var LANGUAGE_PRESETS = ["ja-JP", "en-US", "de-DE", "fr-FR"];
|
|
1019
1120
|
var SILENCE_VALUE_PRESETS = ["300ms", "500ms", "1s"];
|
|
1121
|
+
var AUDIO_DURATION_PRESETS = ["5s", "10s", "30s"];
|
|
1020
1122
|
var SILENCE_TYPE_PRESETS = [
|
|
1021
1123
|
"Leading",
|
|
1022
1124
|
"Tailing",
|
|
@@ -1025,6 +1127,11 @@ var SILENCE_TYPE_PRESETS = [
|
|
|
1025
1127
|
"Semicolon",
|
|
1026
1128
|
"Enumerationcomma"
|
|
1027
1129
|
];
|
|
1130
|
+
var AUDIO_DURATION_DESCRIPTIONS = {
|
|
1131
|
+
"5s": { ja: "5\u79D2", en: "5 seconds" },
|
|
1132
|
+
"10s": { ja: "10\u79D2", en: "10 seconds" },
|
|
1133
|
+
"30s": { ja: "30\u79D2", en: "30 seconds" }
|
|
1134
|
+
};
|
|
1028
1135
|
var PHONEME_ALPHABET_PRESETS = ["ipa", "sapi", "ups", "x-sampa"];
|
|
1029
1136
|
var VISEME_TYPE_PRESETS = ["redlips_front", "FacialExpression"];
|
|
1030
1137
|
var SSML_ATTRIBUTE_PRESETS = {
|
|
@@ -1068,6 +1175,14 @@ var SSML_ATTRIBUTE_PRESETS = {
|
|
|
1068
1175
|
type: SILENCE_TYPE_PRESETS,
|
|
1069
1176
|
value: SILENCE_VALUE_PRESETS
|
|
1070
1177
|
},
|
|
1178
|
+
"mstts:audioduration": {
|
|
1179
|
+
value: AUDIO_DURATION_PRESETS
|
|
1180
|
+
},
|
|
1181
|
+
"mstts:backgroundaudio": {
|
|
1182
|
+
volume: PROSODY_VOLUME_PRESETS,
|
|
1183
|
+
fadein: AUDIO_DURATION_PRESETS,
|
|
1184
|
+
fadeout: AUDIO_DURATION_PRESETS
|
|
1185
|
+
},
|
|
1071
1186
|
silence: {
|
|
1072
1187
|
type: SILENCE_TYPE_PRESETS,
|
|
1073
1188
|
value: SILENCE_VALUE_PRESETS
|
|
@@ -1555,6 +1670,28 @@ var SSML_COMPLETION_SNIPPETS = [
|
|
|
1555
1670
|
label: "mstts:express-as",
|
|
1556
1671
|
insertText: `<mstts:express-as style="cheerful">\${1:text}</mstts:express-as>`
|
|
1557
1672
|
},
|
|
1673
|
+
{
|
|
1674
|
+
label: "mstts:audioduration",
|
|
1675
|
+
insertText: '<mstts:audioduration value="10s" />'
|
|
1676
|
+
},
|
|
1677
|
+
{
|
|
1678
|
+
label: "mstts:dialog",
|
|
1679
|
+
insertText: `<mstts:dialog>
|
|
1680
|
+
<mstts:turn voice="\${1:en-US-JennyNeural}">\${2:text}</mstts:turn>
|
|
1681
|
+
</mstts:dialog>`
|
|
1682
|
+
},
|
|
1683
|
+
{
|
|
1684
|
+
label: "mstts:turn",
|
|
1685
|
+
insertText: `<mstts:turn voice="\${1:en-US-JennyNeural}">\${2:text}</mstts:turn>`
|
|
1686
|
+
},
|
|
1687
|
+
{
|
|
1688
|
+
label: "mstts:backgroundaudio",
|
|
1689
|
+
insertText: `<mstts:backgroundaudio src="\${1:https://example.com/audio.mp3}" volume="\${2:-3dB}" />`
|
|
1690
|
+
},
|
|
1691
|
+
{
|
|
1692
|
+
label: "mstts:ttsembedding",
|
|
1693
|
+
insertText: `<mstts:ttsembedding>\${1:text}</mstts:ttsembedding>`
|
|
1694
|
+
},
|
|
1558
1695
|
{
|
|
1559
1696
|
label: "sub",
|
|
1560
1697
|
insertText: `<sub alias="\${1:\u8AAD\u307F}">\${2:\u6F22\u5B57}</sub>`
|
|
@@ -1822,6 +1959,13 @@ var SSML_HOVER_COPY = {
|
|
|
1822
1959
|
parameters: {
|
|
1823
1960
|
type: { title: "type", description: "\u30D3\u30BC\u30FC\u30E0\u30A4\u30D9\u30F3\u30C8\u306E\u5F62\u5F0F\u3002" }
|
|
1824
1961
|
}
|
|
1962
|
+
},
|
|
1963
|
+
"mstts:audioduration": {
|
|
1964
|
+
title: "\u97F3\u58F0\u9577",
|
|
1965
|
+
description: "\u5408\u6210\u97F3\u58F0\u306E\u76EE\u6A19\u6642\u9593\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
|
|
1966
|
+
parameters: {
|
|
1967
|
+
value: { title: "value", description: "\u76EE\u6A19\u6642\u9593\u3002\u4F8B: `10s`\u3001`5000ms`\u3001`00:00:10`\u3002" }
|
|
1968
|
+
}
|
|
1825
1969
|
}
|
|
1826
1970
|
}
|
|
1827
1971
|
},
|
|
@@ -1979,6 +2123,13 @@ var SSML_HOVER_COPY = {
|
|
|
1979
2123
|
parameters: {
|
|
1980
2124
|
type: { title: "type", description: "The viseme event format." }
|
|
1981
2125
|
}
|
|
2126
|
+
},
|
|
2127
|
+
"mstts:audioduration": {
|
|
2128
|
+
title: "Audio duration",
|
|
2129
|
+
description: "Sets the target duration of synthesized audio.",
|
|
2130
|
+
parameters: {
|
|
2131
|
+
value: { title: "value", description: "The target duration, such as `10s`, `5000ms`, or `00:00:10`." }
|
|
2132
|
+
}
|
|
1982
2133
|
}
|
|
1983
2134
|
}
|
|
1984
2135
|
}
|
|
@@ -2275,6 +2426,74 @@ var SSML_TAG_DEFINITIONS = [
|
|
|
2275
2426
|
values: VISEME_TYPE_PRESETS2
|
|
2276
2427
|
}
|
|
2277
2428
|
]
|
|
2429
|
+
},
|
|
2430
|
+
{
|
|
2431
|
+
name: "mstts:audioduration",
|
|
2432
|
+
description: "Sets the target duration of synthesized audio.",
|
|
2433
|
+
parameters: [
|
|
2434
|
+
{
|
|
2435
|
+
name: "value",
|
|
2436
|
+
description: "The target duration, such as `10s`, `5000ms`, or `00:00:10`.",
|
|
2437
|
+
example: "10s"
|
|
2438
|
+
}
|
|
2439
|
+
]
|
|
2440
|
+
},
|
|
2441
|
+
{
|
|
2442
|
+
name: "mstts:dialog",
|
|
2443
|
+
description: "Groups multiple Azure dialog turns that can use different voices.",
|
|
2444
|
+
parameters: []
|
|
2445
|
+
},
|
|
2446
|
+
{
|
|
2447
|
+
name: "mstts:turn",
|
|
2448
|
+
description: "Adds one dialog turn using the required Azure voice name.",
|
|
2449
|
+
parameters: [
|
|
2450
|
+
{
|
|
2451
|
+
name: "voice",
|
|
2452
|
+
description: "The Azure voice used for this turn, such as `en-US-JennyNeural`.",
|
|
2453
|
+
example: "en-US-JennyNeural"
|
|
2454
|
+
}
|
|
2455
|
+
]
|
|
2456
|
+
},
|
|
2457
|
+
{
|
|
2458
|
+
name: "mstts:backgroundaudio",
|
|
2459
|
+
description: "Plays background audio while speech is synthesized.",
|
|
2460
|
+
parameters: [
|
|
2461
|
+
{
|
|
2462
|
+
name: "src",
|
|
2463
|
+
description: "An absolute HTTP(S) URL for the background audio file.",
|
|
2464
|
+
example: "https://example.com/music.mp3"
|
|
2465
|
+
},
|
|
2466
|
+
{
|
|
2467
|
+
name: "volume",
|
|
2468
|
+
description: "The background audio volume, for example `-3dB` or `medium`.",
|
|
2469
|
+
example: "-3dB"
|
|
2470
|
+
},
|
|
2471
|
+
{
|
|
2472
|
+
name: "fadein",
|
|
2473
|
+
description: "The fade-in duration, for example `1s`.",
|
|
2474
|
+
example: "1s"
|
|
2475
|
+
},
|
|
2476
|
+
{
|
|
2477
|
+
name: "fadeout",
|
|
2478
|
+
description: "The fade-out duration, for example `500ms`.",
|
|
2479
|
+
example: "500ms"
|
|
2480
|
+
}
|
|
2481
|
+
]
|
|
2482
|
+
},
|
|
2483
|
+
{
|
|
2484
|
+
name: "mstts:ttsembedding",
|
|
2485
|
+
description: "Embeds custom voice or speaker profile metadata for Azure Speech.",
|
|
2486
|
+
parameters: []
|
|
2487
|
+
},
|
|
2488
|
+
{
|
|
2489
|
+
name: "mstts:embedding",
|
|
2490
|
+
description: "Specifies embedding metadata for custom voice scenarios.",
|
|
2491
|
+
parameters: []
|
|
2492
|
+
},
|
|
2493
|
+
{
|
|
2494
|
+
name: "mstts:voiceconversion",
|
|
2495
|
+
description: "Specifies voice conversion metadata for custom voice scenarios.",
|
|
2496
|
+
parameters: []
|
|
2278
2497
|
}
|
|
2279
2498
|
];
|
|
2280
2499
|
var definitionsByName = /* @__PURE__ */ new Map();
|
|
@@ -2816,13 +3035,34 @@ var SSML_INSERTIONS = [
|
|
|
2816
3035
|
suffix: "",
|
|
2817
3036
|
mode: "insert"
|
|
2818
3037
|
})
|
|
3038
|
+
},
|
|
3039
|
+
{
|
|
3040
|
+
id: "mstts:audioduration",
|
|
3041
|
+
icon: "\u25F7",
|
|
3042
|
+
tagName: "mstts:audioduration",
|
|
3043
|
+
selfClosing: true,
|
|
3044
|
+
labels: { ja: "\u97F3\u58F0\u9577", en: "Audio duration" },
|
|
3045
|
+
descriptions: {
|
|
3046
|
+
ja: "\u5408\u6210\u97F3\u58F0\u306E\u76EE\u6A19\u6642\u9593\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
|
|
3047
|
+
en: "Sets the target duration of synthesized audio."
|
|
3048
|
+
},
|
|
3049
|
+
parameterDescription: {
|
|
3050
|
+
ja: "\u76EE\u6A19\u6642\u9593\u3092\u9078\u629E\u3057\u307E\u3059\u3002",
|
|
3051
|
+
en: "Selects the target duration."
|
|
3052
|
+
},
|
|
3053
|
+
options: createInsertionOptions(AUDIO_DURATION_PRESETS, AUDIO_DURATION_DESCRIPTIONS),
|
|
3054
|
+
createTemplate: (value) => ({
|
|
3055
|
+
prefix: `<mstts:audioduration value="${value}"/>`,
|
|
3056
|
+
suffix: "",
|
|
3057
|
+
mode: "insert"
|
|
3058
|
+
})
|
|
2819
3059
|
}
|
|
2820
3060
|
];
|
|
2821
3061
|
var DEFAULT_INSERTION_GROUPS = [
|
|
2822
3062
|
{
|
|
2823
3063
|
id: "pauses",
|
|
2824
3064
|
labels: { ja: "\u9593\u30FB\u7121\u97F3", en: "Pauses" },
|
|
2825
|
-
insertionIds: ["break", "mstts:silence"]
|
|
3065
|
+
insertionIds: ["break", "mstts:silence", "mstts:audioduration"]
|
|
2826
3066
|
},
|
|
2827
3067
|
{
|
|
2828
3068
|
id: "prosody",
|
|
@@ -3059,6 +3299,60 @@ section[data-ssml-editor] {
|
|
|
3059
3299
|
border-radius: 0.25rem;
|
|
3060
3300
|
overflow: visible;
|
|
3061
3301
|
}
|
|
3302
|
+
[data-ssml-editor] .ssml-editor-visual {
|
|
3303
|
+
display: grid;
|
|
3304
|
+
gap: 0.75rem;
|
|
3305
|
+
min-height: 8rem;
|
|
3306
|
+
padding: 0.75rem;
|
|
3307
|
+
border: 1px solid var(--ssml-editor-control-border);
|
|
3308
|
+
border-radius: 0.25rem;
|
|
3309
|
+
}
|
|
3310
|
+
[data-ssml-editor] .ssml-editor-visual-layout {
|
|
3311
|
+
display: grid;
|
|
3312
|
+
grid-template-columns: minmax(12rem, 0.35fr) minmax(16rem, 1fr);
|
|
3313
|
+
gap: 1rem;
|
|
3314
|
+
}
|
|
3315
|
+
[data-ssml-editor] .ssml-editor-visual-tree ul {
|
|
3316
|
+
margin: 0;
|
|
3317
|
+
padding-left: 1.25rem;
|
|
3318
|
+
}
|
|
3319
|
+
[data-ssml-editor] .ssml-editor-visual button {
|
|
3320
|
+
padding: 0.35rem 0.5rem;
|
|
3321
|
+
border: 1px solid var(--ssml-editor-control-border);
|
|
3322
|
+
border-radius: 0.25rem;
|
|
3323
|
+
color: var(--ssml-editor-color);
|
|
3324
|
+
background: var(--ssml-editor-control-bg);
|
|
3325
|
+
cursor: pointer;
|
|
3326
|
+
}
|
|
3327
|
+
[data-ssml-editor] .ssml-editor-visual button[data-selected="true"] {
|
|
3328
|
+
border-color: var(--ssml-editor-active-border);
|
|
3329
|
+
background: var(--ssml-editor-active-bg);
|
|
3330
|
+
}
|
|
3331
|
+
[data-ssml-editor] .ssml-editor-visual textarea {
|
|
3332
|
+
box-sizing: border-box;
|
|
3333
|
+
width: 100%;
|
|
3334
|
+
min-height: 6rem;
|
|
3335
|
+
padding: 0.5rem;
|
|
3336
|
+
color: var(--ssml-editor-color);
|
|
3337
|
+
background: var(--ssml-editor-control-bg);
|
|
3338
|
+
border: 1px solid var(--ssml-editor-control-border);
|
|
3339
|
+
border-radius: 0.25rem;
|
|
3340
|
+
font: inherit;
|
|
3341
|
+
}
|
|
3342
|
+
[data-ssml-editor] .ssml-editor-visual-actions,
|
|
3343
|
+
[data-ssml-editor] .ssml-editor-visual-breadcrumb {
|
|
3344
|
+
display: flex;
|
|
3345
|
+
flex-wrap: wrap;
|
|
3346
|
+
gap: 0.4rem;
|
|
3347
|
+
align-items: center;
|
|
3348
|
+
}
|
|
3349
|
+
[data-ssml-editor] .ssml-editor-visual-errors {
|
|
3350
|
+
padding: 0.5rem;
|
|
3351
|
+
color: #b91c1c;
|
|
3352
|
+
background: #fef2f2;
|
|
3353
|
+
border: 1px solid #b91c1c;
|
|
3354
|
+
border-radius: 0.25rem;
|
|
3355
|
+
}
|
|
3062
3356
|
`.trim();
|
|
3063
3357
|
function injectStyles() {
|
|
3064
3358
|
if (typeof document === "undefined" || document.getElementById(STYLE_ID)) {
|
|
@@ -3072,6 +3366,55 @@ function injectStyles() {
|
|
|
3072
3366
|
function isDarkTheme(theme) {
|
|
3073
3367
|
return theme === "vs-dark" || theme.toLowerCase().includes("dark");
|
|
3074
3368
|
}
|
|
3369
|
+
function isElement(node) {
|
|
3370
|
+
return typeof node !== "string" && node.type !== "text";
|
|
3371
|
+
}
|
|
3372
|
+
function visualElementName(element) {
|
|
3373
|
+
return element.type === "custom" || element.type === "element" ? element.name : element.type;
|
|
3374
|
+
}
|
|
3375
|
+
function collectVisualTextLeaves(nodes, path = [], ancestors = []) {
|
|
3376
|
+
return nodes.flatMap((node, index) => {
|
|
3377
|
+
const currentPath = [...path, index];
|
|
3378
|
+
if (typeof node === "string") return [{ path: currentPath, value: node, ancestors }];
|
|
3379
|
+
if (node.type === "text") return [{ path: currentPath, value: node.value, ancestors }];
|
|
3380
|
+
return collectVisualTextLeaves(node.children ?? [], currentPath, [...ancestors, visualElementName(node)]);
|
|
3381
|
+
});
|
|
3382
|
+
}
|
|
3383
|
+
function updateVisualNodes(nodes, path, update) {
|
|
3384
|
+
if (path.length === 0) return nodes;
|
|
3385
|
+
const [index, ...rest] = path;
|
|
3386
|
+
return nodes.flatMap((node, nodeIndex) => {
|
|
3387
|
+
if (nodeIndex !== index) return [node];
|
|
3388
|
+
if (rest.length === 0) {
|
|
3389
|
+
const next = update(node);
|
|
3390
|
+
return Array.isArray(next) ? next : [next];
|
|
3391
|
+
}
|
|
3392
|
+
if (!isElement(node)) return [node];
|
|
3393
|
+
return [{ ...node, children: updateVisualNodes(node.children ?? [], rest, update) }];
|
|
3394
|
+
});
|
|
3395
|
+
}
|
|
3396
|
+
function updateVisualText(document2, path, value) {
|
|
3397
|
+
return { ...document2, children: updateVisualNodes(document2.children ?? [], path, () => value) };
|
|
3398
|
+
}
|
|
3399
|
+
function wrapVisualText(document2, path, start, end, type, attributes) {
|
|
3400
|
+
return {
|
|
3401
|
+
...document2,
|
|
3402
|
+
children: updateVisualNodes(document2.children ?? [], path, (node) => {
|
|
3403
|
+
const value = typeof node === "string" ? node : node.type === "text" ? node.value : "";
|
|
3404
|
+
if (!value || start === end) return node;
|
|
3405
|
+
if (type === "break") {
|
|
3406
|
+
return [value.slice(0, start), { type, attributes, children: [] }, value.slice(start)].filter(
|
|
3407
|
+
(part) => typeof part === "string" ? part.length > 0 : true
|
|
3408
|
+
);
|
|
3409
|
+
}
|
|
3410
|
+
const selected = value.slice(start, end);
|
|
3411
|
+
const wrapper = { type, attributes, children: [selected] };
|
|
3412
|
+
return [value.slice(0, start), wrapper, value.slice(end)].filter(
|
|
3413
|
+
(part) => typeof part === "string" ? part.length > 0 : true
|
|
3414
|
+
);
|
|
3415
|
+
})
|
|
3416
|
+
};
|
|
3417
|
+
}
|
|
3075
3418
|
function getMenuPosition(trigger, menu) {
|
|
3076
3419
|
const bounds = trigger.getBoundingClientRect();
|
|
3077
3420
|
const margin = 8;
|
|
@@ -3093,6 +3436,7 @@ var SsmlEditorElement = class extends HTMLElementBase {
|
|
|
3093
3436
|
this.toolbarActions = null;
|
|
3094
3437
|
this.display = null;
|
|
3095
3438
|
this.editorContainer = null;
|
|
3439
|
+
this.visualContainer = null;
|
|
3096
3440
|
this.helpPanel = null;
|
|
3097
3441
|
this.openMenu = null;
|
|
3098
3442
|
this.openMenuTrigger = null;
|
|
@@ -3106,6 +3450,8 @@ var SsmlEditorElement = class extends HTMLElementBase {
|
|
|
3106
3450
|
this.documentState = null;
|
|
3107
3451
|
this.decorationsVisible = false;
|
|
3108
3452
|
this.helpOpen = false;
|
|
3453
|
+
this.visualSelectedPath = null;
|
|
3454
|
+
this.visualSelection = { start: 0, end: 0 };
|
|
3109
3455
|
this.handleDocumentPointerDown = (event) => {
|
|
3110
3456
|
const target = event.target;
|
|
3111
3457
|
if (this.openMenu && target instanceof Node && !this.openMenu.contains(target) && !this.openMenuTrigger?.contains(target)) {
|
|
@@ -3147,6 +3493,12 @@ var SsmlEditorElement = class extends HTMLElementBase {
|
|
|
3147
3493
|
set locale(locale) {
|
|
3148
3494
|
this.setAttribute("locale", locale);
|
|
3149
3495
|
}
|
|
3496
|
+
get editMode() {
|
|
3497
|
+
return this.getAttribute("edit-mode") === "visual" ? "visual" : "code";
|
|
3498
|
+
}
|
|
3499
|
+
set editMode(mode) {
|
|
3500
|
+
this.setAttribute("edit-mode", mode);
|
|
3501
|
+
}
|
|
3150
3502
|
prepareDocument(value) {
|
|
3151
3503
|
try {
|
|
3152
3504
|
this.documentState = (0, import_ssml_core2.parseSsml)(value);
|
|
@@ -3189,6 +3541,7 @@ var SsmlEditorElement = class extends HTMLElementBase {
|
|
|
3189
3541
|
}
|
|
3190
3542
|
}
|
|
3191
3543
|
}
|
|
3544
|
+
this.renderVisualEditor();
|
|
3192
3545
|
return;
|
|
3193
3546
|
}
|
|
3194
3547
|
if (name === "theme" && this.monaco) {
|
|
@@ -3206,6 +3559,9 @@ var SsmlEditorElement = class extends HTMLElementBase {
|
|
|
3206
3559
|
this.renderHelp();
|
|
3207
3560
|
return;
|
|
3208
3561
|
}
|
|
3562
|
+
if (name === "edit-mode") {
|
|
3563
|
+
this.updateEditMode();
|
|
3564
|
+
}
|
|
3209
3565
|
if (name === "show-decorations") {
|
|
3210
3566
|
this.decorationsVisible = newValue !== null;
|
|
3211
3567
|
this.updateDecorations();
|
|
@@ -3230,7 +3586,9 @@ var SsmlEditorElement = class extends HTMLElementBase {
|
|
|
3230
3586
|
display.dataset.ssmlEditorDisplay = "";
|
|
3231
3587
|
const editorContainer = document.createElement("div");
|
|
3232
3588
|
editorContainer.className = "ssml-editor-editor";
|
|
3233
|
-
|
|
3589
|
+
const visualContainer = document.createElement("div");
|
|
3590
|
+
visualContainer.className = "ssml-editor-visual";
|
|
3591
|
+
display.append(editorContainer, visualContainer);
|
|
3234
3592
|
root.append(toolbar, display);
|
|
3235
3593
|
this.replaceChildren(root);
|
|
3236
3594
|
this.root = root;
|
|
@@ -3238,8 +3596,11 @@ var SsmlEditorElement = class extends HTMLElementBase {
|
|
|
3238
3596
|
this.toolbarActions = toolbarActions;
|
|
3239
3597
|
this.display = display;
|
|
3240
3598
|
this.editorContainer = editorContainer;
|
|
3599
|
+
this.visualContainer = visualContainer;
|
|
3241
3600
|
this.renderToolbar();
|
|
3242
3601
|
this.renderHelp();
|
|
3602
|
+
this.updateEditMode();
|
|
3603
|
+
this.renderVisualEditor();
|
|
3243
3604
|
}
|
|
3244
3605
|
renderToolbar() {
|
|
3245
3606
|
const toolbar = this.toolbar;
|
|
@@ -3283,10 +3644,10 @@ var SsmlEditorElement = class extends HTMLElementBase {
|
|
|
3283
3644
|
for (const id of toolbarIds) {
|
|
3284
3645
|
const group = groupByButtonId.get(id);
|
|
3285
3646
|
if (previousGroup !== void 0 && group !== previousGroup) {
|
|
3286
|
-
const
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
toolbarActions.append(
|
|
3647
|
+
const separator2 = document.createElement("span");
|
|
3648
|
+
separator2.className = "ssml-editor-toolbar-separator";
|
|
3649
|
+
separator2.setAttribute("aria-hidden", "true");
|
|
3650
|
+
toolbarActions.append(separator2);
|
|
3290
3651
|
}
|
|
3291
3652
|
previousGroup = group;
|
|
3292
3653
|
const insertion = insertionById.get(id);
|
|
@@ -3298,8 +3659,24 @@ var SsmlEditorElement = class extends HTMLElementBase {
|
|
|
3298
3659
|
toolbarActions.append(this.createActionButton(id));
|
|
3299
3660
|
}
|
|
3300
3661
|
}
|
|
3662
|
+
const separator = document.createElement("span");
|
|
3663
|
+
separator.className = "ssml-editor-toolbar-separator";
|
|
3664
|
+
separator.setAttribute("aria-hidden", "true");
|
|
3665
|
+
toolbarActions.append(separator, this.createModeButton("visual", "Visual"), this.createModeButton("code", "Code"));
|
|
3301
3666
|
this.updateActiveButtons();
|
|
3302
3667
|
}
|
|
3668
|
+
createModeButton(mode, label) {
|
|
3669
|
+
const button = document.createElement("button");
|
|
3670
|
+
button.type = "button";
|
|
3671
|
+
button.className = "ssml-editor-toolbar-button";
|
|
3672
|
+
button.dataset.ssmlEditorButton = `edit-mode-${mode}`;
|
|
3673
|
+
button.setAttribute("aria-pressed", String(this.editMode === mode));
|
|
3674
|
+
button.textContent = label;
|
|
3675
|
+
button.addEventListener("click", () => {
|
|
3676
|
+
this.editMode = mode;
|
|
3677
|
+
});
|
|
3678
|
+
return button;
|
|
3679
|
+
}
|
|
3303
3680
|
createActionButton(id) {
|
|
3304
3681
|
const copy = EDITOR_COPY[this.locale];
|
|
3305
3682
|
const labels = {
|
|
@@ -3679,6 +4056,150 @@ var SsmlEditorElement = class extends HTMLElementBase {
|
|
|
3679
4056
|
this.root.dataset.theme = isDarkTheme(this.theme) ? "dark" : "light";
|
|
3680
4057
|
}
|
|
3681
4058
|
}
|
|
4059
|
+
updateEditMode() {
|
|
4060
|
+
if (this.editorContainer && this.visualContainer) {
|
|
4061
|
+
const visual = this.editMode === "visual";
|
|
4062
|
+
this.editorContainer.hidden = visual;
|
|
4063
|
+
this.visualContainer.hidden = !visual;
|
|
4064
|
+
}
|
|
4065
|
+
for (const mode of ["visual", "code"]) {
|
|
4066
|
+
const button = this.toolbarActions?.querySelector(
|
|
4067
|
+
`[data-ssml-editor-button="edit-mode-${mode}"]`
|
|
4068
|
+
);
|
|
4069
|
+
button?.setAttribute("aria-pressed", String(this.editMode === mode));
|
|
4070
|
+
button?.toggleAttribute("data-active", this.editMode === mode);
|
|
4071
|
+
}
|
|
4072
|
+
this.renderVisualEditor();
|
|
4073
|
+
}
|
|
4074
|
+
renderVisualEditor() {
|
|
4075
|
+
const container = this.visualContainer;
|
|
4076
|
+
if (!container) return;
|
|
4077
|
+
container.replaceChildren();
|
|
4078
|
+
if (!this.documentState) {
|
|
4079
|
+
const error = document.createElement("p");
|
|
4080
|
+
error.className = "ssml-editor-visual-errors";
|
|
4081
|
+
error.textContent = "SSML syntax must be valid before visual editing is available.";
|
|
4082
|
+
container.append(error);
|
|
4083
|
+
return;
|
|
4084
|
+
}
|
|
4085
|
+
const documentState = this.documentState;
|
|
4086
|
+
const leaves = collectVisualTextLeaves(documentState.children ?? []);
|
|
4087
|
+
const selectedLeaf = leaves.find((leaf) => leaf.path.join(".") === this.visualSelectedPath?.join(".")) ?? leaves[0];
|
|
4088
|
+
const breadcrumb = document.createElement("div");
|
|
4089
|
+
breadcrumb.className = "ssml-editor-visual-breadcrumb";
|
|
4090
|
+
breadcrumb.textContent = `<speak>${selectedLeaf ? ` / ${selectedLeaf.ancestors.map((name) => `<${name}>`).join(" / ")}` : ""}`;
|
|
4091
|
+
const clear = document.createElement("button");
|
|
4092
|
+
clear.type = "button";
|
|
4093
|
+
clear.textContent = "Clear parent";
|
|
4094
|
+
clear.disabled = !this.visualSelectedPath;
|
|
4095
|
+
clear.addEventListener("click", () => {
|
|
4096
|
+
this.visualSelectedPath = null;
|
|
4097
|
+
this.renderVisualEditor();
|
|
4098
|
+
});
|
|
4099
|
+
breadcrumb.append(clear);
|
|
4100
|
+
container.append(breadcrumb);
|
|
4101
|
+
const diagnostics = (0, import_ssml_core2.validateAzureSsml)((0, import_ssml_core2.buildSsml)(documentState));
|
|
4102
|
+
if (diagnostics.length > 0) {
|
|
4103
|
+
const errors = document.createElement("div");
|
|
4104
|
+
errors.className = "ssml-editor-visual-errors";
|
|
4105
|
+
errors.setAttribute("role", "alert");
|
|
4106
|
+
for (const diagnostic of diagnostics) {
|
|
4107
|
+
const message = document.createElement("div");
|
|
4108
|
+
message.textContent = diagnostic.message;
|
|
4109
|
+
errors.append(message);
|
|
4110
|
+
}
|
|
4111
|
+
container.append(errors);
|
|
4112
|
+
}
|
|
4113
|
+
const layout = document.createElement("div");
|
|
4114
|
+
layout.className = "ssml-editor-visual-layout";
|
|
4115
|
+
const tree = document.createElement("nav");
|
|
4116
|
+
tree.className = "ssml-editor-visual-tree";
|
|
4117
|
+
tree.setAttribute("aria-label", "SSML structure tree");
|
|
4118
|
+
tree.append(document.createTextNode("Structure"));
|
|
4119
|
+
const treeList = document.createElement("ul");
|
|
4120
|
+
const renderTree = (nodes, parent, parentPath = []) => {
|
|
4121
|
+
nodes.forEach((node, index) => {
|
|
4122
|
+
if (!isElement(node)) return;
|
|
4123
|
+
const path = [...parentPath, index];
|
|
4124
|
+
const item = document.createElement("li");
|
|
4125
|
+
const button = document.createElement("button");
|
|
4126
|
+
button.type = "button";
|
|
4127
|
+
button.textContent = `<${visualElementName(node)}>`;
|
|
4128
|
+
button.dataset.selected = String(path.join(".") === this.visualSelectedPath?.join("."));
|
|
4129
|
+
button.addEventListener("click", () => {
|
|
4130
|
+
this.visualSelectedPath = path;
|
|
4131
|
+
this.renderVisualEditor();
|
|
4132
|
+
});
|
|
4133
|
+
item.append(button);
|
|
4134
|
+
if ((node.children ?? []).some(isElement)) {
|
|
4135
|
+
const childList = document.createElement("ul");
|
|
4136
|
+
renderTree(node.children ?? [], childList, path);
|
|
4137
|
+
item.append(childList);
|
|
4138
|
+
}
|
|
4139
|
+
parent.append(item);
|
|
4140
|
+
});
|
|
4141
|
+
};
|
|
4142
|
+
renderTree(documentState.children ?? [], treeList);
|
|
4143
|
+
tree.append(treeList);
|
|
4144
|
+
layout.append(tree);
|
|
4145
|
+
const form = document.createElement("div");
|
|
4146
|
+
form.className = "ssml-editor-visual-form";
|
|
4147
|
+
if (selectedLeaf) {
|
|
4148
|
+
const label = document.createElement("label");
|
|
4149
|
+
label.append(document.createTextNode("Text"));
|
|
4150
|
+
const textarea = document.createElement("textarea");
|
|
4151
|
+
textarea.value = selectedLeaf.value;
|
|
4152
|
+
textarea.readOnly = this.readonly;
|
|
4153
|
+
textarea.addEventListener("select", () => {
|
|
4154
|
+
this.visualSelection = { start: textarea.selectionStart, end: textarea.selectionEnd };
|
|
4155
|
+
});
|
|
4156
|
+
textarea.addEventListener("input", () => {
|
|
4157
|
+
if (!this.readonly) this.replaceDocument(updateVisualText(documentState, selectedLeaf.path, textarea.value));
|
|
4158
|
+
});
|
|
4159
|
+
label.append(textarea);
|
|
4160
|
+
form.append(label);
|
|
4161
|
+
const actions = document.createElement("div");
|
|
4162
|
+
actions.className = "ssml-editor-visual-actions";
|
|
4163
|
+
const wrappers = [
|
|
4164
|
+
["Rate", "prosody", { rate: "slow" }],
|
|
4165
|
+
["Pitch", "prosody", { pitch: "high" }],
|
|
4166
|
+
["Emotion", "mstts:express-as", { style: "cheerful" }],
|
|
4167
|
+
["Pause", "break", { time: "500ms" }],
|
|
4168
|
+
["Pronunciation", "phoneme", { alphabet: "ipa", ph: selectedLeaf.value }]
|
|
4169
|
+
];
|
|
4170
|
+
for (const [labelText, type, attributes] of wrappers) {
|
|
4171
|
+
const button = document.createElement("button");
|
|
4172
|
+
button.type = "button";
|
|
4173
|
+
button.textContent = labelText;
|
|
4174
|
+
button.disabled = this.readonly;
|
|
4175
|
+
button.addEventListener("click", () => {
|
|
4176
|
+
const start = this.visualSelection.start === this.visualSelection.end ? 0 : this.visualSelection.start;
|
|
4177
|
+
const end = this.visualSelection.start === this.visualSelection.end ? selectedLeaf.value.length : this.visualSelection.end;
|
|
4178
|
+
this.replaceDocument(wrapVisualText(documentState, selectedLeaf.path, start, end, type, attributes));
|
|
4179
|
+
this.renderVisualEditor();
|
|
4180
|
+
});
|
|
4181
|
+
actions.append(button);
|
|
4182
|
+
}
|
|
4183
|
+
const preview = document.createElement("button");
|
|
4184
|
+
preview.type = "button";
|
|
4185
|
+
preview.textContent = "Preview selection";
|
|
4186
|
+
preview.addEventListener("click", () => {
|
|
4187
|
+
this.dispatchEvent(
|
|
4188
|
+
new CustomEvent("preview-selection", {
|
|
4189
|
+
detail: { ssml: (0, import_ssml_core2.buildSsml)({ ...documentState, children: [selectedLeaf.value] }) },
|
|
4190
|
+
bubbles: true,
|
|
4191
|
+
composed: true
|
|
4192
|
+
})
|
|
4193
|
+
);
|
|
4194
|
+
});
|
|
4195
|
+
actions.append(preview);
|
|
4196
|
+
form.append(actions);
|
|
4197
|
+
} else {
|
|
4198
|
+
form.textContent = "Select an element or text node to edit it.";
|
|
4199
|
+
}
|
|
4200
|
+
layout.append(form);
|
|
4201
|
+
container.append(layout);
|
|
4202
|
+
}
|
|
3682
4203
|
updateActiveButtons() {
|
|
3683
4204
|
const editor = this.editor;
|
|
3684
4205
|
const model = this.model;
|
|
@@ -3706,6 +4227,7 @@ var SsmlEditorElement = class extends HTMLElementBase {
|
|
|
3706
4227
|
return;
|
|
3707
4228
|
}
|
|
3708
4229
|
const model = monacoModule.editor.createModel(this.prepareDocument(this.value), "xml");
|
|
4230
|
+
this.renderVisualEditor();
|
|
3709
4231
|
const editor = monacoModule.editor.create(container, {
|
|
3710
4232
|
model,
|
|
3711
4233
|
theme: this.theme,
|
|
@@ -3804,7 +4326,8 @@ SsmlEditorElement.observedAttributes = [
|
|
|
3804
4326
|
"locale",
|
|
3805
4327
|
"show-toolbar",
|
|
3806
4328
|
"show-toolbar-labels",
|
|
3807
|
-
"show-decorations"
|
|
4329
|
+
"show-decorations",
|
|
4330
|
+
"edit-mode"
|
|
3808
4331
|
];
|
|
3809
4332
|
|
|
3810
4333
|
// src/index.ts
|