@vortexm/vjt 0.1.8 → 0.1.10

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
@@ -4366,6 +4366,8 @@ var ReferenceRuntime = class {
4366
4366
  return state.isHidden ?? true;
4367
4367
  case "url":
4368
4368
  return state.url ?? "";
4369
+ case "base64":
4370
+ return state.base64 ?? "";
4369
4371
  case "elements":
4370
4372
  if (state.widget === "list") {
4371
4373
  return (state.elements ?? []).map((descriptor, index) => ({
@@ -4453,6 +4455,9 @@ var ReferenceRuntime = class {
4453
4455
  case "url":
4454
4456
  state.url = stringifyReferenceValue(inputValue);
4455
4457
  break;
4458
+ case "base64":
4459
+ state.base64 = stringifyReferenceValue(inputValue);
4460
+ break;
4456
4461
  case "condition":
4457
4462
  state.condition = Boolean(inputValue);
4458
4463
  break;
@@ -4645,9 +4650,11 @@ var ActionRuntime = class {
4645
4650
  assignReference;
4646
4651
  resolveMappedValue;
4647
4652
  setWidgetEnabled;
4653
+ setWidgetVisible;
4648
4654
  clearWidget;
4649
4655
  clearListElementState;
4650
4656
  focusWidget;
4657
+ moveCursorToEnd;
4651
4658
  scrollWidgetToTop;
4652
4659
  scrollWidgetToBottom;
4653
4660
  scrollWidgetToElementByIndex;
@@ -4685,9 +4692,11 @@ var ActionRuntime = class {
4685
4692
  this.assignReference = options.assignReference;
4686
4693
  this.resolveMappedValue = options.resolveMappedValue;
4687
4694
  this.setWidgetEnabled = options.setWidgetEnabled;
4695
+ this.setWidgetVisible = options.setWidgetVisible;
4688
4696
  this.clearWidget = options.clearWidget;
4689
4697
  this.clearListElementState = options.clearListElementState;
4690
4698
  this.focusWidget = options.focusWidget;
4699
+ this.moveCursorToEnd = options.moveCursorToEnd;
4691
4700
  this.scrollWidgetToTop = options.scrollWidgetToTop;
4692
4701
  this.scrollWidgetToBottom = options.scrollWidgetToBottom;
4693
4702
  this.scrollWidgetToElementByIndex = options.scrollWidgetToElementByIndex;
@@ -4883,6 +4892,14 @@ var ActionRuntime = class {
4883
4892
  this.setWidgetEnabled(name.slice(8), false);
4884
4893
  return null;
4885
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
+ }
4886
4903
  if (name.startsWith("clear ")) {
4887
4904
  this.clearWidget(name.slice(6));
4888
4905
  return null;
@@ -4891,6 +4908,10 @@ var ActionRuntime = class {
4891
4908
  this.focusWidget(name.slice(9));
4892
4909
  return null;
4893
4910
  }
4911
+ if (name.startsWith("moveCursorToEnd ")) {
4912
+ this.moveCursorToEnd(name.slice(16));
4913
+ return null;
4914
+ }
4894
4915
  if (name.startsWith("scrollToTop ")) {
4895
4916
  this.scrollWidgetToTop(name.slice(12));
4896
4917
  return inputValue;
@@ -5065,7 +5086,7 @@ var ActionRuntime = class {
5065
5086
  if (Array.isArray(inputValue)) {
5066
5087
  return inputValue.map((entry) => this.resolveMappedValue(action.args, entry, context.responseValue));
5067
5088
  }
5068
- return this.resolveMappedValue(action.args, context.currentValue, context.responseValue);
5089
+ return this.resolveMappedValue(action.args, inputValue, context.responseValue);
5069
5090
  }
5070
5091
  if (name === "ifelse") {
5071
5092
  const branches = isIfElseArgs(action.args) ? action.args : null;
@@ -6294,7 +6315,7 @@ var VERTICAL_ALIGN_MAP2 = { top: "flex-start", center: "center", bottom: "flex-e
6294
6315
  var markdownConverter = new import_showdown.default.Converter({
6295
6316
  simpleLineBreaks: true,
6296
6317
  ghCompatibleHeaderId: true,
6297
- tables: false,
6318
+ tables: true,
6298
6319
  strikethrough: false,
6299
6320
  tasklists: false
6300
6321
  });
@@ -6409,7 +6430,10 @@ var renderCheckbox = (env, node, state, key) => {
6409
6430
 
6410
6431
  // src/lib/widgets/image.ts
6411
6432
  var renderImage = (env, node, state, key) => {
6412
- const url = env.escapeHtml(sanitizeUrl(state.url ?? node.url ?? "", { allowRelative: true }) ?? "");
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);
6413
6437
  const styleParts = [
6414
6438
  "box-sizing:border-box",
6415
6439
  "width:100%",
@@ -6613,6 +6637,32 @@ function renderConfirmModalOverlay(env, modal) {
6613
6637
  }
6614
6638
 
6615
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
+ }
6616
6666
  function bindKeyboardSubmitEvents(element, node, key, env) {
6617
6667
  const hasKeyboardEvents = Boolean(
6618
6668
  node.events?.onEnter?.length || node.events?.onShiftEnter?.length || node.events?.onControlEnter?.length
@@ -6635,6 +6685,10 @@ function bindKeyboardSubmitEvents(element, node, key, env) {
6635
6685
  if (!eventName || !node.events?.[eventName]?.length) {
6636
6686
  return;
6637
6687
  }
6688
+ if (eventName !== "onShiftEnter") {
6689
+ event.preventDefault();
6690
+ event.stopPropagation();
6691
+ }
6638
6692
  void env.dispatchWidgetEvent(node, eventName, key, env.getActionInputValueForElement(element, node), null);
6639
6693
  });
6640
6694
  }
@@ -6706,6 +6760,11 @@ function bindWidgetEvents(root, env) {
6706
6760
  }
6707
6761
  if (element instanceof HTMLInputElement && element.dataset.widget === "edit") {
6708
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
+ }
6709
6768
  element.addEventListener("input", () => {
6710
6769
  const state = env.stateByKey.get(key);
6711
6770
  if (state) {
@@ -6724,6 +6783,11 @@ function bindWidgetEvents(root, env) {
6724
6783
  }
6725
6784
  if (element instanceof HTMLTextAreaElement && element.dataset.widget === "textarea") {
6726
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
+ }
6727
6791
  element.addEventListener("input", () => {
6728
6792
  const state = env.stateByKey.get(key);
6729
6793
  if (state) {
@@ -7225,6 +7289,8 @@ var RuntimeRenderer = class {
7225
7289
  pendingScrollAction = null;
7226
7290
  pendingScrollFrameId = null;
7227
7291
  activeScrollAnimationFrameId = null;
7292
+ pendingCursorReference = null;
7293
+ pendingCursorFrameId = null;
7228
7294
  constructor(description, options = {}) {
7229
7295
  this.description = deepClone(description);
7230
7296
  this.resourceManager = options.resourceManager ?? null;
@@ -7260,6 +7326,8 @@ var RuntimeRenderer = class {
7260
7326
  return this.language;
7261
7327
  case "theme":
7262
7328
  return this.theme;
7329
+ case "isMobile":
7330
+ return typeof window === "undefined" ? false : isMobileViewport();
7263
7331
  case "urlPath":
7264
7332
  return typeof window === "undefined" ? "" : window.location.pathname;
7265
7333
  default:
@@ -7324,9 +7392,11 @@ var RuntimeRenderer = class {
7324
7392
  assignReference: (reference, inputValue, currentValue, options2) => this.referenceRuntime.assignReference(reference, inputValue, currentValue, options2),
7325
7393
  resolveMappedValue: (template, currentValue, responseValue) => this.referenceRuntime.resolveMappedValue(template, currentValue, responseValue),
7326
7394
  setWidgetEnabled: (widgetId, enabled) => this.setWidgetEnabled(widgetId, enabled),
7395
+ setWidgetVisible: (widgetId, visible) => this.setWidgetVisible(widgetId, visible),
7327
7396
  clearWidget: (widgetId) => this.clearWidget(widgetId),
7328
7397
  clearListElementState: (listKey) => this.clearListElementState(listKey),
7329
7398
  focusWidget: (reference) => this.focusWidget(reference),
7399
+ moveCursorToEnd: (reference) => this.moveCursorToEnd(reference),
7330
7400
  scrollWidgetToTop: (reference) => this.scrollWidgetToTop(reference),
7331
7401
  scrollWidgetToBottom: (reference) => this.scrollWidgetToBottom(reference),
7332
7402
  scrollWidgetToElementByIndex: (reference, index) => this.scrollWidgetToElementByIndex(reference, index),
@@ -7466,6 +7536,7 @@ var RuntimeRenderer = class {
7466
7536
  this.attachWidgetEvents(this.root);
7467
7537
  restoreElementState(this.root, preservedState, this.stateByKey);
7468
7538
  this.applyPendingScrollAction();
7539
+ this.applyPendingCursorAction();
7469
7540
  this.activeContextMenu = adjustContextMenuPosition(this.root, this.activeContextMenu);
7470
7541
  this.pendingResetModalIds.clear();
7471
7542
  if (typeof this.onRuntimeSnapshot === "function") {
@@ -7986,6 +8057,9 @@ var RuntimeRenderer = class {
7986
8057
  existing.id = stateId2;
7987
8058
  this.stateById.set(stateId2, existing);
7988
8059
  }
8060
+ if (existing.visible === void 0) {
8061
+ existing.visible = normalizeBoolean(node.visible, true);
8062
+ }
7989
8063
  this.syncStateDefinition(existing, node);
7990
8064
  return existing;
7991
8065
  }
@@ -8036,7 +8110,8 @@ var RuntimeRenderer = class {
8036
8110
  id: stateId,
8037
8111
  name: node.name,
8038
8112
  text: typeof node.text === "string" ? node.text : void 0,
8039
- 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
8040
8115
  };
8041
8116
  break;
8042
8117
  case "conditional-container":
@@ -8273,6 +8348,9 @@ var RuntimeRenderer = class {
8273
8348
  const key = itemContext ? this.resolveItemNodeKey(node, itemContext.listId, itemContext.index, path) : this.resolveNodeKey(node, path);
8274
8349
  this.nodeByKey.set(key, node);
8275
8350
  const state = this.ensureWidgetState(node, key);
8351
+ if ((state.visible ?? normalizeBoolean(node.visible, true)) === false) {
8352
+ return "";
8353
+ }
8276
8354
  switch (node.widget) {
8277
8355
  case "adaptive-layout":
8278
8356
  return renderAdaptiveLayout(env, node, state, key, path, itemContext);
@@ -8762,6 +8840,13 @@ var RuntimeRenderer = class {
8762
8840
  }
8763
8841
  state.enabled = enabled;
8764
8842
  }
8843
+ setWidgetVisible(widgetId, visible) {
8844
+ const state = this.stateById.get(widgetId);
8845
+ if (!state) {
8846
+ return;
8847
+ }
8848
+ state.visible = visible;
8849
+ }
8765
8850
  clearListElementState(listKey) {
8766
8851
  this.referenceRuntime.clearListElementState(listKey);
8767
8852
  }
@@ -8787,6 +8872,16 @@ var RuntimeRenderer = class {
8787
8872
  nestedTarget.focus({ preventScroll: true });
8788
8873
  }
8789
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
+ }
8790
8885
  scrollWidgetToTop(reference) {
8791
8886
  this.pendingScrollAction = { kind: "top", reference };
8792
8887
  this.schedulePendingScrollAction();
@@ -8813,17 +8908,17 @@ var RuntimeRenderer = class {
8813
8908
  return;
8814
8909
  }
8815
8910
  const pending = this.pendingScrollAction;
8816
- const element = this.findScrollableWidgetElement(pending.reference);
8817
- if (!element) {
8818
- return;
8819
- }
8820
8911
  if (pending.kind === "top") {
8821
- this.animateScrollTop(element, 0);
8912
+ this.animateScrollTop(pending.reference, 0);
8822
8913
  this.pendingScrollAction = null;
8823
8914
  return;
8824
8915
  }
8825
8916
  if (pending.kind === "bottom") {
8826
- this.animateScrollTop(element, element.scrollHeight);
8917
+ const element = this.findScrollableWidgetElement(pending.reference);
8918
+ if (!element) {
8919
+ return;
8920
+ }
8921
+ this.animateScrollTop(pending.reference, element.scrollHeight);
8827
8922
  this.pendingScrollAction = null;
8828
8923
  return;
8829
8924
  }
@@ -8832,17 +8927,21 @@ var RuntimeRenderer = class {
8832
8927
  this.pendingScrollAction = null;
8833
8928
  return;
8834
8929
  }
8835
- this.performScrollToElementByIndex(element, index);
8930
+ this.performScrollToElementByIndex(pending.reference, index);
8836
8931
  this.pendingScrollAction = null;
8837
8932
  }
8838
- performScrollToElementByIndex(element, index) {
8933
+ performScrollToElementByIndex(reference, index) {
8934
+ const element = this.findScrollableWidgetElement(reference);
8935
+ if (!element) {
8936
+ return;
8937
+ }
8839
8938
  if (element instanceof HTMLSelectElement) {
8840
8939
  const option = element.options.item(index);
8841
8940
  if (!option) {
8842
8941
  return;
8843
8942
  }
8844
8943
  const maxScrollTop = Math.max(0, element.scrollHeight - element.clientHeight);
8845
- this.animateScrollTop(element, Math.max(0, Math.min(option.offsetTop, maxScrollTop)));
8944
+ this.animateScrollTop(reference, Math.max(0, Math.min(option.offsetTop, maxScrollTop)));
8846
8945
  return;
8847
8946
  }
8848
8947
  const listElement = element.querySelector(`.vjt-list-element[data-list-index="${index}"], .vjt-grid-cell[data-list-index="${index}"]`);
@@ -8853,41 +8952,88 @@ var RuntimeRenderer = class {
8853
8952
  const targetTop = element.scrollTop + (itemRect.top - containerRect.top);
8854
8953
  const remainingContentHeight = element.scrollHeight - targetTop;
8855
8954
  const nextScrollTop = remainingContentHeight > element.clientHeight ? Math.max(0, Math.min(targetTop, maxScrollTop)) : maxScrollTop;
8856
- this.animateScrollTop(element, nextScrollTop);
8955
+ this.animateScrollTop(reference, nextScrollTop);
8857
8956
  }
8858
8957
  }
8859
- animateScrollTop(element, targetTop) {
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
+ }
8860
8971
  if (typeof window === "undefined") {
8861
- element.scrollTop = targetTop;
8972
+ initialElement.scrollTop = Math.max(0, Math.min(targetTop, getMaxScrollTop(initialElement)));
8862
8973
  return;
8863
8974
  }
8864
8975
  if (this.activeScrollAnimationFrameId !== null) {
8865
8976
  window.cancelAnimationFrame(this.activeScrollAnimationFrameId);
8866
8977
  this.activeScrollAnimationFrameId = null;
8867
8978
  }
8868
- const startTop = element.scrollTop;
8869
- const clampedTargetTop = Math.max(0, targetTop);
8979
+ const startTop = initialElement.scrollTop;
8980
+ const clampedTargetTop = Math.max(0, Math.min(targetTop, getMaxScrollTop(initialElement)));
8870
8981
  const delta = clampedTargetTop - startTop;
8871
8982
  if (Math.abs(delta) < 1) {
8872
- element.scrollTop = clampedTargetTop;
8983
+ initialElement.scrollTop = clampedTargetTop;
8873
8984
  return;
8874
8985
  }
8875
8986
  const durationMs = 300;
8876
8987
  const startTime = window.performance.now();
8877
8988
  const easeInOutCubic = (t) => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
8878
8989
  const tick = (now) => {
8990
+ const liveElement = resolveLiveElement();
8991
+ if (!liveElement) {
8992
+ this.activeScrollAnimationFrameId = null;
8993
+ return;
8994
+ }
8879
8995
  const elapsed = now - startTime;
8880
8996
  const progress = Math.min(1, elapsed / durationMs);
8881
- element.scrollTop = startTop + delta * easeInOutCubic(progress);
8997
+ const liveTargetTop = Math.max(0, Math.min(clampedTargetTop, getMaxScrollTop(liveElement)));
8998
+ liveElement.scrollTop = startTop + (liveTargetTop - startTop) * easeInOutCubic(progress);
8882
8999
  if (progress < 1) {
8883
9000
  this.activeScrollAnimationFrameId = window.requestAnimationFrame(tick);
8884
9001
  } else {
8885
- element.scrollTop = clampedTargetTop;
9002
+ liveElement.scrollTop = liveTargetTop;
8886
9003
  this.activeScrollAnimationFrameId = null;
8887
9004
  }
8888
9005
  };
8889
9006
  this.activeScrollAnimationFrameId = window.requestAnimationFrame(tick);
8890
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
+ }
8891
9037
  findScrollableWidgetElement(reference) {
8892
9038
  if (!(this.root instanceof HTMLElement)) {
8893
9039
  return null;
@@ -29,9 +29,11 @@ type ActionRuntimeOptions = {
29
29
  }) => void;
30
30
  resolveMappedValue: (template: unknown, currentValue: unknown, responseValue: unknown) => unknown;
31
31
  setWidgetEnabled: (widgetId: string, enabled: boolean) => void;
32
+ setWidgetVisible: (widgetId: string, visible: boolean) => void;
32
33
  clearWidget: (widgetId: string) => void;
33
34
  clearListElementState: (listKey: string) => void;
34
35
  focusWidget: (reference: string) => void;
36
+ moveCursorToEnd: (reference: string) => void;
35
37
  scrollWidgetToTop: (reference: string) => void;
36
38
  scrollWidgetToBottom: (reference: string) => void;
37
39
  scrollWidgetToElementByIndex: (reference: string, index: unknown) => void;
@@ -82,9 +84,11 @@ export declare class ActionRuntime {
82
84
  private readonly assignReference;
83
85
  private readonly resolveMappedValue;
84
86
  private readonly setWidgetEnabled;
87
+ private readonly setWidgetVisible;
85
88
  private readonly clearWidget;
86
89
  private readonly clearListElementState;
87
90
  private readonly focusWidget;
91
+ private readonly moveCursorToEnd;
88
92
  private readonly scrollWidgetToTop;
89
93
  private readonly scrollWidgetToBottom;
90
94
  private readonly scrollWidgetToElementByIndex;
@@ -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';
6
+ export type WidgetEventName = 'onClick' | 'onUserValueChange' | 'onRefresh' | 'onEnter' | 'onShiftEnter' | 'onControlEnter' | 'onPaste';
7
7
  export type SystemEventName = 'onBeforeRender' | 'onAfterRender' | 'onBeforeNavigate' | 'onAfterNavigate' | 'onSpeechDetected' | 'onRecordingStarted' | 'onRecordingStopped' | 'onRecordingError' | 'onListeningError' | 'onListeringError' | 'onPlayFinished' | 'onPlayingStopped';
8
8
  export type PrimitiveRequestType = 'int' | 'float' | 'boolean' | 'string';
9
9
  export type RouteDefinition = {
@@ -58,6 +58,7 @@ export type BaseNode = {
58
58
  name?: string;
59
59
  style?: string | string[];
60
60
  css?: string;
61
+ visible?: boolean | string;
61
62
  enabled?: boolean | string;
62
63
  events?: WidgetEvents;
63
64
  [key: string]: unknown;
@@ -103,8 +104,10 @@ export type WidgetState = {
103
104
  currentRef?: string;
104
105
  text?: string;
105
106
  url?: string;
107
+ base64?: string;
106
108
  placeholder?: string;
107
109
  enabled?: boolean;
110
+ visible?: boolean;
108
111
  editable?: boolean;
109
112
  checked?: boolean;
110
113
  condition?: boolean;
@@ -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', key: string, inputValue?: unknown, pointer?: {
15
+ dispatchWidgetEvent: (node: DescriptionNode, eventName: 'onClick' | 'onUserValueChange' | 'onEnter' | 'onShiftEnter' | 'onControlEnter' | 'onPaste', key: string, inputValue?: unknown, pointer?: {
16
16
  x: number;
17
17
  y: number;
18
18
  } | null) => Promise<void>;
@@ -3,5 +3,6 @@ import type { WidgetRenderer } from './context.js';
3
3
  export type ImageNode = BaseNode & {
4
4
  widget: 'image';
5
5
  url?: string;
6
+ base64?: string;
6
7
  };
7
8
  export declare const renderImage: WidgetRenderer<ImageNode>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vortexm/vjt",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
package/vjt-styles.css CHANGED
@@ -122,6 +122,29 @@ body {
122
122
  background: color-mix(in srgb, var(--vjt-surface-muted) 86%, transparent);
123
123
  }
124
124
 
125
+ .vjt-md-text-body table {
126
+ width: 100%;
127
+ border-collapse: collapse;
128
+ margin: 10px 0;
129
+ overflow: hidden;
130
+ border: 1px solid var(--vjt-border);
131
+ border-radius: 12px;
132
+ background: color-mix(in srgb, var(--vjt-surface) 92%, transparent);
133
+ }
134
+
135
+ .vjt-md-text-body th,
136
+ .vjt-md-text-body td {
137
+ padding: 10px 12px;
138
+ border: 1px solid color-mix(in srgb, var(--vjt-border) 78%, transparent);
139
+ text-align: left;
140
+ vertical-align: top;
141
+ }
142
+
143
+ .vjt-md-text-body th {
144
+ font-weight: 700;
145
+ background: color-mix(in srgb, var(--vjt-surface-muted) 82%, transparent);
146
+ }
147
+
125
148
  .vjt-static-text:is(h1, h2, h3, h4, h5, h6) {
126
149
  font-family: var(--vjt-font-heading);
127
150
  letter-spacing: -0.02em;