Skip to content

Commit 00f28aa

Browse files
committed
feat(history): use localstorage for query local result
1 parent 420aa69 commit 00f28aa

File tree

6 files changed

+96
-14
lines changed

6 files changed

+96
-14
lines changed

web/src/app/components/history.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"use client";
2+
import { historyQueryKey } from "@/app/utils/local-storage";
3+
import { LocalHistory } from "@/app/interfaces/history";
4+
import { Answer } from "@/app/components/answer";
5+
import { Sources } from "@/app/components/sources";
6+
import { Relates } from "@/app/components/relates";
7+
import { Title } from "@/app/components/title";
8+
import { Fragment } from "react";
9+
10+
export const HistoryResult = () => {
11+
const history = window.localStorage.getItem(historyQueryKey);
12+
if (!history) return null;
13+
let historyRecord: LocalHistory[];
14+
try {
15+
historyRecord = JSON.parse(history);
16+
} catch {
17+
historyRecord = [];
18+
}
19+
return historyRecord.map(
20+
({ query, rid, sources, markdown, relates, timestamp }) => {
21+
return (
22+
<Fragment key={`${rid}-${timestamp}`}>
23+
<div className={"mt-6 border-t pt-4"}>
24+
<Title query={query} />
25+
</div>
26+
<div className="flex flex-col gap-8">
27+
<Answer markdown={markdown} sources={sources}></Answer>
28+
<Sources sources={sources}></Sources>
29+
<Relates relates={relates}></Relates>
30+
</div>
31+
</Fragment>
32+
);
33+
},
34+
);
35+
};

web/src/app/components/result.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,33 @@ import { Relates } from "@/app/components/relates";
44
import { Sources } from "@/app/components/sources";
55
import { Relate } from "@/app/interfaces/relate";
66
import { Source } from "@/app/interfaces/source";
7+
import { LocalHistory } from "@/app/interfaces/history";
78
import { parseStreaming } from "@/app/utils/parse-streaming";
89
import { Annoyed } from "lucide-react";
9-
import { FC, useEffect, useState } from "react";
10+
import { FC, useCallback, useEffect, useState } from "react";
11+
import { historyQueryKey } from "@/app/utils/local-storage";
1012

1113
export const Result: FC<{ query: string; rid: string }> = ({ query, rid }) => {
1214
const [sources, setSources] = useState<Source[]>([]);
1315
const [markdown, setMarkdown] = useState<string>("");
1416
const [relates, setRelates] = useState<Relate[] | null>(null);
1517
const [error, setError] = useState<number | null>(null);
18+
const handleFinish = useCallback(
19+
(result: LocalHistory) => {
20+
const localHistory = window.localStorage.getItem(historyQueryKey);
21+
let history: LocalHistory[];
22+
try {
23+
history = JSON.parse(localHistory || "[]");
24+
} catch {
25+
history = [];
26+
}
27+
window.localStorage.setItem(
28+
historyQueryKey,
29+
JSON.stringify([result, ...history]),
30+
);
31+
},
32+
[rid, query],
33+
);
1634
useEffect(() => {
1735
const controller = new AbortController();
1836
void parseStreaming(
@@ -22,6 +40,7 @@ export const Result: FC<{ query: string; rid: string }> = ({ query, rid }) => {
2240
setSources,
2341
setMarkdown,
2442
setRelates,
43+
handleFinish,
2544
setError,
2645
);
2746
return () => {

web/src/app/interfaces/history.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { Relate } from "@/app/interfaces/relate";
2+
import { Source } from "@/app/interfaces/source";
3+
4+
export interface LocalHistory {
5+
markdown: string;
6+
relates: Relate[];
7+
sources: Source[];
8+
rid: string;
9+
query: string;
10+
timestamp: number;
11+
}

web/src/app/search/page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Result } from "@/app/components/result";
33
import { Search } from "@/app/components/search";
44
import { Title } from "@/app/components/title";
55
import { useSearchParams } from "next/navigation";
6+
import { HistoryResult } from "@/app/components/history";
67
export default function SearchPage() {
78
const searchParams = useSearchParams();
89
const query = decodeURIComponent(searchParams.get("q") || "");
@@ -14,6 +15,7 @@ export default function SearchPage() {
1415
<div className="px-4 md:px-8 pt-6 pb-24 rounded-2xl ring-8 ring-zinc-300/20 border border-zinc-200 h-full overflow-auto">
1516
<Title query={query}></Title>
1617
<Result key={rid} query={query} rid={rid}></Result>
18+
<HistoryResult />
1719
</div>
1820
<div className="h-80 pointer-events-none w-full rounded-b-2xl backdrop-filter absolute bottom-0 bg-gradient-to-b from-transparent to-white [mask-image:linear-gradient(to_top,white,transparent)]"></div>
1921
<div className="absolute z-10 flex items-center justify-center bottom-6 px-4 md:px-8 w-full">

web/src/app/utils/local-storage.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const historyQueryKey = "lepton_previous_query";

web/src/app/utils/parse-streaming.ts

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Relate } from "@/app/interfaces/relate";
22
import { Source } from "@/app/interfaces/source";
33
import { fetchStream } from "@/app/utils/fetch-stream";
4+
import { LocalHistory } from "@/app/interfaces/history";
45

56
const LLM_SPLIT = "__LLM_RESPONSE__";
67
const RELATED_SPLIT = "__RELATED_QUESTIONS__";
@@ -12,6 +13,7 @@ export const parseStreaming = async (
1213
onSources: (value: Source[]) => void,
1314
onMarkdown: (value: string) => void,
1415
onRelates: (value: Relate[]) => void,
16+
onFinish: (result: LocalHistory) => void,
1517
onError?: (status: number) => void,
1618
) => {
1719
const decoder = new TextDecoder();
@@ -30,18 +32,19 @@ export const parseStreaming = async (
3032
search_uuid,
3133
}),
3234
});
35+
let finalRelates: Relate[] = [];
36+
let finalMarkdown: string = "";
37+
let finalSources: Source[] = [];
3338
if (response.status !== 200) {
3439
onError?.(response.status);
3540
return;
3641
}
3742
const markdownParse = (text: string) => {
38-
onMarkdown(
39-
text
40-
.replace(/\[\[([cC])itation/g, "[citation")
41-
.replace(/[cC]itation:(\d+)]]/g, "citation:$1]")
42-
.replace(/\[\[([cC]itation:\d+)]](?!])/g, `[$1]`)
43-
.replace(/\[[cC]itation:(\d+)]/g, "[citation]($1)"),
44-
);
43+
return text
44+
.replace(/\[\[([cC])itation/g, "[citation")
45+
.replace(/[cC]itation:(\d+)]]/g, "citation:$1]")
46+
.replace(/\[\[([cC]itation:\d+)]](?!])/g, `[$1]`)
47+
.replace(/\[[cC]itation:(\d+)]/g, "[citation]($1)");
4548
};
4649
fetchStream(
4750
response,
@@ -52,27 +55,38 @@ export const parseStreaming = async (
5255
const [sources, rest] = chunks.split(LLM_SPLIT);
5356
if (!sourcesEmitted) {
5457
try {
55-
onSources(JSON.parse(sources));
58+
finalSources = JSON.parse(sources);
5659
} catch (e) {
57-
onSources([]);
60+
finalSources = [];
5861
}
62+
onSources(finalSources);
5963
}
6064
sourcesEmitted = true;
6165
if (rest.includes(RELATED_SPLIT)) {
6266
const [md] = rest.split(RELATED_SPLIT);
63-
markdownParse(md);
67+
finalMarkdown = markdownParse(md);
6468
} else {
65-
markdownParse(rest);
69+
finalMarkdown = markdownParse(rest);
6670
}
71+
onMarkdown(finalMarkdown);
6772
}
6873
},
6974
() => {
7075
const [_, relates] = chunks.split(RELATED_SPLIT);
7176
try {
72-
onRelates(JSON.parse(relates));
77+
finalRelates = JSON.parse(relates);
7378
} catch (e) {
74-
onRelates([]);
79+
finalRelates = [];
7580
}
81+
onRelates(finalRelates);
82+
onFinish({
83+
markdown: finalMarkdown,
84+
sources: finalSources,
85+
relates: finalRelates,
86+
rid: search_uuid,
87+
query,
88+
timestamp: new Date().valueOf(),
89+
});
7690
},
7791
);
7892
};

0 commit comments

Comments
 (0)