@thinkingos/vsl-sdk 0.1.1 → 0.3.0

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.mjs CHANGED
@@ -48,7 +48,13 @@ var CSS_PROPERTIES = [
48
48
  ["opacity", "opacity"],
49
49
  ["pointerEvents", "pointer-events"],
50
50
  ["overflow", "overflow"],
51
- ["height", "height"]
51
+ ["height", "height"],
52
+ ["backgroundColor", "background-color"],
53
+ ["color", "color"],
54
+ ["border", "border"],
55
+ ["borderRadius", "border-radius"],
56
+ ["boxShadow", "box-shadow"],
57
+ ["fontFamily", "font-family"]
52
58
  ];
53
59
  function captureCss(el) {
54
60
  const view = viewOf(el);
@@ -258,14 +264,16 @@ function isAriaHidden(attributes) {
258
264
  }
259
265
  function segmentOne(el) {
260
266
  const { attributes } = el;
267
+ const isFileInput = el.tag === "input" && attributes["type"] === "file";
261
268
  return {
262
269
  tag: el.tag,
263
270
  indexPath: el.indexPath,
264
271
  rect: el.rect,
265
272
  attributes,
266
- t: resolveLevel1Type(el.tag) ?? resolveAriaRoleType(attributes["role"]) ?? resolveLevel3Type(el.css, attributes),
273
+ t: isFileInput ? "file_input" : resolveLevel1Type(el.tag) ?? resolveAriaRoleType(attributes["role"]) ?? resolveLevel3Type(el.css, attributes),
267
274
  txt: attributes["aria-label"] ?? el.text,
268
275
  st: resolveSt(attributes),
276
+ css: el.css,
269
277
  ch: []
270
278
  };
271
279
  }
@@ -318,6 +326,8 @@ function defaultActions(t, st, attributes) {
318
326
  return ["click", "select"];
319
327
  case "textarea":
320
328
  return ["click", "type", "clear"];
329
+ case "file_input":
330
+ return ["click", "upload"];
321
331
  default:
322
332
  return void 0;
323
333
  }
@@ -330,6 +340,39 @@ function baseSemanticScore(el) {
330
340
  if (el.txt) score += 1;
331
341
  return score;
332
342
  }
343
+ function extractVisualStyles(css) {
344
+ if (!css) return void 0;
345
+ const style = {};
346
+ let hasContent = false;
347
+ if (css.backgroundColor && css.backgroundColor !== "transparent" && !css.backgroundColor.includes("rgba(0, 0, 0, 0)")) {
348
+ style.bg = css.backgroundColor;
349
+ hasContent = true;
350
+ }
351
+ if (css.color && css.color !== "rgb(0, 0, 0)") {
352
+ style.fg = css.color;
353
+ hasContent = true;
354
+ }
355
+ if (css.border && css.border !== "none" && !css.border.startsWith("0px")) {
356
+ style.border = css.border;
357
+ hasContent = true;
358
+ }
359
+ if (css.borderRadius && css.borderRadius !== "0px") {
360
+ style.radius = css.borderRadius;
361
+ hasContent = true;
362
+ }
363
+ if (css.boxShadow && css.boxShadow !== "none") {
364
+ style.shadow = css.boxShadow;
365
+ hasContent = true;
366
+ }
367
+ if (css.fontFamily || css.fontSize || css.fontWeight) {
368
+ style.font = {};
369
+ if (css.fontFamily) style.font.family = css.fontFamily;
370
+ if (css.fontSize) style.font.size = css.fontSize;
371
+ if (css.fontWeight) style.font.weight = css.fontWeight;
372
+ hasContent = true;
373
+ }
374
+ return hasContent ? style : void 0;
375
+ }
333
376
  function toVslObject(el, viewport, includedChildren, textBlocks, counter) {
334
377
  const t = el.t ?? "container";
335
378
  const object = {
@@ -356,6 +399,12 @@ function toVslObject(el, viewport, includedChildren, textBlocks, counter) {
356
399
  if (includedChildren.length > 0) object.ch = includedChildren;
357
400
  if (el.vf !== void 0) object.vf = el.vf;
358
401
  if (el.vf_meta !== void 0) object.vf_meta = el.vf_meta;
402
+ const sty = extractVisualStyles(el.css);
403
+ if (sty !== void 0) object.sty = sty;
404
+ if (t === "file_input") {
405
+ if (el.attributes["accept"] !== void 0) object.accept = el.attributes["accept"];
406
+ if (el.attributes["multiple"] !== void 0) object.multiple = true;
407
+ }
359
408
  return object;
360
409
  }
361
410
  function convert(el, viewport, textBlocks, counter) {
@@ -542,6 +591,8 @@ function objectsEqual(a, b) {
542
591
  return computeContentHash(a) === computeContentHash(b) && computeCoordHash(a) === computeCoordHash(b);
543
592
  }
544
593
  function tuple2Equal(a, b) {
594
+ if (a === null || a === void 0) return b === null || b === void 0;
595
+ if (b === null || b === void 0) return false;
545
596
  return a[0] === b[0] && a[1] === b[1];
546
597
  }
547
598
  function stringArrayEqual(a, b) {
@@ -678,6 +729,25 @@ var VslSnapshotSession = class {
678
729
  constructor(store = createCacheStore()) {
679
730
  this.store = store;
680
731
  }
732
+ /**
733
+ * Сбрасывает состояние сессии (URL, viewport, документ, версия).
734
+ * Следующий вызов snapshot() вернёт полный документ (как первый вызов).
735
+ */
736
+ clear() {
737
+ this.store.clear();
738
+ this.lastUrl = null;
739
+ this.lastViewport = null;
740
+ this.lastDocument = null;
741
+ this.version = 0;
742
+ }
743
+ /** Возвращает последний полный документ (не diff). */
744
+ getLastDocument() {
745
+ return this.lastDocument;
746
+ }
747
+ /** Текущая версия документа (инкрементируется при каждом diff). */
748
+ getVersion() {
749
+ return this.version;
750
+ }
681
751
  /**
682
752
  * Строит VSL из DOM поддерева root (extractDomTree → segmentTree →
683
753
  * buildVslDocument) и возвращает полный документ либо дифф (см. шапку).
@@ -686,6 +756,15 @@ var VslSnapshotSession = class {
686
756
  const elements = segmentTree(extractDomTree(root));
687
757
  return this.commit(elements, this.buildOptions(root, input), input);
688
758
  }
759
+ /**
760
+ * Вариант snapshot() для pre-extracted elements (MCP сервер, Node.js контекст).
761
+ * Принимает ExtractedElement[] напрямую (без DOM Element), проходит тот же
762
+ * pipeline: segmentTree → buildVslDocument → cache/diff.
763
+ */
764
+ snapshotFromElements(extractedElements, input) {
765
+ const elements = segmentTree(extractedElements);
766
+ return this.commit(elements, this.buildOptionsFromInput(input), input);
767
+ }
689
768
  /**
690
769
  * Vision-вариант конвейера (T1.5.5): segmentTree → enrichWithVision (Level 5
691
770
  * fallback — аннотация t + vf/vf_meta на элементах ДО сборки) → buildVslDocument
@@ -699,6 +778,16 @@ var VslSnapshotSession = class {
699
778
  const snapshot = this.commit(elements, this.buildOptions(root, input), input);
700
779
  return { snapshot, fragments: visionResult.fragments };
701
780
  }
781
+ /**
782
+ * Vision-вариант для pre-extracted elements (MCP сервер, Node.js контекст).
783
+ * Полный pipeline: segmentTree → enrichWithVision → buildVslDocument → cache/diff.
784
+ */
785
+ async snapshotWithVisionFromElements(extractedElements, input, vision) {
786
+ const elements = segmentTree(extractedElements);
787
+ const visionResult = await enrichWithVision(elements, vision);
788
+ const snapshot = this.commit(elements, this.buildOptionsFromInput(input), input);
789
+ return { snapshot, fragments: visionResult.fragments };
790
+ }
702
791
  /** BuildOptions из input + портативный title (логика бывшего snapshot()). */
703
792
  buildOptions(root, input) {
704
793
  const options = {
@@ -710,6 +799,20 @@ var VslSnapshotSession = class {
710
799
  if (input.background !== void 0) options.background = input.background;
711
800
  return options;
712
801
  }
802
+ /**
803
+ * BuildOptions из input без DOM Element (для snapshotFromElements).
804
+ * Title берётся только из input (нет фолбэка на root.ownerDocument.title).
805
+ */
806
+ buildOptionsFromInput(input) {
807
+ const options = {
808
+ viewport: { width: input.viewport.width, height: input.viewport.height },
809
+ url: input.url
810
+ };
811
+ if (input.title !== void 0) options.title = input.title;
812
+ if (input.timestamp !== void 0) options.timestamp = input.timestamp;
813
+ if (input.background !== void 0) options.background = input.background;
814
+ return options;
815
+ }
713
816
  /** Сборка документа + кэш/дифф (логика бывшего snapshot(), см. шапку модуля). */
714
817
  commit(elements, options, input) {
715
818
  const nextDocument = buildVslDocument(elements, options);