Library-Thumbnails: Auto-Capture + Base64-Preview in der UI

Beim Hinzufuegen oder Importieren eines Library-Items wird automatisch
ein PNG-Thumbnail vom Item generiert (Top-View, 128x128) und in
library/previews/<id>.png abgelegt. Frontend rendert die Previews als
Base64-Data-URIs (sicher gegen WebKit-file://-Restriktionen).

library.py:
- _previews_dir(): legt previews/-Folder an
- _preview_rel_for(asset_rel): predictable PNG-Pfad pro Item
- _capture_thumbnail_of_objects(): hided andere Objekte temporaer,
  switcht auf Top-Parallel, ZoomBoundingBox, CaptureToBitmap → PNG,
  restored Viewport + Hidden-State
- read_preview_data_uri(): liest PNG + encoded als data:image/png;base64
- Hook in convert_to_3dm_via_import (vor Cleanup) + save_selection_to_asset

rhinopanel.py:
- _enrich_library_items_with_previews(): haengt previewDataUri an
  jedes Item das ein preview-Feld hat
- Initial-Params + _send_library + ElementeBridge._cmd_list_library
  liefern angereicherte Items
- _add_library_file + _save_selection_as_library setzen preview-Pfad
  im Item wenn Thumbnail-Datei existiert

Frontend:
- SymbolPicker.ItemPreview: rendert <div backgroundImage> mit Base64-URI
  wenn vorhanden, sonst Icon-Fallback
- ProjectSettingsDialog Symbole-Tab: List-Row + Detail-Identity zeigen
  Thumbnail (32px in Liste, 56px im Detail), Icon nur als Fallback

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-25 19:07:33 +02:00
parent 827bd8d4d7
commit e1b63aa4e6
5 changed files with 236 additions and 23 deletions
+38 -8
View File
@@ -147,6 +147,24 @@ def _list_hatch_patterns_full(doc):
return out
def _enrich_library_items_with_previews(items):
"""Liefert Liste mit zusaetzlichem 'previewDataUri'-Feld pro Item
(base64-PNG falls preview-Datei existiert). Items werden NICHT
persistiert — nur fuer den Transport ans Frontend angereichert."""
import library
out = []
for it in (items or []):
if not isinstance(it, dict):
out.append(it); continue
enriched = dict(it)
prev = it.get("preview")
if prev:
uri = library.read_preview_data_uri(prev)
if uri: enriched["previewDataUri"] = uri
out.append(enriched)
return out
def _list_linetypes_full(doc):
"""Vollstaendiges Linetype-Listing fuer die Verwaltungs-UI.
Mac Rhino 8 GetSegment(i) returnt (length: float, isLine: bool):
@@ -893,7 +911,8 @@ class EbenenBridge(panel_base.BaseBridge):
try:
import library
lib_manifest = library.load_manifest()
lib_items = lib_manifest.get("items", [])
lib_items = _enrich_library_items_with_previews(
lib_manifest.get("items", []))
lib_root = library.library_root()
except Exception:
lib_items = []; lib_root = ""
@@ -1183,12 +1202,15 @@ class EbenenBridge(panel_base.BaseBridge):
# ---- Library-Item CRUD ----
def _send_library(self):
"""Sendet aktuelle Library-Items ans Frontend
(LIBRARY_ITEMS-Message)."""
(LIBRARY_ITEMS-Message). Items kommen mit
previewDataUri (base64 PNG) wenn Vorschau vorhanden."""
try:
import library
m = library.load_manifest()
enriched = _enrich_library_items_with_previews(
m.get("items", []))
self.send("LIBRARY_ITEMS", {
"items": m.get("items", []),
"items": enriched,
"libraryRoot": library.library_root(),
})
except Exception as ex:
@@ -1247,18 +1269,21 @@ class EbenenBridge(panel_base.BaseBridge):
self.send("LIBRARY_ERROR", {
"msg": "Konnte Datei nicht importieren — siehe Log."})
return
# Preview-Pfad falls Thumbnail erzeugt wurde
preview_rel = library._preview_rel_for(rel)
preview_abs = os.path.join(library.library_root(), preview_rel)
has_preview = os.path.isfile(preview_abs)
if target_id:
# Bestehendes Item updaten
m = library.load_manifest()
for it in m.get("items", []):
if it.get("id") == target_id:
key = "files" + variant
it[key] = [rel]
if not it.get("type"): it["type"] = item_type
if has_preview: it["preview"] = preview_rel
break
library.save_manifest(m)
else:
# Neues Item — stem aus dem Pfad oben bereits berechnet
import uuid as _uuid
new_id = "obj-" + _uuid.uuid4().hex[:10]
item = {
@@ -1269,6 +1294,7 @@ class EbenenBridge(panel_base.BaseBridge):
"tags": [],
"files" + variant: [rel],
}
if has_preview: item["preview"] = preview_rel
library.add_item(item)
self._send_library()
except Exception as ex:
@@ -1343,10 +1369,13 @@ class EbenenBridge(panel_base.BaseBridge):
self.send("LIBRARY_ERROR", {
"msg": "Konnte Selection nicht speichern."})
return
preview_rel = library._preview_rel_for(rel)
preview_abs = os.path.join(library.library_root(), preview_rel)
has_preview = os.path.isfile(preview_abs)
if target_id:
library.update_item(target_id, {
("files" + variant): [rel],
})
patch = {("files" + variant): [rel]}
if has_preview: patch["preview"] = preview_rel
library.update_item(target_id, patch)
else:
import uuid as _uuid
new_id = "obj-" + _uuid.uuid4().hex[:10]
@@ -1358,6 +1387,7 @@ class EbenenBridge(panel_base.BaseBridge):
"tags": [],
("files" + variant): [rel],
}
if has_preview: item["preview"] = preview_rel
library.add_item(item)
self._send_library()
b = _ProjectSettingsBridge()