fix(web): the page never zooms on a pinch; rows never swipe while selecting or with two fingers #1003
Closed
kayg
wants to merge 8 commits from
t3/prevent-pinch-zoom-swipe into dev
pull from: t3/prevent-pinch-zoom-swipe
merge into: kayg:dev
kayg:main
kayg:feat/style-sheet-fixes
kayg:storage/wal-checkpoint-off-save-1051
kayg:t3/paged-search-reconcile-final
kayg:feat/pass8
kayg:review/calendar-2
kayg:fix/shared-row-swipes
kayg:feat/contrast
kayg:dev
kayg:feat/collections-db
kayg:fix/instant-keys-1056
kayg:feat/money-round
kayg:perf/stalls-1054
kayg:wip/tiering-v3-cold4
kayg:wip/tiering-v3-cold5
kayg:port/rows-actions
kayg:port/rows-drop
kayg:port/hidden-blocks
kayg:port/rows
kayg:port/port-tables
kayg:port/port-tables-2
kayg:t3/mail-1011-liveness
kayg:port/devices
kayg:port/port-attachments
kayg:port/port-attachments-2
kayg:port/port-attachments-3
kayg:docs/design-consistency
kayg:docs/engineering-standards
kayg:fix/rusqlite-0.41-rustsec-2026-0459
kayg:feat/iphone-fixes
kayg:shared/restore-and-stale-918-919
kayg:fix/phone-top-band-contrast
kayg:feat/notes-approve
kayg:perf/soak-max-gate
kayg:feat/files-s1-identity
kayg:fix/lifecycle-event-id
kayg:feat/quickfix
kayg:wip/tiering-v3-cold
kayg:perf/web-mockup-frame-fixes
kayg:port/contacts-merge
kayg:port/contacts-edit
kayg:test/shared-room-no-deleted-text
kayg:perf/first-paint-credits
kayg:fix/shared-history-after-grant
kayg:fix/phone-swipe-regressions
kayg:feat/contrast-r1
kayg:review/sharing-pr3-review
kayg:feat/tiering-v3-s2-tags
kayg:feat/folder-drop
kayg:fix/deskpills-lazy-share
kayg:feat/sharing-s1-pr3
kayg:tmp/money-round-before
kayg:fix/phone-pane-backdrop-roots
kayg:fix/palette-preload-chips
kayg:fix/invite-defaults
kayg:port/tasks-matrix
kayg:fix/restore-green-gates
kayg:fix/sharing-fast-path-hardening
kayg:t3/photos-s1
kayg:feat/tiering-v3-s1-namespace
kayg:notes-archive-hide
kayg:feat/tasks-actions
kayg:t3/photos-plan-slice-one
kayg:feat/whiteboard-s1
kayg:t3/paged-search-reconcile
kayg:t3/smooth-pane-transitions
kayg:t3/index-position-startup-scan
kayg:fix/private-staging-759b-r2
kayg:feat/dav-transport
kayg:feat/imap-notes-fetch
kayg:feat/imap-notes-projection
kayg:feat/imap-notes-readonly
kayg:feat/imap-notes-auth
kayg:feat/imap-notes-server-foundation
kayg:docs/undo-block-ids-plan
kayg:fix/undo-768
kayg:proto/block-identity-v7d
kayg:issue/carddav-read-perf-fix-r2
kayg:issue/carddav-read-r2
kayg:docs/imap-766-plan-v2
kayg:docs/imap-notes-plan
kayg:docs/quota-plan-2
kayg:fix/parallel-test-flakes
kayg:feat/contacts-companion-pure
kayg:docs/calendar-backend-v2-plan
kayg:feat/calendar-ical-converter
kayg:docs/tasks-writes-v2-plan
kayg:docs/carddav-writes-v2-plan
kayg:docs/contacts-import-v2-plan
kayg:issue/610-pm-midnight
kayg:feat/calendar-v2-s1-containment
kayg:docs/dav-recorder-v2-plan
kayg:plan/previews-865
kayg:plan/save-log-durable-entry
kayg:fix/e2e-data-on-ram
kayg:feat/appwindow3
kayg:feat/appwindow2
kayg:feat/appwindow
kayg:feat/money-p1
kayg:feat/keys-sheet
kayg:review/calendar-2-themes
kayg:feat/smooth-cursor
kayg:fix/float-style
kayg:review/calendar-2-float
kayg:release/2026-11-06-hotfix
kayg:archive/port/rows-drop-code
kayg:archive/port/rows-actions
kayg:archive/port/rows
kayg:scratch/tasks-894-ui-gates
kayg:review/calendar-2-night-wip
kayg:release/2026-11-05
kayg:archive/feat/tiering-v3-s1-namespace
kayg:archive/flaky/rows-drop-fixed
kayg:archive/flaky/rows-actions
kayg:archive/feat/tiering-v3-reader
kayg:archive/feat/tiering-v3-roots
kayg:archive/flaky/rows
kayg:sim/bm15-final
kayg:sim/bm15-fix
kayg:sim/bm15-bis-932
kayg:sim/bm15-bis-909
kayg:sim/bm15
kayg:sim/bm15-bis-887
kayg:perf/tiering-option-b
kayg:port/style-3
kayg:port/style-2
kayg:port/style
kayg:fix/sw-lazy-precache
kayg:fix/vim-w-waits-for-save
kayg:fix/signout-test-races
kayg:fp-scratch/all
kayg:sim/bm15-first-paint
kayg:archive/bm15/rows-drop
kayg:archive/bm15/rows-actions
kayg:fix/palette-snippet-contrast
kayg:archive/bm15/rows
kayg:archive/bm15/hidden
kayg:archive/plan/save-log-durable-entry
kayg:mock/money-fix2
kayg:archive/fix/e2e-data-on-ram
kayg:mock/appearance-all
kayg:mock/pill-press
kayg:archive/docs/design-consistency
kayg:mock/auto-swipes
kayg:mock/mail-actions
kayg:archive/feat/mail-p1
kayg:mock/money-static
kayg:archive/review/standards-917
kayg:archive/docs/engineering-standards
kayg:perf/tiering-761-fix
kayg:mock/type-audit
kayg:mock/third-pane
kayg:feat/contacts-import-v2-s1
kayg:perf/executor-hot-path
kayg:archive/port/hidden-blocks
kayg:archive/port/devices
kayg:scratch/executor-on-894
kayg:scratch/executor-spans2
kayg:scratch/executor-spans
kayg:train/2026-11-03-bm14c
kayg:archive/port/rows-drop
kayg:feat/tiering-761-d-scale
kayg:feat/tiering-761-c-index
kayg:feat/tiering-761-b-reader
kayg:archive/review/sharing-pr3-review-9fec3d1f
kayg:train/2026-11-03-bm14b
kayg:perf/ports-lazy-budgets
kayg:feat/tiering-761-a-migration
kayg:train/2026-11-03-bm14
kayg:train/2026-11-03-bm12
kayg:archive/review/sharing-pr3-review
kayg:archive/feat/sharing-s1-pr3
kayg:train/2026-11-03-bm11
kayg:archive/feat/imap-notes-fetch
kayg:train/2026-11-03-bm10
kayg:archive/review/sharing-pr3-review-code
kayg:archive/plan/previews-865
kayg:train/2026-11-02-bm9
kayg:archive/port/port-attachments-3
kayg:archive/port/contacts-merge
kayg:fix/motion-audit-859
kayg:train/2026-11-02-bm8
kayg:archive/scratch/bm8-prep
kayg:archive/port/port-pickers
kayg:archive/port/port-tables-2
kayg:archive/port/port-tables
kayg:archive/feat/sharing-s1-pr3-code
kayg:archive/scratch/bm7-fixed
kayg:train/2026-11-02-bm7
kayg:archive/fix/parallel-test-flakes-code-minipc01
kayg:train/2026-11-02-bm6
kayg:docs/imap-766-readonly-slice
kayg:feat/dav-recorder-v3
kayg:research/788-diagnosis
kayg:archive/fix/parallel-test-flakes
kayg:archive/perf/pr792-cold-index-edit
kayg:docs/sharing-slice-1
kayg:archive/docs/imap-766-plan-v2
kayg:feat/dav-recorder-v2
kayg:archive/docs/dav-recorder-v2-plan
kayg:spike/imap-766-s0-cases
kayg:archive/fix/undo-768
kayg:feat/dav-recorder
kayg:archive/docs/contacts-import-v2-plan
kayg:archive/feat/calendar-ical-converter
kayg:feat/contacts-import
kayg:perf/cold-growth-on-764
kayg:perf/cold-growth-on-tiering
kayg:archive/docs/tasks-writes-v2-plan
kayg:archive/docs/calendar-backend-v2-plan
kayg:archive/feat/contacts-companion-pure
kayg:feat/tasks-set-status
kayg:feat/tasks-move-reorder
kayg:archive/docs/carddav-writes-v2-plan
kayg:archive/feat/calendar-v2-s1-containment
kayg:feat/tasks-delete-promote-duplicate
kayg:archive/feat/dav-transport
kayg:archive/fix/carddav-rebuild-r1
kayg:archive/fix/carddav-retention-r1
kayg:feat/carddav-writes
kayg:perf/quota-on-tiering
kayg:perf/quota-simple
kayg:perf/cold-growth-on-quota-stack
kayg:feat/calendar-identity
kayg:feat/calendar-event-parser
kayg:feat/calendar-props-maps
kayg:port/glass-when-ring
kayg:feat/tiering-761-s1
kayg:wip/10by32-01-impl-759b-scratch-20261030
kayg:wip/6by32-01-bm3-webkit-pause-20261030
kayg:wip/10by32-01-bm3-chromium-pause-20261030
kayg:wip/6by32-01-tier-761-s1-pause-20261030
kayg:spike/imap-766-s0
kayg:wip/6by32-02-calternal-pause-20261030
kayg:wip/8by64-01-review-688-pause-20261030
kayg:wip/6by32-01-verify-744-pause-20261030
kayg:wip/6by32-01-verify-742-pause-20261030
kayg:fix/private-staging-759b
kayg:wip/6by32-01-review-748-pause-20261030
kayg:wip/6by32-01-review-733c-pause-20261030
kayg:wip/6by32-01-review-733b-pause-20261030
kayg:wip/6by32-01-perf-idle-8-pause-20261030
kayg:wip/6by32-01-perf767-base-pause-20261030
kayg:wip/6by32-01-perf767b-767-pause-20261030
kayg:wip/6by32-01-perf767b-764-pause-20261030
kayg:wip/6by32-01-perf767-767-pause-20261030
kayg:wip/6by32-01-perf767-764-pause-20261030
kayg:wip/6by32-01-lists-b-pause-20261030
kayg:wip/6by32-01-ids-verify5-pause-20261030
kayg:wip/6by32-01-ids-verify4-pause-20261030
kayg:review/calendar-month
kayg:wip/10by32-02-undo-768-20261030
kayg:test/ids-verify3
kayg:wip/10by32-02-bm3-chromium-20261030
kayg:wip/10by32-01-review-753-20261030
kayg:wip/10by32-01-perf-idle-2-20261030
kayg:wip/10by32-01-bm2-webkit2-20261030
kayg:plan/759b-staging
kayg:train/2026-10-30-bm2
kayg:review/758b-tests
kayg:review/758-tests
kayg:review/764d-tests
kayg:review/767b-tests
kayg:review/preview-2026-10-29
kayg:review/767-tests
kayg:docs/tiering-731-plan
kayg:wip/c6b02-when2
kayg:wip/c6b02-tasks-create-fix5
kayg:wip/c6b02-settings-frost-mock
kayg:wip/c6b02-settings-frost-base
kayg:wip/c6b02-settings-frost
kayg:wip/c6b02-ring
kayg:wip/c6b02-review-733d
kayg:wip/c6b02-bm1-full-a
kayg:wip/compute02-when-field
kayg:wip/compute02-webkit-lists-repro
kayg:wip/compute02-train-bl2-dev
kayg:wip/compute02-dev-green-pw164
kayg:docs/deploy-plan
kayg:review/764c-tests
kayg:review/764b-tests
kayg:review/748g-tests
kayg:review/764-tests
kayg:review/759b-tests
kayg:fix/storage-owner-liveness-642
kayg:fix/storage-fix-7
kayg:wip/compute02-storage-fix-7
kayg:review/759-tests
kayg:feat/tiering-731
kayg:wip/tiering-731-first-attempt
kayg:wip/compute02-storage-fix-1
kayg:review/754g-tests
kayg:perf/idle-honest-2
kayg:review/754f-tests
kayg:review/754e-tests
kayg:review/755-tests
kayg:review/754d-tests
kayg:review/754c-tests
kayg:review/754b-tests
kayg:review/754-tests
kayg:fix/storage-private-tmp-642-dev
kayg:wip/compute02-verify-750
kayg:test/ids-verify5
kayg:test/ids-verify4
kayg:fix/storage-exact-file-quarantine-642
kayg:wip/compute02-verify-740
kayg:perf/one-flush-save
kayg:review/calendar-static
kayg:wip/compute02-verify-737
kayg:review/indexing-ring
kayg:review/when-field-2
kayg:wip/compute02-review-733
kayg:proto/block-identity-2-review
kayg:proto/block-identity-review
kayg:wip/c6b02-tc-design
kayg:review/when-field
kayg:issue/tasks-create-ids
kayg:issue/tasks-create
kayg:review/combined
kayg:wip/compute02-review-combined
kayg:review/task-polish
kayg:review/top-buttons
kayg:review/q1-q5
kayg:train/2026-10-26-bl2
kayg:archive/wt-398h
kayg:archive/train-bm-proptest-seed
kayg:archive/integ325-293
kayg:archive/integ325-ci
kayg:archive/client-auth-review-baseline
kayg:archive/wt-sim-4853
kayg:archive/wt-sim-4811
kayg:archive/wt-448m
kayg:archive/wt-398a-allocator
kayg:issue/calendar-adr
kayg:archive/old/train/2026-10-23bk2
kayg:archive/old/train/2026-10-23bk
kayg:issue/469-mockup-9a
kayg:issue/smart-tasks-mockup
kayg:issue/469-menus
kayg:issue/652-signout-flake
kayg:issue/652-history-fix7
kayg:issue/652-history-fix6
kayg:archive/old/issue/spring-check-perf
kayg:issue/spring-check-perf
kayg:issue/659-rename-fast
kayg:archive/old/train/2026-10-21ar
kayg:issue/search-hardening-3
kayg:issue/652-history-fix5
kayg:issue/660-port-sheets-glass
kayg:issue/652-history-fix4
kayg:issue/652-history-fix3
kayg:issue/652-history-fix2
kayg:issue/652-history-flake
kayg:train/2026-10-21an
kayg:issue/settings-admin-port
kayg:issue/search-port
kayg:train/2026-10-20am
kayg:issue/storage-split-fix2
kayg:issue/storage-split
kayg:archive/old/issue/folder-ids-fix
kayg:train/2026-10-20al
kayg:archive/old/issue/folder-ids
kayg:train/2026-10-20ak
kayg:issue/tables-mockup
kayg:train/2026-10-19aj
kayg:train/2026-10-19ai
kayg:issue/469-merge-fixes
kayg:issue/sidebar-band
kayg:archive/old/issue/608-settings-spec
kayg:train/2026-10-19ah
kayg:issue/swipe-offscreen
kayg:train/2026-10-19ag
kayg:archive/old/issue/590-nlp-adr
kayg:archive/old/issue/587-event-after-write-2
kayg:train/2026-10-19af
kayg:archive/old/issue/587-event-after-write
kayg:issue/469-pill-room-c
kayg:issue/469-pill-room
kayg:train/2026-10-19ae
kayg:issue/469-narrow-handle
kayg:issue/397-ios-swipe-2
kayg:archive/old/issue/adrs-photos-files-tasks
kayg:issue/469-desk-fixes
kayg:archive/old/issue/auto-update-adr
kayg:issue/469-filter-gap
kayg:issue/469-port-1b
kayg:issue/442-opacity-flake
kayg:archive/397-ios-swipe-2
kayg:issue/327-web-fast-3
kayg:archive/old/issue/469-port-1
kayg:issue/469-port-1
kayg:issue/327-web-fast-2
kayg:archive/old/issue/327-web-fast
kayg:archive/old/issue/425-stt-bench-vq1
kayg:archive/old/issue/445-no-bleed
kayg:archive/old/issue/400-swipe-mockup-2
kayg:issue/426-client-review
kayg:issue/426-client-2
kayg:issue/426-client
kayg:archive/old/issue/476-safe-area-lab
kayg:archive/445-top-light-2
kayg:archive/old/issue/445-top-light
kayg:issue/471-blocks-mockup
kayg:archive/old/issue/448-links-app-2
kayg:archive/old/issue/448-links-app
kayg:archive/old/issue/398-cold-links
kayg:issue/434-442-small-2
kayg:issue/434-442-small
kayg:archive/old/issue/398-unload
kayg:archive/old/issue/445-bottom-band
kayg:issue/445-bottom-band
kayg:issue/351-rem-3
kayg:archive/312-views-mockup
kayg:issue/351-rem-2
kayg:issue/351-rem
kayg:archive/old/issue/327-first-note
kayg:issue/395-width-phone
kayg:archive/old/issue/327-typing
kayg:issue/396-ios-edges
kayg:issue/397-ios-swipe
kayg:archive/398-allocator
kayg:archive/old/issue/327-notes-index
kayg:issue/345-347-contrast-empty-2
kayg:archive/old/issue/44-arenas
kayg:issue/44-arenas
kayg:archive/integ325
kayg:archive/old/issue/327-perf-harness
kayg:archive/old/issue/44-thp
kayg:issue/44-thp
kayg:archive/old/issue/285-sticky-pills
kayg:archive/old/issue/44-trim-3
kayg:issue/44-trim-3
kayg:archive/old/issue/165-cards
kayg:archive/old/issue/165-picker-2
kayg:archive/old/issue/212-multi-select
No reviewers
Labels
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".
No due date set.
Dependencies
No dependencies set
Reference
kayg/calternal!1003
Loading…
Reference in a new issue
No description provided.
Delete branch "t3/prevent-pinch-zoom-swipe"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes both bugs kayg reported on prod from the iPhone (11-07 ~11:45 IST): the page zooms on a pinch, and a row half-swipes while two fingers select. Rules from kayg: "pinching should be bound to contextual actions and if there's none, to no action" and "selected items cannot be horizontally swiped as the bottom bar is already showing contextual items". Rebased on dev
98f14a3d1(= prod). Head9401f7134.Bug 1: pinch zooms the whole page
touch-action: pan-x pan-yonhtml(packages/ui/src/styles/base.css): no pinch-zoom or double-tap zoom from any element on Chrome / Firefox / Android. The buttons' owntouch-action: manipulationis gone (redundant: the effective value is the intersection with html's).packages/ui/src/lib/no-page-zoom.ts):gesturestart/change/endare cancelled unless the pinch begins inside[data-pinch]. It installs when theidle-screenschunk is evaluated, not when a screen mounts:Settings.svelteandSignIn.sveltecallinstallNoPageZoom()from a<script module>block (module evaluation). That chunk is fetched right after first paint (warmUIon idle when signed in;signIn.load()as soon as auth says signed out), so the first paint pays nothing and nothing depends on Settings being mounted or opened. (Review point onf7511158: the call used to sit in the instance script.App.sveltedoes pre-mount Settings hidden on idle for signed-in users, andSignInis never instantiated for them, so it worked, but only by that coupling; now it doesn't.)gw.mjs, polled every ~10–20 ms): cold first load 511 ms after navigation start (first contentful paint 301 ms, so ~210 ms after paint); warm reloads 186–288 ms (FCP 94–180 ms), i.e. ~90–110 ms after paint. Raw: comment below.import "…/no-page-zoom"for its side effect is dropped by the build (the ui package declares only its CSS as having side effects), so the guard vanished in my first attempt and the byte count looked better than it was. It is an explicit call (from<script module>), documented in the module, and the dist was checked to contain it (idle-screens-*.js).apps/webyet and no other pinch handler (the only one was the lists' lazygesturestarthandler intwo-finger.ts, which this replaces). The contract for a future viewer: weardata-pinch, settouch-action: none, read the fingers yourself. Tested byno-page-zoom.spec.ts. Waiting for the viewer to exist (agreed).Bug 2: a row swipes while selecting
Root cause: when a second finger landed on a row that was already swiping,
tstartset the gesture tonullwithout finishing it, so the row stayed half open (the screenshot) and the list'sshut()refuses to close a "swiping" row. Nothing stopped a swipe that began before selecting started.Fix, in the shared row (
apps/web/src/rows/swipe-row.ts, used by everyRowActionslist; today Notes, Journal and Trash, later Tasks and the rest):abort()never runs an action, not even an armed long swipe (Delete).handle.off(), a one-line getter), no swipe is claimed, selected or not; a swipe already moving goes back on its next move, or on let-go.touchcancelputs the row back (it used to release, which could run a long-swipe Delete).claimsSwipeneeds ≥10 px sideways, more than 2× the vertical, within 300 ms; multi-touch never starts one.First paint: PR ≤ dev, in bytes (dev
98f14a3d1vs PR9401f7134,node scripts/bundle-size.mjs <dist> --json, builds are deterministic)perf/web.sh --bundle-only --gate(dev-02): exit 0;web.bundle.first_js_brotli225.613 KB (≤ 240),first_css_brotli17.955 KB (≤ 22.1),chunk.index225.613 (≤ 226.25),total_js_brotli471.895 (≤ 488.3). No budget line changed;perf/budgets.jsonis identical to dev.Real cuts that fund the swipe change and the CSS declaration (no behaviour change; checked by grep over
apps/web,apps/mockup,packages, docs):Icon.svelte: thesunandmoonpaths (no<Icon>draws them; the sky draws its own discs) deleted;users(drawn only by Settings) moved behind it intoicons-extra.ts(icons.test.tspasses).swipe-open.ts:isOpendeleted (markShutalready ignores a row that isn't the open one);Row.activewas dead; onebeathelper for the pill's two pulses.tokens.css:--strong-weight(×2),--icon-bar,--chrome-ink,--ease-in-out,--r-controlhad novar()or string reference anywhere.<script module>happened to land 30 B lower again). Left alone on purpose: the--s-1…--s-10tokens and five block-type icons are also unused/lazy-only, but cutting them did not help the count, and the icons are shared with the mockup.Gates
CALTERNAL_BIN=… bunx playwright test e2e/no-page-zoom.spec.ts e2e/swipe-guard.spec.ts e2e/swipe-rows.spec.ts e2e/select-rows.spec.ts e2e/touch-select.spec.ts e2e/icons-extra.spec.ts e2e/bar-taps.spec.ts e2e/pill-press.spec.ts --project=chromium-1440 --project=chromium-390 --workers=1--project=webkit-1440 --project=webkit-390bun run check/bun run lint/bun run test(apps/web)perf/web.sh --bundle-only --gatepm.mjsABAB ×3 on the final headFlake seen earlier, not caused here:
touch-select.spec.ts:246(the editor's block swipe, Chromium 390) failed intermittently on a first run on both dev and the PR (--repeat-each=30: PR 1–2, dev 1); did not recur in the final runs. The Chromium CDP pinch spec cannot fail by itself (Chromium's emulated pinch does not zoom even an unguarded page; Playwright WebKit cannot send a pinch), so the simulator is the real proof.iOS simulator, cold, signed in, Settings never opened (final head
9401f7134)Each on a fresh origin (no service-worker cache), loaded, ~7 s idle, then a real 2.5× pinch: note opened directly by
/notes/<id>: no zoom; All-notes list: no zoom; Home Screen app (added from that URL, launched standalone): no zoom. (The standalone app starts on the daily note, not on the linked note: that is the app's start route; Settings was not opened in any of the three.) New specno-page-zoom.spec.ts: a note opened straight by URL, Settings never opened, gets the guard (WebKit + Chromium). Screenshots in the comments below. Earlier simulator evidence (swipe, selection, second finger) is fromf7511158; the row code did not change since.iOS simulator earlier (iPhone 18 Pro, iOS 27, Safari, real fingers; head
f75111586), screenshots 1206×2622 in the commentsdev zooms on a 2.5× pinch on a note page, the PR does not; a short swipe outside selection still opens the row; two fingers slid down select 3 notes and then left/right drags on selected and unselected rows move nothing; a second finger landing 0.25 s after a left swipe began leaves the row at rest. (On this simulator another agent's run once took the screen between my steps; that step was re-run.)
Perf (Mac VM, real Safari, 10,000 notes, 390×844, dev vs PR, ABAB ×3, fresh server per arm, final head)
Frame deltas (ms): scroll A p50 17 / p95 20 / p99 23 / max 25 / ≥50 ms 0; scroll B p50 17 / p95 20 / p99 23 / max 26 / ≥50 ms 0. Idle baseline A max 30, B max 32. Typing (keydown → second rAF after
input, 171 keys per arm): A p50 19 / p95 30; B p50 20 / p95 30. Free memory ≥ 3 GB incl. inactive, swap 0. (dev-02 shares the Studio with this VM and was running its spec job during the guard-window timing, so those numbers are an upper bound.)Open
RowActions.iOS 27 simulator, DEV build (origin/dev
792b335), note page, 2.5× pinch out: the page zooms (left: before pinch, right: after).iOS 27 simulator, PR build
9042c77c3, same note page and same 2.5× pinch: nothing moves (left before, right after).PR build: a short left swipe outside selection still opens the row (Delete shown).
PR build: two real fingers slid down select 3 notes; then a left drag on a selected row, a right drag on a selected row, a left drag on an unselected row: no row moves (in this order).
PR build: a left swipe started, a second finger landed 0.25 s later: the row is back at rest (left: before, right: after).
Mac VM real-Safari run (10k notes, ABAB ×3), raw output of the throwaway
pm.mjs(rAF frame deltas in ms; the script is not committed):Final head
f75111586, iOS 27 simulator: note page, 2.5× pinch (guard now in the idle-screens chunk): nothing moves (before / after).Final head: a short left swipe outside selection still opens the row.
Final head: two real fingers slid down select 3 notes; then left drag on a selected row, right drag on a selected row, left drag on an unselected row: no row moves.
Final head: a left swipe started, a second finger landed 0.25 s later: list at rest, nothing deleted (before / after).
Mac VM real-Safari runs on the final head (throwaway scripts, not committed).
Guard window (
gw.mjs):List scroll + typing, 10k notes (
pm.mjs):Head
9401f7134, iOS 27 simulator, cold + signed in + Settings never opened. Note opened directly by/notes/<id>on a fresh origin; ~7 s later a real 2.5× pinch: nothing moves (before / after).Same, All-notes list (fresh origin, Settings never opened): after the 2.5× pinch nothing moves.
Same, as the Home Screen app (added from the note URL, launched standalone on a fresh origin, Settings never opened; it opens on the daily note, the app's start route): before / after a 2.5× pinch, nothing moves.
Merged into dev (U1): specs in four projects, iOS 27 simulator proof (cold, signed in, Settings never opened: note, list, Home Screen app), Mac Safari scroll/typing flat, first paint −63 B JS / −30 B CSS vs dev. Checked the simulator shots at 2×.
Pull request closed