Skip to content

fix(devknobs): safari's bottom bar shows the whole host and the real page under it - #100

Merged
mertbuilds merged 61 commits into
mainfrom
fix/bottom-bar
Nov 13, 2026
Merged

mertbuilds merged 61 commits into
mainfrom
fix/bottom-bar

Conversation

@mertbuilds

Copy link
Copy Markdown
Collaborator

Safari's bottom bar in the phone frame: the domain pill no longer cuts the host, and the band under the bar shows the page's real images and videos instead of a grey slab with a hard cut.

Bug (0.7.1, iPhone frame, mertduzgun.com dark): the pill read "mertduzgun.c..." with room left, and a video card was cut at the bar with a grey box under it.

Root cause

  • Pill: the domain's limit was set once for the expanded bar at 17px, and the minimized pill only scaled it by 13/17, while the pill's own width came from a separate 13px measure. Two width sources for one text: any host over about 13 characters was cut on 393 to 402 pt phones.
  • Band: the band under the bar is a copy of the page (svg foreignObject). copyPage blanks every image, and the band, unlike the fold pictures, never drew them back. So every img, video, canvas and CSS background under the bar showed as its empty box (the grey is the site's own background-color on the media box). Videos had three more causes: a positioned sound button over the video dropped it, a video without a frame was skipped, and Chrome drew its media controls into the copy (a stray "/" under the pill).
  • Why it kept coming back: the band is a second render of the page, and every feature it did not reproduce showed up as a new "bug" at the viewport edge. Nothing compared the band with the live page.

What changes

  • Pill and field: one domainRoom/domainFit gives both the text limit and the pill size. The domain lays out at its real size per state (FLIP scale only during the morph). The expanded field follows real iOS Safari, measured on the simulator: 17pt when it fits, shrinks to fit down to 13pt, then keeps the end of the host with a 9px start fade. No ellipsis anywhere. Text room keeps 21pt from the icons (25 on the Duo cover). Descenders are no longer clipped.
  • Band media (new bandmedia.ts): images, video frames, canvases and CSS backgrounds near the band go into the copy as real pixels. Images from other origins are asked plain first, then with CORS only for files that came, and a host is dropped after two refusals, so the console sees at most one line per address. Pictures that cannot be read go on an overlay, with whatever paints above them (by stacking-context rules) drawn back on top. Playing videos redraw on every frame; videos without a frame show their poster.
  • Copy fixes: the copy keeps the viewport's overflow (pages with html,body{height:100%} body{overflow-x:hidden} no longer get a blank band), follows a body scroller, and hides Chrome's media controls (also in the fold pictures).
  • scripts/underbar-oracle.ts: a headless check that compares the band with the live page, pixel by pixel, on built-in fixtures (images, backgrounds, canvas, videos, paint-order cases) or any URL. Not run in CI.

Proof

  • bun test in packages/devknobs: 4580 pass, 1 fail (time.test.ts:376, zone name on this Mac only; fails on main too). tsc, pnpm lint, pnpm format:check clean.
  • Pill matrix test: every Safari iPhone, iPad Air 11 and Pixel 10, portrait and landscape, every layout and state, 5 hosts up to 40 characters. On the old code 37 cases cut mertduzgun.com and other hosts; all pass now.
  • Field vs real Safari (iOS 26.5 / 27.1 simulator, ego Chromium):
Device, host Real Safari knobs now
17 Pro, mertduzgun.com 17pt, fits 16.86, fits
17 Pro, claudemarketplaces.com 13pt, start faded 13, start faded
Pro Max, claudemarketplaces.com about 14.5pt, fits 14.49, fits
SE, mertduzgun.com 13pt, fits 13.01, fits
  • Band oracle (mean channel gap of band vs live page, 0 to 255; pass below 12):
Page main this PR
fixture hero / paper / chart / away 95.4 / 81.5 / 41.6 / 138.2 1.4 / 6.3 / 4.2 / 0.2
mertduzgun.com dark, image under the bar 68 6.7
body{overflow-x:hidden} page, text 31 6.2
fade carousel, scaled clip, 70 background images 120 to 162 0 to 2.3
video paint-order cases, looping video fail ok, 0 pictures over 4 loops
  • Console: an idle page with 70 backgrounds from a host without CORS logged 63 CORS lines and took 64 pictures on the intermediate build; now 2 and 3.
  • Screenshots (ego, mertduzgun.com, before | after): added in a comment once the ego run finishes.
  • code-reviewer: pill 2 rounds (PASS), field 2 rounds (PASS), band 3 rounds then a rethink of the CORS rule (PASS after), video 3 rounds (last round's two list fixes applied, no blocker).

Known limits

  • Images and videos from a host without CORS show on an overlay; their frame may lag the live video by one frame while it plays.
  • Cross-origin stylesheets are still left out of the copy (the extension has no fetch path for them; no new permission added).
  • Stacking triggers not listed in contextOf (for example -webkit-box-reflect) are guessed as no context.
  • On pages that scroll inside body, Safari's bars do not minimize on scroll (pre-existing, not touched).
  • Under bun test --randomize, 34 Duo-fold and frame tests fail on main too (test order coupling, not touched).

Risk

Normal: drawing only, inside knobs' own frame (packages/devknobs engine). No storage, network to new hosts, permissions or release files. It ships to users with the next knobs release; the site picks it up on merge.

How to revert

Revert the squash commit. No state or data depends on it.

One pass over the text skips comments, strings, template literals and regex
bodies. It replaces the comment-stripping regex, the directive regex that
backtracked exponentially on long comment heads, and the import-line regex.
Strings that look like comments no longer hide a mount.
Read the knobs key first, then the devknobs key. Write and remove both
for two minor releases so an old copy and a new copy on one origin share
state. The storage view and fresh mode treat both prefixes as our own.
An old copy (the 0.7.0 extension) writes and removes only devknobs
keys, so reads follow the old name and writes put it first. The storage
view group label reads knobs.
Docs, skill, READMEs, install prompt, init and the demo use the new
names: window.knobs, Knobs, Knobs* types, x-knobs-now, ?knobs=fresh,
data-knobs*, knobs storage keys. Old names still work. Bump package and
extension to 0.8.0.
Docs, skill and install prompt name window.devknobs for pages with a
knobs of their own and copies before 0.8. A named element no longer
keeps the api off window.knobs (Object.hasOwn).
Same text as the 0.7.1 store update, so 0.8.0 keeps it.
@mertbuilds

Copy link
Copy Markdown
Collaborator Author

Screenshots (ego browser, mertduzgun.com, this PR's build vs main's build injected in place of the store extension)

56 cells checked: iPhone 18 Pro, 18 Pro Max, Air, 17, 16, SE, Duo closed × light/dark × bar expanded/minimized × image/video under the bar. Every after cell shows the whole host and the page continuing under the bar; no grey slab, hard cut or doubled content.

before after

More after shots:

Before: iPhone SE pill reads "mertduzg…" and the image under the bar is a grey box. After: "mertduzgun.com" whole, image and video run on under the bar.

@mertbuilds
mertbuilds merged commit a396ab4 into main Nov 13, 2026
3 checks passed
@mertbuilds
mertbuilds deleted the fix/bottom-bar branch November 13, 2026 23:08
mertbuilds added a commit that referenced this pull request Nov 14, 2026
* chore(release): knobs 0.7.2

Bump @vinena/knobs and the extension to 0.7.2 with the changelog entry for the bottom bar fix (#100).

* docs(knobs): Safari with a capital in the 0.7.2 entry

---------

Co-authored-by: Mert Duzgun <46185450+mert-duzgun@users.noreply.github.com>
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.

1 participant