mkdocs2confluence 0.14.0__tar.gz → 0.16.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.
- {mkdocs2confluence-0.14.0/src/mkdocs2confluence.egg-info → mkdocs2confluence-0.16.0}/PKG-INFO +10 -1
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/README.md +9 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/pyproject.toml +29 -1
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0/src/mkdocs2confluence.egg-info}/PKG-INFO +10 -1
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs2confluence.egg-info/SOURCES.txt +8 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/compiler/page.py +21 -1
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/emitter/xhtml.py +19 -2
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/ir/nodes.py +13 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/loader/config.py +26 -0
- mkdocs2confluence-0.16.0/src/mkdocs_to_confluence/preprocess/captions.py +51 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/preprocess/frontmatter.py +16 -6
- mkdocs2confluence-0.16.0/src/mkdocs_to_confluence/transforms/admonition_titles.py +55 -0
- mkdocs2confluence-0.16.0/src/mkdocs_to_confluence/transforms/attachment_previews.py +42 -0
- mkdocs2confluence-0.16.0/src/mkdocs_to_confluence/transforms/captions.py +31 -0
- mkdocs2confluence-0.16.0/tests/test_admonition_titles.py +62 -0
- mkdocs2confluence-0.16.0/tests/test_attachment_previews.py +148 -0
- mkdocs2confluence-0.16.0/tests/test_captions.py +158 -0
- mkdocs2confluence-0.16.0/tests/test_exclude_properties_config.py +52 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_frontmatter.py +49 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_publish_pipeline.py +49 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/LICENSE +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/setup.cfg +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs2confluence.egg-info/dependency_links.txt +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs2confluence.egg-info/entry_points.txt +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs2confluence.egg-info/requires.txt +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs2confluence.egg-info/top_level.txt +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/__init__.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/cli.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/compiler/__init__.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/compiler/models.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/emitter/__init__.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/ir/__init__.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/ir/document.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/ir/treeutil.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/loader/__init__.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/loader/extra_css.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/loader/nav.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/loader/page.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/parser/__init__.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/parser/markdown.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/pdf/__init__.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/pdf/generator.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/pdf/render.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/preprocess/__init__.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/preprocess/abbrevs.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/preprocess/fence.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/preprocess/icons.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/preprocess/includes.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/preprocess/linkdefs.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/preview/__init__.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/preview/render.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/preview/server.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/publisher/__init__.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/publisher/changelog.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/publisher/client.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/publisher/executor.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/publisher/http_retry.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/publisher/models.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/publisher/pipeline.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/publisher/planner.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/skill_installer.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/skills/mkdocs-changelog/SKILL.md +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/skills/mkdocs-changelog/scripts/changelog_data.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/sync/__init__.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/sync/anchoring.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/sync/command.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/sync/comments.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/sync/github.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/sync/platform.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/sync/state.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/transforms/__init__.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/transforms/abbrevs.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/transforms/assets.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/transforms/editlink.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/transforms/footer.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/transforms/images.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/transforms/internallinks.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/transforms/mermaid.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_abbrevs.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_changelog_config.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_changelog_publish.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_children_macro.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_cli.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_editlink.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_emitter.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_extra_css.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_footer.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_icons.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_images.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_internallinks.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_ir.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_linkdefs.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_loader.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_mermaid.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_page_loader.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_parser.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_pdf.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_preprocess.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_preview.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_publish_client.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_publish_config.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_server.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_skill_installer.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_sync_anchoring.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_sync_command.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_sync_comments.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_sync_github.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_sync_state.py +0 -0
- {mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/tests/test_treeutil.py +0 -0
{mkdocs2confluence-0.14.0/src/mkdocs2confluence.egg-info → mkdocs2confluence-0.16.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mkdocs2confluence
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.16.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
|
|
@@ -53,6 +53,7 @@ Dynamic: license-file
|
|
|
53
53
|
[](https://github.com/astral-sh/ruff)
|
|
54
54
|
[](https://mypy-lang.org/)
|
|
55
55
|
[](https://github.com/PyCQA/bandit)
|
|
56
|
+
[](https://github.com/seddonym/import-linter)
|
|
56
57
|
[](https://slsa.dev)
|
|
57
58
|
[](https://securityscorecards.dev/viewer/?uri=github.com/jeckyl2010/mkdocs2confluence)
|
|
58
59
|
|
|
@@ -159,9 +160,17 @@ confluence:
|
|
|
159
160
|
parent_page_id: "123456" # optional root page
|
|
160
161
|
mermaid_render: kroki # "kroki" (default) | "kroki:https://your-kroki" | "none"
|
|
161
162
|
full_width: true # default: true
|
|
163
|
+
attachment_preview: false # default: false — inline PDF/Office previews
|
|
162
164
|
changelog: CHANGELOG.md # optional: publish as a top-level "What's New" page
|
|
165
|
+
exclude_properties: # optional: front matter keys to hide from Page Properties
|
|
166
|
+
- source_documents
|
|
167
|
+
- internal_ref
|
|
163
168
|
```
|
|
164
169
|
|
|
170
|
+
`exclude_properties` lists raw front matter keys to omit from the Page Properties table (e.g. internal tooling fields). Matching is exact and case-sensitive; `title`, `tags`, and `status` keep their special behavior even if excluded from the table.
|
|
171
|
+
|
|
172
|
+
`attachment_preview` (bool, default `false`) renders links to local PDF/Office files (`.pdf`, `.doc(x)`, `.xls(x)`, `.ppt(x)`) as inline `view-file` previews instead of download links. Other file types always remain download links.
|
|
173
|
+
|
|
165
174
|
The `confluence:` block is also accepted under `extra:` for MkDocs strict-mode compatibility. The API token is read from `token:` in `mkdocs.yml`, then `CONFLUENCE_API_TOKEN`, then `MK2CONF_TOKEN`.
|
|
166
175
|
|
|
167
176
|
### Changelog / What's New page
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
[](https://github.com/astral-sh/ruff)
|
|
12
12
|
[](https://mypy-lang.org/)
|
|
13
13
|
[](https://github.com/PyCQA/bandit)
|
|
14
|
+
[](https://github.com/seddonym/import-linter)
|
|
14
15
|
[](https://slsa.dev)
|
|
15
16
|
[](https://securityscorecards.dev/viewer/?uri=github.com/jeckyl2010/mkdocs2confluence)
|
|
16
17
|
|
|
@@ -117,9 +118,17 @@ confluence:
|
|
|
117
118
|
parent_page_id: "123456" # optional root page
|
|
118
119
|
mermaid_render: kroki # "kroki" (default) | "kroki:https://your-kroki" | "none"
|
|
119
120
|
full_width: true # default: true
|
|
121
|
+
attachment_preview: false # default: false — inline PDF/Office previews
|
|
120
122
|
changelog: CHANGELOG.md # optional: publish as a top-level "What's New" page
|
|
123
|
+
exclude_properties: # optional: front matter keys to hide from Page Properties
|
|
124
|
+
- source_documents
|
|
125
|
+
- internal_ref
|
|
121
126
|
```
|
|
122
127
|
|
|
128
|
+
`exclude_properties` lists raw front matter keys to omit from the Page Properties table (e.g. internal tooling fields). Matching is exact and case-sensitive; `title`, `tags`, and `status` keep their special behavior even if excluded from the table.
|
|
129
|
+
|
|
130
|
+
`attachment_preview` (bool, default `false`) renders links to local PDF/Office files (`.pdf`, `.doc(x)`, `.xls(x)`, `.ppt(x)`) as inline `view-file` previews instead of download links. Other file types always remain download links.
|
|
131
|
+
|
|
123
132
|
The `confluence:` block is also accepted under `extra:` for MkDocs strict-mode compatibility. The API token is read from `token:` in `mkdocs.yml`, then `CONFLUENCE_API_TOKEN`, then `MK2CONF_TOKEN`.
|
|
124
133
|
|
|
125
134
|
### Changelog / What's New page
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mkdocs2confluence"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.16.0"
|
|
4
4
|
description = "Publish MkDocs Material pages to Confluence Cloud — admonitions, Mermaid diagrams, tabs, page properties and more"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "GPL-3.0-or-later" }
|
|
@@ -103,8 +103,36 @@ exclude_lines = [
|
|
|
103
103
|
"raise NotImplementedError",
|
|
104
104
|
]
|
|
105
105
|
|
|
106
|
+
# Architecture boundaries — enforces the compile pipeline stage separation
|
|
107
|
+
# documented in .github/copilot-instructions.md. Higher layers may import lower
|
|
108
|
+
# ones; siblings joined by "|" must stay independent of each other.
|
|
109
|
+
[tool.importlinter]
|
|
110
|
+
root_package = "mkdocs_to_confluence"
|
|
111
|
+
|
|
112
|
+
[[tool.importlinter.contracts]]
|
|
113
|
+
name = "Compiler pipeline stage layering"
|
|
114
|
+
type = "layers"
|
|
115
|
+
containers = ["mkdocs_to_confluence"]
|
|
116
|
+
layers = [
|
|
117
|
+
"sync",
|
|
118
|
+
"publisher",
|
|
119
|
+
"compiler",
|
|
120
|
+
"preprocess | parser | transforms | emitter",
|
|
121
|
+
"ir | loader",
|
|
122
|
+
]
|
|
123
|
+
|
|
124
|
+
[[tool.importlinter.contracts]]
|
|
125
|
+
name = "Renderer layering (preview/pdf)"
|
|
126
|
+
type = "layers"
|
|
127
|
+
containers = ["mkdocs_to_confluence"]
|
|
128
|
+
layers = [
|
|
129
|
+
"pdf",
|
|
130
|
+
"preview",
|
|
131
|
+
]
|
|
132
|
+
|
|
106
133
|
[dependency-groups]
|
|
107
134
|
dev = [
|
|
135
|
+
"import-linter>=2.11",
|
|
108
136
|
"pip-audit>=2.10.0",
|
|
109
137
|
"radon>=6.0.1",
|
|
110
138
|
"vulture>=2.16",
|
{mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0/src/mkdocs2confluence.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mkdocs2confluence
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.16.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
|
|
@@ -53,6 +53,7 @@ Dynamic: license-file
|
|
|
53
53
|
[](https://github.com/astral-sh/ruff)
|
|
54
54
|
[](https://mypy-lang.org/)
|
|
55
55
|
[](https://github.com/PyCQA/bandit)
|
|
56
|
+
[](https://github.com/seddonym/import-linter)
|
|
56
57
|
[](https://slsa.dev)
|
|
57
58
|
[](https://securityscorecards.dev/viewer/?uri=github.com/jeckyl2010/mkdocs2confluence)
|
|
58
59
|
|
|
@@ -159,9 +160,17 @@ confluence:
|
|
|
159
160
|
parent_page_id: "123456" # optional root page
|
|
160
161
|
mermaid_render: kroki # "kroki" (default) | "kroki:https://your-kroki" | "none"
|
|
161
162
|
full_width: true # default: true
|
|
163
|
+
attachment_preview: false # default: false — inline PDF/Office previews
|
|
162
164
|
changelog: CHANGELOG.md # optional: publish as a top-level "What's New" page
|
|
165
|
+
exclude_properties: # optional: front matter keys to hide from Page Properties
|
|
166
|
+
- source_documents
|
|
167
|
+
- internal_ref
|
|
163
168
|
```
|
|
164
169
|
|
|
170
|
+
`exclude_properties` lists raw front matter keys to omit from the Page Properties table (e.g. internal tooling fields). Matching is exact and case-sensitive; `title`, `tags`, and `status` keep their special behavior even if excluded from the table.
|
|
171
|
+
|
|
172
|
+
`attachment_preview` (bool, default `false`) renders links to local PDF/Office files (`.pdf`, `.doc(x)`, `.xls(x)`, `.ppt(x)`) as inline `view-file` previews instead of download links. Other file types always remain download links.
|
|
173
|
+
|
|
165
174
|
The `confluence:` block is also accepted under `extra:` for MkDocs strict-mode compatibility. The API token is read from `token:` in `mkdocs.yml`, then `CONFLUENCE_API_TOKEN`, then `MK2CONF_TOKEN`.
|
|
166
175
|
|
|
167
176
|
### Changelog / What's New page
|
{mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs2confluence.egg-info/SOURCES.txt
RENAMED
|
@@ -31,6 +31,7 @@ src/mkdocs_to_confluence/pdf/generator.py
|
|
|
31
31
|
src/mkdocs_to_confluence/pdf/render.py
|
|
32
32
|
src/mkdocs_to_confluence/preprocess/__init__.py
|
|
33
33
|
src/mkdocs_to_confluence/preprocess/abbrevs.py
|
|
34
|
+
src/mkdocs_to_confluence/preprocess/captions.py
|
|
34
35
|
src/mkdocs_to_confluence/preprocess/fence.py
|
|
35
36
|
src/mkdocs_to_confluence/preprocess/frontmatter.py
|
|
36
37
|
src/mkdocs_to_confluence/preprocess/icons.py
|
|
@@ -58,19 +59,26 @@ src/mkdocs_to_confluence/sync/platform.py
|
|
|
58
59
|
src/mkdocs_to_confluence/sync/state.py
|
|
59
60
|
src/mkdocs_to_confluence/transforms/__init__.py
|
|
60
61
|
src/mkdocs_to_confluence/transforms/abbrevs.py
|
|
62
|
+
src/mkdocs_to_confluence/transforms/admonition_titles.py
|
|
61
63
|
src/mkdocs_to_confluence/transforms/assets.py
|
|
64
|
+
src/mkdocs_to_confluence/transforms/attachment_previews.py
|
|
65
|
+
src/mkdocs_to_confluence/transforms/captions.py
|
|
62
66
|
src/mkdocs_to_confluence/transforms/editlink.py
|
|
63
67
|
src/mkdocs_to_confluence/transforms/footer.py
|
|
64
68
|
src/mkdocs_to_confluence/transforms/images.py
|
|
65
69
|
src/mkdocs_to_confluence/transforms/internallinks.py
|
|
66
70
|
src/mkdocs_to_confluence/transforms/mermaid.py
|
|
67
71
|
tests/test_abbrevs.py
|
|
72
|
+
tests/test_admonition_titles.py
|
|
73
|
+
tests/test_attachment_previews.py
|
|
74
|
+
tests/test_captions.py
|
|
68
75
|
tests/test_changelog_config.py
|
|
69
76
|
tests/test_changelog_publish.py
|
|
70
77
|
tests/test_children_macro.py
|
|
71
78
|
tests/test_cli.py
|
|
72
79
|
tests/test_editlink.py
|
|
73
80
|
tests/test_emitter.py
|
|
81
|
+
tests/test_exclude_properties_config.py
|
|
74
82
|
tests/test_extra_css.py
|
|
75
83
|
tests/test_footer.py
|
|
76
84
|
tests/test_frontmatter.py
|
{mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/compiler/page.py
RENAMED
|
@@ -13,6 +13,7 @@ from mkdocs_to_confluence.preprocess.abbrevs import (
|
|
|
13
13
|
extract_abbreviations,
|
|
14
14
|
strip_abbreviation_defs,
|
|
15
15
|
)
|
|
16
|
+
from mkdocs_to_confluence.preprocess.captions import rewrite_figure_captions
|
|
16
17
|
from mkdocs_to_confluence.preprocess.frontmatter import extract_front_matter
|
|
17
18
|
from mkdocs_to_confluence.preprocess.icons import strip_icon_shortcodes
|
|
18
19
|
from mkdocs_to_confluence.preprocess.includes import (
|
|
@@ -26,7 +27,14 @@ from mkdocs_to_confluence.preprocess.linkdefs import (
|
|
|
26
27
|
strip_link_defs,
|
|
27
28
|
)
|
|
28
29
|
from mkdocs_to_confluence.transforms.abbrevs import apply_abbreviations
|
|
30
|
+
from mkdocs_to_confluence.transforms.admonition_titles import (
|
|
31
|
+
strip_links_in_admonition_titles,
|
|
32
|
+
)
|
|
29
33
|
from mkdocs_to_confluence.transforms.assets import resolve_local_assets
|
|
34
|
+
from mkdocs_to_confluence.transforms.attachment_previews import (
|
|
35
|
+
resolve_attachment_previews,
|
|
36
|
+
)
|
|
37
|
+
from mkdocs_to_confluence.transforms.captions import resolve_captions
|
|
30
38
|
from mkdocs_to_confluence.transforms.editlink import attach_source_url
|
|
31
39
|
from mkdocs_to_confluence.transforms.footer import build_source_footer
|
|
32
40
|
from mkdocs_to_confluence.transforms.internallinks import resolve_internal_links
|
|
@@ -53,15 +61,22 @@ def compile_page(
|
|
|
53
61
|
docs_dir=config.docs_dir,
|
|
54
62
|
)
|
|
55
63
|
preprocessed = strip_unsupported_html(preprocessed)
|
|
64
|
+
preprocessed = rewrite_figure_captions(preprocessed)
|
|
56
65
|
preprocessed = strip_html_comments(preprocessed)
|
|
57
66
|
preprocessed = strip_icon_shortcodes(preprocessed)
|
|
58
|
-
|
|
67
|
+
exclude_properties = (
|
|
68
|
+
config.confluence.exclude_properties if config.confluence else ()
|
|
69
|
+
)
|
|
70
|
+
front_matter, preprocessed = extract_front_matter(
|
|
71
|
+
preprocessed, exclude_properties=exclude_properties
|
|
72
|
+
)
|
|
59
73
|
abbrevs = extract_abbreviations(preprocessed)
|
|
60
74
|
preprocessed = strip_abbreviation_defs(preprocessed)
|
|
61
75
|
link_defs = collect_link_defs(preprocessed)
|
|
62
76
|
preprocessed = expand_link_refs(preprocessed, link_defs)
|
|
63
77
|
preprocessed = strip_link_defs(preprocessed)
|
|
64
78
|
ir_nodes = parse(preprocessed)
|
|
79
|
+
ir_nodes = strip_links_in_admonition_titles(ir_nodes, node.docs_path or "")
|
|
65
80
|
if is_section_index:
|
|
66
81
|
ir_nodes = ir_nodes + (ChildrenMacro(),)
|
|
67
82
|
ir_nodes = apply_abbreviations(ir_nodes, abbrevs, page_text=preprocessed)
|
|
@@ -70,6 +85,11 @@ def compile_page(
|
|
|
70
85
|
page_path=node.source_path,
|
|
71
86
|
docs_dir=config.docs_dir,
|
|
72
87
|
)
|
|
88
|
+
ir_nodes = resolve_captions(ir_nodes)
|
|
89
|
+
attachment_preview = (
|
|
90
|
+
config.confluence.attachment_preview if config.confluence else False
|
|
91
|
+
)
|
|
92
|
+
ir_nodes = resolve_attachment_previews(ir_nodes, enabled=attachment_preview)
|
|
73
93
|
mermaid_render = config.confluence.mermaid_render if config.confluence else "kroki"
|
|
74
94
|
if mermaid_render != "none":
|
|
75
95
|
kroki_url = (
|
{mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/emitter/xhtml.py
RENAMED
|
@@ -25,6 +25,7 @@ from mkdocs_to_confluence.ir.nodes import (
|
|
|
25
25
|
AbbrevGlossaryBlock,
|
|
26
26
|
Admonition,
|
|
27
27
|
AnchorNode,
|
|
28
|
+
AttachmentPreview,
|
|
28
29
|
BlockQuote,
|
|
29
30
|
BoldNode,
|
|
30
31
|
BulletList,
|
|
@@ -685,6 +686,8 @@ def _emit_inline(node: IRNode) -> str:
|
|
|
685
686
|
return f"<code{style_attr}>{html.escape(node.code)}</code>"
|
|
686
687
|
if isinstance(node, LinkNode):
|
|
687
688
|
return _emit_link(node)
|
|
689
|
+
if isinstance(node, AttachmentPreview):
|
|
690
|
+
return _emit_attachment_preview(node)
|
|
688
691
|
if isinstance(node, ImageNode):
|
|
689
692
|
return _emit_image(node)
|
|
690
693
|
if isinstance(node, FootnoteRef):
|
|
@@ -709,6 +712,15 @@ def _emit_inline(node: IRNode) -> str:
|
|
|
709
712
|
return html.escape(repr(node))
|
|
710
713
|
|
|
711
714
|
|
|
715
|
+
def _emit_attachment_preview(node: AttachmentPreview) -> str:
|
|
716
|
+
filename = html.escape(node.filename, quote=True)
|
|
717
|
+
return (
|
|
718
|
+
'<ac:structured-macro ac:name="view-file">'
|
|
719
|
+
f'<ac:parameter ac:name="name"><ri:attachment ri:filename="{filename}"/></ac:parameter>'
|
|
720
|
+
"</ac:structured-macro>"
|
|
721
|
+
)
|
|
722
|
+
|
|
723
|
+
|
|
712
724
|
def _emit_link(node: LinkNode) -> str:
|
|
713
725
|
# Same-page anchor link: [text](#id) → ac:link with only ac:anchor.
|
|
714
726
|
# Confluence strips a raw <a href="#id">, so it must use the anchor form.
|
|
@@ -758,14 +770,19 @@ def _emit_image(node: ImageNode) -> str:
|
|
|
758
770
|
height_attr = f' ac:height="{node.height}"' if node.height is not None else ""
|
|
759
771
|
align_attr = f' ac:align="{html.escape(node.align)}"' if node.align else ""
|
|
760
772
|
size_attrs = width_attr + height_attr + align_attr
|
|
773
|
+
caption = (
|
|
774
|
+
f"<ac:caption><p>{html.escape(node.caption)}</p></ac:caption>"
|
|
775
|
+
if node.caption
|
|
776
|
+
else ""
|
|
777
|
+
)
|
|
761
778
|
# Local file → attachment reference; URL → external ri:url
|
|
762
779
|
src = node.src
|
|
763
780
|
if src.startswith(("http://", "https://", "//", "data:")):
|
|
764
781
|
ref = f'<ri:url ri:value="{html.escape(src)}"/>'
|
|
765
|
-
return f"<ac:image{alt_attr}{title_attr}{size_attrs}>{ref}</ac:image>"
|
|
782
|
+
return f"<ac:image{alt_attr}{title_attr}{size_attrs}>{ref}{caption}</ac:image>"
|
|
766
783
|
else:
|
|
767
784
|
filename = html.escape(node.attachment_name or Path(src).name)
|
|
768
785
|
# data-local-path is used by the preview renderer only (not valid XHTML)
|
|
769
786
|
local_attr = f' data-local-path="{html.escape(src)}"'
|
|
770
787
|
ref = f'<ri:attachment ri:filename="{filename}"/>'
|
|
771
|
-
return f"<ac:image{alt_attr}{title_attr}{size_attrs}{local_attr}>{ref}</ac:image>"
|
|
788
|
+
return f"<ac:image{alt_attr}{title_attr}{size_attrs}{local_attr}>{ref}{caption}</ac:image>"
|
|
@@ -162,6 +162,7 @@ class ImageNode(IRNode):
|
|
|
162
162
|
src: str
|
|
163
163
|
alt: str
|
|
164
164
|
title: str | None = None
|
|
165
|
+
caption: str | None = None
|
|
165
166
|
attachment_name: str | None = None
|
|
166
167
|
width: int | None = None
|
|
167
168
|
height: int | None = None
|
|
@@ -452,6 +453,18 @@ class ChildrenMacro(IRNode):
|
|
|
452
453
|
"""
|
|
453
454
|
|
|
454
455
|
|
|
456
|
+
@dataclass(frozen=True)
|
|
457
|
+
class AttachmentPreview(IRNode):
|
|
458
|
+
"""An inline preview of an uploaded attachment (PDF/Office file).
|
|
459
|
+
|
|
460
|
+
``filename`` is the collision-safe Confluence attachment name (the same
|
|
461
|
+
value carried on the originating ``LinkNode.attachment_name``). The emitter
|
|
462
|
+
renders a ``view-file`` macro referencing the attachment.
|
|
463
|
+
"""
|
|
464
|
+
|
|
465
|
+
filename: str
|
|
466
|
+
|
|
467
|
+
|
|
455
468
|
@dataclass(frozen=True)
|
|
456
469
|
class SourceFooter(IRNode):
|
|
457
470
|
"""Footer panel showing source-control links and last-commit info.
|
{mkdocs2confluence-0.14.0 → mkdocs2confluence-0.16.0}/src/mkdocs_to_confluence/loader/config.py
RENAMED
|
@@ -35,6 +35,8 @@ class ConfluenceConfig:
|
|
|
35
35
|
github_base_branch: str = "main" # base branch for review PRs
|
|
36
36
|
allow_any_host: bool = False # set True to allow non-Atlassian Cloud base_url hosts
|
|
37
37
|
changelog_file: str | None = None # path relative to docs_dir; None means disabled
|
|
38
|
+
exclude_properties: tuple[str, ...] = () # front matter keys to omit from Page Properties table
|
|
39
|
+
attachment_preview: bool = False # render PDF/Office attachment links as view-file macros
|
|
38
40
|
|
|
39
41
|
|
|
40
42
|
@dataclass(frozen=True)
|
|
@@ -276,6 +278,28 @@ def load_config(mkdocs_yml: Path) -> MkDocsConfig:
|
|
|
276
278
|
)
|
|
277
279
|
changelog_file = cl_str
|
|
278
280
|
|
|
281
|
+
# exclude_properties (optional) — raw front matter keys to omit from
|
|
282
|
+
# the Page Properties table. Pure list of literal keys, no wildcards.
|
|
283
|
+
raw_exclude = raw_conf.get("exclude_properties")
|
|
284
|
+
if raw_exclude is None:
|
|
285
|
+
exclude_properties: tuple[str, ...] = ()
|
|
286
|
+
elif isinstance(raw_exclude, list):
|
|
287
|
+
exclude_properties = tuple(str(k) for k in raw_exclude)
|
|
288
|
+
else:
|
|
289
|
+
raise ConfigError(
|
|
290
|
+
"mkdocs.yml: 'confluence.exclude_properties' must be a list of "
|
|
291
|
+
f"front matter keys, got {type(raw_exclude).__name__}."
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
# attachment_preview (optional) — render PDF/Office attachment links inline
|
|
295
|
+
raw_preview = raw_conf.get("attachment_preview", False)
|
|
296
|
+
if not isinstance(raw_preview, bool):
|
|
297
|
+
raise ConfigError(
|
|
298
|
+
"mkdocs.yml: 'confluence.attachment_preview' must be a boolean, "
|
|
299
|
+
f"got {type(raw_preview).__name__}."
|
|
300
|
+
)
|
|
301
|
+
attachment_preview = raw_preview
|
|
302
|
+
|
|
279
303
|
confluence = ConfluenceConfig(
|
|
280
304
|
base_url=base_url.rstrip("/"),
|
|
281
305
|
space_key=space_key,
|
|
@@ -291,6 +315,8 @@ def load_config(mkdocs_yml: Path) -> MkDocsConfig:
|
|
|
291
315
|
github_base_branch=str(raw_conf.get("github_base_branch", "main")),
|
|
292
316
|
allow_any_host=allow_any_host,
|
|
293
317
|
changelog_file=changelog_file,
|
|
318
|
+
exclude_properties=exclude_properties,
|
|
319
|
+
attachment_preview=attachment_preview,
|
|
294
320
|
)
|
|
295
321
|
|
|
296
322
|
# --- extra_css (optional) ---
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Figure/figcaption preprocess rewrite.
|
|
2
|
+
|
|
3
|
+
Material for MkDocs authors captions with the ``md_in_html`` figure form::
|
|
4
|
+
|
|
5
|
+
<figure markdown="span">
|
|
6
|
+

|
|
7
|
+
<figcaption>The caption</figcaption>
|
|
8
|
+
</figure>
|
|
9
|
+
|
|
10
|
+
Confluence storage format has no ``<figure>`` element, so this pass rewrites
|
|
11
|
+
such a block into a single titled Markdown image::
|
|
12
|
+
|
|
13
|
+

|
|
14
|
+
|
|
15
|
+
The image then flows through the normal parser and the ``resolve_captions``
|
|
16
|
+
transform promotes the title to an ``ac:caption``. The figcaption text always
|
|
17
|
+
wins over any pre-existing image title (it is substituted into the title slot).
|
|
18
|
+
|
|
19
|
+
Limitations (graceful — non-matching blocks are left untouched):
|
|
20
|
+
|
|
21
|
+
* The figcaption is treated as plain text (``[^<]*``); inline HTML such as
|
|
22
|
+
``<em>`` inside a figcaption is not supported and the block is skipped.
|
|
23
|
+
* An image with a trailing attr-list (``{ width=300 }``) is skipped.
|
|
24
|
+
* ``src`` must be space-free (percent-encode spaces), per Markdown convention.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
import re
|
|
30
|
+
|
|
31
|
+
# The caption group is plain-text only (``[^<]*``) so it cannot run past a
|
|
32
|
+
# missing ``</figcaption>`` and swallow a following ``<figure>`` block.
|
|
33
|
+
_FIGURE_RE = re.compile(
|
|
34
|
+
r"<figure\b[^>]*>\s*"
|
|
35
|
+
r"!\[(?P<alt>[^\]]*)\]\((?P<src>[^\s)]+)(?:\s+\"[^\"]*\")?\)\s*"
|
|
36
|
+
r"<figcaption>(?P<cap>[^<]*)</figcaption>\s*"
|
|
37
|
+
r"</figure>",
|
|
38
|
+
re.IGNORECASE,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def rewrite_figure_captions(text: str) -> str:
|
|
43
|
+
"""Rewrite ``<figure>…<figcaption>…</figure>`` blocks to titled images."""
|
|
44
|
+
|
|
45
|
+
def _sub(m: re.Match[str]) -> str:
|
|
46
|
+
alt = m.group("alt")
|
|
47
|
+
src = m.group("src")
|
|
48
|
+
cap = m.group("cap").strip().replace('"', "'")
|
|
49
|
+
return f''
|
|
50
|
+
|
|
51
|
+
return _FIGURE_RE.sub(_sub, text)
|
|
@@ -80,11 +80,16 @@ _FRONT_MATTER_RE = re.compile(r"\A---\s*\n(.*?\n?)---\s*\n?", re.DOTALL)
|
|
|
80
80
|
# ── Public API ────────────────────────────────────────────────────────────────
|
|
81
81
|
|
|
82
82
|
|
|
83
|
-
def extract_front_matter(
|
|
83
|
+
def extract_front_matter(
|
|
84
|
+
text: str, exclude_properties: tuple[str, ...] = ()
|
|
85
|
+
) -> tuple[FrontMatter | None, str]:
|
|
84
86
|
"""Parse YAML front matter from the top of *text*.
|
|
85
87
|
|
|
86
88
|
Args:
|
|
87
89
|
text: Raw markdown content.
|
|
90
|
+
exclude_properties: Raw front matter keys to omit from the Page
|
|
91
|
+
Properties table. Matching is exact and case-sensitive. Special
|
|
92
|
+
behaviors (title, tags->labels, status) are unaffected.
|
|
88
93
|
|
|
89
94
|
Returns:
|
|
90
95
|
A ``(FrontMatter | None, remaining_text)`` tuple. ``FrontMatter`` is
|
|
@@ -105,14 +110,19 @@ def extract_front_matter(text: str) -> tuple[FrontMatter | None, str]:
|
|
|
105
110
|
if not isinstance(raw, dict):
|
|
106
111
|
return None, text
|
|
107
112
|
|
|
108
|
-
return _build_node(raw), remaining
|
|
113
|
+
return _build_node(raw, exclude_properties), remaining
|
|
109
114
|
|
|
110
115
|
|
|
111
116
|
# ── Private helpers ───────────────────────────────────────────────────────────
|
|
112
117
|
|
|
113
118
|
|
|
114
|
-
def _build_node(raw: dict[str, Any]) -> FrontMatter:
|
|
115
|
-
"""Convert a raw front matter dict to a :class:`FrontMatter` IR node.
|
|
119
|
+
def _build_node(raw: dict[str, Any], exclude: tuple[str, ...] = ()) -> FrontMatter:
|
|
120
|
+
"""Convert a raw front matter dict to a :class:`FrontMatter` IR node.
|
|
121
|
+
|
|
122
|
+
``exclude`` lists raw keys to omit from the properties table (table rows
|
|
123
|
+
only — title/labels/status side-effects are computed independently below).
|
|
124
|
+
"""
|
|
125
|
+
skip = _STRIP_FIELDS | set(exclude)
|
|
116
126
|
title: str | None = _stringify(raw.get("title")) if "title" in raw else None
|
|
117
127
|
subtitle: str | None = _stringify(raw.get("subtitle")) if "subtitle" in raw else None
|
|
118
128
|
|
|
@@ -129,14 +139,14 @@ def _build_node(raw: dict[str, Any]) -> FrontMatter:
|
|
|
129
139
|
seen: set[str] = set()
|
|
130
140
|
|
|
131
141
|
for key in _FIELD_ORDER:
|
|
132
|
-
if key in raw and key not in
|
|
142
|
+
if key in raw and key not in skip and key != "subtitle":
|
|
133
143
|
display = _DISPLAY_NAMES.get(key, _humanize(key))
|
|
134
144
|
properties.append((display, _format_value(key, raw[key])))
|
|
135
145
|
seen.add(key)
|
|
136
146
|
|
|
137
147
|
# Append any remaining unknown fields in document order.
|
|
138
148
|
for key, value in raw.items():
|
|
139
|
-
if key in seen or key in
|
|
149
|
+
if key in seen or key in skip or key == "subtitle":
|
|
140
150
|
continue
|
|
141
151
|
display = _DISPLAY_NAMES.get(key, _humanize(key))
|
|
142
152
|
properties.append((display, _format_value(key, value)))
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Degrade Markdown links inside admonition titles.
|
|
2
|
+
|
|
3
|
+
A Confluence macro title is an ``<ac:parameter>`` and holds plain text only —
|
|
4
|
+
it cannot contain ``<ac:link>`` or any markup. A Markdown link written in an
|
|
5
|
+
admonition title therefore cannot render. This transform replaces each inline
|
|
6
|
+
link ``[text](target)`` in an ``Admonition`` title with its ``text`` and warns
|
|
7
|
+
to stderr so the author can adjust the source.
|
|
8
|
+
|
|
9
|
+
Images (````) are deliberately left untouched (negative lookbehind on
|
|
10
|
+
``!``). Reference-style links and autolinks are out of scope.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import dataclasses
|
|
16
|
+
import re
|
|
17
|
+
import sys
|
|
18
|
+
|
|
19
|
+
from mkdocs_to_confluence.ir.nodes import Admonition, IRNode, walk
|
|
20
|
+
from mkdocs_to_confluence.ir.treeutil import replace_nodes
|
|
21
|
+
|
|
22
|
+
# Inline Markdown link [text](target), but not an image (no leading '!').
|
|
23
|
+
_LINK_RE = re.compile(r"(?<!!)\[([^\]]+)\]\(([^)]+)\)")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def strip_links_in_admonition_titles(
|
|
27
|
+
nodes: tuple[IRNode, ...], page_path: str
|
|
28
|
+
) -> tuple[IRNode, ...]:
|
|
29
|
+
"""Replace inline links in admonition titles with their link text.
|
|
30
|
+
|
|
31
|
+
Emits a transpiler warning to stderr for each affected title. Returns the
|
|
32
|
+
original *nodes* unchanged when no title contained a link.
|
|
33
|
+
"""
|
|
34
|
+
replacements: dict[int, IRNode] = {}
|
|
35
|
+
|
|
36
|
+
for top in nodes:
|
|
37
|
+
for node in walk(top):
|
|
38
|
+
if not isinstance(node, Admonition) or node.title is None:
|
|
39
|
+
continue
|
|
40
|
+
new_title = _LINK_RE.sub(r"\1", node.title)
|
|
41
|
+
if new_title == node.title:
|
|
42
|
+
continue
|
|
43
|
+
_warn(
|
|
44
|
+
"link in admonition title not supported by Confluence "
|
|
45
|
+
f'(using link text): "{node.title}" in {page_path}'
|
|
46
|
+
)
|
|
47
|
+
replacements[id(node)] = dataclasses.replace(node, title=new_title)
|
|
48
|
+
|
|
49
|
+
if not replacements:
|
|
50
|
+
return nodes
|
|
51
|
+
return replace_nodes(nodes, replacements)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _warn(msg: str) -> None:
|
|
55
|
+
print(f" warning {msg}", file=sys.stderr)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Attachment inline preview transform.
|
|
2
|
+
|
|
3
|
+
When enabled, replaces ``LinkNode``s that point at an uploaded PDF or Office
|
|
4
|
+
attachment (``attachment_name`` set by ``resolve_local_assets``) with an
|
|
5
|
+
:class:`AttachmentPreview` node, which the emitter renders as a Confluence
|
|
6
|
+
``view-file`` macro. Links to non-previewable file types, external URLs, and
|
|
7
|
+
internal pages are left unchanged.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from mkdocs_to_confluence.ir.nodes import AttachmentPreview, IRNode, LinkNode, walk
|
|
13
|
+
from mkdocs_to_confluence.ir.treeutil import replace_nodes
|
|
14
|
+
|
|
15
|
+
PREVIEWABLE_EXTENSIONS: frozenset[str] = frozenset(
|
|
16
|
+
{".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx"}
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def resolve_attachment_previews(
|
|
21
|
+
nodes: tuple[IRNode, ...], *, enabled: bool
|
|
22
|
+
) -> tuple[IRNode, ...]:
|
|
23
|
+
"""Swap eligible attachment links for ``AttachmentPreview`` nodes."""
|
|
24
|
+
if not enabled:
|
|
25
|
+
return nodes
|
|
26
|
+
replacements: dict[int, IRNode] = {}
|
|
27
|
+
for top_node in nodes:
|
|
28
|
+
for node in walk(top_node):
|
|
29
|
+
if not isinstance(node, LinkNode) or node.attachment_name is None:
|
|
30
|
+
continue
|
|
31
|
+
ext = _extension(node.attachment_name)
|
|
32
|
+
if ext not in PREVIEWABLE_EXTENSIONS:
|
|
33
|
+
continue
|
|
34
|
+
replacements[id(node)] = AttachmentPreview(filename=node.attachment_name)
|
|
35
|
+
if not replacements:
|
|
36
|
+
return nodes
|
|
37
|
+
return replace_nodes(nodes, replacements)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _extension(name: str) -> str:
|
|
41
|
+
dot = name.rfind(".")
|
|
42
|
+
return name[dot:].lower() if dot != -1 else ""
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Caption resolution transform.
|
|
2
|
+
|
|
3
|
+
Fills :attr:`ImageNode.caption` from the image ``title`` attribute when no
|
|
4
|
+
caption is already present, and clears ``title`` so the same text is not also
|
|
5
|
+
emitted as a hover tooltip. Images whose caption is already set (e.g. from a
|
|
6
|
+
``<figcaption>`` rewrite) are left untouched, so figcaptions take precedence.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import dataclasses
|
|
12
|
+
|
|
13
|
+
from mkdocs_to_confluence.ir.nodes import ImageNode, IRNode, walk
|
|
14
|
+
from mkdocs_to_confluence.ir.treeutil import replace_nodes
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def resolve_captions(nodes: tuple[IRNode, ...]) -> tuple[IRNode, ...]:
|
|
18
|
+
"""Promote image ``title`` to ``caption`` where no caption exists yet."""
|
|
19
|
+
replacements: dict[int, IRNode] = {}
|
|
20
|
+
for top_node in nodes:
|
|
21
|
+
for node in walk(top_node):
|
|
22
|
+
if not isinstance(node, ImageNode):
|
|
23
|
+
continue
|
|
24
|
+
if node.caption is not None or node.title is None:
|
|
25
|
+
continue
|
|
26
|
+
replacements[id(node)] = dataclasses.replace(
|
|
27
|
+
node, caption=node.title, title=None
|
|
28
|
+
)
|
|
29
|
+
if not replacements:
|
|
30
|
+
return nodes
|
|
31
|
+
return replace_nodes(nodes, replacements)
|