@pie-players/pie-assessment-toolkit 0.3.50 → 0.3.51
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 +25 -0
- package/dist/components/ItemToolBar.custom-element.js +98 -14
- package/dist/components/PieAssessmentToolkit.custom-element.js +119 -29
- package/dist/components/SectionToolBar.custom-element.js +98 -14
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/services/TTSService.d.ts +4 -0
- package/dist/services/TTSService.js +54 -20
- package/dist/services/TTSService.js.map +1 -1
- package/dist/services/ToolkitCoordinator.d.ts +3 -2
- package/dist/services/ToolkitCoordinator.js.map +1 -1
- package/dist/services/interfaces.d.ts +4 -0
- package/dist/services/interfaces.js.map +1 -1
- package/dist/services/tts-runtime-config.d.ts +15 -1
- package/dist/services/tts-runtime-config.js +76 -10
- package/dist/services/tts-runtime-config.js.map +1 -1
- package/dist/tools/registrations/tts.js +6 -8
- package/dist/tools/registrations/tts.js.map +1 -1
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -616,12 +616,37 @@ tools: {
|
|
|
616
616
|
}
|
|
617
617
|
```
|
|
618
618
|
|
|
619
|
+
Hosts that need semantic UI copy can use object-form options. The `rate` still
|
|
620
|
+
drives playback and provider mapping; `label` / `ariaLabel` only change visible
|
|
621
|
+
and accessible button text.
|
|
622
|
+
|
|
623
|
+
```typescript
|
|
624
|
+
tools: {
|
|
625
|
+
providers: {
|
|
626
|
+
textToSpeech: {
|
|
627
|
+
enabled: true,
|
|
628
|
+
backend: "server",
|
|
629
|
+
serverProvider: "custom",
|
|
630
|
+
settings: {
|
|
631
|
+
speedOptions: [
|
|
632
|
+
{ rate: 0.8, label: "Slow", ariaLabel: "Slow speed" },
|
|
633
|
+
{ rate: 1.5, label: "Fast", ariaLabel: "Fast speed" }
|
|
634
|
+
]
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
```
|
|
640
|
+
|
|
619
641
|
`speedOptions` semantics:
|
|
620
642
|
|
|
621
643
|
- Omitted or non-array: default speed buttons are shown (`0.8x`, `1.25x`).
|
|
622
644
|
- Explicit empty array (`[]`): hide all speed buttons.
|
|
623
645
|
- Invalid-only arrays (for example `["fast", -1, 1]`): fall back to defaults.
|
|
624
646
|
- Valid numeric values are deduplicated and keep first-seen order.
|
|
647
|
+
- Object-form entries use the same numeric validation/deduping by `rate`.
|
|
648
|
+
- Missing labels fall back to numeric text like `1.5x`; missing `ariaLabel`
|
|
649
|
+
falls back to a matching accessible name like `Fast speed`.
|
|
625
650
|
- `1` is excluded (normal speed is already available by toggling active speed off).
|
|
626
651
|
|
|
627
652
|
### Runtime Fallback: Server TTS -> Browser TTS
|
|
@@ -6871,22 +6871,81 @@ var isServerBackend = (backend) => backend === "polly" || backend === "google" |
|
|
|
6871
6871
|
var withDefault = (value, fallback2) => value === undefined ? fallback2 : value;
|
|
6872
6872
|
var normalizeTTSLayoutMode = (value, fallback2 = "left-aligned") => typeof value === "string" && VALID_TTS_LAYOUT_MODES.has(value) ? value : fallback2;
|
|
6873
6873
|
var DEFAULT_TTS_SPEED_OPTIONS = Object.freeze([0.8, 1.25]);
|
|
6874
|
-
var
|
|
6874
|
+
var normalizeSpeedRate = (entry) => {
|
|
6875
|
+
if (typeof entry !== "number" || !Number.isFinite(entry) || entry <= 0) {
|
|
6876
|
+
return;
|
|
6877
|
+
}
|
|
6878
|
+
const rounded = Math.round(entry * 100) / 100;
|
|
6879
|
+
return rounded === 1 ? undefined : rounded;
|
|
6880
|
+
};
|
|
6881
|
+
var trimOptionalText = (value) => {
|
|
6882
|
+
if (typeof value !== "string")
|
|
6883
|
+
return;
|
|
6884
|
+
const trimmed = value.trim();
|
|
6885
|
+
return trimmed.length ? trimmed : undefined;
|
|
6886
|
+
};
|
|
6887
|
+
var formatSpeedLabel = (rate) => `${rate}x`;
|
|
6888
|
+
var formatSpeedAriaLabel = (label, usedDefaultLabel) => usedDefaultLabel ? `Speed ${label}` : `${label} speed`;
|
|
6889
|
+
var normalizeSpeedAriaLabel = (label, ariaLabel, usedDefaultLabel) => {
|
|
6890
|
+
if (!ariaLabel)
|
|
6891
|
+
return formatSpeedAriaLabel(label, usedDefaultLabel);
|
|
6892
|
+
if (ariaLabel.toLowerCase().includes(label.toLowerCase()))
|
|
6893
|
+
return ariaLabel;
|
|
6894
|
+
return `${label} ${ariaLabel}`;
|
|
6895
|
+
};
|
|
6896
|
+
var normalizeTTSSpeedOptionConfig = (entry) => {
|
|
6897
|
+
if (typeof entry === "number")
|
|
6898
|
+
return normalizeSpeedRate(entry);
|
|
6899
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
6900
|
+
return;
|
|
6901
|
+
const record = entry;
|
|
6902
|
+
const rate = normalizeSpeedRate(record.rate);
|
|
6903
|
+
if (rate === undefined)
|
|
6904
|
+
return;
|
|
6905
|
+
const label = trimOptionalText(record.label);
|
|
6906
|
+
const ariaLabel = trimOptionalText(record.ariaLabel);
|
|
6907
|
+
return {
|
|
6908
|
+
rate,
|
|
6909
|
+
...label ? { label } : {},
|
|
6910
|
+
...ariaLabel ? { ariaLabel } : {}
|
|
6911
|
+
};
|
|
6912
|
+
};
|
|
6913
|
+
var normalizeTTSSpeedOptionConfigs = (value) => {
|
|
6875
6914
|
if (!Array.isArray(value))
|
|
6876
6915
|
return [...DEFAULT_TTS_SPEED_OPTIONS];
|
|
6877
6916
|
if (value.length === 0)
|
|
6878
6917
|
return [];
|
|
6879
|
-
const
|
|
6918
|
+
const dedupedRates = new Set;
|
|
6919
|
+
const normalized = [];
|
|
6880
6920
|
for (const entry of value) {
|
|
6881
|
-
|
|
6921
|
+
const option = normalizeTTSSpeedOptionConfig(entry);
|
|
6922
|
+
if (option === undefined)
|
|
6882
6923
|
continue;
|
|
6883
|
-
const
|
|
6884
|
-
if (
|
|
6924
|
+
const rate = typeof option === "number" ? option : option.rate;
|
|
6925
|
+
if (dedupedRates.has(rate))
|
|
6885
6926
|
continue;
|
|
6886
|
-
|
|
6927
|
+
dedupedRates.add(rate);
|
|
6928
|
+
normalized.push(option);
|
|
6887
6929
|
}
|
|
6888
|
-
return
|
|
6930
|
+
return normalized.length ? normalized : [...DEFAULT_TTS_SPEED_OPTIONS];
|
|
6889
6931
|
};
|
|
6932
|
+
var normalizeTTSSpeedControlOptions = (value) => normalizeTTSSpeedOptionConfigs(value).map((option) => {
|
|
6933
|
+
if (typeof option === "number") {
|
|
6934
|
+
const label2 = formatSpeedLabel(option);
|
|
6935
|
+
return {
|
|
6936
|
+
rate: option,
|
|
6937
|
+
label: label2,
|
|
6938
|
+
ariaLabel: formatSpeedAriaLabel(label2, true)
|
|
6939
|
+
};
|
|
6940
|
+
}
|
|
6941
|
+
const defaultLabel = formatSpeedLabel(option.rate);
|
|
6942
|
+
const label = option.label || defaultLabel;
|
|
6943
|
+
return {
|
|
6944
|
+
rate: option.rate,
|
|
6945
|
+
label,
|
|
6946
|
+
ariaLabel: normalizeSpeedAriaLabel(label, option.ariaLabel, label === defaultLabel)
|
|
6947
|
+
};
|
|
6948
|
+
});
|
|
6890
6949
|
var applyRuntimeDefaults = (config) => {
|
|
6891
6950
|
const withLayoutDefaults = {
|
|
6892
6951
|
...config,
|
|
@@ -7043,7 +7102,7 @@ var ttsToolRegistration = {
|
|
|
7043
7102
|
settings.layoutMode = normalizeTTSLayoutMode(settings.layoutMode);
|
|
7044
7103
|
}
|
|
7045
7104
|
if (settings && "speedOptions" in settings) {
|
|
7046
|
-
settings.speedOptions =
|
|
7105
|
+
settings.speedOptions = normalizeTTSSpeedOptionConfigs(settings.speedOptions);
|
|
7047
7106
|
}
|
|
7048
7107
|
const normalizedConfig = {
|
|
7049
7108
|
...config
|
|
@@ -7052,7 +7111,7 @@ var ttsToolRegistration = {
|
|
|
7052
7111
|
normalizedConfig.layoutMode = normalizeTTSLayoutMode(normalizedConfig.layoutMode);
|
|
7053
7112
|
}
|
|
7054
7113
|
if ("speedOptions" in normalizedConfig) {
|
|
7055
|
-
normalizedConfig.speedOptions =
|
|
7114
|
+
normalizedConfig.speedOptions = normalizeTTSSpeedOptionConfigs(normalizedConfig.speedOptions);
|
|
7056
7115
|
}
|
|
7057
7116
|
if (settings) {
|
|
7058
7117
|
normalizedConfig.settings = settings;
|
|
@@ -7075,7 +7134,7 @@ var ttsToolRegistration = {
|
|
|
7075
7134
|
const resolveRuntimeSettings = () => resolveTTSRuntimeSettings(toolbarContext.toolkitCoordinator?.getToolConfig(this.toolId) || undefined);
|
|
7076
7135
|
const resolveElementSpeedOptions = () => {
|
|
7077
7136
|
const runtimeSettings = resolveRuntimeSettings();
|
|
7078
|
-
return
|
|
7137
|
+
return normalizeTTSSpeedControlOptions(runtimeSettings.speedOptions);
|
|
7079
7138
|
};
|
|
7080
7139
|
const resolveLayoutMode = () => resolveTTSLayoutMode(resolveRuntimeSettings());
|
|
7081
7140
|
const resolveHostLayout = () => resolveTTSHostToolbarLayout(resolveRuntimeSettings());
|
|
@@ -9334,13 +9393,34 @@ function ItemToolBar($$anchor, $$props) {
|
|
|
9334
9393
|
const maxHeight = shellConfig?.maxHeight ?? window.innerHeight;
|
|
9335
9394
|
return { minWidth, minHeight, maxWidth, maxHeight };
|
|
9336
9395
|
};
|
|
9396
|
+
const applyContentMinWidth = () => {
|
|
9397
|
+
const configuredMinWidth = currentArgs.mounted.entry.shell?.minWidth;
|
|
9398
|
+
const shouldScroll = typeof configuredMinWidth === "number" && configuredMinWidth > 0 && width < configuredMinWidth;
|
|
9399
|
+
const minWidthValue = shouldScroll ? `${configuredMinWidth}px` : "";
|
|
9400
|
+
if (mountedContentElement) {
|
|
9401
|
+
mountedContentElement.style.minWidth = minWidthValue;
|
|
9402
|
+
}
|
|
9403
|
+
if (headerEl) {
|
|
9404
|
+
headerEl.style.minWidth = minWidthValue;
|
|
9405
|
+
}
|
|
9406
|
+
if (contentEl) {
|
|
9407
|
+
contentEl.style.minWidth = minWidthValue;
|
|
9408
|
+
}
|
|
9409
|
+
if (shellEl) {
|
|
9410
|
+
shellEl.style.overflowX = shouldScroll ? "auto" : "visible";
|
|
9411
|
+
shellEl.style.overflowY = "visible";
|
|
9412
|
+
}
|
|
9413
|
+
};
|
|
9337
9414
|
const applyPositionAndSize = () => {
|
|
9338
9415
|
const { minWidth, minHeight, maxWidth, maxHeight } = getShellBounds();
|
|
9339
|
-
|
|
9340
|
-
|
|
9416
|
+
const effectiveMinWidth = Math.min(minWidth, window.innerWidth);
|
|
9417
|
+
const effectiveMinHeight = Math.min(minHeight, window.innerHeight);
|
|
9418
|
+
width = clamp(width, effectiveMinWidth, Math.max(effectiveMinWidth, Math.min(maxWidth, window.innerWidth)));
|
|
9419
|
+
height = clamp(height, effectiveMinHeight, Math.max(effectiveMinHeight, Math.min(maxHeight, window.innerHeight)));
|
|
9341
9420
|
x2 = clamp(x2, 0, Math.max(0, window.innerWidth - width));
|
|
9342
9421
|
y2 = clamp(y2, 0, Math.max(0, window.innerHeight - height));
|
|
9343
9422
|
applyShellStyle();
|
|
9423
|
+
applyContentMinWidth();
|
|
9344
9424
|
notifyHostedResize();
|
|
9345
9425
|
};
|
|
9346
9426
|
const moveBy = (dx, dy) => {
|
|
@@ -9441,6 +9521,7 @@ function ItemToolBar($$anchor, $$props) {
|
|
|
9441
9521
|
element2.style.height = "100%";
|
|
9442
9522
|
element2.style.flex = "1 1 auto";
|
|
9443
9523
|
element2.style.minHeight = "0";
|
|
9524
|
+
applyContentMinWidth();
|
|
9444
9525
|
if (mountedContentElement && mountedContentElement !== element2) {
|
|
9445
9526
|
if (mountedContentElement.parentNode === contentEl) {
|
|
9446
9527
|
invokeElementUnmount(mountedContentElement);
|
|
@@ -9689,8 +9770,10 @@ function ItemToolBar($$anchor, $$props) {
|
|
|
9689
9770
|
if (resizePointerId === event2.pointerId) {
|
|
9690
9771
|
event2.preventDefault();
|
|
9691
9772
|
const shellConfig = currentArgs.mounted.entry.shell;
|
|
9692
|
-
const
|
|
9693
|
-
const
|
|
9773
|
+
const configuredMinWidth = shellConfig?.minWidth ?? 320;
|
|
9774
|
+
const configuredMinHeight = shellConfig?.minHeight ?? 240;
|
|
9775
|
+
const minWidth = Math.min(configuredMinWidth, window.innerWidth);
|
|
9776
|
+
const minHeight = Math.min(configuredMinHeight, window.innerHeight);
|
|
9694
9777
|
const maxWidth = shellConfig?.maxWidth ?? window.innerWidth;
|
|
9695
9778
|
const maxHeight = shellConfig?.maxHeight ?? window.innerHeight;
|
|
9696
9779
|
const dx = event2.clientX - resizeStartX;
|
|
@@ -9721,6 +9804,7 @@ function ItemToolBar($$anchor, $$props) {
|
|
|
9721
9804
|
height = newHeight;
|
|
9722
9805
|
}
|
|
9723
9806
|
applyShellStyle();
|
|
9807
|
+
applyContentMinWidth();
|
|
9724
9808
|
notifyHostedResize();
|
|
9725
9809
|
}
|
|
9726
9810
|
};
|
|
@@ -18971,22 +18971,81 @@ var isServerBackend = (backend) => backend === "polly" || backend === "google" |
|
|
|
18971
18971
|
var withDefault = (value, fallback2) => value === undefined ? fallback2 : value;
|
|
18972
18972
|
var normalizeTTSLayoutMode = (value, fallback2 = "left-aligned") => typeof value === "string" && VALID_TTS_LAYOUT_MODES.has(value) ? value : fallback2;
|
|
18973
18973
|
var DEFAULT_TTS_SPEED_OPTIONS = Object.freeze([0.8, 1.25]);
|
|
18974
|
-
var
|
|
18974
|
+
var normalizeSpeedRate = (entry) => {
|
|
18975
|
+
if (typeof entry !== "number" || !Number.isFinite(entry) || entry <= 0) {
|
|
18976
|
+
return;
|
|
18977
|
+
}
|
|
18978
|
+
const rounded = Math.round(entry * 100) / 100;
|
|
18979
|
+
return rounded === 1 ? undefined : rounded;
|
|
18980
|
+
};
|
|
18981
|
+
var trimOptionalText = (value) => {
|
|
18982
|
+
if (typeof value !== "string")
|
|
18983
|
+
return;
|
|
18984
|
+
const trimmed = value.trim();
|
|
18985
|
+
return trimmed.length ? trimmed : undefined;
|
|
18986
|
+
};
|
|
18987
|
+
var formatSpeedLabel = (rate) => `${rate}x`;
|
|
18988
|
+
var formatSpeedAriaLabel = (label, usedDefaultLabel) => usedDefaultLabel ? `Speed ${label}` : `${label} speed`;
|
|
18989
|
+
var normalizeSpeedAriaLabel = (label, ariaLabel, usedDefaultLabel) => {
|
|
18990
|
+
if (!ariaLabel)
|
|
18991
|
+
return formatSpeedAriaLabel(label, usedDefaultLabel);
|
|
18992
|
+
if (ariaLabel.toLowerCase().includes(label.toLowerCase()))
|
|
18993
|
+
return ariaLabel;
|
|
18994
|
+
return `${label} ${ariaLabel}`;
|
|
18995
|
+
};
|
|
18996
|
+
var normalizeTTSSpeedOptionConfig = (entry) => {
|
|
18997
|
+
if (typeof entry === "number")
|
|
18998
|
+
return normalizeSpeedRate(entry);
|
|
18999
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
19000
|
+
return;
|
|
19001
|
+
const record = entry;
|
|
19002
|
+
const rate = normalizeSpeedRate(record.rate);
|
|
19003
|
+
if (rate === undefined)
|
|
19004
|
+
return;
|
|
19005
|
+
const label = trimOptionalText(record.label);
|
|
19006
|
+
const ariaLabel = trimOptionalText(record.ariaLabel);
|
|
19007
|
+
return {
|
|
19008
|
+
rate,
|
|
19009
|
+
...label ? { label } : {},
|
|
19010
|
+
...ariaLabel ? { ariaLabel } : {}
|
|
19011
|
+
};
|
|
19012
|
+
};
|
|
19013
|
+
var normalizeTTSSpeedOptionConfigs = (value) => {
|
|
18975
19014
|
if (!Array.isArray(value))
|
|
18976
19015
|
return [...DEFAULT_TTS_SPEED_OPTIONS];
|
|
18977
19016
|
if (value.length === 0)
|
|
18978
19017
|
return [];
|
|
18979
|
-
const
|
|
19018
|
+
const dedupedRates = new Set;
|
|
19019
|
+
const normalized = [];
|
|
18980
19020
|
for (const entry of value) {
|
|
18981
|
-
|
|
19021
|
+
const option = normalizeTTSSpeedOptionConfig(entry);
|
|
19022
|
+
if (option === undefined)
|
|
18982
19023
|
continue;
|
|
18983
|
-
const
|
|
18984
|
-
if (
|
|
19024
|
+
const rate = typeof option === "number" ? option : option.rate;
|
|
19025
|
+
if (dedupedRates.has(rate))
|
|
18985
19026
|
continue;
|
|
18986
|
-
|
|
19027
|
+
dedupedRates.add(rate);
|
|
19028
|
+
normalized.push(option);
|
|
18987
19029
|
}
|
|
18988
|
-
return
|
|
19030
|
+
return normalized.length ? normalized : [...DEFAULT_TTS_SPEED_OPTIONS];
|
|
18989
19031
|
};
|
|
19032
|
+
var normalizeTTSSpeedControlOptions = (value) => normalizeTTSSpeedOptionConfigs(value).map((option) => {
|
|
19033
|
+
if (typeof option === "number") {
|
|
19034
|
+
const label2 = formatSpeedLabel(option);
|
|
19035
|
+
return {
|
|
19036
|
+
rate: option,
|
|
19037
|
+
label: label2,
|
|
19038
|
+
ariaLabel: formatSpeedAriaLabel(label2, true)
|
|
19039
|
+
};
|
|
19040
|
+
}
|
|
19041
|
+
const defaultLabel = formatSpeedLabel(option.rate);
|
|
19042
|
+
const label = option.label || defaultLabel;
|
|
19043
|
+
return {
|
|
19044
|
+
rate: option.rate,
|
|
19045
|
+
label,
|
|
19046
|
+
ariaLabel: normalizeSpeedAriaLabel(label, option.ariaLabel, label === defaultLabel)
|
|
19047
|
+
};
|
|
19048
|
+
});
|
|
18990
19049
|
var applyRuntimeDefaults = (config) => {
|
|
18991
19050
|
const withLayoutDefaults = {
|
|
18992
19051
|
...config,
|
|
@@ -19143,7 +19202,7 @@ var ttsToolRegistration = {
|
|
|
19143
19202
|
settings.layoutMode = normalizeTTSLayoutMode(settings.layoutMode);
|
|
19144
19203
|
}
|
|
19145
19204
|
if (settings && "speedOptions" in settings) {
|
|
19146
|
-
settings.speedOptions =
|
|
19205
|
+
settings.speedOptions = normalizeTTSSpeedOptionConfigs(settings.speedOptions);
|
|
19147
19206
|
}
|
|
19148
19207
|
const normalizedConfig = {
|
|
19149
19208
|
...config
|
|
@@ -19152,7 +19211,7 @@ var ttsToolRegistration = {
|
|
|
19152
19211
|
normalizedConfig.layoutMode = normalizeTTSLayoutMode(normalizedConfig.layoutMode);
|
|
19153
19212
|
}
|
|
19154
19213
|
if ("speedOptions" in normalizedConfig) {
|
|
19155
|
-
normalizedConfig.speedOptions =
|
|
19214
|
+
normalizedConfig.speedOptions = normalizeTTSSpeedOptionConfigs(normalizedConfig.speedOptions);
|
|
19156
19215
|
}
|
|
19157
19216
|
if (settings) {
|
|
19158
19217
|
normalizedConfig.settings = settings;
|
|
@@ -19175,7 +19234,7 @@ var ttsToolRegistration = {
|
|
|
19175
19234
|
const resolveRuntimeSettings = () => resolveTTSRuntimeSettings(toolbarContext.toolkitCoordinator?.getToolConfig(this.toolId) || undefined);
|
|
19176
19235
|
const resolveElementSpeedOptions = () => {
|
|
19177
19236
|
const runtimeSettings = resolveRuntimeSettings();
|
|
19178
|
-
return
|
|
19237
|
+
return normalizeTTSSpeedControlOptions(runtimeSettings.speedOptions);
|
|
19179
19238
|
};
|
|
19180
19239
|
const resolveLayoutMode = () => resolveTTSLayoutMode(resolveRuntimeSettings());
|
|
19181
19240
|
const resolveHostLayout = () => resolveTTSHostToolbarLayout(resolveRuntimeSettings());
|
|
@@ -23839,6 +23898,7 @@ class TTSService {
|
|
|
23839
23898
|
lastError = null;
|
|
23840
23899
|
speakRunId = 0;
|
|
23841
23900
|
currentBoundaryOffset = 0;
|
|
23901
|
+
activeWordBoundaryOffset = 0;
|
|
23842
23902
|
seekSegments = [];
|
|
23843
23903
|
sentenceHighlightSegments = [];
|
|
23844
23904
|
currentSeekSegmentIndex = 0;
|
|
@@ -24232,11 +24292,11 @@ class TTSService {
|
|
|
24232
24292
|
this.currentBoundaryOffset = 0;
|
|
24233
24293
|
const originalOnWordBoundary = this.provider.onWordBoundary;
|
|
24234
24294
|
this.provider.onWordBoundary = (word, position, length) => {
|
|
24295
|
+
originalOnWordBoundary?.(word, position, length);
|
|
24235
24296
|
if (Number.isFinite(position)) {
|
|
24236
24297
|
this.currentBoundaryOffset = position;
|
|
24237
24298
|
this.currentSeekSegmentIndex = this.getCurrentSeekSegmentIndex();
|
|
24238
24299
|
}
|
|
24239
|
-
originalOnWordBoundary?.(word, position, length);
|
|
24240
24300
|
};
|
|
24241
24301
|
try {
|
|
24242
24302
|
await providerWithPlan.speakSegments(segments);
|
|
@@ -24247,6 +24307,10 @@ class TTSService {
|
|
|
24247
24307
|
}
|
|
24248
24308
|
return;
|
|
24249
24309
|
}
|
|
24310
|
+
this.configureWordBoundaryHighlighting({
|
|
24311
|
+
highlightMode: options?.highlightMode || "word",
|
|
24312
|
+
wordBoundaryOffset: this.activeWordBoundaryOffset
|
|
24313
|
+
});
|
|
24250
24314
|
for (const segment of segments) {
|
|
24251
24315
|
if (runId !== this.speakRunId)
|
|
24252
24316
|
return;
|
|
@@ -24366,6 +24430,7 @@ class TTSService {
|
|
|
24366
24430
|
this.initializeSpeakTracking(highlightText, options);
|
|
24367
24431
|
const highlightMode = options?.highlightModeOverride || (speechMatchesVisibleText ? this.resolveHighlightMode() : "sentence");
|
|
24368
24432
|
this.activeHighlightMode = highlightMode;
|
|
24433
|
+
this.activeWordBoundaryOffset = options?.wordBoundaryOffset || 0;
|
|
24369
24434
|
const hasExplicitBreaks = this.hasExplicitBreakSemantics(contentToSpeak);
|
|
24370
24435
|
const shouldUsePlan = !!this.currentContentElement && !usedCatalogSpoken && !hasExplicitBreaks && speechMatchesVisibleText;
|
|
24371
24436
|
this.seekSegments = hasExplicitBreaks || !speechMatchesVisibleText ? [] : shouldUsePlan && this.currentContentElement ? this.createSpeechPlan(this.currentContentElement, normalizedText) : this.createSeekSegmentsFromText(highlightText);
|
|
@@ -24951,6 +25016,7 @@ class TTSService {
|
|
|
24951
25016
|
this.currentContentElement = null;
|
|
24952
25017
|
this.normalizedToDOM.clear();
|
|
24953
25018
|
this.currentBoundaryOffset = 0;
|
|
25019
|
+
this.activeWordBoundaryOffset = 0;
|
|
24954
25020
|
this.seekSegments = [];
|
|
24955
25021
|
this.sentenceHighlightSegments = [];
|
|
24956
25022
|
this.currentSeekSegmentIndex = 0;
|
|
@@ -25005,31 +25071,27 @@ class TTSService {
|
|
|
25005
25071
|
this.setState(PlaybackState.PLAYING);
|
|
25006
25072
|
}
|
|
25007
25073
|
}
|
|
25008
|
-
|
|
25009
|
-
if (!
|
|
25010
|
-
return;
|
|
25011
|
-
|
|
25012
|
-
|
|
25013
|
-
|
|
25014
|
-
if (this.
|
|
25015
|
-
return;
|
|
25016
|
-
if (this.seekSegments.length === 0)
|
|
25017
|
-
return;
|
|
25018
|
-
const delta = Number.isFinite(units) ? Math.trunc(units) : 0;
|
|
25019
|
-
if (delta === 0)
|
|
25020
|
-
return;
|
|
25021
|
-
const currentIndex = this.getCurrentSeekSegmentIndex();
|
|
25022
|
-
const targetIndex = Math.max(0, Math.min(this.seekSegments.length - 1, currentIndex + delta));
|
|
25023
|
-
if (targetIndex === currentIndex)
|
|
25074
|
+
normalizePlaybackRate(rate) {
|
|
25075
|
+
if (!Number.isFinite(rate))
|
|
25076
|
+
return 1;
|
|
25077
|
+
return Math.max(0.25, Math.min(4, rate));
|
|
25078
|
+
}
|
|
25079
|
+
async restartFromSeekIndex(targetIndex) {
|
|
25080
|
+
if (!this.provider || this.seekSegments.length === 0)
|
|
25024
25081
|
return;
|
|
25082
|
+
const safeTargetIndex = Math.max(0, Math.min(this.seekSegments.length - 1, targetIndex));
|
|
25025
25083
|
this.speakRunId += 1;
|
|
25026
25084
|
this.provider.onWordBoundary = undefined;
|
|
25027
25085
|
this.provider.stop();
|
|
25028
|
-
this.currentSeekSegmentIndex =
|
|
25086
|
+
this.currentSeekSegmentIndex = safeTargetIndex;
|
|
25029
25087
|
const runId = ++this.speakRunId;
|
|
25030
|
-
const restartSegments = this.seekSegments.slice(
|
|
25088
|
+
const restartSegments = this.seekSegments.slice(safeTargetIndex);
|
|
25031
25089
|
this.setState(PlaybackState.PLAYING);
|
|
25032
25090
|
try {
|
|
25091
|
+
this.configureWordBoundaryHighlighting({
|
|
25092
|
+
highlightMode: this.activeHighlightMode,
|
|
25093
|
+
wordBoundaryOffset: this.activeWordBoundaryOffset
|
|
25094
|
+
});
|
|
25033
25095
|
await this.speakWithPlan(restartSegments, runId, {
|
|
25034
25096
|
highlightMode: this.activeHighlightMode
|
|
25035
25097
|
});
|
|
@@ -25046,6 +25108,33 @@ class TTSService {
|
|
|
25046
25108
|
throw error;
|
|
25047
25109
|
}
|
|
25048
25110
|
}
|
|
25111
|
+
async setPlaybackRate(rate) {
|
|
25112
|
+
const nextRate = this.normalizePlaybackRate(rate);
|
|
25113
|
+
await this.updateSettings({ rate: nextRate });
|
|
25114
|
+
if (this.state !== PlaybackState.PLAYING || !this.currentText || this.seekSegments.length === 0 || this.hasExplicitBreakSemantics(this.currentText)) {
|
|
25115
|
+
return;
|
|
25116
|
+
}
|
|
25117
|
+
await this.restartFromSeekIndex(this.getCurrentSeekSegmentIndex());
|
|
25118
|
+
}
|
|
25119
|
+
async seekBy(units) {
|
|
25120
|
+
if (!this.provider || !this.currentText)
|
|
25121
|
+
return;
|
|
25122
|
+
if (this.state !== PlaybackState.PLAYING && this.state !== PlaybackState.PAUSED) {
|
|
25123
|
+
return;
|
|
25124
|
+
}
|
|
25125
|
+
if (this.hasExplicitBreakSemantics(this.currentText))
|
|
25126
|
+
return;
|
|
25127
|
+
if (this.seekSegments.length === 0)
|
|
25128
|
+
return;
|
|
25129
|
+
const delta = Number.isFinite(units) ? Math.trunc(units) : 0;
|
|
25130
|
+
if (delta === 0)
|
|
25131
|
+
return;
|
|
25132
|
+
const currentIndex = this.getCurrentSeekSegmentIndex();
|
|
25133
|
+
const targetIndex = Math.max(0, Math.min(this.seekSegments.length - 1, currentIndex + delta));
|
|
25134
|
+
if (targetIndex === currentIndex)
|
|
25135
|
+
return;
|
|
25136
|
+
await this.restartFromSeekIndex(targetIndex);
|
|
25137
|
+
}
|
|
25049
25138
|
async seekForward(units = 1) {
|
|
25050
25139
|
const step = Number.isFinite(units) ? Math.max(1, Math.trunc(units)) : 1;
|
|
25051
25140
|
await this.seekBy(step);
|
|
@@ -25069,6 +25158,7 @@ class TTSService {
|
|
|
25069
25158
|
this.currentContentElement = null;
|
|
25070
25159
|
this.normalizedToDOM.clear();
|
|
25071
25160
|
this.currentBoundaryOffset = 0;
|
|
25161
|
+
this.activeWordBoundaryOffset = 0;
|
|
25072
25162
|
this.seekSegments = [];
|
|
25073
25163
|
this.sentenceHighlightSegments = [];
|
|
25074
25164
|
this.currentSeekSegmentIndex = 0;
|
|
@@ -11054,22 +11054,81 @@ var isServerBackend = (backend) => backend === "polly" || backend === "google" |
|
|
|
11054
11054
|
var withDefault = (value, fallback2) => value === undefined ? fallback2 : value;
|
|
11055
11055
|
var normalizeTTSLayoutMode = (value, fallback2 = "left-aligned") => typeof value === "string" && VALID_TTS_LAYOUT_MODES.has(value) ? value : fallback2;
|
|
11056
11056
|
var DEFAULT_TTS_SPEED_OPTIONS = Object.freeze([0.8, 1.25]);
|
|
11057
|
-
var
|
|
11057
|
+
var normalizeSpeedRate = (entry) => {
|
|
11058
|
+
if (typeof entry !== "number" || !Number.isFinite(entry) || entry <= 0) {
|
|
11059
|
+
return;
|
|
11060
|
+
}
|
|
11061
|
+
const rounded = Math.round(entry * 100) / 100;
|
|
11062
|
+
return rounded === 1 ? undefined : rounded;
|
|
11063
|
+
};
|
|
11064
|
+
var trimOptionalText = (value) => {
|
|
11065
|
+
if (typeof value !== "string")
|
|
11066
|
+
return;
|
|
11067
|
+
const trimmed = value.trim();
|
|
11068
|
+
return trimmed.length ? trimmed : undefined;
|
|
11069
|
+
};
|
|
11070
|
+
var formatSpeedLabel = (rate) => `${rate}x`;
|
|
11071
|
+
var formatSpeedAriaLabel = (label, usedDefaultLabel) => usedDefaultLabel ? `Speed ${label}` : `${label} speed`;
|
|
11072
|
+
var normalizeSpeedAriaLabel = (label, ariaLabel, usedDefaultLabel) => {
|
|
11073
|
+
if (!ariaLabel)
|
|
11074
|
+
return formatSpeedAriaLabel(label, usedDefaultLabel);
|
|
11075
|
+
if (ariaLabel.toLowerCase().includes(label.toLowerCase()))
|
|
11076
|
+
return ariaLabel;
|
|
11077
|
+
return `${label} ${ariaLabel}`;
|
|
11078
|
+
};
|
|
11079
|
+
var normalizeTTSSpeedOptionConfig = (entry) => {
|
|
11080
|
+
if (typeof entry === "number")
|
|
11081
|
+
return normalizeSpeedRate(entry);
|
|
11082
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
11083
|
+
return;
|
|
11084
|
+
const record = entry;
|
|
11085
|
+
const rate = normalizeSpeedRate(record.rate);
|
|
11086
|
+
if (rate === undefined)
|
|
11087
|
+
return;
|
|
11088
|
+
const label = trimOptionalText(record.label);
|
|
11089
|
+
const ariaLabel = trimOptionalText(record.ariaLabel);
|
|
11090
|
+
return {
|
|
11091
|
+
rate,
|
|
11092
|
+
...label ? { label } : {},
|
|
11093
|
+
...ariaLabel ? { ariaLabel } : {}
|
|
11094
|
+
};
|
|
11095
|
+
};
|
|
11096
|
+
var normalizeTTSSpeedOptionConfigs = (value) => {
|
|
11058
11097
|
if (!Array.isArray(value))
|
|
11059
11098
|
return [...DEFAULT_TTS_SPEED_OPTIONS];
|
|
11060
11099
|
if (value.length === 0)
|
|
11061
11100
|
return [];
|
|
11062
|
-
const
|
|
11101
|
+
const dedupedRates = new Set;
|
|
11102
|
+
const normalized = [];
|
|
11063
11103
|
for (const entry of value) {
|
|
11064
|
-
|
|
11104
|
+
const option = normalizeTTSSpeedOptionConfig(entry);
|
|
11105
|
+
if (option === undefined)
|
|
11065
11106
|
continue;
|
|
11066
|
-
const
|
|
11067
|
-
if (
|
|
11107
|
+
const rate = typeof option === "number" ? option : option.rate;
|
|
11108
|
+
if (dedupedRates.has(rate))
|
|
11068
11109
|
continue;
|
|
11069
|
-
|
|
11110
|
+
dedupedRates.add(rate);
|
|
11111
|
+
normalized.push(option);
|
|
11070
11112
|
}
|
|
11071
|
-
return
|
|
11113
|
+
return normalized.length ? normalized : [...DEFAULT_TTS_SPEED_OPTIONS];
|
|
11072
11114
|
};
|
|
11115
|
+
var normalizeTTSSpeedControlOptions = (value) => normalizeTTSSpeedOptionConfigs(value).map((option) => {
|
|
11116
|
+
if (typeof option === "number") {
|
|
11117
|
+
const label2 = formatSpeedLabel(option);
|
|
11118
|
+
return {
|
|
11119
|
+
rate: option,
|
|
11120
|
+
label: label2,
|
|
11121
|
+
ariaLabel: formatSpeedAriaLabel(label2, true)
|
|
11122
|
+
};
|
|
11123
|
+
}
|
|
11124
|
+
const defaultLabel = formatSpeedLabel(option.rate);
|
|
11125
|
+
const label = option.label || defaultLabel;
|
|
11126
|
+
return {
|
|
11127
|
+
rate: option.rate,
|
|
11128
|
+
label,
|
|
11129
|
+
ariaLabel: normalizeSpeedAriaLabel(label, option.ariaLabel, label === defaultLabel)
|
|
11130
|
+
};
|
|
11131
|
+
});
|
|
11073
11132
|
var applyRuntimeDefaults = (config) => {
|
|
11074
11133
|
const withLayoutDefaults = {
|
|
11075
11134
|
...config,
|
|
@@ -11224,7 +11283,7 @@ var ttsToolRegistration = {
|
|
|
11224
11283
|
settings.layoutMode = normalizeTTSLayoutMode(settings.layoutMode);
|
|
11225
11284
|
}
|
|
11226
11285
|
if (settings && "speedOptions" in settings) {
|
|
11227
|
-
settings.speedOptions =
|
|
11286
|
+
settings.speedOptions = normalizeTTSSpeedOptionConfigs(settings.speedOptions);
|
|
11228
11287
|
}
|
|
11229
11288
|
const normalizedConfig = {
|
|
11230
11289
|
...config
|
|
@@ -11233,7 +11292,7 @@ var ttsToolRegistration = {
|
|
|
11233
11292
|
normalizedConfig.layoutMode = normalizeTTSLayoutMode(normalizedConfig.layoutMode);
|
|
11234
11293
|
}
|
|
11235
11294
|
if ("speedOptions" in normalizedConfig) {
|
|
11236
|
-
normalizedConfig.speedOptions =
|
|
11295
|
+
normalizedConfig.speedOptions = normalizeTTSSpeedOptionConfigs(normalizedConfig.speedOptions);
|
|
11237
11296
|
}
|
|
11238
11297
|
if (settings) {
|
|
11239
11298
|
normalizedConfig.settings = settings;
|
|
@@ -11256,7 +11315,7 @@ var ttsToolRegistration = {
|
|
|
11256
11315
|
const resolveRuntimeSettings = () => resolveTTSRuntimeSettings(toolbarContext.toolkitCoordinator?.getToolConfig(this.toolId) || undefined);
|
|
11257
11316
|
const resolveElementSpeedOptions = () => {
|
|
11258
11317
|
const runtimeSettings = resolveRuntimeSettings();
|
|
11259
|
-
return
|
|
11318
|
+
return normalizeTTSSpeedControlOptions(runtimeSettings.speedOptions);
|
|
11260
11319
|
};
|
|
11261
11320
|
const resolveLayoutMode = () => resolveTTSLayoutMode(resolveRuntimeSettings());
|
|
11262
11321
|
const resolveHostLayout = () => resolveTTSHostToolbarLayout(resolveRuntimeSettings());
|
|
@@ -13499,13 +13558,34 @@ function ItemToolBar($$anchor, $$props) {
|
|
|
13499
13558
|
const maxHeight = shellConfig?.maxHeight ?? window.innerHeight;
|
|
13500
13559
|
return { minWidth, minHeight, maxWidth, maxHeight };
|
|
13501
13560
|
};
|
|
13561
|
+
const applyContentMinWidth = () => {
|
|
13562
|
+
const configuredMinWidth = currentArgs.mounted.entry.shell?.minWidth;
|
|
13563
|
+
const shouldScroll = typeof configuredMinWidth === "number" && configuredMinWidth > 0 && width < configuredMinWidth;
|
|
13564
|
+
const minWidthValue = shouldScroll ? `${configuredMinWidth}px` : "";
|
|
13565
|
+
if (mountedContentElement) {
|
|
13566
|
+
mountedContentElement.style.minWidth = minWidthValue;
|
|
13567
|
+
}
|
|
13568
|
+
if (headerEl) {
|
|
13569
|
+
headerEl.style.minWidth = minWidthValue;
|
|
13570
|
+
}
|
|
13571
|
+
if (contentEl) {
|
|
13572
|
+
contentEl.style.minWidth = minWidthValue;
|
|
13573
|
+
}
|
|
13574
|
+
if (shellEl) {
|
|
13575
|
+
shellEl.style.overflowX = shouldScroll ? "auto" : "visible";
|
|
13576
|
+
shellEl.style.overflowY = "visible";
|
|
13577
|
+
}
|
|
13578
|
+
};
|
|
13502
13579
|
const applyPositionAndSize = () => {
|
|
13503
13580
|
const { minWidth, minHeight, maxWidth, maxHeight } = getShellBounds();
|
|
13504
|
-
|
|
13505
|
-
|
|
13581
|
+
const effectiveMinWidth = Math.min(minWidth, window.innerWidth);
|
|
13582
|
+
const effectiveMinHeight = Math.min(minHeight, window.innerHeight);
|
|
13583
|
+
width = clamp(width, effectiveMinWidth, Math.max(effectiveMinWidth, Math.min(maxWidth, window.innerWidth)));
|
|
13584
|
+
height = clamp(height, effectiveMinHeight, Math.max(effectiveMinHeight, Math.min(maxHeight, window.innerHeight)));
|
|
13506
13585
|
x2 = clamp(x2, 0, Math.max(0, window.innerWidth - width));
|
|
13507
13586
|
y2 = clamp(y2, 0, Math.max(0, window.innerHeight - height));
|
|
13508
13587
|
applyShellStyle();
|
|
13588
|
+
applyContentMinWidth();
|
|
13509
13589
|
notifyHostedResize();
|
|
13510
13590
|
};
|
|
13511
13591
|
const moveBy = (dx, dy) => {
|
|
@@ -13606,6 +13686,7 @@ function ItemToolBar($$anchor, $$props) {
|
|
|
13606
13686
|
element2.style.height = "100%";
|
|
13607
13687
|
element2.style.flex = "1 1 auto";
|
|
13608
13688
|
element2.style.minHeight = "0";
|
|
13689
|
+
applyContentMinWidth();
|
|
13609
13690
|
if (mountedContentElement && mountedContentElement !== element2) {
|
|
13610
13691
|
if (mountedContentElement.parentNode === contentEl) {
|
|
13611
13692
|
invokeElementUnmount(mountedContentElement);
|
|
@@ -13854,8 +13935,10 @@ function ItemToolBar($$anchor, $$props) {
|
|
|
13854
13935
|
if (resizePointerId === event2.pointerId) {
|
|
13855
13936
|
event2.preventDefault();
|
|
13856
13937
|
const shellConfig = currentArgs.mounted.entry.shell;
|
|
13857
|
-
const
|
|
13858
|
-
const
|
|
13938
|
+
const configuredMinWidth = shellConfig?.minWidth ?? 320;
|
|
13939
|
+
const configuredMinHeight = shellConfig?.minHeight ?? 240;
|
|
13940
|
+
const minWidth = Math.min(configuredMinWidth, window.innerWidth);
|
|
13941
|
+
const minHeight = Math.min(configuredMinHeight, window.innerHeight);
|
|
13859
13942
|
const maxWidth = shellConfig?.maxWidth ?? window.innerWidth;
|
|
13860
13943
|
const maxHeight = shellConfig?.maxHeight ?? window.innerHeight;
|
|
13861
13944
|
const dx = event2.clientX - resizeStartX;
|
|
@@ -13886,6 +13969,7 @@ function ItemToolBar($$anchor, $$props) {
|
|
|
13886
13969
|
height = newHeight;
|
|
13887
13970
|
}
|
|
13888
13971
|
applyShellStyle();
|
|
13972
|
+
applyContentMinWidth();
|
|
13889
13973
|
notifyHostedResize();
|
|
13890
13974
|
}
|
|
13891
13975
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -56,8 +56,8 @@ export { PlaybackState, TTSService } from "./services/TTSService.js";
|
|
|
56
56
|
export { PIE_TTS_CONTROL_HANDOFF_EVENT, type TTSControlHandoffDetail, } from "./services/tts-control-events.js";
|
|
57
57
|
export { BrowserTTSProvider } from "./services/tts/browser-provider.js";
|
|
58
58
|
export type { SREMathSpeechOptions } from "./services/tts/math-speech.js";
|
|
59
|
-
export type { TTSHostToolbarLayout, TTSLayoutMode, TTSRuntimeSettings, } from "./services/tts-runtime-config.js";
|
|
60
|
-
export { DEFAULT_TTS_SPEED_OPTIONS, formatTTSSpeedOptionsAsText, normalizeTTSLayoutMode, normalizeTTSSpeedOptions, parseTTSSpeedOptionsFromText, resolveTTSHostToolbarLayout, resolveTTSLayoutMode, resolveTTSRuntimeSettings, } from "./services/tts-runtime-config.js";
|
|
59
|
+
export type { NormalizedTTSSpeedOption, TTSHostToolbarLayout, TTSLayoutMode, TTSRuntimeSettings, TTSSpeedOption, TTSSpeedOptionConfig, } from "./services/tts-runtime-config.js";
|
|
60
|
+
export { DEFAULT_TTS_SPEED_OPTIONS, formatTTSSpeedOptionsAsText, normalizeTTSLayoutMode, normalizeTTSSpeedControlOptions, normalizeTTSSpeedOptionConfigs, normalizeTTSSpeedOptions, parseTTSSpeedOptionsFromText, resolveTTSHostToolbarLayout, resolveTTSLayoutMode, resolveTTSRuntimeSettings, } from "./services/tts-runtime-config.js";
|
|
61
61
|
export type { ITTSProvider, ITTSProviderImplementation, TTSSpeechSegment, TTSFeature, TTSProviderCapabilities, } from "@pie-players/pie-tts";
|
|
62
62
|
export type { CreateLoadItemOptions, LoadItem, LoadItemOptions, } from "./item-loader.js";
|
|
63
63
|
export { createFetchItemLoader, createLoadItem, ItemLoadError, } from "./item-loader.js";
|
package/dist/index.js
CHANGED
|
@@ -39,7 +39,7 @@ export { createScopedToolId, parseScopedToolId, toOverlayToolId, } from "./servi
|
|
|
39
39
|
export { PlaybackState, TTSService } from "./services/TTSService.js";
|
|
40
40
|
export { PIE_TTS_CONTROL_HANDOFF_EVENT, } from "./services/tts-control-events.js";
|
|
41
41
|
export { BrowserTTSProvider } from "./services/tts/browser-provider.js";
|
|
42
|
-
export { DEFAULT_TTS_SPEED_OPTIONS, formatTTSSpeedOptionsAsText, normalizeTTSLayoutMode, normalizeTTSSpeedOptions, parseTTSSpeedOptionsFromText, resolveTTSHostToolbarLayout, resolveTTSLayoutMode, resolveTTSRuntimeSettings, } from "./services/tts-runtime-config.js";
|
|
42
|
+
export { DEFAULT_TTS_SPEED_OPTIONS, formatTTSSpeedOptionsAsText, normalizeTTSLayoutMode, normalizeTTSSpeedControlOptions, normalizeTTSSpeedOptionConfigs, normalizeTTSSpeedOptions, parseTTSSpeedOptionsFromText, resolveTTSHostToolbarLayout, resolveTTSLayoutMode, resolveTTSRuntimeSettings, } from "./services/tts-runtime-config.js";
|
|
43
43
|
export { createFetchItemLoader, createLoadItem, ItemLoadError, } from "./item-loader.js";
|
|
44
44
|
export { createMemoryStorage, createTestAttemptSessionIdentifier, createNewTestAttemptSession, getBrowserLocalStorage, getOrCreateAnonymousDeviceId, getTestAttemptSessionStorageKey, loadTestAttemptSession, saveTestAttemptSession, setCurrentPosition, upsertItemSessionFromPieSessionChange, upsertVisitedItem, } from "./attempt/TestSession.js";
|
|
45
45
|
export { createNewAssessmentSession, getAssessmentSessionStorageKey, loadAssessmentSession, saveAssessmentSession, setCurrentSectionPosition, upsertSectionSession, } from "./attempt/AssessmentSession.js";
|