@vortexm/vjt 0.1.13 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +390 -28
- package/dist/lib/action-runtime.d.ts +6 -0
- package/dist/lib/references.d.ts +2 -0
- package/dist/lib/voice-runtime.d.ts +17 -0
- package/dist/lib/widgets/button.d.ts +1 -0
- package/dist/lib/widgets/grid-view.d.ts +1 -0
- package/dist/lib/widgets/list.d.ts +1 -0
- package/package.json +1 -1
- package/vjt-styles.css +58 -0
package/dist/index.js
CHANGED
|
@@ -4220,6 +4220,20 @@ function stringifyReferenceValue(value) {
|
|
|
4220
4220
|
}
|
|
4221
4221
|
return JSON.stringify(value);
|
|
4222
4222
|
}
|
|
4223
|
+
function resolveInlineI18nValue(host, value) {
|
|
4224
|
+
if (typeof value !== "string") {
|
|
4225
|
+
return value;
|
|
4226
|
+
}
|
|
4227
|
+
if (value.startsWith("i18n:") && !value.slice(5).includes(" ")) {
|
|
4228
|
+
return host.resolveI18nValue(value);
|
|
4229
|
+
}
|
|
4230
|
+
if (!value.includes("i18n:")) {
|
|
4231
|
+
return value;
|
|
4232
|
+
}
|
|
4233
|
+
return value.replaceAll(/i18n:([A-Za-z0-9_.-]+)/g, (_match, key) => {
|
|
4234
|
+
return host.resolveI18nValue(`i18n:${key}`);
|
|
4235
|
+
});
|
|
4236
|
+
}
|
|
4223
4237
|
function isReferenceValueEmpty(value) {
|
|
4224
4238
|
if (value === null || value === void 0) {
|
|
4225
4239
|
return true;
|
|
@@ -4274,6 +4288,10 @@ var ReferenceRuntime = class {
|
|
|
4274
4288
|
constructor(host) {
|
|
4275
4289
|
this.host = host;
|
|
4276
4290
|
}
|
|
4291
|
+
getListSelectionIndex(listId) {
|
|
4292
|
+
const listState = this.host.stateByKey.get(listId) ?? this.host.stateById.get(listId);
|
|
4293
|
+
return listState?.selected ?? -1;
|
|
4294
|
+
}
|
|
4277
4295
|
resolveScopedRootReference(scope, path, currentValue, responseValue, inputValue) {
|
|
4278
4296
|
if (scope === "current") {
|
|
4279
4297
|
if (path.length === 0) {
|
|
@@ -4388,6 +4406,9 @@ var ReferenceRuntime = class {
|
|
|
4388
4406
|
}
|
|
4389
4407
|
resolveCurrentReference(reference, currentValue) {
|
|
4390
4408
|
if (this.isListElementContext(currentValue)) {
|
|
4409
|
+
if (reference === "isSelected") {
|
|
4410
|
+
return currentValue.index === this.getListSelectionIndex(currentValue.listId);
|
|
4411
|
+
}
|
|
4391
4412
|
const resolvedDescriptor = this.resolveListDescriptorNode(currentValue.descriptor, currentValue);
|
|
4392
4413
|
const firstPart = reference.split(".")[0];
|
|
4393
4414
|
const namedNode = this.findNamedWidget(resolvedDescriptor, firstPart, currentValue);
|
|
@@ -4395,6 +4416,9 @@ var ReferenceRuntime = class {
|
|
|
4395
4416
|
const widgetKey = this.host.resolveItemNodeKey(namedNode, currentValue.listId, currentValue.index, `named.${firstPart}`);
|
|
4396
4417
|
const state = this.host.ensureWidgetState(namedNode, widgetKey);
|
|
4397
4418
|
const rest = reference.split(".").slice(1).join(".");
|
|
4419
|
+
if (rest === "isSelected") {
|
|
4420
|
+
return currentValue.index === this.getListSelectionIndex(currentValue.listId);
|
|
4421
|
+
}
|
|
4398
4422
|
return rest ? this.readWidgetField(state, rest) : state;
|
|
4399
4423
|
}
|
|
4400
4424
|
const directDescriptorValue = readPath(currentValue.descriptor, reference.split("."));
|
|
@@ -4420,9 +4444,9 @@ var ReferenceRuntime = class {
|
|
|
4420
4444
|
readWidgetField(state, field) {
|
|
4421
4445
|
switch (field) {
|
|
4422
4446
|
case "text":
|
|
4423
|
-
return state.text ?? "";
|
|
4447
|
+
return resolveInlineI18nValue(this.host, state.text ?? "");
|
|
4424
4448
|
case "placeholder":
|
|
4425
|
-
return state.placeholder ?? "";
|
|
4449
|
+
return resolveInlineI18nValue(this.host, state.placeholder ?? "");
|
|
4426
4450
|
case "checked":
|
|
4427
4451
|
return state.checked ?? false;
|
|
4428
4452
|
case "enabled":
|
|
@@ -4433,6 +4457,8 @@ var ReferenceRuntime = class {
|
|
|
4433
4457
|
return state.condition ?? false;
|
|
4434
4458
|
case "selected":
|
|
4435
4459
|
return state.selected ?? 0;
|
|
4460
|
+
case "item":
|
|
4461
|
+
return state.selected ?? (state.widget === "list" || state.widget === "grid-view" ? -1 : 0);
|
|
4436
4462
|
case "value":
|
|
4437
4463
|
if (state.widget === "combobox") {
|
|
4438
4464
|
const index = state.selected ?? 0;
|
|
@@ -4454,9 +4480,9 @@ var ReferenceRuntime = class {
|
|
|
4454
4480
|
case "isHidden":
|
|
4455
4481
|
return state.isHidden ?? true;
|
|
4456
4482
|
case "url":
|
|
4457
|
-
return state.url ?? "";
|
|
4483
|
+
return resolveInlineI18nValue(this.host, state.url ?? "");
|
|
4458
4484
|
case "base64":
|
|
4459
|
-
return state.base64 ?? "";
|
|
4485
|
+
return resolveInlineI18nValue(this.host, state.base64 ?? "");
|
|
4460
4486
|
case "elements":
|
|
4461
4487
|
if (state.widget === "list") {
|
|
4462
4488
|
return (state.elements ?? []).map((descriptor, index) => ({
|
|
@@ -4538,6 +4564,9 @@ var ReferenceRuntime = class {
|
|
|
4538
4564
|
case "selected":
|
|
4539
4565
|
state.selected = toFiniteNumber(inputValue) ?? 0;
|
|
4540
4566
|
break;
|
|
4567
|
+
case "item":
|
|
4568
|
+
state.selected = toFiniteNumber(inputValue) ?? 0;
|
|
4569
|
+
break;
|
|
4541
4570
|
case "isHidden":
|
|
4542
4571
|
state.isHidden = Boolean(inputValue);
|
|
4543
4572
|
break;
|
|
@@ -4604,15 +4633,25 @@ var ReferenceRuntime = class {
|
|
|
4604
4633
|
}
|
|
4605
4634
|
resolveMappedValue(template, currentValue, responseValue, inputValue = currentValue) {
|
|
4606
4635
|
if (typeof template === "string") {
|
|
4607
|
-
if (template.startsWith("
|
|
4608
|
-
return this.
|
|
4636
|
+
if (template.startsWith("i18n:") && !template.slice(5).includes(" ")) {
|
|
4637
|
+
return this.host.resolveI18nValue(template);
|
|
4638
|
+
}
|
|
4639
|
+
let resolvedTemplate = template;
|
|
4640
|
+
if (resolvedTemplate.includes("i18n:")) {
|
|
4641
|
+
resolvedTemplate = resolvedTemplate.replaceAll(/i18n:([A-Za-z0-9_.-]+)/g, (_match, key) => {
|
|
4642
|
+
return this.host.resolveI18nValue(`i18n:${key}`);
|
|
4643
|
+
});
|
|
4644
|
+
}
|
|
4645
|
+
if (resolvedTemplate.startsWith("$ref:")) {
|
|
4646
|
+
return this.resolveReference(resolvedTemplate.slice(5), currentValue, responseValue, inputValue);
|
|
4609
4647
|
}
|
|
4610
|
-
if (
|
|
4611
|
-
return
|
|
4648
|
+
if (resolvedTemplate.includes("$ref:")) {
|
|
4649
|
+
return resolvedTemplate.replaceAll(/\$ref:([A-Za-z0-9_.]+)/g, (_match, ref) => {
|
|
4612
4650
|
const resolved = this.resolveReference(ref, currentValue, responseValue, inputValue);
|
|
4613
4651
|
return stringifyReferenceValue(resolved);
|
|
4614
4652
|
});
|
|
4615
4653
|
}
|
|
4654
|
+
return resolvedTemplate;
|
|
4616
4655
|
}
|
|
4617
4656
|
if (Array.isArray(template)) {
|
|
4618
4657
|
return template.map((entry) => this.resolveMappedValue(entry, currentValue, responseValue, inputValue));
|
|
@@ -4748,7 +4787,10 @@ var ActionRuntime = class {
|
|
|
4748
4787
|
scrollWidgetToBottom;
|
|
4749
4788
|
scrollWidgetToElementByIndex;
|
|
4750
4789
|
playAudio;
|
|
4790
|
+
playStaticAudio;
|
|
4751
4791
|
stopPlaying;
|
|
4792
|
+
showInfoToast;
|
|
4793
|
+
showErrorToast;
|
|
4752
4794
|
copyToClipboard;
|
|
4753
4795
|
selectFile;
|
|
4754
4796
|
confirmModal;
|
|
@@ -4790,7 +4832,10 @@ var ActionRuntime = class {
|
|
|
4790
4832
|
this.scrollWidgetToBottom = options.scrollWidgetToBottom;
|
|
4791
4833
|
this.scrollWidgetToElementByIndex = options.scrollWidgetToElementByIndex;
|
|
4792
4834
|
this.playAudio = options.playAudio;
|
|
4835
|
+
this.playStaticAudio = options.playStaticAudio;
|
|
4793
4836
|
this.stopPlaying = options.stopPlaying;
|
|
4837
|
+
this.showInfoToast = options.showInfoToast;
|
|
4838
|
+
this.showErrorToast = options.showErrorToast;
|
|
4794
4839
|
this.copyToClipboard = options.copyToClipboard;
|
|
4795
4840
|
this.selectFile = options.selectFile;
|
|
4796
4841
|
this.confirmModal = options.confirmModal;
|
|
@@ -4911,14 +4956,37 @@ var ActionRuntime = class {
|
|
|
4911
4956
|
await this.navigateTo(name.slice(9));
|
|
4912
4957
|
return null;
|
|
4913
4958
|
}
|
|
4959
|
+
if (name.startsWith("pause ")) {
|
|
4960
|
+
const durationMs = Math.max(0, Math.trunc(this.toNumber(name.slice(6))));
|
|
4961
|
+
await new Promise((resolve) => {
|
|
4962
|
+
if (typeof window === "undefined") {
|
|
4963
|
+
setTimeout(resolve, durationMs);
|
|
4964
|
+
return;
|
|
4965
|
+
}
|
|
4966
|
+
window.setTimeout(resolve, durationMs);
|
|
4967
|
+
});
|
|
4968
|
+
return inputValue;
|
|
4969
|
+
}
|
|
4914
4970
|
if (name.startsWith("play ")) {
|
|
4915
4971
|
await this.playAudio(this.resolveReference(name.slice(5), context.currentValue, context.responseValue, inputValue));
|
|
4916
4972
|
return null;
|
|
4917
4973
|
}
|
|
4974
|
+
if (name.startsWith("playStatic ")) {
|
|
4975
|
+
await this.playStaticAudio(name.slice(11).trim());
|
|
4976
|
+
return null;
|
|
4977
|
+
}
|
|
4918
4978
|
if (name === "copyToClipboard") {
|
|
4919
4979
|
await this.copyToClipboard(inputValue);
|
|
4920
4980
|
return inputValue;
|
|
4921
4981
|
}
|
|
4982
|
+
if (name === "infoToast") {
|
|
4983
|
+
this.showInfoToast(this.stringifyValue(inputValue));
|
|
4984
|
+
return inputValue;
|
|
4985
|
+
}
|
|
4986
|
+
if (name === "errorToast") {
|
|
4987
|
+
this.showErrorToast(this.stringifyValue(inputValue));
|
|
4988
|
+
return inputValue;
|
|
4989
|
+
}
|
|
4922
4990
|
if (name === "selectFile") {
|
|
4923
4991
|
return this.selectFile(action.args);
|
|
4924
4992
|
}
|
|
@@ -5002,6 +5070,18 @@ var ActionRuntime = class {
|
|
|
5002
5070
|
this.moveCursorToEnd(name.slice(16));
|
|
5003
5071
|
return null;
|
|
5004
5072
|
}
|
|
5073
|
+
if (name.startsWith("setItem ")) {
|
|
5074
|
+
const reference = name.slice(8).trim();
|
|
5075
|
+
this.assignReference(`${reference}.item`, Math.max(-1, Math.trunc(this.toNumber(inputValue))), context.currentValue);
|
|
5076
|
+
return inputValue;
|
|
5077
|
+
}
|
|
5078
|
+
if (name === "setItemCurrent") {
|
|
5079
|
+
const current = context.currentValue;
|
|
5080
|
+
if (isListElementLike(current)) {
|
|
5081
|
+
this.assignReference(`${current.listId}.item`, Math.max(0, Math.trunc(current.index)), current);
|
|
5082
|
+
}
|
|
5083
|
+
return inputValue;
|
|
5084
|
+
}
|
|
5005
5085
|
if (name.startsWith("scrollToTop ")) {
|
|
5006
5086
|
this.scrollWidgetToTop(name.slice(12));
|
|
5007
5087
|
return inputValue;
|
|
@@ -5109,7 +5189,8 @@ var ActionRuntime = class {
|
|
|
5109
5189
|
if (name.startsWith("append ")) {
|
|
5110
5190
|
const reference = name.slice(7);
|
|
5111
5191
|
const currentText = this.resolveReference(reference, context.currentValue, context.responseValue, inputValue);
|
|
5112
|
-
const
|
|
5192
|
+
const appendValue = this.resolveMappedValue(action.args, context.currentValue, context.responseValue, inputValue);
|
|
5193
|
+
const nextValue = `${this.stringifyValue(currentText)}${this.stringifyValue(appendValue)}`;
|
|
5113
5194
|
this.assignReference(reference, nextValue, context.currentValue);
|
|
5114
5195
|
return nextValue;
|
|
5115
5196
|
}
|
|
@@ -5536,7 +5617,7 @@ function updatePointerFromMouseEvent(event) {
|
|
|
5536
5617
|
};
|
|
5537
5618
|
}
|
|
5538
5619
|
function getListElementContextFromElement(element, stateByKey) {
|
|
5539
|
-
const listElement = element.closest(".vjt-list-element");
|
|
5620
|
+
const listElement = element.closest(".vjt-list-element, .vjt-grid-cell");
|
|
5540
5621
|
if (!(listElement instanceof HTMLElement)) {
|
|
5541
5622
|
return null;
|
|
5542
5623
|
}
|
|
@@ -5774,6 +5855,10 @@ var DEFAULT_STYLE_MAP = {
|
|
|
5774
5855
|
spoilerFrameDark: "border: 1px solid rgba(109,143,179,0.2); background: rgba(255,255,255,0.035); border-radius: 10px;",
|
|
5775
5856
|
clickableRowLight: "padding: 4px 8px; border-radius: 10px; border: 1px solid rgba(111, 143, 177, 0.16); background: rgba(255,255,255,0.32); cursor: pointer;",
|
|
5776
5857
|
clickableRowDark: "padding: 4px 8px; border-radius: 10px; border: 1px solid rgba(109,143,179,0.14); background: rgba(255,255,255,0.03); cursor: pointer;",
|
|
5858
|
+
selectableTileLight: "padding: 12px 14px; border-radius: 12px; border: 1px solid rgba(111, 143, 177, 0.18); background: rgba(255,255,255,0.34); cursor: pointer; transition: background 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease;",
|
|
5859
|
+
selectableTileDark: "padding: 12px 14px; border-radius: 12px; border: 1px solid rgba(109,143,179,0.16); background: rgba(255,255,255,0.03); cursor: pointer; transition: background 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease;",
|
|
5860
|
+
selectableTileSelectedLight: "padding: 12px 14px; border-radius: 12px; border: 1px solid rgba(87, 136, 191, 0.52); background: rgba(120, 169, 221, 0.18); box-shadow: 0 0 0 4px rgba(120, 169, 221, 0.12); cursor: pointer;",
|
|
5861
|
+
selectableTileSelectedDark: "padding: 12px 14px; border-radius: 12px; border: 1px solid rgba(138, 186, 238, 0.48); background: rgba(83, 124, 168, 0.26); box-shadow: 0 0 0 4px rgba(83, 124, 168, 0.18); cursor: pointer;",
|
|
5777
5862
|
employeeLinkLight: "display:flex; align-items:center; width:100%; height:100%; color:#54749a; font-weight:600;",
|
|
5778
5863
|
employeeLinkDark: "display:flex; align-items:center; width:100%; height:100%; color:#b8d0ea; font-weight:600;"
|
|
5779
5864
|
};
|
|
@@ -5784,6 +5869,9 @@ var MAX_RECORDING_MS = 10 * 60 * 1e3;
|
|
|
5784
5869
|
var MAX_LISTENING_SILENCE_MS = 60 * 60 * 1e3;
|
|
5785
5870
|
var SILENCE_STOP_MS = 2e3;
|
|
5786
5871
|
var DEFAULT_SPEECH_THRESHOLD = 0.035;
|
|
5872
|
+
var LISTENING_TARGET_PRE_ROLL_MS = 300;
|
|
5873
|
+
var LISTENING_SEGMENT_MS = 1e3;
|
|
5874
|
+
var LISTENING_SEGMENT_STEP_MS = 500;
|
|
5787
5875
|
var RECORDING_MIME_CANDIDATES = [
|
|
5788
5876
|
"audio/webm;codecs=opus",
|
|
5789
5877
|
"audio/webm",
|
|
@@ -5864,6 +5952,11 @@ var VoiceRuntime = class {
|
|
|
5864
5952
|
listenAnalyser = null;
|
|
5865
5953
|
listenSource = null;
|
|
5866
5954
|
listenFrameId = null;
|
|
5955
|
+
listeningIdleSessions = [];
|
|
5956
|
+
listeningLaneTimeoutIds = [];
|
|
5957
|
+
listeningLaneIntervalIds = [];
|
|
5958
|
+
listeningPromotedSession = null;
|
|
5959
|
+
listeningCaptureActive = false;
|
|
5867
5960
|
lastSpeechAt = 0;
|
|
5868
5961
|
listeningStartedAt = 0;
|
|
5869
5962
|
recordingStartedFromListening = false;
|
|
@@ -5898,15 +5991,37 @@ var VoiceRuntime = class {
|
|
|
5898
5991
|
copy.set(bytes);
|
|
5899
5992
|
const blob = new Blob([copy.buffer], { type: payload.mimeType });
|
|
5900
5993
|
const url = URL.createObjectURL(blob);
|
|
5901
|
-
|
|
5994
|
+
await this.playResolvedSource(url, payload, true, {
|
|
5995
|
+
mimeType: payload.mimeType,
|
|
5996
|
+
bytes: bytes.byteLength
|
|
5997
|
+
});
|
|
5998
|
+
}
|
|
5999
|
+
async playStatic(url) {
|
|
6000
|
+
if (typeof url !== "string" || !url.trim()) {
|
|
6001
|
+
return;
|
|
6002
|
+
}
|
|
6003
|
+
const safeUrl = sanitizeUrl(url.trim(), { allowRelative: true });
|
|
6004
|
+
if (!safeUrl) {
|
|
6005
|
+
logRuntimeError("voice.playStatic", new Error("Blocked unsafe audio url"), { url });
|
|
6006
|
+
return;
|
|
6007
|
+
}
|
|
6008
|
+
await this.playResolvedSource(safeUrl, {
|
|
6009
|
+
audioData: safeUrl,
|
|
6010
|
+
mimeType: "audio/url"
|
|
6011
|
+
}, false, {
|
|
6012
|
+
url: safeUrl
|
|
6013
|
+
});
|
|
6014
|
+
}
|
|
6015
|
+
async playResolvedSource(sourceUrl, payload, revokeUrl, debugDetails) {
|
|
6016
|
+
const audio = new Audio(sourceUrl);
|
|
5902
6017
|
audio.preload = "auto";
|
|
5903
|
-
const player = { audio, url, payload };
|
|
6018
|
+
const player = { audio, url: sourceUrl, revokeUrl, payload };
|
|
5904
6019
|
audio.onended = () => {
|
|
5905
6020
|
void this.finishPlayback(player, "finished");
|
|
5906
6021
|
};
|
|
5907
6022
|
audio.onerror = () => {
|
|
5908
6023
|
this.detachPlayer(player);
|
|
5909
|
-
logRuntimeError("voice.play", new Error("Audio playback failed"),
|
|
6024
|
+
logRuntimeError("voice.play", new Error("Audio playback failed"), debugDetails);
|
|
5910
6025
|
};
|
|
5911
6026
|
this.activePlayers.push(player);
|
|
5912
6027
|
while (this.activePlayers.length > MAX_CONCURRENT_PLAYERS) {
|
|
@@ -5918,8 +6033,7 @@ var VoiceRuntime = class {
|
|
|
5918
6033
|
void this.finishPlayback(oldest, "stopped");
|
|
5919
6034
|
}
|
|
5920
6035
|
logRuntimeDebug(this.debugLogging, "voice-play", {
|
|
5921
|
-
|
|
5922
|
-
bytes: bytes.byteLength,
|
|
6036
|
+
...debugDetails,
|
|
5923
6037
|
activePlayers: this.activePlayers.length
|
|
5924
6038
|
});
|
|
5925
6039
|
await audio.play();
|
|
@@ -5941,6 +6055,10 @@ var VoiceRuntime = class {
|
|
|
5941
6055
|
}
|
|
5942
6056
|
}
|
|
5943
6057
|
stopRecording() {
|
|
6058
|
+
if (this.listeningCaptureActive) {
|
|
6059
|
+
void this.stopListeningCapture();
|
|
6060
|
+
return;
|
|
6061
|
+
}
|
|
5944
6062
|
if (!this.mediaRecorder || this.mediaRecorder.state === "inactive") {
|
|
5945
6063
|
return;
|
|
5946
6064
|
}
|
|
@@ -5963,9 +6081,12 @@ var VoiceRuntime = class {
|
|
|
5963
6081
|
this.listenContext = context;
|
|
5964
6082
|
this.listenAnalyser = analyser;
|
|
5965
6083
|
this.listenSource = source;
|
|
6084
|
+
this.listeningIdleSessions = [];
|
|
6085
|
+
this.listeningPromotedSession = null;
|
|
5966
6086
|
this.lastSpeechAt = 0;
|
|
5967
6087
|
this.listeningStartedAt = performance.now();
|
|
5968
6088
|
this.speechEventTriggered = false;
|
|
6089
|
+
this.startListeningRecorderSchedule(stream);
|
|
5969
6090
|
const sampleBuffer = new Uint8Array(analyser.fftSize);
|
|
5970
6091
|
const step = async () => {
|
|
5971
6092
|
if (!this.listening || !this.listenAnalyser) {
|
|
@@ -5986,14 +6107,14 @@ var VoiceRuntime = class {
|
|
|
5986
6107
|
this.speechEventTriggered = true;
|
|
5987
6108
|
await this.triggerSystemEvent("onSpeechDetected", null);
|
|
5988
6109
|
}
|
|
5989
|
-
if (!this.
|
|
5990
|
-
this.
|
|
6110
|
+
if (!this.listeningCaptureActive) {
|
|
6111
|
+
this.beginListeningCapture();
|
|
5991
6112
|
}
|
|
5992
|
-
} else if (this.
|
|
5993
|
-
this.
|
|
6113
|
+
} else if (this.listeningCaptureActive && this.lastSpeechAt > 0 && now - this.lastSpeechAt >= SILENCE_STOP_MS) {
|
|
6114
|
+
await this.stopListeningCapture();
|
|
5994
6115
|
this.lastSpeechAt = 0;
|
|
5995
6116
|
this.speechEventTriggered = false;
|
|
5996
|
-
} else if (!this.
|
|
6117
|
+
} else if (!this.listeningCaptureActive && now - this.listeningStartedAt >= MAX_LISTENING_SILENCE_MS) {
|
|
5997
6118
|
await this.stopListening();
|
|
5998
6119
|
return;
|
|
5999
6120
|
}
|
|
@@ -6026,9 +6147,12 @@ var VoiceRuntime = class {
|
|
|
6026
6147
|
this.listenContext = null;
|
|
6027
6148
|
this.listenAnalyser = null;
|
|
6028
6149
|
this.listenSource = null;
|
|
6029
|
-
if (this.
|
|
6030
|
-
this.
|
|
6150
|
+
if (this.listeningCaptureActive) {
|
|
6151
|
+
await this.stopListeningCapture();
|
|
6031
6152
|
}
|
|
6153
|
+
this.clearListeningRecorderSchedule();
|
|
6154
|
+
await this.discardListeningIdleSessions();
|
|
6155
|
+
this.listeningCaptureActive = false;
|
|
6032
6156
|
this.recordingStartedFromListening = false;
|
|
6033
6157
|
this.releaseInputStreamIfIdle();
|
|
6034
6158
|
}
|
|
@@ -6108,6 +6232,8 @@ var VoiceRuntime = class {
|
|
|
6108
6232
|
}
|
|
6109
6233
|
async handleRecordingError(error) {
|
|
6110
6234
|
logRuntimeError("voice.recording", error);
|
|
6235
|
+
this.listeningCaptureActive = false;
|
|
6236
|
+
this.recordingStartedFromListening = false;
|
|
6111
6237
|
await this.triggerSystemEvent("onRecordingError", this.normalizeErrorMessage(error));
|
|
6112
6238
|
}
|
|
6113
6239
|
async handleListeningError(error) {
|
|
@@ -6152,6 +6278,196 @@ var VoiceRuntime = class {
|
|
|
6152
6278
|
this.mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
6153
6279
|
return this.mediaStream;
|
|
6154
6280
|
}
|
|
6281
|
+
startListeningRecorderSchedule(stream) {
|
|
6282
|
+
this.clearListeningRecorderSchedule();
|
|
6283
|
+
this.launchListeningRecorder(stream);
|
|
6284
|
+
const delayedStart = window.setTimeout(() => {
|
|
6285
|
+
if (!this.listening || this.listeningCaptureActive) {
|
|
6286
|
+
return;
|
|
6287
|
+
}
|
|
6288
|
+
this.launchListeningRecorder(stream);
|
|
6289
|
+
const laneB = window.setInterval(() => {
|
|
6290
|
+
if (!this.listening || this.listeningCaptureActive) {
|
|
6291
|
+
return;
|
|
6292
|
+
}
|
|
6293
|
+
this.launchListeningRecorder(stream);
|
|
6294
|
+
}, LISTENING_SEGMENT_MS);
|
|
6295
|
+
this.listeningLaneIntervalIds.push(laneB);
|
|
6296
|
+
}, LISTENING_SEGMENT_STEP_MS);
|
|
6297
|
+
this.listeningLaneTimeoutIds.push(delayedStart);
|
|
6298
|
+
const laneA = window.setInterval(() => {
|
|
6299
|
+
if (!this.listening || this.listeningCaptureActive) {
|
|
6300
|
+
return;
|
|
6301
|
+
}
|
|
6302
|
+
this.launchListeningRecorder(stream);
|
|
6303
|
+
}, LISTENING_SEGMENT_MS);
|
|
6304
|
+
this.listeningLaneIntervalIds.push(laneA);
|
|
6305
|
+
}
|
|
6306
|
+
clearListeningRecorderSchedule() {
|
|
6307
|
+
for (const timeoutId of this.listeningLaneTimeoutIds) {
|
|
6308
|
+
window.clearTimeout(timeoutId);
|
|
6309
|
+
}
|
|
6310
|
+
this.listeningLaneTimeoutIds = [];
|
|
6311
|
+
for (const intervalId of this.listeningLaneIntervalIds) {
|
|
6312
|
+
window.clearInterval(intervalId);
|
|
6313
|
+
}
|
|
6314
|
+
this.listeningLaneIntervalIds = [];
|
|
6315
|
+
}
|
|
6316
|
+
launchListeningRecorder(stream) {
|
|
6317
|
+
const session = this.createListeningRecorderSession(stream);
|
|
6318
|
+
this.listeningIdleSessions.push(session);
|
|
6319
|
+
session.recorder.start();
|
|
6320
|
+
session.stopTimeoutId = window.setTimeout(() => {
|
|
6321
|
+
if (!session.promoted && session.recorder.state !== "inactive") {
|
|
6322
|
+
session.recorder.stop();
|
|
6323
|
+
}
|
|
6324
|
+
}, LISTENING_SEGMENT_MS);
|
|
6325
|
+
}
|
|
6326
|
+
createListeningRecorderSession(stream) {
|
|
6327
|
+
if (typeof MediaRecorder === "undefined") {
|
|
6328
|
+
throw new Error("MediaRecorder is not supported in this browser");
|
|
6329
|
+
}
|
|
6330
|
+
const preferredMimeType = this.getPreferredRecordingMimeType();
|
|
6331
|
+
const options = preferredMimeType ? { mimeType: preferredMimeType } : void 0;
|
|
6332
|
+
const recorder = options ? new MediaRecorder(stream, options) : new MediaRecorder(stream);
|
|
6333
|
+
let resolveStopped = () => {
|
|
6334
|
+
};
|
|
6335
|
+
const stopped = new Promise((resolve) => {
|
|
6336
|
+
resolveStopped = resolve;
|
|
6337
|
+
});
|
|
6338
|
+
const session = {
|
|
6339
|
+
recorder,
|
|
6340
|
+
mimeType: recorder.mimeType || preferredMimeType || "audio/webm",
|
|
6341
|
+
startedAt: performance.now(),
|
|
6342
|
+
chunks: [],
|
|
6343
|
+
stopTimeoutId: null,
|
|
6344
|
+
promoted: false,
|
|
6345
|
+
discard: false,
|
|
6346
|
+
stopped,
|
|
6347
|
+
resolveStopped
|
|
6348
|
+
};
|
|
6349
|
+
recorder.ondataavailable = (event) => {
|
|
6350
|
+
if (event.data && event.data.size > 0) {
|
|
6351
|
+
session.chunks.push(event.data);
|
|
6352
|
+
}
|
|
6353
|
+
};
|
|
6354
|
+
recorder.onerror = (event) => {
|
|
6355
|
+
void this.handleRecordingError(event.error ?? new Error("Unknown listening recording error"));
|
|
6356
|
+
};
|
|
6357
|
+
recorder.onstop = () => {
|
|
6358
|
+
void this.handleListeningRecorderStop(session);
|
|
6359
|
+
};
|
|
6360
|
+
return session;
|
|
6361
|
+
}
|
|
6362
|
+
beginListeningCapture() {
|
|
6363
|
+
const winner = this.selectListeningRecorderWinner();
|
|
6364
|
+
if (!winner) {
|
|
6365
|
+
return;
|
|
6366
|
+
}
|
|
6367
|
+
this.listeningCaptureActive = true;
|
|
6368
|
+
this.recordingStartedFromListening = true;
|
|
6369
|
+
this.listeningPromotedSession = winner;
|
|
6370
|
+
winner.promoted = true;
|
|
6371
|
+
winner.discard = false;
|
|
6372
|
+
if (winner.stopTimeoutId !== null) {
|
|
6373
|
+
window.clearTimeout(winner.stopTimeoutId);
|
|
6374
|
+
winner.stopTimeoutId = null;
|
|
6375
|
+
}
|
|
6376
|
+
this.clearListeningRecorderSchedule();
|
|
6377
|
+
for (const session of this.listeningIdleSessions) {
|
|
6378
|
+
if (session === winner) {
|
|
6379
|
+
continue;
|
|
6380
|
+
}
|
|
6381
|
+
void this.discardListeningSession(session);
|
|
6382
|
+
}
|
|
6383
|
+
const preRollMs = Math.max(0, Math.round(performance.now() - winner.startedAt));
|
|
6384
|
+
logRuntimeDebug(this.debugLogging, "voice-recording-started", {
|
|
6385
|
+
mimeType: winner.mimeType,
|
|
6386
|
+
fromListening: true,
|
|
6387
|
+
preRollMs
|
|
6388
|
+
});
|
|
6389
|
+
void this.triggerSystemEvent("onRecordingStarted", null);
|
|
6390
|
+
}
|
|
6391
|
+
selectListeningRecorderWinner() {
|
|
6392
|
+
const activeSessions = this.listeningIdleSessions.filter((session) => session.recorder.state !== "inactive");
|
|
6393
|
+
if (activeSessions.length === 0) {
|
|
6394
|
+
return null;
|
|
6395
|
+
}
|
|
6396
|
+
const targetStartedAt = performance.now() - LISTENING_TARGET_PRE_ROLL_MS;
|
|
6397
|
+
const suitable = activeSessions.filter((session) => session.startedAt <= targetStartedAt).sort((left, right) => right.startedAt - left.startedAt);
|
|
6398
|
+
if (suitable.length > 0) {
|
|
6399
|
+
return suitable[0];
|
|
6400
|
+
}
|
|
6401
|
+
return activeSessions.sort((left, right) => left.startedAt - right.startedAt)[0] ?? null;
|
|
6402
|
+
}
|
|
6403
|
+
async stopListeningCapture() {
|
|
6404
|
+
if (!this.listeningCaptureActive || !this.listeningPromotedSession) {
|
|
6405
|
+
return;
|
|
6406
|
+
}
|
|
6407
|
+
const session = this.listeningPromotedSession;
|
|
6408
|
+
this.listeningCaptureActive = false;
|
|
6409
|
+
this.listeningPromotedSession = null;
|
|
6410
|
+
if (session.recorder.state !== "inactive") {
|
|
6411
|
+
session.recorder.stop();
|
|
6412
|
+
}
|
|
6413
|
+
await session.stopped;
|
|
6414
|
+
}
|
|
6415
|
+
async discardListeningSession(session) {
|
|
6416
|
+
session.discard = true;
|
|
6417
|
+
if (session.stopTimeoutId !== null) {
|
|
6418
|
+
window.clearTimeout(session.stopTimeoutId);
|
|
6419
|
+
session.stopTimeoutId = null;
|
|
6420
|
+
}
|
|
6421
|
+
if (session.recorder.state !== "inactive") {
|
|
6422
|
+
session.recorder.stop();
|
|
6423
|
+
}
|
|
6424
|
+
await session.stopped;
|
|
6425
|
+
}
|
|
6426
|
+
async discardListeningIdleSessions() {
|
|
6427
|
+
const sessions = [...this.listeningIdleSessions];
|
|
6428
|
+
for (const session of sessions) {
|
|
6429
|
+
if (session === this.listeningPromotedSession) {
|
|
6430
|
+
continue;
|
|
6431
|
+
}
|
|
6432
|
+
await this.discardListeningSession(session);
|
|
6433
|
+
}
|
|
6434
|
+
this.listeningIdleSessions = this.listeningPromotedSession ? [this.listeningPromotedSession] : [];
|
|
6435
|
+
}
|
|
6436
|
+
async handleListeningRecorderStop(session) {
|
|
6437
|
+
if (session.stopTimeoutId !== null) {
|
|
6438
|
+
window.clearTimeout(session.stopTimeoutId);
|
|
6439
|
+
session.stopTimeoutId = null;
|
|
6440
|
+
}
|
|
6441
|
+
this.listeningIdleSessions = this.listeningIdleSessions.filter((entry) => entry !== session);
|
|
6442
|
+
try {
|
|
6443
|
+
if (session.discard || !session.promoted) {
|
|
6444
|
+
return;
|
|
6445
|
+
}
|
|
6446
|
+
const blob = new Blob(session.chunks, { type: session.mimeType });
|
|
6447
|
+
const audioData = await blobToBase64(blob);
|
|
6448
|
+
logRuntimeDebug(this.debugLogging, "voice-recording-stopped", {
|
|
6449
|
+
mimeType: session.mimeType,
|
|
6450
|
+
size: blob.size,
|
|
6451
|
+
fromListening: true
|
|
6452
|
+
});
|
|
6453
|
+
await this.triggerSystemEvent("onRecordingStopped", {
|
|
6454
|
+
audioData,
|
|
6455
|
+
mimeType: session.mimeType
|
|
6456
|
+
});
|
|
6457
|
+
} catch (error) {
|
|
6458
|
+
await this.handleRecordingError(error);
|
|
6459
|
+
} finally {
|
|
6460
|
+
session.resolveStopped();
|
|
6461
|
+
if (session.promoted) {
|
|
6462
|
+
this.recordingStartedFromListening = false;
|
|
6463
|
+
if (this.listening && this.mediaStream?.active) {
|
|
6464
|
+
this.listeningStartedAt = performance.now();
|
|
6465
|
+
this.speechEventTriggered = false;
|
|
6466
|
+
this.startListeningRecorderSchedule(this.mediaStream);
|
|
6467
|
+
}
|
|
6468
|
+
}
|
|
6469
|
+
}
|
|
6470
|
+
}
|
|
6155
6471
|
clearRecordingTimeout() {
|
|
6156
6472
|
if (this.recordingTimeoutId !== null) {
|
|
6157
6473
|
window.clearTimeout(this.recordingTimeoutId);
|
|
@@ -6189,7 +6505,9 @@ var VoiceRuntime = class {
|
|
|
6189
6505
|
}
|
|
6190
6506
|
player.audio.onended = null;
|
|
6191
6507
|
player.audio.onerror = null;
|
|
6192
|
-
|
|
6508
|
+
if (player.revokeUrl) {
|
|
6509
|
+
URL.revokeObjectURL(player.url);
|
|
6510
|
+
}
|
|
6193
6511
|
}
|
|
6194
6512
|
async finishPlayback(player, reason) {
|
|
6195
6513
|
if (!this.activePlayers.includes(player)) {
|
|
@@ -6203,7 +6521,9 @@ var VoiceRuntime = class {
|
|
|
6203
6521
|
await this.triggerSystemEvent(reason === "finished" ? "onPlayFinished" : "onPlayingStopped", payload);
|
|
6204
6522
|
}
|
|
6205
6523
|
async handlePageDeactivation() {
|
|
6206
|
-
const hadActiveRecording = Boolean(
|
|
6524
|
+
const hadActiveRecording = Boolean(
|
|
6525
|
+
this.mediaRecorder && this.mediaRecorder.state !== "inactive" || this.listeningCaptureActive
|
|
6526
|
+
);
|
|
6207
6527
|
const hadActiveListening = this.listening;
|
|
6208
6528
|
if (!hadActiveRecording && !hadActiveListening) {
|
|
6209
6529
|
return;
|
|
@@ -6352,7 +6672,7 @@ function renderGridLayout(env, node, cellsVisible, path, itemContext) {
|
|
|
6352
6672
|
|
|
6353
6673
|
// src/lib/widgets/conditional-container.ts
|
|
6354
6674
|
var renderConditionalContainer = (env, node, state, key, path, itemContext) => {
|
|
6355
|
-
const condition = typeof node.condition === "object" && node.condition !== null ? env.evaluateConditionExpression(node.condition, itemContext
|
|
6675
|
+
const condition = typeof node.condition === "object" && node.condition !== null ? env.evaluateConditionExpression(node.condition, itemContext ?? null) : state.condition ?? env.normalizeBoolean(node.condition, true);
|
|
6356
6676
|
const chosen = condition ? node.default : node.alternative;
|
|
6357
6677
|
const content = chosen ? env.renderNode(chosen, `${path}.${condition ? "default" : "alternative"}`, itemContext) : "";
|
|
6358
6678
|
const hasOwnBox = Boolean(
|
|
@@ -6585,10 +6905,12 @@ var renderButton = (env, node, state, key, _path, itemContext) => {
|
|
|
6585
6905
|
const enabled = state.enabled ?? env.normalizeBoolean(node.enabled, true);
|
|
6586
6906
|
const disabledAttribute = enabled ? "" : " disabled";
|
|
6587
6907
|
const text = env.escapeHtml(env.resolveTextValue(state.text ?? node.text, itemContext));
|
|
6908
|
+
const hintValue = typeof node.hint === "string" ? env.resolveTextValue(node.hint, itemContext) : "";
|
|
6909
|
+
const hintAttribute = hintValue ? ` title="${env.escapeHtml(hintValue)}"` : "";
|
|
6588
6910
|
const styleString = env.buildStyleString(node);
|
|
6589
6911
|
const styleAttribute = styleString ? ` style="${env.escapeHtml(styleString)}"` : "";
|
|
6590
6912
|
const buttonType = env.escapeHtml(node.type === "submit" ? "submit" : "button");
|
|
6591
|
-
return `<button${common}${env.buildWidgetDataAttributes(key, node)} class="vjt-button" data-widget="button" type="${buttonType}"${styleAttribute}${disabledAttribute}>${text}</button>`;
|
|
6913
|
+
return `<button${common}${env.buildWidgetDataAttributes(key, node)} class="vjt-button" data-widget="button" type="${buttonType}"${styleAttribute}${hintAttribute}${disabledAttribute}>${text}</button>`;
|
|
6592
6914
|
};
|
|
6593
6915
|
|
|
6594
6916
|
// src/lib/widgets/checkbox.ts
|
|
@@ -7467,6 +7789,9 @@ var RuntimeRenderer = class {
|
|
|
7467
7789
|
activeModalId = null;
|
|
7468
7790
|
activeContextMenu = null;
|
|
7469
7791
|
activeConfirmModal = null;
|
|
7792
|
+
activeToasts = [];
|
|
7793
|
+
toastCounter = 0;
|
|
7794
|
+
toastTimeoutIds = /* @__PURE__ */ new Map();
|
|
7470
7795
|
lastPointer = { x: 24, y: 24 };
|
|
7471
7796
|
pendingResetModalIds = /* @__PURE__ */ new Set();
|
|
7472
7797
|
pendingScrollAction = null;
|
|
@@ -7544,6 +7869,7 @@ var RuntimeRenderer = class {
|
|
|
7544
7869
|
setVarValue: (name, value) => {
|
|
7545
7870
|
this.vars.set(name, value);
|
|
7546
7871
|
},
|
|
7872
|
+
resolveI18nValue: (value) => this.resolveI18nValue(value),
|
|
7547
7873
|
ensureWidgetState: (node, key) => this.ensureWidgetState(node, key),
|
|
7548
7874
|
resolveItemNodeKey: (node, listKey, index, fallbackPath) => this.resolveItemNodeKey(node, listKey, index, fallbackPath),
|
|
7549
7875
|
indexListElementNodes: (listNode, element, index) => this.indexListElementNodes(listNode, element, index)
|
|
@@ -7585,7 +7911,10 @@ var RuntimeRenderer = class {
|
|
|
7585
7911
|
scrollWidgetToBottom: (reference) => this.scrollWidgetToBottom(reference),
|
|
7586
7912
|
scrollWidgetToElementByIndex: (reference, index) => this.scrollWidgetToElementByIndex(reference, index),
|
|
7587
7913
|
playAudio: (value) => this.voiceRuntime.play(value),
|
|
7914
|
+
playStaticAudio: (url) => this.voiceRuntime.playStatic(url),
|
|
7588
7915
|
stopPlaying: () => this.voiceRuntime.stopPlaying(),
|
|
7916
|
+
showInfoToast: (message) => this.showToast("info", message),
|
|
7917
|
+
showErrorToast: (message) => this.showToast("error", message),
|
|
7589
7918
|
copyToClipboard: (value) => this.copyToClipboard(value),
|
|
7590
7919
|
selectFile: (args) => this.selectFile(args),
|
|
7591
7920
|
confirmModal: (args, inputValue) => this.confirmModal(args, inputValue),
|
|
@@ -7705,6 +8034,10 @@ var RuntimeRenderer = class {
|
|
|
7705
8034
|
source.close();
|
|
7706
8035
|
}
|
|
7707
8036
|
this.eventSources.length = 0;
|
|
8037
|
+
for (const timeoutId of this.toastTimeoutIds.values()) {
|
|
8038
|
+
window.clearTimeout(timeoutId);
|
|
8039
|
+
}
|
|
8040
|
+
this.toastTimeoutIds.clear();
|
|
7708
8041
|
this.voiceRuntime.dispose();
|
|
7709
8042
|
};
|
|
7710
8043
|
}
|
|
@@ -7905,6 +8238,21 @@ var RuntimeRenderer = class {
|
|
|
7905
8238
|
this.activeConfirmModal = null;
|
|
7906
8239
|
activeModal.resolve(confirmed);
|
|
7907
8240
|
}
|
|
8241
|
+
showToast(kind, message) {
|
|
8242
|
+
const text = message.trim();
|
|
8243
|
+
if (!text) {
|
|
8244
|
+
return;
|
|
8245
|
+
}
|
|
8246
|
+
const toastId = ++this.toastCounter;
|
|
8247
|
+
this.activeToasts.push({ id: toastId, kind, text });
|
|
8248
|
+
void this.rerenderRoot();
|
|
8249
|
+
const timeoutId = window.setTimeout(() => {
|
|
8250
|
+
this.toastTimeoutIds.delete(toastId);
|
|
8251
|
+
this.activeToasts = this.activeToasts.filter((entry) => entry.id !== toastId);
|
|
8252
|
+
void this.rerenderRoot();
|
|
8253
|
+
}, 3600);
|
|
8254
|
+
this.toastTimeoutIds.set(toastId, timeoutId);
|
|
8255
|
+
}
|
|
7908
8256
|
reindexStaticTree() {
|
|
7909
8257
|
this.nodeByKey.clear();
|
|
7910
8258
|
this.nodeById.clear();
|
|
@@ -8325,7 +8673,8 @@ var RuntimeRenderer = class {
|
|
|
8325
8673
|
name: node.name,
|
|
8326
8674
|
enabled: normalizeBoolean(node.enabled, true),
|
|
8327
8675
|
definitionSignature: buildDefinitionSignature(node.elements ?? []),
|
|
8328
|
-
elements: deepClone(node.elements ?? [])
|
|
8676
|
+
elements: deepClone(node.elements ?? []),
|
|
8677
|
+
selected: toFiniteNumber3(node.item) ?? toFiniteNumber3(node.selected) ?? -1
|
|
8329
8678
|
};
|
|
8330
8679
|
break;
|
|
8331
8680
|
case "listbox":
|
|
@@ -8423,6 +8772,7 @@ var RuntimeRenderer = class {
|
|
|
8423
8772
|
if (state.widget === "list" || state.widget === "grid-view") {
|
|
8424
8773
|
this.clearListElementState(state.key);
|
|
8425
8774
|
state.elements = deepClone(node.elements ?? []);
|
|
8775
|
+
state.selected = toFiniteNumber3(node.item) ?? toFiniteNumber3(node.selected) ?? state.selected ?? -1;
|
|
8426
8776
|
state.definitionSignature = nextSignature;
|
|
8427
8777
|
}
|
|
8428
8778
|
}
|
|
@@ -8602,7 +8952,18 @@ var RuntimeRenderer = class {
|
|
|
8602
8952
|
const modalMarkup = this.activeModalId ? renderModalOverlay(env, this.activeModalId) : "";
|
|
8603
8953
|
const menuMarkup = this.activeContextMenu ? renderContextMenuOverlay(env, this.activeContextMenu) : "";
|
|
8604
8954
|
const confirmMarkup = this.activeConfirmModal ? renderConfirmModalOverlay(env, this.activeConfirmModal) : "";
|
|
8605
|
-
|
|
8955
|
+
const toastMarkup = this.renderToastOverlay();
|
|
8956
|
+
return `${modalMarkup}${menuMarkup}${confirmMarkup}${toastMarkup}`;
|
|
8957
|
+
}
|
|
8958
|
+
renderToastOverlay() {
|
|
8959
|
+
if (this.activeToasts.length === 0) {
|
|
8960
|
+
return "";
|
|
8961
|
+
}
|
|
8962
|
+
const items = this.activeToasts.map((toast) => {
|
|
8963
|
+
const kindClass = toast.kind === "error" ? " vjt-toast--error" : " vjt-toast--info";
|
|
8964
|
+
return `<div class="vjt-toast${kindClass}" role="status" aria-live="polite">${escapeHtml(toast.text)}</div>`;
|
|
8965
|
+
}).join("");
|
|
8966
|
+
return `<div class="vjt-toast-stack">${items}</div>`;
|
|
8606
8967
|
}
|
|
8607
8968
|
updatePointerFromEvent(event) {
|
|
8608
8969
|
this.lastPointer = updatePointerFromMouseEvent(event);
|
|
@@ -8712,6 +9073,7 @@ var RuntimeRenderer = class {
|
|
|
8712
9073
|
case "grid-view":
|
|
8713
9074
|
this.clearListElementState(state.key);
|
|
8714
9075
|
state.elements = [];
|
|
9076
|
+
state.selected = -1;
|
|
8715
9077
|
break;
|
|
8716
9078
|
case "listbox":
|
|
8717
9079
|
state.listboxElements = [];
|
|
@@ -38,7 +38,10 @@ type ActionRuntimeOptions = {
|
|
|
38
38
|
scrollWidgetToBottom: (reference: string) => void;
|
|
39
39
|
scrollWidgetToElementByIndex: (reference: string, index: unknown) => void;
|
|
40
40
|
playAudio: (value: unknown) => Promise<void>;
|
|
41
|
+
playStaticAudio: (url: string) => Promise<void>;
|
|
41
42
|
stopPlaying: () => Promise<void>;
|
|
43
|
+
showInfoToast: (message: string) => void;
|
|
44
|
+
showErrorToast: (message: string) => void;
|
|
42
45
|
copyToClipboard: (value: unknown) => Promise<void>;
|
|
43
46
|
selectFile: (options: unknown) => Promise<unknown>;
|
|
44
47
|
confirmModal: (options: unknown, inputValue: unknown) => Promise<boolean>;
|
|
@@ -93,7 +96,10 @@ export declare class ActionRuntime {
|
|
|
93
96
|
private readonly scrollWidgetToBottom;
|
|
94
97
|
private readonly scrollWidgetToElementByIndex;
|
|
95
98
|
private readonly playAudio;
|
|
99
|
+
private readonly playStaticAudio;
|
|
96
100
|
private readonly stopPlaying;
|
|
101
|
+
private readonly showInfoToast;
|
|
102
|
+
private readonly showErrorToast;
|
|
97
103
|
private readonly copyToClipboard;
|
|
98
104
|
private readonly selectFile;
|
|
99
105
|
private readonly confirmModal;
|
package/dist/lib/references.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ type ReferenceRuntimeHost = {
|
|
|
10
10
|
getVarValue: (name: string) => unknown;
|
|
11
11
|
getVarsSnapshot: () => Record<string, unknown>;
|
|
12
12
|
setVarValue: (name: string, value: unknown) => void;
|
|
13
|
+
resolveI18nValue: (value: string | undefined) => string;
|
|
13
14
|
ensureWidgetState: (node: DescriptionNode, key: string) => WidgetState;
|
|
14
15
|
resolveItemNodeKey: (node: DescriptionNode, listKey: string, index: number, fallbackPath: string) => string;
|
|
15
16
|
indexListElementNodes: (listNode: ListNode | GridViewNode, element: DescriptionNode, index: number) => void;
|
|
@@ -17,6 +18,7 @@ type ReferenceRuntimeHost = {
|
|
|
17
18
|
export declare class ReferenceRuntime {
|
|
18
19
|
private readonly host;
|
|
19
20
|
constructor(host: ReferenceRuntimeHost);
|
|
21
|
+
private getListSelectionIndex;
|
|
20
22
|
private resolveScopedRootReference;
|
|
21
23
|
private readVarReference;
|
|
22
24
|
private writeVarReference;
|
|
@@ -16,6 +16,11 @@ export declare class VoiceRuntime {
|
|
|
16
16
|
private listenAnalyser;
|
|
17
17
|
private listenSource;
|
|
18
18
|
private listenFrameId;
|
|
19
|
+
private listeningIdleSessions;
|
|
20
|
+
private listeningLaneTimeoutIds;
|
|
21
|
+
private listeningLaneIntervalIds;
|
|
22
|
+
private listeningPromotedSession;
|
|
23
|
+
private listeningCaptureActive;
|
|
19
24
|
private lastSpeechAt;
|
|
20
25
|
private listeningStartedAt;
|
|
21
26
|
private recordingStartedFromListening;
|
|
@@ -24,6 +29,8 @@ export declare class VoiceRuntime {
|
|
|
24
29
|
private readonly pageHideHandler;
|
|
25
30
|
constructor(options: VoiceRuntimeOptions);
|
|
26
31
|
play(value: unknown): Promise<void>;
|
|
32
|
+
playStatic(url: string): Promise<void>;
|
|
33
|
+
private playResolvedSource;
|
|
27
34
|
stopPlaying(): Promise<void>;
|
|
28
35
|
startRecording(): Promise<void>;
|
|
29
36
|
stopRecording(): void;
|
|
@@ -37,6 +44,16 @@ export declare class VoiceRuntime {
|
|
|
37
44
|
private normalizePlayablePayload;
|
|
38
45
|
private getPreferredRecordingMimeType;
|
|
39
46
|
private ensureInputStream;
|
|
47
|
+
private startListeningRecorderSchedule;
|
|
48
|
+
private clearListeningRecorderSchedule;
|
|
49
|
+
private launchListeningRecorder;
|
|
50
|
+
private createListeningRecorderSession;
|
|
51
|
+
private beginListeningCapture;
|
|
52
|
+
private selectListeningRecorderWinner;
|
|
53
|
+
private stopListeningCapture;
|
|
54
|
+
private discardListeningSession;
|
|
55
|
+
private discardListeningIdleSessions;
|
|
56
|
+
private handleListeningRecorderStop;
|
|
40
57
|
private clearRecordingTimeout;
|
|
41
58
|
private releaseInputStreamIfIdle;
|
|
42
59
|
private stopStreamTracks;
|
package/package.json
CHANGED
package/vjt-styles.css
CHANGED
|
@@ -862,3 +862,61 @@ body {
|
|
|
862
862
|
.vjt-confirm-actions .vjt-button {
|
|
863
863
|
min-width: 120px;
|
|
864
864
|
}
|
|
865
|
+
|
|
866
|
+
.vjt-toast-stack {
|
|
867
|
+
position: fixed;
|
|
868
|
+
left: 50%;
|
|
869
|
+
bottom: 56px;
|
|
870
|
+
transform: translateX(-50%);
|
|
871
|
+
z-index: 1200;
|
|
872
|
+
display: flex;
|
|
873
|
+
flex-direction: column;
|
|
874
|
+
align-items: center;
|
|
875
|
+
gap: 12px;
|
|
876
|
+
pointer-events: none;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
.vjt-toast {
|
|
880
|
+
min-width: 320px;
|
|
881
|
+
max-width: min(680px, calc(100vw - 28px));
|
|
882
|
+
padding: 16px 20px;
|
|
883
|
+
border-radius: 18px;
|
|
884
|
+
border: 1px solid var(--vjt-border);
|
|
885
|
+
background: var(--vjt-surface);
|
|
886
|
+
color: var(--vjt-text);
|
|
887
|
+
box-shadow: var(--vjt-shadow);
|
|
888
|
+
line-height: 1.5;
|
|
889
|
+
white-space: pre-wrap;
|
|
890
|
+
font-size: 17px;
|
|
891
|
+
pointer-events: auto;
|
|
892
|
+
user-select: text;
|
|
893
|
+
-webkit-user-select: text;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
.vjt-toast--info {
|
|
897
|
+
background: color-mix(in srgb, var(--vjt-surface) 88%, var(--vjt-accent) 12%);
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
.vjt-toast--error {
|
|
901
|
+
border-color: color-mix(in srgb, #d84f4f 55%, var(--vjt-border));
|
|
902
|
+
background: color-mix(in srgb, var(--vjt-surface) 80%, #a01f1f 20%);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
@media (max-width: 720px) {
|
|
906
|
+
.vjt-toast-stack {
|
|
907
|
+
left: 16px;
|
|
908
|
+
right: 16px;
|
|
909
|
+
bottom: 32px;
|
|
910
|
+
transform: none;
|
|
911
|
+
align-items: stretch;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
.vjt-toast {
|
|
915
|
+
min-width: 0;
|
|
916
|
+
max-width: none;
|
|
917
|
+
width: 100%;
|
|
918
|
+
padding: 18px 20px;
|
|
919
|
+
border-radius: 20px;
|
|
920
|
+
font-size: 18px;
|
|
921
|
+
}
|
|
922
|
+
}
|