Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
20 changes: 17 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
14 changes: 7 additions & 7 deletions docs/releases/1.0.0/readiness.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@

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 |
|---|---|---|---|---|
| Baseline | PASS | 2915687ebd656444054387fa474e0393bf420d7b | 2026-09-15 local run: `npm run check:version` ("Version metadata is consistent: 0.7.4"), `npm run lint:obsidian-warnings`, `npm run build`, `npm test` (20 files, 360 tests passed) — all exit 0. Remote read-only: 0 open issues, 0 open PRs; release 0.7.4 (published 2026-08-26) with `main.js`, `manifest.json`, `styles.css`; latest `verify` runs green including HEAD 2915687. No vault plugin symlink present, so builds cannot update a live plugin. | T0 complete |
| 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/<tag>/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/<tag>/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 |
27 changes: 27 additions & 0 deletions docs/releases/1.0.0/release-notes.md
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
2 changes: 1 addition & 1 deletion src/settings/settings-tab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down