Skip to content

Add wafflebase slides import CLI + backend writer#245

Merged
hackerwins merged 4 commits into
mainfrom
feat/slides-cli-import
May 16, 2026
Merged

Add wafflebase slides import CLI + backend writer#245
hackerwins merged 4 commits into
mainfrom
feat/slides-cli-import

Conversation

@hackerwins

@hackerwins hackerwins commented May 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

Implements Task 6 of docs/tasks/active/20260515-pptx-import-todo.md — the deferred follow-up to #243. Users can now import a .pptx via the CLI in addition to the deck list UI:

wafflebase slides import deck.pptx --title "My Deck"
  • packages/backend/src/yorkie/slides-tree.tswriteSlidesRoot / readSlidesRoot for the slides Yorkie root (plain JSON bulk-assign, matches slides-view.tsx's pending-import doc.update).
  • packages/backend/src/api/v1/docs-content.controller.tsPUT/GET /documents/:id/content now dispatches by persisted document type. Slides → writeSlidesRoot; docs → existing Tree CRDT path. A sniffBodyShape helper picks the validator arm; mismatched shape/type returns 400 before any Yorkie attach.
  • packages/cli/src/slides/{pptx-import,import}.ts — thin importPptx wrapper (base64 inline uploader, matches docs import) and orchestrator mirroring runDocsImport. A parser injection point keeps unit tests deterministic without real .pptx bytes.
  • HttpClient gets putSlidesContent and a widened createDocument type union.
  • Per-slide + per-element shape validation (id, type, frame) so corrupted decks fail with a useful 400 instead of crashing the renderer later.

Test plan

  • pnpm verify:fast green
  • New unit specs for the writer round-trip, controller dispatch + validation arms (27 cases), orchestrator success / failure / dry-run / replace flows (13 cases), pptx-import wrapper (4 cases)
  • pnpm verify:integration:docker — e2e slides-pptx-import.e2e-spec.ts gated by RUN_DB_INTEGRATION_TESTS + RUN_YORKIE_INTEGRATION_TESTS; synthesises a minimal .pptx inline so no binary is committed
  • Local smoke: wafflebase slides import deck.pptx against a dev backend; confirm GET /documents/:id/content returns the parsed deck

Known limitations

  • Image upload follows the docs CLI pattern (base64 data: URLs). Real /api/v1/.../images upload is deferred to a follow-up.
  • runSlidesImport (and the docs CLI it mirrors) does not delete the placeholder document if the PUT fails, leaving an empty deck in the workspace. Pre-existing pattern; track as a follow-up that fixes both flows.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added slides document support across CLI, client, and Content API, including PPTX import, create/replace flows, dry-run, and confirmation handling.
  • Bug Fixes / Validation
    • Content endpoints now validate payload shape against persisted document type and return 400 for mismatches; added detailed slides payload validation.
  • Tests
    • New unit, integration, and e2e tests covering PPTX import, CLI flows, Yorkie read/write round-trips, and granular slides-content validation.
  • Documentation
    • Expanded import and backend content handling notes.

Review Change Stack

Implements Task 6 of docs/tasks/active/20260515-pptx-import-todo.md —
the deferred follow-up to PR #243. Users can now import a .pptx via
the CLI in addition to the deck list UI:

  wafflebase slides import deck.pptx --title "My Deck"

Why a parallel writer instead of reusing the frontend's pending-import
flow: the CLI never attaches a Yorkie client, so the parsed deck has
to be persisted through the backend. The existing docs content
endpoint already had this shape (REST in, Yorkie write), so the
slides side mirrors it.

Backend
- `packages/backend/src/yorkie/slides-tree.ts` — `writeSlidesRoot` and
  `readSlidesRoot` for the slides Yorkie root. Plain JSON bulk-assign
  (slides body is not a Tree CRDT — that experiment was reverted in
  Phase 5a), matching `slides-view.tsx`'s `doc.update` pattern.
- `packages/backend/src/api/v1/docs-content.controller.ts` —
  `PUT/GET /documents/:id/content` now dispatches on the persisted
  document type. Slides bodies route to `writeSlidesRoot`, docs to
  the existing tree writer. A `sniffBodyShape` helper picks the
  validator arm via `Array.isArray(body.slides|blocks)`; mismatched
  shape/type returns 400 before any Yorkie attach.
- Validation includes per-slide and per-element shape checks (`id`,
  `type`, `frame`) so corrupted decks fail with a useful 400 instead
  of persisting and crashing the renderer later.

CLI
- `packages/cli/src/slides/pptx-import.ts` — thin wrapper around
  `@wafflebase/slides/node`'s `importPptx`. Defaults to a base64
  inline uploader (matches `docs import` — keeps the import
  round-trippable without a separate `/images` upload pass).
- `packages/cli/src/slides/import.ts` — orchestrator mirroring
  `runDocsImport`, with a `parser` injection point so unit tests
  avoid building real .pptx bytes per case.
- `packages/cli/src/commands/slides.ts` + bin wiring.
- HttpClient gets `putSlidesContent` and a widened `createDocument`
  type union.

Testing
- Unit specs cover the writer round-trip, the controller's dispatch
  + validation arms (docs + slides), and the orchestrator's success /
  failure / dry-run / replace flows.
- `packages/backend/test/slides-pptx-import.e2e-spec.ts` exercises
  the full CLI → API → Yorkie path. Gated by the existing
  `RUN_DB_INTEGRATION_TESTS` + `RUN_YORKIE_INTEGRATION_TESTS` env
  switches; the synthetic .pptx is generated inline via the slides
  fixture builder so no binary needs to be committed.

`pnpm verify:fast` green.

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

coderabbitai Bot commented May 16, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@hackerwins has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 43 minutes and 4 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6146e7b4-e685-4e30-af14-56969d9d5821

📥 Commits

Reviewing files that changed from the base of the PR and between a8773d4 and 081a486.

📒 Files selected for processing (1)
  • packages/cli/src/slides/import.ts
📝 Walkthrough

Walkthrough

Adds end-to-end PPTX slide-deck import: Yorkie slides serialization, dual-type content API with defensive body-shape validation, CLI PPTX parsing and create/replace orchestration, CLI wiring/HTTP client updates, integration tests, and documentation/type export updates.

Changes

Slides PPTX Import Feature

Layer / File(s) Summary
Yorkie Slides Root Serialization
packages/backend/src/yorkie/slides-tree.ts, packages/backend/src/yorkie/slides-tree.spec.ts
SlidesYorkieRoot shape, readSlidesRoot normalizes to SlidesDocument with defaults, writeSlidesRoot destructively replaces root, and tests cover round-trip, replacement, and empty-root defaults.
Backend Content API - Dual Type Support
packages/backend/src/api/v1/docs-content.controller.ts, packages/backend/src/api/v1/docs-content.controller.spec.ts
GET/PUT /api/v1/.../content now supports doc and slides: persisted type lookup, dispatch to read/write arms, sniffBodyShape for defensive validation, and new slides-specific validators emitting 400 on first invalidity.
CLI PPTX Import Wrapper
packages/cli/src/slides/pptx-import.ts, packages/cli/test/slides-pptx-import.test.ts
inlineBase64SlidesUploader encodes images as data URLs with MIME fallback; importPptx wraps the parser and throws InvalidPptxError (code = 'INVALID_PPTX') on parse failures; tests validate behavior.
CLI Slides Import Orchestration
packages/cli/src/slides/import.ts, packages/cli/test/slides-import.test.ts
runSlidesImport implements create (POST+PUT) and replace (PUT with confirmation) flows, supports --dryRun/--quiet, enforces TTY/--yes confirmation logic, summaries ImportReport counters, and emits structured JSON output on success/failure; tests cover success and error paths.
CLI Command Registration and HTTP Client
packages/cli/src/commands/slides.ts, packages/cli/src/bin.ts, packages/cli/src/client/http-client.ts, packages/cli/package.json
Registers slides import <file> command, extends HttpClient.createDocument to accept 'slides', adds putSlidesContent, and adds @wafflebase/slides workspace dependency.
End-to-End Integration Test
packages/backend/test/slides-pptx-import.e2e-spec.ts, packages/cli/scripts/gen-sample-pptx.mjs
Gated integration suite spawns Nest app and CLI via tsx, uses committed PPTX fixture, verifies import creates slides document and GET /content returns slides JSON, tests --replace --yes, and asserts PUT rejects docs-shaped body with 400.
Documentation and Type Exports
docs/tasks/active/20260515-pptx-import-lessons.md, packages/slides/src/node.ts, .github/workflows/ci.yml
Notes on orchestrator design, defensive body-shape sniffing vs persisted type authority, slides Node type re-exports, and CI step to rebuild packages the CLI imports.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • wafflebase/wafflebase#183: Related prior changes touching the docs content API surface that this PR extends to support slides payloads.

Poem

"A rabbit parsed each PPTX byte,
turned slides to JSON in the night,
Yorkie roots now hold the show,
CLI and server ebb and flow,
tiny hops, big decks — what a sight!" 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.04% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly describes the main change: adding slides import support to the CLI and implementing a backend writer for persisting slides documents.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/slides-cli-import

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.

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/backend/test/slides-pptx-import.e2e-spec.ts`:
- Around line 243-249: The test parses importResult.stdout into JSON without
asserting the CLI succeeded; change the mismatch test to first assert the runCli
result indicates success (e.g., check importResult.exitCode === 0 or use the
test framework's expect assertion) before calling JSON.parse on
importResult.stdout so failures produce a clear CLI-failure assertion instead of
a noisy JSON parse error; update the block that uses runCli and the importResult
variable (and the subsequent JSON.parse and { id } extraction) accordingly.
- Around line 82-88: The test currently resolves the child process on the 'exit'
event which can fire before stdio is flushed; update the handler on the
ChildProcess instance (child) to listen for the 'close' event instead of 'exit'
and call resolveResult({ exitCode: signal ? 1 : (code ?? 1), stdout, stderr })
there so stdout/stderr are fully captured for later
JSON.parse(importResult.stdout); leave the same parameters and resolution shape
but move it to 'close'.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a922cac2-6b1d-41ca-9eb6-e060cddd8473

📥 Commits

Reviewing files that changed from the base of the PR and between 898f923 and 97071cd.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (15)
  • docs/tasks/active/20260515-pptx-import-lessons.md
  • packages/backend/src/api/v1/docs-content.controller.spec.ts
  • packages/backend/src/api/v1/docs-content.controller.ts
  • packages/backend/src/yorkie/slides-tree.spec.ts
  • packages/backend/src/yorkie/slides-tree.ts
  • packages/backend/test/slides-pptx-import.e2e-spec.ts
  • packages/cli/package.json
  • packages/cli/src/bin.ts
  • packages/cli/src/client/http-client.ts
  • packages/cli/src/commands/slides.ts
  • packages/cli/src/slides/import.ts
  • packages/cli/src/slides/pptx-import.ts
  • packages/cli/test/slides-import.test.ts
  • packages/cli/test/slides-pptx-import.test.ts
  • packages/slides/src/node.ts

Comment thread packages/backend/test/slides-pptx-import.e2e-spec.ts Outdated
Comment thread packages/backend/test/slides-pptx-import.e2e-spec.ts
@github-actions

github-actions Bot commented May 16, 2026

Copy link
Copy Markdown
Contributor

Verification: verify:self

Result: ✅ PASS in 190.2s

Lane Status Duration
sheets:build ✅ pass 12.4s
docs:build ✅ pass 11.6s
slides:build ✅ pass 12.1s
verify:fast ✅ pass 112.1s
frontend:build ✅ pass 16.6s
verify:frontend:chunks ✅ pass 0.3s
backend:build ✅ pass 4.6s
cli:build ✅ pass 2.0s
verify:entropy ✅ pass 18.5s

Verification: verify:integration

Result: ✅ PASS

@codecov

codecov Bot commented May 16, 2026

Copy link
Copy Markdown

hackerwins and others added 2 commits May 16, 2026 13:14
verify-integration on PR #245 failed with `jszip_1.default is not a
constructor` — the slides fixture builder relies on `new JSZip()`, but
ts-jest compiles `import JSZip from 'jszip'` to `jszip_1.default()`
under its strict CommonJS interop. That's the same trap the docs e2e
hit back when it tried to import `@wafflebase/docs` directly. The
docs fix was to pre-generate the `.docx` binary; do the same here.

- `packages/cli/scripts/gen-sample-pptx.mjs` — tsx script that calls
  the slides fixture builder and writes the bytes to the backend
  fixtures dir. Mirrors `gen-sample-docx.mjs`.
- `packages/backend/test/fixtures/slides-cli-sample.pptx` — committed
  binary (8.7 KB).
- `slides-pptx-import.e2e-spec.ts` — drops the relative import of the
  slides fixture and `readFileSync`s the committed binary instead.

While here, address CodeRabbit's two inline comments:

- Resolve the spawned CLI on `'close'` instead of `'exit'` so
  stdout/stderr are guaranteed flushed before JSON.parse. Add a
  `settled` flag so a stray `error` after `close` cannot double-
  resolve.
- Assert `exitCode === 0` before parsing JSON in the body-shape-
  mismatch test, surfacing a clear CLI failure rather than a noisy
  JSON.parse stack trace.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
verify-integration runs on a fresh runner separate from verify-self,
so it starts without any `dist/` from the prior build job. The CLI
e2e specs spawn `tsx packages/cli/src/bin.ts`, which eagerly imports
`@wafflebase/docs` and `@wafflebase/slides/node` — both resolve via
each package's `exports` field to files under `dist/`. Without the
build artifacts the CLI subprocess exits 1 immediately on startup,
making every spawn-based e2e (including the previously-green
docs-cli-roundtrip) look as if its first assertion failed.

This used to work on `main` only because the CLI never reached the
slides import path; the docs `dist/` apparently lived in some cached
state on the integration runner. Once this PR's CLI started touching
`@wafflebase/slides/node`, the missing-dist failure became
deterministic. Make the dependency explicit by building both packages
in the integration lane before invoking the e2e suite.

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

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/backend/test/slides-pptx-import.e2e-spec.ts`:
- Around line 12-15: Update the stale header comment to reflect that the test
now reads a committed .pptx fixture via SAMPLE_PPTX_PATH instead of generating
it in beforeAll; locate the header in slides-pptx-import.e2e-spec.ts and replace
the description that says the fixture is generated in beforeAll with a brief
"why" note stating the test uses a committed SAMPLE_PPTX_PATH fixture (to keep
tests deterministic and avoid runtime generation), and remove or update any
mention of the prior builder/unit-test generation behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 59df4cc0-8f3b-49f3-85fb-068d63a9d69b

📥 Commits

Reviewing files that changed from the base of the PR and between 97071cd and a8773d4.

⛔ Files ignored due to path filters (1)
  • packages/backend/test/fixtures/slides-cli-sample.pptx is excluded by !**/*.pptx
📒 Files selected for processing (3)
  • .github/workflows/ci.yml
  • packages/backend/test/slides-pptx-import.e2e-spec.ts
  • packages/cli/scripts/gen-sample-pptx.mjs

Comment on lines +12 to +15
* The .pptx fixture is generated in `beforeAll` using the same
* builder the slides package's unit tests use — a tiny synthetic
* deck with 1 blank slide. Avoids committing a binary fixture and
* keeps the test runnable in CI without any external file.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update stale fixture-generation comment to match current behavior.

Lines 12-15 still claim the .pptx is generated in beforeAll and not committed, but this test now reads a committed fixture (SAMPLE_PPTX_PATH). Please align the header comment with the implemented approach to avoid future confusion.

As per coding guidelines, "Write clear comments explaining the 'why' behind complex logic, not just the 'what'".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/backend/test/slides-pptx-import.e2e-spec.ts` around lines 12 - 15,
Update the stale header comment to reflect that the test now reads a committed
.pptx fixture via SAMPLE_PPTX_PATH instead of generating it in beforeAll; locate
the header in slides-pptx-import.e2e-spec.ts and replace the description that
says the fixture is generated in beforeAll with a brief "why" note stating the
test uses a committed SAMPLE_PPTX_PATH fixture (to keep tests deterministic and
avoid runtime generation), and remove or update any mention of the prior
builder/unit-test generation behavior.

The slides e2e expected `meta.title === 'IT Slides'` after the CLI's
`--title 'IT Slides'`, but the PPTX parser fills `meta.title` with
whatever string the source `.pptx` carries (the synthetic fixture
uses "Imported deck"). That value was making it into the Yorkie
root and overriding the user's intent.

`createDocument` already records the title on the Document row from
the same `inferredTitle`, so the deck list reflected the right name —
the divergence only surfaced through `GET /content`. Mirror the row
title onto `deck.meta.title` before the PUT so the deck list, the
editor header, and the GET response all agree.

Only the default (POST + PUT) flow is touched. `--replace` keeps the
parser's title so re-importing into an existing deck doesn't silently
rename its meta when the caller omits `--title`.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@hackerwins
hackerwins merged commit 571bce5 into main May 16, 2026
4 checks passed
@hackerwins
hackerwins deleted the feat/slides-cli-import branch May 16, 2026 05:15
hackerwins added a commit that referenced this pull request May 16, 2026
PR2 (parser + UI, #243) and PR3 / Task 6 (CLI + backend writer, #245)
both shipped. The CLI orchestrator and the backend slides-content
dispatch were the last open items; check the remaining boxes and let
`tasks:archive` move the pair to `docs/tasks/archive/2026/05/`.

Image upload (base64 → real `/images` POST) and orphan-deck cleanup
on PUT failure are tracked as follow-ups in the merge commit body,
not as still-open items in this todo.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
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