Skip to content

Commit 7952474

Browse files
committed
fix(copy-message): missing citations
1 parent 0cc77ce commit 7952474

File tree

5 files changed

+53
-29
lines changed

5 files changed

+53
-29
lines changed

src/hooks/useCopyPplxThread.ts

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ import { useQuery } from "@tanstack/react-query";
22
import { sendMessage } from "webext-bridge/content-script";
33

44
import { toast } from "@/components/ui/use-toast";
5+
import { threadMessageBlocksDomObserverStore } from "@/plugins/_core/dom-observers/thread/message-blocks/store";
56
import { ThreadMessageApiResponse } from "@/services/pplx-api/pplx-api.types";
67
import { pplxApiQueries } from "@/services/pplx-api/query-keys";
7-
import { INTERNAL_ATTRIBUTES, DOM_SELECTORS } from "@/utils/dom-selectors";
8+
import { DOM_SELECTORS } from "@/utils/dom-selectors";
89
import { errorWrapper } from "@/utils/error-wrapper";
910
import { ThreadExport } from "@/utils/thread-export";
1011
import { parseUrl } from "@/utils/utils";
@@ -91,22 +92,40 @@ async function copyMessageWithCitations({
9192
}: {
9293
messageBlockIndex: number;
9394
}) {
94-
const message = await sendMessage(
95+
const content = await sendMessage(
9596
"reactVdom:getMessageContent",
9697
{
9798
index: messageBlockIndex,
9899
},
99100
"window",
100101
);
101102

102-
if (message == null) {
103-
$(
104-
`[data-cplx-component="${INTERNAL_ATTRIBUTES.THREAD.MESSAGE.BLOCK}"][data-index="${messageBlockIndex}"] [data-cplx-component="${INTERNAL_ATTRIBUTES.THREAD.MESSAGE.TEXT_COL_CHILD.BOTTOM_BAR}"] ${DOM_SELECTORS.THREAD.MESSAGE.TEXT_COL_CHILD.BOTTOM_BAR_CHILD.COPY_BUTTON}`,
105-
).trigger("click");
103+
if (content == null) {
104+
const $bottomBar =
105+
threadMessageBlocksDomObserverStore.getState().messageBlocks?.[
106+
messageBlockIndex
107+
]?.nodes.$bottomBar;
108+
109+
if (!$bottomBar || !$bottomBar.length) return;
110+
111+
const $copyButton = $bottomBar.find(
112+
DOM_SELECTORS.THREAD.MESSAGE.TEXT_COL_CHILD.BOTTOM_BAR_CHILD.COPY_BUTTON,
113+
);
114+
115+
if (!$copyButton.length) return;
116+
117+
$copyButton.trigger("click");
118+
106119
return;
107120
}
108121

109-
navigator.clipboard.writeText(message);
122+
if (content.webResults && content.webResults.length) {
123+
navigator.clipboard.writeText(
124+
`${content.answer}\n\nCitations:\n${ThreadExport.formatWebResults(content.webResults)}`,
125+
);
126+
} else {
127+
navigator.clipboard.writeText(content.answer);
128+
}
110129
}
111130

112131
async function copyMessageWithoutCitations({

src/plugins/_core/react-vdom/listeners.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { onMessage } from "webext-bridge/window";
33
import { LanguageModelCode } from "@/data/plugins/query-box/language-model-selector/language-models.types";
44
import { INTERNAL_ATTRIBUTES, DOM_SELECTORS } from "@/utils/dom-selectors";
55
import { errorWrapper } from "@/utils/error-wrapper";
6+
import { PplxWebResult } from "@/utils/thread-export";
67
import { getCookie, getReactFiberKey } from "@/utils/utils";
78

89
export type ReactVdomEvents = {
@@ -12,7 +13,10 @@ export type ReactVdomEvents = {
1213
"reactVdom:getMessageDisplayModelCode": (params: {
1314
index: number;
1415
}) => string | null;
15-
"reactVdom:getMessageContent": (params: { index: number }) => string | null;
16+
"reactVdom:getMessageContent": (params: { index: number }) => {
17+
answer: string;
18+
webResults: PplxWebResult[] | undefined;
19+
} | null;
1620
"reactVdom:getCodeBlockContent": (params: {
1721
messageBlockIndex: number;
1822
codeBlockIndex: number;
@@ -94,21 +98,23 @@ export function setupReactVdomListeners() {
9498

9599
if (!$el.length) return null;
96100

97-
const [answer, error] = errorWrapper(() =>
101+
const [result, error] = errorWrapper(() =>
98102
findReactFiberNodeValue({
99103
fiberNode: ($el[0] as any)[getReactFiberKey($el[0])],
100104
condition: (node) =>
101-
node.memoizedProps.children[3].props.response.answer != null,
102-
select: (node) =>
103-
node.memoizedProps.children[3].props.response.answer as string,
105+
node.memoizedProps.children[3].props.response != null,
106+
select: (node) => ({
107+
answer: node.memoizedProps.children[3].props.response
108+
.answer as string,
109+
webResults: node.memoizedProps.children[3].props.response
110+
?.web_results as PplxWebResult[] | undefined,
111+
}),
104112
}),
105113
)();
106114

107115
if (error) console.warn("[VDOM Plugin] getMessageContent", error);
108116

109-
if (error || answer == null) return null;
110-
111-
return answer;
117+
return result;
112118
});
113119

114120
onMessage(

src/plugins/thread-better-message-toolbars/message-words-and-characters-count.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,19 +62,19 @@ csLoaderRegistry.register({
6262
return;
6363
}
6464

65-
const answer = await sendMessage(
65+
const content = await sendMessage(
6666
"reactVdom:getMessageContent",
6767
{ index },
6868
"window",
6969
);
7070

71-
if (answer == null) {
71+
if (content == null) {
7272
return;
7373
}
7474

75-
const answerWordsCount = answer.split(" ").length;
76-
const answerCharactersCount = answer.length;
77-
const answerTokensCount = Math.ceil(answer.length / 4);
75+
const answerWordsCount = content.answer.split(" ").length;
76+
const answerCharactersCount = content.answer.length;
77+
const answerTokensCount = Math.ceil(content.answer.length / 4);
7878

7979
const answerWordsAndCharactersCountContainer =
8080
createAnswerHeadingContainer(

src/utils/dom-selectors.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ export const DOM_SELECTORS = {
3232
VISUAL_COL: ".col-span-4",
3333
TEXT_COL_CHILD: {
3434
/** The query box */
35-
QUERY_WRAPPER:
36-
"div.mt-md.md\\:mt-lg.mb-3.md\\:mb-6:has(> .group\\/title)",
35+
QUERY_WRAPPER: `div.mt-md.md\\:mt-lg.mb-3.md\\:mb-6:has(> .group\\/title), div.mt-md.md\\:mt-lg.mb-3.md\\:mb-6:has(div[style*="height"])`,
3736
QUERY: ".group\\/query",
3837
QUERY_HOVER_CONTAINER: ".pointer-events-none.absolute.bottom-0.right-0",
3938
QUERY_HOVER_CONTAINER_CHILD: {
@@ -46,7 +45,7 @@ export const DOM_SELECTORS = {
4645
/** The sources heading */
4746
SOURCES: ".mb-lg.border-borderMain\\/50.ring-borderMain\\/50",
4847
/** The answer wrapper */
49-
ANSWER: ".relative.default.font-sans.text-base",
48+
ANSWER: ".mb-md > .relative.default.font-sans.text-base",
5049
/** The bottom toolbar of the message (share, rewrite, model name, etc.) */
5150
BOTTOM_BAR: ".mt-sm.flex.items-center.justify-between",
5251
BOTTOM_BAR_CHILD: {

src/utils/thread-export.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import { jsonUtils } from "@/utils/utils";
44

55
type ThreadAnswer = {
66
answer: string;
7-
web_results: WebResult[];
7+
web_results: PplxWebResult[];
88
};
99

10-
type WebResult = {
10+
export type PplxWebResult = {
1111
name: string;
1212
url: string;
1313
snippet: string;
@@ -33,10 +33,10 @@ export class ThreadExport {
3333

3434
private static extractWebResults(
3535
message: ThreadMessageApiResponse,
36-
): WebResult[] {
36+
): PplxWebResult[] {
3737
const text = jsonUtils.safeParse(message.text);
3838

39-
const webResults = text.web_results as WebResult[] | undefined;
39+
const webResults = text.web_results as PplxWebResult[] | undefined;
4040

4141
if (webResults != null) {
4242
return webResults;
@@ -54,7 +54,7 @@ export class ThreadExport {
5454
);
5555
}
5656

57-
private static formatWebResults(webResults: WebResult[]) {
57+
static formatWebResults(webResults: PplxWebResult[]) {
5858
return webResults
5959
.map(
6060
(webResult, index) =>
@@ -86,7 +86,7 @@ export class ThreadExport {
8686
.join(" \n");
8787
}
8888

89-
private static trimReferences(answer: string, webResults: WebResult[]) {
89+
private static trimReferences(answer: string, webResults: PplxWebResult[]) {
9090
webResults.forEach((_, index) => {
9191
const findText = `\\[${index + 1}\\]`;
9292
answer = answer.replace(new RegExp(findText, "g"), "");

0 commit comments

Comments
 (0)