Back to home

xiayuhkust

dsh-visual-probe

Visual-verification tools for DeepSeek Harness (dsh): flicker, visual regression, and WCAG contrast as numbers a text-only agent can read.

Stars
0
Language
JavaScript
Created
Aug 15, 2026
Updated
Aug 15, 2026

Introduction

dsh-plugin-browser-probe

English | 中文

Visual-verification tools for DeepSeek Harness (dsh). It runs a headless browser and turns three kinds of UI check into numbers, so a text-only agent can verify them by reading metrics instead of seeing the page:

ToolQuestion it answersReturns
flicker_detectDoes the page flicker?how many frame pairs change, and by how much
visual_diffDid my edit change anything unintended?how much differs, and which screen zones
contrast_probeIs any text too light to read?WCAG contrast ratio per text element, and which fail

Each returns data, not an image — the agent reads the numbers and decides. Built on Playwright + pngjs.

Runtime: dsh 0.1.x developer preview; interfaces may shift with dsh.

Install

Heavy dependency (Playwright downloads a browser), so it is a standalone package.

dsh plugin --profile web add -w dsh-plugin-browser-probe
npx playwright install chromium-headless-shell   # one-time browser download

Mount it in the profile's cordis.patch.yml (use - insert: to add a new row, not a bare - id:):

- insert:
    - id: browser-probe
      name: dsh-plugin-browser-probe
      config:
        frames: 10        # flicker: frames to capture (default 10)
        intervalMs: 150   # flicker: ms between frames (default 150)
        settleMs: 400     # wait after load before capturing (default 400)

Restart dsh web to take effect. dsh --profile web --dump-config confirms the row is present.

Tools

flicker_detect

Captures consecutive frames of a page and measures the fraction of pixels that change between them. A verdict is FLICKER only when change recurs (≥2 frame pairs over threshold) — a single changed pair is a one-off load transition, not flicker.

ParameterDescription
urlPage to inspect (file:// or http://).
framesFrames to capture. Default 10.
intervalMsMilliseconds between frames. Default 150.
settleMsWait after load before the first frame. Default 400.
selectorOptional CSS selector; crops the measurement to that element's box — the reliable way to catch flicker in a small widget.
regionOptional {x, y, width, height} rectangle; values ≤1 are viewport fractions, otherwise pixels.

Whole-frame measurement reliably catches flicker over ~8% of the screen. For a small flickering element (a caret, a thin line), pass selector or region so the ratio is computed inside that area.

Returns hotPairs/totalPairs, maxChangedRatio, isFlickering, scope, verdict.

visual_diff

Compares two rendered states — two URLs, or two saved PNG paths — and reports how much and where they differ. The frame is split into an 8×6 grid; changed zones are reported as top/middle/bottom × left/center/right, so a small change still surfaces with its location.

ParameterDescription
aBaseline state: an http/file URL, or a path to a saved .png.
bComparison state: same.
settleMsFor URL inputs, wait after load before capturing. Default 400.

Returns changedRatio, changedZones, changedCells (worst first), dimensionsMatch, verdict. Typical use: keep a known-good URL (or capture before your edit), then diff the after-state and confirm only the intended zones changed.

contrast_probe

Reads each text element's color and effective background from the DOM and computes the objective WCAG contrast ratio, reporting which text fails the standard (AA needs 4.5:1 for normal text, 3:1 for large). Large text is detected by font size/weight; semi-transparent text is blended over its background first.

ParameterDescription
urlPage to inspect (file:// or http://).
selectorOptional CSS selector to limit the scan to one area. Omit to scan the whole page.
standardAA (default, 4.5/3) or AAA (7/4.5).
settleMsWait after load before reading styles. Default 400.

Returns checked (count), worstRatio, failures (each with the ratio, required ratio, and actual fg/bg colors), verdict.

Command line

Each tool's core also runs standalone, useful for a quick check or CI:

node src/flicker-detect.mjs <url> [frames] [intervalMs] [settleMs] [region:x,y,w,h | sel:CSS]
node src/visual-diff.mjs <a-url-or-png> <b-url-or-png> [settleMs]
node src/contrast-probe.mjs <url> [selector] [AA|AAA]

testpages/ holds fixtures you can point them at (a flickering page, a before/after pair, graded-contrast text).

Notes

  • Zero @deepseek-ai/dsh-* import: dsh 0.1.x-rc's loader does not resolve host internal imports for third-party plugins, so tools are registered by constructing the tool object directly with ctx.tools.register.
  • networkidle is not used before capture — a live app that polls may never go idle; a fixed settleMs wait is used instead.

License

MIT