Skip to content

UN-4506 [DEV] Show text and images in the v1 DocumentViewer, with a switcher slot - #2409

Open
muhammad-ali-e wants to merge 24 commits into
UN-4505-v1-pdf-toolbarfrom
UN-4506-v1-text-image-views
Open

muhammad-ali-e wants to merge 24 commits into
UN-4505-v1-pdf-toolbarfrom
UN-4506-v1-text-image-views

Conversation

@muhammad-ali-e

Copy link
Copy Markdown
Contributor

What

New and changed files in components/patterns/document-viewer/:

  • TextView.tsx: text with line highlights, used for Raw Text (V21), HITL text and LLMWhisperer results.
  • ImageView.tsx: an image shown as a one-page document.
  • DocumentViewer now accepts a DocumentSource: a PDF, an image or text.
  • ViewerToolbar gains a leading slot for the document switcher (C10).

Part of the v1 DocumentViewer (P1-T21, UN-4390).

Why

The consumer pages show more than PDFs:

  • Prompt Studio shows extracted text.
  • Agentic Studio shows raw text.
  • HITL shows text and highlights.
  • The LLMWhisperer Playground shows images.
  • Prompt Studio and Agentic Studio switch between documents.

One viewer handles all of these, so each page doesn't need its own.

How

  • Sources. DocumentSource is a union of three shapes:

    • PdfSource (URL or bytes)
    • {kind: "image", url, alt?}
    • {kind: "text", text}

    usePdfDocument only runs for PDFs.

  • TextView:

    • Shows one <li> per line, with a line number.
    • Uses whitespace-pre-wrap, so LLMWhisperer's column layout survives.
    • Uses the mono type role and Geist Mono, which the README reserves for model output.
    • Highlights whole lines ({id, line, tone?}, 0-based, matching LLMWhisperer's line_metadata keys) in the default, active and low-confidence tones.
    • With onTextHighlightClick, each highlighted line is a button.
    • scrollToHighlight centres the line.
    • Blank text shows the empty state.
  • ImageView:

    • Treats the image as a single page.
    • Its natural size is reported in points, so fit width / fit page and −/+ work exactly as for a PDF page, with zoom 1 = natural size.
    • Page-0 highlights draw over it.
    • A failed load shows the failed state, and Retry remounts the <img>.
    • The image size is stored with its URL, so a new image never reuses the previous one's size.
  • Toolbar:

    • The switcher slot sits at the toolbar's leading edge and shows in every state, including loading and failed, so you can always switch away from a broken document.
    • Paging only shows for PDFs. Zoom shows for PDFs and images.
    • Text gets a toolbar only when there's a switcher.
  • Keyboard: + and − do nothing in text.

  • Not checked against Figma: I couldn't open frame C10 from here. The slot is placed by the plan's description, so please compare it with C10 before this merges. The switcher itself is page-owned (P5-T04); this PR only reserves its place.

Can this PR break any existing features. If yes, please list possible items. If no, please explain why. (PS: Admins do not merge the PR without this section filled)

  • No. This is new code under src/v1/ and nothing imports it yet. The only change for existing callers is that source now also accepts image and text sources; PDF sources work as before.

Database Migrations

  • None

Env Config

  • None

Relevant Docs

  • P1-T21 implementation plan. Figma frames C10 (switcher), V21 (raw text) and LLMW P07 (confidence).

Related Issues or PRs

Dependencies Versions

  • None

Notes on Testing

  • bun run typecheck:v1, lint:v1 and test:v1 pass (137 unit tests, 56 boundary fixtures). biome ci src/ reports no errors.
  • TextView:
    • every line is shown, including blank ones, with its number
    • highlight tones and the active highlight
    • only highlighted lines are clickable
    • highlights are centred exactly on request
    • blank text shows the empty state
  • ImageView:
    • loading state, then size in points, then CSS size at 1× and 0.5×
    • highlights draw over the image
    • after a failure, Retry loads the image again
  • DocumentViewer:
    • text shows the switcher with no paging or zoom
    • the switcher stays during loading and after a failure
    • an image fits at 50% and steps to 75%, with no paging
    • a null source shows the empty state

Screenshots

  • None yet. The gallery comes in UN-4507.

Checklist

I have read and understood the Contribution Guidelines.


Generated by Claude Code

muhammad-ali-e and others added 2 commits October 29, 2026 08:07
…switcher slot

- DocumentViewer takes any DocumentSource: a PDF (URL or bytes), an
  image ({kind: "image", url}) or text ({kind: "text", text}).
- TextView shows text line by line with line numbers, keeping the
  layout's spacing, and highlights whole lines (0-based, as LLMWhisperer
  numbers them) in default, active and low-confidence tones; highlighted
  lines report clicks and can be scrolled to.
- ImageView shows an image as a one-page document: the toolbar's zoom
  and fits apply (zoom 1 is its natural size), page highlights draw over
  it, and a failed load offers Retry.
- The toolbar gains a leading slot for the document switcher, shown in
  every state so a failed document can be switched away from; paging
  shows only for PDFs and zoom only for PDFs and images.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R7trgcAzu6jxDnit3eeozm

Copy link
Copy Markdown
Contributor Author

pre-commit.ci - pr is red because of the base branch, not this PR.

The failing hook is check-shebang-scripts-are-executable, on frontend/scripts/check-v1-styles.mjs. That file has a #! line but no executable bit. It comes from the tokens PR #2399 (UN-4299-design-tokens-themes); this PR doesn't touch it. The hook doesn't fail on #2399 itself only because pre-commit.ci doesn't run on that PR. Running pre-commit run --from-ref $(git merge-base origin/main HEAD) locally reproduces it, and this PR's own files pass every hook.

There's no fix to port yet. The fix is a one-line change on #2399: git add --chmod=+x frontend/scripts/check-v1-styles.mjs (or drop the shebang). Once #2399 has it, this check should go green here without any change to this PR.


Generated by Claude Code

muhammad-ali-e and others added 6 commits November 2, 2026 06:28
# Conflicts:
#	frontend/src/v1/components/patterns/document-viewer/DocumentViewer.test.tsx
#	frontend/src/v1/components/patterns/document-viewer/ViewerToolbar.tsx
# Conflicts:
#	frontend/src/v1/components/patterns/document-viewer/ViewerToolbar.tsx
…ages

- Paging keys on text and images keep the browser's own scrolling; they
  used to be swallowed and jump to the top. Text ignores zoom keys.
- Scroll requests wait until an image has loaded, not just until its view
  has mounted, so an early highlight scroll lands once the image is sized.
- Returning to an image shown earlier waits for its reload instead of
  reusing the old size.
- The image view draws only page 0's highlights.
- Text rows are memoised, so moving the active highlight re-renders only
  the lines involved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R7trgcAzu6jxDnit3eeozm
…r source

Brings in the layout-based current page and the source-bound scroll
requests from UN-4505, and extends them to text and images: a waiting
scroll is dropped when another text or image is shown, including one
parked on an empty text. A new text gets a fresh view at the top, as a
new PDF or image already did. readyRef is set at commit, not in render.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R7trgcAzu6jxDnit3eeozm
… and images

Waiting scroll requests now carry the source they were made for, with a
key per kind (the text itself for text, the URL for an image), in place of
the passive clear effect, which dropped requests made at commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R7trgcAzu6jxDnit3eeozm
@muhammad-ali-e
muhammad-ali-e marked this pull request as ready for review November 2, 2026 07:00
@greptile-apps

greptile-apps Bot commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

via Greptile

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no new actionable issues were established.

Summary

Adds text and image views to DocumentViewer, plus a leading slot for a document switcher.

  • Text documents show numbered lines with clickable highlights.
  • Image documents appear as single pages with zoom and highlights.
  • The viewer picks content and controls to match each document source.

Reviews (8) · Last reviewed commit: "Merge branch 'UN-4505-v1-pdf-toolbar' in..." · Reviewed by Greptile

muhammad-ali-e and others added 2 commits November 2, 2026 08:08
Each row sized its own number column, so line 10 started further right than
line 9 and the text lost its columns. The gutter is now as wide as the
longest line number, shared through a custom property. Raised by Greptile
on #2409.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R7trgcAzu6jxDnit3eeozm
greptile-apps[bot]
greptile-apps Bot previously approved these changes Nov 2, 2026
Only a box highlight moves the image view across, as on a PDF page; a line
spans the image and keeps the reader's horizontal scroll.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R7trgcAzu6jxDnit3eeozm
@greptile-apps
greptile-apps Bot dismissed their stale review November 2, 2026 08:32

Dismissed because a newer commit was pushed; Greptile will re-review the current head.

greptile-apps[bot]
greptile-apps Bot previously approved these changes Nov 2, 2026

@praveen-formido praveen-formido left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Text and image views plus the switcher slot. Two items I would fix first: a text content change is treated as a new document, so a live-edited text (the HITL case) remounts, scrolls to the top and re-centres the highlight on every keystroke; and zoom keys pressed while an image is still loading switch to custom mode, so the image never fits once it arrives. The rest are smaller.

One more that sits on a line outside this diff: in scrollTo (DocumentViewer.tsx:247) a highlight on a page other than 0 of an image source never resolves, because ImageView only has one page, so scrollToHighlight returns false and the request stays in pendingRef forever. Dropping the request when the source is an image and highlight.page !== 0 (or clamping to page 0) clears it.

muhammad-ali-e and others added 2 commits November 3, 2026 10:11
# Conflicts:
#	frontend/src/v1/components/patterns/document-viewer/DocumentViewer.tsx
…one source key, empty text

- A text source can carry an id; the view keeps its place while the id
  stays the same, so a live-edited text no longer remounts per keystroke.
- One sourceKey drives every per-source reset (image size, page index)
  and keys the text and image views, replacing three separate mechanisms.
- Zoom keys are ignored until an image has a size, so it still opens in
  its fit mode.
- A scroll to a highlight on another page of an image is dropped instead
  of waiting for ever.
- ImageView resets when its URL changes, reports a highlight scroll as not
  yet possible until it has loaded, and keeps the centre spot when zooming.
- The image region is named Image viewer, so it no longer repeats the
  image's alt text.
- Blank text says no text was found rather than no document.
- Text lines use content-visibility, so the browser skips layout and paint
  for lines off screen while every line stays in the DOM.
- One NO_HIGHLIGHTS constant and one page frame class are shared.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R7trgcAzu6jxDnit3eeozm
@greptile-apps
greptile-apps Bot dismissed their stale review November 3, 2026 10:19

Dismissed because a newer commit was pushed; Greptile will re-review the current head.

Copy link
Copy Markdown
Contributor Author

Thanks @praveen-formido. On the review note about scrollTo with an image source: fixed in c51fbef. A highlight request for an image is dropped when that highlight is on a page other than 0, so it no longer waits in the pending slot for ever (and can't fire later if the highlight list changes). Test: "drops a scroll to a highlight on another page of an image instead of keeping it".


Generated by Claude Code

# Conflicts:
#	frontend/src/v1/components/patterns/document-viewer/DocumentViewer.test.tsx
#	frontend/src/v1/components/patterns/document-viewer/DocumentViewer.tsx
greptile-apps[bot]
greptile-apps Bot previously approved these changes Nov 3, 2026
@greptile-apps
greptile-apps Bot dismissed their stale review November 3, 2026 10:22

Dismissed because a newer commit was pushed; Greptile will re-review the current head.

… the first image zoom on the view

- A text kept by its id changes in place, so a scroll waiting on it is
  retried when the text changes, not only when the highlights do.
- ImageView reads its anchor from the view once the image is first laid
  out, instead of starting from the image's centre.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R7trgcAzu6jxDnit3eeozm
@sonarqubecloud

sonarqubecloud Bot commented Nov 3, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants