diff --git a/CLAUDE.md b/CLAUDE.md index 97e9de1..918a3f1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,7 +2,7 @@ ## Project Overview -Obsidian plugin for exporting notes, folders, and selected Markdown files to PDF, Word, Markdown bundles, and HTML. +Obsidian plugin for exporting notes, folders, and selected Markdown files to PDF, Word, EPUB, Markdown bundles, and HTML. - Plugin ID: `document-exporter` - Current version: see `manifest.json` diff --git a/README.md b/README.md index 78b321c..a61a7ce 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,8 @@ Export single notes, entire folders, or hand-picked files into PDF, Word, EPUB, - **PDF** — generates a print-ready `.pdf` with native Obsidian rendering - **Word document** — generates a `.docx` document for editing and sharing - **EPUB e-book** — generates a self-contained `.epub` with embedded images and a navigable table of contents (desktop and mobile) -- **Markdown bundle** — creates one `.md` file per source note plus a shared `assets/` directory, with attachments copied and links rewritten -- **HTML document** — generates a standalone `.html` with table of contents, native rendering, and linked assets +- **Markdown bundle** — batch export creates one `.md` file per source note plus a shared `assets/` directory, with attachments copied and links rewritten +- **HTML document** — batch export creates one standalone `.html` per source note, with table of contents, native rendering, and linked assets - **Progress bar with cancel** — visual progress indicator for every export, with a cancel button that preserves already-exported files ## Usage @@ -69,7 +69,21 @@ Open **Settings → Document Exporter**. All settings are indexed and searchable | Expand note embeds | Inline `![[Note]]` embeds into exported documents | On | | Include source path comments | Add HTML comments showing each section's origin | Off | | Copy attachments | Copy referenced images and files into the export | On | -| Overwrite existing exports | Overwrite if output already exists; otherwise a timestamped folder is created | Off | +| Overwrite existing exports | Replace existing export files when enabled. When disabled and the destination already exists, the export uses a new timestamped folder for its documents, attachments and report. "Destination" means the selected root for a single note and the batch leaf for folder/selected-file exports — including an existing empty destination. No source folder or previous export is moved or changed; only the new export's destination changes. | Off | + +## Format capabilities + +Native rendering (Obsidian's own engine) is used for desktop PDF and in-app HTML; DOCX and EPUB use format-specific basic conversion. Exact visual parity with Obsidian is not promised. + +| Format | Rendering path | Local images | Non-image attachments | Links | Platforms | Documented limitations | +|--------|----------------|--------------|----------------------|-------|-----------|------------------------| +| PDF | Obsidian native print | Copied to `assets/` | Copied to `assets/` | Rewritten relative | Desktop only | Requires the desktop app | +| Word (.docx) | Format-specific converter | Embedded in the document | Copied to `assets/` | Internal headings, external URLs | Desktop, mobile | Basic conversion; no exact visual parity | +| EPUB | Format-specific converter | Packaged inside the `.epub` | Not packaged | Cross-note links omitted | Desktop, mobile | Images only | +| Markdown bundle | Markdown passthrough | Copied to `assets/` | Copied to `assets/` | Rewritten relative | Desktop, mobile | — | +| HTML | Native in-app renderer (basic converter as fallback) | Copied to `assets/` | Copied to `assets/` | Rewritten relative | Desktop, mobile | — | + +Artifact verification status: the automated headless contract suite covers Markdown, HTML (fallback path), DOCX and EPUB output — link destinations, attachment bytes, package XML and relationships. Interactive verification of exports produced through the app's own dialogs on each platform is tracked in the 1.0.0 release readiness record. ## Limitations diff --git a/docs/releases/1.0.0/readiness.md b/docs/releases/1.0.0/readiness.md index 50aeac1..c0a8dfd 100644 --- a/docs/releases/1.0.0/readiness.md +++ b/docs/releases/1.0.0/readiness.md @@ -2,7 +2,7 @@ Release decision: NOT READY Source commit: branch `fix/1.0-export-integrity` (implementation commits e2c9dd3, 23e670a, 3098745, 064f518 on top of merged main 207a4d3) -Runtime: Node v24.16.0, npm 11.13.0, macOS 27.0 (arm64); Obsidian 1.11.5 installed; QA workspace: /var/folders/cg/8_2x8c9s5xx3dl1trdcs3ndh0000gn/T/document-exporter-1.0-qa.UgHjVD (temporary, retained until release verification) +Runtime: Node v24.16.0, npm 11.13.0, macOS 27.0 (arm64); Obsidian 1.13.7 installed (verified running); QA workspace: /var/folders/cg/8_2x8c9s5xx3dl1trdcs3ndh0000gn/T/document-exporter-1.0-qa.UgHjVD (temporary, retained until release verification) | Gate | Status | Source commit / artifact SHA-256 | Evidence | Remaining action | |---|---|---|---|---| @@ -10,9 +10,9 @@ Runtime: Node v24.16.0, npm 11.13.0, macOS 27.0 (arm64); Obsidian 1.11.5 install | Output integrity | PASS | merged main 207a4d3 (PR #84, commit b44207c) | T1 reproduced the overwrite corruption as required (expected `[1]`, received `[2]` in `src/export/ExportIntegrity.test.ts`). T2 added directory isolation, exclusive writes (`wx` external / create-only vault), report-name protection and a 10-case regression matrix. Five export suites 145/145, full suite green, lint and build exit 0. | T6 reruns the two-run case through the native export dialog | | Outcomes | PASS | e2c9dd3 (T3), 23e670a (T4) | `resolveExportStatus` state table (8 rows) plus 9 integration scenarios: early cancel, mid-batch cancel, cancel after render, second-source read failure (partial), PDF renderer rejection (failed), shared-attachment retry, missing input keeps original total, report-write failure stays completed-with-warning, unresolved-link-only stays completed. Runner finalizes through one path; `onFileComplete` reports completed count minus one. `exportResultMessage` distinguishes completed/partial/cancelled/failed for the UI. Full suite 411/411, lint and build exit 0. | Native dialog feedback wording checked in T6 (A08) | | Headless artifacts | PASS | 3098745 | Fixture generator: 520 synthetic files, all manifest SHA-256 verified; second run refused non-empty destination; PNGs decode via macOS `sips` with matching dimensions (640x240, 160x100). Contract suite 10/10: Markdown/HTML-fallback/DOCX/EPUB content cases (markers, tables, code, XML validity, relationships, spine resolution, image byte equality, no `app://`), folder batch (nested primaries, relative links, shared attachment bytes), collision A→B, export-report name protection, missing-attachment and cancellation injections. `RELEASE_ARTIFACT_DIR` persistence verified with SHA-256 index (no PDF claims). Independent `unzip -t` passed on generated DOCX and EPUB. | Native artifact acceptance in T6 | -| Native artifacts | BLOCKED | Unmeasured | Environment probed 2026-09-16: available — macOS 27.0, Obsidian 1.11.5, `/Users/Roger/my-vault` with the plugin installed as an independent directory (not a symlink). Missing — an interactive acceptance session against a fixed candidate build (A01-A12 drive real dialogs and real output inspection); Java runtime for EPUBCheck; a real DOCX reader (no Word/LibreOffice in /Applications) and a real EPUB reader. No native case has been executed; no PASS is claimed. | Run A01-A12 per protocol T6.2/T6.3 against the final candidate (install or record EPUBCheck/Java and reader gaps explicitly) | -| Platforms | BLOCKED | Unmeasured | Only the macOS row of the T6.4 matrix is executable here. Missing — Windows, Linux, iOS, Android environments and an isolated desktop Obsidian 1.4.0 install. None tested; no platform claim made. | Execute the bounded platform matrix on the required devices | -| Documentation | NOT RUN | Unmeasured | No review recorded | Execute T7 after T6 evidence (capability table and privacy wording are evidence-bound) | -| Release gate | PASS (local) | 064f518 | `scripts/check-version.mjs` extended to lockfile root and `versions.json`↔`minAppVersion` mapping; `node --test scripts/check-version.test.mjs` 8/8 (drift, missing entry, mapping mismatch, lockfile cases, wrong tag); `npm run check:version` OK; CI adds the script test before `check:version`; release workflow runs script test, version gate, lint, build, test before attestation and prefers `docs/releases//release-notes.md`; both workflow files parse as valid YAML; CLAUDE.md release guidance updated. | CI must verify the workflow changes on the PR | -| Upgrade / candidate | NOT RUN | Unmeasured | Blocked by T6: the plan requires T1-T8 including initial native artifact/platform rows before the version bump | Complete T6, then execute T9 (bump, final gates, install/upgrade smoke) | -| Published assets | NOT RUN | Unmeasured | Not published | Execute T10 after authorization | +| Native artifacts | BLOCKED | candidate build main.js SHA-256 c0f73a64f49b59a37a4fc83668d45cc084fe0c1b1ec6a94168249a55e275bfdc (from main f0552fa) | 2026-09-17 session: tools installed and verified — OpenJDK 26 + EPUBCheck 5.2.1, LibreOffice 26.8.0.3 (~/Applications), Books/Preview present. Candidate installed into `/Users/Roger/my-vault`'s plugin directory and confirmed loaded in Obsidian 1.13.7 on macOS 27 (ribbon entry visible); 521 fixture files staged, then fully removed and the original plugin restored (main.js bc226ff2…) after the session. BLOCKED reason: the export dialog could not be driven by UI automation — one initial ribbon AXPress opened the modal, but select changes, Next/Export button presses, synthetic keys and coordinate clicks never reached the Electron DOM layer, and DOM context menus do not respond. A01-A12 were NOT executed; no native PASS is claimed. Supplementary real-tool checks of HEADLESS-generated artifacts (clearly not native evidence): EPUBCheck 5.2.1 `--failonwarnings` → 0 fatals/0 errors/0 warnings; LibreOffice headless conversion of the DOCX → clean PDF; `unzip -t` clean. | Human-driven or working-automation native session per protocol T6.2/T6.3 against the fixed candidate (tools are now installed); QA workspace retained at the recorded temporary path | +| Platforms | BLOCKED | Unmeasured | Only the macOS row of the T6.4 matrix is executable here, and it is blocked by the same dialog-automation gap above. Missing — Windows, Linux, iOS, Android environments and an isolated desktop Obsidian 1.4.0 install. None tested; no platform claim made. | Execute the bounded platform matrix on the required devices after the macOS interactive gap is closed | +| Documentation | IN PROGRESS | — | T7 executing 2026-09-17 with evidence-scoped content: descriptions/setting text are code-backed; the capability table cites headless-verified rows and marks native verification pending; the privacy paragraph is unchanged (A10 not run, per plan gating). | Capability table native columns and privacy wording finalize after T6 evidence | +| Release gate | PASS (local) | 064f518 | `scripts/check-version.mjs` extended to lockfile root and `versions.json`↔`minAppVersion` mapping; `node --test scripts/check-version.test.mjs` 8/8 (drift, missing entry, mapping mismatch, lockfile cases, wrong tag); `npm run check:version` OK; CI adds the script test before `check:version`; release workflow runs script test, version gate, lint, build, test before attestation and prefers `docs/releases//release-notes.md`; both workflow files parse as valid YAML; CLAUDE.md release guidance updated. | None — CI verified the workflow changes on PR #85 (verify pass) | +| Upgrade / candidate | BLOCKED | Unmeasured | T9 not executed on 2026-09-17: the plan's bump prerequisite (T1-T8 complete, including initial native artifact/platform rows) is not met — native artifacts and platforms are BLOCKED. Per the plan, available work continued without a release-ready claim; no version bump, no tag, no `RELEASE_TAG` gate run. | Close the T6 interactive gap, rerun native/platform rows, then execute T9 (bump, final gates, install/upgrade smoke) | +| Published assets | BLOCKED | Unmeasured | Not published. T10 requires all required gates PASS and publication authorization; Native artifacts and Platforms are BLOCKED, so publication is refused by the plan's own rule ("Never publish with a required gate marked BLOCKED or FAIL"). | Resolve T6 native and platform rows (or an explicit, documented product-decision scope change by the owner), then authorize T10 | diff --git a/docs/releases/1.0.0/release-notes.md b/docs/releases/1.0.0/release-notes.md new file mode 100644 index 0000000..d4baac8 --- /dev/null +++ b/docs/releases/1.0.0/release-notes.md @@ -0,0 +1,27 @@ +# Document Exporter 1.0.0 + +## Protecting your previous exports + +This release makes exports non-destructive by default. + +- **No more silent overwrites.** With "Overwrite existing exports" off (the default), an export never modifies an existing file. When the destination already exists — including an empty folder — the whole export (documents, attachments, and report) moves to a new timestamped folder instead. Nothing you exported before is moved, changed, or deleted. +- **Attachments are protected too.** The protection covers every output file, not just the main document. A second export of a different note can no longer replace the first export's images, even when both use the same image filename. +- **Reports never overwrite anything.** Export reports always get a fresh, conflict-free name — even with overwrite enabled — and a note named `export-report.md` keeps its own file. + +## Clearer export results + +- Exports now finish with an explicit status: **complete**, **partially complete**, **cancelled**, or **failed**, with completed and possibly-incomplete file counts. +- Cancelling preserves everything already exported and says exactly what may be incomplete — no more "success" messages for half-finished runs. +- The export report lists status, completed paths, possibly-incomplete paths, warnings, and errors. + +## Destination layout change + +When overwrite is off and the destination exists, the new export lands in a sibling timestamped folder (for example `exports/2026-09-17T10-30-00/`). This applies to single-note exports (destination = the selected output root) and batch exports (destination = the batch leaf folder). + +## Formats and platforms + +PDF, Word (.docx), EPUB, Markdown bundles, and HTML on desktop and mobile Obsidian. PDF requires the desktop app. See the README's format capability table for rendering paths, attachment handling, and per-format limitations. + +## Limitations + +Unchanged from 0.7.4: block-reference embeds are not expanded; EPUB packages images only (other attachments and cross-note links are omitted); Dataview queries are not executed; Canvas files are not supported. diff --git a/docs/superpowers/plans/2026-09-12-1.0.0-release-readiness.md b/docs/superpowers/plans/2026-09-12-1.0.0-release-readiness.md index e1d8b9e..00964aa 100644 --- a/docs/superpowers/plans/2026-09-12-1.0.0-release-readiness.md +++ b/docs/superpowers/plans/2026-09-12-1.0.0-release-readiness.md @@ -53,7 +53,7 @@ Alternatives intentionally rejected: pre-scanning every renderer to predict exac - [x] T4 — Present accurate completion/cancellation/failure messages. - [x] T5 — Add reproducible artifact fixtures and automated contract checks. - [ ] T6 — Execute native artifact and compatibility acceptance. -- [ ] T7 — Align docs, settings and metadata with verified behavior. +- [x] T7 — Align docs, settings and metadata with verified behavior. *(Executed evidence-scoped: capability table cites headless verification, native columns and the privacy paragraph stay pending T6, per the plan's gating.)* - [x] T8 — Strengthen version checks and tag release verification. - [ ] T9 — Validate upgrade and the final 1.0.0 candidate. - [ ] T10 — Publish through the authorized PR/tag workflow and verify shipped assets. diff --git a/manifest.json b/manifest.json index d8e5d49..face6e2 100644 --- a/manifest.json +++ b/manifest.json @@ -3,8 +3,8 @@ "name": "Document Exporter", "version": "0.7.4", "minAppVersion": "1.4.0", - "description": "Export notes, folders, and selected Markdown files to PDF, Word, Markdown bundles, and HTML.", + "description": "Export notes, folders, and selected Markdown files to PDF, Word, EPUB, Markdown bundles, and HTML.", "author": "Roger Deng", "authorUrl": "https://github.com/rogerdigital", "isDesktopOnly": false -} \ No newline at end of file +} diff --git a/package.json b/package.json index 5357f7a..3dca0df 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "document-exporter", "version": "0.7.4", - "description": "Export notes, folders, and selected Markdown files to PDF, Word, Markdown bundles, and HTML.", + "description": "Export notes, folders, and selected Markdown files to PDF, Word, EPUB, Markdown bundles, and HTML.", "main": "main.js", "scripts": { "dev": "node esbuild.config.mjs", diff --git a/src/settings/settings-tab.ts b/src/settings/settings-tab.ts index 0f4462b..14a583c 100644 --- a/src/settings/settings-tab.ts +++ b/src/settings/settings-tab.ts @@ -45,7 +45,7 @@ const SETTING_META = { }, overwriteExisting: { name: "Overwrite existing exports", - desc: "Overwrite if the output folder already exists. Otherwise a timestamped folder is created.", + desc: "Replace existing export files when enabled. When disabled and the destination already exists, the export uses a new timestamped folder for its documents, attachments and report.", aliases: ["replace", "timestamped folder"], }, } as const;