Skip to content

feat(check): warn when a line break cuts a Korean word in two - #7409

Open
wonjangcloud9 wants to merge 1 commit into
heygen-com:mainfrom
wonjangcloud9:feat/check-korean-word-split
Open

wonjangcloud9 wants to merge 1 commit into
heygen-com:mainfrom
wonjangcloud9:feat/check-korean-word-split

Conversation

@wonjangcloud9

Copy link
Copy Markdown
Contributor

What

check now reports text_word_split (warning) when a line break lands inside a Korean word: between two Hangul syllables, or between a Hangul particle and the Latin word or number it is attached to (HyperFrames|로). The fix hint asks for word-break: keep-all, or for more room when keep-all is already set. The captions reference gets one line telling agents to set keep-all on Korean captions, and the CLI docs describe the code.

Why

Korean separates words with spaces, but Chrome's default word-break: normal lets a line end between any two Hangul syllables. A wrapped Korean caption renders a word cut in half, and check passes it.

Repro: a 900 px wide, 64 px Noto Sans KR caption reading "구독자 여러분 안녕하세요 오늘은 한국어 자막이 어떻게 줄바꿈되는지 확인해보겠습니다" renders its second line as "한국어 자막이 어떻게 줄바꿈되는" and its third as "지 확인해보겠습니다": the word 줄바꿈되는지 ("whether it wraps") loses its last syllable to the next line. npx hyperframes check on main:

Layout
  ◇ 0 issues across 9 sample(s)

With this branch:

Layout
  ⚠ t=0.5, 0.833, 1.167, 1.5, 1.833, 2.167, 2.5, 2.833s text_word_split #c "구독자 여러분 안녕하세요 오늘은 한국어 자막이 어떻게 줄바꿈되는지 확인해보겠습니다" — A line break cuts a Korean word in two ("줄바꿈되는|지").
        Fix: Set word-break: keep-all on the text or a parent so Korean lines break only at spaces.
  0 error(s), 1 warning(s), 0 info(s)

After adding word-break: keep-all to the caption, the render keeps 줄바꿈되는지 whole at the start of line 3 and check --no-cache reports 0 layout issues.

Related work

No existing issue (searched for word-break, keep-all, Hangul, mid-word). #5885 is the Arabic counterpart: script-specific breakage that check and lint do not report. #3353 handled CJK in transcripts and deliberately left Hangul out because Korean uses spaces; the same reasoning applies here, which is why only Korean is checked and Chinese/Japanese line breaks between characters stay quiet.

How

  • layout-audit.browser.js: for each text element's direct text nodes that contain Hangul and span more than one line box, walk adjacent pairs of word characters where at least one is Hangul, measure each character with a Range, and report the first pair whose centres sit more than half a glyph apart vertically. Comparing centres instead of counting client rects keeps a same-line font-fallback fragment (a Latin face with Korean fallback) quiet, and a small caption tilt still reads as one line. Text without Hangul and single-line text nodes return before any per-character measuring; the scan stops at 2000 characters.
  • Severity warning, and the code joins PERSISTENCE_TIERED_CODES, so a one-sample transient (a typewriter reveal caught mid-word) demotes to info like the other text findings.
  • Scope: direct text nodes only. Components that split text into one inline-block span per syllable are not covered; happy to follow up if that is wanted.

Test plan

  • Unit tests added: happy-dom cases in layout-audit.browser.test.ts (split between syllables, Latin word and particle split, break at a space, no Hangul, keep-all already set). Turning the detector off fails the three positive cases.
  • Chromium cases in layout-audit.chromium.test.ts, real line breaking: syllable split, the same under rotate(-4deg), Latin word and particle split are reported; keep-all wrapping at spaces, Arial with Korean fallback wrapped at spaces, a single line, <br>, wrapped English, wrapped Japanese are not. Ran both layout audit files with PUPPETEER_EXECUTABLE_PATH set to chrome-headless-shell 152: 316/316 pass.
  • layoutAudit.test.ts, check.test.ts and checkBrowser* tests (298 pass); bun run lint, bun run lint:skills, typecheck and the pre-commit hooks pass.
  • Manual testing: rendered a 1080×1920 Korean caption composition with hyperframes render before and after keep-all and compared frames; ran check --no-cache on both (output above).
  • Documentation updated: docs/packages/cli.mdx, skills/hyperframes/references/captions.md.
  • Comments say why.

🤖 Generated with Claude Code

@mintlify

mintlify Bot commented Nov 8, 2026 •

Copy link
Copy Markdown

Preview approval required

This pull request comes from a fork, so Mintlify won't build a preview until someone with write access approves commit 17cffcf.

Approve preview

@miguel-heygen miguel-heygen left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good, thanks for the clear repro.

  • The detector only measures characters in text nodes that contain Hangul and span more than one line box, and it stops at 2000 characters, so the cost stays off non-Korean text.
  • Comparing glyph centres rather than counting client rects is the right call: same-line font-fallback fragments and small rotations stay quiet, and the Chromium cases cover both.
  • Joining PERSISTENCE_TIERED_CODES keeps typewriter reveals caught mid-word at info.
  • Docs and the captions reference line match the behaviour, and the skills manifest hash is in sync.

CI is green at 873e669. The one Windows engine lane failure (frameCapture-verifyArming.browser.test.ts timing out at 5000ms) is outside this change and passed on rerun.

Inline-block-per-syllable components can be a follow-up if anyone hits them.

@miguel-heygen miguel-heygen left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The code is good to go, and the approval at 873e669 still stands. The only thing blocking the merge is that main requires verified commit signatures, and the merge queue won't accept this PR until its commit is signed ("Commits must have verified signatures").

Could you sign the commit and force-push? With a GPG or SSH signing key added to your GitHub account:

git commit --amend -S --no-edit
git push --force-with-lease

Setup docs: https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits

Once the commit shows as "Verified", I'll look over the new head and queue it. Thanks for the fix!

auto-merge was automatically disabled November 9, 2026 12:25

Head branch was pushed to by a user without write access

@wonjangcloud9
wonjangcloud9 force-pushed the feat/check-korean-word-split branch from 873e669 to 6b4db7e Compare November 9, 2026 12:25
@wonjangcloud9

Copy link
Copy Markdown
Contributor Author

Thanks for the review! I signed the commit with an SSH signing key and force-pushed: the new head 6b4db7e shows as Verified, and its tree is identical to 873e669 (no code changes).

@wonjangcloud9
wonjangcloud9 force-pushed the feat/check-korean-word-split branch from 6b4db7e to 05bc2f4 Compare November 11, 2026 16:22
@wonjangcloud9

Copy link
Copy Markdown
Contributor Author

Rebased onto main (ffb13c3) to clear the conflict, which was only in the generated skills-manifest.json (regenerated with gen-skills-manifest.ts). The new head 05bc2f4 is signed and the rest of the diff is unchanged. layout-audit.browser.test.ts, tsc --noEmit, oxlint and oxfmt pass on the rebased branch. @miguel-heygen, could you take another look when you have a moment? The earlier change request was only about the commit signature.

Korean puts spaces between words, but under the default `word-break: normal`
Chrome may end a line between any two Hangul syllables, so a wrapped caption
renders "줄바꿈되는 / 지" and `check` passed it with 0 layout issues.

The layout audit now reports `text_word_split` (warning) when two adjacent
characters of a Korean word sit on different lines: two Hangul syllables, or a
Hangul particle and the Latin word or number it is attached to. The fix hint
asks for `word-break: keep-all`, or for more room when keep-all is already set.
Persistence-tiered like the other text findings, so a one-sample transient
demotes to info.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@wonjangcloud9
wonjangcloud9 force-pushed the feat/check-korean-word-split branch from 05bc2f4 to 17cffcf Compare November 14, 2026 04:08
@wonjangcloud9

wonjangcloud9 commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor Author

Rebased onto main (0f008f94). The only conflict was the generated skills-manifest.json again, regenerated with gen-skills-manifest.ts; the rest of the diff is unchanged. The new head 17cffcfd is signed (Verified), and layout-audit.browser.test.ts + layout-audit.chromium.test.ts (317 tests), tsc --noEmit for the CLI, oxlint and oxfmt pass on it.

That should cover the commit-signing request from the Nov 8 review — could you take another look when you have a moment? Thanks!

This branch has not been deployed

No deployments
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.

2 participants