typsphinx 0.4.4__tar.gz → 0.6.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 (81) hide show
  1. {typsphinx-0.4.4/typsphinx.egg-info → typsphinx-0.6.0}/PKG-INFO +16 -18
  2. {typsphinx-0.4.4 → typsphinx-0.6.0}/README.md +9 -9
  3. {typsphinx-0.4.4 → typsphinx-0.6.0}/pyproject.toml +23 -12
  4. typsphinx-0.6.0/tests/test_admonitions.py +433 -0
  5. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_builder.py +1 -1
  6. typsphinx-0.6.0/tests/test_confval_field_body_render_gate.py +192 -0
  7. typsphinx-0.6.0/tests/test_corpus_gate.py +557 -0
  8. typsphinx-0.6.0/tests/test_cross_doc_label_namespace_render_gate.py +230 -0
  9. typsphinx-0.6.0/tests/test_deflist_definition_multiblock_render_gate.py +174 -0
  10. typsphinx-0.6.0/tests/test_deflist_nested_definition_render_gate.py +229 -0
  11. typsphinx-0.6.0/tests/test_deflist_term_concat_render_gate.py +167 -0
  12. typsphinx-0.6.0/tests/test_deflist_term_inline_children_gate.py +321 -0
  13. typsphinx-0.6.0/tests/test_desc_container_propagated_target_render_gate.py +224 -0
  14. typsphinx-0.6.0/tests/test_desc_signature_anchor_render_gate.py +195 -0
  15. typsphinx-0.6.0/tests/test_desc_signature_concat_render_gate.py +195 -0
  16. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_documentation_configuration.py +2 -1
  17. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_documentation_usage.py +2 -1
  18. typsphinx-0.6.0/tests/test_duplicate_include_label_render_gate.py +202 -0
  19. typsphinx-0.6.0/tests/test_epigraph_render_gate.py +202 -0
  20. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_extension.py +19 -0
  21. typsphinx-0.6.0/tests/test_field_list_in_list_item_render_gate.py +179 -0
  22. typsphinx-0.6.0/tests/test_footnotes.py +173 -0
  23. typsphinx-0.6.0/tests/test_glob_image_render_gate.py +190 -0
  24. typsphinx-0.6.0/tests/test_label_at_char_render_gate.py +213 -0
  25. typsphinx-0.6.0/tests/test_line_blocks.py +147 -0
  26. typsphinx-0.6.0/tests/test_list_item_nested_block_render_gate.py +177 -0
  27. typsphinx-0.6.0/tests/test_paragraph_propagated_target_render_gate.py +212 -0
  28. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_pdf_generation.py +1 -1
  29. typsphinx-0.6.0/tests/test_pdf_render_gate.py +1777 -0
  30. typsphinx-0.6.0/tests/test_preview_smoke_gate.py +112 -0
  31. typsphinx-0.6.0/tests/test_ref_target_nested_list_render_gate.py +232 -0
  32. typsphinx-0.6.0/tests/test_rubric_propagated_target_render_gate.py +245 -0
  33. typsphinx-0.6.0/tests/test_static_asset_copy_gate.py +155 -0
  34. typsphinx-0.6.0/tests/test_substitution_definition_render_gate.py +224 -0
  35. typsphinx-0.6.0/tests/test_table_in_list_item_render_gate.py +189 -0
  36. typsphinx-0.6.0/tests/test_target_label_render_gate.py +159 -0
  37. typsphinx-0.6.0/tests/test_topics.py +202 -0
  38. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_translator.py +355 -68
  39. typsphinx-0.6.0/tests/test_typst_string_escape_gate.py +65 -0
  40. typsphinx-0.6.0/tests/test_xref_orphan_degrade_render_gate.py +195 -0
  41. {typsphinx-0.4.4 → typsphinx-0.6.0}/typsphinx/__init__.py +7 -3
  42. {typsphinx-0.4.4 → typsphinx-0.6.0}/typsphinx/builder.py +166 -5
  43. {typsphinx-0.4.4 → typsphinx-0.6.0}/typsphinx/template_engine.py +4 -4
  44. {typsphinx-0.4.4 → typsphinx-0.6.0}/typsphinx/templates/base.typ +3 -3
  45. typsphinx-0.6.0/typsphinx/translator.py +4581 -0
  46. {typsphinx-0.4.4 → typsphinx-0.6.0}/typsphinx/writer.py +3 -3
  47. {typsphinx-0.4.4 → typsphinx-0.6.0/typsphinx.egg-info}/PKG-INFO +16 -18
  48. typsphinx-0.6.0/typsphinx.egg-info/SOURCES.txt +76 -0
  49. {typsphinx-0.4.4 → typsphinx-0.6.0}/typsphinx.egg-info/requires.txt +5 -7
  50. typsphinx-0.4.4/tests/test_admonitions.py +0 -216
  51. typsphinx-0.4.4/typsphinx/translator.py +0 -2679
  52. typsphinx-0.4.4/typsphinx.egg-info/SOURCES.txt +0 -46
  53. {typsphinx-0.4.4 → typsphinx-0.6.0}/LICENSE +0 -0
  54. {typsphinx-0.4.4 → typsphinx-0.6.0}/setup.cfg +0 -0
  55. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_builder_requirement13.py +0 -0
  56. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_config.py +0 -0
  57. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_config_other_options.py +0 -0
  58. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_config_template_mapping.py +0 -0
  59. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_config_toctree_defaults.py +0 -0
  60. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_documentation_installation.py +0 -0
  61. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_entry_points.py +0 -0
  62. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_examples_basic.py +0 -0
  63. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_inline_references.py +0 -0
  64. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_integration_advanced.py +0 -0
  65. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_integration_basic.py +0 -0
  66. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_integration_multi_doc.py +0 -0
  67. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_integration_nested_toctree.py +0 -0
  68. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_math_fallback.py +0 -0
  69. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_math_mitex.py +0 -0
  70. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_math_native.py +0 -0
  71. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_nested_toctree_paths.py +0 -0
  72. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_preview_version_sync.py +0 -0
  73. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_template_assets.py +0 -0
  74. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_template_codly.py +0 -0
  75. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_template_engine.py +0 -0
  76. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_template_mitex.py +0 -0
  77. {typsphinx-0.4.4 → typsphinx-0.6.0}/tests/test_toctree_requirement13.py +0 -0
  78. {typsphinx-0.4.4 → typsphinx-0.6.0}/typsphinx/pdf.py +0 -0
  79. {typsphinx-0.4.4 → typsphinx-0.6.0}/typsphinx.egg-info/dependency_links.txt +0 -0
  80. {typsphinx-0.4.4 → typsphinx-0.6.0}/typsphinx.egg-info/entry_points.txt +0 -0
  81. {typsphinx-0.4.4 → typsphinx-0.6.0}/typsphinx.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: typsphinx
3
- Version: 0.4.4
3
+ Version: 0.6.0
4
4
  Summary: Sphinx extension for Typst output
5
5
  Author-email: YuSabo <yusabo90002@gmail.com>
6
6
  License-Expression: MIT
@@ -13,18 +13,16 @@ Classifier: Development Status :: 5 - Production/Stable
13
13
  Classifier: Framework :: Sphinx :: Extension
14
14
  Classifier: Intended Audience :: Developers
15
15
  Classifier: Programming Language :: Python :: 3
16
- Classifier: Programming Language :: Python :: 3.10
17
- Classifier: Programming Language :: Python :: 3.11
18
16
  Classifier: Programming Language :: Python :: 3.12
19
17
  Classifier: Programming Language :: Python :: 3.13
20
18
  Classifier: Topic :: Documentation :: Sphinx
21
19
  Classifier: Topic :: Software Development :: Documentation
22
- Requires-Python: >=3.10
20
+ Requires-Python: >=3.12
23
21
  Description-Content-Type: text/markdown
24
22
  License-File: LICENSE
25
- Requires-Dist: sphinx<9,>=5.0
26
- Requires-Dist: docutils<0.22,>=0.18
27
- Requires-Dist: typst<0.15,>=0.14.1
23
+ Requires-Dist: sphinx<10,>=9.1
24
+ Requires-Dist: docutils<0.23,>=0.21
25
+ Requires-Dist: typst<0.16,>=0.15.0
28
26
  Provides-Extra: dev
29
27
  Requires-Dist: pytest<10,>=8.4; extra == "dev"
30
28
  Requires-Dist: pytest-cov>=4.0; extra == "dev"
@@ -34,14 +32,14 @@ Requires-Dist: black<27,>=26; extra == "dev"
34
32
  Requires-Dist: ruff<0.16,>=0.15; extra == "dev"
35
33
  Requires-Dist: mypy<3.0,>=1.13; extra == "dev"
36
34
  Requires-Dist: pre-commit>=3.0; extra == "dev"
37
- Requires-Dist: types-docutils>=0.18; extra == "dev"
35
+ Requires-Dist: types-docutils>=0.21; extra == "dev"
38
36
  Requires-Dist: twine>=5.0; extra == "dev"
39
37
  Requires-Dist: build>=1.0; extra == "dev"
38
+ Requires-Dist: pypdf<7,>=6.14; extra == "dev"
40
39
  Provides-Extra: docs
41
40
  Requires-Dist: furo>=2024.0; extra == "docs"
42
41
  Requires-Dist: sphinx-autodoc-typehints>=1.0; extra == "docs"
43
42
  Requires-Dist: sphinx-intl>=2.0; extra == "docs"
44
- Requires-Dist: tomli>=2.0; python_version < "3.11" and extra == "docs"
45
43
  Dynamic: license-file
46
44
 
47
45
  # typsphinx
@@ -66,7 +64,7 @@ typsphinx is a Sphinx extension that enables generating Typst documents from reS
66
64
  - **Convert Sphinx documentation to Typst format**: Seamlessly transform your reStructuredText/Markdown documents
67
65
  - **Standard docutils nodes**: Full support for paragraphs, sections, lists, tables, admonitions, and more
68
66
  - **Mathematical expressions**:
69
- - LaTeX syntax via mitex (`@preview/mitex:0.2.4`)
67
+ - LaTeX syntax via mitex (`@preview/mitex:0.2.7`)
70
68
  - Native Typst math syntax
71
69
  - **Code blocks with syntax highlighting**: Using codly package (`@preview/codly:1.3.0`)
72
70
  - Automatic line numbering
@@ -80,9 +78,9 @@ typsphinx is a Sphinx extension that enables generating Typst documents from reS
80
78
 
81
79
  ## Requirements
82
80
 
83
- - Python 3.10 or higher
84
- - Sphinx 5.0 or higher
85
- - typst-py 0.11.1 or higher
81
+ - Python 3.12 or higher
82
+ - Sphinx 9.1 or higher
83
+ - typst-py 0.15.0 or higher
86
84
 
87
85
  ## Installation
88
86
 
@@ -266,7 +264,7 @@ This project uses uv for fast package management and follows TDD (Test-Driven De
266
264
  # Install with development dependencies
267
265
  uv sync --extra dev
268
266
 
269
- # Run tests (375 tests, 91% coverage)
267
+ # Run tests (413 tests)
270
268
  uv run pytest
271
269
 
272
270
  # Run tests with coverage report
@@ -278,7 +276,7 @@ uv run tox
278
276
  # Run specific tox environments
279
277
  uv run tox -e lint # Run linters (black, ruff)
280
278
  uv run tox -e type # Run type checking (mypy)
281
- uv run tox -e py311 # Run tests on Python 3.11
279
+ uv run tox -e py312 # Run tests on Python 3.12
282
280
  uv run tox -e docs-html # Build HTML documentation
283
281
  uv run tox -e docs-pdf # Build PDF documentation
284
282
  uv run tox -e docs # Build both HTML and PDF docs
@@ -286,7 +284,7 @@ uv run tox -e docs # Build both HTML and PDF docs
286
284
 
287
285
  ### Testing Strategy
288
286
 
289
- - **Unit tests**: 375 tests covering all major components
287
+ - **Unit tests**: 413 tests covering all major components
290
288
  - **Integration tests**: Full build process validation
291
289
  - **Example projects**: `examples/basic/` and `examples/advanced/`
292
290
  - **Code coverage**: 93% overall
@@ -366,5 +364,5 @@ See [CHANGELOG.md](CHANGELOG.md) for detailed version history.
366
364
 
367
365
  ---
368
366
 
369
- **Status**: Stable (v0.4.2) - Production ready
370
- **Python**: 3.10+ | **Sphinx**: 5.0+ | **Typst**: 0.11.1+
367
+ **Status**: Stable (v0.5.0) - Production ready
368
+ **Python**: 3.12+ | **Sphinx**: 9.1+ | **Typst**: 0.15+
@@ -20,7 +20,7 @@ typsphinx is a Sphinx extension that enables generating Typst documents from reS
20
20
  - **Convert Sphinx documentation to Typst format**: Seamlessly transform your reStructuredText/Markdown documents
21
21
  - **Standard docutils nodes**: Full support for paragraphs, sections, lists, tables, admonitions, and more
22
22
  - **Mathematical expressions**:
23
- - LaTeX syntax via mitex (`@preview/mitex:0.2.4`)
23
+ - LaTeX syntax via mitex (`@preview/mitex:0.2.7`)
24
24
  - Native Typst math syntax
25
25
  - **Code blocks with syntax highlighting**: Using codly package (`@preview/codly:1.3.0`)
26
26
  - Automatic line numbering
@@ -34,9 +34,9 @@ typsphinx is a Sphinx extension that enables generating Typst documents from reS
34
34
 
35
35
  ## Requirements
36
36
 
37
- - Python 3.10 or higher
38
- - Sphinx 5.0 or higher
39
- - typst-py 0.11.1 or higher
37
+ - Python 3.12 or higher
38
+ - Sphinx 9.1 or higher
39
+ - typst-py 0.15.0 or higher
40
40
 
41
41
  ## Installation
42
42
 
@@ -220,7 +220,7 @@ This project uses uv for fast package management and follows TDD (Test-Driven De
220
220
  # Install with development dependencies
221
221
  uv sync --extra dev
222
222
 
223
- # Run tests (375 tests, 91% coverage)
223
+ # Run tests (413 tests)
224
224
  uv run pytest
225
225
 
226
226
  # Run tests with coverage report
@@ -232,7 +232,7 @@ uv run tox
232
232
  # Run specific tox environments
233
233
  uv run tox -e lint # Run linters (black, ruff)
234
234
  uv run tox -e type # Run type checking (mypy)
235
- uv run tox -e py311 # Run tests on Python 3.11
235
+ uv run tox -e py312 # Run tests on Python 3.12
236
236
  uv run tox -e docs-html # Build HTML documentation
237
237
  uv run tox -e docs-pdf # Build PDF documentation
238
238
  uv run tox -e docs # Build both HTML and PDF docs
@@ -240,7 +240,7 @@ uv run tox -e docs # Build both HTML and PDF docs
240
240
 
241
241
  ### Testing Strategy
242
242
 
243
- - **Unit tests**: 375 tests covering all major components
243
+ - **Unit tests**: 413 tests covering all major components
244
244
  - **Integration tests**: Full build process validation
245
245
  - **Example projects**: `examples/basic/` and `examples/advanced/`
246
246
  - **Code coverage**: 93% overall
@@ -320,5 +320,5 @@ See [CHANGELOG.md](CHANGELOG.md) for detailed version history.
320
320
 
321
321
  ---
322
322
 
323
- **Status**: Stable (v0.4.2) - Production ready
324
- **Python**: 3.10+ | **Sphinx**: 5.0+ | **Typst**: 0.11.1+
323
+ **Status**: Stable (v0.5.0) - Production ready
324
+ **Python**: 3.12+ | **Sphinx**: 9.1+ | **Typst**: 0.15+
@@ -4,10 +4,10 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "typsphinx"
7
- version = "0.4.4"
7
+ version = "0.6.0"
8
8
  description = "Sphinx extension for Typst output"
9
9
  readme = "README.md"
10
- requires-python = ">=3.10"
10
+ requires-python = ">=3.12"
11
11
  license = "MIT"
12
12
  authors = [
13
13
  {name = "YuSabo", email = "yusabo90002@gmail.com"}
@@ -18,8 +18,6 @@ classifiers = [
18
18
  "Framework :: Sphinx :: Extension",
19
19
  "Intended Audience :: Developers",
20
20
  "Programming Language :: Python :: 3",
21
- "Programming Language :: Python :: 3.10",
22
- "Programming Language :: Python :: 3.11",
23
21
  "Programming Language :: Python :: 3.12",
24
22
  "Programming Language :: Python :: 3.13",
25
23
  "Topic :: Documentation :: Sphinx",
@@ -27,9 +25,9 @@ classifiers = [
27
25
  ]
28
26
 
29
27
  dependencies = [
30
- "sphinx>=5.0,<9",
31
- "docutils>=0.18,<0.22",
32
- "typst>=0.14.1,<0.15",
28
+ "sphinx>=9.1,<10",
29
+ "docutils>=0.21,<0.23",
30
+ "typst>=0.15.0,<0.16",
33
31
  ]
34
32
 
35
33
  [project.optional-dependencies]
@@ -42,15 +40,15 @@ dev = [
42
40
  "ruff>=0.15,<0.16",
43
41
  "mypy>=1.13,<3.0",
44
42
  "pre-commit>=3.0",
45
- "types-docutils>=0.18",
43
+ "types-docutils>=0.21",
46
44
  "twine>=5.0",
47
45
  "build>=1.0",
46
+ "pypdf>=6.14,<7",
48
47
  ]
49
48
  docs = [
50
49
  "furo>=2024.0",
51
50
  "sphinx-autodoc-typehints>=1.0",
52
51
  "sphinx-intl>=2.0",
53
- "tomli>=2.0; python_version < '3.11'",
54
52
  ]
55
53
 
56
54
  [project.urls]
@@ -82,10 +80,23 @@ markers = [
82
80
  "slow: marks tests as slow (deselect with '-m \"not slow\"')",
83
81
  "integration: marks tests as integration tests",
84
82
  ]
83
+ filterwarnings = [
84
+ "error::DeprecationWarning",
85
+ # Escalated beyond CONTEXT.md's literal "DeprecationWarning" text: Sphinx's own
86
+ # RemovedInSphinxNNWarning family (e.g. builder.app) subclasses
87
+ # PendingDeprecationWarning, not DeprecationWarning — without this line, future
88
+ # Sphinx deprecations would pass through un-gated. See 08-RESEARCH.md Pitfall 2.
89
+ "error::PendingDeprecationWarning",
90
+ # No third-party ignore:: entries needed as of Sphinx 9.1.0/docutils 0.22.4/
91
+ # typst-py 0.15.0 (verified zero third-party warnings in 08-RESEARCH.md). If a
92
+ # future dependency bump introduces one, add a targeted
93
+ # ignore::DeprecationWarning:<module> entry here — do not weaken the two `error`
94
+ # lines above.
95
+ ]
85
96
 
86
97
  [tool.black]
87
98
  line-length = 88
88
- target-version = ["py310", "py311", "py312", "py313"]
99
+ target-version = ["py312", "py313"]
89
100
  include = '\.pyi?$'
90
101
  exclude = '''
91
102
  /(
@@ -100,7 +111,7 @@ exclude = '''
100
111
 
101
112
  [tool.ruff]
102
113
  line-length = 88
103
- target-version = "py310"
114
+ target-version = "py312"
104
115
 
105
116
  [tool.ruff.lint]
106
117
  select = ["E", "F", "W", "I", "N", "UP", "B", "A", "C4", "T20"]
@@ -117,7 +128,7 @@ ignore = [
117
128
  ]
118
129
 
119
130
  [tool.mypy]
120
- python_version = "3.10"
131
+ python_version = "3.12"
121
132
  warn_return_any = false
122
133
  warn_unused_configs = true
123
134
  disallow_untyped_defs = false
@@ -0,0 +1,433 @@
1
+ """
2
+ Tests for admonition node conversion to Typst gentle-clues.
3
+
4
+ Task 3.4: アドモニション(Admonition)の変換
5
+ """
6
+
7
+ from docutils import nodes
8
+ from docutils.parsers.rst import states
9
+ from docutils.utils import Reporter
10
+ from sphinx import addnodes
11
+ from sphinx.testing.util import SphinxTestApp
12
+
13
+ from typsphinx.translator import TypstTranslator
14
+ from typsphinx.writer import TypstWriter
15
+
16
+
17
+ def create_document():
18
+ """Helper function to create a minimal document with reporter."""
19
+ reporter = Reporter("", 2, 4)
20
+ doc = nodes.document("", reporter=reporter)
21
+ doc.settings = states.Struct()
22
+ doc.settings.env = None
23
+ doc.settings.language_code = "en"
24
+ doc.settings.strict_visitor = False
25
+ return doc
26
+
27
+
28
+ class TestAdmonitionConversion:
29
+ """Test admonition node conversion using gentle-clues package."""
30
+
31
+ def test_note_converts_to_info(self, temp_sphinx_app: SphinxTestApp):
32
+ """Test that nodes.note converts to info[]."""
33
+ # Create a note admonition
34
+ note = nodes.note()
35
+ para = nodes.paragraph(text="This is a note.")
36
+ note += para
37
+
38
+ # Create document
39
+ doc = create_document()
40
+ doc += note
41
+
42
+ # Translate
43
+ writer = TypstWriter(temp_sphinx_app.builder)
44
+ writer.document = doc
45
+ translator = TypstTranslator(doc, temp_sphinx_app.builder)
46
+ doc.walkabout(translator)
47
+
48
+ output = translator.astext()
49
+ assert "info({" in output
50
+ assert "info[" not in output
51
+ assert 'par({text("This is a note.")})' in output
52
+
53
+ def test_warning_converts_to_warning(self, temp_sphinx_app: SphinxTestApp):
54
+ """Test that nodes.warning converts to warning[]."""
55
+ warning = nodes.warning()
56
+ para = nodes.paragraph(text="This is a warning.")
57
+ warning += para
58
+
59
+ doc = create_document()
60
+ doc += warning
61
+
62
+ writer = TypstWriter(temp_sphinx_app.builder)
63
+ writer.document = doc
64
+ translator = TypstTranslator(doc, temp_sphinx_app.builder)
65
+ doc.walkabout(translator)
66
+
67
+ output = translator.astext()
68
+ assert "warning({" in output
69
+ assert "warning[" not in output
70
+ assert 'par({text("This is a warning.")})' in output
71
+
72
+ def test_tip_converts_to_tip(self, temp_sphinx_app: SphinxTestApp):
73
+ """Test that nodes.tip converts to tip[]."""
74
+ tip = nodes.tip()
75
+ para = nodes.paragraph(text="Here's a tip.")
76
+ tip += para
77
+
78
+ doc = create_document()
79
+ doc += tip
80
+
81
+ writer = TypstWriter(temp_sphinx_app.builder)
82
+ writer.document = doc
83
+ translator = TypstTranslator(doc, temp_sphinx_app.builder)
84
+ doc.walkabout(translator)
85
+
86
+ output = translator.astext()
87
+ assert "tip({" in output
88
+ assert "tip[" not in output
89
+ assert 'par({text("Here\'s a tip.")})' in output
90
+
91
+ def test_important_converts_to_warning_with_title(
92
+ self, temp_sphinx_app: SphinxTestApp
93
+ ):
94
+ """Test that nodes.important converts to warning(title: "Important")[]."""
95
+ important = nodes.important()
96
+ para = nodes.paragraph(text="This is important.")
97
+ important += para
98
+
99
+ doc = create_document()
100
+ doc += important
101
+
102
+ writer = TypstWriter(temp_sphinx_app.builder)
103
+ writer.document = doc
104
+ translator = TypstTranslator(doc, temp_sphinx_app.builder)
105
+ doc.walkabout(translator)
106
+
107
+ output = translator.astext()
108
+ assert "warning({" in output
109
+ assert "warning[" not in output
110
+ assert ', title: "Important"' in output
111
+ assert 'par({text("This is important.")})' in output
112
+
113
+ def test_caution_converts_to_warning(self, temp_sphinx_app: SphinxTestApp):
114
+ """Test that nodes.caution converts to warning[]."""
115
+ caution = nodes.caution()
116
+ para = nodes.paragraph(text="Be cautious.")
117
+ caution += para
118
+
119
+ doc = create_document()
120
+ doc += caution
121
+
122
+ writer = TypstWriter(temp_sphinx_app.builder)
123
+ writer.document = doc
124
+ translator = TypstTranslator(doc, temp_sphinx_app.builder)
125
+ doc.walkabout(translator)
126
+
127
+ output = translator.astext()
128
+ assert "warning({" in output
129
+ assert "warning[" not in output
130
+ assert 'par({text("Be cautious.")})' in output
131
+
132
+ def test_seealso_converts_to_info_with_title(self, temp_sphinx_app: SphinxTestApp):
133
+ """Test that addnodes.seealso converts to info(title: "See Also")[]."""
134
+ seealso = addnodes.seealso()
135
+ para = nodes.paragraph(text="See related documentation.")
136
+ seealso += para
137
+
138
+ doc = create_document()
139
+ doc += seealso
140
+
141
+ writer = TypstWriter(temp_sphinx_app.builder)
142
+ writer.document = doc
143
+ translator = TypstTranslator(doc, temp_sphinx_app.builder)
144
+ doc.walkabout(translator)
145
+
146
+ output = translator.astext()
147
+ assert "info({" in output
148
+ assert "info[" not in output
149
+ assert ', title: "See Also"' in output
150
+ assert 'par({text("See related documentation.")})' in output
151
+
152
+ def test_admonition_with_multiple_paragraphs(self, temp_sphinx_app: SphinxTestApp):
153
+ """Test admonition with multiple paragraphs."""
154
+ note = nodes.note()
155
+ para1 = nodes.paragraph(text="First paragraph.")
156
+ para2 = nodes.paragraph(text="Second paragraph.")
157
+ note += para1
158
+ note += para2
159
+
160
+ doc = create_document()
161
+ doc += note
162
+
163
+ writer = TypstWriter(temp_sphinx_app.builder)
164
+ writer.document = doc
165
+ translator = TypstTranslator(doc, temp_sphinx_app.builder)
166
+ doc.walkabout(translator)
167
+
168
+ output = translator.astext()
169
+ assert "info({" in output
170
+ assert "info[" not in output
171
+ assert 'par({text("First paragraph.")})' in output
172
+ assert 'par({text("Second paragraph.")})' in output
173
+
174
+ def test_nested_admonitions(self, temp_sphinx_app: SphinxTestApp):
175
+ """Test nested admonitions."""
176
+ outer_note = nodes.note()
177
+ para1 = nodes.paragraph(text="Outer note.")
178
+ inner_warning = nodes.warning()
179
+ para2 = nodes.paragraph(text="Inner warning.")
180
+ inner_warning += para2
181
+ outer_note += para1
182
+ outer_note += inner_warning
183
+
184
+ doc = create_document()
185
+ doc += outer_note
186
+
187
+ writer = TypstWriter(temp_sphinx_app.builder)
188
+ writer.document = doc
189
+ translator = TypstTranslator(doc, temp_sphinx_app.builder)
190
+ doc.walkabout(translator)
191
+
192
+ output = translator.astext()
193
+ assert "info({" in output
194
+ assert "info[" not in output
195
+ assert 'par({text("Outer note.")})' in output
196
+ assert "warning({" in output
197
+ assert "warning[" not in output
198
+ assert 'par({text("Inner warning.")})' in output
199
+
200
+ def test_nested_list_in_note(self, temp_sphinx_app: SphinxTestApp):
201
+ """Test a bullet list nested inside a note (D-05)."""
202
+ note = nodes.note()
203
+ bullet_list = nodes.bullet_list()
204
+ item1 = nodes.list_item()
205
+ item1 += nodes.paragraph(text="Item one.")
206
+ item2 = nodes.list_item()
207
+ item2 += nodes.paragraph(text="Item two.")
208
+ bullet_list += item1
209
+ bullet_list += item2
210
+ note += bullet_list
211
+
212
+ doc = create_document()
213
+ doc += note
214
+
215
+ writer = TypstWriter(temp_sphinx_app.builder)
216
+ writer.document = doc
217
+ translator = TypstTranslator(doc, temp_sphinx_app.builder)
218
+ doc.walkabout(translator)
219
+
220
+ output = translator.astext()
221
+ # The note stays in code-mode content-block form and the nested
222
+ # list's code-mode form evaluates inside it (no literal-source leak).
223
+ assert "info({" in output
224
+ assert "info[" not in output
225
+ assert "list(" in output
226
+ assert 'text("Item one.")' in output
227
+ assert 'text("Item two.")' in output
228
+
229
+ def test_nested_code_block_in_note(self, temp_sphinx_app: SphinxTestApp):
230
+ """Test a literal/code block nested inside a note (D-05)."""
231
+ note = nodes.note()
232
+ literal_block = nodes.literal_block(text="x = 1")
233
+ note += literal_block
234
+
235
+ doc = create_document()
236
+ doc += note
237
+
238
+ writer = TypstWriter(temp_sphinx_app.builder)
239
+ writer.document = doc
240
+ translator = TypstTranslator(doc, temp_sphinx_app.builder)
241
+ doc.walkabout(translator)
242
+
243
+ output = translator.astext()
244
+ assert "info({" in output
245
+ assert "info[" not in output
246
+ assert "```" in output
247
+ assert "x = 1" in output
248
+
249
+ def test_admonition_with_title_in_content(self, temp_sphinx_app: SphinxTestApp):
250
+ """Test admonition with custom title in first paragraph."""
251
+ # In Sphinx, custom admonitions have the title as the first child
252
+ note = nodes.note()
253
+ title = nodes.title(text="Custom Title")
254
+ para = nodes.paragraph(text="Content here.")
255
+ note += title
256
+ note += para
257
+
258
+ doc = create_document()
259
+ doc += note
260
+
261
+ writer = TypstWriter(temp_sphinx_app.builder)
262
+ writer.document = doc
263
+ translator = TypstTranslator(doc, temp_sphinx_app.builder)
264
+ doc.walkabout(translator)
265
+
266
+ output = translator.astext()
267
+ # Should use the dynamic (node-derived) title buffered as a code
268
+ # block, emitted exactly once, with the body evaluating in code mode.
269
+ assert "info({" in output
270
+ assert "info[" not in output
271
+ assert ", title: {" in output
272
+ assert output.count("Custom Title") == 1
273
+ assert "heading(" not in output # double-emission bug fixed
274
+ assert 'par({text("Content here.")})' in output
275
+
276
+ def test_admonition_title_preserves_inline_markup(
277
+ self, temp_sphinx_app: SphinxTestApp
278
+ ):
279
+ """Test that an admonition title preserves inline markup (D-02).
280
+
281
+ The title's rendered content is captured via the buffer-swap idiom
282
+ (routing through the normal inline visitors) rather than flattened
283
+ with node.astext(), so an emphasis child inside the title survives
284
+ as an emph(...) code-mode call instead of being reduced to plain
285
+ text. This also locks down the title double-emission regression
286
+ found in RESEARCH.md: heading( must never appear in the output.
287
+
288
+ Note: nodes.note() does not carry a title in real rST usage; this
289
+ is a constructed shape used purely to exercise the admonition-aware
290
+ visit_title/depart_title branch. The real docutils shape for a
291
+ title-bearing admonition is nodes.admonition (generic `.. admonition::`),
292
+ covered by Plan 03's D-06 generic-admonition test.
293
+ """
294
+ note = nodes.note()
295
+ title = nodes.title()
296
+ title += nodes.Text("Custom ")
297
+ title += nodes.emphasis(text="Title")
298
+ para = nodes.paragraph(text="Content here.")
299
+ note += title
300
+ note += para
301
+
302
+ doc = create_document()
303
+ doc += note
304
+
305
+ writer = TypstWriter(temp_sphinx_app.builder)
306
+ writer.document = doc
307
+ translator = TypstTranslator(doc, temp_sphinx_app.builder)
308
+ doc.walkabout(translator)
309
+
310
+ output = translator.astext()
311
+ assert "info({" in output
312
+ assert "info[" not in output
313
+ assert ", title: {" in output
314
+ assert "emph({" in output
315
+ assert output.count("Custom") == 1
316
+ assert output.count("Title") == 1
317
+ assert "heading(" not in output # double-emission bug fixed
318
+
319
+ def test_hint_converts_to_tip(self, temp_sphinx_app: SphinxTestApp):
320
+ """Test that nodes.hint converts to tip[] (D-06).
321
+
322
+ gentle-clues 1.3.1 has no dedicated `hint` clue; `tip` is the
323
+ verified closest analog.
324
+ """
325
+ hint = nodes.hint()
326
+ para = nodes.paragraph(text="Here's a hint.")
327
+ hint += para
328
+
329
+ doc = create_document()
330
+ doc += hint
331
+
332
+ writer = TypstWriter(temp_sphinx_app.builder)
333
+ writer.document = doc
334
+ translator = TypstTranslator(doc, temp_sphinx_app.builder)
335
+ doc.walkabout(translator)
336
+
337
+ output = translator.astext()
338
+ assert "tip({" in output
339
+ assert "tip[" not in output
340
+ assert 'par({text("Here\'s a hint.")})' in output
341
+
342
+ def test_error_converts_to_error(self, temp_sphinx_app: SphinxTestApp):
343
+ """Test that nodes.error converts to error[] (D-06)."""
344
+ error = nodes.error()
345
+ para = nodes.paragraph(text="This is an error.")
346
+ error += para
347
+
348
+ doc = create_document()
349
+ doc += error
350
+
351
+ writer = TypstWriter(temp_sphinx_app.builder)
352
+ writer.document = doc
353
+ translator = TypstTranslator(doc, temp_sphinx_app.builder)
354
+ doc.walkabout(translator)
355
+
356
+ output = translator.astext()
357
+ assert "error({" in output
358
+ assert "error[" not in output
359
+ assert 'par({text("This is an error.")})' in output
360
+
361
+ def test_danger_converts_to_danger(self, temp_sphinx_app: SphinxTestApp):
362
+ """Test that nodes.danger converts to danger[] (D-06)."""
363
+ danger = nodes.danger()
364
+ para = nodes.paragraph(text="This is dangerous.")
365
+ danger += para
366
+
367
+ doc = create_document()
368
+ doc += danger
369
+
370
+ writer = TypstWriter(temp_sphinx_app.builder)
371
+ writer.document = doc
372
+ translator = TypstTranslator(doc, temp_sphinx_app.builder)
373
+ doc.walkabout(translator)
374
+
375
+ output = translator.astext()
376
+ assert "danger({" in output
377
+ assert "danger[" not in output
378
+ assert 'par({text("This is dangerous.")})' in output
379
+
380
+ def test_attention_converts_to_warning(self, temp_sphinx_app: SphinxTestApp):
381
+ """Test that nodes.attention converts to warning[] (D-06).
382
+
383
+ gentle-clues 1.3.1 has no dedicated `attention` clue; `warning` is
384
+ the verified analog, consistent with the existing `caution`/
385
+ `important` -> `warning` precedent.
386
+ """
387
+ attention = nodes.attention()
388
+ para = nodes.paragraph(text="Pay attention.")
389
+ attention += para
390
+
391
+ doc = create_document()
392
+ doc += attention
393
+
394
+ writer = TypstWriter(temp_sphinx_app.builder)
395
+ writer.document = doc
396
+ translator = TypstTranslator(doc, temp_sphinx_app.builder)
397
+ doc.walkabout(translator)
398
+
399
+ output = translator.astext()
400
+ assert "warning({" in output
401
+ assert "warning[" not in output
402
+ assert 'par({text("Pay attention.")})' in output
403
+
404
+ def test_generic_admonition_converts_to_clue(self, temp_sphinx_app: SphinxTestApp):
405
+ """Test that a generic nodes.admonition converts to clue[] (D-06).
406
+
407
+ Uses the real docutils shape produced by `.. admonition:: <title>` —
408
+ a `nodes.admonition` with a real `nodes.title` child — rather than
409
+ an injected title on an unrelated admonition type. The title flows
410
+ through the same buffer-swap `visit_title`/`depart_title` path used
411
+ by all other admonition types.
412
+ """
413
+ admonition = nodes.admonition()
414
+ title = nodes.title(text="My Note")
415
+ para = nodes.paragraph(text="Generic admonition body.")
416
+ admonition += title
417
+ admonition += para
418
+
419
+ doc = create_document()
420
+ doc += admonition
421
+
422
+ writer = TypstWriter(temp_sphinx_app.builder)
423
+ writer.document = doc
424
+ translator = TypstTranslator(doc, temp_sphinx_app.builder)
425
+ doc.walkabout(translator)
426
+
427
+ output = translator.astext()
428
+ assert "clue({" in output
429
+ assert "clue[" not in output
430
+ assert ", title: {" in output
431
+ assert output.count("My Note") == 1
432
+ assert "heading(" not in output
433
+ assert 'par({text("Generic admonition body.")})' in output