markdocx 4.0.0__tar.gz → 6.0.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: markdocx
3
- Version: 4.0.0
3
+ Version: 6.0.0
4
4
  Summary: Convert AI-generated Markdown textbooks to polished DOCX with native math equations and syntax-highlighted code
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "markdocx"
3
- version = "4.0.0"
3
+ version = "6.0.0"
4
4
  description = "Convert AI-generated Markdown textbooks to polished DOCX with native math equations and syntax-highlighted code"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -665,6 +665,12 @@ caption: Hình 8: Quy trình ETL
665
665
  - Khuyến nghị dùng `[Bắt đầu]` và `[Kết thúc]` cho bước đầu và cuối
666
666
  - Giữ quy trình tối đa **8 bước** để dễ đọc
667
667
  - Dùng `title:` và `caption:` để đặt nhãn
668
+ - **Mỗi bước viết ngắn gọn, tối đa 10-12 ký tự** (ví dụ: `Thu nhận ảnh`, `Xử lý ảnh`)
669
+ - Nếu tên bước dài hơn 10 ký tự, hệ thống sẽ tự động xuống dòng
670
+ - **Bố cục ngang (`horizontal`):** viết tên bước **thật ngắn** (2-3 từ), không dùng câu dài
671
+ - ✅ Đúng: `(Thu nhận ảnh)`, `(Trích xuất)`, `(Phân loại)`
672
+ - ❌ Sai: `(Thu nhận và tiền xử lý ảnh đầu vào)`, `(Trích xuất đặc trưng từ dữ liệu)`
673
+ - **Bố cục dọc (`vertical`):** có thể viết dài hơn (tối đa 25-30 ký tự)
668
674
 
669
675
  ---
670
676
 
@@ -702,6 +702,12 @@ caption: Figure 8: ETL pipeline
702
702
  - `[Start]` and `[End]` are recommended for the first and last steps
703
703
  - Keep workflows to **8 steps or fewer** for readability
704
704
  - Use `title:` and `caption:` for labeling
705
+ - **Keep each step label short — max 10-12 characters** (e.g., `Read Data`, `Transform`)
706
+ - Text longer than 10 characters will automatically wrap to 2 lines
707
+ - **Horizontal layout (`horizontal`):** use **very short** step labels (2-3 words max)
708
+ - ✅ Correct: `(Read Data)`, `(Transform)`, `(Classify)`
709
+ - ❌ Wrong: `(Read and preprocess input images)`, `(Extract features from data)`
710
+ - **Vertical layout (`vertical`):** may use longer labels (up to 25-30 characters)
705
711
 
706
712
  ---
707
713
 
@@ -7,5 +7,5 @@ with native math equations and syntax-highlighted code.
7
7
 
8
8
  from markdocx.core import convert_file, convert_directory
9
9
 
10
- __version__ = "4.0.0"
10
+ __version__ = "6.0.0"
11
11
  __all__ = ["convert_file", "convert_directory"]
@@ -766,31 +766,46 @@ def _render_workflow(code: str):
766
766
  fig_w = max(6, box_w + 3.0)
767
767
  fig_h = max(4, n * spacing_y + 2.0)
768
768
  else:
769
- # Horizontal: limited width per box, allow wrapping
770
- max_chars_per_line = 14
771
- box_w = max(2.2, min(3.5, max_text_len * 0.15 + 0.8))
772
- box_h = 1.0
773
- fontsize_normal = 11
774
- fontsize_decision = 10
775
- spacing_x = box_w + 1.0 # center-to-center horizontal distance
776
-
777
- fig_w = max(6, n * spacing_x + 2.0)
778
- fig_h = max(3.5, 4.0)
769
+ # Horizontal with multi-row zigzag layout
770
+ # Determine how many steps per row (max 4 per row for readability)
771
+ cols_per_row = min(4, n) # at most 4 boxes per row
772
+ if n > 4:
773
+ cols_per_row = (n + 1) // 2 # split roughly in half
774
+ cols_per_row = min(cols_per_row, 4) # cap at 4
775
+
776
+ max_chars_per_line = 16
777
+ box_w = max(3.0, min(4.5, max_text_len * 0.16 + 1.5))
778
+ box_h = 1.2
779
+ fontsize_normal = 13
780
+ fontsize_decision = 12
781
+ spacing_x = box_w + 1.5 # horizontal gap between boxes
782
+ spacing_y = 2.5 # vertical gap between rows
783
+
784
+ num_rows = (n + cols_per_row - 1) // cols_per_row
785
+ fig_w = max(8, cols_per_row * spacing_x + 2.0)
786
+ fig_h = max(4, num_rows * spacing_y + 2.5)
779
787
 
780
788
  fig, ax = plt.subplots(figsize=(fig_w, fig_h))
781
- # Do NOT use set_aspect("equal") — it distorts boxes
782
789
  ax.axis("off")
783
790
 
784
791
  # ── Calculate positions ─────────────────────────────────────
785
792
  positions = []
786
- for i in range(n):
787
- if is_vertical:
793
+ if is_vertical:
794
+ for i in range(n):
788
795
  x = fig_w / 2
789
796
  y = fig_h - 1.5 - i * spacing_y
790
- else:
791
- x = 1.5 + i * spacing_x
792
- y = fig_h / 2
793
- positions.append((x, y))
797
+ positions.append((x, y))
798
+ else:
799
+ # Zigzag: row 0 left→right, row 1 right→left, row 2 left→right, ...
800
+ for i in range(n):
801
+ row = i // cols_per_row
802
+ col = i % cols_per_row
803
+ # Reverse direction on odd rows
804
+ if row % 2 == 1:
805
+ col = cols_per_row - 1 - col
806
+ x = 1.5 + col * spacing_x
807
+ y = fig_h - 1.8 - row * spacing_y
808
+ positions.append((x, y))
794
809
 
795
810
  # Adjust axes limits
796
811
  all_x = [p[0] for p in positions]
@@ -881,12 +896,13 @@ def _render_workflow(code: str):
881
896
  cur_type = steps[i].get("type", "process")
882
897
  next_type = steps[i + 1].get("type", "process")
883
898
 
884
- # Recompute effective heights for arrow endpoints
885
899
  cur_text = _wrap_text(steps[i].get("text", ""), max_chars_per_line)
886
900
  next_text = _wrap_text(steps[i + 1].get("text", ""), max_chars_per_line)
887
901
  cur_h = max(box_h, box_h + (cur_text.count("\n")) * 0.3)
888
902
  next_h = max(box_h, box_h + (next_text.count("\n")) * 0.3)
889
903
 
904
+ same_row = abs(y1 - y2) < 0.1 # same horizontal row
905
+
890
906
  if is_vertical:
891
907
  if cur_type == "decision":
892
908
  start_y = y1 - cur_h * 0.9 - 0.08
@@ -900,18 +916,38 @@ def _render_workflow(code: str):
900
916
  arrowprops=dict(arrowstyle="-|>", color="#555555",
901
917
  lw=2, mutation_scale=15),
902
918
  zorder=2)
919
+ elif same_row:
920
+ # Horizontal arrow on same row
921
+ if x2 > x1:
922
+ # Left to right
923
+ start_x = x1 + box_w / 2 + 0.08
924
+ end_x = x2 - box_w / 2 - 0.08
925
+ else:
926
+ # Right to left
927
+ start_x = x1 - box_w / 2 - 0.08
928
+ end_x = x2 + box_w / 2 + 0.08
929
+ if cur_type == "decision":
930
+ start_x = x1 + (box_w * 0.55 + 0.08) * (1 if x2 > x1 else -1)
931
+ if next_type == "decision":
932
+ end_x = x2 - (box_w * 0.55 + 0.08) * (1 if x2 > x1 else -1)
933
+ ax.annotate("", xy=(end_x, y2), xytext=(start_x, y1),
934
+ arrowprops=dict(arrowstyle="-|>", color="#555555",
935
+ lw=2, mutation_scale=15),
936
+ zorder=2)
903
937
  else:
938
+ # Vertical arrow between rows (row transition)
904
939
  if cur_type == "decision":
905
- start_x = x1 + box_w * 0.55 + 0.08
940
+ start_y = y1 - cur_h * 0.9 - 0.08
906
941
  else:
907
- start_x = x1 + box_w / 2 + 0.08
942
+ start_y = y1 - cur_h / 2 - 0.08
908
943
  if next_type == "decision":
909
- end_x = x2 - box_w * 0.55 - 0.08
944
+ end_y = y2 + next_h * 0.9 + 0.08
910
945
  else:
911
- end_x = x2 - box_w / 2 - 0.08
912
- ax.annotate("", xy=(end_x, y2), xytext=(start_x, y1),
946
+ end_y = y2 + next_h / 2 + 0.08
947
+ ax.annotate("", xy=(x2, end_y), xytext=(x1, start_y),
913
948
  arrowprops=dict(arrowstyle="-|>", color="#555555",
914
- lw=2, mutation_scale=15),
949
+ lw=2, mutation_scale=15,
950
+ connectionstyle="arc3,rad=0"),
915
951
  zorder=2)
916
952
 
917
953
  if title:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes