@vortexm/vjt 0.1.7 → 0.1.9
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.d.ts +1 -1
- package/dist/index.js +690 -54
- package/dist/lib/action-runtime.d.ts +14 -1
- package/dist/lib/references.d.ts +6 -2
- package/dist/lib/resource-manager.d.ts +4 -1
- package/dist/lib/types.d.ts +12 -1
- package/dist/lib/voice-runtime.d.ts +1 -1
- package/dist/lib/widgets/bindings.d.ts +1 -1
- package/dist/lib/widgets/context.d.ts +1 -0
- package/dist/lib/widgets/image.d.ts +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4154,6 +4154,32 @@ function readPath(target, path) {
|
|
|
4154
4154
|
}
|
|
4155
4155
|
return current;
|
|
4156
4156
|
}
|
|
4157
|
+
function writePath(target, path, value) {
|
|
4158
|
+
if (!isPlainObject2(target) || path.length === 0) {
|
|
4159
|
+
return false;
|
|
4160
|
+
}
|
|
4161
|
+
let current = target;
|
|
4162
|
+
for (let index = 0; index < path.length - 1; index += 1) {
|
|
4163
|
+
const segment = path[index];
|
|
4164
|
+
if (isBlockedObjectKey(segment)) {
|
|
4165
|
+
return false;
|
|
4166
|
+
}
|
|
4167
|
+
const next = current[segment];
|
|
4168
|
+
if (isPlainObject2(next)) {
|
|
4169
|
+
current = next;
|
|
4170
|
+
continue;
|
|
4171
|
+
}
|
|
4172
|
+
const created = {};
|
|
4173
|
+
current[segment] = created;
|
|
4174
|
+
current = created;
|
|
4175
|
+
}
|
|
4176
|
+
const leaf = path[path.length - 1];
|
|
4177
|
+
if (isBlockedObjectKey(leaf)) {
|
|
4178
|
+
return false;
|
|
4179
|
+
}
|
|
4180
|
+
current[leaf] = value;
|
|
4181
|
+
return true;
|
|
4182
|
+
}
|
|
4157
4183
|
function sanitizeIdFragment(value) {
|
|
4158
4184
|
return value.replace(/[^A-Za-z0-9_-]/g, "");
|
|
4159
4185
|
}
|
|
@@ -4273,8 +4299,9 @@ var ReferenceRuntime = class {
|
|
|
4273
4299
|
}
|
|
4274
4300
|
resolveCurrentReference(reference, currentValue) {
|
|
4275
4301
|
if (this.isListElementContext(currentValue)) {
|
|
4302
|
+
const resolvedDescriptor = this.resolveListDescriptorNode(currentValue.descriptor, currentValue);
|
|
4276
4303
|
const firstPart = reference.split(".")[0];
|
|
4277
|
-
const namedNode = this.findNamedWidget(
|
|
4304
|
+
const namedNode = this.findNamedWidget(resolvedDescriptor, firstPart, currentValue);
|
|
4278
4305
|
if (namedNode) {
|
|
4279
4306
|
const widgetKey = this.host.resolveItemNodeKey(namedNode, currentValue.listId, currentValue.index, `named.${firstPart}`);
|
|
4280
4307
|
const state = this.host.ensureWidgetState(namedNode, widgetKey);
|
|
@@ -4285,12 +4312,16 @@ var ReferenceRuntime = class {
|
|
|
4285
4312
|
if (directDescriptorValue !== void 0) {
|
|
4286
4313
|
return directDescriptorValue;
|
|
4287
4314
|
}
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4315
|
+
const resolvedDescriptorValue = resolvedDescriptor !== currentValue.descriptor ? readPath(resolvedDescriptor, reference.split(".")) : void 0;
|
|
4316
|
+
if (resolvedDescriptorValue !== void 0) {
|
|
4317
|
+
return resolvedDescriptorValue;
|
|
4318
|
+
}
|
|
4319
|
+
if (typeof resolvedDescriptor.widget === "string") {
|
|
4320
|
+
const descriptorKey = this.host.resolveItemNodeKey(resolvedDescriptor, currentValue.listId, currentValue.index, "item");
|
|
4321
|
+
const descriptorState = this.host.ensureWidgetState(resolvedDescriptor, descriptorKey);
|
|
4291
4322
|
return this.readWidgetField(descriptorState, reference);
|
|
4292
4323
|
}
|
|
4293
|
-
return readPath(
|
|
4324
|
+
return readPath(resolvedDescriptor, reference.split("."));
|
|
4294
4325
|
}
|
|
4295
4326
|
if (isPlainObject2(currentValue) || Array.isArray(currentValue)) {
|
|
4296
4327
|
return readPath(currentValue, reference.split("."));
|
|
@@ -4335,6 +4366,8 @@ var ReferenceRuntime = class {
|
|
|
4335
4366
|
return state.isHidden ?? true;
|
|
4336
4367
|
case "url":
|
|
4337
4368
|
return state.url ?? "";
|
|
4369
|
+
case "base64":
|
|
4370
|
+
return state.base64 ?? "";
|
|
4338
4371
|
case "elements":
|
|
4339
4372
|
if (state.widget === "list") {
|
|
4340
4373
|
return (state.elements ?? []).map((descriptor, index) => ({
|
|
@@ -4374,8 +4407,12 @@ var ReferenceRuntime = class {
|
|
|
4374
4407
|
return readPath(state, field.split("."));
|
|
4375
4408
|
}
|
|
4376
4409
|
}
|
|
4377
|
-
assignReference(reference, inputValue,
|
|
4378
|
-
if (
|
|
4410
|
+
assignReference(reference, inputValue, currentValue, options) {
|
|
4411
|
+
if (hasBlockedReferenceSegment(reference)) {
|
|
4412
|
+
return;
|
|
4413
|
+
}
|
|
4414
|
+
if (reference.startsWith("current.")) {
|
|
4415
|
+
this.assignCurrentReference(reference.slice(8), inputValue, currentValue);
|
|
4379
4416
|
return;
|
|
4380
4417
|
}
|
|
4381
4418
|
if (reference.startsWith("cookies.")) {
|
|
@@ -4418,6 +4455,9 @@ var ReferenceRuntime = class {
|
|
|
4418
4455
|
case "url":
|
|
4419
4456
|
state.url = stringifyReferenceValue(inputValue);
|
|
4420
4457
|
break;
|
|
4458
|
+
case "base64":
|
|
4459
|
+
state.base64 = stringifyReferenceValue(inputValue);
|
|
4460
|
+
break;
|
|
4421
4461
|
case "condition":
|
|
4422
4462
|
state.condition = Boolean(inputValue);
|
|
4423
4463
|
break;
|
|
@@ -4443,6 +4483,19 @@ var ReferenceRuntime = class {
|
|
|
4443
4483
|
break;
|
|
4444
4484
|
}
|
|
4445
4485
|
}
|
|
4486
|
+
assignCurrentReference(reference, inputValue, currentValue) {
|
|
4487
|
+
const path = reference.split(".").filter(Boolean);
|
|
4488
|
+
if (path.length === 0) {
|
|
4489
|
+
return;
|
|
4490
|
+
}
|
|
4491
|
+
if (this.isListElementContext(currentValue)) {
|
|
4492
|
+
writePath(currentValue.descriptor, path, inputValue);
|
|
4493
|
+
return;
|
|
4494
|
+
}
|
|
4495
|
+
if (isPlainObject2(currentValue)) {
|
|
4496
|
+
writePath(currentValue, path, inputValue);
|
|
4497
|
+
}
|
|
4498
|
+
}
|
|
4446
4499
|
clearListElementState(listKey) {
|
|
4447
4500
|
const generatedPrefix = `${sanitizeIdFragment(listKey)}Element`;
|
|
4448
4501
|
for (const key of Array.from(this.host.stateByKey.keys())) {
|
|
@@ -4493,7 +4546,7 @@ var ReferenceRuntime = class {
|
|
|
4493
4546
|
isListElementContext(value) {
|
|
4494
4547
|
return isPlainObject2(value) && value.kind === "list-element" && typeof value.listId === "string" && typeof value.index === "number" && isPlainObject2(value.descriptor) && typeof value.descriptor.widget === "string";
|
|
4495
4548
|
}
|
|
4496
|
-
findNamedWidget(node, name) {
|
|
4549
|
+
findNamedWidget(node, name, currentValue) {
|
|
4497
4550
|
if (!node) {
|
|
4498
4551
|
return null;
|
|
4499
4552
|
}
|
|
@@ -4502,12 +4555,12 @@ var ReferenceRuntime = class {
|
|
|
4502
4555
|
}
|
|
4503
4556
|
switch (node.widget) {
|
|
4504
4557
|
case "adaptive-layout":
|
|
4505
|
-
return this.findNamedWidget(node.desktop, name) ?? this.findNamedWidget(node.mobile, name);
|
|
4558
|
+
return this.findNamedWidget(node.desktop, name, currentValue) ?? this.findNamedWidget(node.mobile, name, currentValue);
|
|
4506
4559
|
case "container-layout":
|
|
4507
4560
|
if (node.type === "grid") {
|
|
4508
4561
|
for (const row of node.grid?.rows ?? []) {
|
|
4509
4562
|
for (const column of row.columns ?? []) {
|
|
4510
|
-
const match = this.findNamedWidget(column.child, name);
|
|
4563
|
+
const match = this.findNamedWidget(column.child, name, currentValue);
|
|
4511
4564
|
if (match) {
|
|
4512
4565
|
return match;
|
|
4513
4566
|
}
|
|
@@ -4516,20 +4569,20 @@ var ReferenceRuntime = class {
|
|
|
4516
4569
|
return null;
|
|
4517
4570
|
}
|
|
4518
4571
|
for (const cell of node.children ?? []) {
|
|
4519
|
-
const match = this.findNamedWidget(cell.child, name);
|
|
4572
|
+
const match = this.findNamedWidget(cell.child, name, currentValue);
|
|
4520
4573
|
if (match) {
|
|
4521
4574
|
return match;
|
|
4522
4575
|
}
|
|
4523
4576
|
}
|
|
4524
4577
|
return null;
|
|
4525
4578
|
case "panel":
|
|
4526
|
-
return this.findNamedWidget(node.child, name);
|
|
4579
|
+
return this.findNamedWidget(node.child, name, currentValue);
|
|
4527
4580
|
case "conditional-container":
|
|
4528
|
-
return this.findNamedWidget(node.default, name) ?? this.findNamedWidget(node.alternative, name);
|
|
4581
|
+
return this.findNamedWidget(node.default, name, currentValue) ?? this.findNamedWidget(node.alternative, name, currentValue);
|
|
4529
4582
|
case "list":
|
|
4530
4583
|
case "grid-view":
|
|
4531
4584
|
for (const element of node.elements ?? []) {
|
|
4532
|
-
const match = this.findNamedWidget(element, name);
|
|
4585
|
+
const match = this.findNamedWidget(element, name, currentValue);
|
|
4533
4586
|
if (match) {
|
|
4534
4587
|
return match;
|
|
4535
4588
|
}
|
|
@@ -4537,7 +4590,7 @@ var ReferenceRuntime = class {
|
|
|
4537
4590
|
return null;
|
|
4538
4591
|
case "overlay-container":
|
|
4539
4592
|
for (const layer of node.layers ?? []) {
|
|
4540
|
-
const match = this.findNamedWidget(layer.child, name);
|
|
4593
|
+
const match = this.findNamedWidget(layer.child, name, currentValue);
|
|
4541
4594
|
if (match) {
|
|
4542
4595
|
return match;
|
|
4543
4596
|
}
|
|
@@ -4545,20 +4598,40 @@ var ReferenceRuntime = class {
|
|
|
4545
4598
|
return null;
|
|
4546
4599
|
case "tabs":
|
|
4547
4600
|
for (const tab of node.tabs ?? []) {
|
|
4548
|
-
const match = this.findNamedWidget(tab.content, name);
|
|
4601
|
+
const match = this.findNamedWidget(tab.content, name, currentValue);
|
|
4549
4602
|
if (match) {
|
|
4550
4603
|
return match;
|
|
4551
4604
|
}
|
|
4552
4605
|
}
|
|
4553
4606
|
return null;
|
|
4554
4607
|
case "spoiler":
|
|
4555
|
-
return typeof node.content === "string" ? null : this.findNamedWidget(node.content, name);
|
|
4608
|
+
return typeof node.content === "string" ? null : this.findNamedWidget(node.content, name, currentValue);
|
|
4609
|
+
case "ui-reference": {
|
|
4610
|
+
const referenced = currentValue ? this.resolveUiReferenceNode(node, currentValue) : null;
|
|
4611
|
+
return referenced ? this.findNamedWidget(referenced, name, currentValue) : null;
|
|
4612
|
+
}
|
|
4556
4613
|
case "modal-window":
|
|
4557
|
-
return this.findNamedWidget(node.child, name);
|
|
4614
|
+
return this.findNamedWidget(node.child, name, currentValue);
|
|
4558
4615
|
default:
|
|
4559
4616
|
return null;
|
|
4560
4617
|
}
|
|
4561
4618
|
}
|
|
4619
|
+
resolveListDescriptorNode(node, currentValue) {
|
|
4620
|
+
if (node.widget !== "ui-reference") {
|
|
4621
|
+
return node;
|
|
4622
|
+
}
|
|
4623
|
+
return this.resolveUiReferenceNode(node, currentValue) ?? node;
|
|
4624
|
+
}
|
|
4625
|
+
resolveUiReferenceNode(node, currentValue) {
|
|
4626
|
+
if (node.widget !== "ui-reference") {
|
|
4627
|
+
return null;
|
|
4628
|
+
}
|
|
4629
|
+
const resolvedValue = typeof node.ref === "string" && node.ref.startsWith("$ref:") ? this.resolveReference(node.ref.slice(5), currentValue, null) : node.ref;
|
|
4630
|
+
if (typeof resolvedValue !== "string" || resolvedValue.length === 0) {
|
|
4631
|
+
return null;
|
|
4632
|
+
}
|
|
4633
|
+
return this.host.getUiResource(resolvedValue);
|
|
4634
|
+
}
|
|
4562
4635
|
};
|
|
4563
4636
|
|
|
4564
4637
|
// src/lib/action-runtime.ts
|
|
@@ -4577,9 +4650,14 @@ var ActionRuntime = class {
|
|
|
4577
4650
|
assignReference;
|
|
4578
4651
|
resolveMappedValue;
|
|
4579
4652
|
setWidgetEnabled;
|
|
4653
|
+
setWidgetVisible;
|
|
4580
4654
|
clearWidget;
|
|
4581
4655
|
clearListElementState;
|
|
4582
4656
|
focusWidget;
|
|
4657
|
+
moveCursorToEnd;
|
|
4658
|
+
scrollWidgetToTop;
|
|
4659
|
+
scrollWidgetToBottom;
|
|
4660
|
+
scrollWidgetToElementByIndex;
|
|
4583
4661
|
playAudio;
|
|
4584
4662
|
stopPlaying;
|
|
4585
4663
|
copyToClipboard;
|
|
@@ -4614,9 +4692,14 @@ var ActionRuntime = class {
|
|
|
4614
4692
|
this.assignReference = options.assignReference;
|
|
4615
4693
|
this.resolveMappedValue = options.resolveMappedValue;
|
|
4616
4694
|
this.setWidgetEnabled = options.setWidgetEnabled;
|
|
4695
|
+
this.setWidgetVisible = options.setWidgetVisible;
|
|
4617
4696
|
this.clearWidget = options.clearWidget;
|
|
4618
4697
|
this.clearListElementState = options.clearListElementState;
|
|
4619
4698
|
this.focusWidget = options.focusWidget;
|
|
4699
|
+
this.moveCursorToEnd = options.moveCursorToEnd;
|
|
4700
|
+
this.scrollWidgetToTop = options.scrollWidgetToTop;
|
|
4701
|
+
this.scrollWidgetToBottom = options.scrollWidgetToBottom;
|
|
4702
|
+
this.scrollWidgetToElementByIndex = options.scrollWidgetToElementByIndex;
|
|
4620
4703
|
this.playAudio = options.playAudio;
|
|
4621
4704
|
this.stopPlaying = options.stopPlaying;
|
|
4622
4705
|
this.copyToClipboard = options.copyToClipboard;
|
|
@@ -4678,7 +4761,7 @@ var ActionRuntime = class {
|
|
|
4678
4761
|
let output = rawOutput;
|
|
4679
4762
|
if (action.andThen?.length) {
|
|
4680
4763
|
if (output !== null && output !== void 0) {
|
|
4681
|
-
output = await this.runActions(action.andThen, output,
|
|
4764
|
+
output = await this.runActions(action.andThen, output, context);
|
|
4682
4765
|
} else {
|
|
4683
4766
|
output = null;
|
|
4684
4767
|
}
|
|
@@ -4765,7 +4848,7 @@ var ActionRuntime = class {
|
|
|
4765
4848
|
return null;
|
|
4766
4849
|
}
|
|
4767
4850
|
if (name === "startListening") {
|
|
4768
|
-
await this.startListening();
|
|
4851
|
+
await this.startListening(action.args);
|
|
4769
4852
|
return null;
|
|
4770
4853
|
}
|
|
4771
4854
|
if (name === "stopListening") {
|
|
@@ -4809,6 +4892,14 @@ var ActionRuntime = class {
|
|
|
4809
4892
|
this.setWidgetEnabled(name.slice(8), false);
|
|
4810
4893
|
return null;
|
|
4811
4894
|
}
|
|
4895
|
+
if (name.startsWith("makeVisible ")) {
|
|
4896
|
+
this.setWidgetVisible(name.slice(12), true);
|
|
4897
|
+
return null;
|
|
4898
|
+
}
|
|
4899
|
+
if (name.startsWith("makeInvisible ")) {
|
|
4900
|
+
this.setWidgetVisible(name.slice(14), false);
|
|
4901
|
+
return null;
|
|
4902
|
+
}
|
|
4812
4903
|
if (name.startsWith("clear ")) {
|
|
4813
4904
|
this.clearWidget(name.slice(6));
|
|
4814
4905
|
return null;
|
|
@@ -4817,6 +4908,22 @@ var ActionRuntime = class {
|
|
|
4817
4908
|
this.focusWidget(name.slice(9));
|
|
4818
4909
|
return null;
|
|
4819
4910
|
}
|
|
4911
|
+
if (name.startsWith("moveCursorToEnd ")) {
|
|
4912
|
+
this.moveCursorToEnd(name.slice(16));
|
|
4913
|
+
return null;
|
|
4914
|
+
}
|
|
4915
|
+
if (name.startsWith("scrollToTop ")) {
|
|
4916
|
+
this.scrollWidgetToTop(name.slice(12));
|
|
4917
|
+
return inputValue;
|
|
4918
|
+
}
|
|
4919
|
+
if (name.startsWith("scrollToBottom ")) {
|
|
4920
|
+
this.scrollWidgetToBottom(name.slice(15));
|
|
4921
|
+
return inputValue;
|
|
4922
|
+
}
|
|
4923
|
+
if (name.startsWith("scrollToElementByIndex ")) {
|
|
4924
|
+
this.scrollWidgetToElementByIndex(name.slice(23), inputValue);
|
|
4925
|
+
return inputValue;
|
|
4926
|
+
}
|
|
4820
4927
|
if (name.startsWith("setUi ")) {
|
|
4821
4928
|
const widgetId = name.slice(6);
|
|
4822
4929
|
const resourceRef = typeof inputValue === "string" ? inputValue : "";
|
|
@@ -4827,14 +4934,62 @@ var ActionRuntime = class {
|
|
|
4827
4934
|
}
|
|
4828
4935
|
if (name.startsWith("get ")) {
|
|
4829
4936
|
const reference = name.slice(4);
|
|
4830
|
-
if (Array.isArray(
|
|
4831
|
-
return
|
|
4937
|
+
if (Array.isArray(inputValue) && this.isCurrentScopedReference(reference)) {
|
|
4938
|
+
return inputValue.map((entry) => this.resolveReference(reference, entry, context.responseValue)).filter((entry) => entry !== null && entry !== void 0);
|
|
4832
4939
|
}
|
|
4833
4940
|
return this.resolveReference(reference, context.currentValue, context.responseValue);
|
|
4834
4941
|
}
|
|
4942
|
+
if (name.startsWith("count ")) {
|
|
4943
|
+
return this.countValue(this.resolveReference(name.slice(6), context.currentValue, context.responseValue));
|
|
4944
|
+
}
|
|
4945
|
+
if (name === "indexOf") {
|
|
4946
|
+
if (isListElementLike(context.currentValue)) {
|
|
4947
|
+
return context.currentValue.index;
|
|
4948
|
+
}
|
|
4949
|
+
if (typeof context.currentIndex === "number") {
|
|
4950
|
+
return context.currentIndex;
|
|
4951
|
+
}
|
|
4952
|
+
return null;
|
|
4953
|
+
}
|
|
4954
|
+
if (name === "foreach") {
|
|
4955
|
+
if (Array.isArray(inputValue) && action.andThen?.length) {
|
|
4956
|
+
const currentList = inputValue;
|
|
4957
|
+
for (let index = 0; index < currentList.length; index += 1) {
|
|
4958
|
+
const entry = currentList[index];
|
|
4959
|
+
await this.runActions(action.andThen, entry, {
|
|
4960
|
+
...context,
|
|
4961
|
+
currentValue: entry,
|
|
4962
|
+
currentList,
|
|
4963
|
+
currentIndex: index
|
|
4964
|
+
});
|
|
4965
|
+
}
|
|
4966
|
+
}
|
|
4967
|
+
return inputValue;
|
|
4968
|
+
}
|
|
4969
|
+
if (name === "inc") {
|
|
4970
|
+
return this.toNumber(inputValue) + 1;
|
|
4971
|
+
}
|
|
4972
|
+
if (name === "dec") {
|
|
4973
|
+
return this.toNumber(inputValue) - 1;
|
|
4974
|
+
}
|
|
4835
4975
|
if (name.startsWith("equals ")) {
|
|
4836
4976
|
return this.resolveReference(name.slice(7), context.currentValue, context.responseValue) === action.args;
|
|
4837
4977
|
}
|
|
4978
|
+
if (name.startsWith("greaterThan ")) {
|
|
4979
|
+
return this.toNumber(inputValue) > this.toNumber(this.resolveReference(name.slice(12), context.currentValue, context.responseValue));
|
|
4980
|
+
}
|
|
4981
|
+
if (name.startsWith("greaterThanOrEquals ")) {
|
|
4982
|
+
return this.toNumber(inputValue) >= this.toNumber(this.resolveReference(name.slice(20), context.currentValue, context.responseValue));
|
|
4983
|
+
}
|
|
4984
|
+
if (name.startsWith("lessThan ")) {
|
|
4985
|
+
return this.toNumber(inputValue) < this.toNumber(this.resolveReference(name.slice(9), context.currentValue, context.responseValue));
|
|
4986
|
+
}
|
|
4987
|
+
if (name.startsWith("lessThanOrEquals ")) {
|
|
4988
|
+
return this.toNumber(inputValue) <= this.toNumber(this.resolveReference(name.slice(17), context.currentValue, context.responseValue));
|
|
4989
|
+
}
|
|
4990
|
+
if (name.startsWith("and ")) {
|
|
4991
|
+
return Boolean(inputValue) && Boolean(this.resolveReference(name.slice(4), context.currentValue, context.responseValue));
|
|
4992
|
+
}
|
|
4838
4993
|
if (name.startsWith("isEmpty ")) {
|
|
4839
4994
|
return this.isEmptyReference(name.slice(8), context.currentValue, context.responseValue);
|
|
4840
4995
|
}
|
|
@@ -4852,8 +5007,8 @@ var ActionRuntime = class {
|
|
|
4852
5007
|
}
|
|
4853
5008
|
if (name.startsWith("filter ")) {
|
|
4854
5009
|
const reference = name.slice(7);
|
|
4855
|
-
if (Array.isArray(
|
|
4856
|
-
return
|
|
5010
|
+
if (Array.isArray(inputValue) && this.isCurrentScopedReference(reference)) {
|
|
5011
|
+
return inputValue.filter((entry) => this.resolveReference(reference, entry, context.responseValue));
|
|
4857
5012
|
}
|
|
4858
5013
|
const value = this.resolveReference(reference, context.currentValue, context.responseValue);
|
|
4859
5014
|
return value ? inputValue : null;
|
|
@@ -4878,6 +5033,12 @@ var ActionRuntime = class {
|
|
|
4878
5033
|
this.clearListElementState(listState.key);
|
|
4879
5034
|
return listState.elements;
|
|
4880
5035
|
}
|
|
5036
|
+
const inputListState = this.asListWidgetState(inputValue);
|
|
5037
|
+
if (inputListState && Array.isArray(inputListState.elements)) {
|
|
5038
|
+
inputListState.elements.push(valueToAdd);
|
|
5039
|
+
this.clearListElementState(inputListState.key);
|
|
5040
|
+
return inputListState.elements;
|
|
5041
|
+
}
|
|
4881
5042
|
if (context.currentList && typeof context.currentIndex === "number") {
|
|
4882
5043
|
return context.currentIndex === context.currentList.length - 1 ? [this.unwrapListValue(inputValue), valueToAdd] : this.unwrapListValue(inputValue);
|
|
4883
5044
|
}
|
|
@@ -4902,6 +5063,12 @@ var ActionRuntime = class {
|
|
|
4902
5063
|
return listState.elements;
|
|
4903
5064
|
}
|
|
4904
5065
|
}
|
|
5066
|
+
const inputListState = this.asListWidgetState(inputValue);
|
|
5067
|
+
if (inputListState && Array.isArray(inputListState.elements)) {
|
|
5068
|
+
inputListState.elements.push(valueToInsert);
|
|
5069
|
+
this.clearListElementState(inputListState.key);
|
|
5070
|
+
return inputListState.elements;
|
|
5071
|
+
}
|
|
4905
5072
|
if (context.currentList && typeof context.currentIndex === "number") {
|
|
4906
5073
|
return [this.unwrapListValue(inputValue), valueToInsert];
|
|
4907
5074
|
}
|
|
@@ -4916,10 +5083,10 @@ var ActionRuntime = class {
|
|
|
4916
5083
|
return inputValue;
|
|
4917
5084
|
}
|
|
4918
5085
|
if (name === "map") {
|
|
4919
|
-
if (Array.isArray(
|
|
4920
|
-
return
|
|
5086
|
+
if (Array.isArray(inputValue)) {
|
|
5087
|
+
return inputValue.map((entry) => this.resolveMappedValue(action.args, entry, context.responseValue));
|
|
4921
5088
|
}
|
|
4922
|
-
return this.resolveMappedValue(action.args,
|
|
5089
|
+
return this.resolveMappedValue(action.args, inputValue, context.responseValue);
|
|
4923
5090
|
}
|
|
4924
5091
|
if (name === "ifelse") {
|
|
4925
5092
|
const branches = isIfElseArgs(action.args) ? action.args : null;
|
|
@@ -4964,6 +5131,46 @@ var ActionRuntime = class {
|
|
|
4964
5131
|
isCurrentScopedReference(reference) {
|
|
4965
5132
|
return reference === "current" || reference.startsWith("current.") || reference.startsWith("this.") || reference === "this";
|
|
4966
5133
|
}
|
|
5134
|
+
countValue(value) {
|
|
5135
|
+
if (Array.isArray(value) || typeof value === "string") {
|
|
5136
|
+
return value.length;
|
|
5137
|
+
}
|
|
5138
|
+
if (value && typeof value === "object") {
|
|
5139
|
+
const widgetState = value;
|
|
5140
|
+
if (widgetState.widget === "list" || widgetState.widget === "grid-view") {
|
|
5141
|
+
return Array.isArray(widgetState.elements) ? widgetState.elements.length : 0;
|
|
5142
|
+
}
|
|
5143
|
+
if (widgetState.widget === "listbox") {
|
|
5144
|
+
return Array.isArray(widgetState.listboxElements) ? widgetState.listboxElements.length : 0;
|
|
5145
|
+
}
|
|
5146
|
+
if (widgetState.widget === "combobox") {
|
|
5147
|
+
return Array.isArray(widgetState.comboboxElements) ? widgetState.comboboxElements.length : 0;
|
|
5148
|
+
}
|
|
5149
|
+
if ("length" in widgetState && typeof widgetState.length === "number") {
|
|
5150
|
+
return widgetState.length;
|
|
5151
|
+
}
|
|
5152
|
+
}
|
|
5153
|
+
return 0;
|
|
5154
|
+
}
|
|
5155
|
+
toNumber(value) {
|
|
5156
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
5157
|
+
return value;
|
|
5158
|
+
}
|
|
5159
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
5160
|
+
const parsed = Number.parseFloat(value);
|
|
5161
|
+
if (Number.isFinite(parsed)) {
|
|
5162
|
+
return parsed;
|
|
5163
|
+
}
|
|
5164
|
+
}
|
|
5165
|
+
return 0;
|
|
5166
|
+
}
|
|
5167
|
+
asListWidgetState(value) {
|
|
5168
|
+
if (!value || typeof value !== "object") {
|
|
5169
|
+
return null;
|
|
5170
|
+
}
|
|
5171
|
+
const state = value;
|
|
5172
|
+
return state.widget === "list" || state.widget === "grid-view" ? state : null;
|
|
5173
|
+
}
|
|
4967
5174
|
};
|
|
4968
5175
|
function isIfElseArgs(value) {
|
|
4969
5176
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -5393,7 +5600,7 @@ var MAX_CONCURRENT_PLAYERS = 5;
|
|
|
5393
5600
|
var MAX_RECORDING_MS = 10 * 60 * 1e3;
|
|
5394
5601
|
var MAX_LISTENING_SILENCE_MS = 60 * 60 * 1e3;
|
|
5395
5602
|
var SILENCE_STOP_MS = 2e3;
|
|
5396
|
-
var
|
|
5603
|
+
var DEFAULT_SPEECH_THRESHOLD = 0.035;
|
|
5397
5604
|
var RECORDING_MIME_CANDIDATES = [
|
|
5398
5605
|
"audio/webm;codecs=opus",
|
|
5399
5606
|
"audio/webm",
|
|
@@ -5446,6 +5653,21 @@ function blobToBase64(blob) {
|
|
|
5446
5653
|
reader.readAsDataURL(blob);
|
|
5447
5654
|
});
|
|
5448
5655
|
}
|
|
5656
|
+
function normalizeSpeechThreshold(value) {
|
|
5657
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
5658
|
+
return Math.max(0, Math.min(1, value));
|
|
5659
|
+
}
|
|
5660
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
5661
|
+
const parsed = Number.parseFloat(value);
|
|
5662
|
+
if (Number.isFinite(parsed)) {
|
|
5663
|
+
return Math.max(0, Math.min(1, parsed));
|
|
5664
|
+
}
|
|
5665
|
+
}
|
|
5666
|
+
return DEFAULT_SPEECH_THRESHOLD;
|
|
5667
|
+
}
|
|
5668
|
+
function isStartListeningArgs(value) {
|
|
5669
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5670
|
+
}
|
|
5449
5671
|
var VoiceRuntime = class {
|
|
5450
5672
|
debugLogging;
|
|
5451
5673
|
triggerSystemEvent;
|
|
@@ -5542,11 +5764,12 @@ var VoiceRuntime = class {
|
|
|
5542
5764
|
this.clearRecordingTimeout();
|
|
5543
5765
|
this.mediaRecorder.stop();
|
|
5544
5766
|
}
|
|
5545
|
-
async startListening() {
|
|
5767
|
+
async startListening(args) {
|
|
5546
5768
|
if (this.listening) {
|
|
5547
5769
|
return;
|
|
5548
5770
|
}
|
|
5549
5771
|
try {
|
|
5772
|
+
const silenceThreshold = isStartListeningArgs(args) ? normalizeSpeechThreshold(args.silenceThreshold) : DEFAULT_SPEECH_THRESHOLD;
|
|
5550
5773
|
const stream = await this.ensureInputStream();
|
|
5551
5774
|
const context = new AudioContext();
|
|
5552
5775
|
const analyser = context.createAnalyser();
|
|
@@ -5573,7 +5796,7 @@ var VoiceRuntime = class {
|
|
|
5573
5796
|
}
|
|
5574
5797
|
const rms = Math.sqrt(squareSum / sampleBuffer.length);
|
|
5575
5798
|
const now = performance.now();
|
|
5576
|
-
if (rms >=
|
|
5799
|
+
if (rms >= silenceThreshold) {
|
|
5577
5800
|
this.lastSpeechAt = now;
|
|
5578
5801
|
this.listeningStartedAt = now;
|
|
5579
5802
|
if (!this.speechEventTriggered) {
|
|
@@ -5595,6 +5818,9 @@ var VoiceRuntime = class {
|
|
|
5595
5818
|
void step();
|
|
5596
5819
|
});
|
|
5597
5820
|
};
|
|
5821
|
+
logRuntimeDebug(this.debugLogging, "voice-listening-started", {
|
|
5822
|
+
silenceThreshold
|
|
5823
|
+
});
|
|
5598
5824
|
void step();
|
|
5599
5825
|
} catch (error) {
|
|
5600
5826
|
await this.handleListeningError(error);
|
|
@@ -6010,7 +6236,7 @@ var renderGridView = (env, node, state, key) => {
|
|
|
6010
6236
|
index: realIndex,
|
|
6011
6237
|
descriptor: element
|
|
6012
6238
|
};
|
|
6013
|
-
return `<div class="vjt-grid-cell">${env.renderNode(element, `${key}.elements.${realIndex}`, context)}</div>`;
|
|
6239
|
+
return `<div class="vjt-grid-cell" data-list-id="${env.escapeHtml(key)}" data-list-index="${realIndex}">${env.renderNode(element, `${key}.elements.${realIndex}`, context)}</div>`;
|
|
6014
6240
|
}).join("");
|
|
6015
6241
|
rows.push(`<div class="vjt-grid-row">${rowMarkup}</div>`);
|
|
6016
6242
|
}
|
|
@@ -6056,13 +6282,13 @@ var renderCombobox = (env, node, state, key) => {
|
|
|
6056
6282
|
var HEADING_TAGS = /* @__PURE__ */ new Set(["h1", "h2", "h3", "h4", "h5", "h6"]);
|
|
6057
6283
|
var TEXT_ALIGN_MAP = { left: "left", center: "center", right: "right" };
|
|
6058
6284
|
var VERTICAL_ALIGN_MAP = { top: "flex-start", center: "center", bottom: "flex-end" };
|
|
6059
|
-
var renderStaticText = (env, node, state, key) => {
|
|
6285
|
+
var renderStaticText = (env, node, state, key, _path, itemContext) => {
|
|
6060
6286
|
const resolvedHeading = node.heading && HEADING_TAGS.has(node.heading) ? node.heading : null;
|
|
6061
6287
|
const tag = resolvedHeading ?? "div";
|
|
6062
6288
|
const align = TEXT_ALIGN_MAP[node["horiz-align"] ?? "left"];
|
|
6063
6289
|
const verticalAlign = VERTICAL_ALIGN_MAP[node["vert-align"] ?? "center"];
|
|
6064
6290
|
const common = state.id ? ` id="${env.escapeHtml(state.id)}"` : "";
|
|
6065
|
-
const text = env.escapeHtml(env.
|
|
6291
|
+
const text = env.escapeHtml(env.resolveTextValue(state.text ?? node.text, itemContext));
|
|
6066
6292
|
const inlineStyle = [
|
|
6067
6293
|
"display:flex",
|
|
6068
6294
|
`align-items:${verticalAlign}`,
|
|
@@ -6093,11 +6319,11 @@ var markdownConverter = new import_showdown.default.Converter({
|
|
|
6093
6319
|
strikethrough: false,
|
|
6094
6320
|
tasklists: false
|
|
6095
6321
|
});
|
|
6096
|
-
var renderMdText = (env, node, state, key) => {
|
|
6322
|
+
var renderMdText = (env, node, state, key, _path, itemContext) => {
|
|
6097
6323
|
const align = TEXT_ALIGN_MAP2[node["horiz-align"] ?? "left"];
|
|
6098
6324
|
const verticalAlign = VERTICAL_ALIGN_MAP2[node["vert-align"] ?? "center"];
|
|
6099
6325
|
const common = state.id ? ` id="${env.escapeHtml(state.id)}"` : "";
|
|
6100
|
-
const markdown = env.
|
|
6326
|
+
const markdown = env.resolveTextValue(state.text ?? node.text, itemContext);
|
|
6101
6327
|
const html = sanitizeGeneratedHtml(markdownConverter.makeHtml(sanitizeMarkdownSource(markdown)));
|
|
6102
6328
|
const inlineStyle = [
|
|
6103
6329
|
"display:flex",
|
|
@@ -6119,24 +6345,24 @@ var renderMdText = (env, node, state, key) => {
|
|
|
6119
6345
|
};
|
|
6120
6346
|
|
|
6121
6347
|
// src/lib/widgets/edit.ts
|
|
6122
|
-
function resolvePlaceholder(env, node, statePlaceholder) {
|
|
6348
|
+
function resolvePlaceholder(env, node, itemContext, statePlaceholder) {
|
|
6123
6349
|
if (statePlaceholder) {
|
|
6124
|
-
return env.
|
|
6350
|
+
return env.resolveTextValue(statePlaceholder, itemContext);
|
|
6125
6351
|
}
|
|
6126
6352
|
if (node.placeholder) {
|
|
6127
|
-
return env.
|
|
6353
|
+
return env.resolveTextValue(node.placeholder, itemContext);
|
|
6128
6354
|
}
|
|
6129
6355
|
if ((node.type ?? "string") === "date") {
|
|
6130
6356
|
return env.getDateFormat(node);
|
|
6131
6357
|
}
|
|
6132
6358
|
return "";
|
|
6133
6359
|
}
|
|
6134
|
-
var renderEdit = (env, node, state, key) => {
|
|
6360
|
+
var renderEdit = (env, node, state, key, _path, itemContext) => {
|
|
6135
6361
|
const common = state.id ? ` id="${env.escapeHtml(state.id)}"` : "";
|
|
6136
6362
|
const enabled = state.enabled ?? env.normalizeBoolean(node.enabled, true);
|
|
6137
6363
|
const editable = state.editable ?? env.normalizeBoolean(node.editable, true);
|
|
6138
|
-
const placeholder = env.escapeHtml(resolvePlaceholder(env, node, state.placeholder));
|
|
6139
|
-
const value = env.escapeHtml(env.
|
|
6364
|
+
const placeholder = env.escapeHtml(resolvePlaceholder(env, node, itemContext, state.placeholder));
|
|
6365
|
+
const value = env.escapeHtml(env.resolveTextValue(state.text ?? node.text, itemContext));
|
|
6140
6366
|
const inputType = node.type === "password" ? "password" : "text";
|
|
6141
6367
|
const disabledAttribute = enabled ? "" : " disabled";
|
|
6142
6368
|
const readonlyAttribute = editable ? "" : " readonly";
|
|
@@ -6159,23 +6385,23 @@ var renderEdit = (env, node, state, key) => {
|
|
|
6159
6385
|
};
|
|
6160
6386
|
|
|
6161
6387
|
// src/lib/widgets/textarea.ts
|
|
6162
|
-
var renderTextarea = (env, node, state, key) => {
|
|
6388
|
+
var renderTextarea = (env, node, state, key, _path, itemContext) => {
|
|
6163
6389
|
const common = state.id ? ` id="${env.escapeHtml(state.id)}"` : "";
|
|
6164
6390
|
const enabled = state.enabled ?? env.normalizeBoolean(node.enabled, true);
|
|
6165
6391
|
const editable = state.editable ?? env.normalizeBoolean(node.editable, true);
|
|
6166
|
-
const placeholder = env.escapeHtml(env.
|
|
6167
|
-
const value = env.escapeHtml(state.text ?? env.
|
|
6392
|
+
const placeholder = env.escapeHtml(env.resolveTextValue(state.placeholder ?? node.placeholder, itemContext));
|
|
6393
|
+
const value = env.escapeHtml(state.text ?? env.resolveTextValue(node.text, itemContext));
|
|
6168
6394
|
const styleString = env.buildStyleString(node);
|
|
6169
6395
|
const styleAttribute = styleString ? ` style="${env.escapeHtml(styleString)}"` : "";
|
|
6170
6396
|
return `<textarea${common}${env.buildWidgetDataAttributes(key, node)} class="vjt-textarea" data-widget="textarea" placeholder="${placeholder}"${enabled ? "" : " disabled"}${editable ? "" : " readonly"}${styleAttribute}>${value}</textarea>`;
|
|
6171
6397
|
};
|
|
6172
6398
|
|
|
6173
6399
|
// src/lib/widgets/button.ts
|
|
6174
|
-
var renderButton = (env, node, state, key) => {
|
|
6400
|
+
var renderButton = (env, node, state, key, _path, itemContext) => {
|
|
6175
6401
|
const common = state.id ? ` id="${env.escapeHtml(state.id)}"` : "";
|
|
6176
6402
|
const enabled = state.enabled ?? env.normalizeBoolean(node.enabled, true);
|
|
6177
6403
|
const disabledAttribute = enabled ? "" : " disabled";
|
|
6178
|
-
const text = env.escapeHtml(env.
|
|
6404
|
+
const text = env.escapeHtml(env.resolveTextValue(state.text ?? node.text, itemContext));
|
|
6179
6405
|
const styleString = env.buildStyleString(node);
|
|
6180
6406
|
const styleAttribute = styleString ? ` style="${env.escapeHtml(styleString)}"` : "";
|
|
6181
6407
|
const buttonType = env.escapeHtml(node.type === "submit" ? "submit" : "button");
|
|
@@ -6204,7 +6430,10 @@ var renderCheckbox = (env, node, state, key) => {
|
|
|
6204
6430
|
|
|
6205
6431
|
// src/lib/widgets/image.ts
|
|
6206
6432
|
var renderImage = (env, node, state, key) => {
|
|
6207
|
-
const
|
|
6433
|
+
const rawBase64 = typeof state.base64 === "string" ? state.base64 : typeof node.base64 === "string" ? node.base64 : "";
|
|
6434
|
+
const rawUrl = sanitizeUrl(state.url ?? node.url ?? "", { allowRelative: true }) ?? "";
|
|
6435
|
+
const imageSource = rawBase64 ? rawBase64.startsWith("data:") ? rawBase64 : `data:image/png;base64,${rawBase64}` : rawUrl;
|
|
6436
|
+
const url = env.escapeHtml(imageSource);
|
|
6208
6437
|
const styleParts = [
|
|
6209
6438
|
"box-sizing:border-box",
|
|
6210
6439
|
"width:100%",
|
|
@@ -6224,10 +6453,10 @@ var renderImage = (env, node, state, key) => {
|
|
|
6224
6453
|
};
|
|
6225
6454
|
|
|
6226
6455
|
// src/lib/widgets/link.ts
|
|
6227
|
-
var renderLink = (env, node, state, key) => {
|
|
6456
|
+
var renderLink = (env, node, state, key, _path, itemContext) => {
|
|
6228
6457
|
const type = node.type === "button" || node.type === "text" ? node.type : "link";
|
|
6229
6458
|
const enabled = state.enabled ?? env.normalizeBoolean(node.enabled, true);
|
|
6230
|
-
const text = env.escapeHtml(env.
|
|
6459
|
+
const text = env.escapeHtml(env.resolveTextValue(state.text ?? node.text, itemContext));
|
|
6231
6460
|
const classes = `vjt-link vjt-link--${type}${enabled ? "" : " is-disabled"}`;
|
|
6232
6461
|
const styleString = env.buildStyleString(node);
|
|
6233
6462
|
const styleAttribute = styleString ? ` style="${env.escapeHtml(styleString)}"` : "";
|
|
@@ -6408,6 +6637,32 @@ function renderConfirmModalOverlay(env, modal) {
|
|
|
6408
6637
|
}
|
|
6409
6638
|
|
|
6410
6639
|
// src/lib/widgets/bindings.ts
|
|
6640
|
+
function readBlobAsDataUrl(blob) {
|
|
6641
|
+
return new Promise((resolve, reject) => {
|
|
6642
|
+
const reader = new FileReader();
|
|
6643
|
+
reader.onload = () => resolve(typeof reader.result === "string" ? reader.result : "");
|
|
6644
|
+
reader.onerror = () => reject(reader.error ?? new Error("Failed to read clipboard blob"));
|
|
6645
|
+
reader.readAsDataURL(blob);
|
|
6646
|
+
});
|
|
6647
|
+
}
|
|
6648
|
+
async function getPasteInputValue(event) {
|
|
6649
|
+
const clipboardData = event.clipboardData;
|
|
6650
|
+
if (!clipboardData) {
|
|
6651
|
+
return "";
|
|
6652
|
+
}
|
|
6653
|
+
for (const item of Array.from(clipboardData.items)) {
|
|
6654
|
+
if (!item.type.startsWith("image/")) {
|
|
6655
|
+
continue;
|
|
6656
|
+
}
|
|
6657
|
+
const file = item.getAsFile();
|
|
6658
|
+
if (!file) {
|
|
6659
|
+
continue;
|
|
6660
|
+
}
|
|
6661
|
+
const dataUrl = await readBlobAsDataUrl(file);
|
|
6662
|
+
return dataUrl;
|
|
6663
|
+
}
|
|
6664
|
+
return clipboardData.getData("text/plain");
|
|
6665
|
+
}
|
|
6411
6666
|
function bindKeyboardSubmitEvents(element, node, key, env) {
|
|
6412
6667
|
const hasKeyboardEvents = Boolean(
|
|
6413
6668
|
node.events?.onEnter?.length || node.events?.onShiftEnter?.length || node.events?.onControlEnter?.length
|
|
@@ -6430,6 +6685,10 @@ function bindKeyboardSubmitEvents(element, node, key, env) {
|
|
|
6430
6685
|
if (!eventName || !node.events?.[eventName]?.length) {
|
|
6431
6686
|
return;
|
|
6432
6687
|
}
|
|
6688
|
+
if (eventName !== "onShiftEnter") {
|
|
6689
|
+
event.preventDefault();
|
|
6690
|
+
event.stopPropagation();
|
|
6691
|
+
}
|
|
6433
6692
|
void env.dispatchWidgetEvent(node, eventName, key, env.getActionInputValueForElement(element, node), null);
|
|
6434
6693
|
});
|
|
6435
6694
|
}
|
|
@@ -6501,6 +6760,11 @@ function bindWidgetEvents(root, env) {
|
|
|
6501
6760
|
}
|
|
6502
6761
|
if (element instanceof HTMLInputElement && element.dataset.widget === "edit") {
|
|
6503
6762
|
bindKeyboardSubmitEvents(element, node, key, env);
|
|
6763
|
+
if (node.events?.onPaste?.length) {
|
|
6764
|
+
element.addEventListener("paste", (event) => {
|
|
6765
|
+
void getPasteInputValue(event).then((inputValue) => env.dispatchWidgetEvent(node, "onPaste", key, inputValue, null));
|
|
6766
|
+
});
|
|
6767
|
+
}
|
|
6504
6768
|
element.addEventListener("input", () => {
|
|
6505
6769
|
const state = env.stateByKey.get(key);
|
|
6506
6770
|
if (state) {
|
|
@@ -6519,6 +6783,11 @@ function bindWidgetEvents(root, env) {
|
|
|
6519
6783
|
}
|
|
6520
6784
|
if (element instanceof HTMLTextAreaElement && element.dataset.widget === "textarea") {
|
|
6521
6785
|
bindKeyboardSubmitEvents(element, node, key, env);
|
|
6786
|
+
if (node.events?.onPaste?.length) {
|
|
6787
|
+
element.addEventListener("paste", (event) => {
|
|
6788
|
+
void getPasteInputValue(event).then((inputValue) => env.dispatchWidgetEvent(node, "onPaste", key, inputValue, null));
|
|
6789
|
+
});
|
|
6790
|
+
}
|
|
6522
6791
|
element.addEventListener("input", () => {
|
|
6523
6792
|
const state = env.stateByKey.get(key);
|
|
6524
6793
|
if (state) {
|
|
@@ -6984,6 +7253,7 @@ var RuntimeRenderer = class {
|
|
|
6984
7253
|
i18n;
|
|
6985
7254
|
actionsMap;
|
|
6986
7255
|
requestsMap;
|
|
7256
|
+
routes;
|
|
6987
7257
|
sseConfigs;
|
|
6988
7258
|
systemEvents;
|
|
6989
7259
|
resourceManager;
|
|
@@ -7003,6 +7273,7 @@ var RuntimeRenderer = class {
|
|
|
7003
7273
|
vars = /* @__PURE__ */ new Map();
|
|
7004
7274
|
resizeHandler = null;
|
|
7005
7275
|
pointerHandler = null;
|
|
7276
|
+
popstateHandler = null;
|
|
7006
7277
|
eventSources = [];
|
|
7007
7278
|
referenceRuntime;
|
|
7008
7279
|
networkRuntime;
|
|
@@ -7015,6 +7286,11 @@ var RuntimeRenderer = class {
|
|
|
7015
7286
|
activeConfirmModal = null;
|
|
7016
7287
|
lastPointer = { x: 24, y: 24 };
|
|
7017
7288
|
pendingResetModalIds = /* @__PURE__ */ new Set();
|
|
7289
|
+
pendingScrollAction = null;
|
|
7290
|
+
pendingScrollFrameId = null;
|
|
7291
|
+
activeScrollAnimationFrameId = null;
|
|
7292
|
+
pendingCursorReference = null;
|
|
7293
|
+
pendingCursorFrameId = null;
|
|
7018
7294
|
constructor(description, options = {}) {
|
|
7019
7295
|
this.description = deepClone(description);
|
|
7020
7296
|
this.resourceManager = options.resourceManager ?? null;
|
|
@@ -7028,6 +7304,8 @@ var RuntimeRenderer = class {
|
|
|
7028
7304
|
this.theme = options.theme === "light" ? "light" : "dark";
|
|
7029
7305
|
this.actionsMap = options.actionsMap ?? this.resourceManager?.getActionsMap() ?? {};
|
|
7030
7306
|
this.requestsMap = options.requestsMap ?? this.resourceManager?.getRequestsMap() ?? {};
|
|
7307
|
+
const resolvedRoutes = options.routes ?? this.resourceManager?.getRoutes() ?? [];
|
|
7308
|
+
this.routes = Array.isArray(resolvedRoutes) ? resolvedRoutes.map((route) => ({ ...route })) : Array.isArray(resolvedRoutes?.routes) ? resolvedRoutes.routes.map((route) => ({ ...route })) : [];
|
|
7031
7309
|
const resolvedSseConfigs = options.sseConfigs ?? this.resourceManager?.getSseConfigs() ?? [];
|
|
7032
7310
|
this.sseConfigs = Array.isArray(resolvedSseConfigs) ? resolvedSseConfigs : resolvedSseConfigs ? [resolvedSseConfigs] : [];
|
|
7033
7311
|
this.systemEvents = options.systemEvents ?? this.resourceManager?.getSystemEvents() ?? {};
|
|
@@ -7041,12 +7319,15 @@ var RuntimeRenderer = class {
|
|
|
7041
7319
|
stateByKey: this.stateByKey,
|
|
7042
7320
|
nodeById: this.nodeById,
|
|
7043
7321
|
nodeByKey: this.nodeByKey,
|
|
7322
|
+
getUiResource: (ref) => this.resourceManager?.getUi(ref) ?? null,
|
|
7044
7323
|
getAppValue: (name) => {
|
|
7045
7324
|
switch (name) {
|
|
7046
7325
|
case "language":
|
|
7047
7326
|
return this.language;
|
|
7048
7327
|
case "theme":
|
|
7049
7328
|
return this.theme;
|
|
7329
|
+
case "isMobile":
|
|
7330
|
+
return typeof window === "undefined" ? false : isMobileViewport();
|
|
7050
7331
|
case "urlPath":
|
|
7051
7332
|
return typeof window === "undefined" ? "" : window.location.pathname;
|
|
7052
7333
|
default:
|
|
@@ -7111,9 +7392,14 @@ var RuntimeRenderer = class {
|
|
|
7111
7392
|
assignReference: (reference, inputValue, currentValue, options2) => this.referenceRuntime.assignReference(reference, inputValue, currentValue, options2),
|
|
7112
7393
|
resolveMappedValue: (template, currentValue, responseValue) => this.referenceRuntime.resolveMappedValue(template, currentValue, responseValue),
|
|
7113
7394
|
setWidgetEnabled: (widgetId, enabled) => this.setWidgetEnabled(widgetId, enabled),
|
|
7395
|
+
setWidgetVisible: (widgetId, visible) => this.setWidgetVisible(widgetId, visible),
|
|
7114
7396
|
clearWidget: (widgetId) => this.clearWidget(widgetId),
|
|
7115
7397
|
clearListElementState: (listKey) => this.clearListElementState(listKey),
|
|
7116
7398
|
focusWidget: (reference) => this.focusWidget(reference),
|
|
7399
|
+
moveCursorToEnd: (reference) => this.moveCursorToEnd(reference),
|
|
7400
|
+
scrollWidgetToTop: (reference) => this.scrollWidgetToTop(reference),
|
|
7401
|
+
scrollWidgetToBottom: (reference) => this.scrollWidgetToBottom(reference),
|
|
7402
|
+
scrollWidgetToElementByIndex: (reference, index) => this.scrollWidgetToElementByIndex(reference, index),
|
|
7117
7403
|
playAudio: (value) => this.voiceRuntime.play(value),
|
|
7118
7404
|
stopPlaying: () => this.voiceRuntime.stopPlaying(),
|
|
7119
7405
|
copyToClipboard: (value) => this.copyToClipboard(value),
|
|
@@ -7121,7 +7407,7 @@ var RuntimeRenderer = class {
|
|
|
7121
7407
|
confirmModal: (args, inputValue) => this.confirmModal(args, inputValue),
|
|
7122
7408
|
startRecording: () => this.voiceRuntime.startRecording(),
|
|
7123
7409
|
stopRecording: () => Promise.resolve(this.voiceRuntime.stopRecording()),
|
|
7124
|
-
startListening: () => this.voiceRuntime.startListening(),
|
|
7410
|
+
startListening: (args) => this.voiceRuntime.startListening(args),
|
|
7125
7411
|
stopListening: () => this.voiceRuntime.stopListening(),
|
|
7126
7412
|
setUiReference: (widgetId, resourceRef) => {
|
|
7127
7413
|
const node = this.nodeById.get(widgetId);
|
|
@@ -7151,11 +7437,12 @@ var RuntimeRenderer = class {
|
|
|
7151
7437
|
getInlineActions: (node) => this.getInlineActions(node),
|
|
7152
7438
|
isWidgetEnabled: (node, key) => this.isWidgetEnabled(node, key)
|
|
7153
7439
|
});
|
|
7154
|
-
this.indexStaticNodes(this.
|
|
7440
|
+
this.indexStaticNodes(this.getActiveDescription(), "root");
|
|
7155
7441
|
this.applyRuntimeSnapshot(this.initialRuntimeSnapshot);
|
|
7156
7442
|
}
|
|
7157
7443
|
renderMarkup() {
|
|
7158
|
-
const
|
|
7444
|
+
const activeDescription = this.getActiveDescription();
|
|
7445
|
+
const markup = `${this.renderNode(activeDescription, "root", null)}${this.renderGlobalOverlays()}`;
|
|
7159
7446
|
logRuntimeDebug(this.debugLogging, "html", markup);
|
|
7160
7447
|
return markup;
|
|
7161
7448
|
}
|
|
@@ -7205,6 +7492,17 @@ var RuntimeRenderer = class {
|
|
|
7205
7492
|
};
|
|
7206
7493
|
window.addEventListener("pointerdown", handlePointer);
|
|
7207
7494
|
this.pointerHandler = handlePointer;
|
|
7495
|
+
const handlePopState = () => {
|
|
7496
|
+
void (async () => {
|
|
7497
|
+
await this.runSystemEvent("onBeforeNavigate");
|
|
7498
|
+
await this.rerenderRoot();
|
|
7499
|
+
await this.runSystemEvent("onAfterNavigate");
|
|
7500
|
+
})().catch((error) => {
|
|
7501
|
+
logRuntimeError("popstate", error);
|
|
7502
|
+
});
|
|
7503
|
+
};
|
|
7504
|
+
window.addEventListener("popstate", handlePopState);
|
|
7505
|
+
this.popstateHandler = handlePopState;
|
|
7208
7506
|
return () => {
|
|
7209
7507
|
if (this.resizeHandler) {
|
|
7210
7508
|
window.removeEventListener("resize", this.resizeHandler);
|
|
@@ -7212,6 +7510,9 @@ var RuntimeRenderer = class {
|
|
|
7212
7510
|
if (this.pointerHandler) {
|
|
7213
7511
|
window.removeEventListener("pointerdown", this.pointerHandler);
|
|
7214
7512
|
}
|
|
7513
|
+
if (this.popstateHandler) {
|
|
7514
|
+
window.removeEventListener("popstate", this.popstateHandler);
|
|
7515
|
+
}
|
|
7215
7516
|
for (const source of this.eventSources) {
|
|
7216
7517
|
source.close();
|
|
7217
7518
|
}
|
|
@@ -7234,6 +7535,8 @@ var RuntimeRenderer = class {
|
|
|
7234
7535
|
this.attachInputBehavior(this.root);
|
|
7235
7536
|
this.attachWidgetEvents(this.root);
|
|
7236
7537
|
restoreElementState(this.root, preservedState, this.stateByKey);
|
|
7538
|
+
this.applyPendingScrollAction();
|
|
7539
|
+
this.applyPendingCursorAction();
|
|
7237
7540
|
this.activeContextMenu = adjustContextMenuPosition(this.root, this.activeContextMenu);
|
|
7238
7541
|
this.pendingResetModalIds.clear();
|
|
7239
7542
|
if (typeof this.onRuntimeSnapshot === "function") {
|
|
@@ -7413,7 +7716,113 @@ var RuntimeRenderer = class {
|
|
|
7413
7716
|
this.nodeByKey.clear();
|
|
7414
7717
|
this.nodeById.clear();
|
|
7415
7718
|
this.stateById.clear();
|
|
7416
|
-
this.indexStaticNodes(this.
|
|
7719
|
+
this.indexStaticNodes(this.getActiveDescription(), "root");
|
|
7720
|
+
}
|
|
7721
|
+
getActiveDescription() {
|
|
7722
|
+
if (this.hasRoutes() && !this.isCurrentPathValid()) {
|
|
7723
|
+
return this.buildNotFoundDescription(this.getCurrentPathname());
|
|
7724
|
+
}
|
|
7725
|
+
return this.description;
|
|
7726
|
+
}
|
|
7727
|
+
hasRoutes() {
|
|
7728
|
+
return this.routes.length > 0;
|
|
7729
|
+
}
|
|
7730
|
+
isCurrentPathValid() {
|
|
7731
|
+
const pathname = this.getCurrentPathname();
|
|
7732
|
+
if (pathname === "/") {
|
|
7733
|
+
return true;
|
|
7734
|
+
}
|
|
7735
|
+
return this.routes.some((entry) => this.normalizePathname(entry.path) === pathname);
|
|
7736
|
+
}
|
|
7737
|
+
getCurrentPathname() {
|
|
7738
|
+
if (typeof window === "undefined") {
|
|
7739
|
+
return "/";
|
|
7740
|
+
}
|
|
7741
|
+
return this.normalizePathname(window.location.pathname);
|
|
7742
|
+
}
|
|
7743
|
+
normalizePathname(path) {
|
|
7744
|
+
if (!path || path === "/" || path === "/index.html") {
|
|
7745
|
+
return "/";
|
|
7746
|
+
}
|
|
7747
|
+
const normalized = path.startsWith("/") ? path : `/${path}`;
|
|
7748
|
+
return normalized.length > 1 ? normalized.replace(/\/+$/, "") : normalized;
|
|
7749
|
+
}
|
|
7750
|
+
buildNotFoundDescription(pathname) {
|
|
7751
|
+
const dictionary = this.getBuiltinNotFoundTexts();
|
|
7752
|
+
return {
|
|
7753
|
+
widget: "panel",
|
|
7754
|
+
id: "vjt-not-found-panel",
|
|
7755
|
+
css: "position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);width:min(520px, calc(100vw - 32px));padding:28px;border-radius:20px;background:color-mix(in srgb, var(--vjt-surface) 92%, black 8%);border:1px solid var(--vjt-border);box-shadow:var(--vjt-shadow);",
|
|
7756
|
+
child: {
|
|
7757
|
+
widget: "container-layout",
|
|
7758
|
+
type: "vertical",
|
|
7759
|
+
children: [
|
|
7760
|
+
{
|
|
7761
|
+
minHeightPx: 64,
|
|
7762
|
+
maxHeightPx: 64,
|
|
7763
|
+
marginBottomPx: 8,
|
|
7764
|
+
child: {
|
|
7765
|
+
widget: "static-text",
|
|
7766
|
+
heading: "h1",
|
|
7767
|
+
text: dictionary.title,
|
|
7768
|
+
css: "font-size:42px;font-weight:800;letter-spacing:0.04em;text-align:center;justify-content:center;"
|
|
7769
|
+
}
|
|
7770
|
+
},
|
|
7771
|
+
{
|
|
7772
|
+
minHeightPx: 36,
|
|
7773
|
+
maxHeightPx: 36,
|
|
7774
|
+
marginBottomPx: 8,
|
|
7775
|
+
child: {
|
|
7776
|
+
widget: "static-text",
|
|
7777
|
+
heading: "h3",
|
|
7778
|
+
text: dictionary.caption,
|
|
7779
|
+
css: "text-align:center;justify-content:center;"
|
|
7780
|
+
}
|
|
7781
|
+
},
|
|
7782
|
+
{
|
|
7783
|
+
minHeightPx: 64,
|
|
7784
|
+
marginBottomPx: 16,
|
|
7785
|
+
child: {
|
|
7786
|
+
widget: "static-text",
|
|
7787
|
+
text: `${dictionary.note} ${pathname}`,
|
|
7788
|
+
css: "text-align:center;justify-content:center;color:var(--vjt-muted);"
|
|
7789
|
+
}
|
|
7790
|
+
},
|
|
7791
|
+
{
|
|
7792
|
+
minHeightPx: 52,
|
|
7793
|
+
maxHeightPx: 52,
|
|
7794
|
+
child: {
|
|
7795
|
+
widget: "button",
|
|
7796
|
+
text: dictionary.home,
|
|
7797
|
+
events: {
|
|
7798
|
+
onClick: [
|
|
7799
|
+
{
|
|
7800
|
+
action: "navigate /"
|
|
7801
|
+
}
|
|
7802
|
+
]
|
|
7803
|
+
}
|
|
7804
|
+
}
|
|
7805
|
+
}
|
|
7806
|
+
]
|
|
7807
|
+
}
|
|
7808
|
+
};
|
|
7809
|
+
}
|
|
7810
|
+
getBuiltinNotFoundTexts() {
|
|
7811
|
+
const language = this.language.toLowerCase();
|
|
7812
|
+
if (language.startsWith("ru")) {
|
|
7813
|
+
return {
|
|
7814
|
+
title: "404",
|
|
7815
|
+
caption: "\u0421\u0442\u0440\u0430\u043D\u0438\u0446\u0430 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u0430",
|
|
7816
|
+
note: "\u0417\u0430\u043F\u0440\u043E\u0448\u0435\u043D\u043D\u044B\u0439 \u043F\u0443\u0442\u044C \u043D\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442:",
|
|
7817
|
+
home: "\u041D\u0430 \u0433\u043B\u0430\u0432\u043D\u0443\u044E"
|
|
7818
|
+
};
|
|
7819
|
+
}
|
|
7820
|
+
return {
|
|
7821
|
+
title: "404",
|
|
7822
|
+
caption: "Page not found",
|
|
7823
|
+
note: "The requested path does not exist:",
|
|
7824
|
+
home: "Go home"
|
|
7825
|
+
};
|
|
7417
7826
|
}
|
|
7418
7827
|
getUiReferenceTarget(node, key) {
|
|
7419
7828
|
if (node.widget !== "ui-reference") {
|
|
@@ -7648,6 +8057,9 @@ var RuntimeRenderer = class {
|
|
|
7648
8057
|
existing.id = stateId2;
|
|
7649
8058
|
this.stateById.set(stateId2, existing);
|
|
7650
8059
|
}
|
|
8060
|
+
if (existing.visible === void 0) {
|
|
8061
|
+
existing.visible = normalizeBoolean(node.visible, true);
|
|
8062
|
+
}
|
|
7651
8063
|
this.syncStateDefinition(existing, node);
|
|
7652
8064
|
return existing;
|
|
7653
8065
|
}
|
|
@@ -7698,7 +8110,8 @@ var RuntimeRenderer = class {
|
|
|
7698
8110
|
id: stateId,
|
|
7699
8111
|
name: node.name,
|
|
7700
8112
|
text: typeof node.text === "string" ? node.text : void 0,
|
|
7701
|
-
url: "url" in node ? typeof node.url === "string" ? node.url : "" : void 0
|
|
8113
|
+
url: "url" in node ? typeof node.url === "string" ? node.url : "" : void 0,
|
|
8114
|
+
base64: "base64" in node ? typeof node.base64 === "string" ? node.base64 : "" : void 0
|
|
7702
8115
|
};
|
|
7703
8116
|
break;
|
|
7704
8117
|
case "conditional-container":
|
|
@@ -7871,6 +8284,20 @@ var RuntimeRenderer = class {
|
|
|
7871
8284
|
}
|
|
7872
8285
|
return `??${key}??`;
|
|
7873
8286
|
}
|
|
8287
|
+
resolveTextValue(value, itemContext) {
|
|
8288
|
+
if (typeof value !== "string") {
|
|
8289
|
+
return value ?? "";
|
|
8290
|
+
}
|
|
8291
|
+
if (value.startsWith("$ref:")) {
|
|
8292
|
+
const resolved = this.referenceRuntime.resolveReference(value.slice(5), itemContext, null);
|
|
8293
|
+
if (resolved == null) {
|
|
8294
|
+
return "";
|
|
8295
|
+
}
|
|
8296
|
+
const resolvedText = typeof resolved === "string" ? resolved : typeof resolved === "number" || typeof resolved === "boolean" || typeof resolved === "bigint" ? String(resolved) : JSON.stringify(resolved);
|
|
8297
|
+
return this.resolveI18nValue(resolvedText);
|
|
8298
|
+
}
|
|
8299
|
+
return this.resolveI18nValue(value);
|
|
8300
|
+
}
|
|
7874
8301
|
buildWidgetDataAttributes(key, node) {
|
|
7875
8302
|
const parts = [` data-widget-key="${escapeHtml(key)}"`];
|
|
7876
8303
|
if (node.id) {
|
|
@@ -7889,6 +8316,7 @@ var RuntimeRenderer = class {
|
|
|
7889
8316
|
escapeHtml,
|
|
7890
8317
|
buildStyleString: (node) => this.buildStyleString(node),
|
|
7891
8318
|
resolveI18nValue: (value) => this.resolveI18nValue(value),
|
|
8319
|
+
resolveTextValue: (value, itemContext) => this.resolveTextValue(value, itemContext),
|
|
7892
8320
|
getUiResource: (ref) => this.resourceManager?.getUi(ref) ?? null,
|
|
7893
8321
|
resolveReference: (reference, currentValue, responseValue) => this.referenceRuntime.resolveReference(reference, currentValue, responseValue),
|
|
7894
8322
|
buildWidgetDataAttributes: (key, node) => this.buildWidgetDataAttributes(key, node),
|
|
@@ -7920,6 +8348,9 @@ var RuntimeRenderer = class {
|
|
|
7920
8348
|
const key = itemContext ? this.resolveItemNodeKey(node, itemContext.listId, itemContext.index, path) : this.resolveNodeKey(node, path);
|
|
7921
8349
|
this.nodeByKey.set(key, node);
|
|
7922
8350
|
const state = this.ensureWidgetState(node, key);
|
|
8351
|
+
if ((state.visible ?? normalizeBoolean(node.visible, true)) === false) {
|
|
8352
|
+
return "";
|
|
8353
|
+
}
|
|
7923
8354
|
switch (node.widget) {
|
|
7924
8355
|
case "adaptive-layout":
|
|
7925
8356
|
return renderAdaptiveLayout(env, node, state, key, path, itemContext);
|
|
@@ -8409,6 +8840,13 @@ var RuntimeRenderer = class {
|
|
|
8409
8840
|
}
|
|
8410
8841
|
state.enabled = enabled;
|
|
8411
8842
|
}
|
|
8843
|
+
setWidgetVisible(widgetId, visible) {
|
|
8844
|
+
const state = this.stateById.get(widgetId);
|
|
8845
|
+
if (!state) {
|
|
8846
|
+
return;
|
|
8847
|
+
}
|
|
8848
|
+
state.visible = visible;
|
|
8849
|
+
}
|
|
8412
8850
|
clearListElementState(listKey) {
|
|
8413
8851
|
this.referenceRuntime.clearListElementState(listKey);
|
|
8414
8852
|
}
|
|
@@ -8434,6 +8872,199 @@ var RuntimeRenderer = class {
|
|
|
8434
8872
|
nestedTarget.focus({ preventScroll: true });
|
|
8435
8873
|
}
|
|
8436
8874
|
}
|
|
8875
|
+
moveCursorToEnd(reference) {
|
|
8876
|
+
this.pendingCursorReference = reference;
|
|
8877
|
+
if (typeof window === "undefined" || this.pendingCursorFrameId !== null) {
|
|
8878
|
+
return;
|
|
8879
|
+
}
|
|
8880
|
+
this.pendingCursorFrameId = window.requestAnimationFrame(() => {
|
|
8881
|
+
this.pendingCursorFrameId = null;
|
|
8882
|
+
this.applyPendingCursorAction();
|
|
8883
|
+
});
|
|
8884
|
+
}
|
|
8885
|
+
scrollWidgetToTop(reference) {
|
|
8886
|
+
this.pendingScrollAction = { kind: "top", reference };
|
|
8887
|
+
this.schedulePendingScrollAction();
|
|
8888
|
+
}
|
|
8889
|
+
scrollWidgetToBottom(reference) {
|
|
8890
|
+
this.pendingScrollAction = { kind: "bottom", reference };
|
|
8891
|
+
this.schedulePendingScrollAction();
|
|
8892
|
+
}
|
|
8893
|
+
scrollWidgetToElementByIndex(reference, indexValue) {
|
|
8894
|
+
this.pendingScrollAction = { kind: "index", reference, indexValue };
|
|
8895
|
+
this.schedulePendingScrollAction();
|
|
8896
|
+
}
|
|
8897
|
+
schedulePendingScrollAction() {
|
|
8898
|
+
if (typeof window === "undefined" || this.pendingScrollFrameId !== null) {
|
|
8899
|
+
return;
|
|
8900
|
+
}
|
|
8901
|
+
this.pendingScrollFrameId = window.requestAnimationFrame(() => {
|
|
8902
|
+
this.pendingScrollFrameId = null;
|
|
8903
|
+
this.applyPendingScrollAction();
|
|
8904
|
+
});
|
|
8905
|
+
}
|
|
8906
|
+
applyPendingScrollAction() {
|
|
8907
|
+
if (!this.pendingScrollAction) {
|
|
8908
|
+
return;
|
|
8909
|
+
}
|
|
8910
|
+
const pending = this.pendingScrollAction;
|
|
8911
|
+
if (pending.kind === "top") {
|
|
8912
|
+
this.animateScrollTop(pending.reference, 0);
|
|
8913
|
+
this.pendingScrollAction = null;
|
|
8914
|
+
return;
|
|
8915
|
+
}
|
|
8916
|
+
if (pending.kind === "bottom") {
|
|
8917
|
+
const element = this.findScrollableWidgetElement(pending.reference);
|
|
8918
|
+
if (!element) {
|
|
8919
|
+
return;
|
|
8920
|
+
}
|
|
8921
|
+
this.animateScrollTop(pending.reference, element.scrollHeight);
|
|
8922
|
+
this.pendingScrollAction = null;
|
|
8923
|
+
return;
|
|
8924
|
+
}
|
|
8925
|
+
const index = this.toScrollableIndex(pending.indexValue);
|
|
8926
|
+
if (index === null) {
|
|
8927
|
+
this.pendingScrollAction = null;
|
|
8928
|
+
return;
|
|
8929
|
+
}
|
|
8930
|
+
this.performScrollToElementByIndex(pending.reference, index);
|
|
8931
|
+
this.pendingScrollAction = null;
|
|
8932
|
+
}
|
|
8933
|
+
performScrollToElementByIndex(reference, index) {
|
|
8934
|
+
const element = this.findScrollableWidgetElement(reference);
|
|
8935
|
+
if (!element) {
|
|
8936
|
+
return;
|
|
8937
|
+
}
|
|
8938
|
+
if (element instanceof HTMLSelectElement) {
|
|
8939
|
+
const option = element.options.item(index);
|
|
8940
|
+
if (!option) {
|
|
8941
|
+
return;
|
|
8942
|
+
}
|
|
8943
|
+
const maxScrollTop = Math.max(0, element.scrollHeight - element.clientHeight);
|
|
8944
|
+
this.animateScrollTop(reference, Math.max(0, Math.min(option.offsetTop, maxScrollTop)));
|
|
8945
|
+
return;
|
|
8946
|
+
}
|
|
8947
|
+
const listElement = element.querySelector(`.vjt-list-element[data-list-index="${index}"], .vjt-grid-cell[data-list-index="${index}"]`);
|
|
8948
|
+
if (listElement instanceof HTMLElement) {
|
|
8949
|
+
const maxScrollTop = Math.max(0, element.scrollHeight - element.clientHeight);
|
|
8950
|
+
const containerRect = element.getBoundingClientRect();
|
|
8951
|
+
const itemRect = listElement.getBoundingClientRect();
|
|
8952
|
+
const targetTop = element.scrollTop + (itemRect.top - containerRect.top);
|
|
8953
|
+
const remainingContentHeight = element.scrollHeight - targetTop;
|
|
8954
|
+
const nextScrollTop = remainingContentHeight > element.clientHeight ? Math.max(0, Math.min(targetTop, maxScrollTop)) : maxScrollTop;
|
|
8955
|
+
this.animateScrollTop(reference, nextScrollTop);
|
|
8956
|
+
}
|
|
8957
|
+
}
|
|
8958
|
+
animateScrollTop(reference, targetTop) {
|
|
8959
|
+
const resolveLiveElement = () => {
|
|
8960
|
+
const resolved = this.findScrollableWidgetElement(reference);
|
|
8961
|
+
return resolved instanceof HTMLElement ? resolved : null;
|
|
8962
|
+
};
|
|
8963
|
+
const getMaxScrollTop = (element) => Math.max(
|
|
8964
|
+
0,
|
|
8965
|
+
element.scrollHeight - element.clientHeight
|
|
8966
|
+
);
|
|
8967
|
+
const initialElement = resolveLiveElement();
|
|
8968
|
+
if (!initialElement) {
|
|
8969
|
+
return;
|
|
8970
|
+
}
|
|
8971
|
+
if (typeof window === "undefined") {
|
|
8972
|
+
initialElement.scrollTop = Math.max(0, Math.min(targetTop, getMaxScrollTop(initialElement)));
|
|
8973
|
+
return;
|
|
8974
|
+
}
|
|
8975
|
+
if (this.activeScrollAnimationFrameId !== null) {
|
|
8976
|
+
window.cancelAnimationFrame(this.activeScrollAnimationFrameId);
|
|
8977
|
+
this.activeScrollAnimationFrameId = null;
|
|
8978
|
+
}
|
|
8979
|
+
const startTop = initialElement.scrollTop;
|
|
8980
|
+
const clampedTargetTop = Math.max(0, Math.min(targetTop, getMaxScrollTop(initialElement)));
|
|
8981
|
+
const delta = clampedTargetTop - startTop;
|
|
8982
|
+
if (Math.abs(delta) < 1) {
|
|
8983
|
+
initialElement.scrollTop = clampedTargetTop;
|
|
8984
|
+
return;
|
|
8985
|
+
}
|
|
8986
|
+
const durationMs = 300;
|
|
8987
|
+
const startTime = window.performance.now();
|
|
8988
|
+
const easeInOutCubic = (t) => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
|
|
8989
|
+
const tick = (now) => {
|
|
8990
|
+
const liveElement = resolveLiveElement();
|
|
8991
|
+
if (!liveElement) {
|
|
8992
|
+
this.activeScrollAnimationFrameId = null;
|
|
8993
|
+
return;
|
|
8994
|
+
}
|
|
8995
|
+
const elapsed = now - startTime;
|
|
8996
|
+
const progress = Math.min(1, elapsed / durationMs);
|
|
8997
|
+
const liveTargetTop = Math.max(0, Math.min(clampedTargetTop, getMaxScrollTop(liveElement)));
|
|
8998
|
+
liveElement.scrollTop = startTop + (liveTargetTop - startTop) * easeInOutCubic(progress);
|
|
8999
|
+
if (progress < 1) {
|
|
9000
|
+
this.activeScrollAnimationFrameId = window.requestAnimationFrame(tick);
|
|
9001
|
+
} else {
|
|
9002
|
+
liveElement.scrollTop = liveTargetTop;
|
|
9003
|
+
this.activeScrollAnimationFrameId = null;
|
|
9004
|
+
}
|
|
9005
|
+
};
|
|
9006
|
+
this.activeScrollAnimationFrameId = window.requestAnimationFrame(tick);
|
|
9007
|
+
}
|
|
9008
|
+
applyPendingCursorAction() {
|
|
9009
|
+
if (!(this.root instanceof HTMLElement) || !this.pendingCursorReference) {
|
|
9010
|
+
return;
|
|
9011
|
+
}
|
|
9012
|
+
const reference = this.pendingCursorReference;
|
|
9013
|
+
const widgetId = reference.split(".")[0]?.trim();
|
|
9014
|
+
if (!widgetId) {
|
|
9015
|
+
this.pendingCursorReference = null;
|
|
9016
|
+
return;
|
|
9017
|
+
}
|
|
9018
|
+
const directTarget = this.root.querySelector(`#${CSS.escape(widgetId)}`);
|
|
9019
|
+
const widgetRoot = directTarget ?? this.root.querySelector(`[data-widget-id="${CSS.escape(widgetId)}"]`);
|
|
9020
|
+
if (!widgetRoot) {
|
|
9021
|
+
return;
|
|
9022
|
+
}
|
|
9023
|
+
const inputTarget = widgetRoot instanceof HTMLInputElement || widgetRoot instanceof HTMLTextAreaElement ? widgetRoot : widgetRoot.querySelector("input, textarea");
|
|
9024
|
+
if (!inputTarget || inputTarget.disabled) {
|
|
9025
|
+
this.pendingCursorReference = null;
|
|
9026
|
+
return;
|
|
9027
|
+
}
|
|
9028
|
+
if (typeof inputTarget.focus === "function") {
|
|
9029
|
+
inputTarget.focus({ preventScroll: true });
|
|
9030
|
+
}
|
|
9031
|
+
if (typeof inputTarget.setSelectionRange === "function") {
|
|
9032
|
+
const end = inputTarget.value.length;
|
|
9033
|
+
inputTarget.setSelectionRange(end, end);
|
|
9034
|
+
}
|
|
9035
|
+
this.pendingCursorReference = null;
|
|
9036
|
+
}
|
|
9037
|
+
findScrollableWidgetElement(reference) {
|
|
9038
|
+
if (!(this.root instanceof HTMLElement)) {
|
|
9039
|
+
return null;
|
|
9040
|
+
}
|
|
9041
|
+
const widgetId = reference.split(".")[0]?.trim();
|
|
9042
|
+
if (!widgetId) {
|
|
9043
|
+
return null;
|
|
9044
|
+
}
|
|
9045
|
+
const directTarget = this.root.querySelector(`#${CSS.escape(widgetId)}`);
|
|
9046
|
+
if (directTarget) {
|
|
9047
|
+
return directTarget;
|
|
9048
|
+
}
|
|
9049
|
+
for (const element of Array.from(this.root.querySelectorAll("[data-widget-id]"))) {
|
|
9050
|
+
if (element.dataset.widgetId === widgetId) {
|
|
9051
|
+
return element;
|
|
9052
|
+
}
|
|
9053
|
+
}
|
|
9054
|
+
return null;
|
|
9055
|
+
}
|
|
9056
|
+
toScrollableIndex(value) {
|
|
9057
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
9058
|
+
return Math.max(0, Math.trunc(value));
|
|
9059
|
+
}
|
|
9060
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
9061
|
+
const parsed = Number.parseInt(value, 10);
|
|
9062
|
+
if (Number.isFinite(parsed)) {
|
|
9063
|
+
return Math.max(0, parsed);
|
|
9064
|
+
}
|
|
9065
|
+
}
|
|
9066
|
+
return null;
|
|
9067
|
+
}
|
|
8437
9068
|
};
|
|
8438
9069
|
function renderJson(description, options = {}) {
|
|
8439
9070
|
const renderer = new RuntimeRenderer(description, options);
|
|
@@ -8449,6 +9080,7 @@ var ResourceManager = class {
|
|
|
8449
9080
|
ui = /* @__PURE__ */ new Map();
|
|
8450
9081
|
actions = {};
|
|
8451
9082
|
requests = {};
|
|
9083
|
+
routes = [];
|
|
8452
9084
|
sse = {};
|
|
8453
9085
|
systemEvents = {};
|
|
8454
9086
|
i18n = {};
|
|
@@ -8459,6 +9091,7 @@ var ResourceManager = class {
|
|
|
8459
9091
|
}
|
|
8460
9092
|
this.actions = { ...input.actions ?? {} };
|
|
8461
9093
|
this.requests = { ...input.requests ?? {} };
|
|
9094
|
+
this.routes = Array.isArray(input.routes) ? input.routes.map((route) => ({ ...route })) : Array.isArray(input.routes?.routes) ? input.routes.routes.map((route) => ({ ...route })) : [];
|
|
8462
9095
|
this.sse = { ...input.sse ?? {} };
|
|
8463
9096
|
this.systemEvents = { ...input.systemEvents ?? {} };
|
|
8464
9097
|
this.i18n = { ...input.i18n ?? {} };
|
|
@@ -8476,6 +9109,9 @@ var ResourceManager = class {
|
|
|
8476
9109
|
getRequestsMap() {
|
|
8477
9110
|
return this.requests;
|
|
8478
9111
|
}
|
|
9112
|
+
getRoutes() {
|
|
9113
|
+
return this.routes.map((route) => ({ ...route }));
|
|
9114
|
+
}
|
|
8479
9115
|
getSseConfigs() {
|
|
8480
9116
|
return Object.values(this.sse);
|
|
8481
9117
|
}
|