feat: honor images-only/labels-only rack view mode in offline SVG export

The rack elevation page's "Bilder und Beschriftungen" dropdown only
toggles CSS visibility client-side after the browser already has the
SVG, so a freshly server-rendered export had no way to know which mode
was selected and always exported everything.

apply_rack_view_mode() replicates that filtering server-side by
removing the .label text elements (images-only) or the .device-image /
.device-image-label elements (labels-only) from the parsed SVG.
RackElevationOfflineSVGView now accepts a ?view= query param, and the
rack detail page panel syncs its download links to the page's own
"select.rack-view" dropdown so the exported file matches what's
currently shown.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-17 15:30:38 +02:00
co-authored by Claude Sonnet 5
parent 20ec9453c2
commit bfc4382403
6 changed files with 92 additions and 7 deletions
+30
View File
@@ -10,6 +10,36 @@ _ANCHOR_TAGS = (f"{{{SVG_NS}}}a", "a")
_IMAGE_TAGS = (f"{{{SVG_NS}}}image", "image")
_HREF_ATTRS = ("href", f"{{{XLINK_NS}}}href")
RACK_VIEW_MODES = {"images-and-labels", "images-only", "labels-only"}
_LABEL_CLASSES = {"label"}
_IMAGE_CLASSES = {"device-image", "device-image-label"}
def apply_rack_view_mode(svg_source, mode):
"""Mirror the rack elevation page's "Bilder"/"Beschriftungen" toggle server-side.
That toggle only hides elements client-side via CSS after the browser
already has the full SVG, so a freshly server-rendered export needs its
own equivalent to honor the mode the user had selected.
"""
if mode not in RACK_VIEW_MODES or mode == "images-and-labels":
return svg_source
root = ET.fromstring(svg_source)
if mode == "images-only":
_remove_elements_with_class(root, _LABEL_CLASSES)
elif mode == "labels-only":
_remove_elements_with_class(root, _IMAGE_CLASSES)
return ET.tostring(root, encoding="unicode")
def _remove_elements_with_class(parent, class_names):
for child in list(parent):
classes = set(child.attrib.get("class", "").split())
if classes & class_names:
parent.remove(child)
else:
_remove_elements_with_class(child, class_names)
def strip_svg_hyperlinks(svg_source, image_loader=None):
"""Return svg_source with every <a> wrapper unwrapped and dead links removed.