mkdocs2confluence 0.6.9__tar.gz → 0.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. {mkdocs2confluence-0.6.9/src/mkdocs2confluence.egg-info → mkdocs2confluence-0.7.0}/PKG-INFO +142 -105
  2. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/README.md +139 -104
  3. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/pyproject.toml +8 -1
  4. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0/src/mkdocs2confluence.egg-info}/PKG-INFO +142 -105
  5. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs2confluence.egg-info/SOURCES.txt +5 -0
  6. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs2confluence.egg-info/requires.txt +3 -0
  7. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/cli.py +138 -0
  8. mkdocs2confluence-0.7.0/src/mkdocs_to_confluence/pdf/__init__.py +1 -0
  9. mkdocs2confluence-0.7.0/src/mkdocs_to_confluence/pdf/generator.py +33 -0
  10. mkdocs2confluence-0.7.0/src/mkdocs_to_confluence/pdf/render.py +276 -0
  11. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/preview/server.py +1 -1
  12. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/tests/test_cli.py +20 -0
  13. mkdocs2confluence-0.7.0/tests/test_pdf.py +201 -0
  14. mkdocs2confluence-0.7.0/tests/test_server.py +247 -0
  15. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/LICENSE +0 -0
  16. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/setup.cfg +0 -0
  17. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs2confluence.egg-info/dependency_links.txt +0 -0
  18. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs2confluence.egg-info/entry_points.txt +0 -0
  19. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs2confluence.egg-info/top_level.txt +0 -0
  20. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/__init__.py +0 -0
  21. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/emitter/__init__.py +0 -0
  22. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/emitter/xhtml.py +0 -0
  23. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/ir/__init__.py +0 -0
  24. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/ir/document.py +0 -0
  25. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/ir/nodes.py +0 -0
  26. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/ir/treeutil.py +0 -0
  27. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/loader/__init__.py +0 -0
  28. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/loader/config.py +0 -0
  29. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/loader/extra_css.py +0 -0
  30. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/loader/nav.py +0 -0
  31. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/loader/page.py +0 -0
  32. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/parser/__init__.py +0 -0
  33. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/parser/markdown.py +0 -0
  34. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/preprocess/__init__.py +0 -0
  35. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/preprocess/abbrevs.py +0 -0
  36. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/preprocess/fence.py +0 -0
  37. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/preprocess/frontmatter.py +0 -0
  38. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/preprocess/icons.py +0 -0
  39. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/preprocess/includes.py +0 -0
  40. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/preprocess/linkdefs.py +0 -0
  41. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/preview/__init__.py +0 -0
  42. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/preview/render.py +0 -0
  43. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/publisher/__init__.py +0 -0
  44. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/publisher/client.py +0 -0
  45. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/publisher/pipeline.py +0 -0
  46. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/transforms/__init__.py +0 -0
  47. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/transforms/abbrevs.py +0 -0
  48. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/transforms/assets.py +0 -0
  49. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/transforms/editlink.py +0 -0
  50. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/transforms/images.py +0 -0
  51. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/transforms/internallinks.py +0 -0
  52. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/src/mkdocs_to_confluence/transforms/mermaid.py +0 -0
  53. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/tests/test_abbrevs.py +0 -0
  54. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/tests/test_editlink.py +0 -0
  55. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/tests/test_emitter.py +0 -0
  56. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/tests/test_extra_css.py +0 -0
  57. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/tests/test_frontmatter.py +0 -0
  58. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/tests/test_icons.py +0 -0
  59. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/tests/test_images.py +0 -0
  60. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/tests/test_internallinks.py +0 -0
  61. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/tests/test_ir.py +0 -0
  62. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/tests/test_linkdefs.py +0 -0
  63. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/tests/test_loader.py +0 -0
  64. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/tests/test_mermaid.py +0 -0
  65. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/tests/test_page_loader.py +0 -0
  66. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/tests/test_parser.py +0 -0
  67. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/tests/test_preprocess.py +0 -0
  68. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/tests/test_preview.py +0 -0
  69. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/tests/test_publish_client.py +0 -0
  70. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/tests/test_publish_config.py +0 -0
  71. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/tests/test_publish_pipeline.py +0 -0
  72. {mkdocs2confluence-0.6.9 → mkdocs2confluence-0.7.0}/tests/test_treeutil.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkdocs2confluence
3
- Version: 0.6.9
3
+ Version: 0.7.0
4
4
  Summary: Publish MkDocs Material pages to Confluence Cloud — admonitions, Mermaid diagrams, tabs, page properties and more
5
5
  Author: Anders Hybertz
6
6
  License: GPL-3.0-or-later
@@ -26,6 +26,8 @@ License-File: LICENSE
26
26
  Requires-Dist: PyYAML>=6.0.3
27
27
  Requires-Dist: httpx>=0.27
28
28
  Requires-Dist: tinycss2>=1.5.1
29
+ Provides-Extra: pdf
30
+ Requires-Dist: weasyprint>=60.0; extra == "pdf"
29
31
  Provides-Extra: dev
30
32
  Requires-Dist: pytest; extra == "dev"
31
33
  Requires-Dist: pytest-cov; extra == "dev"
@@ -50,160 +52,171 @@ Dynamic: license-file
50
52
  [![mypy](https://img.shields.io/badge/type--checked-mypy-blue.svg)](https://mypy-lang.org/)
51
53
  [![security: bandit](https://img.shields.io/badge/security-bandit-yellow.svg)](https://github.com/PyCQA/bandit)
52
54
 
53
- A Python CLI tool that compiles MkDocs-flavoured Markdown into native Confluence storage XHTML and publishes it to Confluence.
55
+ A Python CLI tool that compiles MkDocs-flavoured Markdown into native Confluence storage XHTML and publishes it directly to Confluence Cloud. It is a **compiler/transpiler**, not an HTML converter — every construct maps to its native Confluence equivalent, so pages look and behave like hand-authored Confluence content.
54
56
 
55
- It is a **compiler/transpiler**, not an HTML converter every Markdown construct is mapped to the equivalent native Confluence macro or element, so pages look and behave like hand-authored Confluence content.
56
-
57
- > **Zensical compatible** — [Zensical](https://zensical.org/) is the modern successor to MkDocs + Material for MkDocs, built by the same team. Since Zensical uses the same `mkdocs.yml` format and Python Markdown extensions, your Zensical project works with this tool today — no changes required.
58
-
59
- ---
60
-
61
- ## Architecture
62
-
63
- ![Architecture](https://raw.githubusercontent.com/jeckyl2010/mkdocs2confluence/main/docs/architecture.png)
64
-
65
- Each stage is a separate Python module under `src/mkdocs_to_confluence/`. The **plan** phase makes all API read calls (find existing pages); the **execute** phase makes all write calls, ensuring parents always exist before children.
57
+ > **Zensical compatible** — [Zensical](https://zensical.org/) is the modern successor to MkDocs + Material for MkDocs. Since it uses the same `mkdocs.yml` format and Python Markdown extensions, your Zensical project works with mk2conf today with no changes required.
66
58
 
67
59
  ---
68
60
 
69
61
  ## Installation
70
62
 
71
- Requires Python 3.12+.
72
-
73
- **From PyPI:**
63
+ Requires Python 3.12+. The PyPI package is `mkdocs2confluence`; the CLI command is `mk2conf`.
74
64
 
75
65
  ```bash
76
66
  pip install mkdocs2confluence
67
+ # or, for an isolated install:
68
+ pipx install mkdocs2confluence
77
69
  ```
78
70
 
79
- Or with `pipx` for an isolated install:
71
+ To use the `mk2conf pdf` subcommand, install the optional PDF extra:
80
72
 
81
73
  ```bash
82
- pipx install mkdocs2confluence
74
+ pip install "mkdocs2confluence[pdf]"
83
75
  ```
84
76
 
77
+ > **macOS only**: WeasyPrint requires Pango for font rendering:
78
+ > ```bash
79
+ > brew install pango
80
+ > ```
81
+
85
82
  **From source** (see [Setup.md](Setup.md)):
86
83
 
87
84
  ```bash
88
85
  git clone https://github.com/jeckyl2010/mkdocs2confluence.git
89
- cd mkdocs2confluence
90
- pip install -e ".[dev]"
86
+ cd mkdocs2confluence && uv sync
91
87
  ```
92
88
 
93
- > **Package name vs. command name** — This follows the same convention used by many popular CLI tools (e.g. `pip install httpie` → `http` command). The PyPI package is `mkdocs2confluence` (descriptive, easy to find), and the CLI command is `mk2conf` (short, fast to type). Install once, run everywhere as `mk2conf`.
94
-
95
89
  ---
96
90
 
97
91
  ## Quick start
98
92
 
99
93
  ```bash
100
- # Print Confluence storage XHTML to stdout
101
- mk2conf preview --config mkdocs.yml --page index.md
94
+ # Inspect the Confluence storage XHTML for a page
95
+ mk2conf preview --page index.md
102
96
 
103
- # Open a browser-friendly HTML preview
104
- mk2conf preview --config mkdocs.yml --page index.md --html --out /tmp/preview.html
97
+ # Open a live browser preview — rebuilds on every file save
98
+ mk2conf preview --page index.md --watch
99
+ mk2conf preview --section Guide --watch
105
100
 
106
101
  # Dry-run: see what would be published without touching Confluence
107
- mk2conf publish --config mkdocs.yml --dry-run
102
+ mk2conf publish --dry-run
108
103
 
109
104
  # Publish all nav pages to Confluence
110
- CONFLUENCE_API_TOKEN=your_token mk2conf publish --config mkdocs.yml
105
+ mk2conf publish
106
+
107
+ # Export a nav section to a stand-alone PDF document
108
+ mk2conf pdf --section Guide --out guide.pdf
109
+ ```
110
+
111
+ ---
112
+
113
+ ## Configuration
114
+
115
+ Add a `confluence:` block to your `mkdocs.yml`:
116
+
117
+ ```yaml
118
+ confluence:
119
+ base_url: https://yourorg.atlassian.net
120
+ space_key: TECH
121
+ email: user@example.com
122
+ token: !ENV CONFLUENCE_API_TOKEN # never hardcode the token
123
+ parent_page_id: "123456" # optional root page
124
+ mermaid_render: kroki # "kroki" (default) | "kroki:https://your-kroki" | "none"
125
+ full_width: true # default: true
126
+ ```
127
+
128
+ The API token is read from (in priority order):
129
+
130
+ 1. `token:` in `mkdocs.yml` — typically via `!ENV CONFLUENCE_API_TOKEN`
131
+ 2. `CONFLUENCE_API_TOKEN` environment variable
132
+ 3. `MK2CONF_TOKEN` environment variable
133
+
134
+ ---
135
+
136
+ ## Your first publish
137
+
138
+ Once you have added the `confluence:` block (see above), this is all you need to get from zero to a live Confluence page:
139
+
140
+ ```bash
141
+ # 1. Set your API token (add this to your shell profile or CI secrets)
142
+ export CONFLUENCE_API_TOKEN=your_api_token_here
143
+
144
+ # 2. Preview one page locally to verify the output before touching Confluence
145
+ mk2conf preview --page docs/index.md --watch
146
+
147
+ # 3. Dry-run to see exactly what would be created or updated
148
+ mk2conf publish --dry-run
149
+
150
+ # 4. Publish
151
+ mk2conf publish
111
152
  ```
112
153
 
154
+ That is it. mk2conf reads `mkdocs.yml` in the current directory, compiles every page listed in `nav:`, mirrors the hierarchy under `parent_page_id`, and skips any page that has not changed since the last run.
155
+
113
156
  ---
114
157
 
115
158
  ## Commands
116
159
 
117
160
  ### `mk2conf preview`
118
161
 
119
- Compile a single page and inspect the output — no Confluence API calls required. Mermaid diagrams are rendered via Kroki unless `mermaid_render: none` is set.
162
+ Compile and inspect output locally — no Confluence API calls. Mermaid diagrams are rendered via Kroki unless `mermaid_render: none` is set.
120
163
 
121
164
  ```
122
- mk2conf preview [--config PATH] --page PATH [--out FILE] [--html]
165
+ mk2conf preview [--config PATH] --page PATH [--out FILE] [--html] [--watch]
166
+ mk2conf preview [--config PATH] --section SECTION [--out FILE] [--watch]
123
167
  ```
124
168
 
125
169
  | Flag | Default | Description |
126
170
  |---|---|---|
127
- | `--config PATH` | `./mkdocs.yml` | Path to your `mkdocs.yml` |
128
- | `--page PATH` | *(required)* | Relative path to the Markdown file |
129
- | `--out FILE` | stdout | Write output to a file |
130
- | `--html` | off | Render macros as styled HTML for local browser review |
171
+ | `--config PATH` | `./mkdocs.yml` | Path to `mkdocs.yml` |
172
+ | `--page PATH` | *(required unless --section)* | Relative path to the Markdown file |
173
+ | `--section SECTION` | *(none)* | Nav section title to render, slash-separated for nested sections (e.g. `Guide` or `Guide/Setup`). Renders all pages in that subtree as a browseable HTML index. |
174
+ | `--out FILE` | stdout | Write output to a file or directory |
175
+ | `--html` | off | Render macros as styled browser-viewable HTML |
176
+ | `--watch` | off | Serve on `http://localhost:8765` and auto-rebuild on file changes. Implies `--html`. `Ctrl+C` to stop. |
131
177
 
132
- The `--html` flag renders Confluence macros as visual HTML panels so you can review a page locally without a Confluence instance. It is for review only — the actual storage XHTML is always the `--html`-free output.
178
+ `--html` is for local review only — the actual Confluence storage XHTML is the `--html`-free output.
133
179
 
134
180
  ---
135
181
 
136
182
  ### `mk2conf publish`
137
183
 
138
- Compile all pages listed in `nav:` and publish them to Confluence Cloud.
184
+ Compile all pages in `nav:` and publish to Confluence Cloud.
139
185
 
140
186
  ```
141
- mk2conf publish [--config PATH] [--page PATH] [--section PATH] [--dry-run] [--report FILE] [--prune]
187
+ mk2conf publish [--config PATH] [--page PATH] [--section SECTION] [--dry-run] [--report FILE] [--prune]
142
188
  ```
143
189
 
144
190
  | Flag | Default | Description |
145
191
  |---|---|---|
146
- | `--config PATH` | `./mkdocs.yml` | Path to your `mkdocs.yml` |
192
+ | `--config PATH` | `./mkdocs.yml` | Path to `mkdocs.yml` |
147
193
  | `--page PATH` | *(all nav pages)* | Publish a single page only |
148
- | `--section PATH` | *(whole nav)* | Publish only a nav subtree (e.g. `Guide` or `Guide/Setup`) |
149
- | `--dry-run` | off | Print the publish plan without making any Confluence API calls (Mermaid diagrams are still rendered via Kroki unless `mermaid_render: none` is set) |
150
- | `--report FILE` | *(none)* | Write a JSON publish report to `FILE` |
151
- | `--prune` | off | Delete managed Confluence pages no longer in `nav:` (see [Orphan pruning](#orphan-pruning)) |
194
+ | `--section SECTION` | *(whole nav)* | Publish only a nav subtree, slash-separated for nested sections (e.g. `Guide` or `Guide/Setup`) |
195
+ | `--dry-run` | off | Print the publish plan; no Confluence API writes |
196
+ | `--report FILE` | *(none)* | Write a JSON publish report |
197
+ | `--prune` | off | Delete managed pages no longer in `nav:`. Only pages stamped by mk2conf are eligible — manually-created Confluence pages are never touched. Ignored on partial (`--page` / `--section`) runs. |
152
198
 
153
- #### Configuration
199
+ #### Publish behaviour
154
200
 
155
- Add a `confluence:` block to your `mkdocs.yml`:
156
-
157
- ```yaml
158
- confluence:
159
- base_url: https://yourorg.atlassian.net
160
- space_key: TECH
161
- email: user@example.com
162
- token: !ENV CONFLUENCE_API_TOKEN # never hardcode the token
163
- parent_page_id: "123456" # optional root page
164
- mermaid_render: kroki # optional: "kroki" (default), "kroki:https://your-kroki" or "none"
165
- full_width: true # optional: publish pages in full-width layout (default: true)
166
- ```
167
-
168
- The API token is read from (in priority order):
169
- 1. The `token:` field in `mkdocs.yml` (typically via `!ENV CONFLUENCE_API_TOKEN`)
170
- 2. `CONFLUENCE_API_TOKEN` environment variable
171
- 3. `MK2CONF_TOKEN` environment variable
172
-
173
- #### Publish rules
174
-
175
- - **Only pages in `nav:` are published** — the nav is the publish gate. Pages not listed in the nav are never touched, keeping drafts and WIP content private.
176
- - Pages with `ready: false` in their YAML front matter are **skipped**, even if listed in the nav.
177
- - Section nodes (nav groups without a page) become empty parent pages in Confluence, mirroring the nav hierarchy.
178
- - All locally linked assets are uploaded as Confluence page attachments automatically.
179
- - **Smart update detection** — before calling the Confluence update API, mk2conf compares a `sha256` hash of the compiled output against the hash stored from the previous run (kept as a hidden Confluence page property `mk2conf-content-hash`). Pages whose content has not changed are skipped entirely — no version bump, no watcher notification. As a side effect, Confluence's built-in version history becomes a meaningful audit trail: every version represents a real content change, each stamped *"Updated by mk2conf"* so automated publishes are clearly distinguishable from manual edits. You can diff any two versions directly inside Confluence, with inline highlighting showing exactly which paragraphs, headings, or code blocks changed.
180
- - **Orphan pruning** — every page created by mk2conf is stamped with a hidden `mk2conf-managed` property. Pass `--prune` to automatically delete managed pages that have been removed from `nav:`. Manually-created Confluence pages are never deleted.
181
-
182
- #### Orphan pruning
183
-
184
- ```bash
185
- mk2conf publish --prune
186
- ```
187
-
188
- After each publish run, `--prune` fetches all descendants of the configured `parent_page_id`, diffs them against the pages just published, and deletes any managed orphans — i.e. pages that carry the `mk2conf-managed` stamp but are no longer in the MkDocs nav.
189
-
190
- > **Safety:** Only pages stamped by mk2conf are eligible for deletion. Any page created directly in Confluence will never be touched, even if it sits inside the managed hierarchy.
191
- >
192
- > **Partial runs:** `--prune` is silently ignored when `--page` or `--section` is used, because a partial publish would only cover a subset of the nav and would incorrectly identify out-of-scope pages as orphans.
201
+ - **Only `nav:` pages are published** — pages absent from the nav are never touched (natural draft gate).
202
+ - Pages with `ready: false` in front matter are skipped, even if listed in `nav:`.
203
+ - Section nodes (nav groups without a page) become empty parent pages, mirroring the nav hierarchy.
204
+ - Local assets are uploaded as Confluence page attachments automatically.
205
+ - **Unchanged pages are skipped** — a `sha256` hash of the compiled output is stored as a hidden page property; pages with identical content since the last run produce no version bump and no notification.
193
206
 
194
207
  #### Mermaid rendering
195
208
 
196
- | `mermaid_render` value | Behaviour |
209
+ | `mermaid_render` | Behaviour |
197
210
  |---|---|
198
- | `kroki` *(default)* | Render via `https://kroki.io`. PNGs are cached in `~/.cache/mk2conf/mermaid/`. |
211
+ | `kroki` *(default)* | Render via `https://kroki.io`. PNGs cached in `~/.cache/mk2conf/mermaid/`. |
199
212
  | `kroki:https://your-kroki` | Render via a self-hosted Kroki instance. |
200
- | `none` | Skip rendering — fall back to a `code` macro labelled `mermaid`. |
213
+ | `none` | Fall back to a `code` macro labelled `mermaid`. |
201
214
 
202
215
  If Kroki is unreachable the run continues, falling back to the `code` macro for affected diagrams.
203
216
 
204
217
  #### Styling from extra.css
205
218
 
206
- If your `mkdocs.yml` has an `extra_css:` list, mk2conf reads those files and applies a whitelisted set of CSS properties as inline `style="..."` attributes in the Confluence output.
219
+ If `mkdocs.yml` lists `extra_css:` files, mk2conf reads them and applies a whitelisted set of CSS properties as inline `style="..."` attributes on Confluence output.
207
220
 
208
221
  | Selector | Applied to |
209
222
  |---|---|
@@ -212,11 +225,9 @@ If your `mkdocs.yml` has an `extra_css:` list, mk2conf reads those files and app
212
225
  | `h1` – `h6` | Headings |
213
226
  | `code` (not `pre code`) | Inline code spans |
214
227
 
215
- Supported properties: `background-color`, `color`, `font-weight`, `font-style`, `font-size`, `text-align`, `border`.
228
+ Supported properties: `background-color`, `color`, `font-weight`, `font-style`, `font-size`, `text-align`, `border`. CSS custom properties (`var(--name)`) are resolved automatically, including chained variables and `var(--name, fallback)` syntax.
216
229
 
217
- CSS custom properties (`var(--name)`) are resolved automatically, including chained variables and `var(--name, fallback)` syntax.
218
-
219
- **Works best with simple, flat CSS.** Complex Material for MkDocs theme overrides — compound selectors (`.md-typeset table:not([class]) thead th`), `color-mix()`, `@media` blocks, `:has()` etc. — are silently skipped. For these, maintain a small separate file:
230
+ Complex Material theme overrides (compound selectors, `color-mix()`, `@media`, `:has()`) are silently skipped. For best results, maintain a small dedicated overrides file:
220
231
 
221
232
  ```css
222
233
  /* confluence-overrides.css */
@@ -228,12 +239,34 @@ code { background-color: #f5f5f5; }
228
239
 
229
240
  ```yaml
230
241
  extra_css:
231
- - stylesheets/extra.css # full Material theme
232
- - stylesheets/confluence-overrides.css # simple Confluence-targeted styles
242
+ - stylesheets/extra.css # full Material theme
243
+ - stylesheets/confluence-overrides.css # simple Confluence-targeted styles
233
244
  ```
234
245
 
235
246
  ---
236
247
 
248
+ ### `mk2conf pdf`
249
+
250
+ Export a nav section or single page to a stand-alone, printer-ready PDF document. Requires `pip install "mkdocs2confluence[pdf]"`.
251
+
252
+ ```
253
+ mk2conf pdf [--config PATH] (--section SECTION | --page PATH) [--out FILE] [--author TEXT] [--doc-version TEXT] [--quiet]
254
+ ```
255
+
256
+ | Flag | Default | Description |
257
+ |---|---|---|
258
+ | `--config PATH` | `./mkdocs.yml` | Path to `mkdocs.yml` |
259
+ | `--section SECTION` | *(required unless --page)* | Export a nav subtree by section title |
260
+ | `--page PATH` | *(required unless --section)* | Export a single page |
261
+ | `--out FILE` | `<section-or-page>.pdf` | Output PDF path |
262
+ | `--author TEXT` | *(none)* | Author name printed on the cover page |
263
+ | `--doc-version TEXT` | *(none)* | Document version printed on the cover page |
264
+ | `--quiet` | off | Suppress progress output |
265
+
266
+ The PDF includes a **cover page**, **table of contents** with page numbers, and one chapter per nav page with automatic page breaks. Code blocks avoid mid-block splits; Mermaid diagrams appear as embedded PNGs (same Kroki-rendered images used for Confluence).
267
+
268
+ ---
269
+
237
270
  ## Supported Markdown features
238
271
 
239
272
  ### Block elements
@@ -288,7 +321,7 @@ extra_css:
288
321
  | Internal links `[text](page.md)` | Native Confluence page link; `#fragment` anchors preserved |
289
322
  | `awesome-pages` nav (`.pages` files) | Fully supported |
290
323
  | Edit link banner | `info` macro linking back to source in GitHub/GitLab |
291
- | Grid cards `<div class="grid cards" markdown>` | Native `ac:layout` multi-column sections (auto-detects 1/2/3 columns from card count); admonitions, paragraphs, and mixed content fully supported inside cards |
324
+ | Grid cards `<div class="grid cards" markdown>` | Native `ac:layout` multi-column sections (auto-detects 1/2/3 columns from card count) |
292
325
 
293
326
  ### YAML front matter → Page Properties
294
327
 
@@ -313,7 +346,6 @@ ready: true
313
346
  | `subtitle` | Rendered as italic lead paragraph above the properties table |
314
347
  | `tags` | Also applied as Confluence page labels |
315
348
  | `ready` | `true` → ✅ Ready · `false` → 📝 Draft (skips publish) |
316
- | `source` | Stripped (internal tooling field) |
317
349
  | *other fields* | Title-cased key, value stringified |
318
350
 
319
351
  If `repo_url` + `edit_uri` are set in `mkdocs.yml`, an **Edit Source** row links to the source file. If `site_url` is set, a **Published Page** row links to the rendered MkDocs site.
@@ -322,21 +354,25 @@ If `repo_url` + `edit_uri` are set in `mkdocs.yml`, an **Edit Source** row links
322
354
 
323
355
  MkDocs abbreviation definitions (`*[ABBR]: Full term`) are expanded inline — Confluence has no native `<abbr>` tooltip. The **first occurrence** in body text is expanded as `IAM (Identity and Access Management)`; subsequent occurrences are left as-is. Abbreviations that only appear in headings or code are collected into an auto-appended **Glossary** section.
324
356
 
325
- ### Graceful degradation
326
-
327
- Any unrecognised block is preserved as a visible `warning` macro — no content is silently lost.
328
-
329
357
  ---
330
358
 
331
359
  ## Known limitations
332
360
 
333
361
  | Feature | Behaviour |
334
362
  |---|---|
335
- | **Admonition styling** | `tip`, `info`, `warning`, `note` use Confluence's fixed native macro styling — no custom colours. `danger`, `error`, and `bug` use a custom red `panel` macro with 🚨 prefix. All other types are mapped to the nearest native macro. |
363
+ | **Admonition styling** | `tip`, `info`, `warning`, `note` use Confluence's fixed native macro colours. `danger`, `error`, `bug` use a custom red `panel` macro with 🚨 prefix. All other types are mapped to the nearest native macro. |
336
364
  | **Abbreviation tooltips** | No native tooltip support. First occurrence expanded inline; remainder left as-is. |
337
- | **Grid cards** | Wrapper stripped; inner admonitions rendered individually. |
338
- | **Page ordering** | Confluence sorts child pages alphabetically. The v2 REST API has no write endpoint for child ordering; nav order cannot be enforced. |
339
- | **Code language aliases** | Pygments short aliases (`py`, `js`, `yml`, `ts`, `sh`) are passed through as-is; Confluence requires full language names for syntax highlighting. |
365
+ | **Page ordering** | Confluence sorts child pages alphabetically; the v2 REST API has no write endpoint for ordering. |
366
+ | **Code language aliases** | Short aliases (`py`, `js`, `yml`, `ts`, `sh`) are passed through as-is; Confluence requires full language names for syntax highlighting. |
367
+ | **Unrecognised blocks** | Preserved as a visible `warning` macro no content is silently lost. |
368
+
369
+ ---
370
+
371
+ ## Architecture
372
+
373
+ ![Architecture](https://raw.githubusercontent.com/jeckyl2010/mkdocs2confluence/main/docs/architecture.png)
374
+
375
+ Each stage is a separate Python module under `src/mkdocs_to_confluence/`. The **plan** phase makes all API read calls (find existing pages); the **execute** phase makes all write calls, ensuring parent pages always exist before their children.
340
376
 
341
377
  ---
342
378
 
@@ -345,8 +381,9 @@ Any unrecognised block is preserved as a visible `warning` macro — no content
345
381
  See [Setup.md](Setup.md) for environment setup.
346
382
 
347
383
  ```bash
348
- pytest # run tests
349
- ruff check src tests # lint
350
- mypy src # type-check
351
- bandit -r src -ll # security scan
384
+ uv run pytest -q # run tests
385
+ uv run ruff check src tests # lint
386
+ uv run vulture src --min-confidence 80 # dead code check
387
+ uv run mypy src # type-check
388
+ uv run bandit -r src -ll # security scan
352
389
  ```