Skip to content
2 changes: 1 addition & 1 deletion CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ summary, keywords, and connection metadata.
## Parsed Chunk Card

A Parsed Chunk Card renders one Parsed Chunk. It owns chunk source metadata,
content rendering, summaries, keywords, artifact references, and sanitized
content rendering, extracted entities, artifact references, and sanitized
table HTML for that card only.

## Chat Thread
Expand Down
43 changes: 18 additions & 25 deletions src/components/chunks-panel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ describe("ChunksPanel", () => {
expect(
await screen.findByRole("img", { name: "Page 4" }),
).toBeTruthy();
expect(screen.getByText("image/png")).toBeTruthy();
expect(screen.queryByText("Page image")).toBeNull();
expect(screen.queryByText("image/png")).toBeNull();
expect(screen.queryByText("Budget")).toBeNull();
expect(screen.queryByTestId("chunk-card-shell-table_1")).toBeNull();
expect(
Expand Down Expand Up @@ -885,11 +886,10 @@ describe("ChunksPanel", () => {
selectListView();

const openOriginalButton = screen.getByRole("button", {
name: "Open original file",
name: "Original",
});

expect(openOriginalButton.className).toContain("font-normal");
expect(openOriginalButton.className).not.toContain("font-semibold");
expect(openOriginalButton).toBeTruthy();

await user.click(openOriginalButton);

Expand Down Expand Up @@ -941,7 +941,7 @@ describe("ChunksPanel", () => {
);
});

it("opens the original PDF preview at the clicked chunk page", async () => {
it("opens the original PDF preview from the Original tab", async () => {
mockVisibleVirtualViewport();
const user = userEvent.setup();
vi.stubGlobal(
Expand Down Expand Up @@ -973,14 +973,10 @@ describe("ChunksPanel", () => {
);
selectListView();

await user.click(
screen.getByRole("button", { name: "Open page 2 in original file" }),
);
await user.click(screen.getByRole("button", { name: "Original" }));

expect(screen.getByRole("heading", { name: "Original File" })).toBeTruthy();
expect(screen.getByTestId("source-original-preview").getAttribute(
"data-target-page",
)).toBe("2");
expect(screen.getByTestId("source-original-preview")).toBeTruthy();
});

it("keeps the original PDF preview mounted when switching back to parsed chunks", async () => {
Expand Down Expand Up @@ -1015,9 +1011,7 @@ describe("ChunksPanel", () => {
);
selectListView();

await user.click(
screen.getByRole("button", { name: "Open page 2 in original file" }),
);
await user.click(screen.getByRole("button", { name: "Original" }));

const mountedOriginalPreview = screen.getByTestId("source-original-preview");

Expand Down Expand Up @@ -1194,16 +1188,17 @@ describe("ChunksPanel", () => {
expect(financialSourcePanel.textContent).not.toContain(
"TSLA-Q4-2025-Update.pdf",
);
expect(storageSourcePanel.textContent).toContain("OPERATIONAL SUMMARY");
expect(storageSourcePanel.textContent).toContain(
"OPERATIONAL SUMMARY / Energy generation and storage",
"Energy generation and storage",
);
expect(storageSourcePanel.textContent).not.toContain("Default_Root");
expect(storageSourcePanel.textContent).not.toContain(
"TSLA-Q4-2025-Update.pdf",
);
});

it("renders text chunks with structured source, summary, content, and keyword sections", () => {
it("renders text chunks with source, unlabeled content, and entity tags", () => {
mockVisibleVirtualViewport();

render(
Expand Down Expand Up @@ -1231,20 +1226,18 @@ describe("ChunksPanel", () => {
expect(
screen.getByTestId("chunk-source-panel-text_1").textContent,
).not.toContain("TSLA-Q4-2025-UPDATE.PDF");
expect(screen.getByTestId("chunk-summary-panel-text_1").textContent).toContain(
"Tesla continues to use its North American footprint",
);
expect(screen.queryByTestId("chunk-summary-panel-text_1")).toBeNull();
expect(screen.queryByText("Summary")).toBeNull();
expect(screen.queryByText("Content")).toBeNull();
expect(screen.queryByText("Keywords")).toBeNull();
expect(screen.getByTestId("chunk-content-panel-text_1").textContent).toContain(
"Tesla is adding Supercharging and AI training capacity.",
);
expect(screen.getByTestId("chunk-keywords-panel-text_1").textContent).toContain(
expect(screen.getByTestId("chunk-entities-panel-text_1").textContent).toContain(
"AI training capacity",
);
expect(
screen.getByTestId("chunk-keywords-panel-text_1").className,
).toContain("bg-emerald-50/70");
expect(screen.getByText("Robotaxi").className).toContain("bg-emerald-100/90");
expect(screen.getByText("Robotaxi").className).toContain("text-emerald-800");
expect(screen.getByText("Robotaxi").className).toContain("text-primary");
expect(screen.getByText("Robotaxi").className).toContain("border-primary/35");
});

it("allows horizontal scrolling for wide chunk content", async () => {
Expand Down
28 changes: 0 additions & 28 deletions src/components/chunks-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ import type { ChatImageHighlightBox } from "@/domains/chat/types";
import { chunksPanelState } from "@/components/chunks-panel-state";
import { MAX_UPLOAD_MB } from "@/domains/sources/validation";
import { useSourceOriginalPreviewWarmup } from "@/components/source-original-preview-warmup";
import { sourceOriginalPreviewModel } from "@/components/source-original-preview-model";
import type { ParsedChunkView } from "@/domains/chunks/types";
import type {
SourceOriginalFileView,
Expand Down Expand Up @@ -122,12 +121,6 @@ export function ChunksPanel({
);
const effectiveVisibleView = isPageAssetSource ? "parsed" : undefined;
const originalPreviewCacheKey = selectedSourceFile?.url ?? null;
const isOriginalPreviewAvailable =
!isPageAssetSource &&
sourceOriginalPreviewModel.canPreviewOriginalFile(
selectedSource,
selectedSourceFile,
);
const [mountedOriginalPreviewKey, setMountedOriginalPreviewKey] = useState<
string | null
>(null);
Expand All @@ -142,11 +135,9 @@ export function ChunksPanel({
useState<number>(sectionTreeDefaultZoomPercent);
const {
activeFocusedChunkId,
handleChunkSelected: selectChunk,
handleOriginalViewSelected: selectOriginalView,
handleParsedViewSelected,
handleViewportScroll,
hasOriginalFile,
hasOriginalView,
measureVirtualChunkElement,
originalTargetPageNumber,
Expand Down Expand Up @@ -183,13 +174,6 @@ export function ChunksPanel({
}
}, [originalPreviewCacheKey]);

const handleChunkSelected = useCallback(
(chunk: ParsedChunkView): void => {
rememberOriginalPreview();
selectChunk(chunk);
},
[rememberOriginalPreview, selectChunk],
);
const handleOriginalViewSelected = useCallback((): void => {
rememberOriginalPreview();
selectOriginalView();
Expand Down Expand Up @@ -438,13 +422,7 @@ export function ChunksPanel({
focusedPageNumber={focusedPageNumber}
focusedPageRequestId={focusedPageRequestId}
highlightRegions={focusedHighlightRegions}
isOriginalPreviewAvailable={isOriginalPreviewAvailable}
measureElement={measureVirtualChunkElement}
onChunkClick={
hasOriginalFile && !isPageAssetSource
? handleChunkSelected
: undefined
}
onReferenceClick={requestChunkFocus}
selectedSourceFile={selectedSourceFile}
/>
Expand Down Expand Up @@ -1223,9 +1201,7 @@ function VirtualChunkRow({
focusedPageNumber,
focusedPageRequestId,
highlightRegions,
isOriginalPreviewAvailable,
measureElement,
onChunkClick,
onReferenceClick,
selectedSourceFile,
}: {
Expand All @@ -1236,9 +1212,7 @@ function VirtualChunkRow({
focusedPageNumber: number | null;
focusedPageRequestId: number;
highlightRegions: readonly ChatImageHighlightBox[];
isOriginalPreviewAvailable: boolean;
measureElement: (node: HTMLDivElement | null) => void;
onChunkClick?: (chunk: ParsedChunkView) => void;
onReferenceClick: (chunkId: string) => void;
selectedSourceFile: SourceOriginalFileView | null;
}): ReactNode {
Expand Down Expand Up @@ -1268,8 +1242,6 @@ function VirtualChunkRow({
focusedPageNumber={focusedPageNumber}
focusedPageRequestId={focusedPageRequestId}
highlightRegions={highlightRegions}
isOriginalPreviewAvailable={isOriginalPreviewAvailable}
onChunkClick={onChunkClick}
onReferenceClick={onReferenceClick}
sourceOriginalFile={selectedSourceFile}
/>
Expand Down
52 changes: 47 additions & 5 deletions src/components/parsed-chunk-card-model.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ describe("parsedChunkCardModel", () => {

expect(metadata).toEqual({
pageLabel: "Pages 2, 4, 8-9",
sectionLabel: "FINANCIAL SUMMARY",
sectionLabel: "Root / FINANCIAL SUMMARY",
sectionSegments: ["Root", "FINANCIAL SUMMARY"],
typeLabel: "Text",
leadLabel: "Pages 2, 4, 8-9",
})
})

Expand All @@ -33,18 +35,21 @@ describe("parsedChunkCardModel", () => {

expect(metadata).toEqual({
pageLabel: "Pages 4-6",
sectionLabel: "pages/4-6",
sectionLabel: "Root / pages/4-6",
sectionSegments: ["Root", "pages/4-6"],
typeLabel: "Page",
leadLabel: "Pages 4-6",
})
})

it("uses Page N and the parse path for page-asset cards", () => {
it("uses Page N and the hierarchical section path for page-asset cards", () => {
const metadata = parsedChunkCardModel.getSourceMetadata(
makeChunk({
type: "page",
pageNums: [4],
filePath: "pages/page-000004.png",
sectionPath: "Page 4",
sectionPath:
"Default_Root/Micron Q1-26 Earnings Deck_R.pdf-->Safe harbor statement",
pageAssets: [
{
pageNumber: 4,
Expand All @@ -57,11 +62,48 @@ describe("parsedChunkCardModel", () => {

expect(metadata).toEqual({
pageLabel: "Page 4",
sectionLabel: "pages/page-000004.png",
sectionLabel: "Root / Safe harbor statement",
sectionSegments: ["Root", "Safe harbor statement"],
typeLabel: "Page",
leadLabel: "Page 4",
})
})

it("extracts unique entity tags from typed Knowhere entities", () => {
const tags = parsedChunkCardModel.getEntityTags(
makeChunk({
keywords: ["Robotaxi"],
entities: [
{ text: "Securities and Exchange Commission", type: "organization" },
{ text: "Form 10-K", type: "document" },
{ text: "Form 10-K", type: "document" },
{ text: " ", type: "organization" },
],
}),
)

expect(tags).toEqual([
{
text: "Securities and Exchange Commission",
type: "organization",
},
{ text: "Form 10-K", type: "document" },
])
})

it("falls back to keyword texts as entity tags when entities are missing", () => {
const tags = parsedChunkCardModel.getEntityTags(
makeChunk({
keywords: ["Robotaxi", "Supercharging", "Robotaxi", " "],
}),
)

expect(tags).toEqual([
{ text: "Robotaxi", type: null },
{ text: "Supercharging", type: null },
])
})

it("splits text content into text and reference parts with display-ready labels", () => {
const parts = parsedChunkCardModel.getTextContentParts(
makeChunk({
Expand Down
Loading
Loading