@adecore/agents-react 0.37.2 → 0.37.3
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/dist/chat/chat-client.d.ts +1 -0
- package/dist/chat/chat-client.js +2 -1
- package/dist/chat/chat-references.d.ts +16 -1
- package/dist/chat/chat-references.js +29 -3
- package/dist/chat/drafts.d.ts +2 -0
- package/dist/chat/drafts.js +21 -5
- package/dist/chat/index.d.ts +1 -1
- package/dist/chat/mentions.d.ts +31 -14
- package/dist/chat/mentions.js +186 -41
- package/dist/chat/stash.d.ts +1 -0
- package/dist/chat/stash.js +4 -2
- package/dist/chat/ui/Composer.js +438 -429
- package/dist/chat/ui/ComposerInput.d.ts +1 -0
- package/dist/chat/ui/ComposerInput.js +2 -0
- package/dist/chat/ui/Markdown.d.ts +3 -1
- package/dist/chat/ui/Markdown.js +194 -120
- package/dist/chat/ui/MessageActions.js +70 -66
- package/dist/chat/ui/ReferenceChip.d.ts +15 -0
- package/dist/chat/ui/ReferenceChip.js +255 -0
- package/dist/chat/ui/Timeline.js +52 -29
- package/dist/chat/ui/TimelineMenu.js +84 -78
- package/dist/chat/ui/chip-clipboard.d.ts +9 -0
- package/dist/chat/ui/chip-clipboard.js +105 -0
- package/dist/chat/ui/chips.d.ts +5 -0
- package/dist/chat/ui/chips.js +15 -1
- package/dist/chat/ui/composer/ComposerMenu.js +140 -79
- package/dist/chat/ui/composer/DraftParts.d.ts +0 -4
- package/dist/chat/ui/composer/DraftParts.js +0 -41
- package/dist/chat/ui/composer/chip-paste.d.ts +15 -0
- package/dist/chat/ui/composer/chip-paste.js +104 -0
- package/dist/chat/ui/composer/chips.d.ts +5 -0
- package/dist/chat/ui/composer/chips.js +59 -9
- package/dist/chat/ui/composer/key-handler.js +9 -0
- package/dist/chat/ui/composer/use-composer-draft.d.ts +0 -1
- package/dist/chat/ui/composer/use-composer-draft.js +2 -10
- package/dist/chat/ui/composer/use-composer-menus.d.ts +12 -2
- package/dist/chat/ui/composer/use-composer-menus.js +192 -149
- package/dist/chat/ui/composer/use-composer-submit.js +17 -12
- package/dist/chat/ui/composer/use-draft-files.d.ts +7 -1
- package/dist/chat/ui/composer/use-draft-files.js +90 -58
- package/dist/chat/ui/composer/use-take-back.js +2 -1
- package/dist/chat/ui/index.d.ts +0 -1
- package/dist/chat/ui/index.js +0 -1
- package/dist/chat/ui/reference-icons.d.ts +3 -0
- package/dist/chat/ui/reference-icons.js +15 -0
- package/dist/chat/ui/reference-lookup.d.ts +7 -0
- package/dist/chat/ui/reference-lookup.js +6 -0
- package/dist/chat/ui/rehype-chips.d.ts +8 -1
- package/dist/chat/ui/rehype-chips.js +35 -11
- package/dist/chat/ui/rows/MessageRows.js +218 -158
- package/dist/chat/ui/thread-references.d.ts +4 -0
- package/dist/chat/ui/thread-references.js +78 -0
- package/dist/host-hooks.d.ts +2 -0
- package/dist/host-hooks.js +7 -0
- package/dist/host.d.ts +3 -0
- package/dist/locales/en/agent-chat.d.ts +20 -3
- package/dist/locales/en/agent-chat.js +24 -7
- package/dist/locales/nl/agent-chat.d.ts +20 -3
- package/dist/locales/nl/agent-chat.js +24 -7
- package/package.json +8 -8
- package/src/chat/chat-client.ts +4 -1
- package/src/chat/chat-references.ts +44 -3
- package/src/chat/drafts.ts +24 -6
- package/src/chat/index.ts +1 -1
- package/src/chat/mentions.ts +202 -36
- package/src/chat/stash.ts +6 -2
- package/src/chat/ui/Composer.tsx +14 -4
- package/src/chat/ui/ComposerInput.tsx +4 -0
- package/src/chat/ui/Markdown.tsx +57 -25
- package/src/chat/ui/MessageActions.tsx +5 -2
- package/src/chat/ui/ReferenceChip.tsx +74 -0
- package/src/chat/ui/Timeline.tsx +24 -2
- package/src/chat/ui/TimelineMenu.tsx +7 -3
- package/src/chat/ui/chip-clipboard.ts +115 -0
- package/src/chat/ui/chips.ts +20 -1
- package/src/chat/ui/composer/ComposerMenu.tsx +53 -37
- package/src/chat/ui/composer/DraftParts.tsx +0 -12
- package/src/chat/ui/composer/chip-paste.ts +122 -0
- package/src/chat/ui/composer/chips.ts +60 -10
- package/src/chat/ui/composer/key-handler.ts +9 -0
- package/src/chat/ui/composer/use-composer-draft.ts +1 -7
- package/src/chat/ui/composer/use-composer-menus.ts +68 -29
- package/src/chat/ui/composer/use-composer-submit.ts +13 -6
- package/src/chat/ui/composer/use-draft-files.ts +39 -5
- package/src/chat/ui/composer/use-take-back.ts +2 -1
- package/src/chat/ui/index.ts +0 -1
- package/src/chat/ui/reference-icons.ts +17 -0
- package/src/chat/ui/reference-lookup.ts +13 -0
- package/src/chat/ui/rehype-chips.ts +38 -13
- package/src/chat/ui/rows/MessageRows.tsx +88 -64
- package/src/chat/ui/thread-references.ts +47 -0
- package/src/host-hooks.ts +9 -0
- package/src/host.ts +6 -1
- package/src/locales/en/agent-chat.ts +24 -7
- package/src/locales/nl/agent-chat.ts +24 -7
- package/dist/chat/ui/ChatReferenceChip.d.ts +0 -4
- package/dist/chat/ui/ChatReferenceChip.js +0 -110
- package/src/chat/ui/ChatReferenceChip.tsx +0 -26
package/dist/chat/ui/Timeline.js
CHANGED
|
@@ -457,6 +457,8 @@ export function Timeline({
|
|
|
457
457
|
});
|
|
458
458
|
const [loadingEarlier, setLoadingEarlier] = useState(false);
|
|
459
459
|
const loadingEarlierRef = useRef(false);
|
|
460
|
+
const anchorRef = useRef(null);
|
|
461
|
+
const headRef = useRef(undefined);
|
|
460
462
|
const loadEarlier = useCallback(() => {
|
|
461
463
|
if (cursor === null || loadingEarlierRef.current) {
|
|
462
464
|
return;
|
|
@@ -559,7 +561,7 @@ export function Timeline({
|
|
|
559
561
|
const [, settleLayout] = useReducer(count => count + 1, 0);
|
|
560
562
|
// Changing this callback invalidates every cached measurement, including on a scroll.
|
|
561
563
|
const getItemKey = useCallback(index => rows[index].id, [rows]);
|
|
562
|
-
//
|
|
564
|
+
// Follow the browser's actual end below. Virtual end anchoring drifts when growth still fits without scrolling.
|
|
563
565
|
// oxlint-disable-next-line react/incompatible-library
|
|
564
566
|
const virtualizer = useVirtualizer({
|
|
565
567
|
count: loadingHistory ? 0 : rows.length,
|
|
@@ -567,7 +569,6 @@ export function Timeline({
|
|
|
567
569
|
estimateSize: () => ESTIMATED_ROW_PX,
|
|
568
570
|
getItemKey,
|
|
569
571
|
overscan: 8,
|
|
570
|
-
anchorTo: 'end',
|
|
571
572
|
// Include the end before the first pin, so its actual heights are known before revealing it.
|
|
572
573
|
rangeExtractor: range => {
|
|
573
574
|
const indexes = defaultRangeExtractor(range);
|
|
@@ -617,10 +618,32 @@ export function Timeline({
|
|
|
617
618
|
viewport: element_2.clientHeight
|
|
618
619
|
};
|
|
619
620
|
}, [rows.length, totalSize, composerHeight, frame.height]);
|
|
621
|
+
// Prepending history moves rows; keep the reader on the same row at the same height.
|
|
622
|
+
useLayoutEffect(() => {
|
|
623
|
+
const element_3 = scrollRef.current;
|
|
624
|
+
const head = order?.[0];
|
|
625
|
+
const moved = head !== headRef.current;
|
|
626
|
+
headRef.current = head;
|
|
627
|
+
if (element_3 === null) {
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
const anchor = anchorRef.current;
|
|
631
|
+
const index_2 = moved && anchor !== null && !followRef.current ? rows.findIndex(row_8 => row_8.id === anchor.id) : -1;
|
|
632
|
+
if (anchor !== null && index_2 !== -1) {
|
|
633
|
+
virtualizer.getTotalSize();
|
|
634
|
+
element_3.scrollTop = virtualizer.measurementsCache[index_2].start - anchor.offset;
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
const row_9 = virtualizer.getVirtualItemForOffset(element_3.scrollTop);
|
|
638
|
+
anchorRef.current = row_9 === undefined ? null : {
|
|
639
|
+
id: String(row_9.key),
|
|
640
|
+
offset: row_9.start - element_3.scrollTop
|
|
641
|
+
};
|
|
642
|
+
});
|
|
620
643
|
// Close to the top, the page before goes in; again after each page that left too little above the reader.
|
|
621
644
|
useEffect(() => {
|
|
622
|
-
const
|
|
623
|
-
if (
|
|
645
|
+
const element_4 = scrollRef.current;
|
|
646
|
+
if (element_4 !== null && wantsEarlier(element_4)) {
|
|
624
647
|
loadEarlier();
|
|
625
648
|
}
|
|
626
649
|
}, [rows, loadEarlier, frame.height]);
|
|
@@ -634,8 +657,8 @@ export function Timeline({
|
|
|
634
657
|
const measurements = virtualizer.measurementsCache;
|
|
635
658
|
const starts = {
|
|
636
659
|
length: ticks.length,
|
|
637
|
-
at:
|
|
638
|
-
const tick = ticks.at(
|
|
660
|
+
at: index_3 => {
|
|
661
|
+
const tick = ticks.at(index_3);
|
|
639
662
|
return tick === undefined ? undefined : measurements[tick.rowIndex]?.start ?? tick.rowIndex * ESTIMATED_ROW_PX;
|
|
640
663
|
}
|
|
641
664
|
};
|
|
@@ -662,7 +685,7 @@ export function Timeline({
|
|
|
662
685
|
starts,
|
|
663
686
|
inView
|
|
664
687
|
};
|
|
665
|
-
const followStrip =
|
|
688
|
+
const followStrip = element_5 => {
|
|
666
689
|
const {
|
|
667
690
|
starts: current_0,
|
|
668
691
|
inView: shown
|
|
@@ -672,7 +695,7 @@ export function Timeline({
|
|
|
672
695
|
}
|
|
673
696
|
const next = messagesInView({
|
|
674
697
|
starts: current_0,
|
|
675
|
-
scrollTop:
|
|
698
|
+
scrollTop: element_5.scrollTop,
|
|
676
699
|
visibleHeight: visibleHeightRef.current
|
|
677
700
|
});
|
|
678
701
|
if (next?.first !== shown.first || next?.last !== shown.last) {
|
|
@@ -697,11 +720,11 @@ export function Timeline({
|
|
|
697
720
|
openGroup: groups.add,
|
|
698
721
|
openSubagent: subagents.add,
|
|
699
722
|
// A row already on screen stays put; the find scrolls to the match itself once it is drawn.
|
|
700
|
-
scrollToRow:
|
|
723
|
+
scrollToRow: index_4 => {
|
|
701
724
|
const top_0 = scrollRef.current?.scrollTop ?? 0;
|
|
702
725
|
const bottom = top_0 + visibleHeightRef.current;
|
|
703
|
-
if (!virtualizer.getVirtualItems().some(virtualRow_0 => virtualRow_0.index ===
|
|
704
|
-
virtualizer.scrollToIndex(
|
|
726
|
+
if (!virtualizer.getVirtualItems().some(virtualRow_0 => virtualRow_0.index === index_4 && virtualRow_0.end > top_0 && virtualRow_0.start < bottom)) {
|
|
727
|
+
virtualizer.scrollToIndex(index_4, {
|
|
705
728
|
align: 'center'
|
|
706
729
|
});
|
|
707
730
|
}
|
|
@@ -719,8 +742,8 @@ export function Timeline({
|
|
|
719
742
|
}, [searching, loadEarlier]);
|
|
720
743
|
const found = useMemo(() => searching ? ticksWithHits(ticks, chatFind.hitRows) : [], [searching, ticks, chatFind.hitRows]);
|
|
721
744
|
const foundCurrent = chatFind.currentRow === null ? null : tickOfRow(ticks, chatFind.currentRow);
|
|
722
|
-
const jumpTo =
|
|
723
|
-
const tick_0 = ticks[
|
|
745
|
+
const jumpTo = index_5 => {
|
|
746
|
+
const tick_0 = ticks[index_5];
|
|
724
747
|
if (!tick_0) {
|
|
725
748
|
return;
|
|
726
749
|
}
|
|
@@ -763,10 +786,10 @@ export function Timeline({
|
|
|
763
786
|
}
|
|
764
787
|
return;
|
|
765
788
|
}
|
|
766
|
-
const
|
|
767
|
-
if (
|
|
789
|
+
const index_6 = rows.findIndex(row_10 => row_10.id === seeking.itemId);
|
|
790
|
+
if (index_6 !== -1) {
|
|
768
791
|
setSeeking(null);
|
|
769
|
-
showRowAtTop(virtualizer, followRef,
|
|
792
|
+
showRowAtTop(virtualizer, followRef, index_6);
|
|
770
793
|
if (seeking.options?.flash) {
|
|
771
794
|
useTimelineFlash.getState().flash(seeking.key, seeking.itemId, false);
|
|
772
795
|
}
|
|
@@ -837,9 +860,9 @@ export function Timeline({
|
|
|
837
860
|
turnId: turnId_0,
|
|
838
861
|
target: target_0
|
|
839
862
|
} = revealing;
|
|
840
|
-
const changes = target_0 === 'changes' ? rows.findIndex(
|
|
863
|
+
const changes = target_0 === 'changes' ? rows.findIndex(row_11 => row_11.kind === 'changed-files' && row_11.turnId === turnId_0) : -1;
|
|
841
864
|
const opening = order?.map(id_0 => items?.[id_0]).find(item_0 => item_0?.kind === 'user' && item_0.turnId === turnId_0);
|
|
842
|
-
const found_0 = changes !== -1 ? changes : rows.findIndex(
|
|
865
|
+
const found_0 = changes !== -1 ? changes : rows.findIndex(row_12 => row_12.id === opening?.id || row_12.id === `start-${turnId_0}` || row_12.id === `fold-${turnId_0}`);
|
|
843
866
|
if (found_0 !== -1) {
|
|
844
867
|
setRevealing(null);
|
|
845
868
|
showRowAtTop(virtualizer, followRef, found_0);
|
|
@@ -860,7 +883,7 @@ export function Timeline({
|
|
|
860
883
|
// The strip is memoized and draws on every scroll, so it gets one callback for its life.
|
|
861
884
|
const jumpRef = useRef(jumpTo);
|
|
862
885
|
jumpRef.current = jumpTo;
|
|
863
|
-
const pick = useCallback(
|
|
886
|
+
const pick = useCallback(index_7 => jumpRef.current(index_7), []);
|
|
864
887
|
// Read when the card asks rather than on every render, so the strip's memo holds while the thread streams.
|
|
865
888
|
const cardChat = useMemo(() => ({
|
|
866
889
|
chatId,
|
|
@@ -868,8 +891,8 @@ export function Timeline({
|
|
|
868
891
|
if (fork === null) {
|
|
869
892
|
return false;
|
|
870
893
|
}
|
|
871
|
-
const
|
|
872
|
-
return forkRefusal(
|
|
894
|
+
const row_13 = useChats.getState().byKey[keyOf(chatId)];
|
|
895
|
+
return forkRefusal(row_13?.info ?? null, row_13?.structure[turnId_2]) === null;
|
|
873
896
|
},
|
|
874
897
|
fork: turnId_3 => fork?.(chatId, turnId_3)
|
|
875
898
|
}), [keyOf, chatId, fork]);
|
|
@@ -878,11 +901,11 @@ export function Timeline({
|
|
|
878
901
|
if (!onMainAgent) {
|
|
879
902
|
return false;
|
|
880
903
|
}
|
|
881
|
-
const
|
|
882
|
-
if (
|
|
904
|
+
const index_8 = stepMessage(starts, i => ticks[i].kind === 'person', scrollTop, direction);
|
|
905
|
+
if (index_8 === null) {
|
|
883
906
|
return false;
|
|
884
907
|
}
|
|
885
|
-
jumpTo(
|
|
908
|
+
jumpTo(index_8);
|
|
886
909
|
return true;
|
|
887
910
|
};
|
|
888
911
|
useEffect(() => registerMessageStepper(keyOf(chatId), direction_0 => stepRef.current(direction_0)), [keyOf, chatId]);
|
|
@@ -993,22 +1016,22 @@ export function Timeline({
|
|
|
993
1016
|
children: /*#__PURE__*/_jsx(FindRevealContext.Provider, {
|
|
994
1017
|
value: chatFind.reveal,
|
|
995
1018
|
children: virtualRows.map(virtualRow_1 => {
|
|
996
|
-
const
|
|
1019
|
+
const row_14 = rows[virtualRow_1.index];
|
|
997
1020
|
return /*#__PURE__*/_jsx(ThreadRow, {
|
|
998
|
-
row:
|
|
1021
|
+
row: row_14,
|
|
999
1022
|
previous: virtualRow_1.index > 0 ? rows[virtualRow_1.index - 1] : null,
|
|
1000
1023
|
index: virtualRow_1.index,
|
|
1001
1024
|
top: virtualRow_1.start,
|
|
1002
1025
|
measure: virtualizer.measureElement,
|
|
1003
1026
|
chatId: chatId,
|
|
1004
|
-
flashNonce: flash !== null && flashedRow ===
|
|
1005
|
-
bookmark: marks.get(
|
|
1027
|
+
flashNonce: flash !== null && flashedRow === row_14.id ? flash.nonce : null,
|
|
1028
|
+
bookmark: marks.get(row_14.id) ?? null,
|
|
1006
1029
|
toggleGroup: groups.toggle,
|
|
1007
1030
|
toggleTurn: turns.toggle,
|
|
1008
1031
|
toggleSubagent: subagents.toggle,
|
|
1009
1032
|
openSubagent: openSubagent,
|
|
1010
1033
|
openConversation: openConversation
|
|
1011
|
-
},
|
|
1034
|
+
}, row_14.id);
|
|
1012
1035
|
})
|
|
1013
1036
|
})
|
|
1014
1037
|
})
|
|
@@ -9,12 +9,14 @@ import { markdownOf, messageTextOf } from '../logic/timeline-copy.js';
|
|
|
9
9
|
import { useChatHost } from '../../host.js';
|
|
10
10
|
import { useChatScope } from '../../scope.js';
|
|
11
11
|
import { useChatRow } from '../../state/chats.js';
|
|
12
|
+
import { copyWithChips, rowClipboardChips } from './chip-clipboard.js';
|
|
12
13
|
import { useFileLinkCwd, useFileLinkScopeId, useOpenFileLink } from './file-links.js';
|
|
14
|
+
import { useReferenceLookup } from './thread-references.js';
|
|
13
15
|
import { ContextMenu, copyText, DisabledReason, EDIT_SHORTCUTS, Icon, Kbd, selectAllWithin } from '@adecore/ui';
|
|
14
16
|
/* The menu behind a right-click in a thread, mostly for copying: a whole message, a code block or an answer's markdown. */
|
|
15
17
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
16
18
|
export function TimelineMenuPopup(t0) {
|
|
17
|
-
const $ = _c(
|
|
19
|
+
const $ = _c(72);
|
|
18
20
|
const {
|
|
19
21
|
target,
|
|
20
22
|
thread,
|
|
@@ -26,10 +28,11 @@ export function TimelineMenuPopup(t0) {
|
|
|
26
28
|
const {
|
|
27
29
|
t
|
|
28
30
|
} = useTranslation("agent-chat");
|
|
31
|
+
const row = target.row;
|
|
29
32
|
let t3;
|
|
30
|
-
if ($[0] !==
|
|
31
|
-
t3 =
|
|
32
|
-
$[0] =
|
|
33
|
+
if ($[0] !== row) {
|
|
34
|
+
t3 = row === null ? null : messageTextOf(row);
|
|
35
|
+
$[0] = row;
|
|
33
36
|
$[1] = t3;
|
|
34
37
|
} else {
|
|
35
38
|
t3 = $[1];
|
|
@@ -47,7 +50,7 @@ export function TimelineMenuPopup(t0) {
|
|
|
47
50
|
const turnId = t4;
|
|
48
51
|
let t5;
|
|
49
52
|
if ($[5] !== turnId) {
|
|
50
|
-
t5 =
|
|
53
|
+
t5 = row_0 => turnId === null ? null : forkRefusal(row_0?.info ?? null, row_0?.structure[turnId]);
|
|
51
54
|
$[5] = turnId;
|
|
52
55
|
$[6] = t5;
|
|
53
56
|
} else {
|
|
@@ -66,6 +69,7 @@ export function TimelineMenuPopup(t0) {
|
|
|
66
69
|
const markable = chatId !== null && (target.row?.kind === "user" || target.row?.kind === "assistant") ? target.row.id : null;
|
|
67
70
|
const cwd = useFileLinkCwd();
|
|
68
71
|
const scopeId = useFileLinkScopeId();
|
|
72
|
+
const lookup = useReferenceLookup(chatId ?? "");
|
|
69
73
|
const {
|
|
70
74
|
fork,
|
|
71
75
|
fileLinks
|
|
@@ -150,22 +154,24 @@ export function TimelineMenuPopup(t0) {
|
|
|
150
154
|
t13 = $[25];
|
|
151
155
|
}
|
|
152
156
|
let t14;
|
|
153
|
-
if ($[26] !==
|
|
154
|
-
t14 = message !== null && /*#__PURE__*/_jsxs(ContextMenu.Item, {
|
|
155
|
-
onClick: () =>
|
|
157
|
+
if ($[26] !== lookup || $[27] !== message || $[28] !== row || $[29] !== t) {
|
|
158
|
+
t14 = row !== null && message !== null && /*#__PURE__*/_jsxs(ContextMenu.Item, {
|
|
159
|
+
onClick: () => copyWithChips(rowClipboardChips(row, message, lookup)),
|
|
156
160
|
children: [/*#__PURE__*/_jsx(Icon, {
|
|
157
161
|
icon: MessageSquare,
|
|
158
162
|
size: 14
|
|
159
163
|
}), " ", t("timeline.menu.copyMessage")]
|
|
160
164
|
});
|
|
161
|
-
$[26] =
|
|
162
|
-
$[27] =
|
|
163
|
-
$[28] =
|
|
165
|
+
$[26] = lookup;
|
|
166
|
+
$[27] = message;
|
|
167
|
+
$[28] = row;
|
|
168
|
+
$[29] = t;
|
|
169
|
+
$[30] = t14;
|
|
164
170
|
} else {
|
|
165
|
-
t14 = $[
|
|
171
|
+
t14 = $[30];
|
|
166
172
|
}
|
|
167
173
|
let t15;
|
|
168
|
-
if ($[
|
|
174
|
+
if ($[31] !== t || $[32] !== target.code) {
|
|
169
175
|
t15 = target.code !== null && /*#__PURE__*/_jsxs(ContextMenu.Item, {
|
|
170
176
|
onClick: () => copyText(target.code ?? ""),
|
|
171
177
|
children: [/*#__PURE__*/_jsx(Icon, {
|
|
@@ -173,14 +179,14 @@ export function TimelineMenuPopup(t0) {
|
|
|
173
179
|
size: 14
|
|
174
180
|
}), " ", t("timeline.menu.copyCode")]
|
|
175
181
|
});
|
|
176
|
-
$[
|
|
177
|
-
$[
|
|
178
|
-
$[
|
|
182
|
+
$[31] = t;
|
|
183
|
+
$[32] = target.code;
|
|
184
|
+
$[33] = t15;
|
|
179
185
|
} else {
|
|
180
|
-
t15 = $[
|
|
186
|
+
t15 = $[33];
|
|
181
187
|
}
|
|
182
188
|
let t16;
|
|
183
|
-
if ($[
|
|
189
|
+
if ($[34] !== markdown || $[35] !== t) {
|
|
184
190
|
t16 = markdown !== null && /*#__PURE__*/_jsxs(ContextMenu.Item, {
|
|
185
191
|
onClick: () => copyText(markdown),
|
|
186
192
|
children: [/*#__PURE__*/_jsx(Icon, {
|
|
@@ -188,14 +194,14 @@ export function TimelineMenuPopup(t0) {
|
|
|
188
194
|
size: 14
|
|
189
195
|
}), " ", t("timeline.menu.copyMarkdown")]
|
|
190
196
|
});
|
|
191
|
-
$[
|
|
192
|
-
$[
|
|
193
|
-
$[
|
|
197
|
+
$[34] = markdown;
|
|
198
|
+
$[35] = t;
|
|
199
|
+
$[36] = t16;
|
|
194
200
|
} else {
|
|
195
|
-
t16 = $[
|
|
201
|
+
t16 = $[36];
|
|
196
202
|
}
|
|
197
203
|
let t17;
|
|
198
|
-
if ($[
|
|
204
|
+
if ($[37] !== chatId || $[38] !== fork || $[39] !== forkBlocked || $[40] !== t || $[41] !== turnId) {
|
|
199
205
|
t17 = chatId !== null && turnId !== null && fork !== null && /*#__PURE__*/_jsx(DisabledReason, {
|
|
200
206
|
reason: forkBlocked,
|
|
201
207
|
children: /*#__PURE__*/_jsxs(ContextMenu.Item, {
|
|
@@ -207,74 +213,74 @@ export function TimelineMenuPopup(t0) {
|
|
|
207
213
|
}), " ", t("timeline.menu.forkFromHere")]
|
|
208
214
|
})
|
|
209
215
|
});
|
|
210
|
-
$[
|
|
211
|
-
$[
|
|
212
|
-
$[
|
|
213
|
-
$[
|
|
214
|
-
$[
|
|
215
|
-
$[
|
|
216
|
+
$[37] = chatId;
|
|
217
|
+
$[38] = fork;
|
|
218
|
+
$[39] = forkBlocked;
|
|
219
|
+
$[40] = t;
|
|
220
|
+
$[41] = turnId;
|
|
221
|
+
$[42] = t17;
|
|
216
222
|
} else {
|
|
217
|
-
t17 = $[
|
|
223
|
+
t17 = $[42];
|
|
218
224
|
}
|
|
219
225
|
let t18;
|
|
220
|
-
if ($[
|
|
226
|
+
if ($[43] !== chatId || $[44] !== markable) {
|
|
221
227
|
t18 = chatId !== null && markable !== null && /*#__PURE__*/_jsx(BookmarkMenuItems, {
|
|
222
228
|
chatId: chatId,
|
|
223
229
|
itemId: markable
|
|
224
230
|
});
|
|
225
|
-
$[
|
|
226
|
-
$[
|
|
227
|
-
$[
|
|
231
|
+
$[43] = chatId;
|
|
232
|
+
$[44] = markable;
|
|
233
|
+
$[45] = t18;
|
|
228
234
|
} else {
|
|
229
|
-
t18 = $[
|
|
235
|
+
t18 = $[45];
|
|
230
236
|
}
|
|
231
237
|
let t19;
|
|
232
|
-
if ($[
|
|
238
|
+
if ($[46] === Symbol.for("react.memo_cache_sentinel")) {
|
|
233
239
|
t19 = /*#__PURE__*/_jsx(ContextMenu.Separator, {});
|
|
234
|
-
$[
|
|
240
|
+
$[46] = t19;
|
|
235
241
|
} else {
|
|
236
|
-
t19 = $[
|
|
242
|
+
t19 = $[46];
|
|
237
243
|
}
|
|
238
244
|
let t20;
|
|
239
|
-
if ($[
|
|
245
|
+
if ($[47] !== thread.current) {
|
|
240
246
|
t20 = () => selectAllWithin(thread.current);
|
|
241
|
-
$[
|
|
242
|
-
$[
|
|
247
|
+
$[47] = thread.current;
|
|
248
|
+
$[48] = t20;
|
|
243
249
|
} else {
|
|
244
|
-
t20 = $[
|
|
250
|
+
t20 = $[48];
|
|
245
251
|
}
|
|
246
252
|
let t21;
|
|
247
|
-
if ($[
|
|
253
|
+
if ($[49] === Symbol.for("react.memo_cache_sentinel")) {
|
|
248
254
|
t21 = /*#__PURE__*/_jsx(Icon, {
|
|
249
255
|
icon: Scan,
|
|
250
256
|
size: 14
|
|
251
257
|
});
|
|
252
|
-
$[
|
|
258
|
+
$[49] = t21;
|
|
253
259
|
} else {
|
|
254
|
-
t21 = $[
|
|
260
|
+
t21 = $[49];
|
|
255
261
|
}
|
|
256
262
|
let t22;
|
|
257
|
-
if ($[
|
|
263
|
+
if ($[50] !== t) {
|
|
258
264
|
t22 = t("common.action.selectAll");
|
|
259
|
-
$[
|
|
260
|
-
$[
|
|
265
|
+
$[50] = t;
|
|
266
|
+
$[51] = t22;
|
|
261
267
|
} else {
|
|
262
|
-
t22 = $[
|
|
268
|
+
t22 = $[51];
|
|
263
269
|
}
|
|
264
270
|
let t23;
|
|
265
|
-
if ($[
|
|
271
|
+
if ($[52] !== t20 || $[53] !== t22) {
|
|
266
272
|
t23 = /*#__PURE__*/_jsxs(ContextMenu.Item, {
|
|
267
273
|
onClick: t20,
|
|
268
274
|
children: [t21, " ", t22]
|
|
269
275
|
});
|
|
270
|
-
$[
|
|
271
|
-
$[
|
|
272
|
-
$[
|
|
276
|
+
$[52] = t20;
|
|
277
|
+
$[53] = t22;
|
|
278
|
+
$[54] = t23;
|
|
273
279
|
} else {
|
|
274
|
-
t23 = $[
|
|
280
|
+
t23 = $[54];
|
|
275
281
|
}
|
|
276
282
|
let t24;
|
|
277
|
-
if ($[
|
|
283
|
+
if ($[55] !== fileLinks || $[56] !== openInPreview || $[57] !== t || $[58] !== target.path) {
|
|
278
284
|
t24 = target.path !== null && fileLinks !== null && /*#__PURE__*/_jsxs(_Fragment, {
|
|
279
285
|
children: [/*#__PURE__*/_jsx(ContextMenu.Separator, {}), /*#__PURE__*/_jsxs(ContextMenu.Item, {
|
|
280
286
|
onClick: openInPreview,
|
|
@@ -284,41 +290,41 @@ export function TimelineMenuPopup(t0) {
|
|
|
284
290
|
}), " ", t("timeline.menu.openInPreview")]
|
|
285
291
|
})]
|
|
286
292
|
});
|
|
287
|
-
$[
|
|
288
|
-
$[
|
|
289
|
-
$[
|
|
290
|
-
$[
|
|
291
|
-
$[
|
|
293
|
+
$[55] = fileLinks;
|
|
294
|
+
$[56] = openInPreview;
|
|
295
|
+
$[57] = t;
|
|
296
|
+
$[58] = target.path;
|
|
297
|
+
$[59] = t24;
|
|
292
298
|
} else {
|
|
293
|
-
t24 = $[
|
|
299
|
+
t24 = $[59];
|
|
294
300
|
}
|
|
295
301
|
let t25;
|
|
296
|
-
if ($[
|
|
302
|
+
if ($[60] !== column) {
|
|
297
303
|
t25 = column && /*#__PURE__*/_jsxs(_Fragment, {
|
|
298
304
|
children: [/*#__PURE__*/_jsx(ContextMenu.Separator, {}), /*#__PURE__*/_jsx(ReadingWidthSubmenu, {})]
|
|
299
305
|
});
|
|
300
|
-
$[
|
|
301
|
-
$[
|
|
306
|
+
$[60] = column;
|
|
307
|
+
$[61] = t25;
|
|
302
308
|
} else {
|
|
303
|
-
t25 = $[
|
|
309
|
+
t25 = $[61];
|
|
304
310
|
}
|
|
305
311
|
let t26;
|
|
306
|
-
if ($[
|
|
312
|
+
if ($[62] !== t13 || $[63] !== t14 || $[64] !== t15 || $[65] !== t16 || $[66] !== t17 || $[67] !== t18 || $[68] !== t23 || $[69] !== t24 || $[70] !== t25) {
|
|
307
313
|
t26 = /*#__PURE__*/_jsxs(ContextMenu.Popup, {
|
|
308
314
|
children: [t13, t14, t15, t16, t17, t18, t19, t23, t24, t25]
|
|
309
315
|
});
|
|
310
|
-
$[
|
|
311
|
-
$[
|
|
312
|
-
$[
|
|
313
|
-
$[
|
|
314
|
-
$[
|
|
315
|
-
$[
|
|
316
|
-
$[
|
|
317
|
-
$[
|
|
318
|
-
$[
|
|
319
|
-
$[
|
|
320
|
-
} else {
|
|
321
|
-
t26 = $[
|
|
316
|
+
$[62] = t13;
|
|
317
|
+
$[63] = t14;
|
|
318
|
+
$[64] = t15;
|
|
319
|
+
$[65] = t16;
|
|
320
|
+
$[66] = t17;
|
|
321
|
+
$[67] = t18;
|
|
322
|
+
$[68] = t23;
|
|
323
|
+
$[69] = t24;
|
|
324
|
+
$[70] = t25;
|
|
325
|
+
$[71] = t26;
|
|
326
|
+
} else {
|
|
327
|
+
t26 = $[71];
|
|
322
328
|
}
|
|
323
329
|
return t26;
|
|
324
330
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ChatAssistantItem, ChatUserItem } from '@adecore/agent-contracts';
|
|
2
|
+
import { type ClipboardChips } from '../mentions.js';
|
|
3
|
+
import type { TimelineRow } from '../logic/timeline.js';
|
|
4
|
+
import type { ReferenceLookup } from './reference-lookup.js';
|
|
5
|
+
export declare function messageClipboardChips(item: ChatUserItem | ChatAssistantItem, text: string, lookup: Pick<ReferenceLookup, 'items' | 'referenced'>): ClipboardChips;
|
|
6
|
+
export declare function rowClipboardChips(row: TimelineRow, text: string, lookup: Pick<ReferenceLookup, 'items' | 'referenced'>): ClipboardChips;
|
|
7
|
+
export declare function selectionChips(selection: Selection): ClipboardChips;
|
|
8
|
+
export declare function copySelectionWithChips(event: ClipboardEvent): void;
|
|
9
|
+
export declare function copyWithChips(chips: ClipboardChips): void;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { copyText } from '@adecore/ui';
|
|
2
|
+
import { referenceTokens } from '../chat-references.js';
|
|
3
|
+
import { clipboardChipsOf, hasClipboardChips, tokenizeChips, tokenizeNamedReferences, writeClipboardChips } from '../mentions.js';
|
|
4
|
+
/* The chips a copy of a whole message carries: what the person picked, or what a reply names. */
|
|
5
|
+
export function messageClipboardChips(item, text, lookup) {
|
|
6
|
+
if (item.kind === 'assistant') {
|
|
7
|
+
return clipboardChipsOf(text, tokenizeNamedReferences(text, lookup.items, lookup.referenced));
|
|
8
|
+
}
|
|
9
|
+
const tokens = referenceTokens([...(item.chats ?? []), ...(item.references ?? [])], lookup.items);
|
|
10
|
+
return clipboardChipsOf(text, tokenizeChips(text, item.mentions ?? [], item.skills ?? [], tokens));
|
|
11
|
+
}
|
|
12
|
+
/* The chips a copy of a row of the thread carries; only a message has any. */
|
|
13
|
+
export function rowClipboardChips(row, text, lookup) {
|
|
14
|
+
if (row.kind === 'user' || row.kind === 'assistant') {
|
|
15
|
+
return messageClipboardChips(row.item, text, lookup);
|
|
16
|
+
}
|
|
17
|
+
return clipboardChipsOf(text, []);
|
|
18
|
+
}
|
|
19
|
+
/* What a chip the transcript drew stands for, read back from the attributes `TokenChip` writes. */
|
|
20
|
+
function segmentOf(chip) {
|
|
21
|
+
const value = chip.getAttribute('data-chip-value');
|
|
22
|
+
if (value === null) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
switch (chip.getAttribute('data-chip')) {
|
|
26
|
+
case 'mention':
|
|
27
|
+
return {
|
|
28
|
+
kind: 'mention',
|
|
29
|
+
path: value
|
|
30
|
+
};
|
|
31
|
+
case 'skill':
|
|
32
|
+
return {
|
|
33
|
+
kind: 'skill',
|
|
34
|
+
name: value
|
|
35
|
+
};
|
|
36
|
+
case 'reference':
|
|
37
|
+
{
|
|
38
|
+
const name = chip.getAttribute('data-chip-name');
|
|
39
|
+
return name === null || name === '' ? null : {
|
|
40
|
+
kind: 'reference',
|
|
41
|
+
id: value,
|
|
42
|
+
name
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
default:
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/* The chips a selection holds whole, beside the text it copies; a chip cut in half is only text. */
|
|
50
|
+
export function selectionChips(selection) {
|
|
51
|
+
const segments = [];
|
|
52
|
+
for (let i = 0; i < selection.rangeCount; i++) {
|
|
53
|
+
const container = selection.getRangeAt(i).commonAncestorContainer;
|
|
54
|
+
const root = container instanceof Element ? container : container.parentElement;
|
|
55
|
+
const own = root?.closest('[data-chip]');
|
|
56
|
+
const candidates = own ? [own] : [...(root?.querySelectorAll('[data-chip]') ?? [])];
|
|
57
|
+
for (const chip of candidates) {
|
|
58
|
+
const segment = selection.containsNode(chip, false) ? segmentOf(chip) : null;
|
|
59
|
+
if (segment !== null) {
|
|
60
|
+
segments.push(segment);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return clipboardChipsOf(selection.toString(), segments);
|
|
65
|
+
}
|
|
66
|
+
/* A copy of a selection in the thread, which carries the chips in it as well when it holds any. */
|
|
67
|
+
export function copySelectionWithChips(event) {
|
|
68
|
+
const selection = document.getSelection();
|
|
69
|
+
if (event.defaultPrevented || event.clipboardData === null || selection === null || selection.isCollapsed) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const chips = selectionChips(selection);
|
|
73
|
+
if (!hasClipboardChips(chips)) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
writeClipboardChips(event.clipboardData, chips);
|
|
77
|
+
event.preventDefault();
|
|
78
|
+
}
|
|
79
|
+
/*
|
|
80
|
+
* Copies a text and its chips. The asynchronous clipboard writes no type of an app's own that a paste
|
|
81
|
+
* can read, so the copy goes through a copy event, which only a page with the user's gesture gets.
|
|
82
|
+
*/
|
|
83
|
+
export function copyWithChips(chips) {
|
|
84
|
+
if (!hasClipboardChips(chips) || typeof document.execCommand !== 'function') {
|
|
85
|
+
void copyText(chips.text);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
let written = false;
|
|
89
|
+
const write = event => {
|
|
90
|
+
if (event.clipboardData !== null) {
|
|
91
|
+
writeClipboardChips(event.clipboardData, chips);
|
|
92
|
+
event.preventDefault();
|
|
93
|
+
written = true;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
document.addEventListener('copy', write, true);
|
|
97
|
+
try {
|
|
98
|
+
document.execCommand('copy');
|
|
99
|
+
} finally {
|
|
100
|
+
document.removeEventListener('copy', write, true);
|
|
101
|
+
}
|
|
102
|
+
if (!written) {
|
|
103
|
+
void copyText(chips.text);
|
|
104
|
+
}
|
|
105
|
+
}
|
package/dist/chat/ui/chips.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
export declare const MENTION_TONE = "bg-accent-soft text-accent shadow-[inset_0_0_0_1px_color-mix(in_srgb,var(--accent)_25%,transparent)]";
|
|
2
2
|
export declare const SKILL_TONE = "bg-skill-soft text-skill shadow-[inset_0_0_0_1px_color-mix(in_srgb,var(--skill)_25%,transparent)]";
|
|
3
|
+
export declare const REFERENCE_TONE = "bg-text/8 text-text shadow-[inset_0_0_0_1px_var(--border-strong)]";
|
|
4
|
+
export declare const REFERENCE_GONE_TONE = "bg-text/4 text-text-faint shadow-[inset_0_0_0_1px_var(--border)]";
|
|
3
5
|
export declare const CHIP_IN_EDITOR = "box-decoration-clone rounded-md px-1 py-0.5";
|
|
6
|
+
export declare const TOKEN_IN_MESSAGE = "box-decoration-clone rounded-md px-1.5 py-0.5 text-xs font-medium";
|
|
7
|
+
export declare const TOKEN_ICON = "mr-1 inline align-[calc(0.5cap-7px)]!";
|
|
8
|
+
export declare const TOKEN_ICON_IN_EDITOR = "mr-1 inline-block align-[calc(0.5cap-6px)]";
|
|
4
9
|
export declare const CHIP_IN_MESSAGE = "inline-flex h-5 max-w-full items-center gap-1 rounded-md px-1.5 align-[-3px] text-xs/none font-medium";
|
package/dist/chat/ui/chips.js
CHANGED
|
@@ -1,11 +1,25 @@
|
|
|
1
|
-
/* An `@file` or
|
|
1
|
+
/* An `@file`, `$skill` or `#reference` token drawn as a pill: the tone says which it is, the shape
|
|
2
2
|
says where it is drawn. */
|
|
3
3
|
export const MENTION_TONE = 'bg-accent-soft text-accent shadow-[inset_0_0_0_1px_color-mix(in_srgb,var(--accent)_25%,transparent)]';
|
|
4
4
|
/* A skill is the same token shape as a file mention, in its own hue. */
|
|
5
5
|
export const SKILL_TONE = 'bg-skill-soft text-skill shadow-[inset_0_0_0_1px_color-mix(in_srgb,var(--skill)_25%,transparent)]';
|
|
6
|
+
/* A `#` to a view, node or chat is neutral, so it never reads as a file or a skill. */
|
|
7
|
+
export const REFERENCE_TONE = 'bg-text/8 text-text shadow-[inset_0_0_0_1px_var(--border-strong)]';
|
|
8
|
+
/* A `#` whose view or node is no longer in the project. */
|
|
9
|
+
export const REFERENCE_GONE_TONE = 'bg-text/4 text-text-faint shadow-[inset_0_0_0_1px_var(--border)]';
|
|
6
10
|
/* The pill in the composer's editor, around the token's own characters: the editor places the caret
|
|
7
11
|
from what it drew, so padding is safe, and a chip that wraps keeps it on every fragment. */
|
|
8
12
|
export const CHIP_IN_EDITOR = 'box-decoration-clone rounded-md px-1 py-0.5';
|
|
13
|
+
/* A picked or named token in a message or a reply, a shade smaller than the text around it. Inline like
|
|
14
|
+
the editor's pill, so its name sits on the baseline of the sentence, and a selection copies it on the
|
|
15
|
+
same line as the words around it, where a flex box would put it on lines of its own. */
|
|
16
|
+
export const TOKEN_IN_MESSAGE = 'box-decoration-clone rounded-md px-1.5 py-0.5 text-xs font-medium';
|
|
17
|
+
/* The mark in place of the sigil, a 14px icon. Inline, since the base styles make an svg a block of its
|
|
18
|
+
own. Its middle goes on the middle of the name's capitals, where a mark reads as centered; the middle
|
|
19
|
+
of the x-height `Icon` aligns on sits low next to a capital. */
|
|
20
|
+
export const TOKEN_ICON = 'mr-1 inline align-[calc(0.5cap-7px)]!';
|
|
21
|
+
/* The same middle for the 12px mark the composer draws, in a box whose bottom edge is its baseline. */
|
|
22
|
+
export const TOKEN_ICON_IN_EDITOR = 'mr-1 inline-block align-[calc(0.5cap-6px)]';
|
|
9
23
|
/* The same pill in the transcript: a real inline box a shade smaller than the text around it, with
|
|
10
24
|
the icon in place of the sigil. */
|
|
11
25
|
export const CHIP_IN_MESSAGE = 'inline-flex h-5 max-w-full items-center gap-1 rounded-md px-1.5 align-[-3px] text-xs/none font-medium';
|