MolinoPro

README

Master Codebase Guidebook
Markdown + HTML Dev-Docs Renderer - Frontend Client Module

Default Index
Open README.md
Root: README.mdarchive
Milestones
                ┌───────────────────────────────┐
                │            Client             │
                │ (Next.js App Router UI layer) │
                └───────────────┬───────────────┘
                                │
                                │ Edit mode (unlimited content)
                                │  - TopSurfaceRegion edits Document2Data
                                │  - NO pagination during typing
                                ▼
                     ┌─────────────────────┐
                     │   FolioBinder.tsx   │  (client orchestrator)
                     │  - queueSave()      │  (doc save only)
                     │  - NO paginate here │
                     └───────────┬─────────┘
                                 │
                                 │ Edit → View boundary (or manual toolbar)
                                 │  (single trigger)
                                 ▼
                ┌──────────────────────────────────┐
                │ Server Action (only paginator)    │
                │ commitPaginationToDocument.ts     │
                └───────────────┬──────────────────┘
                                │
                                │ 1) Load ProjectDocument + head DocumentPage
                                │ 2) Snapshot surface from Document2Data
                                ▼
                  ┌──────────────────────────┐
                  │ buildPageSurfaceFrom...  │  (pure-ish builder)
                  │ Document2Data → PageSurface
                  └───────────────┬──────────┘
                                  │
                                  │ 3) Pure pagination (no Prisma)
                                  ▼
                  ┌──────────────────────────┐
                  │ paginateSurface()         │  (pure reducer)
                  │ PageSurface → PageSurface[]
                  └───────────────┬──────────┘
                                  │
                                  │ 4) Transaction (atomic)
                                  ▼
            ┌──────────────────────────────────────────┐
            │ prisma.$transaction                       │
            │  - update head DocumentPage.surface       │
            │  - delete downstream pages (order > head) │
            │  - create overflow pages                  │
            │  - crop Document2Data AFTER persistence   │
            └──────────────────────────────────────────┘
                                  │
                                  │ revalidatePath(`/documents/${id}`)
                                  ▼
                ┌──────────────────────────────────┐
                │ Server re-render (Suspense)       │
                │ Fetch doc + documentPages + VMs   │
                └───────────────┬──────────────────┘
                                │
                                ▼
                  ┌──────────────────────────┐
                  │ PaperStack.tsx           │
                  │  - engine pages (page 1) │
                  │  - wagon pages (page N)  │
                  └───────────────┬──────────┘
                                  │
                                  ▼
         ┌────────────────────────────────────────────┐
         │ WagonPage.tsx (render-only)                │
         │  - renderPageSurface(surface)              │
         │  - no buttons, no edits, no pagination     │
         └────────────────────────────────────────────┘
		 
		 
	

Below is the locked execution plan and the exact sequence. Nothing outside this order should be touched until its step is complete.

🔒 ARCHITECTURE STATUS: LOCKED

The following principles are now non-negotiable: • Only one paginator: commitPaginationToDocument • No client pagination • No autosave-triggered pagination • No per-page mutation outside server actions • Page 1 edits → snapshot → server → pages • Wagon pages are render-only

We will now refactor file-by-file, not layer-by-layer, to keep the system runnable at every step.

🧭 EXECUTION STRATEGY

Mode • One file per step • Each step must: • compile • run • preserve behavior (or intentionally simplify)

Order • Server authority first • Then reducer purity • Then rendering • Then triggers • UI last

✅ STEP SEQUENCE (LOCKED)

STEP 1 — Server Authority (CORE)

File

app/documents/components/folio-pages/actions/commitPaginationToDocument.ts

Goal

Make this the only pagination mechanism.

Actions • Verify: • Uses buildPageSurfaceFromDocument • Uses paginateSurface • Runs inside single transaction • Remove: • Any assumptions about client pagination • Ensure: • Page 1 rewritten • Downstream pages deleted • Overflow pages created • Document2Data cropped only after success

🔒 After this step:

Pagination authority is centralized and stable.

STEP 2 — Pure Reducer Cleanup

File

app/documents/components/folio-pages/paginationReducer.ts

Goal

Make it 100% pure and surface-only.

Actions • Replace all Prisma types with:

PageSurface["sections"][number]

•	Remove:
•	DocumentPage
•	Section
•	consumedSectionIds
•	Final return:

{ pages: PageSurface[] }

🔒 After this step:

Pagination logic is reusable, testable, deterministic.

STEP 3 — Surface Rendering (NEW FILE)

File (new)

app/documents/components/folio-pages/renderPageSurface.tsx

Goal

Single renderer for all wagon pages.

Responsibilities • Render PageSurface.sections • No context writes • No edit mode • No pagination logic

🔒 After this step:

Wagon pages have a clean, isolated renderer.

STEP 4 — WagonPage Simplification

File

app/documents/components/folio-pages/WagonPage.tsx

Goal

Make WagonPage render-only.

Actions • Remove: • buttons • pagination calls • Render: • optional header/footer shell • renderPageSurface(surface)

🔒 After this step:

Wagon pages become passive print surfaces.

STEP 5 — Disable Legacy Paths

Files • savePageSurfaceAndPaginate.ts • useAutoPaginateOnMount

Actions • Stop importing • Stop calling • Leave file if needed, but unused

🔒 After this step:

No ghost pagination paths remain.

STEP 6 — Edit → View Trigger (Client)

File (new or existing)

usePaginateOnEditExit.ts

Goal

Trigger pagination once, at the correct lifecycle moment.

Behavior • When edit mode → view mode: • call commitPaginationToDocument • Never during typing • Never during autosave

🔒 After this step:

The “washing machine cycle” is real and controlled.

STEP 7 — Toolbar Wiring

File

DocumentEditDock.tsx

Goal

Toolbar is the only manual trigger.

Actions • Keep onPaginateWagon • No inline buttons anywhere else • No pagination logic in document body

🔒 After this step:

UI respects document purity.

🧼 FINAL SYSTEM BEHAVIOR (CONFIRMED) • Edit mode: • unlimited text • no cropping • View mode: • renders only allowed rows • Pagination: • snapshot → split → persist → crop • Existing pages: • always pushed down • never overwritten • No flicker • No loops • No race conditions

▶️ NEXT ACTION (YOU CHOOSE)

Reply with one number: • 1 → Start with commitPaginationToDocument.ts (recommended) • 2 → Start with paginationReducer.ts • 3 → Create renderPageSurface.tsx • 4 → WagonPage refactor • 5 → Edit→View trigger hook

We will proceed exactly one file at a time.

app/documents/components/ ├── folio/ # Page-1 engine (canonical doc) │ ├── FolioBinder.tsx # client orchestrator + doc save authority │ ├── PaperStack.tsx # renders engine pages + wagon pages (VM-driven) │ ├── FolioEngine.tsx # engine page renderer (flow units) │ ├── PaperDeskSurface.tsx # A4 shell (visual only) │ └── lib/ │ ├── initializers.ts # ensure defaults into Document2Data │ ├── buildBlocks.ts # persisted blocks → SectionType blocks │ ├── renderBlock.tsx # SectionType → Region renderer (page-1 only) │ └── flow/ # layout helpers only (NOT persistence) │ ├── normalizeFlow.ts │ ├── deriveFlowLayout.ts │ └── ... │ ├── folio-pages/ # Page-N persistence + pagination │ ├── types/ │ │ └── folio-pages.types.ts # PageSurface, PaginationInput/Result │ ├── paginationReducer.ts # PURE PageSurface → PageSurface[] │ ├── renderPageSurface.tsx # NEW: render-only surface renderer (no edits) │ ├── WagonPage.tsx # render-only wrapper + Paper shell │ ├── lib/ │ │ └── buildPageSurfaceFromDocument.ts # Document2Data → PageSurface snapshot │ └── actions/ │ ├── commitPaginationToDocument.ts # ONLY paginator (Prisma transaction) │ └── (legacy) savePageSurfaceAndPaginate.ts # to be disabled/unreferenced │ ├── regions/ # UI sections (page-1 editor + view) │ ├── TopSurfaceRegion.tsx # edits Document2Data.topSurfaceSection │ ├── HeaderRegion.tsx / BannerRegion.tsx ... │ └── ... │ ├── sections/ │ ├── Default_SectionRegistry.tsx # SectionType registry (page-1) │ └── SectionShell.tsx │ └── doc-toolbar/ └── DocumentEditDock.tsx # manual paginate trigger only (no inline buttons)

Understood — and you’re aligned with the correct model.

What you are asking for is render parity, not editor parity.

Below is the precise, clean formulation of what you want, and how to do it without introducing a second interpretation layer.

Core requirement (re-stated precisely)

The on-screen page view and the exported (PDF / download) view must be the same rendered HTML. No re-parsing, no “note editor interpretation”, no alternate formatting pass.

This is exactly the right requirement for a document system.

Correct separation of concerns (locked)

  1. Editor (authoring only) • AppleNotesEditor • Double-click to edit • Produces text content (markdown-ish) • Exists only while editing

It is not part of: • pagination • rendering • export • print

  1. Renderer (single source of truth)

You already have this: • DocTextParser • renderFlowUnit • section components (DocSection_Surface, etc.) • FolioEngine

This renderer: • consumes stored content • outputs final HTML • is deterministic

This renderer is what must be reused everywhere.

  1. Pagination (pure, mechanical) • paginationReducer • savePageSurfaceAndPaginate • produces page-local surfaces • no HTML, no editor, no React

Correct as-is.

The golden rule (this is the key)

Export must reuse the same React render tree as the screen view.

No secondary formatter. No XHTML reinterpretation. No markdown → PDF pipeline.

The correct export architecture for Folio

A. One render tree

You already have it:

<DocumentRoot> <PaperStack> <page-card page=1> <FolioEngine page=1> rendered HTML </FolioEngine> </page-card> <page-card page=2> <WagoPage page=2> rendered HTML </FolioEngine> </page-card> </PaperStack> </DocumentRoot>

This tree is the document.

B. Two modes, same tree

You only change CSS + flags, not components.

Screen mode • editing enabled • shadows • scroll • toolbars

Print / export mode • editing disabled • no shadows • fixed margins • page breaks enforced

Same JSX. Same renderer.

C. How to implement export correctly

  1. Add a render-only route

/documents/[id]/print

This route: • fetches Document2Data • fetches DocumentPage[] • renders PaperStack • sets canEdit = false • loads print CSS

  1. Use print CSS (not new components)

@media print { body { background: none; }

.paper-canvas { break-after: page; page-break-after: always; }

.editor-controls { display: none; } }

  1. Generate PDF via browser • Playwright / Puppeteer • Chromium print-to-PDF • zero reinterpretation

Result: pixel-identical output

About XHTML (now answered cleanly)

Your renderXhtml.ts idea is conceptually valid, but: • ❌ unnecessary for Chromium-based PDF • ❌ introduces failure modes • ❌ duplicates the render pipeline

When XHTML would be justified

Only if: • you export to XML-native tools • you require strict archival formats • you do non-browser layout engines

For Folio: not needed.

HTML is the canonical format.

Why this guarantees parity

Because: • Pagination happens before rendering • Rendering happens once • Export uses the same render • CSS handles media differences

No “export formatter” exists.

That is the key.

Final confirmation (answering your sentence directly)

“what I mostly need is the page version and the downloaded version to be the same”

✅ Yes — and the only correct way to guarantee that is: • single renderer • single HTML output • CSS-based mode switching • browser-native PDF

render a strict XHTML5 string before we hit Print → PDF.?