Skip to content

Add manual page break (Phase 4.2)#109

Merged
hackerwins merged 11 commits into
mainfrom
feature/page-break
Apr 8, 2026
Merged

Add manual page break (Phase 4.2)#109
hackerwins merged 11 commits into
mainfrom
feature/page-break

Conversation

@hackerwins

@hackerwins hackerwins commented Apr 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Add page-break BlockType — content-free block following the horizontal-rule pattern
  • Ctrl+Enter (Cmd+Enter on Mac) inserts a page break at cursor position
  • Pagination engine forces a new page after page-break blocks
  • Canvas rendering: Google Docs-style dashed line with centered "Page break" label
  • Yorkie serialization support for collaborative editing

Changes

  • Data model: 'page-break' added to BlockType union, createBlock produces empty inlines
  • Doc model: splitBlock, deleteBackward, setBlockType handle page-break as non-editable
  • Layout: Fixed 20px height block with no text runs
  • Pagination: startNewPage() called after page-break line placement
  • Rendering: Dashed lines flanking centered label, textBaseline: 'middle' alignment
  • Input: Ctrl+Enter handler, input on page-break creates paragraph via ensureEditableBlock
  • Yorkie: Empty-inlines deserialization check for page-break

Test plan

  • Unit tests for createBlock, Doc model operations (split/delete/setType)
  • Unit tests for layout (fixed height, no runs)
  • Unit tests for pagination (force split, consecutive breaks, break at start)
  • pnpm verify:fast passes
  • Manual: Ctrl+Enter inserts page break, content moves to next page
  • Manual: Backspace deletes page-break block
  • Manual: Typing on page-break creates new paragraph

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features
    • Introduced page break functionality to control document pagination
    • Insert page breaks using Ctrl+Enter (or Cmd+Enter on Mac) to force subsequent content onto a new page
    • Page breaks display as dashed lines with a centered label and are non-editable, deletable only via backspace

hackerwins and others added 11 commits April 8, 2026 20:15
Phase 4.2 is no longer a non-goal. Document the data model,
pagination behavior, rendering, input handling, and Yorkie
serialization for the page-break block type.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Content-free block type following the horizontal-rule pattern.
createBlock('page-break') produces empty inlines.
Add page-break to splitBlock, deleteBackward, and setBlockType
following the same non-editable pattern as horizontal-rule.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Same 20px height as horizontal-rule, no text runs.
Page-break line is placed on the current page, then startNewPage()
is called so subsequent content begins on a fresh page.
Google Docs style: gray dashed lines flanking a centered 'Page break'
text label. Uses setLineDash([4,4]) with #ccc stroke color.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Ctrl+Enter (Cmd+Enter on Mac) splits the block at cursor and inserts
a page-break block between the halves. Text input is blocked on
page-break blocks, same as horizontal-rule.
When reading back from Yorkie Tree, page-break blocks should
produce empty inlines like horizontal-rule, not a default inline.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
When the cursor is on a page-break block and the user types,
call ensureEditableBlock() to split and create a paragraph
rather than silently discarding the input. The old return-early
caused textarea contents to leak into adjacent blocks.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Set textBaseline to 'middle' so the label sits centered on the
dashed line instead of floating above it.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@coderabbitai

coderabbitai Bot commented Apr 8, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR implements the 'page-break' block type as a non-editable control block that forces pagination to start on a new page. The feature includes design documentation, an implementation plan, and code changes across the document model, layout/rendering, pagination, editor input handling, and Yorkie serialization. The implementation mirrors the existing 'horizontal-rule' pattern throughout.

Changes

Cohort / File(s) Summary
Design & Implementation Planning
docs/design/docs/docs-pagination.md, docs/tasks/active/20260325-docs-wordprocessor-todo.md, docs/tasks/active/20260408-page-break-todo.md
Added design specification for page-break block type, marked Phase 4.2 complete, and documented step-by-step implementation plan with verification steps.
Block Type Model
packages/docs/src/model/types.ts
Extended BlockType union with 'page-break' discriminant and updated createBlock() to initialize page-break blocks with empty inlines array.
Document Operations
packages/docs/src/model/document.ts
Extended deleteBackward, splitBlock (top-level and cell-internal), and setBlockType to handle page-break equivalently to horizontal-rule: prevent splitting, delete on backspace, and clear inlines on type conversion.
Layout & Rendering
packages/docs/src/view/layout.ts, packages/docs/src/view/doc-canvas.ts
Added fixed-height layout treatment for page-break (HR_HEIGHT = 20 with zero text runs); implemented canvas rendering as centered "Page break" label with dashed horizontal lines on both sides.
Pagination
packages/docs/src/view/pagination.ts
Modified paginateLayout() to call startNewPage() when encountering page-break blocks, ensuring subsequent content starts on a new page; omit marginBottom spacing for page-break blocks.
Editor Input Handling
packages/docs/src/view/text-editor.ts
Updated editable-block logic to treat page-break as non-editable; mapped Ctrl/Meta+Enter to new handlePageBreak() method that splits the block and inserts a page-break block between halves; text input now calls ensureEditableBlock() for page-break blocks.
Data Serialization
packages/frontend/src/app/docs/yorkie-doc-store.ts
Updated treeNodeToBlock() to return empty inlines array for page-break blocks (matching horizontal-rule behavior) instead of default paragraph-style inline.
Test Coverage
packages/docs/test/model/document.test.ts, packages/docs/test/model/types.test.ts, packages/docs/test/view/layout.test.ts, packages/docs/test/view/pagination.test.ts
Added unit tests validating block splitting/deletion behavior, fixed-height layout rendering (20px), page-break initialization, and pagination splitting across three consecutive page-break scenarios.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Editor as text-editor
    participant DocModel as Doc Model
    participant Layout as computeLayout
    participant Pagination as paginateLayout
    participant Canvas as doc-canvas

    User->>Editor: Press Ctrl+Enter
    Editor->>Editor: handlePageBreak()
    Editor->>DocModel: splitBlock(currentBlock, cursor)
    DocModel->>DocModel: Create paragraph after split
    Editor->>DocModel: insertBlockAt(pageBreakBlock)
    DocModel->>DocModel: Insert 'page-break' between halves
    Editor->>Layout: computeLayout(blocks)
    Layout->>Layout: Detect 'page-break' type
    Layout->>Layout: Assign HR_HEIGHT=20, zero runs
    Layout->>Pagination: Process layout blocks
    Pagination->>Pagination: Encounter 'page-break'
    Pagination->>Pagination: startNewPage()
    Pagination->>Canvas: Supply paginated layout
    Canvas->>Canvas: Detect 'page-break' block
    Canvas->>Canvas: Draw "Page break" label
    Canvas->>Canvas: Draw dashed lines left & right
    Canvas->>User: Render page-break indicator
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 hops through fresh pages
Ctrl+Enter splits my burrow neat,
A dashed line marks the page's beat,
Like horizontal rules of lore,
Now page-breaks open new doors!
✨📄

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main feature being added: manual page break support for Phase 4.2, which is the primary objective of the entire pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/page-break

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

Verification: verify:self

Result: ✅ PASS in 111.3s

Lane Status Duration
sheets:build ✅ pass 13.6s
docs:build ✅ pass 6.6s
verify:fast ✅ pass 55.0s
frontend:build ✅ pass 15.1s
verify:frontend:chunks ✅ pass 0.3s
backend:build ✅ pass 4.3s
cli:build ✅ pass 1.8s
verify:entropy ✅ pass 14.5s

Verification: verify:integration

Result: ✅ PASS

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (3)
packages/docs/src/view/doc-canvas.ts (1)

173-184: Consider extracting hardcoded styling values to Theme constants.

The font ('9px Arial'), colors ('#aaa', '#ccc'), and dash pattern ([4, 4]) are hardcoded. For consistency with other rendering code that uses Theme.* constants, these could be centralized.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/docs/src/view/doc-canvas.ts` around lines 173 - 184, Extract the
hardcoded styling in the Page break render block into Theme constants and use
them instead of literals: replace this.ctx.font = '9px Arial' with
Theme.pageBreakFont, this.ctx.fillStyle = Theme.pageBreakTextColor,
this.ctx.strokeStyle = Theme.pageBreakLineColor and this.ctx.setLineDash([4,4])
with Theme.pageBreakDash (an array); also replace the hardcoded padding added to
labelWidth (+16) with Theme.pageBreakLabelPadding and ensure the label string
'Page break' can be referenced as Theme.pageBreakLabel so all values (font, text
color, line color, dash pattern, label padding, label text) are centralized for
consistency in the DocCanvas page break rendering code.
docs/design/docs/docs-pagination.md (1)

332-396: Comprehensive design documentation for Manual Page Break.

The new section thoroughly documents all aspects of the page-break feature:

  • Data model following the horizontal-rule pattern
  • Pagination engine behavior with clear algorithm description
  • Layout and rendering specifications
  • Input handling including keyboard shortcuts
  • Document model operations
  • Yorkie serialization approach

One minor improvement: the pseudocode block at lines 362-365 could specify a language for consistency.

,

Optional: Add language to code block
-```
+```typescript
 const PAGE_BREAK_HEIGHT = 20;
 lines = [{ runs: [], y: 0, height: PAGE_BREAK_HEIGHT, width: availableWidth }];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/design/docs/docs-pagination.md` around lines 332 - 396, The code fence
for the pseudocode in the computeLayout section is missing a language specifier;
update the triple-backtick block that defines PAGE_BREAK_HEIGHT and lines (the
snippet containing "const PAGE_BREAK_HEIGHT = 20;" and "lines = [{ runs: [], y:
0, height: PAGE_BREAK_HEIGHT, width: availableWidth }];") to use ```typescript
so syntax highlighting and consistency match the rest of the docs.
docs/tasks/active/20260408-page-break-todo.md (1)

11-13: Minor: Heading level jumps from h1 to h3.

The document structure skips h2 and goes directly to h3 for "Task 1". Consider using h2 (##) for task headers for proper document hierarchy, or add a "## Tasks" section header before the task list.

Suggested fix
 ---
 
+## Tasks
+
 ### Task 1: Add `page-break` to data model and createBlock

Or change all ### task headers to ##.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/tasks/active/20260408-page-break-todo.md` around lines 11 - 13, The
document jumps from h1 to h3 for the task header "Task 1: Add `page-break` to
data model and createBlock"; update the heading hierarchy by either changing the
task header "### Task 1: Add `page-break` to data model and createBlock" to an
h2 (replace ### with ##) or insert a parent "## Tasks" section above the task
list so all task entries remain as h3—pick one approach and apply it
consistently for other task headers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@docs/design/docs/docs-pagination.md`:
- Around line 332-396: The code fence for the pseudocode in the computeLayout
section is missing a language specifier; update the triple-backtick block that
defines PAGE_BREAK_HEIGHT and lines (the snippet containing "const
PAGE_BREAK_HEIGHT = 20;" and "lines = [{ runs: [], y: 0, height:
PAGE_BREAK_HEIGHT, width: availableWidth }];") to use ```typescript so syntax
highlighting and consistency match the rest of the docs.

In `@docs/tasks/active/20260408-page-break-todo.md`:
- Around line 11-13: The document jumps from h1 to h3 for the task header "Task
1: Add `page-break` to data model and createBlock"; update the heading hierarchy
by either changing the task header "### Task 1: Add `page-break` to data model
and createBlock" to an h2 (replace ### with ##) or insert a parent "## Tasks"
section above the task list so all task entries remain as h3—pick one approach
and apply it consistently for other task headers.

In `@packages/docs/src/view/doc-canvas.ts`:
- Around line 173-184: Extract the hardcoded styling in the Page break render
block into Theme constants and use them instead of literals: replace
this.ctx.font = '9px Arial' with Theme.pageBreakFont, this.ctx.fillStyle =
Theme.pageBreakTextColor, this.ctx.strokeStyle = Theme.pageBreakLineColor and
this.ctx.setLineDash([4,4]) with Theme.pageBreakDash (an array); also replace
the hardcoded padding added to labelWidth (+16) with Theme.pageBreakLabelPadding
and ensure the label string 'Page break' can be referenced as
Theme.pageBreakLabel so all values (font, text color, line color, dash pattern,
label padding, label text) are centralized for consistency in the DocCanvas page
break rendering code.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6941c5e9-db8c-46d9-8a4d-794ee5d3b9a1

📥 Commits

Reviewing files that changed from the base of the PR and between efc5355 and a1be7f6.

📒 Files selected for processing (14)
  • docs/design/docs/docs-pagination.md
  • docs/tasks/active/20260325-docs-wordprocessor-todo.md
  • docs/tasks/active/20260408-page-break-todo.md
  • packages/docs/src/model/document.ts
  • packages/docs/src/model/types.ts
  • packages/docs/src/view/doc-canvas.ts
  • packages/docs/src/view/layout.ts
  • packages/docs/src/view/pagination.ts
  • packages/docs/src/view/text-editor.ts
  • packages/docs/test/model/document.test.ts
  • packages/docs/test/model/types.test.ts
  • packages/docs/test/view/layout.test.ts
  • packages/docs/test/view/pagination.test.ts
  • packages/frontend/src/app/docs/yorkie-doc-store.ts

@hackerwins
hackerwins merged commit 8d58764 into main Apr 8, 2026
3 checks passed
@hackerwins
hackerwins deleted the feature/page-break branch April 8, 2026 12:10
@hackerwins hackerwins mentioned this pull request Apr 11, 2026
4 tasks
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