1import type { AssistantCitation } from "@t3tools/contracts";
2import {
3 formatAssistantCitationHref,
4 parseAssistantCitationHref,
5} from "@t3tools/shared/assistantCitations";
6import * as Base64Url from "effect/encoding/Base64Url";
7import * as Result from "effect/Result";
8
9import { randomUUID } from "./utils";
10
11declare module "@tanstack/react-router" {
12 interface HistoryState {
13 assistantCitationActivation?: string;
14 }
15}
16
17const CITATION_HASH_PREFIX = "assistant-citation=";
18
19/** Base64url keeps router hash normalization from decoding quote whitespace or source IDs. */
20export function assistantCitationHash(citation: AssistantCitation) {
21 return `${CITATION_HASH_PREFIX}${Base64Url.encode(formatAssistantCitationHref(citation))}`;
22}
23
24export function assistantCitationFromLocation(href: string) {
25 const hashIndex = href.indexOf("#");
26 if (hashIndex === -1) return null;
27 const hash = href.slice(hashIndex + 1);
28 if (!hash.startsWith(CITATION_HASH_PREFIX) || hash.length > 140_000) return null;
29 try {
30 return parseAssistantCitationHref(
31 Result.getOrThrow(Base64Url.decodeString(hash.slice(CITATION_HASH_PREFIX.length))),
32 );
33 } catch {
34 return null;
35 }
36}
37
38/** A fresh activation lets the same link reveal its source again after dismissal. */
39export function assistantCitationNavigation(citation: AssistantCitation) {
40 return {
41 to: "/$environmentId/$threadId" as const,
42 params: { environmentId: citation.environmentId, threadId: citation.threadId },
43 hash: assistantCitationHash(citation),
44 resetScroll: false,
45 state: { assistantCitationActivation: randomUUID() },
46 };
47}