@vortexm/vjt 0.1.13 → 0.1.14
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 +287 -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 +11 -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);
|
|
4609
4638
|
}
|
|
4610
|
-
|
|
4611
|
-
|
|
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);
|
|
4647
|
+
}
|
|
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,8 @@ 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_PRE_ROLL_MS = 300;
|
|
5873
|
+
var LISTENING_RECORDER_TIMESLICE_MS = 100;
|
|
5787
5874
|
var RECORDING_MIME_CANDIDATES = [
|
|
5788
5875
|
"audio/webm;codecs=opus",
|
|
5789
5876
|
"audio/webm",
|
|
@@ -5864,6 +5951,11 @@ var VoiceRuntime = class {
|
|
|
5864
5951
|
listenAnalyser = null;
|
|
5865
5952
|
listenSource = null;
|
|
5866
5953
|
listenFrameId = null;
|
|
5954
|
+
listeningRecorder = null;
|
|
5955
|
+
listeningRecorderMimeType = "audio/webm";
|
|
5956
|
+
listeningPreRollChunks = [];
|
|
5957
|
+
listeningCaptureChunks = [];
|
|
5958
|
+
listeningCaptureActive = false;
|
|
5867
5959
|
lastSpeechAt = 0;
|
|
5868
5960
|
listeningStartedAt = 0;
|
|
5869
5961
|
recordingStartedFromListening = false;
|
|
@@ -5898,15 +5990,37 @@ var VoiceRuntime = class {
|
|
|
5898
5990
|
copy.set(bytes);
|
|
5899
5991
|
const blob = new Blob([copy.buffer], { type: payload.mimeType });
|
|
5900
5992
|
const url = URL.createObjectURL(blob);
|
|
5901
|
-
|
|
5993
|
+
await this.playResolvedSource(url, payload, true, {
|
|
5994
|
+
mimeType: payload.mimeType,
|
|
5995
|
+
bytes: bytes.byteLength
|
|
5996
|
+
});
|
|
5997
|
+
}
|
|
5998
|
+
async playStatic(url) {
|
|
5999
|
+
if (typeof url !== "string" || !url.trim()) {
|
|
6000
|
+
return;
|
|
6001
|
+
}
|
|
6002
|
+
const safeUrl = sanitizeUrl(url.trim(), { allowRelative: true });
|
|
6003
|
+
if (!safeUrl) {
|
|
6004
|
+
logRuntimeError("voice.playStatic", new Error("Blocked unsafe audio url"), { url });
|
|
6005
|
+
return;
|
|
6006
|
+
}
|
|
6007
|
+
await this.playResolvedSource(safeUrl, {
|
|
6008
|
+
audioData: safeUrl,
|
|
6009
|
+
mimeType: "audio/url"
|
|
6010
|
+
}, false, {
|
|
6011
|
+
url: safeUrl
|
|
6012
|
+
});
|
|
6013
|
+
}
|
|
6014
|
+
async playResolvedSource(sourceUrl, payload, revokeUrl, debugDetails) {
|
|
6015
|
+
const audio = new Audio(sourceUrl);
|
|
5902
6016
|
audio.preload = "auto";
|
|
5903
|
-
const player = { audio, url, payload };
|
|
6017
|
+
const player = { audio, url: sourceUrl, revokeUrl, payload };
|
|
5904
6018
|
audio.onended = () => {
|
|
5905
6019
|
void this.finishPlayback(player, "finished");
|
|
5906
6020
|
};
|
|
5907
6021
|
audio.onerror = () => {
|
|
5908
6022
|
this.detachPlayer(player);
|
|
5909
|
-
logRuntimeError("voice.play", new Error("Audio playback failed"),
|
|
6023
|
+
logRuntimeError("voice.play", new Error("Audio playback failed"), debugDetails);
|
|
5910
6024
|
};
|
|
5911
6025
|
this.activePlayers.push(player);
|
|
5912
6026
|
while (this.activePlayers.length > MAX_CONCURRENT_PLAYERS) {
|
|
@@ -5918,8 +6032,7 @@ var VoiceRuntime = class {
|
|
|
5918
6032
|
void this.finishPlayback(oldest, "stopped");
|
|
5919
6033
|
}
|
|
5920
6034
|
logRuntimeDebug(this.debugLogging, "voice-play", {
|
|
5921
|
-
|
|
5922
|
-
bytes: bytes.byteLength,
|
|
6035
|
+
...debugDetails,
|
|
5923
6036
|
activePlayers: this.activePlayers.length
|
|
5924
6037
|
});
|
|
5925
6038
|
await audio.play();
|
|
@@ -5941,6 +6054,10 @@ var VoiceRuntime = class {
|
|
|
5941
6054
|
}
|
|
5942
6055
|
}
|
|
5943
6056
|
stopRecording() {
|
|
6057
|
+
if (this.listeningCaptureActive) {
|
|
6058
|
+
void this.stopListeningCapture();
|
|
6059
|
+
return;
|
|
6060
|
+
}
|
|
5944
6061
|
if (!this.mediaRecorder || this.mediaRecorder.state === "inactive") {
|
|
5945
6062
|
return;
|
|
5946
6063
|
}
|
|
@@ -5959,6 +6076,7 @@ var VoiceRuntime = class {
|
|
|
5959
6076
|
analyser.fftSize = 2048;
|
|
5960
6077
|
const source = context.createMediaStreamSource(stream);
|
|
5961
6078
|
source.connect(analyser);
|
|
6079
|
+
this.startListeningRecorder(stream);
|
|
5962
6080
|
this.listening = true;
|
|
5963
6081
|
this.listenContext = context;
|
|
5964
6082
|
this.listenAnalyser = analyser;
|
|
@@ -5986,14 +6104,14 @@ var VoiceRuntime = class {
|
|
|
5986
6104
|
this.speechEventTriggered = true;
|
|
5987
6105
|
await this.triggerSystemEvent("onSpeechDetected", null);
|
|
5988
6106
|
}
|
|
5989
|
-
if (!this.
|
|
5990
|
-
this.
|
|
6107
|
+
if (!this.listeningCaptureActive) {
|
|
6108
|
+
this.beginListeningCapture();
|
|
5991
6109
|
}
|
|
5992
|
-
} else if (this.
|
|
5993
|
-
this.
|
|
6110
|
+
} else if (this.listeningCaptureActive && this.lastSpeechAt > 0 && now - this.lastSpeechAt >= SILENCE_STOP_MS) {
|
|
6111
|
+
await this.stopListeningCapture();
|
|
5994
6112
|
this.lastSpeechAt = 0;
|
|
5995
6113
|
this.speechEventTriggered = false;
|
|
5996
|
-
} else if (!this.
|
|
6114
|
+
} else if (!this.listeningCaptureActive && now - this.listeningStartedAt >= MAX_LISTENING_SILENCE_MS) {
|
|
5997
6115
|
await this.stopListening();
|
|
5998
6116
|
return;
|
|
5999
6117
|
}
|
|
@@ -6026,9 +6144,13 @@ var VoiceRuntime = class {
|
|
|
6026
6144
|
this.listenContext = null;
|
|
6027
6145
|
this.listenAnalyser = null;
|
|
6028
6146
|
this.listenSource = null;
|
|
6029
|
-
if (this.
|
|
6030
|
-
this.
|
|
6147
|
+
if (this.listeningCaptureActive) {
|
|
6148
|
+
await this.stopListeningCapture();
|
|
6031
6149
|
}
|
|
6150
|
+
this.stopListeningRecorder();
|
|
6151
|
+
this.listeningPreRollChunks = [];
|
|
6152
|
+
this.listeningCaptureChunks = [];
|
|
6153
|
+
this.listeningCaptureActive = false;
|
|
6032
6154
|
this.recordingStartedFromListening = false;
|
|
6033
6155
|
this.releaseInputStreamIfIdle();
|
|
6034
6156
|
}
|
|
@@ -6108,6 +6230,9 @@ var VoiceRuntime = class {
|
|
|
6108
6230
|
}
|
|
6109
6231
|
async handleRecordingError(error) {
|
|
6110
6232
|
logRuntimeError("voice.recording", error);
|
|
6233
|
+
this.listeningCaptureActive = false;
|
|
6234
|
+
this.listeningCaptureChunks = [];
|
|
6235
|
+
this.recordingStartedFromListening = false;
|
|
6111
6236
|
await this.triggerSystemEvent("onRecordingError", this.normalizeErrorMessage(error));
|
|
6112
6237
|
}
|
|
6113
6238
|
async handleListeningError(error) {
|
|
@@ -6152,6 +6277,91 @@ var VoiceRuntime = class {
|
|
|
6152
6277
|
this.mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
6153
6278
|
return this.mediaStream;
|
|
6154
6279
|
}
|
|
6280
|
+
startListeningRecorder(stream) {
|
|
6281
|
+
if (this.listeningRecorder && this.listeningRecorder.state !== "inactive") {
|
|
6282
|
+
return;
|
|
6283
|
+
}
|
|
6284
|
+
if (typeof MediaRecorder === "undefined") {
|
|
6285
|
+
throw new Error("MediaRecorder is not supported in this browser");
|
|
6286
|
+
}
|
|
6287
|
+
const preferredMimeType = this.getPreferredRecordingMimeType();
|
|
6288
|
+
const options = preferredMimeType ? { mimeType: preferredMimeType } : void 0;
|
|
6289
|
+
const recorder = options ? new MediaRecorder(stream, options) : new MediaRecorder(stream);
|
|
6290
|
+
this.listeningRecorder = recorder;
|
|
6291
|
+
this.listeningRecorderMimeType = recorder.mimeType || preferredMimeType || "audio/webm";
|
|
6292
|
+
this.listeningPreRollChunks = [];
|
|
6293
|
+
this.listeningCaptureChunks = [];
|
|
6294
|
+
recorder.ondataavailable = (event) => {
|
|
6295
|
+
if (!event.data || event.data.size === 0) {
|
|
6296
|
+
return;
|
|
6297
|
+
}
|
|
6298
|
+
const timestamp = performance.now();
|
|
6299
|
+
if (this.listeningCaptureActive) {
|
|
6300
|
+
this.listeningCaptureChunks.push(event.data);
|
|
6301
|
+
return;
|
|
6302
|
+
}
|
|
6303
|
+
this.listeningPreRollChunks.push({ data: event.data, timestamp });
|
|
6304
|
+
const cutoff = timestamp - LISTENING_PRE_ROLL_MS;
|
|
6305
|
+
while (this.listeningPreRollChunks.length > 0 && this.listeningPreRollChunks[0].timestamp < cutoff) {
|
|
6306
|
+
this.listeningPreRollChunks.shift();
|
|
6307
|
+
}
|
|
6308
|
+
};
|
|
6309
|
+
recorder.onerror = (event) => {
|
|
6310
|
+
void this.handleRecordingError(event.error ?? new Error("Unknown listening recorder error"));
|
|
6311
|
+
};
|
|
6312
|
+
recorder.start(LISTENING_RECORDER_TIMESLICE_MS);
|
|
6313
|
+
}
|
|
6314
|
+
stopListeningRecorder() {
|
|
6315
|
+
if (!this.listeningRecorder) {
|
|
6316
|
+
return;
|
|
6317
|
+
}
|
|
6318
|
+
const recorder = this.listeningRecorder;
|
|
6319
|
+
this.listeningRecorder = null;
|
|
6320
|
+
recorder.ondataavailable = null;
|
|
6321
|
+
recorder.onerror = null;
|
|
6322
|
+
if (recorder.state !== "inactive") {
|
|
6323
|
+
recorder.stop();
|
|
6324
|
+
}
|
|
6325
|
+
}
|
|
6326
|
+
beginListeningCapture() {
|
|
6327
|
+
this.listeningCaptureActive = true;
|
|
6328
|
+
this.recordingStartedFromListening = true;
|
|
6329
|
+
this.listeningCaptureChunks = this.listeningPreRollChunks.map((chunk) => chunk.data);
|
|
6330
|
+
this.listeningPreRollChunks = [];
|
|
6331
|
+
logRuntimeDebug(this.debugLogging, "voice-recording-started", {
|
|
6332
|
+
mimeType: this.listeningRecorderMimeType,
|
|
6333
|
+
fromListening: true,
|
|
6334
|
+
preRollMs: LISTENING_PRE_ROLL_MS,
|
|
6335
|
+
preRollChunks: this.listeningCaptureChunks.length
|
|
6336
|
+
});
|
|
6337
|
+
void this.triggerSystemEvent("onRecordingStarted", null);
|
|
6338
|
+
}
|
|
6339
|
+
async stopListeningCapture() {
|
|
6340
|
+
if (!this.listeningCaptureActive) {
|
|
6341
|
+
return;
|
|
6342
|
+
}
|
|
6343
|
+
this.listeningCaptureActive = false;
|
|
6344
|
+
const chunks = [...this.listeningCaptureChunks];
|
|
6345
|
+
this.listeningCaptureChunks = [];
|
|
6346
|
+
try {
|
|
6347
|
+
const blob = new Blob(chunks, { type: this.listeningRecorderMimeType });
|
|
6348
|
+
const audioData = await blobToBase64(blob);
|
|
6349
|
+
logRuntimeDebug(this.debugLogging, "voice-recording-stopped", {
|
|
6350
|
+
mimeType: this.listeningRecorderMimeType,
|
|
6351
|
+
size: blob.size,
|
|
6352
|
+
fromListening: true,
|
|
6353
|
+
preRollMs: LISTENING_PRE_ROLL_MS
|
|
6354
|
+
});
|
|
6355
|
+
await this.triggerSystemEvent("onRecordingStopped", {
|
|
6356
|
+
audioData,
|
|
6357
|
+
mimeType: this.listeningRecorderMimeType
|
|
6358
|
+
});
|
|
6359
|
+
} catch (error) {
|
|
6360
|
+
await this.handleRecordingError(error);
|
|
6361
|
+
} finally {
|
|
6362
|
+
this.recordingStartedFromListening = false;
|
|
6363
|
+
}
|
|
6364
|
+
}
|
|
6155
6365
|
clearRecordingTimeout() {
|
|
6156
6366
|
if (this.recordingTimeoutId !== null) {
|
|
6157
6367
|
window.clearTimeout(this.recordingTimeoutId);
|
|
@@ -6162,6 +6372,9 @@ var VoiceRuntime = class {
|
|
|
6162
6372
|
if (this.listening) {
|
|
6163
6373
|
return;
|
|
6164
6374
|
}
|
|
6375
|
+
if (this.listeningRecorder && this.listeningRecorder.state !== "inactive") {
|
|
6376
|
+
return;
|
|
6377
|
+
}
|
|
6165
6378
|
if (this.mediaRecorder && this.mediaRecorder.state !== "inactive") {
|
|
6166
6379
|
return;
|
|
6167
6380
|
}
|
|
@@ -6189,7 +6402,9 @@ var VoiceRuntime = class {
|
|
|
6189
6402
|
}
|
|
6190
6403
|
player.audio.onended = null;
|
|
6191
6404
|
player.audio.onerror = null;
|
|
6192
|
-
|
|
6405
|
+
if (player.revokeUrl) {
|
|
6406
|
+
URL.revokeObjectURL(player.url);
|
|
6407
|
+
}
|
|
6193
6408
|
}
|
|
6194
6409
|
async finishPlayback(player, reason) {
|
|
6195
6410
|
if (!this.activePlayers.includes(player)) {
|
|
@@ -6203,7 +6418,9 @@ var VoiceRuntime = class {
|
|
|
6203
6418
|
await this.triggerSystemEvent(reason === "finished" ? "onPlayFinished" : "onPlayingStopped", payload);
|
|
6204
6419
|
}
|
|
6205
6420
|
async handlePageDeactivation() {
|
|
6206
|
-
const hadActiveRecording = Boolean(
|
|
6421
|
+
const hadActiveRecording = Boolean(
|
|
6422
|
+
this.mediaRecorder && this.mediaRecorder.state !== "inactive" || this.listeningCaptureActive
|
|
6423
|
+
);
|
|
6207
6424
|
const hadActiveListening = this.listening;
|
|
6208
6425
|
if (!hadActiveRecording && !hadActiveListening) {
|
|
6209
6426
|
return;
|
|
@@ -6352,7 +6569,7 @@ function renderGridLayout(env, node, cellsVisible, path, itemContext) {
|
|
|
6352
6569
|
|
|
6353
6570
|
// src/lib/widgets/conditional-container.ts
|
|
6354
6571
|
var renderConditionalContainer = (env, node, state, key, path, itemContext) => {
|
|
6355
|
-
const condition = typeof node.condition === "object" && node.condition !== null ? env.evaluateConditionExpression(node.condition, itemContext
|
|
6572
|
+
const condition = typeof node.condition === "object" && node.condition !== null ? env.evaluateConditionExpression(node.condition, itemContext ?? null) : state.condition ?? env.normalizeBoolean(node.condition, true);
|
|
6356
6573
|
const chosen = condition ? node.default : node.alternative;
|
|
6357
6574
|
const content = chosen ? env.renderNode(chosen, `${path}.${condition ? "default" : "alternative"}`, itemContext) : "";
|
|
6358
6575
|
const hasOwnBox = Boolean(
|
|
@@ -6585,10 +6802,12 @@ var renderButton = (env, node, state, key, _path, itemContext) => {
|
|
|
6585
6802
|
const enabled = state.enabled ?? env.normalizeBoolean(node.enabled, true);
|
|
6586
6803
|
const disabledAttribute = enabled ? "" : " disabled";
|
|
6587
6804
|
const text = env.escapeHtml(env.resolveTextValue(state.text ?? node.text, itemContext));
|
|
6805
|
+
const hintValue = typeof node.hint === "string" ? env.resolveTextValue(node.hint, itemContext) : "";
|
|
6806
|
+
const hintAttribute = hintValue ? ` title="${env.escapeHtml(hintValue)}"` : "";
|
|
6588
6807
|
const styleString = env.buildStyleString(node);
|
|
6589
6808
|
const styleAttribute = styleString ? ` style="${env.escapeHtml(styleString)}"` : "";
|
|
6590
6809
|
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>`;
|
|
6810
|
+
return `<button${common}${env.buildWidgetDataAttributes(key, node)} class="vjt-button" data-widget="button" type="${buttonType}"${styleAttribute}${hintAttribute}${disabledAttribute}>${text}</button>`;
|
|
6592
6811
|
};
|
|
6593
6812
|
|
|
6594
6813
|
// src/lib/widgets/checkbox.ts
|
|
@@ -7467,6 +7686,9 @@ var RuntimeRenderer = class {
|
|
|
7467
7686
|
activeModalId = null;
|
|
7468
7687
|
activeContextMenu = null;
|
|
7469
7688
|
activeConfirmModal = null;
|
|
7689
|
+
activeToasts = [];
|
|
7690
|
+
toastCounter = 0;
|
|
7691
|
+
toastTimeoutIds = /* @__PURE__ */ new Map();
|
|
7470
7692
|
lastPointer = { x: 24, y: 24 };
|
|
7471
7693
|
pendingResetModalIds = /* @__PURE__ */ new Set();
|
|
7472
7694
|
pendingScrollAction = null;
|
|
@@ -7544,6 +7766,7 @@ var RuntimeRenderer = class {
|
|
|
7544
7766
|
setVarValue: (name, value) => {
|
|
7545
7767
|
this.vars.set(name, value);
|
|
7546
7768
|
},
|
|
7769
|
+
resolveI18nValue: (value) => this.resolveI18nValue(value),
|
|
7547
7770
|
ensureWidgetState: (node, key) => this.ensureWidgetState(node, key),
|
|
7548
7771
|
resolveItemNodeKey: (node, listKey, index, fallbackPath) => this.resolveItemNodeKey(node, listKey, index, fallbackPath),
|
|
7549
7772
|
indexListElementNodes: (listNode, element, index) => this.indexListElementNodes(listNode, element, index)
|
|
@@ -7585,7 +7808,10 @@ var RuntimeRenderer = class {
|
|
|
7585
7808
|
scrollWidgetToBottom: (reference) => this.scrollWidgetToBottom(reference),
|
|
7586
7809
|
scrollWidgetToElementByIndex: (reference, index) => this.scrollWidgetToElementByIndex(reference, index),
|
|
7587
7810
|
playAudio: (value) => this.voiceRuntime.play(value),
|
|
7811
|
+
playStaticAudio: (url) => this.voiceRuntime.playStatic(url),
|
|
7588
7812
|
stopPlaying: () => this.voiceRuntime.stopPlaying(),
|
|
7813
|
+
showInfoToast: (message) => this.showToast("info", message),
|
|
7814
|
+
showErrorToast: (message) => this.showToast("error", message),
|
|
7589
7815
|
copyToClipboard: (value) => this.copyToClipboard(value),
|
|
7590
7816
|
selectFile: (args) => this.selectFile(args),
|
|
7591
7817
|
confirmModal: (args, inputValue) => this.confirmModal(args, inputValue),
|
|
@@ -7705,6 +7931,10 @@ var RuntimeRenderer = class {
|
|
|
7705
7931
|
source.close();
|
|
7706
7932
|
}
|
|
7707
7933
|
this.eventSources.length = 0;
|
|
7934
|
+
for (const timeoutId of this.toastTimeoutIds.values()) {
|
|
7935
|
+
window.clearTimeout(timeoutId);
|
|
7936
|
+
}
|
|
7937
|
+
this.toastTimeoutIds.clear();
|
|
7708
7938
|
this.voiceRuntime.dispose();
|
|
7709
7939
|
};
|
|
7710
7940
|
}
|
|
@@ -7905,6 +8135,21 @@ var RuntimeRenderer = class {
|
|
|
7905
8135
|
this.activeConfirmModal = null;
|
|
7906
8136
|
activeModal.resolve(confirmed);
|
|
7907
8137
|
}
|
|
8138
|
+
showToast(kind, message) {
|
|
8139
|
+
const text = message.trim();
|
|
8140
|
+
if (!text) {
|
|
8141
|
+
return;
|
|
8142
|
+
}
|
|
8143
|
+
const toastId = ++this.toastCounter;
|
|
8144
|
+
this.activeToasts.push({ id: toastId, kind, text });
|
|
8145
|
+
void this.rerenderRoot();
|
|
8146
|
+
const timeoutId = window.setTimeout(() => {
|
|
8147
|
+
this.toastTimeoutIds.delete(toastId);
|
|
8148
|
+
this.activeToasts = this.activeToasts.filter((entry) => entry.id !== toastId);
|
|
8149
|
+
void this.rerenderRoot();
|
|
8150
|
+
}, 3600);
|
|
8151
|
+
this.toastTimeoutIds.set(toastId, timeoutId);
|
|
8152
|
+
}
|
|
7908
8153
|
reindexStaticTree() {
|
|
7909
8154
|
this.nodeByKey.clear();
|
|
7910
8155
|
this.nodeById.clear();
|
|
@@ -8325,7 +8570,8 @@ var RuntimeRenderer = class {
|
|
|
8325
8570
|
name: node.name,
|
|
8326
8571
|
enabled: normalizeBoolean(node.enabled, true),
|
|
8327
8572
|
definitionSignature: buildDefinitionSignature(node.elements ?? []),
|
|
8328
|
-
elements: deepClone(node.elements ?? [])
|
|
8573
|
+
elements: deepClone(node.elements ?? []),
|
|
8574
|
+
selected: toFiniteNumber3(node.item) ?? toFiniteNumber3(node.selected) ?? -1
|
|
8329
8575
|
};
|
|
8330
8576
|
break;
|
|
8331
8577
|
case "listbox":
|
|
@@ -8423,6 +8669,7 @@ var RuntimeRenderer = class {
|
|
|
8423
8669
|
if (state.widget === "list" || state.widget === "grid-view") {
|
|
8424
8670
|
this.clearListElementState(state.key);
|
|
8425
8671
|
state.elements = deepClone(node.elements ?? []);
|
|
8672
|
+
state.selected = toFiniteNumber3(node.item) ?? toFiniteNumber3(node.selected) ?? state.selected ?? -1;
|
|
8426
8673
|
state.definitionSignature = nextSignature;
|
|
8427
8674
|
}
|
|
8428
8675
|
}
|
|
@@ -8602,7 +8849,18 @@ var RuntimeRenderer = class {
|
|
|
8602
8849
|
const modalMarkup = this.activeModalId ? renderModalOverlay(env, this.activeModalId) : "";
|
|
8603
8850
|
const menuMarkup = this.activeContextMenu ? renderContextMenuOverlay(env, this.activeContextMenu) : "";
|
|
8604
8851
|
const confirmMarkup = this.activeConfirmModal ? renderConfirmModalOverlay(env, this.activeConfirmModal) : "";
|
|
8605
|
-
|
|
8852
|
+
const toastMarkup = this.renderToastOverlay();
|
|
8853
|
+
return `${modalMarkup}${menuMarkup}${confirmMarkup}${toastMarkup}`;
|
|
8854
|
+
}
|
|
8855
|
+
renderToastOverlay() {
|
|
8856
|
+
if (this.activeToasts.length === 0) {
|
|
8857
|
+
return "";
|
|
8858
|
+
}
|
|
8859
|
+
const items = this.activeToasts.map((toast) => {
|
|
8860
|
+
const kindClass = toast.kind === "error" ? " vjt-toast--error" : " vjt-toast--info";
|
|
8861
|
+
return `<div class="vjt-toast${kindClass}" role="status" aria-live="polite">${escapeHtml(toast.text)}</div>`;
|
|
8862
|
+
}).join("");
|
|
8863
|
+
return `<div class="vjt-toast-stack">${items}</div>`;
|
|
8606
8864
|
}
|
|
8607
8865
|
updatePointerFromEvent(event) {
|
|
8608
8866
|
this.lastPointer = updatePointerFromMouseEvent(event);
|
|
@@ -8712,6 +8970,7 @@ var RuntimeRenderer = class {
|
|
|
8712
8970
|
case "grid-view":
|
|
8713
8971
|
this.clearListElementState(state.key);
|
|
8714
8972
|
state.elements = [];
|
|
8973
|
+
state.selected = -1;
|
|
8715
8974
|
break;
|
|
8716
8975
|
case "listbox":
|
|
8717
8976
|
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 listeningRecorder;
|
|
20
|
+
private listeningRecorderMimeType;
|
|
21
|
+
private listeningPreRollChunks;
|
|
22
|
+
private listeningCaptureChunks;
|
|
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,10 @@ export declare class VoiceRuntime {
|
|
|
37
44
|
private normalizePlayablePayload;
|
|
38
45
|
private getPreferredRecordingMimeType;
|
|
39
46
|
private ensureInputStream;
|
|
47
|
+
private startListeningRecorder;
|
|
48
|
+
private stopListeningRecorder;
|
|
49
|
+
private beginListeningCapture;
|
|
50
|
+
private stopListeningCapture;
|
|
40
51
|
private clearRecordingTimeout;
|
|
41
52
|
private releaseInputStreamIfIdle;
|
|
42
53
|
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
|
+
}
|