IPF commited on
Commit
a03d6ab
·
verified ·
1 Parent(s): 529598a

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +187 -16
app.py CHANGED
@@ -181,13 +181,13 @@ class SimpleBrowser:
181
  # Prioritize link_map as it stores search result metadata
182
  if cursor in self.link_map:
183
  return self.link_map[cursor]
184
-
185
  # Fallback to page_stack for opened pages
186
  if 0 <= cursor < len(self.page_stack):
187
  url = self.page_stack[cursor]
188
  page = self.pages.get(url)
189
  if page:
190
- return {'url': url, 'title': page.get('title', '')}
191
  return None
192
 
193
  def _format_line_numbers(self, text: str, offset: int = 0) -> str:
@@ -213,8 +213,9 @@ class SimpleBrowser:
213
  except:
214
  domain = ''
215
 
216
- self.link_map[i] = {'url': url, 'title': title}
217
- link_map[i] = {'url': url, 'title': title}
 
218
  link_text = f"【{i}†{title}†{domain}】" if domain else f"【{i}†{title}】"
219
  lines.append(f"{link_text}")
220
  lines.append(f" {snippet}")
@@ -485,7 +486,7 @@ def is_final_answer(text: str) -> bool:
485
  # HTML Rendering Helpers (From app_local.py)
486
  # ============================================================
487
  def render_citations(text: str, browser: SimpleBrowser) -> str:
488
- """Convert citation markers to clickable HTML links."""
489
  def replace_citation(m):
490
  cursor_str = m.group(1)
491
  # l1 = m.group(2)
@@ -494,15 +495,30 @@ def render_citations(text: str, browser: SimpleBrowser) -> str:
494
  try:
495
  cursor = int(cursor_str)
496
  index = browser.get_citation_index(cursor)
497
-
498
  # Check if we have URL info
499
  info = browser.get_page_info(cursor)
500
  if info and info.get('url'):
501
- # Return clickable index link pointing to reference section
502
- # Aligned with generate_html_example.py style (green via CSS class)
503
- url = info.get('url')
504
- return f'<a href="{html.escape(url)}" target="_blank" class="citation-link">[{index}]</a>'
505
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
506
  # Fallback if no URL
507
  return f'<span class="citation-link">[{index}]</span>'
508
  except Exception as e:
@@ -512,12 +528,13 @@ def render_citations(text: str, browser: SimpleBrowser) -> str:
512
 
513
  # First pass: replace citations with linked citations
514
  result = re.sub(r'[【\[](\d+)†.*?[】\]]', replace_citation, text)
515
-
516
  # Second pass: Deduplicate adjacent identical citations
517
- # Matches: <a ...>[N]</a> followed by optional whitespace and same link
518
- # We repeat this until no more changes to handle multiple duplicates
519
  while True:
520
- new_result = re.sub(r'(<a [^>]+>\[\d+\]</a>)(\s*)\1', r'\1', result)
 
521
  if new_result == result:
522
  break
523
  result = new_result
@@ -1479,7 +1496,95 @@ def create_interface():
1479
  .answer-section a:hover {
1480
  text-decoration: underline;
1481
  }
1482
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1483
  /* User Message Bubble - 淡蓝色背景,右对齐 */
1484
  .user-message-bubble {
1485
  background: linear-gradient(135deg, #e0f2fe 0%, #bae6fd 100%);
@@ -2066,6 +2171,39 @@ def create_interface():
2066
  color: #f0f9ff !important;
2067
  }
2068
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2069
  /* 用户问题气泡深色模式 */
2070
  .user-message-bubble {
2071
  background: linear-gradient(135deg, #1e3a5f 0%, #1e40af 100%) !important;
@@ -2198,6 +2336,39 @@ def create_interface():
2198
  color: #e0f2fe !important;
2199
  }
2200
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2201
  .dark .search-result-card {
2202
  background: #1f2937 !important;
2203
  border-color: #374151 !important;
 
181
  # Prioritize link_map as it stores search result metadata
182
  if cursor in self.link_map:
183
  return self.link_map[cursor]
184
+
185
  # Fallback to page_stack for opened pages
186
  if 0 <= cursor < len(self.page_stack):
187
  url = self.page_stack[cursor]
188
  page = self.pages.get(url)
189
  if page:
190
+ return {'url': url, 'title': page.get('title', ''), 'snippet': ''}
191
  return None
192
 
193
  def _format_line_numbers(self, text: str, offset: int = 0) -> str:
 
213
  except:
214
  domain = ''
215
 
216
+ # Store snippet information as well
217
+ self.link_map[i] = {'url': url, 'title': title, 'snippet': snippet}
218
+ link_map[i] = {'url': url, 'title': title, 'snippet': snippet}
219
  link_text = f"【{i}†{title}†{domain}】" if domain else f"【{i}†{title}】"
220
  lines.append(f"{link_text}")
221
  lines.append(f" {snippet}")
 
486
  # HTML Rendering Helpers (From app_local.py)
487
  # ============================================================
488
  def render_citations(text: str, browser: SimpleBrowser) -> str:
489
+ """Convert citation markers to clickable HTML links with tooltips."""
490
  def replace_citation(m):
491
  cursor_str = m.group(1)
492
  # l1 = m.group(2)
 
495
  try:
496
  cursor = int(cursor_str)
497
  index = browser.get_citation_index(cursor)
498
+
499
  # Check if we have URL info
500
  info = browser.get_page_info(cursor)
501
  if info and info.get('url'):
502
+ url = info.get('url', '')
503
+ title = info.get('title', 'No Title')
504
+ snippet = info.get('snippet', '')
505
+
506
+ # Unescape HTML entities for display (they were escaped in _clean_links)
507
+ title_display = title
508
+ snippet_display = snippet if snippet else 'No description available'
509
+
510
+ # Truncate URL for display
511
+ url_display = url if len(url) <= 60 else url[:57] + '...'
512
+
513
+ # Create citation with tooltip
514
+ tooltip_html = f'''<span class="citation-tooltip">
515
+ <div class="citation-tooltip-title">{title_display}</div>
516
+ <div class="citation-tooltip-snippet">{snippet_display}</div>
517
+ <div class="citation-tooltip-url">{html.escape(url_display)}</div>
518
+ </span>'''
519
+
520
+ return f'<span class="citation-wrapper"><a href="{html.escape(url)}" target="_blank" class="citation-link">[{index}]</a>{tooltip_html}</span>'
521
+
522
  # Fallback if no URL
523
  return f'<span class="citation-link">[{index}]</span>'
524
  except Exception as e:
 
528
 
529
  # First pass: replace citations with linked citations
530
  result = re.sub(r'[【\[](\d+)†.*?[】\]]', replace_citation, text)
531
+
532
  # Second pass: Deduplicate adjacent identical citations
533
+ # Matches: <span class="citation-wrapper">...</span> followed by optional whitespace and same wrapper
534
+ # We need to be more careful here with the new structure
535
  while True:
536
+ # Match citation wrapper and deduplicate
537
+ new_result = re.sub(r'(<span class="citation-wrapper">.*?</span>)(\s*)\1', r'\1', result)
538
  if new_result == result:
539
  break
540
  result = new_result
 
1496
  .answer-section a:hover {
1497
  text-decoration: underline;
1498
  }
1499
+
1500
+ /* Citation Tooltip - 小便签弹窗 */
1501
+ .citation-wrapper {
1502
+ position: relative;
1503
+ display: inline-block;
1504
+ }
1505
+
1506
+ .citation-link {
1507
+ color: #10a37f;
1508
+ text-decoration: none;
1509
+ font-weight: 600;
1510
+ padding: 2px 4px;
1511
+ border-radius: 4px;
1512
+ background: #e6f7f1;
1513
+ transition: all 0.2s ease;
1514
+ cursor: pointer;
1515
+ border: 1px solid #10a37f30;
1516
+ }
1517
+
1518
+ .citation-link:hover {
1519
+ background: #10a37f;
1520
+ color: white;
1521
+ text-decoration: none;
1522
+ }
1523
+
1524
+ .citation-tooltip {
1525
+ visibility: hidden;
1526
+ opacity: 0;
1527
+ position: absolute;
1528
+ bottom: 125%;
1529
+ left: 50%;
1530
+ transform: translateX(-50%);
1531
+ z-index: 1000;
1532
+ width: 280px;
1533
+ background: linear-gradient(135deg, #ffffff 0%, #f9fafb 100%);
1534
+ border: 2px solid #10a37f;
1535
+ border-radius: 12px;
1536
+ padding: 12px 14px;
1537
+ box-shadow: 0 8px 24px rgba(16, 163, 127, 0.25), 0 4px 8px rgba(0, 0, 0, 0.1);
1538
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
1539
+ pointer-events: none;
1540
+ }
1541
+
1542
+ .citation-tooltip::after {
1543
+ content: "";
1544
+ position: absolute;
1545
+ top: 100%;
1546
+ left: 50%;
1547
+ transform: translateX(-50%);
1548
+ border: 8px solid transparent;
1549
+ border-top-color: #10a37f;
1550
+ }
1551
+
1552
+ .citation-wrapper:hover .citation-tooltip {
1553
+ visibility: visible;
1554
+ opacity: 1;
1555
+ bottom: 130%;
1556
+ }
1557
+
1558
+ .citation-tooltip-title {
1559
+ font-weight: 600;
1560
+ color: #1e293b;
1561
+ font-size: 0.9rem;
1562
+ margin-bottom: 8px;
1563
+ line-height: 1.4;
1564
+ border-bottom: 1px solid #e5e7eb;
1565
+ padding-bottom: 6px;
1566
+ }
1567
+
1568
+ .citation-tooltip-snippet {
1569
+ color: #475569;
1570
+ font-size: 0.8rem;
1571
+ line-height: 1.5;
1572
+ margin-bottom: 8px;
1573
+ max-height: 80px;
1574
+ overflow-y: auto;
1575
+ }
1576
+
1577
+ .citation-tooltip-url {
1578
+ color: #10a37f;
1579
+ font-size: 0.75rem;
1580
+ font-family: 'SF Mono', Monaco, monospace;
1581
+ word-break: break-all;
1582
+ background: #f0fdf4;
1583
+ padding: 4px 6px;
1584
+ border-radius: 4px;
1585
+ border: 1px solid #10a37f20;
1586
+ }
1587
+
1588
  /* User Message Bubble - 淡蓝色背景,右对齐 */
1589
  .user-message-bubble {
1590
  background: linear-gradient(135deg, #e0f2fe 0%, #bae6fd 100%);
 
2171
  color: #f0f9ff !important;
2172
  }
2173
 
2174
+ /* Citation Tooltip 深色模式 */
2175
+ .citation-link {
2176
+ background: #064e3b !important;
2177
+ border-color: #10a37f80 !important;
2178
+ color: #6ee7b7 !important;
2179
+ }
2180
+
2181
+ .citation-link:hover {
2182
+ background: #10a37f !important;
2183
+ color: #ffffff !important;
2184
+ }
2185
+
2186
+ .citation-tooltip {
2187
+ background: linear-gradient(135deg, #1f2937 0%, #111827 100%) !important;
2188
+ border-color: #10a37f !important;
2189
+ box-shadow: 0 8px 24px rgba(16, 163, 127, 0.4), 0 4px 8px rgba(0, 0, 0, 0.3) !important;
2190
+ }
2191
+
2192
+ .citation-tooltip-title {
2193
+ color: #e5e7eb !important;
2194
+ border-bottom-color: #374151 !important;
2195
+ }
2196
+
2197
+ .citation-tooltip-snippet {
2198
+ color: #d1d5db !important;
2199
+ }
2200
+
2201
+ .citation-tooltip-url {
2202
+ color: #6ee7b7 !important;
2203
+ background: #064e3b !important;
2204
+ border-color: #10a37f40 !important;
2205
+ }
2206
+
2207
  /* 用户问题气泡深色模式 */
2208
  .user-message-bubble {
2209
  background: linear-gradient(135deg, #1e3a5f 0%, #1e40af 100%) !important;
 
2336
  color: #e0f2fe !important;
2337
  }
2338
 
2339
+ /* Citation Tooltip Gradio dark 模式 */
2340
+ .dark .citation-link {
2341
+ background: #064e3b !important;
2342
+ border-color: #10a37f80 !important;
2343
+ color: #6ee7b7 !important;
2344
+ }
2345
+
2346
+ .dark .citation-link:hover {
2347
+ background: #10a37f !important;
2348
+ color: #ffffff !important;
2349
+ }
2350
+
2351
+ .dark .citation-tooltip {
2352
+ background: linear-gradient(135deg, #1f2937 0%, #111827 100%) !important;
2353
+ border-color: #10a37f !important;
2354
+ box-shadow: 0 8px 24px rgba(16, 163, 127, 0.4), 0 4px 8px rgba(0, 0, 0, 0.3) !important;
2355
+ }
2356
+
2357
+ .dark .citation-tooltip-title {
2358
+ color: #e5e7eb !important;
2359
+ border-bottom-color: #374151 !important;
2360
+ }
2361
+
2362
+ .dark .citation-tooltip-snippet {
2363
+ color: #d1d5db !important;
2364
+ }
2365
+
2366
+ .dark .citation-tooltip-url {
2367
+ color: #6ee7b7 !important;
2368
+ background: #064e3b !important;
2369
+ border-color: #10a37f40 !important;
2370
+ }
2371
+
2372
  .dark .search-result-card {
2373
  background: #1f2937 !important;
2374
  border-color: #374151 !important;