@quanta-intellect/vessel-browser 0.1.134 → 0.1.137

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.
@@ -2160,6 +2160,16 @@ function createLogger(scope) {
2160
2160
  };
2161
2161
  }
2162
2162
  const logger = createLogger("ContentScript");
2163
+ const MAX_VISIBLE_TEXT = 500;
2164
+ const MAX_HREF_LENGTH = 500;
2165
+ const MAX_ATTR_TEXT = 200;
2166
+ const MAX_DESCRIPTION_LENGTH = 160;
2167
+ const MAX_LABEL_LENGTH = 100;
2168
+ const MAX_SHORT_TEXT = 60;
2169
+ const MAX_RESULT_COUNT = 10;
2170
+ const MAX_DIFF_HEADINGS = 8;
2171
+ const MAX_OPTIONS_PER_SELECT = 8;
2172
+ const MAX_OPTIONS_DISPLAY = 25;
2163
2173
  function looksLikeCorrectOption(value) {
2164
2174
  const text = getTrimmedText(value);
2165
2175
  if (!text) return void 0;
@@ -2209,7 +2219,7 @@ function collectBoundedVisibleText(root, maxLength) {
2209
2219
  }
2210
2220
  function getPageDiffSignature() {
2211
2221
  const title = normalizeSignatureText(document.title);
2212
- const headings = Array.from(document.querySelectorAll("h1, h2, h3")).slice(0, 8).map((el) => normalizeSignatureText(el.textContent)).filter(Boolean).join(" | ");
2222
+ const headings = Array.from(document.querySelectorAll("h1, h2, h3")).slice(0, MAX_DIFF_HEADINGS).map((el) => normalizeSignatureText(el.textContent)).filter(Boolean).join(" | ");
2213
2223
  const mainRoot = document.querySelector("main, article, [role='main']") || document.body;
2214
2224
  const visibleText = collectBoundedVisibleText(mainRoot, 1200);
2215
2225
  return [window.location.href, title, headings, visibleText].join("\n");
@@ -2518,7 +2528,7 @@ function looksLikeDrawer(el, style, rect, areaRatio) {
2518
2528
  return false;
2519
2529
  }
2520
2530
  function looksLikeCartConfirmation(el) {
2521
- const text = (el.textContent || "").slice(0, 500).toLowerCase();
2531
+ const text = (el.textContent || "").slice(0, MAX_VISIBLE_TEXT).toLowerCase();
2522
2532
  const signals = [
2523
2533
  "added to cart",
2524
2534
  "added to bag",
@@ -2601,14 +2611,14 @@ function collectOverlayRadioOptions(root) {
2601
2611
  seen.add(key);
2602
2612
  const checked = node.getAttribute("aria-checked") === "true" || (node instanceof HTMLInputElement ? node.checked : false);
2603
2613
  options.push({
2604
- label: data.text.slice(0, 100),
2614
+ label: data.text.slice(0, MAX_LABEL_LENGTH),
2605
2615
  selector,
2606
2616
  checked,
2607
2617
  labelSource: data.source,
2608
2618
  looksCorrect: looksLikeCorrectOption(data.text)
2609
2619
  });
2610
2620
  });
2611
- return options.slice(0, 8);
2621
+ return options.slice(0, MAX_OPTIONS_PER_SELECT);
2612
2622
  }
2613
2623
  function collectOverlayActions(root) {
2614
2624
  const seen = /* @__PURE__ */ new Set();
@@ -2637,17 +2647,17 @@ function collectOverlayActions(root) {
2637
2647
  if (!data.text) return;
2638
2648
  seen.add(selector);
2639
2649
  actions.push({
2640
- label: data.text.slice(0, 100),
2650
+ label: data.text.slice(0, MAX_LABEL_LENGTH),
2641
2651
  selector,
2642
2652
  kind: getOverlayActionKind(node, data.text),
2643
2653
  disabled: isElementDisabled(node)
2644
2654
  });
2645
2655
  });
2646
- return actions.sort((a, b) => getOverlayActionPriority(b) - getOverlayActionPriority(a)).slice(0, 10);
2656
+ return actions.sort((a, b) => getOverlayActionPriority(b) - getOverlayActionPriority(a)).slice(0, MAX_RESULT_COUNT);
2647
2657
  }
2648
2658
  function getOverlayMessage(el) {
2649
2659
  const heading = el.querySelector("h1, h2, h3, h4, h5, h6");
2650
- return getTrimmedText(heading?.textContent)?.slice(0, 160) || getNodeTextByIds(el.getAttribute("aria-describedby"))?.slice(0, 160) || getTrimmedText(el.textContent)?.slice(0, 160);
2660
+ return getTrimmedText(heading?.textContent)?.slice(0, MAX_DESCRIPTION_LENGTH) || getNodeTextByIds(el.getAttribute("aria-describedby"))?.slice(0, MAX_DESCRIPTION_LENGTH) || getTrimmedText(el.textContent)?.slice(0, MAX_DESCRIPTION_LENGTH);
2651
2661
  }
2652
2662
  function classifyOverlayKind(args) {
2653
2663
  const haystack = [
@@ -2715,7 +2725,7 @@ function detectOverlays() {
2715
2725
  role: getTrimmedText(node.getAttribute("role")) || void 0,
2716
2726
  label: getOverlayLabel(node),
2717
2727
  selector: generateSelector(node),
2718
- text: getTrimmedText(node.textContent)?.slice(0, 160),
2728
+ text: getTrimmedText(node.textContent)?.slice(0, MAX_DESCRIPTION_LENGTH),
2719
2729
  message: getOverlayMessage(node),
2720
2730
  blocksInteraction: blockingSurface,
2721
2731
  dismissSelector: actions.find((action) => action.kind === "dismiss")?.selector,
@@ -2750,7 +2760,7 @@ function isLikelyDormantOverlay(el) {
2750
2760
  return true;
2751
2761
  }
2752
2762
  return /cookie|consent|privacy|gdpr|ccpa|onetrust|ot-sdk|trustarc|didomi|sp_message|qc-cmp|cmp|newsletter|subscribe/.test(
2753
- `${attrs} ${text.slice(0, 200)}`
2763
+ `${attrs} ${text.slice(0, MAX_ATTR_TEXT)}`
2754
2764
  );
2755
2765
  }
2756
2766
  function detectDormantOverlays() {
@@ -2768,10 +2778,10 @@ function detectDormantOverlays() {
2768
2778
  role: getTrimmedText(node.getAttribute("role")) || void 0,
2769
2779
  label: getOverlayLabel(node),
2770
2780
  selector,
2771
- text: getTrimmedText(node.textContent)?.slice(0, 160)
2781
+ text: getTrimmedText(node.textContent)?.slice(0, MAX_DESCRIPTION_LENGTH)
2772
2782
  });
2773
2783
  });
2774
- return matches.slice(0, 10);
2784
+ return matches.slice(0, MAX_RESULT_COUNT);
2775
2785
  }
2776
2786
  function samplePointForRect(rect) {
2777
2787
  if (!isInViewportRect(rect)) return null;
@@ -2962,7 +2972,7 @@ function getSelectOptions(el) {
2962
2972
  const options = Array.from(el.options).map((option) => ({
2963
2973
  label: option.textContent?.trim() || option.value.trim(),
2964
2974
  value: option.value
2965
- })).filter((o) => o.label || o.value).slice(0, 25);
2975
+ })).filter((o) => o.label || o.value).slice(0, MAX_OPTIONS_DISPLAY);
2966
2976
  return options.length > 0 ? options : void 0;
2967
2977
  }
2968
2978
  function getAriaBoolean(el, attr) {
@@ -3007,8 +3017,8 @@ function extractNavigation() {
3007
3017
  if (!text || anchor.getAttribute("href")?.startsWith("#")) return;
3008
3018
  navigation.push({
3009
3019
  type: "link",
3010
- text: text.slice(0, 100),
3011
- href: anchor.href.slice(0, 500),
3020
+ text: text.slice(0, MAX_LABEL_LENGTH),
3021
+ href: anchor.href.slice(0, MAX_HREF_LENGTH),
3012
3022
  ...buildBaseMetadata(anchor),
3013
3023
  context: "nav"
3014
3024
  });
@@ -3053,7 +3063,7 @@ function extractInteractiveElements() {
3053
3063
  const role = getElementRole(btn);
3054
3064
  elements.push({
3055
3065
  type: "button",
3056
- text: text?.slice(0, 100),
3066
+ text: text?.slice(0, MAX_LABEL_LENGTH),
3057
3067
  labelSource: source,
3058
3068
  ...buildBaseMetadata(btn),
3059
3069
  role,
@@ -3068,8 +3078,8 @@ function extractInteractiveElements() {
3068
3078
  if (context === "nav") return;
3069
3079
  elements.push({
3070
3080
  type: "link",
3071
- text: text.slice(0, 100),
3072
- href: anchor.href.slice(0, 500),
3081
+ text: text.slice(0, MAX_LABEL_LENGTH),
3082
+ href: anchor.href.slice(0, MAX_HREF_LENGTH),
3073
3083
  ...buildBaseMetadata(anchor),
3074
3084
  context
3075
3085
  });
@@ -3086,7 +3096,7 @@ function extractInteractiveElements() {
3086
3096
  ) : void 0;
3087
3097
  elements.push({
3088
3098
  type: tag === "select" ? "select" : tag === "textarea" ? "textarea" : "input",
3089
- label: label.label?.slice(0, 100),
3099
+ label: label.label?.slice(0, MAX_LABEL_LENGTH),
3090
3100
  labelSource: label.source,
3091
3101
  inputType: element.getAttribute("type") || void 0,
3092
3102
  placeholder: element.getAttribute("placeholder") || void 0,
@@ -3095,7 +3105,7 @@ function extractInteractiveElements() {
3095
3105
  options: element instanceof HTMLSelectElement ? getSelectOptions(element) : void 0,
3096
3106
  ...buildBaseMetadata(input),
3097
3107
  role,
3098
- text: radioText?.slice(0, 100),
3108
+ text: radioText?.slice(0, MAX_LABEL_LENGTH),
3099
3109
  looksCorrect: radioText || label.label ? looksLikeCorrectOption(radioText || label.label) : void 0,
3100
3110
  ...getFieldMetadata(element)
3101
3111
  });
@@ -3126,7 +3136,7 @@ function extractForms() {
3126
3136
  ) : void 0;
3127
3137
  fields.push({
3128
3138
  type: tag === "select" ? "select" : tag === "textarea" ? "textarea" : "input",
3129
- label: label.label?.slice(0, 100),
3139
+ label: label.label?.slice(0, MAX_LABEL_LENGTH),
3130
3140
  labelSource: label.source,
3131
3141
  inputType: element.getAttribute("type") || void 0,
3132
3142
  placeholder: element.getAttribute("placeholder") || void 0,
@@ -3135,7 +3145,7 @@ function extractForms() {
3135
3145
  options: element instanceof HTMLSelectElement ? getSelectOptions(element) : void 0,
3136
3146
  ...buildBaseMetadata(input),
3137
3147
  role,
3138
- text: radioText?.slice(0, 100),
3148
+ text: radioText?.slice(0, MAX_LABEL_LENGTH),
3139
3149
  looksCorrect: radioText || label.label ? looksLikeCorrectOption(radioText || label.label) : void 0,
3140
3150
  ...getFieldMetadata(element)
3141
3151
  });
@@ -3148,7 +3158,7 @@ function extractForms() {
3148
3158
  const { text, source } = getButtonTextWithSource(btn);
3149
3159
  fields.push({
3150
3160
  type: "button",
3151
- text: text?.slice(0, 100),
3161
+ text: text?.slice(0, MAX_LABEL_LENGTH),
3152
3162
  labelSource: source,
3153
3163
  ...buildBaseMetadata(btn)
3154
3164
  });
@@ -3183,7 +3193,7 @@ function extractLandmarks() {
3183
3193
  landmarks.push({
3184
3194
  role,
3185
3195
  label: getTrimmedText(el.getAttribute("aria-label")) || getNodeTextByIds(el.getAttribute("aria-labelledby")) || getTrimmedText(el.id),
3186
- text: getTrimmedText(el.textContent)?.slice(0, 200)
3196
+ text: getTrimmedText(el.textContent)?.slice(0, MAX_ATTR_TEXT)
3187
3197
  });
3188
3198
  });
3189
3199
  });
@@ -3419,12 +3429,12 @@ function interactByIndex(index, action, value) {
3419
3429
  }
3420
3430
  const anchor = el instanceof HTMLAnchorElement ? el : el.closest("a[href]");
3421
3431
  const href = anchor instanceof HTMLAnchorElement ? anchor.href : null;
3422
- return "Clicked: " + (el.getAttribute("aria-label") || el.textContent?.trim().slice(0, 60) || el.tagName.toLowerCase()) + (href ? "\nhref: " + href : "");
3432
+ return "Clicked: " + (el.getAttribute("aria-label") || el.textContent?.trim().slice(0, MAX_SHORT_TEXT) || el.tagName.toLowerCase()) + (href ? "\nhref: " + href : "");
3423
3433
  }
3424
3434
  if (action === "focus") {
3425
3435
  el.scrollIntoView({ behavior: "instant", block: "center", inline: "center" });
3426
3436
  el.focus();
3427
- return "Focused: " + (el.getAttribute("aria-label") || el.textContent?.trim().slice(0, 60) || el.tagName.toLowerCase());
3437
+ return "Focused: " + (el.getAttribute("aria-label") || el.textContent?.trim().slice(0, MAX_SHORT_TEXT) || el.tagName.toLowerCase());
3428
3438
  }
3429
3439
  if (action === "value" && value != null) {
3430
3440
  if (!(el instanceof HTMLInputElement) && !(el instanceof HTMLTextAreaElement) && !(el instanceof HTMLSelectElement)) {
@@ -2657,6 +2657,16 @@
2657
2657
  display: flex;
2658
2658
  flex-direction: column;
2659
2659
  gap: 10px;
2660
+ overscroll-behavior: contain;
2661
+ scrollbar-gutter: stable;
2662
+ background: var(--bg-secondary);
2663
+ contain: layout paint;
2664
+ isolation: isolate;
2665
+ transform: translateZ(0);
2666
+ }
2667
+
2668
+ .sidebar-messages.scroll-fade::after {
2669
+ display: none;
2660
2670
  }
2661
2671
 
2662
2672
  /* Fade-in when tab content switches */
@@ -3538,6 +3548,8 @@
3538
3548
  word-break: break-word;
3539
3549
  user-select: text;
3540
3550
  animation: message-enter 300ms var(--ease-out-expo) both;
3551
+ content-visibility: auto;
3552
+ contain-intrinsic-size: auto 80px;
3541
3553
  }
3542
3554
 
3543
3555
  @keyframes message-enter {
@@ -4056,6 +4056,10 @@ const BookmarkNotifications = () => {
4056
4056
  })();
4057
4057
  };
4058
4058
  delegateEvents(["click"]);
4059
+ const STATUS_MESSAGE_CLEAR_MS = 3e3;
4060
+ const STATUS_MESSAGE_LONG_CLEAR_MS = 5e3;
4061
+ const MAX_PREVIEW_TEXT = 60;
4062
+ const TRUNCATE_KEEP = 57;
4059
4063
  var _tmpl$$n = /* @__PURE__ */ template(`<div class=bookmark-toast-stack aria-live=polite><div class="bookmark-toast highlight-toast"role=status><div class=bookmark-toast-header><div class=bookmark-toast-title></div><button type=button class=bookmark-toast-close aria-label="Dismiss notification">×</button></div><div class=bookmark-toast-message>`);
4060
4064
  const TOAST_DURATION_MS = 3e3;
4061
4065
  const TOAST_EXIT_MS$1 = 300;
@@ -4543,20 +4547,39 @@ const FlowProgress = () => {
4543
4547
  });
4544
4548
  };
4545
4549
  function useScrollFade(el) {
4550
+ let frameId;
4551
+ let hasTopFade;
4552
+ let hasBottomFade;
4546
4553
  const update = () => {
4554
+ frameId = void 0;
4547
4555
  const { scrollTop, scrollHeight, clientHeight } = el;
4548
4556
  const atTop = scrollTop <= 2;
4549
4557
  const atBottom = scrollTop + clientHeight >= scrollHeight - 2;
4550
- el.classList.toggle("fade-top", !atTop);
4551
- el.classList.toggle("fade-bottom", !atBottom);
4558
+ const nextTopFade = !atTop;
4559
+ const nextBottomFade = !atBottom;
4560
+ if (hasTopFade !== nextTopFade) {
4561
+ hasTopFade = nextTopFade;
4562
+ el.classList.toggle("fade-top", nextTopFade);
4563
+ }
4564
+ if (hasBottomFade !== nextBottomFade) {
4565
+ hasBottomFade = nextBottomFade;
4566
+ el.classList.toggle("fade-bottom", nextBottomFade);
4567
+ }
4568
+ };
4569
+ const scheduleUpdate = () => {
4570
+ if (frameId !== void 0) return;
4571
+ frameId = requestAnimationFrame(update);
4552
4572
  };
4553
4573
  el.classList.add("scroll-fade");
4554
- el.addEventListener("scroll", update, { passive: true });
4555
- queueMicrotask(update);
4556
- const observer = new MutationObserver(update);
4574
+ el.addEventListener("scroll", scheduleUpdate, { passive: true });
4575
+ queueMicrotask(scheduleUpdate);
4576
+ const observer = new MutationObserver(scheduleUpdate);
4557
4577
  observer.observe(el, { childList: true, subtree: true });
4558
4578
  onCleanup(() => {
4559
- el.removeEventListener("scroll", update);
4579
+ el.removeEventListener("scroll", scheduleUpdate);
4580
+ if (frameId !== void 0) {
4581
+ cancelAnimationFrame(frameId);
4582
+ }
4560
4583
  observer.disconnect();
4561
4584
  });
4562
4585
  }
@@ -7433,7 +7456,7 @@ const TOOL_ICONS = {
7433
7456
  function renderToolChip(name, args) {
7434
7457
  const icon = TOOL_ICONS[name] || "⚙";
7435
7458
  const displayName = name.replace(/_/g, " ");
7436
- const argsHtml = args ? `<span class="tool-chip-args">${escapeHtml(args.length > 60 ? args.slice(0, 57) + "..." : args)}</span>` : "";
7459
+ const argsHtml = args ? `<span class="tool-chip-args">${escapeHtml(args.length > MAX_PREVIEW_TEXT ? args.slice(0, TRUNCATE_KEEP) + "..." : args)}</span>` : "";
7437
7460
  return `<div class="tool-chip"><span class="tool-chip-icon">${icon}</span><span class="tool-chip-name">${escapeHtml(displayName)}</span>${argsHtml}</div>`;
7438
7461
  }
7439
7462
  function renderMarkdown(source) {
@@ -7482,7 +7505,8 @@ ${token}
7482
7505
  output
7483
7506
  );
7484
7507
  if (typeof purify?.sanitize !== "function") {
7485
- return output;
7508
+ console.warn("[markdown] DOMPurify not available; stripping HTML as fallback");
7509
+ return output.replace(/<[^>]+>/g, "");
7486
7510
  }
7487
7511
  return purify.sanitize(output, {
7488
7512
  ALLOWED_TAGS: [
@@ -12703,8 +12727,6 @@ const SettingsPrivacy = (props) => {
12703
12727
  })();
12704
12728
  };
12705
12729
  delegateEvents(["input", "click"]);
12706
- const STATUS_MESSAGE_CLEAR_MS = 3e3;
12707
- const STATUS_MESSAGE_LONG_CLEAR_MS = 5e3;
12708
12730
  var _tmpl$$4 = /* @__PURE__ */ template(`<div class=settings-feedback-form><input class=settings-input type=email placeholder="Your reply email"><textarea class="settings-textarea settings-feedback-textarea"placeholder="Tell us what happened, what you expected, or what would make Vessel better."></textarea><div class=settings-inline-actions><button class=settings-secondary-btn>`), _tmpl$2$4 = /* @__PURE__ */ template(`<div class=premium-section><div class=premium-active-badge>Premium Active</div><p class=premium-detail></p><div class=premium-actions-row><button class="premium-btn premium-btn-manage">Manage Subscription</button><button class="premium-btn premium-btn-reset">Sign Out`), _tmpl$3$3 = /* @__PURE__ */ template(`<div class=vault-entries>`), _tmpl$4$2 = /* @__PURE__ */ template(`<div class=settings-category-panel><div class=settings-field><label class=settings-label>Support</label><div class=settings-inline-actions><button class=settings-secondary-btn></button></div></div><div class=settings-field><label class=settings-label>Vessel Premium</label></div><div class=settings-field><label class=settings-label>Saved Sessions</label><p class=settings-hint style=margin-bottom:10px>Save the current browser state (tabs, cookies, storage) as a named session. Restore it later from this panel.</p><div class=premium-activate-row style=margin-bottom:8px><input class="settings-input premium-email-input"placeholder="Session name"><button class="premium-btn premium-btn-activate">Save Current`), _tmpl$5$2 = /* @__PURE__ */ template(`<p class=settings-status>`), _tmpl$6$2 = /* @__PURE__ */ template(`<div class=premium-activate-row><input class="settings-input premium-email-input"inputmode=numeric maxlength=6 placeholder="Enter 6-digit code"><button class="premium-btn premium-btn-activate">`), _tmpl$7$2 = /* @__PURE__ */ template(`<button class="premium-btn premium-btn-reset">Clear Saved Email`), _tmpl$8$1 = /* @__PURE__ */ template(`<div class=premium-section><p class=premium-description>Unlock screenshot/vision analysis, session management, Obsidian integration, workflow tracking, DevTools tools, table extraction, Agent Credential Vault, and unlimited tool iterations.</p><div class=premium-activate-row><input class="settings-input premium-email-input"type=email placeholder="Enter your subscription email"><button class="premium-btn premium-btn-activate"></button></div><button class="premium-btn premium-btn-upgrade">`), _tmpl$9 = /* @__PURE__ */ template(`<div class=vault-entry><div class=vault-entry-info><span class=vault-entry-label></span><span class=vault-entry-detail> &middot; <!> cookies &middot; <!> domains</span></div><div style=display:flex;gap:6px;align-items:center><button class="premium-btn premium-btn-activate"title="Restore this session (replaces current tabs and cookies)"style="padding:2px 10px;font-size:12px">Load</button><button class=vault-entry-remove title="Delete session">&times;`);
12709
12731
  const SettingsAccount = (props) => {
12710
12732
  const p = props.premium;
@@ -14299,7 +14321,7 @@ const App = () => {
14299
14321
  const loadingPresence = useAnimatedPresence(() => !!activeTab()?.isLoading, 300);
14300
14322
  const showHighlightResult = (result) => {
14301
14323
  if (result.success && result.text) {
14302
- const preview = result.text.length > 60 ? result.text.slice(0, 57) + "..." : result.text;
14324
+ const preview = result.text.length > MAX_PREVIEW_TEXT ? result.text.slice(0, TRUNCATE_KEEP) + "..." : result.text;
14303
14325
  setHighlightToast({
14304
14326
  title: "Highlight saved",
14305
14327
  message: preview
@@ -5,8 +5,8 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <meta http-equiv="Content-Security-Policy" content="default-src 'self'; base-uri 'none'; object-src 'none'; frame-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; font-src 'self' data:; form-action 'self';" />
7
7
  <title>Vessel</title>
8
- <script type="module" crossorigin src="./assets/index-3HMjZNhK.js"></script>
9
- <link rel="stylesheet" crossorigin href="./assets/index-CWy6khUL.css">
8
+ <script type="module" crossorigin src="./assets/index-k2scA5OB.js"></script>
9
+ <link rel="stylesheet" crossorigin href="./assets/index-CdUTXTU4.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@quanta-intellect/vessel-browser",
3
3
  "mcpName": "io.github.unmodeled-tyler/vessel-browser",
4
- "version": "0.1.134",
4
+ "version": "0.1.137",
5
5
  "description": "AI-native web browser runtime for autonomous agents with human supervision",
6
6
  "main": "./out/main/index.js",
7
7
  "bin": {