Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
39dd86a
minor ui change
keerthnn Apr 14, 2026
0b7bf24
deduplication
shubh1118 Apr 14, 2026
2298024
Update routeTree.gen.ts
keerthnn Apr 15, 2026
3c76cdb
deduplication
shubh1118 Apr 14, 2026
5fe9440
Update routeTree.gen.ts
keerthnn Apr 15, 2026
36fa232
Merge branch 'firstPhase' of https://github.com/KWARC/GloX into first…
keerthnn Apr 15, 2026
2cb8ac7
showing images along with extracted text
keerthnn Apr 15, 2026
6838806
done
shubh1118 Apr 15, 2026
6e19f6b
done
shubh1118 Apr 15, 2026
09baf44
Merge branch 'firstPhase' of https://github.com/KWARC/GloX into first…
shubh1118 Apr 16, 2026
0d61631
fixed error
keerthnn Apr 16, 2026
4352712
formatted
keerthnn Apr 16, 2026
008ba0a
formatted
keerthnn Apr 16, 2026
5ec1da4
formatted
keerthnn Apr 16, 2026
312890e
done
shubh1118 Apr 16, 2026
90c3530
Merge branch 'firstPhase' of https://github.com/KWARC/GloX into first…
shubh1118 Apr 16, 2026
e75f57a
done
shubh1118 Apr 16, 2026
218ee05
migration
keerthnn Apr 17, 2026
0b38525
same file can be uploaded by different user
keerthnn Apr 17, 2026
64017e8
done
shubh1118 Apr 17, 2026
144ea9f
user can access another user pdf
keerthnn Apr 17, 2026
d1e15ea
git reset
keerthnn Apr 17, 2026
37d404f
Merge remote-tracking branch 'origin/nextPhase' into firstPhase
keerthnn Apr 17, 2026
b8125f2
Merge remote-tracking branch 'origin/nextPhase' into firstPhase
keerthnn Apr 17, 2026
224445c
removed files
keerthnn Apr 17, 2026
221cfa3
deleted uploaded files
keerthnn Apr 17, 2026
19793a8
Merge remote-tracking branch 'origin/nextPhase' into firstPhase
keerthnn Apr 17, 2026
0d7ee52
Update PdfToImages.ts
keerthnn Apr 17, 2026
5d243d2
Merge remote-tracking branch 'origin/nextPhase' into firstPhase
keerthnn Apr 17, 2026
07a32ee
Update searchSymbols.ts
keerthnn Apr 17, 2026
17f5d3f
Merge remote-tracking branch 'origin/nextPhase' into firstPhase
keerthnn Apr 17, 2026
baa4cd3
now api is working
shubh1118 Apr 17, 2026
83e32ad
formatted
keerthnn Apr 18, 2026
bd19b67
added confirming not duplicate option
keerthnn Apr 18, 2026
4356436
added deduplication link in header
keerthnn Apr 20, 2026
3c2aeec
using extracted list instead of ftml preview
keerthnn Apr 20, 2026
8ae1da0
added border for images in document pages
keerthnn Apr 20, 2026
7a27f8e
adjusted image height
keerthnn Apr 20, 2026
81f4c0a
refactored code
keerthnn Apr 20, 2026
3cf6900
minor changes
keerthnn Apr 20, 2026
46af10c
Update PdfToImages.ts
keerthnn Apr 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
240 changes: 240 additions & 0 deletions src/components/Duplicate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
import { queryClient } from "@/queryClient";
import { extractSemanticIndex } from "@/server/ftml/semanticIndex";
import { useSymbolSearch } from "@/server/useSymbolSearch";
import { getDefinitionBySymbol } from "@/serverFns/symbol.server";
import {
updateDefinitionAst,
UpdateDefinitionAstResult,
} from "@/serverFns/updateDefinition.server";
import { assertFtmlStatement } from "@/types/ftml.types";
import { OnReplaceNode } from "@/types/Semantic.types";
import {
Box,
Button,
Group,
Loader,
Paper,
Popover,
Stack,
Text,
} from "@mantine/core";
import { useQuery } from "@tanstack/react-query";
import { useMemo, useState } from "react";
import { FtmlPreview } from "./FtmlPreview";
import { RenderSymbolicUri } from "./RenderUri";
import { SymbolPropagationDialog } from "./SymbolPropagationDialog";

type DefinitionType = {
Comment thread
keerthnn marked this conversation as resolved.
Resolved
id: string;
statement: unknown;
};

type DefiniendumType = {
uri: string;
text: string;
symdecl: boolean;
};

type MathHubResultType = {
uri: string;
source: "MATHHUB";
};

const handleReplaceNode: OnReplaceNode = async (
definitionId,
target,
payload,
): Promise<UpdateDefinitionAstResult> => {
const result = await updateDefinitionAst({
data: {
definitionId,
operation: {
kind: "replaceSemantic",
target,
payload,
},
},
});

await queryClient.invalidateQueries({
queryKey: ["dedup-symbols"],
});

return result;
};

type MathHubItemProps = {
r: MathHubResultType;
definition: DefinitionType | null | undefined;
selectedDefiniendum: DefiniendumType | null;
setPendingPropagation: (data: {
localSymbolUri: string;
mathHubUri: string;
primaryDefinitionId: string;
}) => void;
};

function MathHubItem({
r,
definition,
selectedDefiniendum,
setPendingPropagation,
}: MathHubItemProps) {
const [opened, setOpened] = useState(false);

return (
<Popover opened={opened} position="right" withArrow width={350}>
<Popover.Target>
<Paper
p="xs"
withBorder
style={{ cursor: "pointer" }}
onMouseEnter={() => setOpened(true)}
onMouseLeave={() => setOpened(false)}
>
<Group justify="space-between">
<Box style={{ flex: 1, minWidth: 0 }}>
<RenderSymbolicUri uri={r.uri} />
</Box>

<Button
size="xs"
onClick={(e) => {
e.stopPropagation();

if (!definition?.id || !selectedDefiniendum?.uri) return;

setPendingPropagation({
localSymbolUri: selectedDefiniendum.uri,
mathHubUri: r.uri,
primaryDefinitionId: definition.id,
});
}}
>
Use this
</Button>
</Group>
</Paper>
</Popover.Target>

<Popover.Dropdown>
<Box h={150}>
<iframe
src={r.uri.replace("http:", "https:")}
style={{ width: "100%", height: "100%", border: "none" }}
/>
</Box>
</Popover.Dropdown>
</Popover>
);
}

export function Duplicate({ symbolName }: { symbolName: string }) {
const [pendingPropagation, setPendingPropagation] = useState<{
localSymbolUri: string;
mathHubUri: string;
primaryDefinitionId: string;
} | null>(null);

const { data: definition, isLoading } = useQuery<DefinitionType | null>({
queryKey: ["definition-by-symbol", symbolName],
queryFn: () => getDefinitionBySymbol({ data: symbolName }),
});

const parsedStatement = useMemo(() => {
if (!definition?.statement) return null;
try {
return assertFtmlStatement(definition.statement);
} catch {
return null;
}
}, [definition]);

const selectedDefiniendum = useMemo<DefiniendumType | null>(() => {
if (!parsedStatement || !definition) return null;

const { definienda } = extractSemanticIndex(parsedStatement, {
id: definition.id,
statement: parsedStatement,
} as never);

return (
definienda.find((d) => d.text === symbolName) || definienda[0] || null
);
}, [parsedStatement, definition, symbolName]);

const { results, isLoading: isSearching } = useSymbolSearch(
symbolName,
!!symbolName,
);

if (!isLoading && !definition) return null;

const mathHubResults: MathHubResultType[] = results
.filter(
(r): r is MathHubResultType =>
r.source === "MATHHUB" && typeof r.uri === "string",
)
.slice(0, 2);

return (
<>
<Paper withBorder p="lg" mb="md">
<Group align="flex-start" justify="space-between">
<Box w="40%">
<Text fw={700}>{symbolName}</Text>

{isLoading && <Loader size="xs" mt="sm" />}

{parsedStatement && definition?.id && (
<Paper mt="md" p="md" withBorder bg="blue.0">
<Box h={140}>
<FtmlPreview
ftmlAst={parsedStatement}
docId={definition.id}
/>
</Box>
</Paper>
)}
</Box>

<Stack w="55%">
{isSearching && <Loader size="xs" />}

{mathHubResults.length > 0
? mathHubResults.map((r) => (
<MathHubItem
key={r.uri}
r={r}
definition={definition}
selectedDefiniendum={selectedDefiniendum}
setPendingPropagation={setPendingPropagation}
/>
))
: !isSearching && (
<Text size="xs" c="dimmed">
No MathHub results found
</Text>
)}

<Button size="xs" variant="light" color="gray">
NOT A DUPLICATE
</Button>
</Stack>
</Group>
</Paper>

{pendingPropagation && (
<SymbolPropagationDialog
opened={true}
localSymbolUri={pendingPropagation.localSymbolUri}
mathHubUri={pendingPropagation.mathHubUri}
primaryDefinitionId={pendingPropagation.primaryDefinitionId}
onReplaceNode={handleReplaceNode}
onDone={() => setPendingPropagation(null)}
onSkip={() => setPendingPropagation(null)}
/>
)}
</>
);
}
Loading
Loading