Spaces:
Running
Running
Upload app.py
Browse files
app.py
CHANGED
|
@@ -489,8 +489,14 @@ 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 |
-
|
| 493 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 494 |
|
| 495 |
try:
|
| 496 |
cursor = int(cursor_str)
|
|
@@ -507,14 +513,23 @@ def render_citations(text: str, browser: SimpleBrowser) -> str:
|
|
| 507 |
title_display = title
|
| 508 |
snippet_display = snippet if snippet else 'No description available'
|
| 509 |
|
| 510 |
-
#
|
| 511 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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"
|
| 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>'
|
|
@@ -529,9 +544,12 @@ def render_citations(text: str, browser: SimpleBrowser) -> str:
|
|
| 529 |
# First pass: replace citations with linked citations
|
| 530 |
result = re.sub(r'[【\[](\d+)†.*?[】\]]', replace_citation, text)
|
| 531 |
|
| 532 |
-
# Second pass:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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)
|
|
@@ -673,8 +691,12 @@ def render_tool_result(result: str, fn_name: str) -> str:
|
|
| 673 |
<div style="color: #6b7280; font-size: 0.85rem; line-height: 1.5; margin-top: 0.75rem;">
|
| 674 |
{html.escape(summary)}
|
| 675 |
</div>
|
| 676 |
-
<div style="color: #9ca3af; font-size: 0.
|
| 677 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 678 |
</div>
|
| 679 |
</div>
|
| 680 |
</div>
|
|
@@ -1565,6 +1587,18 @@ def create_interface():
|
|
| 1565 |
padding-bottom: 6px;
|
| 1566 |
}
|
| 1567 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1568 |
.citation-tooltip-snippet {
|
| 1569 |
color: #475569;
|
| 1570 |
font-size: 0.8rem;
|
|
@@ -1575,14 +1609,20 @@ def create_interface():
|
|
| 1575 |
}
|
| 1576 |
|
| 1577 |
.citation-tooltip-url {
|
| 1578 |
-
color: #
|
| 1579 |
-
font-size: 0.
|
| 1580 |
font-family: 'SF Mono', Monaco, monospace;
|
| 1581 |
word-break: break-all;
|
| 1582 |
-
|
| 1583 |
-
|
| 1584 |
-
|
| 1585 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1586 |
}
|
| 1587 |
|
| 1588 |
/* User Message Bubble - 淡蓝色背景,右对齐 */
|
|
@@ -2194,14 +2234,25 @@ def create_interface():
|
|
| 2194 |
border-bottom-color: #374151 !important;
|
| 2195 |
}
|
| 2196 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2197 |
.citation-tooltip-snippet {
|
| 2198 |
color: #d1d5db !important;
|
| 2199 |
}
|
| 2200 |
|
| 2201 |
.citation-tooltip-url {
|
| 2202 |
-
color: #
|
| 2203 |
-
|
| 2204 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2205 |
}
|
| 2206 |
|
| 2207 |
/* 用户问题气泡深色模式 */
|
|
@@ -2359,14 +2410,25 @@ def create_interface():
|
|
| 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: #
|
| 2368 |
-
|
| 2369 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2370 |
}
|
| 2371 |
|
| 2372 |
.dark .search-result-card {
|
|
|
|
| 489 |
"""Convert citation markers to clickable HTML links with tooltips."""
|
| 490 |
def replace_citation(m):
|
| 491 |
cursor_str = m.group(1)
|
| 492 |
+
full_match = m.group(0) # Get the full match to extract line info
|
| 493 |
+
|
| 494 |
+
# Extract line information from the citation marker
|
| 495 |
+
# Format: 【{cursor}†L{line_start}(-L{line_end})?】
|
| 496 |
+
line_info = ""
|
| 497 |
+
line_match = re.search(r'†(L\d+(?:-L\d+)?)', full_match)
|
| 498 |
+
if line_match:
|
| 499 |
+
line_info = line_match.group(1)
|
| 500 |
|
| 501 |
try:
|
| 502 |
cursor = int(cursor_str)
|
|
|
|
| 513 |
title_display = title
|
| 514 |
snippet_display = snippet if snippet else 'No description available'
|
| 515 |
|
| 516 |
+
# Extract domain from URL
|
| 517 |
+
try:
|
| 518 |
+
domain = url.split('/')[2] if len(url.split('/')) > 2 else url
|
| 519 |
+
except:
|
| 520 |
+
domain = url
|
| 521 |
+
|
| 522 |
+
# Add line info if available
|
| 523 |
+
line_html = ""
|
| 524 |
+
if line_info:
|
| 525 |
+
line_html = f'<div class="citation-tooltip-line">📍 {line_info}</div>'
|
| 526 |
|
| 527 |
# Create citation with tooltip
|
| 528 |
tooltip_html = f'''<span class="citation-tooltip">
|
| 529 |
<div class="citation-tooltip-title">{title_display}</div>
|
| 530 |
+
{line_html}
|
| 531 |
<div class="citation-tooltip-snippet">{snippet_display}</div>
|
| 532 |
+
<div class="citation-tooltip-url">🔗 {html.escape(domain)}</div>
|
| 533 |
</span>'''
|
| 534 |
|
| 535 |
return f'<span class="citation-wrapper"><a href="{html.escape(url)}" target="_blank" class="citation-link">[{index}]</a>{tooltip_html}</span>'
|
|
|
|
| 544 |
# First pass: replace citations with linked citations
|
| 545 |
result = re.sub(r'[【\[](\d+)†.*?[】\]]', replace_citation, text)
|
| 546 |
|
| 547 |
+
# Second pass: Remove standalone URLs that appear after text (common pattern)
|
| 548 |
+
# This removes URLs that directly follow sentences without proper citation
|
| 549 |
+
result = re.sub(r'(?<=[.!?])\s+(https?://[^\s]+)', '', result)
|
| 550 |
+
|
| 551 |
+
# Third pass: Deduplicate adjacent identical citations
|
| 552 |
# Matches: <span class="citation-wrapper">...</span> followed by optional whitespace and same wrapper
|
|
|
|
| 553 |
while True:
|
| 554 |
# Match citation wrapper and deduplicate
|
| 555 |
new_result = re.sub(r'(<span class="citation-wrapper">.*?</span>)(\s*)\1', r'\1', result)
|
|
|
|
| 691 |
<div style="color: #6b7280; font-size: 0.85rem; line-height: 1.5; margin-top: 0.75rem;">
|
| 692 |
{html.escape(summary)}
|
| 693 |
</div>
|
| 694 |
+
<div style="display: flex; align-items: center; gap: 0.25rem; color: #9ca3af; font-size: 0.7rem; margin-top: 0.5rem;">
|
| 695 |
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
| 696 |
+
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path>
|
| 697 |
+
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path>
|
| 698 |
+
</svg>
|
| 699 |
+
<span style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">{html.escape(url.split('/')[2] if len(url.split('/')) > 2 else url)}</span>
|
| 700 |
</div>
|
| 701 |
</div>
|
| 702 |
</div>
|
|
|
|
| 1587 |
padding-bottom: 6px;
|
| 1588 |
}
|
| 1589 |
|
| 1590 |
+
.citation-tooltip-line {
|
| 1591 |
+
color: #059669;
|
| 1592 |
+
font-size: 0.75rem;
|
| 1593 |
+
font-weight: 600;
|
| 1594 |
+
margin-bottom: 6px;
|
| 1595 |
+
padding: 3px 8px;
|
| 1596 |
+
background: #ecfdf5;
|
| 1597 |
+
border-radius: 4px;
|
| 1598 |
+
display: inline-block;
|
| 1599 |
+
border: 1px solid #10a37f30;
|
| 1600 |
+
}
|
| 1601 |
+
|
| 1602 |
.citation-tooltip-snippet {
|
| 1603 |
color: #475569;
|
| 1604 |
font-size: 0.8rem;
|
|
|
|
| 1609 |
}
|
| 1610 |
|
| 1611 |
.citation-tooltip-url {
|
| 1612 |
+
color: #6b7280;
|
| 1613 |
+
font-size: 0.7rem;
|
| 1614 |
font-family: 'SF Mono', Monaco, monospace;
|
| 1615 |
word-break: break-all;
|
| 1616 |
+
padding: 2px 0;
|
| 1617 |
+
border-top: 1px solid #e5e7eb;
|
| 1618 |
+
padding-top: 6px;
|
| 1619 |
+
text-decoration: none;
|
| 1620 |
+
opacity: 0.8;
|
| 1621 |
+
}
|
| 1622 |
+
|
| 1623 |
+
.citation-tooltip-url:hover {
|
| 1624 |
+
color: #10a37f;
|
| 1625 |
+
opacity: 1;
|
| 1626 |
}
|
| 1627 |
|
| 1628 |
/* User Message Bubble - 淡蓝色背景,右对齐 */
|
|
|
|
| 2234 |
border-bottom-color: #374151 !important;
|
| 2235 |
}
|
| 2236 |
|
| 2237 |
+
.citation-tooltip-line {
|
| 2238 |
+
color: #6ee7b7 !important;
|
| 2239 |
+
background: #064e3b !important;
|
| 2240 |
+
border-color: #10a37f50 !important;
|
| 2241 |
+
}
|
| 2242 |
+
|
| 2243 |
.citation-tooltip-snippet {
|
| 2244 |
color: #d1d5db !important;
|
| 2245 |
}
|
| 2246 |
|
| 2247 |
.citation-tooltip-url {
|
| 2248 |
+
color: #9ca3af !important;
|
| 2249 |
+
border-top-color: #374151 !important;
|
| 2250 |
+
opacity: 0.8 !important;
|
| 2251 |
+
}
|
| 2252 |
+
|
| 2253 |
+
.citation-tooltip-url:hover {
|
| 2254 |
+
color: #10a37f !important;
|
| 2255 |
+
opacity: 1 !important;
|
| 2256 |
}
|
| 2257 |
|
| 2258 |
/* 用户问题气泡深色模式 */
|
|
|
|
| 2410 |
border-bottom-color: #374151 !important;
|
| 2411 |
}
|
| 2412 |
|
| 2413 |
+
.dark .citation-tooltip-line {
|
| 2414 |
+
color: #6ee7b7 !important;
|
| 2415 |
+
background: #064e3b !important;
|
| 2416 |
+
border-color: #10a37f50 !important;
|
| 2417 |
+
}
|
| 2418 |
+
|
| 2419 |
.dark .citation-tooltip-snippet {
|
| 2420 |
color: #d1d5db !important;
|
| 2421 |
}
|
| 2422 |
|
| 2423 |
.dark .citation-tooltip-url {
|
| 2424 |
+
color: #9ca3af !important;
|
| 2425 |
+
border-top-color: #374151 !important;
|
| 2426 |
+
opacity: 0.8 !important;
|
| 2427 |
+
}
|
| 2428 |
+
|
| 2429 |
+
.dark .citation-tooltip-url:hover {
|
| 2430 |
+
color: #10a37f !important;
|
| 2431 |
+
opacity: 1 !important;
|
| 2432 |
}
|
| 2433 |
|
| 2434 |
.dark .search-result-card {
|