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.
Files changed (25) hide show
  1. {codeannex-0.3.1 → codeannex-0.3.3}/PKG-INFO +1 -1
  2. {codeannex-0.3.1 → codeannex-0.3.3}/codeannex/__main__.py +18 -4
  3. {codeannex-0.3.1 → codeannex-0.3.3}/codeannex/core/pdf_builder.py +33 -32
  4. {codeannex-0.3.1 → codeannex-0.3.3}/codeannex.egg-info/PKG-INFO +1 -1
  5. {codeannex-0.3.1 → codeannex-0.3.3}/pyproject.toml +1 -1
  6. {codeannex-0.3.1 → codeannex-0.3.3}/LICENSE +0 -0
  7. {codeannex-0.3.1 → codeannex-0.3.3}/README.md +0 -0
  8. {codeannex-0.3.1 → codeannex-0.3.3}/codeannex/__init__.py +0 -0
  9. {codeannex-0.3.1 → codeannex-0.3.3}/codeannex/core/__init__.py +0 -0
  10. {codeannex-0.3.1 → codeannex-0.3.3}/codeannex/core/config.py +0 -0
  11. {codeannex-0.3.1 → codeannex-0.3.3}/codeannex/interface/__init__.py +0 -0
  12. {codeannex-0.3.1 → codeannex-0.3.3}/codeannex/interface/cli.py +0 -0
  13. {codeannex-0.3.1 → codeannex-0.3.3}/codeannex/io/__init__.py +0 -0
  14. {codeannex-0.3.1 → codeannex-0.3.3}/codeannex/io/file_utils.py +0 -0
  15. {codeannex-0.3.1 → codeannex-0.3.3}/codeannex/io/git_utils.py +0 -0
  16. {codeannex-0.3.1 → codeannex-0.3.3}/codeannex/renderer/__init__.py +0 -0
  17. {codeannex-0.3.1 → codeannex-0.3.3}/codeannex/renderer/fonts.py +0 -0
  18. {codeannex-0.3.1 → codeannex-0.3.3}/codeannex/renderer/highlight.py +0 -0
  19. {codeannex-0.3.1 → codeannex-0.3.3}/codeannex/renderer/text_utils.py +0 -0
  20. {codeannex-0.3.1 → codeannex-0.3.3}/codeannex.egg-info/SOURCES.txt +0 -0
  21. {codeannex-0.3.1 → codeannex-0.3.3}/codeannex.egg-info/dependency_links.txt +0 -0
  22. {codeannex-0.3.1 → codeannex-0.3.3}/codeannex.egg-info/entry_points.txt +0 -0
  23. {codeannex-0.3.1 → codeannex-0.3.3}/codeannex.egg-info/requires.txt +0 -0
  24. {codeannex-0.3.1 → codeannex-0.3.3}/codeannex.egg-info/top_level.txt +0 -0
  25. {codeannex-0.3.1 → codeannex-0.3.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeannex
3
- Version: 0.3.1
3
+ Version: 0.3.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
@@ -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": included += [(fp, "image"), (fp, "text")]
117
- elif ext in IMAGE_EXTENSIONS: included.append((fp, "image"))
118
- elif ext in BINARY_EXTENSIONS: continue
119
- elif classify_file(fp) == "text": included.append((fp, "text"))
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
- self._dctf(mid_x, curr_y, f"{self.config.repo_label}{project_name}", self.config.normal_font, 14, text_color)
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 (Branch | Commit)
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
- tech_items.append(f"Commit: {self.config.commit_sha}")
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, label_suffix=""):
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 + label_suffix, _make_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, label_suffix=""):
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 + label_suffix, _make_bookmark_key(rel)); self._draw_file_header(rel + label_suffix)
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.5); self.c.rect(self.config.margin_left, block_bottom, block_w, self.y - block_bottom, fill=0, stroke=1)
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
- suffix = " (Code)" if ftype == "text" and fpath.suffix.lower() == ".svg" else ""
341
- if ftype == "text": self.render_text_file(fpath, label_suffix=suffix)
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()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeannex
3
- Version: 0.3.1
3
+ Version: 0.3.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.3.1"
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