@vortexm/vjt 0.1.16 → 0.1.18
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 +59 -2
- package/dist/lib/types.d.ts +1 -1
- package/dist/lib/widgets/bindings.d.ts +1 -1
- package/package.json +1 -1
- package/vjt-styles.css +6 -0
package/dist/index.js
CHANGED
|
@@ -4281,7 +4281,18 @@ var NetworkRuntime = class {
|
|
|
4281
4281
|
}
|
|
4282
4282
|
const contentType = response.headers.get("content-type") ?? "";
|
|
4283
4283
|
if (!contentType.toLowerCase().includes("text/event-stream")) {
|
|
4284
|
-
|
|
4284
|
+
const previewText = await response.text().catch(() => "");
|
|
4285
|
+
const normalizedPreview = previewText.replaceAll(/\s+/g, " ").trim().slice(0, 240);
|
|
4286
|
+
const error = new Error(`SSE response has unexpected content-type: ${contentType || "<empty>"}`);
|
|
4287
|
+
error.sseDetails = {
|
|
4288
|
+
status: response.status,
|
|
4289
|
+
statusText: response.statusText,
|
|
4290
|
+
responseUrl: response.url,
|
|
4291
|
+
redirected: response.redirected,
|
|
4292
|
+
contentType,
|
|
4293
|
+
bodyPreview: normalizedPreview
|
|
4294
|
+
};
|
|
4295
|
+
throw error;
|
|
4285
4296
|
}
|
|
4286
4297
|
logRuntimeDebug(this.debugLogging, "sse-open", {
|
|
4287
4298
|
url: safeUrl,
|
|
@@ -4335,7 +4346,8 @@ var NetworkRuntime = class {
|
|
|
4335
4346
|
reconnectReason = error instanceof Error ? error.message : "stream-error";
|
|
4336
4347
|
logRuntimeError("sseConnection", error, {
|
|
4337
4348
|
url: safeUrl,
|
|
4338
|
-
transport: "fetch"
|
|
4349
|
+
transport: "fetch",
|
|
4350
|
+
...typeof error === "object" && error !== null && "sseDetails" in error ? { response: error.sseDetails } : {}
|
|
4339
4351
|
});
|
|
4340
4352
|
} finally {
|
|
4341
4353
|
activeAbortController = null;
|
|
@@ -7898,6 +7910,7 @@ function samePath(left, right) {
|
|
|
7898
7910
|
// src/lib/render.ts
|
|
7899
7911
|
var DEFAULT_DATE_FORMAT = "dd.MM.yyyy";
|
|
7900
7912
|
var MOBILE_RATIO_THRESHOLD = 1.15;
|
|
7913
|
+
var SCROLL_END_DEBOUNCE_MS = 140;
|
|
7901
7914
|
var globalErrorLoggingAttached = false;
|
|
7902
7915
|
function escapeHtml(value) {
|
|
7903
7916
|
return String(value).replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
@@ -8076,6 +8089,7 @@ var RuntimeRenderer = class {
|
|
|
8076
8089
|
activeScrollAnimationFrameId = null;
|
|
8077
8090
|
pendingCursorReference = null;
|
|
8078
8091
|
pendingCursorFrameId = null;
|
|
8092
|
+
scrollEndTimeoutIds = /* @__PURE__ */ new Map();
|
|
8079
8093
|
constructor(description, options = {}) {
|
|
8080
8094
|
this.description = deepClone(description);
|
|
8081
8095
|
this.resourceManager = options.resourceManager ?? null;
|
|
@@ -8315,6 +8329,10 @@ var RuntimeRenderer = class {
|
|
|
8315
8329
|
window.clearTimeout(timeoutId);
|
|
8316
8330
|
}
|
|
8317
8331
|
this.toastTimeoutIds.clear();
|
|
8332
|
+
for (const timeoutId of this.scrollEndTimeoutIds.values()) {
|
|
8333
|
+
window.clearTimeout(timeoutId);
|
|
8334
|
+
}
|
|
8335
|
+
this.scrollEndTimeoutIds.clear();
|
|
8318
8336
|
this.voiceRuntime.dispose();
|
|
8319
8337
|
};
|
|
8320
8338
|
}
|
|
@@ -9504,6 +9522,7 @@ var RuntimeRenderer = class {
|
|
|
9504
9522
|
}
|
|
9505
9523
|
element.addEventListener("scroll", () => {
|
|
9506
9524
|
this.syncScrollStateForElement(element, key);
|
|
9525
|
+
this.scheduleScrollEndEvent(element, node, key);
|
|
9507
9526
|
}, { passive: true });
|
|
9508
9527
|
}
|
|
9509
9528
|
}
|
|
@@ -9825,6 +9844,7 @@ var RuntimeRenderer = class {
|
|
|
9825
9844
|
if (typeof window === "undefined") {
|
|
9826
9845
|
initialElement.scrollTop = Math.max(0, Math.min(targetTop, getMaxScrollTop(initialElement)));
|
|
9827
9846
|
this.syncScrollStateForReference(reference, initialElement);
|
|
9847
|
+
this.scheduleProgrammaticScrollEndForReference(reference);
|
|
9828
9848
|
return;
|
|
9829
9849
|
}
|
|
9830
9850
|
if (this.activeScrollAnimationFrameId !== null) {
|
|
@@ -9837,6 +9857,7 @@ var RuntimeRenderer = class {
|
|
|
9837
9857
|
if (Math.abs(delta) < 1) {
|
|
9838
9858
|
initialElement.scrollTop = clampedTargetTop;
|
|
9839
9859
|
this.syncScrollStateForReference(reference, initialElement);
|
|
9860
|
+
this.scheduleProgrammaticScrollEndForReference(reference);
|
|
9840
9861
|
return;
|
|
9841
9862
|
}
|
|
9842
9863
|
const durationMs = 300;
|
|
@@ -9858,6 +9879,7 @@ var RuntimeRenderer = class {
|
|
|
9858
9879
|
} else {
|
|
9859
9880
|
liveElement.scrollTop = liveTargetTop;
|
|
9860
9881
|
this.syncScrollStateForReference(reference, liveElement);
|
|
9882
|
+
this.scheduleProgrammaticScrollEndForReference(reference);
|
|
9861
9883
|
this.activeScrollAnimationFrameId = null;
|
|
9862
9884
|
}
|
|
9863
9885
|
};
|
|
@@ -9953,6 +9975,41 @@ var RuntimeRenderer = class {
|
|
|
9953
9975
|
state.isScrolledToTop = element.scrollTop <= 1;
|
|
9954
9976
|
state.isScrolledToBottom = element.scrollTop >= maxScrollTop - 1;
|
|
9955
9977
|
}
|
|
9978
|
+
scheduleScrollEndEvent(element, node, key) {
|
|
9979
|
+
if (!node.events?.onScroll?.length || typeof window === "undefined") {
|
|
9980
|
+
return;
|
|
9981
|
+
}
|
|
9982
|
+
const existingTimeoutId = this.scrollEndTimeoutIds.get(key);
|
|
9983
|
+
if (existingTimeoutId !== void 0) {
|
|
9984
|
+
window.clearTimeout(existingTimeoutId);
|
|
9985
|
+
}
|
|
9986
|
+
const timeoutId = window.setTimeout(() => {
|
|
9987
|
+
this.scrollEndTimeoutIds.delete(key);
|
|
9988
|
+
void this.actionRuntime.dispatchWidgetEvent(
|
|
9989
|
+
node,
|
|
9990
|
+
"onScroll",
|
|
9991
|
+
key,
|
|
9992
|
+
getActionInputValueForElement(element, node, this.stateByKey),
|
|
9993
|
+
null
|
|
9994
|
+
);
|
|
9995
|
+
}, SCROLL_END_DEBOUNCE_MS);
|
|
9996
|
+
this.scrollEndTimeoutIds.set(key, timeoutId);
|
|
9997
|
+
}
|
|
9998
|
+
scheduleProgrammaticScrollEndForReference(reference) {
|
|
9999
|
+
const element = this.findScrollableWidgetElement(reference);
|
|
10000
|
+
if (!(element instanceof HTMLElement)) {
|
|
10001
|
+
return;
|
|
10002
|
+
}
|
|
10003
|
+
const key = element.dataset.widgetKey ?? reference.split(".")[0]?.trim() ?? "";
|
|
10004
|
+
if (!key) {
|
|
10005
|
+
return;
|
|
10006
|
+
}
|
|
10007
|
+
const node = this.nodeByKey.get(key) ?? this.nodeById.get(key);
|
|
10008
|
+
if (!node || !isScrollableWidgetNode(node)) {
|
|
10009
|
+
return;
|
|
10010
|
+
}
|
|
10011
|
+
this.scheduleScrollEndEvent(element, node, key);
|
|
10012
|
+
}
|
|
9956
10013
|
toScrollableIndex(value) {
|
|
9957
10014
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
9958
10015
|
return Math.max(0, Math.trunc(value));
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export type LayoutType = 'vertical' | 'horizontal' | 'grid';
|
|
|
3
3
|
export type TextAlign = 'left' | 'center' | 'right';
|
|
4
4
|
export type VerticalAlign = 'top' | 'center' | 'bottom';
|
|
5
5
|
export type HeadingTag = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
|
|
6
|
-
export type WidgetEventName = 'onClick' | 'onUserValueChange' | 'onRefresh' | 'onEnter' | 'onShiftEnter' | 'onControlEnter' | 'onPaste';
|
|
6
|
+
export type WidgetEventName = 'onClick' | 'onUserValueChange' | 'onRefresh' | 'onEnter' | 'onShiftEnter' | 'onControlEnter' | 'onPaste' | 'onScroll';
|
|
7
7
|
export type SystemEventName = 'onBeforeRender' | 'onAfterRender' | 'onBeforeNavigate' | 'onAfterNavigate' | 'onLayoutSwitchToMobile' | 'onLayoutSwitchToDesktop' | 'onSpeechDetected' | 'onRecordingStarted' | 'onRecordingStopped' | 'onRecordingError' | 'onListeningError' | 'onListeringError' | 'onPlayFinished' | 'onPlayingStopped';
|
|
8
8
|
export type PrimitiveRequestType = 'int' | 'float' | 'boolean' | 'string';
|
|
9
9
|
export type ClosableRuntimeResource = {
|
|
@@ -12,7 +12,7 @@ type BindingEnvironment = {
|
|
|
12
12
|
}) => void;
|
|
13
13
|
updatePointerFromEvent: (event: MouseEvent) => void;
|
|
14
14
|
updateOwningListElementDescriptor: (element: HTMLElement, mutate: (descriptor: DescriptionNode) => void) => void;
|
|
15
|
-
dispatchWidgetEvent: (node: DescriptionNode, eventName: 'onClick' | 'onUserValueChange' | 'onEnter' | 'onShiftEnter' | 'onControlEnter' | 'onPaste', key: string, inputValue?: unknown, pointer?: {
|
|
15
|
+
dispatchWidgetEvent: (node: DescriptionNode, eventName: 'onClick' | 'onUserValueChange' | 'onEnter' | 'onShiftEnter' | 'onControlEnter' | 'onPaste' | 'onScroll', key: string, inputValue?: unknown, pointer?: {
|
|
16
16
|
x: number;
|
|
17
17
|
y: number;
|
|
18
18
|
} | null) => Promise<void>;
|
package/package.json
CHANGED
package/vjt-styles.css
CHANGED