From 324f465f791d173ac152e611fb49256aafdccc3f Mon Sep 17 00:00:00 2001
From: Shrikanth Upadhayaya <shrik450@gmail.com>
Date: Thu, 27 Mar 2025 16:17:27 -0400
Subject: [PATCH] Output percentages in comments

---
 cspell.json    |  3 ++-
 lib.py         | 23 +++++++++++++++++------
 main.py        | 37 ++++++++++++++++++++++++++++++++-----
 models.py      |  2 --
 pyproject.toml |  2 +-
 5 files changed, 52 insertions(+), 15 deletions(-)

diff --git a/cspell.json b/cspell.json
index 20433c6..47c909d 100644
--- a/cspell.json
+++ b/cspell.json
@@ -9,6 +9,7 @@
     "words": [
         "netlist",
         "levelname",
-        "multipage"
+        "multipage",
+        "viewbox"
     ]
 }
\ No newline at end of file
diff --git a/lib.py b/lib.py
index 36fe2b1..8d05114 100644
--- a/lib.py
+++ b/lib.py
@@ -3,6 +3,7 @@
 
 import asyncio
 import logging
+from dataclasses import dataclass
 from typing import Any, Optional, Sequence
 from xml.etree import ElementTree as ET
 
@@ -122,7 +123,13 @@ def filter_schematic_json(schematic_json: dict[str, Any]) -> dict[str, Any]:
     return filtered_json
 
 
-def split_multipage_svg(svg_text: str) -> list[str]:
+@dataclass
+class SVGPage:
+    svg_text: str
+    view_bounds: list[float]
+
+
+def split_multipage_svg(svg_text: str) -> list[SVGPage]:
     """
     Split a multi-page SVG into individual SVG files, one for each page.
     Uses ElementTree for proper XML parsing.
@@ -150,7 +157,7 @@ def split_multipage_svg(svg_text: str) -> list[str]:
         if current.tag.endswith("}style") and next_elem.tag.endswith("}g"):
             page_pairs.append((current, next_elem))
 
-    output_files = []
+    output_files: list[SVGPage] = []
 
     for i, (style_elem, g_elem) in enumerate(page_pairs):
         new_svg = ET.Element("svg")
@@ -163,14 +170,14 @@ def split_multipage_svg(svg_text: str) -> list[str]:
 
         width = g_elem.get("data-width")
         height = g_elem.get("data-height")
-        view_box = g_elem.get("data-view-box")
+        view_box: str = g_elem.get("data-view-box")
 
         if width:
             new_svg.set("width", width)
         if height:
             new_svg.set("height", height)
-        if view_box:
-            new_svg.set("viewBox", view_box)
+
+        new_svg.set("viewBox", view_box)
 
         new_svg.append(style_elem)
         del g_elem.attrib["transform"]
@@ -178,7 +185,11 @@ def split_multipage_svg(svg_text: str) -> list[str]:
 
         svg_str = ET.tostring(new_svg, encoding="unicode")
 
-        output_files.append(svg_str)
+        # viewbox is like "-1.346,3.734 421.792,272.694"
+        view_bounds = view_box.replace(",", " ").split(" ")
+        page = SVGPage(svg_text=svg_str, view_bounds=[float(x) for x in view_bounds])
+
+        output_files.append(page)
 
     return output_files
 
diff --git a/main.py b/main.py
index 163b933..2410fe2 100644
--- a/main.py
+++ b/main.py
@@ -10,7 +10,7 @@ import final_agent
 import step_agent
 from lib import filter_schematic_page, render_svg, split_multipage_svg
 
-VERSION = (0, 4, 0)
+VERSION = (0, 4, 1)
 
 RETRY_DELAY = 60
 """
@@ -78,13 +78,13 @@ async def main():
         page_json_path = os.path.join(temp_dir.name, f"page_{i + 1}.json")
 
         with open(page_svg_path, "w") as f:
-            f.write(svg_page)
+            f.write(svg_page.svg_text)
             await render_svg(page_svg_path, page_png_path)
 
         with open(page_json_path, "w") as f:
             page_json = filter_schematic_page(json_page)
             json.dump(page_json, f)
-        page_paths.append((page_json_path, page_png_path))
+        page_paths.append((page_json_path, page_png_path, svg_page.view_bounds))
 
         logger.info(f"Wrote page {i + 1} to {page_json_path}")
 
@@ -100,7 +100,7 @@ async def main():
     current_memory = ""
     current_comments = []
 
-    for i, (page_json_path, page_png_path) in enumerate(page_paths):
+    for i, (page_json_path, page_png_path, page_bounds) in enumerate(page_paths):
         logger.info(f"Reviewing page {i + 1}/{len(page_paths)}")
 
         with open(page_json_path, "r") as f:
@@ -130,7 +130,34 @@ async def main():
             sys.exit(1)
 
         current_memory = result.memory
-        current_comments.extend(result.comments)
+        page_comments = result.comments
+        new_page_comments = []
+        for comment in page_comments:
+            if comment.viewbox is not None:
+                # If this comment has a viewbox, convert the coordinates from
+                # mm to the %age of the SVG viewport, as that is how Hub wants
+                # them.
+
+                # The viewbox is in the form [bottom left x, bottom left y, top right x, top right y]
+                viewbox = comment.viewbox
+                new_viewbox = [
+                    (viewbox[0] - page_bounds[0])
+                    / (page_bounds[2] - page_bounds[0])
+                    * 100,
+                    (viewbox[1] - page_bounds[1])
+                    / (page_bounds[3] - page_bounds[1])
+                    * 100,
+                    (viewbox[2] - page_bounds[0])
+                    / (page_bounds[2] - page_bounds[0])
+                    * 100,
+                    (viewbox[3] - page_bounds[1])
+                    / (page_bounds[3] - page_bounds[1])
+                    * 100,
+                ]
+                comment.viewbox = new_viewbox
+            new_page_comments.append(comment)
+
+        current_comments.extend(new_page_comments)
 
         logger.info(f"Completed review of page {i + 1}")
         logger.debug(f"Memory: {current_memory}")
diff --git a/models.py b/models.py
index b88bb1d..6cfd0fd 100644
--- a/models.py
+++ b/models.py
@@ -1,5 +1,3 @@
-# cspell:words viewbox
-
 import enum
 from typing import Optional
 
diff --git a/pyproject.toml b/pyproject.toml
index cddea21..8c48663 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -2,7 +2,7 @@
 
 [project]
 name = "llm-review"
-version = "0.4.0"
+version = "0.4.1"
 description = "Add your description here"
 readme = "README.md"
 requires-python = ">=3.11"