Skip to content

[Bug]: Viewer camera clips zoomed content to a narrow height-limited stage #1000

Open

Description

@leonkoellerwirth-arch

Archify version or commit

dev @ 824db3a (first observed on main @ b25142d, 3.1.0)

Installation method

Git clone

Diagram type

Generated viewer (observed with Tree; affects any height-limited, narrow stage)

Exact command

ARCHIFY_UPDATE_CHECK_DISABLED=1 node archify/bin/archify.mjs deliver tree tall-narrow.tree.json tall-narrow.html
# open tall-narrow.html at 1440x900, press the zoom-in control three times (175%), then drag to pan

Minimal redacted JSON reproduction

{"schema_version":1,"diagram_type":"tree","meta":{"title":"Tall narrow tree","output":"tall-narrow.html"},"layout":{"direction":"right"},"nodes":[
  {"id":"root","label":"repository/"},
  {"id":"dir-0","label":"dir-0/","parent":"root"},
  {"id":"file-0-0","label":"file-0.js","parent":"dir-0"},
  {"id":"file-0-1","label":"file-1.js","parent":"dir-0"},
  {"id":"file-0-2","label":"file-2.js","parent":"dir-0"},
  {"id":"file-0-3","label":"file-3.js","parent":"dir-0"},
  {"id":"file-0-4","label":"file-4.js","parent":"dir-0"},
  {"id":"file-0-5","label":"file-5.js","parent":"dir-0"},
  {"id":"file-0-6","label":"file-6.js","parent":"dir-0"},
  {"id":"file-0-7","label":"file-7.js","parent":"dir-0"},
  {"id":"file-0-8","label":"file-8.js","parent":"dir-0"},
  {"id":"file-0-9","label":"file-9.js","parent":"dir-0"},
  {"id":"file-0-10","label":"file-10.js","parent":"dir-0"},
  {"id":"file-0-11","label":"file-11.js","parent":"dir-0"},
  {"id":"dir-1","label":"dir-1/","parent":"root"},
  {"id":"file-1-0","label":"file-0.js","parent":"dir-1"},
  {"id":"file-1-1","label":"file-1.js","parent":"dir-1"},
  {"id":"file-1-2","label":"file-2.js","parent":"dir-1"},
  {"id":"file-1-3","label":"file-3.js","parent":"dir-1"},
  {"id":"file-1-4","label":"file-4.js","parent":"dir-1"},
  {"id":"file-1-5","label":"file-5.js","parent":"dir-1"},
  {"id":"file-1-6","label":"file-6.js","parent":"dir-1"},
  {"id":"file-1-7","label":"file-7.js","parent":"dir-1"},
  {"id":"file-1-8","label":"file-8.js","parent":"dir-1"},
  {"id":"file-1-9","label":"file-9.js","parent":"dir-1"},
  {"id":"file-1-10","label":"file-10.js","parent":"dir-1"},
  {"id":"file-1-11","label":"file-11.js","parent":"dir-1"},
  {"id":"dir-2","label":"dir-2/","parent":"root"},
  {"id":"file-2-0","label":"file-0.js","parent":"dir-2"},
  {"id":"file-2-1","label":"file-1.js","parent":"dir-2"},
  {"id":"file-2-2","label":"file-2.js","parent":"dir-2"},
  {"id":"file-2-3","label":"file-3.js","parent":"dir-2"},
  {"id":"file-2-4","label":"file-4.js","parent":"dir-2"},
  {"id":"file-2-5","label":"file-5.js","parent":"dir-2"},
  {"id":"file-2-6","label":"file-6.js","parent":"dir-2"},
  {"id":"file-2-7","label":"file-7.js","parent":"dir-2"},
  {"id":"file-2-8","label":"file-8.js","parent":"dir-2"},
  {"id":"file-2-9","label":"file-9.js","parent":"dir-2"},
  {"id":"file-2-10","label":"file-10.js","parent":"dir-2"},
  {"id":"file-2-11","label":"file-11.js","parent":"dir-2"},
  {"id":"dir-3","label":"dir-3/","parent":"root"},
  {"id":"file-3-0","label":"file-0.js","parent":"dir-3"},
  {"id":"file-3-1","label":"file-1.js","parent":"dir-3"},
  {"id":"file-3-2","label":"file-2.js","parent":"dir-3"},
  {"id":"file-3-3","label":"file-3.js","parent":"dir-3"},
  {"id":"file-3-4","label":"file-4.js","parent":"dir-3"},
  {"id":"file-3-5","label":"file-5.js","parent":"dir-3"},
  {"id":"file-3-6","label":"file-6.js","parent":"dir-3"},
  {"id":"file-3-7","label":"file-7.js","parent":"dir-3"},
  {"id":"file-3-8","label":"file-8.js","parent":"dir-3"},
  {"id":"file-3-9","label":"file-9.js","parent":"dir-3"},
  {"id":"file-3-10","label":"file-10.js","parent":"dir-3"},
  {"id":"file-3-11","label":"file-11.js","parent":"dir-3"}
]}

Validation receipt or exact error

Validation succeeds. This is a Viewer interaction defect, not a candidate error. Condensed validate tree tall-narrow.tree.json --json receipt (paths removed):

{"ok":true,"command":"validate","type":"tree","candidateFrozen":true,"checks":[{"name":"single_svg","ok":true},{"name":"finite_svg","ok":true},{"name":"orthogonal_arrows","ok":true},{"name":"label_route_clearance","ok":true},{"name":"relationship_crossings","ok":true},{"name":"relationship_corridors","ok":true},{"name":"container_border_runs","ok":true},{"name":"route_rhythm","ok":true},{"name":"legend_clearance","ok":true}],"diagnostics":[]}

Expected behavior

Zooming in may use the whole diagram container. At 175% the whole scaled tree is visible. At 300% it fills the container and can be panned to every edge.

Actual behavior

A deep, right-growing tree is height-limited. Its <svg> stage is much narrower than .diagram-container, which centres it (here a 342 px stage in a 930 px content box). clamp() and clipToViewport() in viewer/viewer-camera.js treat the SVG's own box as the viewport. After zooming in, the content is clipped to that narrow column even though the container has free space on both sides. At 175% about 128 px are cut away on each side, and the gap grows with the scale. Panning also stops at the column edges. The same happens when the artifact is embedded in an iframe.

Supporting visual evidence

Measured at 1440×900, dark theme. With the fix, the visible region at 175% is 420–1019 px, which is the whole 598 px scaled stage. Without the fix, the visible width stays below the scaled stage width. The focused browser test in the linked PR reproduces this at 175%, 225% and 300%. It fails on dev and passes with the fix.

Environment

macOS 27.0.1, Google Chrome 155.0.8059.80, Node 24.13.0

Final checks

  • I searched existing issues for the same problem.
  • I removed access tokens, credentials, private repository contents, and personal or customer data.

Related but not overlapping: #329 (pointer-anchored wheel zoom) changes how zoom() anchors, not clamp()/clipToViewport(). The two changes compose.

Activity

  1. added a commit that references this issue on Oct 18, 2026
    dd36ddc
  2. BlueboxC commented on Oct 20, 2026

    @BlueboxC

    We adapted @leonkoellerwirth-arch's PR #1001 on top of Archify v3.2.0 in our fork, retaining contributor attribution: BlueboxC@e8c783f.

    The camera uses the container content box rather than its padding box, excluding the Dock's reserved padding when calculating clipping and pan constraints. It retains the existing SVG viewport and authored geometry, overview at or below 100%, and reset behavior. We regenerated the Viewer template and its owned public examples instead of editing generated scripts directly.

    The focused camera/layout and real-Chrome compatibility checks pass. The complete v3.2.0 fork suite passed 3,373 tests with 125 conditional/platform skips; the maintained Chrome gate passed 301 checks and the seven opt-in Explainer browser tests passed separately. Generated-output, extracted-package and WebM/site checks also passed. This includes our prior offline-font and input/navigation customizations; no perceptual review is claimed.

    The exact fork commit is now on main and installed in our existing Skill. All 213 managed files and modes match the canonical package. The official installed-package smoke passed, including public-CLI Explainer delivery/check/patch; all nine data-chart regressions and fourteen real-Chrome camera checks also passed against the installed runtime. A complete previous-installation preimage is retained, and global agent instructions/hooks and existing user HTML are preserved.

    BlueboxC

  3. tt-a1i commented on Oct 25, 2026

    @tt-a1i
    Owner

    Related integration update: #1051 has landed through #1102, but it fixes zooming OUT tall diagrams and original alignment restoration. This issue concerns zooming IN a narrow height-limited stage and its clamp/clip viewport. Those are distinct boundaries, so this issue and its #1001 work remain open; this batch does not claim to fix them.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions