@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 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
- throw new Error(`SSE response has unexpected content-type: ${contentType || "<empty>"}`);
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("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
@@ -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));
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vortexm/vjt",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
package/vjt-styles.css CHANGED
@@ -557,6 +557,12 @@ body {
557
557
  grid-area: 1 / 1;
558
558
  min-width: 0;
559
559
  min-height: 0;
560
+ position: relative;
561
+ pointer-events: none;
562
+ }
563
+
564
+ .vjt-overlay-layer > * {
565
+ pointer-events: auto;
560
566
  }
561
567
 
562
568
  .vjt-overlay-layer > .vjt-static-text,