codeannex 0.3.1__tar.gz → 0.3.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.3.1 → codeannex-0.3.3}/PKG-INFO +1 -1
- {codeannex-0.3.1 → codeannex-0.3.3}/codeannex/__main__.py +18 -4
- {codeannex-0.3.1 → codeannex-0.3.3}/codeannex/core/pdf_builder.py +33 -32
- {codeannex-0.3.1 → codeannex-0.3.3}/codeannex.egg-info/PKG-INFO +1 -1
- {codeannex-0.3.1 → codeannex-0.3.3}/pyproject.toml +1 -1
- {codeannex-0.3.1 → codeannex-0.3.3}/LICENSE +0 -0
- {codeannex-0.3.1 → codeannex-0.3.3}/README.md +0 -0
- {codeannex-0.3.1 → codeannex-0.3.3}/codeannex/__init__.py +0 -0
- {codeannex-0.3.1 → codeannex-0.3.3}/codeannex/core/__init__.py +0 -0
- {codeannex-0.3.1 → codeannex-0.3.3}/codeannex/core/config.py +0 -0
- {codeannex-0.3.1 → codeannex-0.3.3}/codeannex/interface/__init__.py +0 -0
- {codeannex-0.3.1 → codeannex-0.3.3}/codeannex/interface/cli.py +0 -0
- {codeannex-0.3.1 → codeannex-0.3.3}/codeannex/io/__init__.py +0 -0
- {codeannex-0.3.1 → codeannex-0.3.3}/codeannex/io/file_utils.py +0 -0
- {codeannex-0.3.1 → codeannex-0.3.3}/codeannex/io/git_utils.py +0 -0
- {codeannex-0.3.1 → codeannex-0.3.3}/codeannex/renderer/__init__.py +0 -0
- {codeannex-0.3.1 → codeannex-0.3.3}/codeannex/renderer/fonts.py +0 -0
- {codeannex-0.3.1 → codeannex-0.3.3}/codeannex/renderer/highlight.py +0 -0
- {codeannex-0.3.1 → codeannex-0.3.3}/codeannex/renderer/text_utils.py +0 -0
- {codeannex-0.3.1 → codeannex-0.3.3}/codeannex.egg-info/SOURCES.txt +0 -0
- {codeannex-0.3.1 → codeannex-0.3.3}/codeannex.egg-info/dependency_links.txt +0 -0
- {codeannex-0.3.1 → codeannex-0.3.3}/codeannex.egg-info/entry_points.txt +0 -0
- {codeannex-0.3.1 → codeannex-0.3.3}/codeannex.egg-info/requires.txt +0 -0
- {codeannex-0.3.1 → codeannex-0.3.3}/codeannex.egg-info/top_level.txt +0 -0
- {codeannex-0.3.1 → codeannex-0.3.3}/setup.cfg +0 -0
|
@@ -112,11 +112,25 @@ def main():
|
|
|
112
112
|
if fp.resolve() in (script_path, output_path): continue
|
|
113
113
|
except: pass
|
|
114
114
|
if not fp.is_file(): continue
|
|
115
|
+
|
|
115
116
|
ext = fp.suffix.lower()
|
|
116
|
-
if ext == ".svg":
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
elif
|
|
117
|
+
if ext == ".svg":
|
|
118
|
+
included += [(fp, "image"), (fp, "text")]
|
|
119
|
+
continue
|
|
120
|
+
elif ext in IMAGE_EXTENSIONS:
|
|
121
|
+
included.append((fp, "image"))
|
|
122
|
+
continue
|
|
123
|
+
elif ext in BINARY_EXTENSIONS:
|
|
124
|
+
rel_path = fp.relative_to(root).as_posix()
|
|
125
|
+
print(f"⚠️ Skipping binary/unsupported file: {rel_path}")
|
|
126
|
+
continue
|
|
127
|
+
|
|
128
|
+
file_type = classify_file(fp)
|
|
129
|
+
if file_type == "text":
|
|
130
|
+
included.append((fp, "text"))
|
|
131
|
+
else:
|
|
132
|
+
rel_path = fp.relative_to(root).as_posix()
|
|
133
|
+
print(f"⚠️ Skipping binary file: {rel_path}")
|
|
120
134
|
|
|
121
135
|
if not included:
|
|
122
136
|
print("❌ No compatible files found."); return
|
|
@@ -133,27 +133,27 @@ class ModernAnnexPDF:
|
|
|
133
133
|
curr_y = self.config.page_height * 0.45
|
|
134
134
|
|
|
135
135
|
if project_name:
|
|
136
|
-
|
|
136
|
+
display_repo = f"{self.config.repo_label}{project_name}"
|
|
137
|
+
if self.config.branch_name:
|
|
138
|
+
display_repo += f" ({self.config.branch_name})"
|
|
139
|
+
|
|
140
|
+
name_w = self._gsw(display_repo, self.config.normal_font, 14)
|
|
141
|
+
start_x = mid_x - name_w / 2
|
|
142
|
+
|
|
143
|
+
# Use primary color for project name if it has a link
|
|
144
|
+
final_color = colors.HexColor(self.config.primary_color) if self.config.repo_url else text_color
|
|
145
|
+
self._dtf(start_x, curr_y, display_repo, self.config.normal_font, 14, final_color)
|
|
146
|
+
|
|
147
|
+
if self.config.repo_url:
|
|
148
|
+
self.c.linkURL(self.config.repo_url, (start_x, curr_y - 2, start_x + name_w, curr_y + 12), relative=0, thickness=0, border=None)
|
|
149
|
+
|
|
137
150
|
curr_y -= 8*mm
|
|
138
151
|
|
|
139
|
-
# 4. Technical Metadata (
|
|
140
|
-
tech_items = []
|
|
141
|
-
if self.config.branch_name:
|
|
142
|
-
tech_items.append(f"Branch: {self.config.branch_name}")
|
|
152
|
+
# 4. Technical Metadata (SHA)
|
|
143
153
|
if self.config.commit_sha:
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
if tech_items:
|
|
147
|
-
tech_str = " | ".join(tech_items)
|
|
154
|
+
tech_str = f"Commit: {self.config.commit_sha}"
|
|
148
155
|
self._dctf(mid_x, curr_y, tech_str, self.config.normal_font, 10, colors.HexColor("#6c7086"))
|
|
149
156
|
curr_y -= 12*mm
|
|
150
|
-
|
|
151
|
-
if self.config.repo_url:
|
|
152
|
-
url = self.config.repo_url
|
|
153
|
-
url_w = self._gsw(url, self.config.normal_font, 11)
|
|
154
|
-
start_x = mid_x - url_w / 2
|
|
155
|
-
self._dtf(start_x, curr_y, url, self.config.normal_font, 11, colors.HexColor(self.config.primary_color))
|
|
156
|
-
self.c.linkURL(url, (start_x, curr_y - 2, start_x + url_w, curr_y + 10), relative=0, thickness=0, border=None)
|
|
157
157
|
|
|
158
158
|
def draw_summary_page(self, files: list):
|
|
159
159
|
self.start_new_page()
|
|
@@ -163,7 +163,16 @@ class ModernAnnexPDF:
|
|
|
163
163
|
title_font, 16, colors.HexColor(self.config.title_color))
|
|
164
164
|
self.y -= 12*mm
|
|
165
165
|
nested_tree: dict = {"_files": []}
|
|
166
|
+
|
|
167
|
+
# To avoid double entries for SVG (Image + Code)
|
|
168
|
+
processed_paths = set()
|
|
169
|
+
unique_files = []
|
|
166
170
|
for fpath, ftype in files:
|
|
171
|
+
if fpath not in processed_paths:
|
|
172
|
+
unique_files.append((fpath, ftype))
|
|
173
|
+
processed_paths.add(fpath)
|
|
174
|
+
|
|
175
|
+
for fpath, ftype in unique_files:
|
|
167
176
|
rel = fpath.relative_to(self.project_root)
|
|
168
177
|
curr = nested_tree
|
|
169
178
|
for part in rel.parts[:-1]: curr = curr.setdefault(part, {"_files": []})
|
|
@@ -174,10 +183,7 @@ class ModernAnnexPDF:
|
|
|
174
183
|
def _draw_recursive_summary(self, node: dict, depth: int, path_parts: list, is_last_list: list):
|
|
175
184
|
subdirs = sorted([k for k in node.keys() if k != "_files"], key=str.lower)
|
|
176
185
|
files_in_node = node.get("_files", [])
|
|
177
|
-
|
|
178
|
-
# Subdirectories first, then files (matches the new sort_files logic)
|
|
179
186
|
all_entries = [(d, "dir") for d in subdirs] + [(f, "file") for f in files_in_node]
|
|
180
|
-
|
|
181
187
|
indent_step, text_color = 5*mm, colors.HexColor(self.config.normal_text_color)
|
|
182
188
|
for i, (item, type) in enumerate(all_entries):
|
|
183
189
|
is_last = (i == len(all_entries) - 1)
|
|
@@ -204,9 +210,9 @@ class ModernAnnexPDF:
|
|
|
204
210
|
self.y -= 6*mm
|
|
205
211
|
if type == "dir": self._draw_recursive_summary(node[item], depth + 1, path_parts + [item], is_last_list + [is_last])
|
|
206
212
|
|
|
207
|
-
def render_text_file(self, fpath: Path
|
|
213
|
+
def render_text_file(self, fpath: Path):
|
|
208
214
|
rel = fpath.relative_to(self.project_root).as_posix()
|
|
209
|
-
display_name, bookmark_key = rel
|
|
215
|
+
display_name, bookmark_key = rel, _make_bookmark_key(rel)
|
|
210
216
|
try: content = fpath.read_text(encoding="utf-8", errors="replace")
|
|
211
217
|
except: return
|
|
212
218
|
self._check_space(25*mm)
|
|
@@ -224,10 +230,8 @@ class ModernAnnexPDF:
|
|
|
224
230
|
else:
|
|
225
231
|
lexer = get_lexer_for_filename(str(fpath), stripnl=False)
|
|
226
232
|
except ClassNotFound:
|
|
227
|
-
if not self.is_simulation:
|
|
228
|
-
print(f"ℹ️ Highlighting fallback: No lexer for '{fpath.name}'. Using plain text.")
|
|
233
|
+
if not self.is_simulation: print(f"ℹ️ Highlighting fallback: No lexer for '{fpath.name}'. Using plain text.")
|
|
229
234
|
lexer = TextLexer(stripnl=False)
|
|
230
|
-
|
|
231
235
|
self._render_tokens_to_lines(lexer.get_tokens(content or " "), display_name, bookmark_parts, total_parts)
|
|
232
236
|
self.y -= 4*mm
|
|
233
237
|
|
|
@@ -289,20 +293,18 @@ class ModernAnnexPDF:
|
|
|
289
293
|
if line_num is not None:
|
|
290
294
|
num_str = str(line_num)
|
|
291
295
|
num_char_w = pdfmetrics.stringWidth("0", self.mono_font, self.config.code_font_size)
|
|
292
|
-
# Increase box size from 0.8 to 0.9 for better legibility
|
|
293
296
|
box_hw = self.config.code_font_size * 0.9
|
|
294
297
|
start_x = self.config.margin_left + GUTTER_W - 2.5*mm - len(num_str) * num_char_w
|
|
295
298
|
for char in num_str:
|
|
296
|
-
# Improved vertical centering
|
|
297
299
|
self.c.drawImage(get_digit_sprites()[char], start_x + (num_char_w - box_hw) / 2.0, (self.y - self.config.code_font_size - 1.5) - box_hw * 0.1, width=box_hw, height=box_hw, mask="auto")
|
|
298
300
|
start_x += num_char_w
|
|
299
301
|
code_x = self.config.get_code_x() + 2*mm
|
|
300
302
|
for chunk, color in v_line:
|
|
301
303
|
code_x = draw_text_with_fallback(self.c, code_x, self.y - self.config.code_font_size - 1, chunk, self.mono_font, self.config.code_font_size, self.emoji_font, color, emoji_description=self.config.emoji_description)
|
|
302
304
|
|
|
303
|
-
def render_image_file(self, fpath: Path
|
|
305
|
+
def render_image_file(self, fpath: Path):
|
|
304
306
|
rel = fpath.relative_to(self.project_root).as_posix()
|
|
305
|
-
self._check_space(40*mm); self._register_bookmark(rel
|
|
307
|
+
self._check_space(40*mm); self._register_bookmark(rel, _make_bookmark_key(rel)); self._draw_file_header(rel)
|
|
306
308
|
self._draw_image(fpath)
|
|
307
309
|
|
|
308
310
|
def _draw_image(self, fpath):
|
|
@@ -322,7 +324,7 @@ class ModernAnnexPDF:
|
|
|
322
324
|
draw_y, block_bottom, block_w = self.y - draw_h - padding, self.y - draw_h - 2*padding, self.config.page_width - self.config.margin_left - self.config.margin_right
|
|
323
325
|
if not self.is_simulation:
|
|
324
326
|
self.c.setFillColor(colors.HexColor("#ffffff")); self.c.rect(self.config.margin_left, block_bottom, block_w, self.y - block_bottom, fill=1, stroke=0)
|
|
325
|
-
self.c.setStrokeColor(colors.HexColor("#e6e9ef")); self.c.setLineWidth(0
|
|
327
|
+
self.c.setStrokeColor(colors.HexColor("#e6e9ef")); self.c.setLineWidth(1.0); self.c.rect(self.config.margin_left, block_bottom, block_w, self.y - block_bottom, fill=0, stroke=1)
|
|
326
328
|
draw_x = self.config.margin_left + (block_w - draw_w) / 2
|
|
327
329
|
if img is None and png_data: img = PilImage.open(io.BytesIO(png_data))
|
|
328
330
|
if img: self.c.drawImage(ImageReader(img), draw_x, draw_y, draw_w, draw_h, preserveAspectRatio=True, mask="auto")
|
|
@@ -337,7 +339,6 @@ class ModernAnnexPDF:
|
|
|
337
339
|
self.draw_cover(); self.draw_summary_page(files)
|
|
338
340
|
for i, (fpath, ftype) in enumerate(files):
|
|
339
341
|
if not self.is_simulation: print(f"\r\033[K[{i+1}/{len(files)}] Processing: {fpath.name}", end="")
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
elif ftype == "image": self.render_image_file(fpath, label_suffix=suffix)
|
|
342
|
+
if ftype == "text": self.render_text_file(fpath)
|
|
343
|
+
elif ftype == "image": self.render_image_file(fpath)
|
|
343
344
|
if not self.is_simulation: print(); self.c.save()
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "codeannex"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.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" }
|
|
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
|
|
File without changes
|
|
File without changes
|