@quanta-intellect/vessel-browser 0.1.10 → 0.1.12

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.
@@ -2126,8 +2126,78 @@ function generateStableSelector(el) {
2126
2126
  let elementIndex = 0;
2127
2127
  const elementSelectors = {};
2128
2128
  let indexedElements = /* @__PURE__ */ new WeakMap();
2129
+ const indexedElementRefs = {};
2129
2130
  let activeOverlays = [];
2131
+ const MAX_SHADOW_HOSTS = 150;
2132
+ const MAX_SHADOW_DEPTH = 5;
2133
+ const MAX_WALK_ELEMENTS = 1e4;
2134
+ function collectShadowRoots(root) {
2135
+ const shadowRoots = [];
2136
+ let walked = 0;
2137
+ const walk = (node, depth) => {
2138
+ if (depth > MAX_SHADOW_DEPTH || shadowRoots.length >= MAX_SHADOW_HOSTS) return;
2139
+ const tw = document.createTreeWalker(node, NodeFilter.SHOW_ELEMENT);
2140
+ let el = tw.nextNode();
2141
+ while (el && walked < MAX_WALK_ELEMENTS && shadowRoots.length < MAX_SHADOW_HOSTS) {
2142
+ walked++;
2143
+ if (el.shadowRoot) {
2144
+ shadowRoots.push(el.shadowRoot);
2145
+ walk(el.shadowRoot, depth + 1);
2146
+ }
2147
+ el = tw.nextNode();
2148
+ }
2149
+ };
2150
+ walk(root, 0);
2151
+ return shadowRoots;
2152
+ }
2153
+ function deepQuerySelectorAll(selector, root = document) {
2154
+ const results = [];
2155
+ root.querySelectorAll(selector).forEach((el) => results.push(el));
2156
+ for (const sr of collectShadowRoots(root)) {
2157
+ sr.querySelectorAll(selector).forEach((el) => results.push(el));
2158
+ }
2159
+ return results;
2160
+ }
2161
+ function isInShadowDom(el) {
2162
+ return el.getRootNode() instanceof ShadowRoot;
2163
+ }
2164
+ function generateShadowPiercingSelector(el) {
2165
+ const segments = [];
2166
+ let current = el;
2167
+ while (current) {
2168
+ const rootNode = current.getRootNode();
2169
+ const innerSel = generateStableSelector(current);
2170
+ if (rootNode instanceof ShadowRoot) {
2171
+ segments.unshift(innerSel);
2172
+ current = rootNode.host;
2173
+ } else {
2174
+ segments.unshift(innerSel);
2175
+ break;
2176
+ }
2177
+ }
2178
+ if (segments.length <= 1) return null;
2179
+ return segments.join(" >>> ");
2180
+ }
2181
+ function resolveShadowSelector(selectorPath) {
2182
+ const segments = selectorPath.split(" >>> ").map((s) => s.trim());
2183
+ let scope = document;
2184
+ for (let i = 0; i < segments.length; i++) {
2185
+ const el = scope.querySelector(segments[i]);
2186
+ if (!el) return null;
2187
+ if (i < segments.length - 1) {
2188
+ if (!el.shadowRoot) return null;
2189
+ scope = el.shadowRoot;
2190
+ } else {
2191
+ return el;
2192
+ }
2193
+ }
2194
+ return null;
2195
+ }
2130
2196
  function generateSelector(el) {
2197
+ if (isInShadowDom(el)) {
2198
+ const shadowPath = generateShadowPiercingSelector(el);
2199
+ if (shadowPath) return shadowPath;
2200
+ }
2131
2201
  return generateStableSelector(el);
2132
2202
  }
2133
2203
  function assignIndex(el) {
@@ -2135,6 +2205,7 @@ function assignIndex(el) {
2135
2205
  if (existing != null) return existing;
2136
2206
  elementIndex += 1;
2137
2207
  elementSelectors[elementIndex] = generateSelector(el);
2208
+ indexedElementRefs[elementIndex] = el;
2138
2209
  indexedElements.set(el, elementIndex);
2139
2210
  return elementIndex;
2140
2211
  }
@@ -2475,7 +2546,7 @@ function buildBaseMetadata(el) {
2475
2546
  };
2476
2547
  }
2477
2548
  function extractHeadings() {
2478
- return Array.from(document.querySelectorAll("h1, h2, h3, h4, h5, h6")).map((el) => {
2549
+ return deepQuerySelectorAll("h1, h2, h3, h4, h5, h6").map((el) => {
2479
2550
  const text = el.textContent?.trim() || "";
2480
2551
  if (!text) return null;
2481
2552
  return {
@@ -2486,10 +2557,10 @@ function extractHeadings() {
2486
2557
  }
2487
2558
  function extractNavigation() {
2488
2559
  const navigation = [];
2489
- document.querySelectorAll(
2560
+ deepQuerySelectorAll(
2490
2561
  'nav, [role="navigation"], header nav, [role="banner"] nav'
2491
2562
  ).forEach((nav) => {
2492
- nav.querySelectorAll("a[href]").forEach((link) => {
2563
+ deepQuerySelectorAll("a[href]", nav).forEach((link) => {
2493
2564
  const anchor = link;
2494
2565
  const text = anchor.textContent?.trim();
2495
2566
  if (!text || anchor.getAttribute("href")?.startsWith("#")) return;
@@ -2534,7 +2605,7 @@ function getFieldMetadata(el) {
2534
2605
  }
2535
2606
  function extractInteractiveElements() {
2536
2607
  const elements = [];
2537
- document.querySelectorAll(
2608
+ deepQuerySelectorAll(
2538
2609
  'button, [role="button"], input[type="submit"], input[type="button"]'
2539
2610
  ).forEach((btn) => {
2540
2611
  const input = btn;
@@ -2545,7 +2616,7 @@ function extractInteractiveElements() {
2545
2616
  ...buildBaseMetadata(btn)
2546
2617
  });
2547
2618
  });
2548
- document.querySelectorAll("a[href]").forEach((link) => {
2619
+ deepQuerySelectorAll("a[href]").forEach((link) => {
2549
2620
  const anchor = link;
2550
2621
  const text = anchor.textContent?.trim();
2551
2622
  if (!text || anchor.getAttribute("href")?.startsWith("#")) return;
@@ -2559,7 +2630,7 @@ function extractInteractiveElements() {
2559
2630
  context
2560
2631
  });
2561
2632
  });
2562
- document.querySelectorAll(
2633
+ deepQuerySelectorAll(
2563
2634
  'input:not([type="hidden"]):not([type="submit"]):not([type="button"]), select, textarea'
2564
2635
  ).forEach((input) => {
2565
2636
  const element = input;
@@ -2587,7 +2658,8 @@ function extractForms() {
2587
2658
  }
2588
2659
  return el instanceof HTMLInputElement && (el.type === "submit" || el.type === "image") && el.form === form;
2589
2660
  }
2590
- document.querySelectorAll("form").forEach((form) => {
2661
+ deepQuerySelectorAll("form").forEach((formEl) => {
2662
+ const form = formEl;
2591
2663
  const fields = [];
2592
2664
  form.querySelectorAll(
2593
2665
  "input:not([type='hidden']):not([type='submit']):not([type='button']):not([type='image']), select, textarea"
@@ -2643,7 +2715,7 @@ function extractLandmarks() {
2643
2715
  "dialog, [role='dialog'], [role='alertdialog']"
2644
2716
  ];
2645
2717
  selectors.forEach((selector) => {
2646
- document.querySelectorAll(selector).forEach((el) => {
2718
+ deepQuerySelectorAll(selector).forEach((el) => {
2647
2719
  const tag = el.tagName.toLowerCase();
2648
2720
  const role = el.getAttribute("role") || (tag === "header" ? "banner" : tag === "nav" ? "navigation" : tag === "main" ? "main" : tag === "aside" ? "complementary" : tag === "footer" ? "contentinfo" : tag === "article" ? "article" : tag === "section" ? "region" : tag === "dialog" ? "dialog" : "generic");
2649
2721
  landmarks.push({
@@ -2784,9 +2856,12 @@ function getVisiblePageText() {
2784
2856
  function vesselExtractContent() {
2785
2857
  const extractStructuredContent = (article) => {
2786
2858
  activeOverlays = detectOverlays();
2859
+ const readabilityText = article?.textContent || "";
2860
+ const visibleText = getVisiblePageText();
2861
+ const content = readabilityText.length > visibleText.length * 0.3 ? readabilityText : visibleText;
2787
2862
  return {
2788
2863
  title: article?.title || document.title,
2789
- content: article?.textContent || getVisiblePageText(),
2864
+ content,
2790
2865
  htmlContent: article?.content || "",
2791
2866
  byline: article?.byline || "",
2792
2867
  excerpt: article?.excerpt || "",
@@ -2813,6 +2888,9 @@ function vesselExtractContent() {
2813
2888
  Object.keys(elementSelectors).forEach(
2814
2889
  (key) => delete elementSelectors[key]
2815
2890
  );
2891
+ Object.keys(indexedElementRefs).forEach(
2892
+ (key) => delete indexedElementRefs[key]
2893
+ );
2816
2894
  const documentClone = document.cloneNode(true);
2817
2895
  const reader = new readabilityExports.Readability(documentClone);
2818
2896
  const article = reader.parse();
@@ -2825,9 +2903,43 @@ function vesselExtractContent() {
2825
2903
  function resolveElementSelector(index) {
2826
2904
  return elementSelectors[index] || null;
2827
2905
  }
2906
+ function interactByIndex(index, action, value) {
2907
+ const el = indexedElementRefs[index];
2908
+ if (!el || !(el instanceof HTMLElement)) {
2909
+ return "Error[stale-index]: Element not found — the page may have changed. Call read_page to refresh.";
2910
+ }
2911
+ if (action === "click") {
2912
+ el.focus();
2913
+ el.click();
2914
+ return "Clicked: " + (el.getAttribute("aria-label") || el.textContent?.trim().slice(0, 60) || el.tagName.toLowerCase());
2915
+ }
2916
+ if (action === "focus") {
2917
+ el.focus();
2918
+ return "Focused: " + (el.getAttribute("aria-label") || el.textContent?.trim().slice(0, 60) || el.tagName.toLowerCase());
2919
+ }
2920
+ if (action === "value" && value != null) {
2921
+ if (!(el instanceof HTMLInputElement) && !(el instanceof HTMLTextAreaElement) && !(el instanceof HTMLSelectElement)) {
2922
+ return "Error[not-input]: Element is not a text input";
2923
+ }
2924
+ const proto = el instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : el instanceof HTMLSelectElement ? HTMLSelectElement.prototype : HTMLInputElement.prototype;
2925
+ const desc = Object.getOwnPropertyDescriptor(proto, "value");
2926
+ if (desc?.set) {
2927
+ desc.set.call(el, value);
2928
+ } else {
2929
+ el.value = value;
2930
+ }
2931
+ el.focus();
2932
+ el.dispatchEvent(new Event("input", { bubbles: true }));
2933
+ el.dispatchEvent(new Event("change", { bubbles: true }));
2934
+ return "Typed into: " + (el.getAttribute("aria-label") || el.placeholder || el.name || "input");
2935
+ }
2936
+ return "Error: Unknown action";
2937
+ }
2828
2938
  electron.contextBridge.exposeInMainWorld("__vessel", {
2829
2939
  extractContent: vesselExtractContent,
2830
2940
  getElementSelector: resolveElementSelector,
2941
+ interactByIndex,
2942
+ resolveShadowSelector,
2831
2943
  notifyHighlightSelection: (text) => {
2832
2944
  if (typeof text === "string" && text.trim()) {
2833
2945
  electron.ipcRenderer.send("vessel:highlight-selection", text.trim());
@@ -35,6 +35,7 @@ const Channels = {
35
35
  SIDEBAR_RESIZE: "ui:sidebar-resize",
36
36
  SIDEBAR_RESIZE_START: "ui:sidebar-resize-start",
37
37
  SIDEBAR_RESIZE_COMMIT: "ui:sidebar-resize-commit",
38
+ SIDEBAR_CONTEXT_MENU: "ui:sidebar-context-menu",
38
39
  FOCUS_MODE_TOGGLE: "ui:focus-mode-toggle",
39
40
  SETTINGS_VISIBILITY: "ui:settings-visibility",
40
41
  // Settings
@@ -53,6 +54,11 @@ const Channels = {
53
54
  // Highlights
54
55
  HIGHLIGHT_CAPTURE: "highlights:capture",
55
56
  HIGHLIGHT_CAPTURE_RESULT: "highlights:capture-result",
57
+ HIGHLIGHT_NAV_COUNT: "highlights:nav-count",
58
+ HIGHLIGHT_NAV_SCROLL: "highlights:nav-scroll",
59
+ HIGHLIGHT_NAV_REMOVE: "highlights:nav-remove",
60
+ HIGHLIGHT_NAV_CLEAR: "highlights:nav-clear",
61
+ SIDEBAR_HIGHLIGHT_ACTION: "highlights:sidebar-action",
56
62
  // DevTools panel
57
63
  DEVTOOLS_PANEL_TOGGLE: "devtools-panel:toggle",
58
64
  DEVTOOLS_PANEL_STATE: "devtools-panel:state",
@@ -120,6 +126,15 @@ const api = {
120
126
  const handler = (_, result) => cb(result);
121
127
  electron.ipcRenderer.on(Channels.HIGHLIGHT_CAPTURE_RESULT, handler);
122
128
  return () => electron.ipcRenderer.removeListener(Channels.HIGHLIGHT_CAPTURE_RESULT, handler);
129
+ },
130
+ getCount: () => electron.ipcRenderer.invoke(Channels.HIGHLIGHT_NAV_COUNT),
131
+ scrollTo: (index) => electron.ipcRenderer.invoke(Channels.HIGHLIGHT_NAV_SCROLL, index),
132
+ remove: (index) => electron.ipcRenderer.invoke(Channels.HIGHLIGHT_NAV_REMOVE, index),
133
+ clearAll: () => electron.ipcRenderer.invoke(Channels.HIGHLIGHT_NAV_CLEAR),
134
+ onSidebarAction: (cb) => {
135
+ const handler = (_, action) => cb(action);
136
+ electron.ipcRenderer.on(Channels.SIDEBAR_HIGHLIGHT_ACTION, handler);
137
+ return () => electron.ipcRenderer.removeListener(Channels.SIDEBAR_HIGHLIGHT_ACTION, handler);
123
138
  }
124
139
  },
125
140
  ui: {
@@ -127,6 +142,11 @@ const api = {
127
142
  startSidebarResize: () => electron.ipcRenderer.invoke(Channels.SIDEBAR_RESIZE_START),
128
143
  resizeSidebar: (width) => electron.ipcRenderer.invoke(Channels.SIDEBAR_RESIZE, width),
129
144
  commitSidebarResize: () => electron.ipcRenderer.invoke(Channels.SIDEBAR_RESIZE_COMMIT),
145
+ onSidebarContextMenu: (cb) => {
146
+ const handler = (_, position) => cb(position);
147
+ electron.ipcRenderer.on(Channels.SIDEBAR_CONTEXT_MENU, handler);
148
+ return () => electron.ipcRenderer.removeListener(Channels.SIDEBAR_CONTEXT_MENU, handler);
149
+ },
130
150
  toggleFocusMode: () => electron.ipcRenderer.invoke(Channels.FOCUS_MODE_TOGGLE),
131
151
  setSettingsVisibility: (open) => electron.ipcRenderer.invoke(Channels.SETTINGS_VISIBILITY, open)
132
152
  },
@@ -2187,6 +2187,178 @@
2187
2187
  margin: 14px 0;
2188
2188
  }
2189
2189
 
2190
+ .markdown-content table {
2191
+ width: 100%;
2192
+ border-collapse: collapse;
2193
+ margin: 0 0 10px;
2194
+ font-size: 12.5px;
2195
+ line-height: 1.5;
2196
+ }
2197
+
2198
+ .markdown-content thead {
2199
+ border-bottom: 2px solid rgba(255, 255, 255, 0.12);
2200
+ }
2201
+
2202
+ .markdown-content th {
2203
+ text-align: left;
2204
+ font-weight: 600;
2205
+ color: var(--text-primary);
2206
+ padding: 6px 10px;
2207
+ }
2208
+
2209
+ .markdown-content td {
2210
+ padding: 5px 10px;
2211
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
2212
+ color: var(--text-secondary);
2213
+ }
2214
+
2215
+ .markdown-content tbody tr:hover {
2216
+ background: rgba(255, 255, 255, 0.03);
2217
+ }
2218
+
2219
+
2220
+ /* ═══════════════════════════════════════
2221
+ Tool call action chips
2222
+ ═══════════════════════════════════════ */
2223
+
2224
+ .tool-chip {
2225
+ display: flex;
2226
+ align-items: center;
2227
+ gap: 6px;
2228
+ padding: 3px 10px 3px 7px;
2229
+ margin: 3px 0;
2230
+ background: rgba(255, 255, 255, 0.04);
2231
+ border-left: 2px solid rgba(196, 160, 90, 0.4);
2232
+ border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
2233
+ font-size: 11.5px;
2234
+ line-height: 1.5;
2235
+ color: var(--text-muted);
2236
+ font-family: var(--font-mono);
2237
+ transition: background var(--duration-fast) var(--ease-in-out);
2238
+ }
2239
+
2240
+ .tool-chip:hover {
2241
+ background: rgba(255, 255, 255, 0.07);
2242
+ }
2243
+
2244
+ .tool-chip-icon {
2245
+ flex-shrink: 0;
2246
+ width: 16px;
2247
+ text-align: center;
2248
+ font-size: 12px;
2249
+ color: rgba(196, 160, 90, 0.7);
2250
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
2251
+ }
2252
+
2253
+ .tool-chip-name {
2254
+ color: var(--text-secondary);
2255
+ font-weight: 500;
2256
+ letter-spacing: 0.01em;
2257
+ }
2258
+
2259
+ .tool-chip-args {
2260
+ color: var(--text-muted);
2261
+ opacity: 0.7;
2262
+ font-weight: 400;
2263
+ max-width: 200px;
2264
+ overflow: hidden;
2265
+ text-overflow: ellipsis;
2266
+ white-space: nowrap;
2267
+ }
2268
+
2269
+ .tool-chip-args::before {
2270
+ content: "·";
2271
+ margin-right: 5px;
2272
+ opacity: 0.4;
2273
+ }
2274
+
2275
+
2276
+ /* ═══════════════════════════════════════
2277
+ Inline approval prompts (chat tab)
2278
+ ═══════════════════════════════════════ */
2279
+
2280
+ .chat-approval {
2281
+ display: flex;
2282
+ gap: 10px;
2283
+ margin: 8px 14px;
2284
+ padding: 10px 12px;
2285
+ background: rgba(224, 180, 80, 0.08);
2286
+ border: 1px solid rgba(224, 180, 80, 0.2);
2287
+ border-radius: var(--radius-md);
2288
+ animation: approval-appear 0.25s var(--ease-out-back);
2289
+ }
2290
+
2291
+ @keyframes approval-appear {
2292
+ from { opacity: 0; transform: translateY(6px); }
2293
+ to { opacity: 1; transform: translateY(0); }
2294
+ }
2295
+
2296
+ .chat-approval-icon {
2297
+ flex-shrink: 0;
2298
+ color: #e0b450;
2299
+ margin-top: 1px;
2300
+ }
2301
+
2302
+ .chat-approval-body {
2303
+ flex: 1;
2304
+ min-width: 0;
2305
+ }
2306
+
2307
+ .chat-approval-title {
2308
+ font-size: 12.5px;
2309
+ color: var(--text-primary);
2310
+ margin-bottom: 2px;
2311
+ }
2312
+
2313
+ .chat-approval-detail {
2314
+ font-size: 11.5px;
2315
+ color: var(--text-muted);
2316
+ overflow: hidden;
2317
+ text-overflow: ellipsis;
2318
+ white-space: nowrap;
2319
+ }
2320
+
2321
+ .chat-approval-actions {
2322
+ display: flex;
2323
+ gap: 6px;
2324
+ margin-top: 8px;
2325
+ }
2326
+
2327
+ .chat-approval-btn {
2328
+ font-size: 11.5px;
2329
+ padding: 4px 14px;
2330
+ border-radius: var(--radius-sm);
2331
+ border: none;
2332
+ cursor: pointer;
2333
+ font-weight: 500;
2334
+ transition:
2335
+ background var(--duration-fast) var(--ease-in-out),
2336
+ transform var(--duration-fast) var(--ease-out-back);
2337
+ }
2338
+
2339
+ .chat-approval-btn:active {
2340
+ transform: scale(0.95);
2341
+ }
2342
+
2343
+ .chat-approval-approve {
2344
+ background: rgba(224, 180, 80, 0.18);
2345
+ color: #e0c070;
2346
+ }
2347
+
2348
+ .chat-approval-approve:hover {
2349
+ background: rgba(224, 180, 80, 0.28);
2350
+ }
2351
+
2352
+ .chat-approval-reject {
2353
+ background: rgba(255, 255, 255, 0.06);
2354
+ color: var(--text-muted);
2355
+ }
2356
+
2357
+ .chat-approval-reject:hover {
2358
+ background: rgba(255, 255, 255, 0.1);
2359
+ color: var(--text-secondary);
2360
+ }
2361
+
2190
2362
 
2191
2363
  /* ═══════════════════════════════════════
2192
2364
  Thinking & streaming states
@@ -2361,6 +2533,101 @@
2361
2533
  border-color: var(--border-visible);
2362
2534
  }
2363
2535
 
2536
+ /* ═══════════════════════════════════════
2537
+ Highlight navigation bar
2538
+ ═══════════════════════════════════════ */
2539
+
2540
+ .highlight-nav {
2541
+ display: flex;
2542
+ align-items: center;
2543
+ justify-content: flex-end;
2544
+ gap: 2px;
2545
+ padding: 4px 14px;
2546
+ flex-shrink: 0;
2547
+ }
2548
+
2549
+ .highlight-nav-btn {
2550
+ display: inline-flex;
2551
+ align-items: center;
2552
+ justify-content: center;
2553
+ width: 24px;
2554
+ height: 24px;
2555
+ padding: 0;
2556
+ color: var(--text-muted);
2557
+ background: transparent;
2558
+ border: 1px solid transparent;
2559
+ border-radius: var(--radius-sm);
2560
+ cursor: pointer;
2561
+ transition: color var(--duration-fast) var(--ease-in-out),
2562
+ background var(--duration-fast) var(--ease-in-out);
2563
+ }
2564
+
2565
+ .highlight-nav-btn:hover:not(:disabled) {
2566
+ color: var(--text-primary);
2567
+ background: var(--bg-tertiary);
2568
+ }
2569
+
2570
+ .highlight-nav-btn:disabled {
2571
+ opacity: 0.3;
2572
+ cursor: default;
2573
+ }
2574
+
2575
+ .highlight-nav-label {
2576
+ display: inline-flex;
2577
+ align-items: center;
2578
+ gap: 5px;
2579
+ padding: 3px 10px;
2580
+ font-size: 11px;
2581
+ font-weight: 500;
2582
+ color: rgba(196, 160, 90, 0.85);
2583
+ background: rgba(196, 160, 90, 0.08);
2584
+ border: 1px solid rgba(196, 160, 90, 0.2);
2585
+ border-radius: var(--radius-md);
2586
+ cursor: pointer;
2587
+ transition: background var(--duration-fast) var(--ease-in-out),
2588
+ border-color var(--duration-fast) var(--ease-in-out);
2589
+ white-space: nowrap;
2590
+ }
2591
+
2592
+ .highlight-nav-label:hover {
2593
+ background: rgba(196, 160, 90, 0.14);
2594
+ border-color: rgba(196, 160, 90, 0.35);
2595
+ }
2596
+
2597
+ .hl-context-menu {
2598
+ position: fixed;
2599
+ z-index: 9999;
2600
+ min-width: 160px;
2601
+ padding: 4px 0;
2602
+ background: var(--bg-secondary);
2603
+ border: 1px solid var(--border-visible);
2604
+ border-radius: var(--radius-md);
2605
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
2606
+ }
2607
+
2608
+ .hl-context-item {
2609
+ display: block;
2610
+ width: 100%;
2611
+ padding: 6px 12px;
2612
+ font-size: 11.5px;
2613
+ color: var(--text-secondary);
2614
+ background: transparent;
2615
+ border: none;
2616
+ cursor: pointer;
2617
+ text-align: left;
2618
+ transition: background var(--duration-fast) var(--ease-in-out);
2619
+ }
2620
+
2621
+ .hl-context-item:hover {
2622
+ background: rgba(255, 255, 255, 0.06);
2623
+ color: var(--text-primary);
2624
+ }
2625
+
2626
+ .hl-context-danger:hover {
2627
+ color: #e06060;
2628
+ background: rgba(224, 96, 96, 0.08);
2629
+ }
2630
+
2364
2631
  .sidebar-input-area {
2365
2632
  display: flex;
2366
2633
  gap: 6px;