Files
OpenMAIC/components/stage
Som SamantrayandCommandCodeBot 5dc023d92d feat(export): download narration script as Markdown or DOCX (#1144)
* feat(export): download narration script as Markdown or Word-compatible .doc

Adds two export-menu entries that download the classroom's TTS narration
text (SpeechAction.text per scene) as a local document for lesson prep,
closing #413. The .doc is a minimal HTML document with the Word MIME type
so no new runtime dependency is introduced; .md is the portable plain-text
sibling. Scenes without speech are omitted, HTML is escaped in the .doc
path, and file names are sanitized.

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>

* refactor(export): simplify narration script export hook

Reads stage store state at click time instead of subscribing per render,
drops unobservable exporting state (the handler is synchronous and the
menu-close is the reentrancy guard), removes a redundant empty-scenes
pre-check, and drops a type cast the discriminated union makes
unnecessary.

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>

* fix(export): apply code review findings for narration script

- Skip whitespace-only speech text when collecting scene scripts, so a
  blank course cannot download a heading-only document with a success toast
- Trim and filter empty paragraphs in the .doc serializer to match the
  markdown path
- Strip only dangerous control ranges in the filename sanitizer so emoji
  and ZWJ sequences survive (the broad \p{C} class was splitting surrogate
  pairs)
- Add unit tests for whitespace-only speech, HTML paragraph normalization,
  the <br> branch, emoji-preserving filenames, control-char stripping, and
  markdown whitespace filtering

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>

* fix(export): address review feedback on narration script export

- Decouple the export menu trigger from full media-readiness gating:
  script export (.doc/.md) now stays usable while media generation is
  still pending. PPTX/Resource Pack/ZIP/Video items keep the existing
  media-dependent gate, now with visible disabled state and a tooltip
  explaining why.
- Document the HTML-as-.doc format as a deliberate zero-dependency
  tradeoff (not an oversight) directly above buildDocHtml.
- Sanitize scene/stage titles before they're interpolated into
  Markdown headings, so embedded newlines or leading # runs can't
  inject extra headings or corrupt document structure.
- Localize the "Slide N" fallback label via the i18n system instead of
  hardcoding English, adding export.slideFallback (plus export.textOnly
  and export.mediaPending for the new UI states) across all 12 locales.

Addresses review from @wyuc on PR #1144.

* refactor(export): dedupe export-readiness derivation in header controls

canExport already implies canExportText once it ANDs the media-readiness
check on top, so the trigger's title/aria-label/readiness ternaries had two
independent copies of the same 3-way branch drifting apart. Derive canExport
from canExportText, and share exportReady/exportLabel between the button's
disabled, className, title, and aria-label props.

* fix(export): escape leading # in markdown headings instead of deleting it

Two issues found in code review: (1) a leading newline could shield a
leading # from the strip regex, then trim() would re-expose it unescaped
-- a real order-of-operations bug; (2) unconditionally deleting the
leading # run silently mangled legitimate titles like "#1 Introduction".
Escaping (\#) instead of stripping fixes both: content is preserved and
the character can no longer be mistaken for heading syntax, regardless
of what precedes it.

* fix(review): apply review findings

* fix(export): remove legacy doc script export

---------

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
2026-08-22 21:23:33 +08:00
..