Skip to content

Extract packages/office-preview — a standalone React package for DOCX/XLSX/PPTX rendering #545

Description

@phodal

Problem

src/app/debug/office-wasm-poc has grown into a single debug surface that eagerly wires DOCX, XLSX, and PPTX preview logic together. This makes the office preview stack harder to evolve: format-specific work leaks into shared utilities, renderer files keep growing, and adding new fidelity work now increases coupling instead of improving reuse.

Walnut shows a cleaner modular shape for the same problem space: format-scoped preview panels, separately loaded protocol/model bundles, and a narrow shell that only decides which surface to mount.

Context

  • Current behavior:
    • page-client.tsx statically imports WordPreview, SpreadsheetPreview, and PresentationPreview, so the debug entry owns all three preview surfaces at once.
    • Dynamic imports are currently used mainly for decoder/runtime loading, not for preview UI boundaries.
    • Shared code is concentrated in office-preview-utils.ts (795 lines) and consumed across document, spreadsheet, and presentation flows.
    • Several files are already acting as subsystem roots rather than focused modules:
      • word-preview.tsx (1634 lines)
      • spreadsheet-preview.tsx (1594 lines)
      • spreadsheet-charts.tsx (1569 lines)
      • spreadsheet-conditional-formula.ts (1423 lines)
      • presentation-renderer.ts (1356 lines)
      • spreadsheet-conditional-visuals.ts (1209 lines)
    • The current preview layer still leans heavily on Record<string, unknown> / unknown traversal, which weakens format boundaries and makes shared-vs-format ownership fuzzy.
  • Desired behavior:
    • Keep a thin office preview shell that selects artifact kind and reader mode.
    • Split UI and rendering by format (document, spreadsheet, presentation) with lazy boundaries.
    • Extract explicit shared layers for protocol adapters, rendering primitives, and common UI/utilities.
    • Make office-wasm-poc a composition harness rather than the place where all office behavior lives.

Related History

  • Local analysis found after searching docs/issues/:
    • docs/issues/2026-05-01-office-document-viewer-wasm-reader.md
    • docs/issues/2026-05-05-office-wasm-docx-feature-checklist.md
    • docs/issues/2026-05-05-office-wasm-xlsx-feature-checklist.md
    • docs/issues/2026-05-05-office-wasm-pptx-feature-checklist.md
  • Walnut modularization evidence from tmp/codex-app-analysis:
    • separate bundles for protocol/model layers: proto-*.js, workbook-*.js, presentation-*.js
    • separate format bundles: document-*.js, spreadsheet-*.js, presentation-*.js
    • separate preview/panel bundles such as docx-preview-panel-*.js, which lazily imports docx-preview-*.js
  • Related GitHub issue history: none found after searching repository issues for office wasm, Walnut, office preview, and office document viewer.

Related PR File Context

None found. The repository search did not return a related GitHub issue with linked pull requests for this refactor theme, so there is no prior PR diff context to summarize.

Proposed Approaches

Approach 1: In-place folderized refactor inside office-wasm-poc

Restructure the current debug surface into explicit subtrees such as:

  • office-wasm-poc/shell/*
  • office-wasm-poc/shared/*
  • office-wasm-poc/document/*
  • office-wasm-poc/spreadsheet/*
  • office-wasm-poc/presentation/*

Add lazy loading at the preview-panel boundary so the selected artifact kind loads its own UI/rendering subtree.

Pros:

  • Lowest migration risk
  • Preserves the current debug route and tests
  • Makes ownership boundaries visible immediately

Cons:

  • Still leaves the long-term architecture inside a debug-only path
  • Shared abstractions may remain too local to office-wasm-poc
  • Risk of stopping halfway after file moves without stabilizing shared contracts

Estimated effort: Medium

Approach 2: Extract a reusable office preview module outside the debug route

Move stable shared behavior into a reusable package/module (for example under src/client/office-document-viewer/), leaving office-wasm-poc as a harness that mounts the shared office preview system.

Pros:

  • Best alignment with production reuse
  • Clarifies the difference between protocol/model logic and debug/demo scaffolding
  • Reduces the chance that future office work lands in the debug route by default

Cons:

  • Larger migration and review surface
  • Requires API design for cross-format shared contracts up front
  • More likely to touch app/runtime boundaries during the refactor

Estimated effort: Large

Approach 3: Hybrid staged refactor (format shells first, extraction second)

Stage 1:

  • introduce format-scoped entry modules and lazy preview loading
  • split office-preview-utils.ts into targeted shared modules
  • move renderer/domain code under format folders
  • keep office-wasm-poc as the host surface

Stage 2:

  • promote stable shared contracts and render primitives into reusable office-document-viewer modules
  • keep the debug route as a thin harness over the extracted system

Pros:

  • Captures Walnut's modular benefits quickly
  • Limits risk while creating a clear path toward production reuse
  • Lets tests and characterization checks move with each format incrementally

Cons:

  • Needs discipline to ensure Stage 2 actually happens
  • Temporary duplication may exist during the transition
  • Requires explicit boundary rules to avoid recreating another shared dumping ground

Estimated effort: Large

Recommendation

Start with Approach 3 (Hybrid staged refactor).

It matches the immediate problem best: the current pain is boundary collapse inside office-wasm-poc, but a full extraction in one step is likely too broad. A staged path lets us first enforce Walnut-like modular seams — format panels, lazy UI boundaries, isolated renderers, and explicit shared primitives — then lift the stable pieces into reusable viewer modules once those seams are proven.

The first stage should preserve these design constraints:

  1. page-client.tsx becomes a thin shell and stops statically owning all preview implementations.
  2. Format-specific UI/rendering code lives under format folders with clear ownership.
  3. Shared code is split by concern (protocol, rendering primitives, common hooks/utils, shared UI) rather than by convenience.
  4. Spreadsheet worker/canvas infrastructure remains isolated to spreadsheet modules instead of leaking into generic utilities.
  5. Characterization tests move with the modules so refactoring does not silently change preview behavior.

Out of Scope

  • Adding new DOCX/XLSX/PPTX feature fidelity during the refactor unless directly required to preserve behavior
  • Replacing the Walnut/Routa WASM reader protocol itself
  • Converging the entire office viewer into a final production package in one step
  • Redesigning the end-user office viewing UX outside the current debug/refactor scope

Labels

enhancement, refactor, area:frontend, ui, complexity:large

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions