feat(frontend): update View PDF links to open in-browser viewer (sub-phase 2.5)

ResponsePanel and ChunkList View PDF links now open /pdf-viewer page instead of raw PDF download.
Update ChunkList test mock from getChunkPdfUrl to getPdfViewerUrl.
Add DOMMatrix polyfill to test setup for react-pdf/jsdom compatibility.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
Woody 2026-04-24 17:09:50 +08:00
parent 67e411cdca
commit a3028df4e1
4 changed files with 25 additions and 7 deletions

View File

@ -1,7 +1,7 @@
import React from 'react'
import { Trash2 } from 'lucide-react'
import type { ChunkInfo } from '../types'
import { getChunkPdfUrl } from '../lib/api'
import { getPdfViewerUrl } from '../lib/api'
interface ChunkListProps {
chunks: ChunkInfo[]
@ -67,7 +67,7 @@ export const ChunkList: React.FC<ChunkListProps> = ({
</div>
{chunk.chunk_file_path && (
<a
href={getChunkPdfUrl(chunk.chunk_file_path)}
href={getPdfViewerUrl(chunk.chunk_file_path, chunk.page_number ?? undefined)}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center mt-1 text-xs text-blue-600 hover:text-blue-800 hover:underline"

View File

@ -2,7 +2,7 @@ import React, { useState } from 'react'
import { MessageSquare, AlertCircle, Copy, ChevronDown, ChevronRight } from 'lucide-react'
import ReactMarkdown from 'react-markdown'
import type { SourceMetadata } from '../types'
import { getChunkPdfUrl } from '../lib/api'
import { getPdfViewerUrl } from '../lib/api'
interface ResponsePanelProps {
answer: string | null
@ -148,7 +148,7 @@ export const ResponsePanel: React.FC<ResponsePanelProps> = ({
<div className="text-xs text-gray-400">Chunk {source.chunk_index}</div>
{source.chunk_file_path && (
<a
href={getChunkPdfUrl(source.chunk_file_path)}
href={getPdfViewerUrl(source.chunk_file_path, source.page_number ?? undefined, source.filename)}
target="_blank"
rel="noopener noreferrer"
className="text-xs text-blue-600 hover:text-blue-800 hover:underline"

View File

@ -4,7 +4,10 @@ import { ChunkList } from '../../components/ChunkList'
import type { ChunkInfo } from '../../types'
vi.mock('../../lib/api', () => ({
getChunkPdfUrl: (path: string) => `http://localhost:8000/api/v1/chunks/${path}/pdf`,
getPdfViewerUrl: (filePath: string, page?: number) => {
const base = `/pdf-viewer?url=http://localhost:8000/api/v1/chunks/${filePath}/pdf`
return page !== undefined ? `${base}&page=${page}` : base
},
}))
const mockChunks: ChunkInfo[] = [
@ -62,7 +65,7 @@ describe('ChunkList', () => {
const links = screen.getAllByTestId('view-chunk-pdf-link')
expect(links).toHaveLength(2)
expect(links[0]).toHaveAttribute('href', 'http://localhost:8000/api/v1/chunks/test_page_1.pdf/pdf')
expect(links[0]).toHaveAttribute('href', expect.stringContaining('/pdf-viewer?url='))
expect(links[0]).toHaveAttribute('target', '_blank')
})

View File

@ -7,6 +7,21 @@ global.ResizeObserver = class ResizeObserver {
}
global.DOMRect = class DOMRect {
x = 0; y = 0; width = 0; height = 0; top = 0; right = 0; bottom = 0; left = 0
x: number; y: number; width: number; height: number; top: number; right: number; bottom: number; left: number
constructor(x = 0, y = 0, width = 0, height = 0) {
this.x = x; this.y = y; this.width = width; this.height = height
this.top = y; this.right = x + width; this.bottom = y + height; this.left = x
}
static fromRect() { return new DOMRect() }
toJSON() { return { x: this.x, y: this.y, width: this.width, height: this.height, top: this.top, right: this.right, bottom: this.bottom, left: this.left } }
}
global.DOMMatrix = class DOMMatrix {
a = 1; b = 0; c = 0; d = 1; e = 0; f = 0
is2D = true
isIdentity = true
static fromMatrix() { return new DOMMatrix() }
multiply() { return new DOMMatrix() }
translate() { return new DOMMatrix() }
scale() { return new DOMMatrix() }
}