Files
lefarcenandGitHub ad584ff901 fix(web): keep deck page counters in sync during keyboard paging (#5594)
* fix(web): keep deck page counters in sync during keyboard paging

Host-driven deck navigation (keyboard shortcuts and prev/next buttons)
used to mutate slide classes/transforms directly in the srcdoc bridge,
bypassing the artifact's own navigation script. Decks that render their
own page counter from an internal index never saw the move, so the
bottom page number stayed frozen on the first slide while the canvas
advanced. The bridge now prefers dispatching the navigation key to the
artifact's own keyboard handler and only falls back to direct DOM
mutation when no handler moves the deck.

The mirror gap: iframe-focused keyboard navigation on transform-track
decks mutates only the shared track element, which the bridge's
MutationObserver (slide attributes only) never saw, leaving the HOST
counter stale. The observer now watches the track's style too.

Generated-By: looper 0.0.0-dev (runner=worker, agent=claude-code)

* worker: 使用键盘快捷键翻页的时候,下面的页码不会变动

Generated-By: looper 0.0.0-dev (runner=worker, agent=claude-code)

* fix(web): wait out deferred deck key handlers before the bridge's direct-DOM fallback

Review follow-up for #5594: the artifact-key probe treated only a
synchronous index change as proof the artifact handled the key. Decks
that defer their keydown state update (requestAnimationFrame or
setTimeout) fell through to direct DOM mutation immediately AND received
the key twice — the window dispatch and the document re-dispatch both
reach a deferred window listener — so one host "next" advanced two
slides and desynced the artifact's internal index.

The probe is now asynchronous: dispatch to one target, then wait one
bounded mutation window (MutationObserver on class/style/hidden plus a
48ms cap) for the active index or track transform to change before
trying the next dispatch target or the direct-DOM fallback. Synchronous
handlers still resolve on a zero-latency fast path, and a responsive
dispatch target is cached so later keypresses skip re-probing.

Decks with no keyboard handling must not pay the probe wait: the key
path is gated on the artifact actually shipping keyboard navigation,
seeded from a source scan of the artifact html before any od bridge is
injected plus registration-time tracking through the existing
addEventListener patch — the same two-pronged pattern the od:slide
message-listener flag already uses. This preserves the pinned invariant
that the bridge-owned fallback stays synchronous for handler-less decks.

Regression fixtures: a deferred window-listening deck (red on the
synchronous probe with a visible double advance to slide 3) and
seed-scan assertions proving od-injected bridge scripts do not
contaminate the keyboard-navigation flag.

Generated-By: looper 0.0.0-dev (runner=worker, agent=claude-code)

* fix(web): catch external-script deck keyboard runtimes with a head-start registry hook

Review follow-up for #5594: detectArtifactKeyboardNavigation() only sees
inline HTML bytes, so decks that load their keyboard runtime via
<script src> (design-templates/html-ppt wires ../assets/runtime.js,
which registers an ArrowRight/ArrowLeft document keydown handler) were
classified handler-less. Their listeners also register before the deck
bridge executes at the end of <body>, so the bridge's own
addEventListener patches installed too late — the flag stayed false,
host-driven od:slide fell back to direct DOM mutation, and
artifact-owned counters / internal indices went stale again.

A tiny registry hook is now injected at the start of <head> — before any
artifact script — wrapping window/document addEventListener and flagging
keydown listeners that pass the shared navigation-key screen into
window.__odArtifactKeydownNavigation. The deck bridge reads that flag
lazily alongside its inline-source seed and its own late patches, so
registrations are observed no matter when they happen relative to the
bridge. The bridge marks its own top-of-file keydown listeners via
__odDeckBridgeOwnListenerInstall so the framework branch's key-token
source does not classify every framework deck as artifact-navigable.

The navigation-key screen is now a single shared snippet between the
hook and the bridge. Regression: a fixture that registers its keydown
handler before the bridge evaluates with no keydown token in the html
bytes (the script-src shape) — red on the previous commit (counter stays
1 while the canvas moves), green with the hook.

Generated-By: looper 0.0.0-dev (runner=worker, agent=claude-code)
2026-07-14 08:59:13 +00:00
..