Skip to content

feat(render): --extract-text flag for hybrid output (tiles + text.md) - #106

Merged
yichuan-w merged 4 commits into
StarTrail-org:mainfrom
aafaq-rashid-comprinno:feat/output-hybrid
Aug 29, 2026
Merged

feat(render): --extract-text flag for hybrid output (tiles + text.md)#106
yichuan-w merged 4 commits into
StarTrail-org:mainfrom
aafaq-rashid-comprinno:feat/output-hybrid

Conversation

@aafaq-rashid-comprinno

Copy link
Copy Markdown
Contributor

Addresses #93.

Problem

Passing full screenshot tiles to LLMs costs many vision tokens. For text-heavy pages, extracting the text alongside the visual tiles lets users choose the cheaper representation when no charts/tables are present.

Solution

New --extract-text flag for pixelshot that extracts document.body.innerText via CDP after the page is rendered and saves it as text.md in the tile directory.

pixelshot https://comprinno.net/ -o ./tiles --extract-text --wait-network-idle

Output:

tiles/comprinno.net.png.tiles/
├── tile_0000.jpg   # visual screenshot
├── text.md         # extracted page text
└── tiles.json      # manifest

Design decisions

  • Zero-cost extraction: The DOM is already loaded for screenshotting — innerText is a single CDP call
  • Best-effort: Text extraction failures don't prevent tile capture
  • Opt-in: Default behavior unchanged (--extract-text is off by default)
  • No new dependencies: Uses existing CDP websocket connection

Tested

  • comprinno.net: text.md captures full page content (headings, paragraphs, nav)
  • All 23 existing tests pass
  • Lint clean

Adds an opt-in text extraction mode to pixelshot that saves a text.md
file alongside the screenshot tiles. Uses CDP Runtime.evaluate to grab
document.body.innerText after the page is rendered — zero-cost since
the DOM is already open.

Usage:
  pixelshot https://example.com -o ./tiles --extract-text

Output:
  tiles/example.com.png.tiles/
  ├── tile_0000.jpg   # visual tile (existing)
  ├── text.md         # page text as markdown (new)
  └── tiles.json      # manifest

This enables hybrid workflows where LLMs receive text for text-heavy
paragraphs (cheap tokens) and images only for charts/tables/diagrams
(expensive vision tokens).

Addresses StarTrail-org#93.
@vercel

vercel Bot commented Jun 24, 2026

Copy link
Copy Markdown

@aafaq-rashid-comprinno is attempting to deploy a commit to the andylizf's projects Team on Vercel.

A member of the Team first needs to authorize it.

aafaq-rashid-comprinno and others added 3 commits June 24, 2026 22:26
Verifies text.md is created with page content when extract_text=True,
and not created when the flag is off (default).
…ilures

render_urls picks the turbo backend whenever the installed Chrome is
turbo-capable, which is what `pixelshot install-chrome` produces. fast_cdp
has no text extraction, so --extract-text produced tiles and no text.md
with nothing logged. Added extract_text to the capability guard that
already sends wait_network_idle down the standard path, and logged the
downgrade the same way.

Also replaced the bare `except: pass` around the innerText probe with a
warning — still best-effort, but no longer silent.
@yichuan-w

Copy link
Copy Markdown
Contributor

Merging this. I pushed one commit to your branch rather than sending it back — the flag was silently a no-op for most users.

The gap: render_urls() auto-selects the turbo backend whenever the installed Chrome is turbo-capable (use_turbo = is_turbo_capable(chrome) if turbo is None else turbo), and that's exactly what pixelshot install-chrome installs. fast_cdp.py has no text extraction — extract_text appears zero times in it — so on a normal setup --extract-text wrote tiles and no text.md, and said nothing about it.

The fix is one line in the capability guard that already exists right above your change:

if use_turbo and (
    image_format != "jpeg"
    or viewport_width != VIEWPORT_W
    or wait_network_idle
    or extract_text          # <- added
    or not from_surface
):

plus the matching logger.info so the throughput trade is stated, mirroring what wait_network_idle already does two lines down.

I also added test_extract_text_wins_over_the_turbo_path, which passes turbo=True explicitly and asserts text.md still lands. Confirmed it's not vacuous: reverting the guard line makes it fail (on Linux you get tiles and no text.md; on macOS fast_cdp dies earlier on /dev/shm). Full file is 3/3 green.

Last thing, and this one is not a blocker: the except Exception: pass around the innerText probe now logs a warning instead of swallowing. Still best-effort — the tiles are already on disk by then, so a failed probe shouldn't fail the capture — but a silent pass there would have made the same class of bug invisible again.

If you want a follow-up: teaching fast_cdp the same Runtime.evaluate probe would let --extract-text keep turbo's throughput instead of trading it away.

@yichuan-w
yichuan-w merged commit 86102d0 into StarTrail-org:main Aug 29, 2026
4 of 5 checks passed
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