codeannex 0.4.1__tar.gz → 0.4.3__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 (26) hide show
  1. {codeannex-0.4.1 → codeannex-0.4.3}/PKG-INFO +1 -1
  2. {codeannex-0.4.1 → codeannex-0.4.3}/codeannex/__main__.py +17 -4
  3. codeannex-0.4.3/codeannex/interface/cli.py +167 -0
  4. {codeannex-0.4.1 → codeannex-0.4.3}/codeannex/io/git_utils.py +16 -0
  5. {codeannex-0.4.1 → codeannex-0.4.3}/codeannex/renderer/fonts.py +15 -4
  6. {codeannex-0.4.1 → codeannex-0.4.3}/codeannex.egg-info/PKG-INFO +1 -1
  7. {codeannex-0.4.1 → codeannex-0.4.3}/pyproject.toml +1 -1
  8. codeannex-0.4.1/codeannex/interface/cli.py +0 -142
  9. {codeannex-0.4.1 → codeannex-0.4.3}/LICENSE +0 -0
  10. {codeannex-0.4.1 → codeannex-0.4.3}/README.md +0 -0
  11. {codeannex-0.4.1 → codeannex-0.4.3}/codeannex/__init__.py +0 -0
  12. {codeannex-0.4.1 → codeannex-0.4.3}/codeannex/core/__init__.py +0 -0
  13. {codeannex-0.4.1 → codeannex-0.4.3}/codeannex/core/config.py +0 -0
  14. {codeannex-0.4.1 → codeannex-0.4.3}/codeannex/core/pdf_builder.py +0 -0
  15. {codeannex-0.4.1 → codeannex-0.4.3}/codeannex/interface/__init__.py +0 -0
  16. {codeannex-0.4.1 → codeannex-0.4.3}/codeannex/io/__init__.py +0 -0
  17. {codeannex-0.4.1 → codeannex-0.4.3}/codeannex/io/file_utils.py +0 -0
  18. {codeannex-0.4.1 → codeannex-0.4.3}/codeannex/renderer/__init__.py +0 -0
  19. {codeannex-0.4.1 → codeannex-0.4.3}/codeannex/renderer/highlight.py +0 -0
  20. {codeannex-0.4.1 → codeannex-0.4.3}/codeannex/renderer/text_utils.py +0 -0
  21. {codeannex-0.4.1 → codeannex-0.4.3}/codeannex.egg-info/SOURCES.txt +0 -0
  22. {codeannex-0.4.1 → codeannex-0.4.3}/codeannex.egg-info/dependency_links.txt +0 -0
  23. {codeannex-0.4.1 → codeannex-0.4.3}/codeannex.egg-info/entry_points.txt +0 -0
  24. {codeannex-0.4.1 → codeannex-0.4.3}/codeannex.egg-info/requires.txt +0 -0
  25. {codeannex-0.4.1 → codeannex-0.4.3}/codeannex.egg-info/top_level.txt +0 -0
  26. {codeannex-0.4.1 → codeannex-0.4.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeannex
3
- Version: 0.4.1
3
+ Version: 0.4.3
4
4
  Summary: Generates a professional PDF source code annex with Smart Index, Images and Emoji support.
5
5
  License: MIT
6
6
  Project-URL: Repository, https://github.com/tanhleno/codeannex
@@ -13,12 +13,25 @@ from reportlab.lib.units import cm
13
13
 
14
14
 
15
15
  def check_emoji_font_style():
16
- emoji_font = register_emoji_font()
16
+ from .renderer.fonts import get_system_font_paths
17
+ emoji_font, emoji_path = register_emoji_font()
17
18
  if emoji_font:
18
19
  print(f"✅ Emoji font registered: {emoji_font}")
19
- print("💡 Tip: Install Google Noto fonts for authentic Google emoji style")
20
+ if emoji_path:
21
+ p_lower = emoji_path.lower()
22
+ if "noto" in p_lower:
23
+ print("💡 Tip: Google Noto fonts are being used.")
24
+ elif "symbola" in p_lower:
25
+ print("💡 Tip: Symbola font is being used.")
20
26
  else:
21
- print("⚠️ No emoji font found - emojis may not render correctly")
27
+ print("⚠️ No emoji font found - emojis may not render correctly.")
28
+ print(f"💡 Recommendation: Install 'Symbola' or 'Google Noto Emoji' fonts.")
29
+
30
+ paths = get_system_font_paths()
31
+ print(f"\n🔍 Font search paths:")
32
+ for p in paths:
33
+ print(f" - {p}")
34
+ print(f"\n💡 You can add custom search paths using: --font-path /path/to/fonts")
22
35
  return emoji_font
23
36
 
24
37
 
@@ -55,7 +68,7 @@ def main():
55
68
  repo_url, branch_name = args.repo_url or git_url, args.branch or git_branch
56
69
 
57
70
  mono_font, is_ttf, ttf_path = register_best_font()
58
- emoji_font = register_emoji_font(error_on_missing=not args.emoji_description)
71
+ emoji_font, _ = register_emoji_font(error_on_missing=not args.emoji_description)
59
72
  init_sprites(is_ttf, ttf_path)
60
73
 
61
74
  def get_margin(spec, general, default):
@@ -0,0 +1,167 @@
1
+ import argparse
2
+ from pathlib import Path
3
+ from ..core.config import PDFConfig
4
+ from ..io.git_utils import get_git_info, get_git_remotes
5
+
6
+ # ANSI Colors for a better CLI experience
7
+ BOLD = "\033[1m"
8
+ BLUE = "\033[34m"
9
+ CYAN = "\033[36m"
10
+ GREEN = "\033[32m"
11
+ YELLOW = "\033[33m"
12
+ RESET = "\033[0m"
13
+
14
+ def parse_args():
15
+ parser = argparse.ArgumentParser(description="Generates a PDF code annex with Smart Index and Images.")
16
+ parser.add_argument("dir", nargs="?", default=".", help="Project directory")
17
+ parser.add_argument("-o", "--output", default=None, help="Output PDF filename")
18
+ parser.add_argument("-n", "--name", default=None, help="Project name")
19
+ parser.add_argument("--no-input", action="store_true", help="Disable interactive mode")
20
+ parser.add_argument("--cover-title", default="TECHNICAL ANNEX", help="Cover title")
21
+ parser.add_argument("--margin", type=float, default=None, help="General margin (cm)")
22
+ parser.add_argument("--margin-left", type=float, default=None, help="Left margin (cm)")
23
+ parser.add_argument("--margin-right", type=float, default=None, help="Right margin (cm)")
24
+ parser.add_argument("--margin-top", type=float, default=None, help="Top margin (cm)")
25
+ parser.add_argument("--margin-bottom", type=float, default=None, help="Bottom margin (cm)")
26
+ parser.add_argument("--start-page", type=int, default=1, help="Start page")
27
+ parser.add_argument("--show-project", action="store_true", help="Show project in footer")
28
+ parser.add_argument("--repo-url", default=None, help="Repo URL")
29
+ parser.add_argument("--branch", default=None, help="Branch name")
30
+ parser.add_argument("--repo-label", default="Repository Name: ", help="Label for repo")
31
+ parser.add_argument("--project-label", default="Project: ", help="Label for project")
32
+ parser.add_argument("--page-width", type=float, default=None, help="Width (mm)")
33
+ parser.add_argument("--page-height", type=float, default=None, help="Height (mm)")
34
+ parser.add_argument("--include", action="append", default=None, help="Include pattern")
35
+ parser.add_argument("--exclude", action="append", default=None, help="Exclude pattern")
36
+ parser.add_argument("--no-git", action="store_true", help="No Git")
37
+ parser.add_argument("--file-part-format", default="(part {current}/{total})", help="Part format")
38
+ parser.add_argument("--summary-title", default="Summary / File Index", help="Summary title")
39
+ parser.add_argument("--cover-subtitle", default="Source Code Documentation", help="Cover subtitle")
40
+ parser.add_argument("--page-number-size", type=int, default=8, help="Page number size")
41
+ parser.add_argument("--page-number-format", default="{n}", help="Page number format")
42
+ parser.add_argument("--no-page-numbers", action="store_true", help="No page numbers")
43
+ parser.add_argument("--page-bg-color", default="#ffffff", help="Page BG color")
44
+ parser.add_argument("--normal-font", default=None, help="Normal font")
45
+ parser.add_argument("--normal-size", type=int, default=10, help="Normal size")
46
+ parser.add_argument("--normal-color", default="#4c4f69", help="Normal color")
47
+ parser.add_argument("--bold-font", default=None, help="Bold font")
48
+ parser.add_argument("--title-font", default=None, help="Title font")
49
+ parser.add_argument("--title-size", type=int, default=28, help="Title size")
50
+ parser.add_argument("--title-color", default="#1e1e2e", help="Title color")
51
+ parser.add_argument("--subtitle-font", default=None, help="Subtitle font")
52
+ parser.add_argument("--subtitle-size", type=int, default=18, help="Subtitle size")
53
+ parser.add_argument("--subtitle-color", default=None, help="Subtitle color")
54
+ parser.add_argument("--primary-color", default="#1e66f5", help="Primary color")
55
+ parser.add_argument("--code-size", type=int, default=10, help="Code size")
56
+ parser.add_argument("--code-bg", default="#1e1e2e", help="Code background")
57
+ parser.add_argument("--mono-font", default=None, help="Mono font")
58
+ parser.add_argument("--emoji-font", default=None, help="Emoji font")
59
+ parser.add_argument("--font-path", action="append", default=None, help="Additional directory to search for fonts")
60
+ parser.add_argument("--emoji-description", action="store_true", help="Emoji desc")
61
+ parser.add_argument("--check-emoji-font", action="store_true", help="Check emoji")
62
+ return parser.parse_known_args()
63
+
64
+ def _print_header(step, total, title, details):
65
+ print(f"\n{BLUE}Step {step}/{total}{RESET} {BOLD}--- {title} ---{RESET}")
66
+ if details:
67
+ print(f" {CYAN}i{RESET} {details}")
68
+
69
+ def _ask_section(step, total, title, details, default_yes=False):
70
+ _print_header(step, total, title, details)
71
+ prompt = f"{GREEN}Y{RESET}/n" if default_yes else f"y/{GREEN}N{RESET}"
72
+ res = input(f" Customize this section? ({prompt}): ").strip().lower()
73
+ if not res: return default_yes
74
+ return res == 'y'
75
+
76
+ def _input_field(label, default):
77
+ prompt = f" {label} {CYAN}[{default if default is not None else ''}]{RESET}: "
78
+ return input(prompt).strip()
79
+
80
+ def run_interactive_wizard(args):
81
+ try:
82
+ print(f"\n{BOLD}{BLUE}✨ Welcome to codeannex Interactive Wizard! ✨{RESET}")
83
+ print(f"{YELLOW}Uppercase letters in prompts indicate the [default] action on Enter.{RESET}\n")
84
+
85
+ root = Path(args.dir).resolve()
86
+ git_url, git_branch, _ = get_git_info(root)
87
+ remotes = get_git_remotes(root)
88
+ total_steps = 6
89
+
90
+ # 1. Project Identity
91
+ _print_header(1, total_steps, "Project Identity", "Basic identification of your document")
92
+ args.name = _input_field("Project Name", args.name or root.name) or (args.name or root.name)
93
+
94
+ # 2. Repository Info
95
+ has_git = bool(remotes or git_branch)
96
+ if has_git:
97
+ if len(remotes) > 1:
98
+ _print_header(2, total_steps, "Repository Info", "Multiple Git remotes detected")
99
+ print(f" Available remotes:")
100
+ remote_names = list(remotes.keys())
101
+ for i, name in enumerate(remote_names, 1):
102
+ print(f" {i}. {name} ({remotes[name]})")
103
+
104
+ choice = _input_field("Select remote (number) or press Enter for origin", "1")
105
+ if choice.isdigit() and 1 <= int(choice) <= len(remote_names):
106
+ selected_remote = remote_names[int(choice)-1]
107
+ git_url = remotes[selected_remote]
108
+ elif "origin" in remotes:
109
+ git_url = remotes["origin"]
110
+ else:
111
+ git_url = remotes[remote_names[0]]
112
+ elif len(remotes) == 1:
113
+ git_url = list(remotes.values())[0]
114
+
115
+ _print_header(2, total_steps, "Repository Info", f"Detected: {git_branch or 'N/A'} @ {git_url or 'N/A'}")
116
+ if input(f" Use detected Git info? ({GREEN}Y{RESET}/n): ").strip().lower() != 'n':
117
+ args.branch = git_branch
118
+ args.repo_url = git_url
119
+ else:
120
+ args.branch = _input_field("Branch Name", git_branch or 'None') or git_branch
121
+ args.repo_url = _input_field("Repository URL", git_url or 'None') or git_url
122
+ else:
123
+ if _ask_section(2, total_steps, "Repository Info", "Branch and Repository URL (No Git detected)"):
124
+ args.branch = _input_field("Branch Name", "None") or None
125
+ args.repo_url = _input_field("Repository URL", "None") or None
126
+
127
+ # 3. Visual Style
128
+ if _ask_section(3, total_steps, "Visual Style", "Titles, Subtitles, Accent Colors"):
129
+ args.cover_title = _input_field("Cover Title", args.cover_title) or args.cover_title
130
+ args.cover_subtitle = _input_field("Cover Subtitle", args.cover_subtitle) or args.cover_subtitle
131
+ args.primary_color = _input_field("Primary Accent Color (HEX)", args.primary_color) or args.primary_color
132
+ args.title_color = _input_field("Title Color (HEX)", args.title_color) or args.title_color
133
+
134
+ # 4. Typography
135
+ if _ask_section(4, total_steps, "Typography", "Fonts and Text Sizes"):
136
+ args.title_font = _input_field("Title Font", args.title_font or 'Helvetica') or args.title_font
137
+ args.normal_font = _input_field("Normal Font", args.normal_font or 'Helvetica') or args.normal_font
138
+ args.mono_font = _input_field("Monospace Font", args.mono_font or 'Auto') or args.mono_font
139
+ args.code_size = int(_input_field("Code Font Size", args.code_size) or args.code_size)
140
+
141
+ paths = _input_field("Additional Font Paths (comma-separated)", "None")
142
+ if paths:
143
+ args.font_path = [p.strip() for p in paths.split(",") if p.strip()]
144
+
145
+ # 5. Page Layout
146
+ if _ask_section(5, total_steps, "Page Layout & Margins", "Margins, Paper Size, Page Numbering"):
147
+ args.margin_top = float(_input_field("Top Margin (cm)", args.margin_top or 2.0) or (args.margin_top or 2.0))
148
+ args.margin_bottom = float(_input_field("Bottom Margin (cm)", args.margin_bottom or 2.0) or (args.margin_bottom or 2.0))
149
+ args.page_width = float(_input_field("Page Width (mm)", 210.0) or 210.0)
150
+ args.page_height = float(_input_field("Page Height (mm)", 297.0) or 297.0)
151
+ args.no_page_numbers = input(f" Disable page numbers? (y/{GREEN}N{RESET}): ").strip().lower() == 'y'
152
+ if not args.no_page_numbers:
153
+ args.start_page = int(_input_field("Start Page Number", args.start_page) or args.start_page)
154
+
155
+ # 6. Filters
156
+ if _ask_section(6, total_steps, "File Filters", "Include/Exclude patterns (glob)"):
157
+ inc = _input_field("Include Patterns (comma-separated)", "None")
158
+ if inc: args.include = [p.strip() for p in inc.split(",") if p.strip()]
159
+ exc = _input_field("Exclude Patterns (comma-separated)", "None")
160
+ if exc: args.exclude = [p.strip() for p in exc.split(",") if p.strip()]
161
+
162
+ print(f"\n{BOLD}{GREEN}🚀 Configuration complete! Generating PDF...{RESET}\n")
163
+
164
+ except KeyboardInterrupt:
165
+ import sys
166
+ print(f"\n\n{YELLOW}⚠️ Wizard aborted by user (Ctrl+C).{RESET}")
167
+ sys.exit(0)
@@ -38,6 +38,22 @@ def get_git_info(root: Path, use_git: bool = True) -> tuple[str | None, str | No
38
38
  except (subprocess.CalledProcessError, FileNotFoundError): pass
39
39
  return repo_url, branch_name, commit_sha
40
40
 
41
+ def get_git_remotes(root: Path) -> dict[str, str]:
42
+ """Returns a dictionary of remote names and URLs."""
43
+ try:
44
+ res = subprocess.run(["git", "remote", "-v"], cwd=root, capture_output=True, text=True)
45
+ remotes = {}
46
+ if res.returncode == 0:
47
+ for line in res.stdout.splitlines():
48
+ parts = line.split()
49
+ if len(parts) >= 2:
50
+ name, url = parts[0], parts[1]
51
+ if "(fetch)" in line or len(parts) == 2:
52
+ remotes[name] = url
53
+ return remotes
54
+ except (subprocess.CalledProcessError, FileNotFoundError):
55
+ return {}
56
+
41
57
  def get_git_files(root: Path) -> list[Path]:
42
58
  """Returns a list of files tracked or untracked by Git, respecting .gitignore."""
43
59
  tracked = subprocess.run(["git", "ls-files", "-z"], cwd=root, capture_output=True, check=True).stdout.split(b"\0")
@@ -102,18 +102,24 @@ def register_best_font():
102
102
  if name == "Courier": print("ℹ️ Monospace font fallback: Using standard 'Courier'.")
103
103
  return name, name != "Courier", path
104
104
 
105
+ REGISTERED_EMOJI_FONT_PATH: str | None = None
106
+
105
107
  def register_emoji_font(error_on_missing=False):
108
+ global REGISTERED_EMOJI_FONT_PATH
106
109
  name, path = _register_font("CustomEmoji", EMOJI_SEARCH_PATHS, None)
107
110
  if name is None:
108
111
  dynamic_path = find_font_file("NotoEmoji") or find_font_file("Symbola")
109
- if dynamic_path: name, _ = _register_font("CustomEmoji", [dynamic_path], None)
112
+ if dynamic_path:
113
+ name, path = _register_font("CustomEmoji", [dynamic_path], None)
114
+
115
+ REGISTERED_EMOJI_FONT_PATH = path
110
116
  if name is None:
111
117
  if error_on_missing:
112
118
  import sys
113
119
  print("❌ Error: No emoji font found.", file=sys.stderr)
114
120
  sys.exit(1)
115
121
  else: print("ℹ️ Emoji fallback: No emoji font found.")
116
- return name
122
+ return name, path
117
123
 
118
124
  def get_emoji_font_style(font_path: str | None) -> str | None:
119
125
  if not font_path: return None
@@ -135,8 +141,13 @@ def is_google_like_emoji_font(font_path: str | None) -> bool:
135
141
 
136
142
  def get_current_emoji_font_info() -> dict:
137
143
  if "CustomEmoji" in pdfmetrics._fonts:
138
- return {"name": "CustomEmoji", "is_registered": True, "is_google_like": False, "style": "Unknown"}
139
- return {"name": None, "is_registered": False, "is_google_like": False, "style": None}
144
+ return {
145
+ "name": "CustomEmoji",
146
+ "is_registered": True,
147
+ "path": REGISTERED_EMOJI_FONT_PATH,
148
+ "style": get_emoji_font_style(REGISTERED_EMOJI_FONT_PATH)
149
+ }
150
+ return {"name": None, "is_registered": False, "path": None, "style": None}
140
151
 
141
152
  def is_char_supported(char: str, font_name: str) -> bool:
142
153
  try:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeannex
3
- Version: 0.4.1
3
+ Version: 0.4.3
4
4
  Summary: Generates a professional PDF source code annex with Smart Index, Images and Emoji support.
5
5
  License: MIT
6
6
  Project-URL: Repository, https://github.com/tanhleno/codeannex
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "codeannex"
7
- version = "0.4.1"
7
+ version = "0.4.3"
8
8
  description = "Generates a professional PDF source code annex with Smart Index, Images and Emoji support."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -1,142 +0,0 @@
1
- import argparse
2
- from pathlib import Path
3
- from ..core.config import PDFConfig
4
- from ..io.git_utils import get_git_info
5
-
6
- # ANSI Colors for a better CLI experience
7
- BOLD = "\033[1m"
8
- BLUE = "\033[34m"
9
- CYAN = "\033[36m"
10
- GREEN = "\033[32m"
11
- YELLOW = "\033[33m"
12
- RESET = "\033[0m"
13
-
14
- def parse_args():
15
- parser = argparse.ArgumentParser(description="Generates a PDF code annex with Smart Index and Images.")
16
- parser.add_argument("dir", nargs="?", default=".", help="Project directory")
17
- parser.add_argument("-o", "--output", default=None, help="Output PDF filename")
18
- parser.add_argument("-n", "--name", default=None, help="Project name")
19
- parser.add_argument("--no-input", action="store_true", help="Disable interactive mode")
20
- parser.add_argument("--cover-title", default="TECHNICAL ANNEX", help="Cover title")
21
- parser.add_argument("--margin", type=float, default=None, help="General margin (cm)")
22
- parser.add_argument("--margin-left", type=float, default=None, help="Left margin (cm)")
23
- parser.add_argument("--margin-right", type=float, default=None, help="Right margin (cm)")
24
- parser.add_argument("--margin-top", type=float, default=None, help="Top margin (cm)")
25
- parser.add_argument("--margin-bottom", type=float, default=None, help="Bottom margin (cm)")
26
- parser.add_argument("--start-page", type=int, default=1, help="Start page")
27
- parser.add_argument("--show-project", action="store_true", help="Show project in footer")
28
- parser.add_argument("--repo-url", default=None, help="Repo URL")
29
- parser.add_argument("--branch", default=None, help="Branch name")
30
- parser.add_argument("--repo-label", default="Repository Name: ", help="Label for repo")
31
- parser.add_argument("--project-label", default="Project: ", help="Label for project")
32
- parser.add_argument("--page-width", type=float, default=None, help="Width (mm)")
33
- parser.add_argument("--page-height", type=float, default=None, help="Height (mm)")
34
- parser.add_argument("--include", action="append", default=None, help="Include pattern")
35
- parser.add_argument("--exclude", action="append", default=None, help="Exclude pattern")
36
- parser.add_argument("--no-git", action="store_true", help="No Git")
37
- parser.add_argument("--file-part-format", default="(part {current}/{total})", help="Part format")
38
- parser.add_argument("--summary-title", default="Summary / File Index", help="Summary title")
39
- parser.add_argument("--cover-subtitle", default="Source Code Documentation", help="Cover subtitle")
40
- parser.add_argument("--page-number-size", type=int, default=8, help="Page number size")
41
- parser.add_argument("--page-number-format", default="{n}", help="Page number format")
42
- parser.add_argument("--no-page-numbers", action="store_true", help="No page numbers")
43
- parser.add_argument("--page-bg-color", default="#ffffff", help="Page BG color")
44
- parser.add_argument("--normal-font", default=None, help="Normal font")
45
- parser.add_argument("--normal-size", type=int, default=10, help="Normal size")
46
- parser.add_argument("--normal-color", default="#4c4f69", help="Normal color")
47
- parser.add_argument("--bold-font", default=None, help="Bold font")
48
- parser.add_argument("--title-font", default=None, help="Title font")
49
- parser.add_argument("--title-size", type=int, default=28, help="Title size")
50
- parser.add_argument("--title-color", default="#1e1e2e", help="Title color")
51
- parser.add_argument("--subtitle-font", default=None, help="Subtitle font")
52
- parser.add_argument("--subtitle-size", type=int, default=18, help="Subtitle size")
53
- parser.add_argument("--subtitle-color", default=None, help="Subtitle color")
54
- parser.add_argument("--primary-color", default="#1e66f5", help="Primary color")
55
- parser.add_argument("--code-size", type=int, default=10, help="Code size")
56
- parser.add_argument("--code-bg", default="#1e1e2e", help="Code background")
57
- parser.add_argument("--mono-font", default=None, help="Mono font")
58
- parser.add_argument("--emoji-font", default=None, help="Emoji font")
59
- parser.add_argument("--font-path", action="append", default=None, help="Additional directory to search for fonts")
60
- parser.add_argument("--emoji-description", action="store_true", help="Emoji desc")
61
- parser.add_argument("--check-emoji-font", action="store_true", help="Check emoji")
62
- return parser.parse_known_args()
63
-
64
- def _print_header(step, total, title, details):
65
- print(f"\n{BLUE}Step {step}/{total}{RESET} {BOLD}--- {title} ---{RESET}")
66
- if details:
67
- print(f" {CYAN}i{RESET} {details}")
68
-
69
- def _ask_section(step, total, title, details, default_yes=False):
70
- _print_header(step, total, title, details)
71
- prompt = f"{GREEN}Y{RESET}/n" if default_yes else f"y/{GREEN}N{RESET}"
72
- res = input(f" Customize this section? ({prompt}): ").strip().lower()
73
- if not res: return default_yes
74
- return res == 'y'
75
-
76
- def _input_field(label, default):
77
- prompt = f" {label} {CYAN}[{default if default is not None else ''}]{RESET}: "
78
- return input(prompt).strip()
79
-
80
- def run_interactive_wizard(args):
81
- print(f"\n{BOLD}{BLUE}✨ Welcome to codeannex Interactive Wizard! ✨{RESET}")
82
- print(f"{YELLOW}Uppercase letters in prompts indicate the [default] action on Enter.{RESET}\n")
83
-
84
- root = Path(args.dir).resolve()
85
- git_url, git_branch = get_git_info(root)
86
- total_steps = 6
87
-
88
- # 1. Project Identity
89
- _print_header(1, total_steps, "Project Identity", "Basic identification of your document")
90
- args.name = _input_field("Project Name", args.name or root.name) or (args.name or root.name)
91
-
92
- # 2. Repository Info
93
- has_git = bool(git_url or git_branch)
94
- if has_git:
95
- _print_header(2, total_steps, "Repository Info", f"Detected: {git_branch or 'N/A'} @ {git_url or 'N/A'}")
96
- if input(f" Use detected Git info? ({GREEN}Y{RESET}/n): ").strip().lower() != 'n':
97
- args.branch = git_branch
98
- args.repo_url = git_url
99
- else:
100
- args.branch = _input_field("Branch Name", git_branch or 'None') or git_branch
101
- args.repo_url = _input_field("Repository URL", git_url or 'None') or git_url
102
- else:
103
- if _ask_section(2, total_steps, "Repository Info", "Branch and Repository URL (No Git detected)"):
104
- args.branch = _input_field("Branch Name", "None") or None
105
- args.repo_url = _input_field("Repository URL", "None") or None
106
-
107
- # 3. Visual Style
108
- if _ask_section(3, total_steps, "Visual Style", "Titles, Subtitles, Accent Colors"):
109
- args.cover_title = _input_field("Cover Title", args.cover_title) or args.cover_title
110
- args.cover_subtitle = _input_field("Cover Subtitle", args.cover_subtitle) or args.cover_subtitle
111
- args.primary_color = _input_field("Primary Accent Color (HEX)", args.primary_color) or args.primary_color
112
- args.title_color = _input_field("Title Color (HEX)", args.title_color) or args.title_color
113
-
114
- # 4. Typography
115
- if _ask_section(4, total_steps, "Typography", "Fonts and Text Sizes"):
116
- args.title_font = _input_field("Title Font", args.title_font or 'Helvetica') or args.title_font
117
- args.normal_font = _input_field("Normal Font", args.normal_font or 'Helvetica') or args.normal_font
118
- args.mono_font = _input_field("Monospace Font", args.mono_font or 'Auto') or args.mono_font
119
- args.code_size = int(_input_field("Code Font Size", args.code_size) or args.code_size)
120
-
121
- paths = _input_field("Additional Font Paths (comma-separated)", "None")
122
- if paths:
123
- args.font_path = [p.strip() for p in paths.split(",") if p.strip()]
124
-
125
- # 5. Page Layout
126
- if _ask_section(5, total_steps, "Page Layout & Margins", "Margins, Paper Size, Page Numbering"):
127
- args.margin_top = float(_input_field("Top Margin (cm)", args.margin_top or 2.0) or (args.margin_top or 2.0))
128
- args.margin_bottom = float(_input_field("Bottom Margin (cm)", args.margin_bottom or 2.0) or (args.margin_bottom or 2.0))
129
- args.page_width = float(_input_field("Page Width (mm)", 210.0) or 210.0)
130
- args.page_height = float(_input_field("Page Height (mm)", 297.0) or 297.0)
131
- args.no_page_numbers = input(f" Disable page numbers? (y/{GREEN}N{RESET}): ").strip().lower() == 'y'
132
- if not args.no_page_numbers:
133
- args.start_page = int(_input_field("Start Page Number", args.start_page) or args.start_page)
134
-
135
- # 6. Filters
136
- if _ask_section(6, total_steps, "File Filters", "Include/Exclude patterns (glob)"):
137
- inc = _input_field("Include Patterns (comma-separated)", "None")
138
- if inc: args.include = [p.strip() for p in inc.split(",") if p.strip()]
139
- exc = _input_field("Exclude Patterns (comma-separated)", "None")
140
- if exc: args.exclude = [p.strip() for p in exc.split(",") if p.strip()]
141
-
142
- print(f"\n{BOLD}{GREEN}🚀 Configuration complete! Generating PDF...{RESET}\n")
File without changes
File without changes
File without changes