iris-chatbot 5.0.3 → 5.0.4
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/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iris",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.4",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "iris",
|
|
9
|
-
"version": "5.0.
|
|
9
|
+
"version": "5.0.4",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@anthropic-ai/sdk": "^0.72.1",
|
|
12
12
|
"clsx": "^2.1.1",
|
package/template/package.json
CHANGED
|
@@ -524,6 +524,12 @@ button:focus-visible {
|
|
|
524
524
|
text-underline-offset: 2px;
|
|
525
525
|
}
|
|
526
526
|
|
|
527
|
+
.message-content a.message-content-link-badge {
|
|
528
|
+
margin: 0 2px;
|
|
529
|
+
vertical-align: baseline;
|
|
530
|
+
text-decoration: none;
|
|
531
|
+
}
|
|
532
|
+
|
|
527
533
|
.source-badge-row {
|
|
528
534
|
margin-top: 10px;
|
|
529
535
|
display: flex;
|
|
@@ -531,6 +537,10 @@ button:focus-visible {
|
|
|
531
537
|
gap: 8px;
|
|
532
538
|
}
|
|
533
539
|
|
|
540
|
+
.message-content a.source-badge {
|
|
541
|
+
text-decoration: none;
|
|
542
|
+
}
|
|
543
|
+
|
|
534
544
|
.source-badge {
|
|
535
545
|
display: inline-flex;
|
|
536
546
|
align-items: center;
|
|
@@ -539,18 +549,19 @@ button:focus-visible {
|
|
|
539
549
|
border-radius: 999px;
|
|
540
550
|
border: 1px solid var(--border);
|
|
541
551
|
background: var(--panel-2);
|
|
542
|
-
color:
|
|
552
|
+
color: rgba(255, 255, 255, 0.88);
|
|
543
553
|
text-decoration: none;
|
|
544
554
|
font-size: 12px;
|
|
545
555
|
line-height: 1;
|
|
546
556
|
max-width: 220px;
|
|
547
|
-
transition: border-color 0.18s ease, color 0.18s ease, background 0.18s ease;
|
|
557
|
+
transition: border-color 0.18s ease, color 0.18s ease, background 0.18s ease, box-shadow 0.18s ease;
|
|
548
558
|
}
|
|
549
559
|
|
|
550
560
|
.source-badge:hover {
|
|
551
561
|
border-color: var(--border-strong);
|
|
552
|
-
color:
|
|
562
|
+
color: #ffffff;
|
|
553
563
|
background: var(--panel-3);
|
|
564
|
+
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.35);
|
|
554
565
|
}
|
|
555
566
|
|
|
556
567
|
.source-badge-index {
|
|
@@ -561,15 +572,46 @@ button:focus-visible {
|
|
|
561
572
|
height: 16px;
|
|
562
573
|
border-radius: 999px;
|
|
563
574
|
border: 1px solid var(--border-strong);
|
|
564
|
-
color:
|
|
575
|
+
color: rgba(255, 255, 255, 0.78);
|
|
565
576
|
font-size: 10px;
|
|
566
577
|
font-weight: 700;
|
|
578
|
+
text-decoration: none;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
[data-theme="light"] .source-badge {
|
|
582
|
+
color: #2d2d2d;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
[data-theme="light"] .source-badge:hover {
|
|
586
|
+
color: #111111;
|
|
587
|
+
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
[data-theme="light"] .source-badge-index {
|
|
591
|
+
color: #4a4a4a;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
[data-theme="light"] .source-badge:hover .source-badge-index {
|
|
595
|
+
color: #333333;
|
|
567
596
|
}
|
|
568
597
|
|
|
569
598
|
.source-badge-title {
|
|
570
599
|
overflow: hidden;
|
|
571
600
|
text-overflow: ellipsis;
|
|
572
601
|
white-space: nowrap;
|
|
602
|
+
color: rgba(255, 255, 255, 0.88);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
.source-badge:hover .source-badge-title {
|
|
606
|
+
color: #ffffff;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
[data-theme="light"] .source-badge-title {
|
|
610
|
+
color: #1a1a1a;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
[data-theme="light"] .source-badge:hover .source-badge-title {
|
|
614
|
+
color: #111111;
|
|
573
615
|
}
|
|
574
616
|
|
|
575
617
|
.message-content hr {
|
|
@@ -14,7 +14,13 @@ import {
|
|
|
14
14
|
X,
|
|
15
15
|
} from "lucide-react";
|
|
16
16
|
import { memo, useMemo, useState } from "react";
|
|
17
|
-
import type {
|
|
17
|
+
import type {
|
|
18
|
+
ChatCitationSource,
|
|
19
|
+
MessageNode,
|
|
20
|
+
Thread,
|
|
21
|
+
ToolApproval,
|
|
22
|
+
ToolEvent,
|
|
23
|
+
} from "../lib/types";
|
|
18
24
|
import { splitContentAndSources } from "../lib/utils";
|
|
19
25
|
|
|
20
26
|
const MAX_VISIBLE_TOOL_ITEMS = 8;
|
|
@@ -45,6 +51,11 @@ function MarkdownTable({
|
|
|
45
51
|
);
|
|
46
52
|
}
|
|
47
53
|
|
|
54
|
+
/** Remove parentheses that wrap a single markdown link so the badge is not shown inside (). */
|
|
55
|
+
function stripParenthesesAroundLinks(content: string): string {
|
|
56
|
+
return content.replace(/\s*\(\s*(\[[^\]]*\]\([^)]+\))\s*\)/g, " $1 ");
|
|
57
|
+
}
|
|
58
|
+
|
|
48
59
|
function normalizeMathDelimiters(content: string) {
|
|
49
60
|
// Convert TeX delimiters to remark-math compatible delimiters.
|
|
50
61
|
return content
|
|
@@ -640,6 +651,47 @@ function sourceBadgeLabel(url: string, title?: string): string {
|
|
|
640
651
|
}
|
|
641
652
|
}
|
|
642
653
|
|
|
654
|
+
function normUrl(u: string): string {
|
|
655
|
+
try {
|
|
656
|
+
const p = new URL(u);
|
|
657
|
+
return p.origin + p.pathname.replace(/\/$/, "");
|
|
658
|
+
} catch {
|
|
659
|
+
return u;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/** Extract markdown link URLs from content in order of first appearance (deduped by normalized URL). */
|
|
664
|
+
function extractCitationOrderFromContent(content: string): string[] {
|
|
665
|
+
const ordered: string[] = [];
|
|
666
|
+
const seen = new Set<string>();
|
|
667
|
+
const re = /\[([^\]]*)\]\(([^)]+)\)/g;
|
|
668
|
+
let m;
|
|
669
|
+
while ((m = re.exec(content)) !== null) {
|
|
670
|
+
const url = m[2].trim();
|
|
671
|
+
const n = normUrl(url);
|
|
672
|
+
if (!seen.has(n)) {
|
|
673
|
+
seen.add(n);
|
|
674
|
+
ordered.push(n);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
return ordered;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
/** Reorder sources so their order matches first appearance of each URL in the content. */
|
|
681
|
+
function reorderSourcesByCitationOrder(
|
|
682
|
+
content: string,
|
|
683
|
+
sources: ChatCitationSource[],
|
|
684
|
+
): ChatCitationSource[] {
|
|
685
|
+
const order = extractCitationOrderFromContent(content);
|
|
686
|
+
if (order.length === 0) return sources;
|
|
687
|
+
const orderIndex = (url: string) => {
|
|
688
|
+
const n = normUrl(url);
|
|
689
|
+
const i = order.indexOf(n);
|
|
690
|
+
return i >= 0 ? i : 1e9;
|
|
691
|
+
};
|
|
692
|
+
return [...sources].sort((a, b) => orderIndex(a.url) - orderIndex(b.url));
|
|
693
|
+
}
|
|
694
|
+
|
|
643
695
|
function getTimelineVisual(event: ToolEvent): {
|
|
644
696
|
chipLabel: string;
|
|
645
697
|
chipClassName: string;
|
|
@@ -726,9 +778,16 @@ function MessageCard({
|
|
|
726
778
|
() => splitContentAndSources(message.content || ""),
|
|
727
779
|
[message.content],
|
|
728
780
|
);
|
|
781
|
+
const sourcesOrderedByCitation = useMemo(
|
|
782
|
+
() => reorderSourcesByCitationOrder(messageTextContent, messageSources),
|
|
783
|
+
[messageTextContent, messageSources],
|
|
784
|
+
);
|
|
729
785
|
const isStreamingPlaceholder = isAssistant && isStreaming && !messageTextContent;
|
|
730
786
|
const assistantContent = useMemo(
|
|
731
|
-
() =>
|
|
787
|
+
() =>
|
|
788
|
+
stripParenthesesAroundLinks(
|
|
789
|
+
normalizeMathDelimiters(normalizeMarkdownStructure(messageTextContent)),
|
|
790
|
+
),
|
|
732
791
|
[messageTextContent],
|
|
733
792
|
);
|
|
734
793
|
const renderedAssistantContent = isStreaming
|
|
@@ -825,6 +884,30 @@ function MessageCard({
|
|
|
825
884
|
rehypePlugins={[rehypeKatex]}
|
|
826
885
|
components={{
|
|
827
886
|
table: ({ children }) => <MarkdownTable>{children}</MarkdownTable>,
|
|
887
|
+
a: ({ href, children }) => {
|
|
888
|
+
if (!href) return <a>{children}</a>;
|
|
889
|
+
const idx = sourcesOrderedByCitation.findIndex((s) =>
|
|
890
|
+
normUrl(s.url) === normUrl(href)
|
|
891
|
+
);
|
|
892
|
+
const matchedSource = idx >= 0 ? sourcesOrderedByCitation[idx] : null;
|
|
893
|
+
const rawLabel = sourceBadgeLabel(href, matchedSource?.title);
|
|
894
|
+
const label = rawLabel.replace(/^[\s(]+|[\s)]+$/g, "").trim() || rawLabel;
|
|
895
|
+
const index = idx >= 0 ? idx + 1 : 0;
|
|
896
|
+
return (
|
|
897
|
+
<a
|
|
898
|
+
className="source-badge message-content-link-badge"
|
|
899
|
+
href={href}
|
|
900
|
+
target="_blank"
|
|
901
|
+
rel="noreferrer noopener"
|
|
902
|
+
title={href}
|
|
903
|
+
>
|
|
904
|
+
{index > 0 ? (
|
|
905
|
+
<span className="source-badge-index">{index}</span>
|
|
906
|
+
) : null}
|
|
907
|
+
<span className="source-badge-title">{label}</span>
|
|
908
|
+
</a>
|
|
909
|
+
);
|
|
910
|
+
},
|
|
828
911
|
}}
|
|
829
912
|
>
|
|
830
913
|
{renderedAssistantContent}
|
|
@@ -834,9 +917,9 @@ function MessageCard({
|
|
|
834
917
|
<p>{messageTextContent}</p>
|
|
835
918
|
)}
|
|
836
919
|
|
|
837
|
-
{message.role === "assistant" && !assistantCollapsed &&
|
|
920
|
+
{message.role === "assistant" && !assistantCollapsed && sourcesOrderedByCitation.length > 0 ? (
|
|
838
921
|
<div className="source-badge-row" aria-label="Sources">
|
|
839
|
-
{
|
|
922
|
+
{sourcesOrderedByCitation.map((source, index) => (
|
|
840
923
|
<a
|
|
841
924
|
key={`${source.url}-${index}`}
|
|
842
925
|
className="source-badge"
|