Skip to content

fix(hyperframes): preserve zero data-duration instead of falling back to default (#110)#119

Open
JFJLL wants to merge 1 commit into
nexu-io:mainfrom
JFJLL:fix/issue-110
Open

fix(hyperframes): preserve zero data-duration instead of falling back to default (#110)#119
JFJLL wants to merge 1 commit into
nexu-io:mainfrom
JFJLL:fix/issue-110

Conversation

@JFJLL

@JFJLL JFJLL commented Jun 25, 2026

Copy link
Copy Markdown

Fix: Zero duration incorrectly treated as undefined

Fixes #110

Problem

In parseHyperframes() (next/src/lib/hyperframes.ts), the data-duration attribute was never correctly read from <section> elements due to two bugs:

  1. Missing capture group in openTag regexpick(/<section\b[^>]*>/i, m[0]) used a regex with no capture group, so pick() always returned undefined (since it returns m[1]). This meant extractAttr() received undefined and could never match any attributes.

  2. Falsy || operator — The original Number(extractAttr(openTag, "data-duration")) || undefined treated a duration value of 0 as falsy, replacing it with undefined and falling back to the default 3000ms.

Fix

- const openTag = pick(/<section\b[^>]*>/i, m[0]);
- const dataDur = Number(extractAttr(openTag, "data-duration")) || undefined;
+ const openTag = pick(/(<section\b[^>]*>)/i, m[0]);
+ const dataDurStr = extractAttr(openTag, "data-duration");
+ const dataDur = dataDurStr ? Number(dataDurStr) : undefined;
  • Added capture group (...) to the openTag regex so pick() returns the matched string
  • Replaced || undefined with an explicit empty-string check to preserve 0 as a valid duration

Tests

Added 4 vitest cases in next/src/lib/__tests__/hyperframes.test.ts:

  • data-duration="0" → duration is 0 (not 3000)
  • Missing data-duration → defaults to 3000
  • data-duration="5000" → duration is 5000
  • Mixed frames with zero, positive, and missing durations

… to default

Fixes nexu-io#110

Two bugs in parseHyperframes prevented data-duration attributes from being read:

1. The openTag extraction regex (/<section\b[^>]*>/i) lacked a capture group,
   causing pick() to always return undefined. Added a capture group so
   pick() correctly returns the matched open tag string.

2. The original  treated duration 0
   as falsy, replacing it with undefined (→ default 3000ms). Replaced with
   an explicit empty-string check: .

Added 4 vitest cases covering zero, missing, positive, and mixed durations.
@lefarcen
lefarcen requested a review from mrcfps June 25, 2026 02:09
@lefarcen lefarcen added size/S Small change: 20-99 changed lines risk/medium Medium risk change type/bugfix Bug fix labels Jun 25, 2026
@lefarcen

Copy link
Copy Markdown

Hey @JFJLL — heads-up: PR #118 is also open in this area. Both PRs touch next/src/lib/hyperframes.ts and add regression coverage around zero-duration handling, so it may be worth comparing approaches with @C1-BA-B1-F3 while maintainers decide which one lands.

@mrcfps mrcfps left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JFJLL thanks for tightening up the zero-duration handling here. I found one merge-safe edge case in the new conversion path where malformed duration text can now propagate as NaN; the core data-duration="0" fix and regression coverage are otherwise pointed in the right direction.

🔁 Powered by Looper · runner=reviewer · agent=codex · An autonomous AI dev team for your GitHub repos.

const dataDur = Number(extractAttr(openTag, "data-duration")) || undefined;
const openTag = pick(/(<section\b[^>]*>)/i, m[0]);
const dataDurStr = extractAttr(openTag, "data-duration");
const dataDur = dataDurStr ? Number(dataDurStr) : undefined;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes the "0" case, but it also changes malformed non-empty values from falling through to the marker/default to winning the ?? chain as NaN. For example, data-duration="fast" now makes Number(dataDurStr) return NaN; because NaN is not nullish, line 151 selects it and exportRemotionZip() later passes that to msToFrames(f.duration), which can produce invalid Remotion output instead of using the inline marker or 3000 fallback. Please keep the explicit zero preservation while normalizing non-finite parses back to undefined, e.g. parse once and use Number.isFinite(parsed) ? parsed : undefined, with a small regression case for an invalid data-duration falling back.

🔁 Powered by Looper · runner=reviewer · agent=codex · An autonomous AI dev team for your GitHub repos.

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

Labels

risk/medium Medium risk change size/S Small change: 20-99 changed lines type/bugfix Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🤖 🐛 Zero duration is incorrectly treated as undefined

3 participants