typsphinx 0.4.0__tar.gz → 0.4.2__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 (46) hide show
  1. {typsphinx-0.4.0 → typsphinx-0.4.2}/PKG-INFO +2 -2
  2. {typsphinx-0.4.0 → typsphinx-0.4.2}/README.md +1 -1
  3. {typsphinx-0.4.0 → typsphinx-0.4.2}/pyproject.toml +1 -1
  4. {typsphinx-0.4.0 → typsphinx-0.4.2}/tests/test_translator.py +371 -7
  5. {typsphinx-0.4.0 → typsphinx-0.4.2}/typsphinx/__init__.py +1 -1
  6. {typsphinx-0.4.0 → typsphinx-0.4.2}/typsphinx/translator.py +116 -4
  7. {typsphinx-0.4.0 → typsphinx-0.4.2}/typsphinx.egg-info/PKG-INFO +2 -2
  8. {typsphinx-0.4.0 → typsphinx-0.4.2}/LICENSE +0 -0
  9. {typsphinx-0.4.0 → typsphinx-0.4.2}/setup.cfg +0 -0
  10. {typsphinx-0.4.0 → typsphinx-0.4.2}/tests/test_admonitions.py +0 -0
  11. {typsphinx-0.4.0 → typsphinx-0.4.2}/tests/test_builder.py +0 -0
  12. {typsphinx-0.4.0 → typsphinx-0.4.2}/tests/test_builder_requirement13.py +0 -0
  13. {typsphinx-0.4.0 → typsphinx-0.4.2}/tests/test_config.py +0 -0
  14. {typsphinx-0.4.0 → typsphinx-0.4.2}/tests/test_config_other_options.py +0 -0
  15. {typsphinx-0.4.0 → typsphinx-0.4.2}/tests/test_config_template_mapping.py +0 -0
  16. {typsphinx-0.4.0 → typsphinx-0.4.2}/tests/test_config_toctree_defaults.py +0 -0
  17. {typsphinx-0.4.0 → typsphinx-0.4.2}/tests/test_documentation_configuration.py +0 -0
  18. {typsphinx-0.4.0 → typsphinx-0.4.2}/tests/test_documentation_installation.py +0 -0
  19. {typsphinx-0.4.0 → typsphinx-0.4.2}/tests/test_documentation_usage.py +0 -0
  20. {typsphinx-0.4.0 → typsphinx-0.4.2}/tests/test_entry_points.py +0 -0
  21. {typsphinx-0.4.0 → typsphinx-0.4.2}/tests/test_examples_basic.py +0 -0
  22. {typsphinx-0.4.0 → typsphinx-0.4.2}/tests/test_extension.py +0 -0
  23. {typsphinx-0.4.0 → typsphinx-0.4.2}/tests/test_inline_references.py +0 -0
  24. {typsphinx-0.4.0 → typsphinx-0.4.2}/tests/test_integration_advanced.py +0 -0
  25. {typsphinx-0.4.0 → typsphinx-0.4.2}/tests/test_integration_basic.py +0 -0
  26. {typsphinx-0.4.0 → typsphinx-0.4.2}/tests/test_integration_multi_doc.py +0 -0
  27. {typsphinx-0.4.0 → typsphinx-0.4.2}/tests/test_integration_nested_toctree.py +0 -0
  28. {typsphinx-0.4.0 → typsphinx-0.4.2}/tests/test_math_fallback.py +0 -0
  29. {typsphinx-0.4.0 → typsphinx-0.4.2}/tests/test_math_mitex.py +0 -0
  30. {typsphinx-0.4.0 → typsphinx-0.4.2}/tests/test_math_native.py +0 -0
  31. {typsphinx-0.4.0 → typsphinx-0.4.2}/tests/test_nested_toctree_paths.py +0 -0
  32. {typsphinx-0.4.0 → typsphinx-0.4.2}/tests/test_pdf_generation.py +0 -0
  33. {typsphinx-0.4.0 → typsphinx-0.4.2}/tests/test_template_codly.py +0 -0
  34. {typsphinx-0.4.0 → typsphinx-0.4.2}/tests/test_template_engine.py +0 -0
  35. {typsphinx-0.4.0 → typsphinx-0.4.2}/tests/test_template_mitex.py +0 -0
  36. {typsphinx-0.4.0 → typsphinx-0.4.2}/tests/test_toctree_requirement13.py +0 -0
  37. {typsphinx-0.4.0 → typsphinx-0.4.2}/typsphinx/builder.py +0 -0
  38. {typsphinx-0.4.0 → typsphinx-0.4.2}/typsphinx/pdf.py +0 -0
  39. {typsphinx-0.4.0 → typsphinx-0.4.2}/typsphinx/template_engine.py +0 -0
  40. {typsphinx-0.4.0 → typsphinx-0.4.2}/typsphinx/templates/base.typ +0 -0
  41. {typsphinx-0.4.0 → typsphinx-0.4.2}/typsphinx/writer.py +0 -0
  42. {typsphinx-0.4.0 → typsphinx-0.4.2}/typsphinx.egg-info/SOURCES.txt +0 -0
  43. {typsphinx-0.4.0 → typsphinx-0.4.2}/typsphinx.egg-info/dependency_links.txt +0 -0
  44. {typsphinx-0.4.0 → typsphinx-0.4.2}/typsphinx.egg-info/entry_points.txt +0 -0
  45. {typsphinx-0.4.0 → typsphinx-0.4.2}/typsphinx.egg-info/requires.txt +0 -0
  46. {typsphinx-0.4.0 → typsphinx-0.4.2}/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.0
3
+ Version: 0.4.2
4
4
  Summary: Sphinx extension for Typst output
5
5
  Author-email: Sphinx Typst Contributors <noreply@example.com>
6
6
  License-Expression: MIT
@@ -364,5 +364,5 @@ See [CHANGELOG.md](CHANGELOG.md) for detailed version history.
364
364
 
365
365
  ---
366
366
 
367
- **Status**: Stable (v0.4.0) - Production ready
367
+ **Status**: Stable (v0.4.2) - Production ready
368
368
  **Python**: 3.9+ | **Sphinx**: 5.0+ | **Typst**: 0.11.1+
@@ -319,5 +319,5 @@ See [CHANGELOG.md](CHANGELOG.md) for detailed version history.
319
319
 
320
320
  ---
321
321
 
322
- **Status**: Stable (v0.4.0) - Production ready
322
+ **Status**: Stable (v0.4.2) - Production ready
323
323
  **Python**: 3.9+ | **Sphinx**: 5.0+ | **Typst**: 0.11.1+
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "typsphinx"
7
- version = "0.4.0"
7
+ version = "0.4.2"
8
8
  description = "Sphinx extension for Typst output"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -2416,8 +2416,9 @@ def test_table_cell_colspan(simple_document, mock_builder):
2416
2416
  table.walkabout(translator)
2417
2417
  output = translator.astext()
2418
2418
 
2419
- # Should have table.cell(colspan: 2)
2420
- assert "table.cell(colspan: 2)" in output
2419
+ # Should have table.cell with content first, then colspan: 2
2420
+ assert "table.cell(" in output
2421
+ assert "colspan: 2)" in output
2421
2422
  assert "Spans 2 cols" in output
2422
2423
  assert "Col 3" in output
2423
2424
 
@@ -2464,8 +2465,9 @@ def test_table_cell_rowspan(simple_document, mock_builder):
2464
2465
  table.walkabout(translator)
2465
2466
  output = translator.astext()
2466
2467
 
2467
- # Should have table.cell(rowspan: 2)
2468
- assert "table.cell(rowspan: 2)" in output
2468
+ # Should have table.cell with content first, then rowspan: 2
2469
+ assert "table.cell(" in output
2470
+ assert "rowspan: 2)" in output
2469
2471
  assert "Spans 2 rows" in output
2470
2472
 
2471
2473
 
@@ -2513,8 +2515,9 @@ def test_table_cell_colspan_and_rowspan(simple_document, mock_builder):
2513
2515
  table.walkabout(translator)
2514
2516
  output = translator.astext()
2515
2517
 
2516
- # Should have table.cell(colspan: 2, rowspan: 2)
2517
- assert "table.cell(colspan: 2, rowspan: 2)" in output
2518
+ # Should have table.cell with content first, then colspan and rowspan
2519
+ assert "table.cell(" in output
2520
+ assert "colspan: 2, rowspan: 2)" in output
2518
2521
  assert "Spans 2x2" in output
2519
2522
 
2520
2523
 
@@ -2573,7 +2576,8 @@ def test_table_header_cell_with_colspan(simple_document, mock_builder):
2573
2576
 
2574
2577
  # Should have table.header() with colspan cell
2575
2578
  assert "table.header(" in output
2576
- assert "table.cell(colspan: 2)" in output
2579
+ assert "table.cell(" in output
2580
+ assert "colspan: 2)" in output
2577
2581
  assert "Header 1-2" in output
2578
2582
  assert "Header 3" in output
2579
2583
 
@@ -2618,6 +2622,221 @@ def test_table_normal_cells_without_spanning(simple_document, mock_builder):
2618
2622
  assert "Cell 2" in output
2619
2623
 
2620
2624
 
2625
+ # Empty table cell tests (Issue #68)
2626
+
2627
+
2628
+ def test_table_empty_cells(simple_document, mock_builder):
2629
+ """Test that empty table cells are wrapped in content blocks.
2630
+
2631
+ Related to Issue #68: Empty table cells cause Typst compilation errors.
2632
+ Empty cells should be wrapped in {} to generate valid Typst syntax.
2633
+ """
2634
+ from typsphinx.translator import TypstTranslator
2635
+
2636
+ translator = TypstTranslator(simple_document, mock_builder)
2637
+
2638
+ # Create table with all empty cells
2639
+ table = nodes.table()
2640
+ tgroup = nodes.tgroup(cols=2)
2641
+ tgroup += nodes.colspec(colwidth=1)
2642
+ tgroup += nodes.colspec(colwidth=1)
2643
+
2644
+ tbody = nodes.tbody()
2645
+ row1 = nodes.row()
2646
+ entry1 = nodes.entry() # Empty cell
2647
+ entry2 = nodes.entry() # Empty cell
2648
+ row1 += entry1
2649
+ row1 += entry2
2650
+ tbody += row1
2651
+ tgroup += tbody
2652
+ table += tgroup
2653
+
2654
+ table.walkabout(translator)
2655
+ output = translator.astext()
2656
+
2657
+ # Should contain empty content blocks
2658
+ assert "{}" in output
2659
+ # Should NOT contain bare commas
2660
+ assert ",," not in output
2661
+ assert " ,\n" not in output
2662
+
2663
+
2664
+ def test_table_mixed_empty_and_content(simple_document, mock_builder):
2665
+ """Test table with both empty and non-empty cells.
2666
+
2667
+ Related to Issue #68: Empty table cells cause Typst compilation errors.
2668
+ Tables with mixed empty and non-empty cells should generate valid Typst syntax.
2669
+ """
2670
+ from typsphinx.translator import TypstTranslator
2671
+
2672
+ translator = TypstTranslator(simple_document, mock_builder)
2673
+
2674
+ # Create table: A | (empty) / (empty) | D
2675
+ table = nodes.table()
2676
+ tgroup = nodes.tgroup(cols=2)
2677
+ tgroup += nodes.colspec(colwidth=1)
2678
+ tgroup += nodes.colspec(colwidth=1)
2679
+
2680
+ tbody = nodes.tbody()
2681
+
2682
+ # Row 1: A | empty
2683
+ row1 = nodes.row()
2684
+ entry1 = nodes.entry()
2685
+ entry1 += nodes.paragraph(text="A")
2686
+ entry2 = nodes.entry() # Empty
2687
+ row1 += entry1
2688
+ row1 += entry2
2689
+ tbody += row1
2690
+
2691
+ # Row 2: empty | D
2692
+ row2 = nodes.row()
2693
+ entry3 = nodes.entry() # Empty
2694
+ entry4 = nodes.entry()
2695
+ entry4 += nodes.paragraph(text="D")
2696
+ row2 += entry3
2697
+ row2 += entry4
2698
+ tbody += row2
2699
+
2700
+ tgroup += tbody
2701
+ table += tgroup
2702
+
2703
+ table.walkabout(translator)
2704
+ output = translator.astext()
2705
+
2706
+ # Should contain content for non-empty cells
2707
+ assert "A" in output
2708
+ assert "D" in output
2709
+ # Should contain empty content blocks
2710
+ assert "{}" in output
2711
+ # Should NOT contain bare commas
2712
+ assert ",," not in output
2713
+
2714
+
2715
+ def test_table_empty_colspan_cells(simple_document, mock_builder):
2716
+ """Test that empty cells with colspan use table.cell with empty content.
2717
+
2718
+ Related to Issue #68: Empty table cells cause Typst compilation errors.
2719
+ Empty cells with colspan should use table.cell({}, colspan: N) syntax.
2720
+ """
2721
+ from typsphinx.translator import TypstTranslator
2722
+
2723
+ translator = TypstTranslator(simple_document, mock_builder)
2724
+
2725
+ # Create table with empty colspan cell
2726
+ table = nodes.table()
2727
+ tgroup = nodes.tgroup(cols=2)
2728
+ tgroup += nodes.colspec(colwidth=1)
2729
+ tgroup += nodes.colspec(colwidth=1)
2730
+
2731
+ tbody = nodes.tbody()
2732
+ row1 = nodes.row()
2733
+
2734
+ # Empty cell with colspan=2
2735
+ entry1 = nodes.entry(morecols=1) # Empty, spans 2 columns
2736
+ row1 += entry1
2737
+ tbody += row1
2738
+ tgroup += tbody
2739
+ table += tgroup
2740
+
2741
+ table.walkabout(translator)
2742
+ output = translator.astext()
2743
+
2744
+ # Should use table.cell with empty content
2745
+ assert "table.cell({}" in output
2746
+ assert "colspan: 2" in output
2747
+ # Should NOT contain bare commas or missing content
2748
+ assert "table.cell(," not in output
2749
+
2750
+
2751
+ def test_table_empty_rowspan_cells(simple_document, mock_builder):
2752
+ """Test that empty cells with rowspan use table.cell with empty content.
2753
+
2754
+ Related to Issue #68: Empty table cells cause Typst compilation errors.
2755
+ Empty cells with rowspan should use table.cell({}, rowspan: N) syntax.
2756
+ """
2757
+ from typsphinx.translator import TypstTranslator
2758
+
2759
+ translator = TypstTranslator(simple_document, mock_builder)
2760
+
2761
+ # Create table with empty rowspan cell
2762
+ table = nodes.table()
2763
+ tgroup = nodes.tgroup(cols=2)
2764
+ tgroup += nodes.colspec(colwidth=1)
2765
+ tgroup += nodes.colspec(colwidth=1)
2766
+
2767
+ tbody = nodes.tbody()
2768
+ row1 = nodes.row()
2769
+
2770
+ # Empty cell with rowspan=2
2771
+ entry1 = nodes.entry(morerows=1) # Empty, spans 2 rows
2772
+ entry2 = nodes.entry()
2773
+ entry2 += nodes.paragraph(text="B")
2774
+ row1 += entry1
2775
+ row1 += entry2
2776
+ tbody += row1
2777
+
2778
+ # Row 2
2779
+ row2 = nodes.row()
2780
+ entry3 = nodes.entry()
2781
+ entry3 += nodes.paragraph(text="C")
2782
+ row2 += entry3
2783
+ tbody += row2
2784
+
2785
+ tgroup += tbody
2786
+ table += tgroup
2787
+
2788
+ table.walkabout(translator)
2789
+ output = translator.astext()
2790
+
2791
+ # Should use table.cell with empty content
2792
+ assert "table.cell({}" in output
2793
+ assert "rowspan: 2" in output
2794
+ # Should NOT contain bare commas or missing content
2795
+ assert "table.cell(," not in output
2796
+
2797
+
2798
+ def test_table_empty_colspan_rowspan_cells(simple_document, mock_builder):
2799
+ """Test that empty cells with both colspan and rowspan use table.cell with empty content.
2800
+
2801
+ Related to Issue #68: Empty table cells cause Typst compilation errors.
2802
+ Empty cells with both colspan and rowspan should use table.cell({}, colspan: M, rowspan: N) syntax.
2803
+ """
2804
+ from typsphinx.translator import TypstTranslator
2805
+
2806
+ translator = TypstTranslator(simple_document, mock_builder)
2807
+
2808
+ # Create table with empty colspan+rowspan cell
2809
+ table = nodes.table()
2810
+ tgroup = nodes.tgroup(cols=2)
2811
+ tgroup += nodes.colspec(colwidth=1)
2812
+ tgroup += nodes.colspec(colwidth=1)
2813
+
2814
+ tbody = nodes.tbody()
2815
+ row1 = nodes.row()
2816
+
2817
+ # Empty cell with colspan=2, rowspan=2
2818
+ entry1 = nodes.entry(morecols=1, morerows=1) # Empty, spans 2x2
2819
+ row1 += entry1
2820
+ tbody += row1
2821
+
2822
+ # Row 2 (no cells needed due to spanning)
2823
+ row2 = nodes.row()
2824
+ tbody += row2
2825
+
2826
+ tgroup += tbody
2827
+ table += tgroup
2828
+
2829
+ table.walkabout(translator)
2830
+ output = translator.astext()
2831
+
2832
+ # Should use table.cell with empty content
2833
+ assert "table.cell({}" in output
2834
+ assert "colspan: 2" in output
2835
+ assert "rowspan: 2" in output
2836
+ # Should NOT contain bare commas or missing content
2837
+ assert "table.cell(," not in output
2838
+
2839
+
2621
2840
  # API description nodes tests (Issue #55)
2622
2841
 
2623
2842
 
@@ -2854,3 +3073,148 @@ def test_full_api_description_structure(simple_document, mock_builder):
2854
3073
  assert "Builder class for Typst output." in output
2855
3074
  assert 'strong(text("Parameters")' in output or "Parameters" in output
2856
3075
  assert "app - Sphinx application" in output
3076
+
3077
+
3078
+ # Image path adjustment tests (Issue #69)
3079
+
3080
+
3081
+ def test_image_path_adjustment_root(simple_document, mock_builder):
3082
+ """Test that root document image paths are not adjusted.
3083
+
3084
+ Related to Issue #69: Image paths in root documents should remain unchanged.
3085
+ """
3086
+ from typsphinx.translator import TypstTranslator
3087
+
3088
+ translator = TypstTranslator(simple_document, mock_builder)
3089
+
3090
+ # Set current_docname to root document
3091
+ mock_builder.current_docname = "index"
3092
+
3093
+ # Create image node
3094
+ image = nodes.image(uri="images/logo.png")
3095
+ image["width"] = "200px"
3096
+
3097
+ image.walkabout(translator)
3098
+ output = translator.astext()
3099
+
3100
+ # Root document: path should NOT be adjusted
3101
+ assert 'image("images/logo.png"' in output
3102
+ assert "../images" not in output
3103
+ assert "width: 200px" in output
3104
+
3105
+
3106
+ def test_image_path_adjustment_nested(simple_document, mock_builder):
3107
+ """Test that nested document image paths are adjusted with ../ prefix.
3108
+
3109
+ Related to Issue #69: Nested documents need relative path adjustment.
3110
+ """
3111
+ from typsphinx.translator import TypstTranslator
3112
+
3113
+ translator = TypstTranslator(simple_document, mock_builder)
3114
+
3115
+ # Set current_docname to nested document
3116
+ mock_builder.current_docname = "chapter1/section1"
3117
+
3118
+ # Create image node with source-root-relative path
3119
+ image = nodes.image(uri="images/logo.png")
3120
+ image["width"] = "200px"
3121
+
3122
+ image.walkabout(translator)
3123
+ output = translator.astext()
3124
+
3125
+ # Nested document: path should be adjusted
3126
+ assert 'image("../images/logo.png"' in output
3127
+ assert "width: 200px" in output
3128
+
3129
+
3130
+ def test_image_path_adjustment_deep_nested(simple_document, mock_builder):
3131
+ """Test that deeply nested documents get correct number of ../ prefixes.
3132
+
3133
+ Related to Issue #69: Deep nesting requires multiple ../ levels.
3134
+ """
3135
+ from typsphinx.translator import TypstTranslator
3136
+
3137
+ translator = TypstTranslator(simple_document, mock_builder)
3138
+
3139
+ # Set current_docname to deeply nested document
3140
+ mock_builder.current_docname = "part1/chapter1/section1"
3141
+
3142
+ # Create image node
3143
+ image = nodes.image(uri="images/logo.png")
3144
+
3145
+ image.walkabout(translator)
3146
+ output = translator.astext()
3147
+
3148
+ # Deeply nested: should have ../../ prefix
3149
+ assert 'image("../../images/logo.png"' in output
3150
+
3151
+
3152
+ def test_image_path_adjustment_cross_directory(simple_document, mock_builder):
3153
+ """Test that cross-directory image references are correctly adjusted.
3154
+
3155
+ Related to Issue #69: References from chapter1/ to chapter2/ images.
3156
+ """
3157
+ from typsphinx.translator import TypstTranslator
3158
+
3159
+ translator = TypstTranslator(simple_document, mock_builder)
3160
+
3161
+ # Set current document in chapter1
3162
+ mock_builder.current_docname = "chapter1/section1"
3163
+
3164
+ # Reference image in chapter2
3165
+ image = nodes.image(uri="chapter2/images/diagram.png")
3166
+
3167
+ image.walkabout(translator)
3168
+ output = translator.astext()
3169
+
3170
+ # Cross-directory: ../chapter2/images/diagram.png
3171
+ assert 'image("../chapter2/images/diagram.png"' in output
3172
+
3173
+
3174
+ def test_image_path_adjustment_same_directory(simple_document, mock_builder):
3175
+ """Test that same-directory images use simple relative paths.
3176
+
3177
+ Related to Issue #69: Images in the same directory as the document.
3178
+ """
3179
+ from typsphinx.translator import TypstTranslator
3180
+
3181
+ translator = TypstTranslator(simple_document, mock_builder)
3182
+
3183
+ # Both in chapter1/
3184
+ mock_builder.current_docname = "chapter1/section1"
3185
+
3186
+ # Image also in chapter1/
3187
+ image = nodes.image(uri="chapter1/local-image.png")
3188
+
3189
+ image.walkabout(translator)
3190
+ output = translator.astext()
3191
+
3192
+ # Same directory: just the filename
3193
+ assert 'image("local-image.png"' in output
3194
+ assert "../" not in output
3195
+
3196
+
3197
+ def test_image_path_adjustment_subdirectory(simple_document, mock_builder):
3198
+ """Test that subdirectory images use relative paths to child folders.
3199
+
3200
+ Related to Issue #69: Images in subdirectories relative to the document.
3201
+ Example: chapter1/section1.rst references chapter1/img/diagram.jpeg
3202
+ """
3203
+ from typsphinx.translator import TypstTranslator
3204
+
3205
+ translator = TypstTranslator(simple_document, mock_builder)
3206
+
3207
+ # Document in chapter1/
3208
+ mock_builder.current_docname = "chapter1/section1"
3209
+
3210
+ # Image in chapter1/img/ (subdirectory of same directory)
3211
+ image = nodes.image(uri="chapter1/img/diagram.jpeg")
3212
+ image["width"] = "250px"
3213
+
3214
+ image.walkabout(translator)
3215
+ output = translator.astext()
3216
+
3217
+ # Subdirectory: img/diagram.jpeg (relative to current directory)
3218
+ assert 'image("img/diagram.jpeg"' in output
3219
+ assert "width: 250px" in output
3220
+ assert "../" not in output # No need to go up
@@ -11,7 +11,7 @@ sources using Sphinx, which can then be compiled to PDF using the Typst compiler
11
11
  :license: MIT, see LICENSE for details.
12
12
  """
13
13
 
14
- __version__ = "0.3.0"
14
+ __version__ = "0.4.1"
15
15
  __author__ = "Sphinx Typst Contributors"
16
16
 
17
17
  from typing import Any, Dict
@@ -1215,7 +1215,7 @@ class TypstTranslator(SphinxTranslator):
1215
1215
 
1216
1216
  # Normal cell (no spanning)
1217
1217
  if colspan == 1 and rowspan == 1:
1218
- return f"{indent}[{content}],\n"
1218
+ return f"{indent}{{{content}}},\n"
1219
1219
 
1220
1220
  # Cell with spanning - use table.cell()
1221
1221
  params = []
@@ -1225,7 +1225,7 @@ class TypstTranslator(SphinxTranslator):
1225
1225
  params.append(f"rowspan: {rowspan}")
1226
1226
 
1227
1227
  params_str = ", ".join(params)
1228
- return f"{indent}table.cell({params_str})[{content}],\n"
1228
+ return f"{indent}table.cell({{{content}}}, {params_str}),\n"
1229
1229
 
1230
1230
  def depart_table(self, node: nodes.table) -> None:
1231
1231
  """
@@ -1464,18 +1464,25 @@ class TypstTranslator(SphinxTranslator):
1464
1464
  Visit an image node.
1465
1465
 
1466
1466
  Generates image() function call (no # prefix in code mode).
1467
+ Adjusts image paths for nested documents (Issue #69).
1467
1468
 
1468
1469
  Args:
1469
1470
  node: The image node
1470
1471
  """
1471
1472
  uri = node.get("uri", "")
1472
1473
 
1474
+ # Get current document name for path adjustment (Issue #69)
1475
+ current_docname = getattr(self.builder, "current_docname", None)
1476
+
1477
+ # Adjust path based on output file location (Issue #69)
1478
+ adjusted_uri = self._compute_relative_image_path(uri, current_docname)
1479
+
1473
1480
  # Add proper indentation if inside a figure
1474
1481
  if self.in_figure:
1475
- self.add_text(f' image("{uri}"')
1482
+ self.add_text(f' image("{adjusted_uri}"')
1476
1483
  else:
1477
1484
  # No # prefix in code mode
1478
- self.add_text(f'image("{uri}"')
1485
+ self.add_text(f'image("{adjusted_uri}"')
1479
1486
 
1480
1487
  # Add optional attributes
1481
1488
  if "width" in node:
@@ -1699,6 +1706,111 @@ class TypstTranslator(SphinxTranslator):
1699
1706
 
1700
1707
  return relative_path
1701
1708
 
1709
+ def _compute_relative_image_path(
1710
+ self, image_uri: str, current_docname: Optional[str]
1711
+ ) -> str:
1712
+ """
1713
+ Compute relative path for image() function.
1714
+
1715
+ Adjusts image URIs from source-root-relative to output-file-relative.
1716
+ This is similar to _compute_relative_include_path() but for images.
1717
+
1718
+ Args:
1719
+ image_uri: Image URI from Sphinx (source-root-relative)
1720
+ current_docname: Current document name (e.g., "chapter1/section1")
1721
+
1722
+ Returns:
1723
+ Adjusted relative path for Typst image()
1724
+
1725
+ Examples:
1726
+ >>> _compute_relative_image_path("images/logo.png", "chapter1/section1")
1727
+ "../images/logo.png"
1728
+ >>> _compute_relative_image_path("images/logo.png", "index")
1729
+ "images/logo.png"
1730
+ >>> _compute_relative_image_path("images/logo.png", None)
1731
+ "images/logo.png"
1732
+
1733
+ Notes:
1734
+ This implements Issue #69 fix for nested document image paths.
1735
+ Uses the same logic as _compute_relative_include_path() from Issue #5.
1736
+ """
1737
+ from pathlib import PurePosixPath
1738
+
1739
+ logger.debug(
1740
+ f"Computing relative image path: uri={image_uri}, "
1741
+ f"current={current_docname}"
1742
+ )
1743
+
1744
+ # Fallback to absolute path if current_docname is None
1745
+ if not current_docname:
1746
+ logger.debug(f"No current document, using absolute path: {image_uri}")
1747
+ return image_uri
1748
+
1749
+ current_path = PurePosixPath(current_docname)
1750
+ image_path = PurePosixPath(image_uri)
1751
+ current_dir = current_path.parent
1752
+
1753
+ logger.debug(
1754
+ f"Path components: current_dir={current_dir}, image_path={image_path}"
1755
+ )
1756
+
1757
+ # Root directory case: use absolute path (backward compatibility)
1758
+ if current_dir == PurePosixPath("."):
1759
+ logger.debug(
1760
+ f"Current document is in root directory, "
1761
+ f"using absolute path: {image_uri}"
1762
+ )
1763
+ return image_uri
1764
+
1765
+ # Try to compute relative path
1766
+ try:
1767
+ rel_path = image_path.relative_to(current_dir)
1768
+ result = str(rel_path)
1769
+ logger.debug(
1770
+ f"Same directory reference: {current_dir} -> {image_path}, "
1771
+ f"result: {result}"
1772
+ )
1773
+ return result
1774
+ except ValueError:
1775
+ # Different directory trees - build path via common parent
1776
+ logger.debug(
1777
+ "Cross-directory reference detected, calculating via common parent"
1778
+ )
1779
+
1780
+ current_parts = current_dir.parts
1781
+ image_parts = image_path.parts
1782
+
1783
+ # Find common parent by comparing path components
1784
+ common_length = 0
1785
+ for i, (c, img) in enumerate(zip(current_parts, image_parts)):
1786
+ if c == img:
1787
+ common_length = i + 1
1788
+ else:
1789
+ break
1790
+
1791
+ logger.debug(
1792
+ f"Common parent depth: {common_length}, "
1793
+ f"current_parts={current_parts}, image_parts={image_parts}"
1794
+ )
1795
+
1796
+ # Build path: "../" from current to common parent
1797
+ up_count = len(current_parts) - common_length
1798
+ up_path = "../" * up_count if up_count > 0 else ""
1799
+
1800
+ # Build path: from common parent to image
1801
+ down_parts = image_parts[common_length:]
1802
+ down_path = "/".join(down_parts) if down_parts else ""
1803
+
1804
+ relative_path: str = up_path + down_path
1805
+
1806
+ logger.debug(
1807
+ f"Cross-directory path calculation: up_count={up_count}, "
1808
+ f"up_path='{up_path}', down_path='{down_path}', "
1809
+ f"result: {relative_path}"
1810
+ )
1811
+
1812
+ return relative_path
1813
+
1702
1814
  def visit_toctree(self, node: nodes.Node) -> None:
1703
1815
  """
1704
1816
  Visit a toctree node (Sphinx table of contents tree).
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: typsphinx
3
- Version: 0.4.0
3
+ Version: 0.4.2
4
4
  Summary: Sphinx extension for Typst output
5
5
  Author-email: Sphinx Typst Contributors <noreply@example.com>
6
6
  License-Expression: MIT
@@ -364,5 +364,5 @@ See [CHANGELOG.md](CHANGELOG.md) for detailed version history.
364
364
 
365
365
  ---
366
366
 
367
- **Status**: Stable (v0.4.0) - Production ready
367
+ **Status**: Stable (v0.4.2) - Production ready
368
368
  **Python**: 3.9+ | **Sphinx**: 5.0+ | **Typst**: 0.11.1+
File without changes
File without changes
File without changes
File without changes