@vortexm/vjt 0.1.12 → 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 +373 -48
- package/dist/lib/action-runtime.d.ts +6 -0
- package/dist/lib/references.d.ts +6 -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,30 +4288,93 @@ var ReferenceRuntime = class {
|
|
|
4274
4288
|
constructor(host) {
|
|
4275
4289
|
this.host = host;
|
|
4276
4290
|
}
|
|
4277
|
-
|
|
4278
|
-
|
|
4291
|
+
getListSelectionIndex(listId) {
|
|
4292
|
+
const listState = this.host.stateByKey.get(listId) ?? this.host.stateById.get(listId);
|
|
4293
|
+
return listState?.selected ?? -1;
|
|
4279
4294
|
}
|
|
4280
|
-
|
|
4281
|
-
if (
|
|
4282
|
-
|
|
4295
|
+
resolveScopedRootReference(scope, path, currentValue, responseValue, inputValue) {
|
|
4296
|
+
if (scope === "current") {
|
|
4297
|
+
if (path.length === 0) {
|
|
4298
|
+
return currentValue;
|
|
4299
|
+
}
|
|
4300
|
+
return this.resolveCurrentReference(path.join("."), currentValue);
|
|
4283
4301
|
}
|
|
4284
|
-
if (
|
|
4285
|
-
return inputValue;
|
|
4302
|
+
if (scope === "input") {
|
|
4303
|
+
return path.length === 0 ? inputValue : readPath(inputValue, path);
|
|
4286
4304
|
}
|
|
4287
|
-
if (
|
|
4288
|
-
return responseValue;
|
|
4305
|
+
if (scope === "message" || scope === "response") {
|
|
4306
|
+
return path.length === 0 ? responseValue : readPath(responseValue, path);
|
|
4289
4307
|
}
|
|
4290
|
-
if (
|
|
4291
|
-
return
|
|
4308
|
+
if (path.length === 0) {
|
|
4309
|
+
return this.host.getVarsSnapshot();
|
|
4292
4310
|
}
|
|
4293
|
-
|
|
4294
|
-
|
|
4311
|
+
return this.readVarReference(path.join("."));
|
|
4312
|
+
}
|
|
4313
|
+
readVarReference(path) {
|
|
4314
|
+
const [rootKey, ...rest] = path.split(".").filter(Boolean);
|
|
4315
|
+
if (!rootKey) {
|
|
4316
|
+
return void 0;
|
|
4295
4317
|
}
|
|
4296
|
-
|
|
4297
|
-
|
|
4318
|
+
const rootValue = this.host.getVarValue(rootKey);
|
|
4319
|
+
if (rest.length === 0) {
|
|
4320
|
+
return rootValue;
|
|
4321
|
+
}
|
|
4322
|
+
return readPath(rootValue, rest);
|
|
4323
|
+
}
|
|
4324
|
+
writeVarReference(path, value) {
|
|
4325
|
+
const [rootKey, ...rest] = path.split(".").filter(Boolean);
|
|
4326
|
+
if (!rootKey) {
|
|
4327
|
+
return;
|
|
4298
4328
|
}
|
|
4299
|
-
if (
|
|
4300
|
-
|
|
4329
|
+
if (rest.length === 0) {
|
|
4330
|
+
this.host.setVarValue(rootKey, value);
|
|
4331
|
+
return;
|
|
4332
|
+
}
|
|
4333
|
+
const currentRoot = this.host.getVarValue(rootKey);
|
|
4334
|
+
const nextRoot = isPlainObject2(currentRoot) ? structuredClone(currentRoot) : {};
|
|
4335
|
+
if (writePath(nextRoot, rest, value)) {
|
|
4336
|
+
this.host.setVarValue(rootKey, nextRoot);
|
|
4337
|
+
}
|
|
4338
|
+
}
|
|
4339
|
+
isEmptyReference(reference, currentValue, responseValue, inputValue) {
|
|
4340
|
+
return isReferenceValueEmpty(this.resolveReference(reference, currentValue, responseValue, inputValue));
|
|
4341
|
+
}
|
|
4342
|
+
resolveReference(reference, currentValue, responseValue, inputValue = currentValue) {
|
|
4343
|
+
if (reference === "current" || reference.startsWith("current.")) {
|
|
4344
|
+
return this.resolveScopedRootReference(
|
|
4345
|
+
"current",
|
|
4346
|
+
reference === "current" ? [] : reference.slice(8).split(".").filter(Boolean),
|
|
4347
|
+
currentValue,
|
|
4348
|
+
responseValue,
|
|
4349
|
+
inputValue
|
|
4350
|
+
);
|
|
4351
|
+
}
|
|
4352
|
+
if (reference === "input" || reference.startsWith("input.")) {
|
|
4353
|
+
return this.resolveScopedRootReference(
|
|
4354
|
+
"input",
|
|
4355
|
+
reference === "input" ? [] : reference.slice(6).split(".").filter(Boolean),
|
|
4356
|
+
currentValue,
|
|
4357
|
+
responseValue,
|
|
4358
|
+
inputValue
|
|
4359
|
+
);
|
|
4360
|
+
}
|
|
4361
|
+
if (reference === "message" || reference.startsWith("message.")) {
|
|
4362
|
+
return this.resolveScopedRootReference(
|
|
4363
|
+
"message",
|
|
4364
|
+
reference === "message" ? [] : reference.slice(8).split(".").filter(Boolean),
|
|
4365
|
+
currentValue,
|
|
4366
|
+
responseValue,
|
|
4367
|
+
inputValue
|
|
4368
|
+
);
|
|
4369
|
+
}
|
|
4370
|
+
if (reference === "response" || reference.startsWith("response.")) {
|
|
4371
|
+
return this.resolveScopedRootReference(
|
|
4372
|
+
"response",
|
|
4373
|
+
reference === "response" ? [] : reference.slice(9).split(".").filter(Boolean),
|
|
4374
|
+
currentValue,
|
|
4375
|
+
responseValue,
|
|
4376
|
+
inputValue
|
|
4377
|
+
);
|
|
4301
4378
|
}
|
|
4302
4379
|
if (reference.startsWith("cookies.")) {
|
|
4303
4380
|
return getCookieValue(reference.slice(8));
|
|
@@ -4308,8 +4385,14 @@ var ReferenceRuntime = class {
|
|
|
4308
4385
|
if (reference.startsWith("url.")) {
|
|
4309
4386
|
return getUrlParamValue(reference.slice(4));
|
|
4310
4387
|
}
|
|
4311
|
-
if (reference.startsWith("vars.")) {
|
|
4312
|
-
return this.
|
|
4388
|
+
if (reference === "vars" || reference.startsWith("vars.")) {
|
|
4389
|
+
return this.resolveScopedRootReference(
|
|
4390
|
+
"vars",
|
|
4391
|
+
reference === "vars" ? [] : reference.slice(5).split(".").filter(Boolean),
|
|
4392
|
+
currentValue,
|
|
4393
|
+
responseValue,
|
|
4394
|
+
inputValue
|
|
4395
|
+
);
|
|
4313
4396
|
}
|
|
4314
4397
|
const [widgetId, ...rest] = reference.split(".");
|
|
4315
4398
|
const state = this.host.stateById.get(widgetId);
|
|
@@ -4323,6 +4406,9 @@ var ReferenceRuntime = class {
|
|
|
4323
4406
|
}
|
|
4324
4407
|
resolveCurrentReference(reference, currentValue) {
|
|
4325
4408
|
if (this.isListElementContext(currentValue)) {
|
|
4409
|
+
if (reference === "isSelected") {
|
|
4410
|
+
return currentValue.index === this.getListSelectionIndex(currentValue.listId);
|
|
4411
|
+
}
|
|
4326
4412
|
const resolvedDescriptor = this.resolveListDescriptorNode(currentValue.descriptor, currentValue);
|
|
4327
4413
|
const firstPart = reference.split(".")[0];
|
|
4328
4414
|
const namedNode = this.findNamedWidget(resolvedDescriptor, firstPart, currentValue);
|
|
@@ -4330,6 +4416,9 @@ var ReferenceRuntime = class {
|
|
|
4330
4416
|
const widgetKey = this.host.resolveItemNodeKey(namedNode, currentValue.listId, currentValue.index, `named.${firstPart}`);
|
|
4331
4417
|
const state = this.host.ensureWidgetState(namedNode, widgetKey);
|
|
4332
4418
|
const rest = reference.split(".").slice(1).join(".");
|
|
4419
|
+
if (rest === "isSelected") {
|
|
4420
|
+
return currentValue.index === this.getListSelectionIndex(currentValue.listId);
|
|
4421
|
+
}
|
|
4333
4422
|
return rest ? this.readWidgetField(state, rest) : state;
|
|
4334
4423
|
}
|
|
4335
4424
|
const directDescriptorValue = readPath(currentValue.descriptor, reference.split("."));
|
|
@@ -4355,9 +4444,9 @@ var ReferenceRuntime = class {
|
|
|
4355
4444
|
readWidgetField(state, field) {
|
|
4356
4445
|
switch (field) {
|
|
4357
4446
|
case "text":
|
|
4358
|
-
return state.text ?? "";
|
|
4447
|
+
return resolveInlineI18nValue(this.host, state.text ?? "");
|
|
4359
4448
|
case "placeholder":
|
|
4360
|
-
return state.placeholder ?? "";
|
|
4449
|
+
return resolveInlineI18nValue(this.host, state.placeholder ?? "");
|
|
4361
4450
|
case "checked":
|
|
4362
4451
|
return state.checked ?? false;
|
|
4363
4452
|
case "enabled":
|
|
@@ -4368,6 +4457,8 @@ var ReferenceRuntime = class {
|
|
|
4368
4457
|
return state.condition ?? false;
|
|
4369
4458
|
case "selected":
|
|
4370
4459
|
return state.selected ?? 0;
|
|
4460
|
+
case "item":
|
|
4461
|
+
return state.selected ?? (state.widget === "list" || state.widget === "grid-view" ? -1 : 0);
|
|
4371
4462
|
case "value":
|
|
4372
4463
|
if (state.widget === "combobox") {
|
|
4373
4464
|
const index = state.selected ?? 0;
|
|
@@ -4389,9 +4480,9 @@ var ReferenceRuntime = class {
|
|
|
4389
4480
|
case "isHidden":
|
|
4390
4481
|
return state.isHidden ?? true;
|
|
4391
4482
|
case "url":
|
|
4392
|
-
return state.url ?? "";
|
|
4483
|
+
return resolveInlineI18nValue(this.host, state.url ?? "");
|
|
4393
4484
|
case "base64":
|
|
4394
|
-
return state.base64 ?? "";
|
|
4485
|
+
return resolveInlineI18nValue(this.host, state.base64 ?? "");
|
|
4395
4486
|
case "elements":
|
|
4396
4487
|
if (state.widget === "list") {
|
|
4397
4488
|
return (state.elements ?? []).map((descriptor, index) => ({
|
|
@@ -4448,7 +4539,7 @@ var ReferenceRuntime = class {
|
|
|
4448
4539
|
return;
|
|
4449
4540
|
}
|
|
4450
4541
|
if (reference.startsWith("vars.")) {
|
|
4451
|
-
this.
|
|
4542
|
+
this.writeVarReference(reference.slice(5), inputValue);
|
|
4452
4543
|
return;
|
|
4453
4544
|
}
|
|
4454
4545
|
const [widgetId, ...rest] = reference.split(".");
|
|
@@ -4473,6 +4564,9 @@ var ReferenceRuntime = class {
|
|
|
4473
4564
|
case "selected":
|
|
4474
4565
|
state.selected = toFiniteNumber(inputValue) ?? 0;
|
|
4475
4566
|
break;
|
|
4567
|
+
case "item":
|
|
4568
|
+
state.selected = toFiniteNumber(inputValue) ?? 0;
|
|
4569
|
+
break;
|
|
4476
4570
|
case "isHidden":
|
|
4477
4571
|
state.isHidden = Boolean(inputValue);
|
|
4478
4572
|
break;
|
|
@@ -4539,15 +4633,25 @@ var ReferenceRuntime = class {
|
|
|
4539
4633
|
}
|
|
4540
4634
|
resolveMappedValue(template, currentValue, responseValue, inputValue = currentValue) {
|
|
4541
4635
|
if (typeof template === "string") {
|
|
4542
|
-
if (template.startsWith("
|
|
4543
|
-
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);
|
|
4544
4647
|
}
|
|
4545
|
-
if (
|
|
4546
|
-
return
|
|
4648
|
+
if (resolvedTemplate.includes("$ref:")) {
|
|
4649
|
+
return resolvedTemplate.replaceAll(/\$ref:([A-Za-z0-9_.]+)/g, (_match, ref) => {
|
|
4547
4650
|
const resolved = this.resolveReference(ref, currentValue, responseValue, inputValue);
|
|
4548
4651
|
return stringifyReferenceValue(resolved);
|
|
4549
4652
|
});
|
|
4550
4653
|
}
|
|
4654
|
+
return resolvedTemplate;
|
|
4551
4655
|
}
|
|
4552
4656
|
if (Array.isArray(template)) {
|
|
4553
4657
|
return template.map((entry) => this.resolveMappedValue(entry, currentValue, responseValue, inputValue));
|
|
@@ -4683,7 +4787,10 @@ var ActionRuntime = class {
|
|
|
4683
4787
|
scrollWidgetToBottom;
|
|
4684
4788
|
scrollWidgetToElementByIndex;
|
|
4685
4789
|
playAudio;
|
|
4790
|
+
playStaticAudio;
|
|
4686
4791
|
stopPlaying;
|
|
4792
|
+
showInfoToast;
|
|
4793
|
+
showErrorToast;
|
|
4687
4794
|
copyToClipboard;
|
|
4688
4795
|
selectFile;
|
|
4689
4796
|
confirmModal;
|
|
@@ -4725,7 +4832,10 @@ var ActionRuntime = class {
|
|
|
4725
4832
|
this.scrollWidgetToBottom = options.scrollWidgetToBottom;
|
|
4726
4833
|
this.scrollWidgetToElementByIndex = options.scrollWidgetToElementByIndex;
|
|
4727
4834
|
this.playAudio = options.playAudio;
|
|
4835
|
+
this.playStaticAudio = options.playStaticAudio;
|
|
4728
4836
|
this.stopPlaying = options.stopPlaying;
|
|
4837
|
+
this.showInfoToast = options.showInfoToast;
|
|
4838
|
+
this.showErrorToast = options.showErrorToast;
|
|
4729
4839
|
this.copyToClipboard = options.copyToClipboard;
|
|
4730
4840
|
this.selectFile = options.selectFile;
|
|
4731
4841
|
this.confirmModal = options.confirmModal;
|
|
@@ -4846,14 +4956,37 @@ var ActionRuntime = class {
|
|
|
4846
4956
|
await this.navigateTo(name.slice(9));
|
|
4847
4957
|
return null;
|
|
4848
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
|
+
}
|
|
4849
4970
|
if (name.startsWith("play ")) {
|
|
4850
4971
|
await this.playAudio(this.resolveReference(name.slice(5), context.currentValue, context.responseValue, inputValue));
|
|
4851
4972
|
return null;
|
|
4852
4973
|
}
|
|
4974
|
+
if (name.startsWith("playStatic ")) {
|
|
4975
|
+
await this.playStaticAudio(name.slice(11).trim());
|
|
4976
|
+
return null;
|
|
4977
|
+
}
|
|
4853
4978
|
if (name === "copyToClipboard") {
|
|
4854
4979
|
await this.copyToClipboard(inputValue);
|
|
4855
4980
|
return inputValue;
|
|
4856
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
|
+
}
|
|
4857
4990
|
if (name === "selectFile") {
|
|
4858
4991
|
return this.selectFile(action.args);
|
|
4859
4992
|
}
|
|
@@ -4937,6 +5070,18 @@ var ActionRuntime = class {
|
|
|
4937
5070
|
this.moveCursorToEnd(name.slice(16));
|
|
4938
5071
|
return null;
|
|
4939
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
|
+
}
|
|
4940
5085
|
if (name.startsWith("scrollToTop ")) {
|
|
4941
5086
|
this.scrollWidgetToTop(name.slice(12));
|
|
4942
5087
|
return inputValue;
|
|
@@ -5044,7 +5189,8 @@ var ActionRuntime = class {
|
|
|
5044
5189
|
if (name.startsWith("append ")) {
|
|
5045
5190
|
const reference = name.slice(7);
|
|
5046
5191
|
const currentText = this.resolveReference(reference, context.currentValue, context.responseValue, inputValue);
|
|
5047
|
-
const
|
|
5192
|
+
const appendValue = this.resolveMappedValue(action.args, context.currentValue, context.responseValue, inputValue);
|
|
5193
|
+
const nextValue = `${this.stringifyValue(currentText)}${this.stringifyValue(appendValue)}`;
|
|
5048
5194
|
this.assignReference(reference, nextValue, context.currentValue);
|
|
5049
5195
|
return nextValue;
|
|
5050
5196
|
}
|
|
@@ -5471,7 +5617,7 @@ function updatePointerFromMouseEvent(event) {
|
|
|
5471
5617
|
};
|
|
5472
5618
|
}
|
|
5473
5619
|
function getListElementContextFromElement(element, stateByKey) {
|
|
5474
|
-
const listElement = element.closest(".vjt-list-element");
|
|
5620
|
+
const listElement = element.closest(".vjt-list-element, .vjt-grid-cell");
|
|
5475
5621
|
if (!(listElement instanceof HTMLElement)) {
|
|
5476
5622
|
return null;
|
|
5477
5623
|
}
|
|
@@ -5709,6 +5855,10 @@ var DEFAULT_STYLE_MAP = {
|
|
|
5709
5855
|
spoilerFrameDark: "border: 1px solid rgba(109,143,179,0.2); background: rgba(255,255,255,0.035); border-radius: 10px;",
|
|
5710
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;",
|
|
5711
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;",
|
|
5712
5862
|
employeeLinkLight: "display:flex; align-items:center; width:100%; height:100%; color:#54749a; font-weight:600;",
|
|
5713
5863
|
employeeLinkDark: "display:flex; align-items:center; width:100%; height:100%; color:#b8d0ea; font-weight:600;"
|
|
5714
5864
|
};
|
|
@@ -5719,6 +5869,8 @@ var MAX_RECORDING_MS = 10 * 60 * 1e3;
|
|
|
5719
5869
|
var MAX_LISTENING_SILENCE_MS = 60 * 60 * 1e3;
|
|
5720
5870
|
var SILENCE_STOP_MS = 2e3;
|
|
5721
5871
|
var DEFAULT_SPEECH_THRESHOLD = 0.035;
|
|
5872
|
+
var LISTENING_PRE_ROLL_MS = 300;
|
|
5873
|
+
var LISTENING_RECORDER_TIMESLICE_MS = 100;
|
|
5722
5874
|
var RECORDING_MIME_CANDIDATES = [
|
|
5723
5875
|
"audio/webm;codecs=opus",
|
|
5724
5876
|
"audio/webm",
|
|
@@ -5799,6 +5951,11 @@ var VoiceRuntime = class {
|
|
|
5799
5951
|
listenAnalyser = null;
|
|
5800
5952
|
listenSource = null;
|
|
5801
5953
|
listenFrameId = null;
|
|
5954
|
+
listeningRecorder = null;
|
|
5955
|
+
listeningRecorderMimeType = "audio/webm";
|
|
5956
|
+
listeningPreRollChunks = [];
|
|
5957
|
+
listeningCaptureChunks = [];
|
|
5958
|
+
listeningCaptureActive = false;
|
|
5802
5959
|
lastSpeechAt = 0;
|
|
5803
5960
|
listeningStartedAt = 0;
|
|
5804
5961
|
recordingStartedFromListening = false;
|
|
@@ -5833,15 +5990,37 @@ var VoiceRuntime = class {
|
|
|
5833
5990
|
copy.set(bytes);
|
|
5834
5991
|
const blob = new Blob([copy.buffer], { type: payload.mimeType });
|
|
5835
5992
|
const url = URL.createObjectURL(blob);
|
|
5836
|
-
|
|
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);
|
|
5837
6016
|
audio.preload = "auto";
|
|
5838
|
-
const player = { audio, url, payload };
|
|
6017
|
+
const player = { audio, url: sourceUrl, revokeUrl, payload };
|
|
5839
6018
|
audio.onended = () => {
|
|
5840
6019
|
void this.finishPlayback(player, "finished");
|
|
5841
6020
|
};
|
|
5842
6021
|
audio.onerror = () => {
|
|
5843
6022
|
this.detachPlayer(player);
|
|
5844
|
-
logRuntimeError("voice.play", new Error("Audio playback failed"),
|
|
6023
|
+
logRuntimeError("voice.play", new Error("Audio playback failed"), debugDetails);
|
|
5845
6024
|
};
|
|
5846
6025
|
this.activePlayers.push(player);
|
|
5847
6026
|
while (this.activePlayers.length > MAX_CONCURRENT_PLAYERS) {
|
|
@@ -5853,8 +6032,7 @@ var VoiceRuntime = class {
|
|
|
5853
6032
|
void this.finishPlayback(oldest, "stopped");
|
|
5854
6033
|
}
|
|
5855
6034
|
logRuntimeDebug(this.debugLogging, "voice-play", {
|
|
5856
|
-
|
|
5857
|
-
bytes: bytes.byteLength,
|
|
6035
|
+
...debugDetails,
|
|
5858
6036
|
activePlayers: this.activePlayers.length
|
|
5859
6037
|
});
|
|
5860
6038
|
await audio.play();
|
|
@@ -5876,6 +6054,10 @@ var VoiceRuntime = class {
|
|
|
5876
6054
|
}
|
|
5877
6055
|
}
|
|
5878
6056
|
stopRecording() {
|
|
6057
|
+
if (this.listeningCaptureActive) {
|
|
6058
|
+
void this.stopListeningCapture();
|
|
6059
|
+
return;
|
|
6060
|
+
}
|
|
5879
6061
|
if (!this.mediaRecorder || this.mediaRecorder.state === "inactive") {
|
|
5880
6062
|
return;
|
|
5881
6063
|
}
|
|
@@ -5894,6 +6076,7 @@ var VoiceRuntime = class {
|
|
|
5894
6076
|
analyser.fftSize = 2048;
|
|
5895
6077
|
const source = context.createMediaStreamSource(stream);
|
|
5896
6078
|
source.connect(analyser);
|
|
6079
|
+
this.startListeningRecorder(stream);
|
|
5897
6080
|
this.listening = true;
|
|
5898
6081
|
this.listenContext = context;
|
|
5899
6082
|
this.listenAnalyser = analyser;
|
|
@@ -5921,14 +6104,14 @@ var VoiceRuntime = class {
|
|
|
5921
6104
|
this.speechEventTriggered = true;
|
|
5922
6105
|
await this.triggerSystemEvent("onSpeechDetected", null);
|
|
5923
6106
|
}
|
|
5924
|
-
if (!this.
|
|
5925
|
-
this.
|
|
6107
|
+
if (!this.listeningCaptureActive) {
|
|
6108
|
+
this.beginListeningCapture();
|
|
5926
6109
|
}
|
|
5927
|
-
} else if (this.
|
|
5928
|
-
this.
|
|
6110
|
+
} else if (this.listeningCaptureActive && this.lastSpeechAt > 0 && now - this.lastSpeechAt >= SILENCE_STOP_MS) {
|
|
6111
|
+
await this.stopListeningCapture();
|
|
5929
6112
|
this.lastSpeechAt = 0;
|
|
5930
6113
|
this.speechEventTriggered = false;
|
|
5931
|
-
} else if (!this.
|
|
6114
|
+
} else if (!this.listeningCaptureActive && now - this.listeningStartedAt >= MAX_LISTENING_SILENCE_MS) {
|
|
5932
6115
|
await this.stopListening();
|
|
5933
6116
|
return;
|
|
5934
6117
|
}
|
|
@@ -5961,9 +6144,13 @@ var VoiceRuntime = class {
|
|
|
5961
6144
|
this.listenContext = null;
|
|
5962
6145
|
this.listenAnalyser = null;
|
|
5963
6146
|
this.listenSource = null;
|
|
5964
|
-
if (this.
|
|
5965
|
-
this.
|
|
6147
|
+
if (this.listeningCaptureActive) {
|
|
6148
|
+
await this.stopListeningCapture();
|
|
5966
6149
|
}
|
|
6150
|
+
this.stopListeningRecorder();
|
|
6151
|
+
this.listeningPreRollChunks = [];
|
|
6152
|
+
this.listeningCaptureChunks = [];
|
|
6153
|
+
this.listeningCaptureActive = false;
|
|
5967
6154
|
this.recordingStartedFromListening = false;
|
|
5968
6155
|
this.releaseInputStreamIfIdle();
|
|
5969
6156
|
}
|
|
@@ -6043,6 +6230,9 @@ var VoiceRuntime = class {
|
|
|
6043
6230
|
}
|
|
6044
6231
|
async handleRecordingError(error) {
|
|
6045
6232
|
logRuntimeError("voice.recording", error);
|
|
6233
|
+
this.listeningCaptureActive = false;
|
|
6234
|
+
this.listeningCaptureChunks = [];
|
|
6235
|
+
this.recordingStartedFromListening = false;
|
|
6046
6236
|
await this.triggerSystemEvent("onRecordingError", this.normalizeErrorMessage(error));
|
|
6047
6237
|
}
|
|
6048
6238
|
async handleListeningError(error) {
|
|
@@ -6087,6 +6277,91 @@ var VoiceRuntime = class {
|
|
|
6087
6277
|
this.mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
6088
6278
|
return this.mediaStream;
|
|
6089
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
|
+
}
|
|
6090
6365
|
clearRecordingTimeout() {
|
|
6091
6366
|
if (this.recordingTimeoutId !== null) {
|
|
6092
6367
|
window.clearTimeout(this.recordingTimeoutId);
|
|
@@ -6097,6 +6372,9 @@ var VoiceRuntime = class {
|
|
|
6097
6372
|
if (this.listening) {
|
|
6098
6373
|
return;
|
|
6099
6374
|
}
|
|
6375
|
+
if (this.listeningRecorder && this.listeningRecorder.state !== "inactive") {
|
|
6376
|
+
return;
|
|
6377
|
+
}
|
|
6100
6378
|
if (this.mediaRecorder && this.mediaRecorder.state !== "inactive") {
|
|
6101
6379
|
return;
|
|
6102
6380
|
}
|
|
@@ -6124,7 +6402,9 @@ var VoiceRuntime = class {
|
|
|
6124
6402
|
}
|
|
6125
6403
|
player.audio.onended = null;
|
|
6126
6404
|
player.audio.onerror = null;
|
|
6127
|
-
|
|
6405
|
+
if (player.revokeUrl) {
|
|
6406
|
+
URL.revokeObjectURL(player.url);
|
|
6407
|
+
}
|
|
6128
6408
|
}
|
|
6129
6409
|
async finishPlayback(player, reason) {
|
|
6130
6410
|
if (!this.activePlayers.includes(player)) {
|
|
@@ -6138,7 +6418,9 @@ var VoiceRuntime = class {
|
|
|
6138
6418
|
await this.triggerSystemEvent(reason === "finished" ? "onPlayFinished" : "onPlayingStopped", payload);
|
|
6139
6419
|
}
|
|
6140
6420
|
async handlePageDeactivation() {
|
|
6141
|
-
const hadActiveRecording = Boolean(
|
|
6421
|
+
const hadActiveRecording = Boolean(
|
|
6422
|
+
this.mediaRecorder && this.mediaRecorder.state !== "inactive" || this.listeningCaptureActive
|
|
6423
|
+
);
|
|
6142
6424
|
const hadActiveListening = this.listening;
|
|
6143
6425
|
if (!hadActiveRecording && !hadActiveListening) {
|
|
6144
6426
|
return;
|
|
@@ -6287,7 +6569,7 @@ function renderGridLayout(env, node, cellsVisible, path, itemContext) {
|
|
|
6287
6569
|
|
|
6288
6570
|
// src/lib/widgets/conditional-container.ts
|
|
6289
6571
|
var renderConditionalContainer = (env, node, state, key, path, itemContext) => {
|
|
6290
|
-
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);
|
|
6291
6573
|
const chosen = condition ? node.default : node.alternative;
|
|
6292
6574
|
const content = chosen ? env.renderNode(chosen, `${path}.${condition ? "default" : "alternative"}`, itemContext) : "";
|
|
6293
6575
|
const hasOwnBox = Boolean(
|
|
@@ -6520,10 +6802,12 @@ var renderButton = (env, node, state, key, _path, itemContext) => {
|
|
|
6520
6802
|
const enabled = state.enabled ?? env.normalizeBoolean(node.enabled, true);
|
|
6521
6803
|
const disabledAttribute = enabled ? "" : " disabled";
|
|
6522
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)}"` : "";
|
|
6523
6807
|
const styleString = env.buildStyleString(node);
|
|
6524
6808
|
const styleAttribute = styleString ? ` style="${env.escapeHtml(styleString)}"` : "";
|
|
6525
6809
|
const buttonType = env.escapeHtml(node.type === "submit" ? "submit" : "button");
|
|
6526
|
-
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>`;
|
|
6527
6811
|
};
|
|
6528
6812
|
|
|
6529
6813
|
// src/lib/widgets/checkbox.ts
|
|
@@ -7402,6 +7686,9 @@ var RuntimeRenderer = class {
|
|
|
7402
7686
|
activeModalId = null;
|
|
7403
7687
|
activeContextMenu = null;
|
|
7404
7688
|
activeConfirmModal = null;
|
|
7689
|
+
activeToasts = [];
|
|
7690
|
+
toastCounter = 0;
|
|
7691
|
+
toastTimeoutIds = /* @__PURE__ */ new Map();
|
|
7405
7692
|
lastPointer = { x: 24, y: 24 };
|
|
7406
7693
|
pendingResetModalIds = /* @__PURE__ */ new Set();
|
|
7407
7694
|
pendingScrollAction = null;
|
|
@@ -7475,9 +7762,11 @@ var RuntimeRenderer = class {
|
|
|
7475
7762
|
}
|
|
7476
7763
|
},
|
|
7477
7764
|
getVarValue: (name) => this.vars.get(name),
|
|
7765
|
+
getVarsSnapshot: () => Object.fromEntries(this.vars.entries()),
|
|
7478
7766
|
setVarValue: (name, value) => {
|
|
7479
7767
|
this.vars.set(name, value);
|
|
7480
7768
|
},
|
|
7769
|
+
resolveI18nValue: (value) => this.resolveI18nValue(value),
|
|
7481
7770
|
ensureWidgetState: (node, key) => this.ensureWidgetState(node, key),
|
|
7482
7771
|
resolveItemNodeKey: (node, listKey, index, fallbackPath) => this.resolveItemNodeKey(node, listKey, index, fallbackPath),
|
|
7483
7772
|
indexListElementNodes: (listNode, element, index) => this.indexListElementNodes(listNode, element, index)
|
|
@@ -7519,7 +7808,10 @@ var RuntimeRenderer = class {
|
|
|
7519
7808
|
scrollWidgetToBottom: (reference) => this.scrollWidgetToBottom(reference),
|
|
7520
7809
|
scrollWidgetToElementByIndex: (reference, index) => this.scrollWidgetToElementByIndex(reference, index),
|
|
7521
7810
|
playAudio: (value) => this.voiceRuntime.play(value),
|
|
7811
|
+
playStaticAudio: (url) => this.voiceRuntime.playStatic(url),
|
|
7522
7812
|
stopPlaying: () => this.voiceRuntime.stopPlaying(),
|
|
7813
|
+
showInfoToast: (message) => this.showToast("info", message),
|
|
7814
|
+
showErrorToast: (message) => this.showToast("error", message),
|
|
7523
7815
|
copyToClipboard: (value) => this.copyToClipboard(value),
|
|
7524
7816
|
selectFile: (args) => this.selectFile(args),
|
|
7525
7817
|
confirmModal: (args, inputValue) => this.confirmModal(args, inputValue),
|
|
@@ -7639,6 +7931,10 @@ var RuntimeRenderer = class {
|
|
|
7639
7931
|
source.close();
|
|
7640
7932
|
}
|
|
7641
7933
|
this.eventSources.length = 0;
|
|
7934
|
+
for (const timeoutId of this.toastTimeoutIds.values()) {
|
|
7935
|
+
window.clearTimeout(timeoutId);
|
|
7936
|
+
}
|
|
7937
|
+
this.toastTimeoutIds.clear();
|
|
7642
7938
|
this.voiceRuntime.dispose();
|
|
7643
7939
|
};
|
|
7644
7940
|
}
|
|
@@ -7839,6 +8135,21 @@ var RuntimeRenderer = class {
|
|
|
7839
8135
|
this.activeConfirmModal = null;
|
|
7840
8136
|
activeModal.resolve(confirmed);
|
|
7841
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
|
+
}
|
|
7842
8153
|
reindexStaticTree() {
|
|
7843
8154
|
this.nodeByKey.clear();
|
|
7844
8155
|
this.nodeById.clear();
|
|
@@ -8259,7 +8570,8 @@ var RuntimeRenderer = class {
|
|
|
8259
8570
|
name: node.name,
|
|
8260
8571
|
enabled: normalizeBoolean(node.enabled, true),
|
|
8261
8572
|
definitionSignature: buildDefinitionSignature(node.elements ?? []),
|
|
8262
|
-
elements: deepClone(node.elements ?? [])
|
|
8573
|
+
elements: deepClone(node.elements ?? []),
|
|
8574
|
+
selected: toFiniteNumber3(node.item) ?? toFiniteNumber3(node.selected) ?? -1
|
|
8263
8575
|
};
|
|
8264
8576
|
break;
|
|
8265
8577
|
case "listbox":
|
|
@@ -8357,6 +8669,7 @@ var RuntimeRenderer = class {
|
|
|
8357
8669
|
if (state.widget === "list" || state.widget === "grid-view") {
|
|
8358
8670
|
this.clearListElementState(state.key);
|
|
8359
8671
|
state.elements = deepClone(node.elements ?? []);
|
|
8672
|
+
state.selected = toFiniteNumber3(node.item) ?? toFiniteNumber3(node.selected) ?? state.selected ?? -1;
|
|
8360
8673
|
state.definitionSignature = nextSignature;
|
|
8361
8674
|
}
|
|
8362
8675
|
}
|
|
@@ -8536,7 +8849,18 @@ var RuntimeRenderer = class {
|
|
|
8536
8849
|
const modalMarkup = this.activeModalId ? renderModalOverlay(env, this.activeModalId) : "";
|
|
8537
8850
|
const menuMarkup = this.activeContextMenu ? renderContextMenuOverlay(env, this.activeContextMenu) : "";
|
|
8538
8851
|
const confirmMarkup = this.activeConfirmModal ? renderConfirmModalOverlay(env, this.activeConfirmModal) : "";
|
|
8539
|
-
|
|
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>`;
|
|
8540
8864
|
}
|
|
8541
8865
|
updatePointerFromEvent(event) {
|
|
8542
8866
|
this.lastPointer = updatePointerFromMouseEvent(event);
|
|
@@ -8646,6 +8970,7 @@ var RuntimeRenderer = class {
|
|
|
8646
8970
|
case "grid-view":
|
|
8647
8971
|
this.clearListElementState(state.key);
|
|
8648
8972
|
state.elements = [];
|
|
8973
|
+
state.selected = -1;
|
|
8649
8974
|
break;
|
|
8650
8975
|
case "listbox":
|
|
8651
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
|
@@ -8,7 +8,9 @@ type ReferenceRuntimeHost = {
|
|
|
8
8
|
getAppValue: (name: string) => unknown;
|
|
9
9
|
setAppValue: (name: string, value: unknown) => void;
|
|
10
10
|
getVarValue: (name: string) => unknown;
|
|
11
|
+
getVarsSnapshot: () => Record<string, unknown>;
|
|
11
12
|
setVarValue: (name: string, value: unknown) => void;
|
|
13
|
+
resolveI18nValue: (value: string | undefined) => string;
|
|
12
14
|
ensureWidgetState: (node: DescriptionNode, key: string) => WidgetState;
|
|
13
15
|
resolveItemNodeKey: (node: DescriptionNode, listKey: string, index: number, fallbackPath: string) => string;
|
|
14
16
|
indexListElementNodes: (listNode: ListNode | GridViewNode, element: DescriptionNode, index: number) => void;
|
|
@@ -16,6 +18,10 @@ type ReferenceRuntimeHost = {
|
|
|
16
18
|
export declare class ReferenceRuntime {
|
|
17
19
|
private readonly host;
|
|
18
20
|
constructor(host: ReferenceRuntimeHost);
|
|
21
|
+
private getListSelectionIndex;
|
|
22
|
+
private resolveScopedRootReference;
|
|
23
|
+
private readVarReference;
|
|
24
|
+
private writeVarReference;
|
|
19
25
|
isEmptyReference(reference: string, currentValue: unknown, responseValue: unknown, inputValue?: unknown): boolean;
|
|
20
26
|
resolveReference(reference: string, currentValue: unknown, responseValue: unknown, inputValue?: unknown): unknown;
|
|
21
27
|
resolveCurrentReference(reference: string, currentValue: unknown): unknown;
|
|
@@ -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
|
+
}
|