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.
- {codeannex-0.4.1 → codeannex-0.4.3}/PKG-INFO +1 -1
- {codeannex-0.4.1 → codeannex-0.4.3}/codeannex/__main__.py +17 -4
- codeannex-0.4.3/codeannex/interface/cli.py +167 -0
- {codeannex-0.4.1 → codeannex-0.4.3}/codeannex/io/git_utils.py +16 -0
- {codeannex-0.4.1 → codeannex-0.4.3}/codeannex/renderer/fonts.py +15 -4
- {codeannex-0.4.1 → codeannex-0.4.3}/codeannex.egg-info/PKG-INFO +1 -1
- {codeannex-0.4.1 → codeannex-0.4.3}/pyproject.toml +1 -1
- codeannex-0.4.1/codeannex/interface/cli.py +0 -142
- {codeannex-0.4.1 → codeannex-0.4.3}/LICENSE +0 -0
- {codeannex-0.4.1 → codeannex-0.4.3}/README.md +0 -0
- {codeannex-0.4.1 → codeannex-0.4.3}/codeannex/__init__.py +0 -0
- {codeannex-0.4.1 → codeannex-0.4.3}/codeannex/core/__init__.py +0 -0
- {codeannex-0.4.1 → codeannex-0.4.3}/codeannex/core/config.py +0 -0
- {codeannex-0.4.1 → codeannex-0.4.3}/codeannex/core/pdf_builder.py +0 -0
- {codeannex-0.4.1 → codeannex-0.4.3}/codeannex/interface/__init__.py +0 -0
- {codeannex-0.4.1 → codeannex-0.4.3}/codeannex/io/__init__.py +0 -0
- {codeannex-0.4.1 → codeannex-0.4.3}/codeannex/io/file_utils.py +0 -0
- {codeannex-0.4.1 → codeannex-0.4.3}/codeannex/renderer/__init__.py +0 -0
- {codeannex-0.4.1 → codeannex-0.4.3}/codeannex/renderer/highlight.py +0 -0
- {codeannex-0.4.1 → codeannex-0.4.3}/codeannex/renderer/text_utils.py +0 -0
- {codeannex-0.4.1 → codeannex-0.4.3}/codeannex.egg-info/SOURCES.txt +0 -0
- {codeannex-0.4.1 → codeannex-0.4.3}/codeannex.egg-info/dependency_links.txt +0 -0
- {codeannex-0.4.1 → codeannex-0.4.3}/codeannex.egg-info/entry_points.txt +0 -0
- {codeannex-0.4.1 → codeannex-0.4.3}/codeannex.egg-info/requires.txt +0 -0
- {codeannex-0.4.1 → codeannex-0.4.3}/codeannex.egg-info/top_level.txt +0 -0
- {codeannex-0.4.1 → codeannex-0.4.3}/setup.cfg +0 -0
|
@@ -13,12 +13,25 @@ from reportlab.lib.units import cm
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
def check_emoji_font_style():
|
|
16
|
-
|
|
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
|
-
|
|
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:
|
|
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 {
|
|
139
|
-
|
|
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:
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "codeannex"
|
|
7
|
-
version = "0.4.
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|