Symbole-Tab in Project-Settings (Library-Item-Management)

Project-Settings hat jetzt 5 Tabs. Neuer 'Symbole'-Tab managt die
Dossier-Library: List/Detail wie Materialien, mit 2D + 3D Slot pro Item.

Backend (library.py):
- save_manifest, update_item, delete_item, add_item — full CRUD aufs
  library.json
- copy_to_assets: kopiert User-Dateien in library/assets/ mit
  Konflikt-Resolution (auto-suffix)

Backend (rhinopanel.py / ProjectSettingsBridge):
- _send_library: aktuelle Items + libraryRoot an Frontend
- _add_library_file: File-Picker (.3dm direkt; .dwg/.obj/etc. zeigt
  Hinweis fuer kuenftige Konvertierung), kopiert + appended ans Item
  (variant 2d/3d) oder erstellt neues Item
- _update_library_item: patch by id
- _delete_library_item: entfernt Eintrag aus Manifest
- LIBRARY_ITEMS + LIBRARY_ERROR Messages ans Frontend

Frontend:
- Neuer 'Symbole'-Tab mit List/Detail
- Liste: Name, Type-Icon, '2D'/'3D' Status-Badge
- Detail rechts: Name-Edit (live persist on blur), Type-Toggle
  (Symbol/Objekt), 2D/3D-File-Slots mit Datei-Picker, Tags-Editor
- 'Neues Objekt' Button im Listen-Footer

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-25 03:58:41 +02:00
parent de57c320c2
commit c993935b17
4 changed files with 436 additions and 0 deletions
+91
View File
@@ -148,6 +148,97 @@ def load_manifest():
return _empty_manifest()
def save_manifest(manifest):
"""Schreibt das Manifest zurueck zur library.json. Items werden
normalisiert. Returns True/False."""
ensure_library()
path = os.path.join(library_root(), _MANIFEST_FN)
try:
if not isinstance(manifest, dict): manifest = _empty_manifest()
manifest.setdefault("schemaVersion", _SCHEMA_VERSION)
manifest.setdefault("name", "Dossier-Library")
items = manifest.get("items") or []
manifest["items"] = [_normalize_item(x) for x in items
if _normalize_item(x) is not None]
with open(path, "w") as f:
json.dump(manifest, f, indent=2, ensure_ascii=False)
return True
except Exception as ex:
print("[LIBRARY] save_manifest:", ex)
return False
def update_item(item_id, patch):
"""Patcht ein Item im Manifest. Returns (ok, new_manifest)."""
m = load_manifest()
items = m.get("items", [])
found = False
for it in items:
if it.get("id") == item_id:
for k, v in (patch or {}).items():
it[k] = v
found = True
break
if not found: return False, m
ok = save_manifest(m)
return ok, load_manifest()
def delete_item(item_id):
"""Loescht ein Item aus dem Manifest. Asset-Files bleiben auf Disk
(User koennte sie noch wollen)."""
m = load_manifest()
items = m.get("items", [])
new_items = [it for it in items if it.get("id") != item_id]
if len(new_items) == len(items): return False, m
m["items"] = new_items
ok = save_manifest(m)
return ok, load_manifest()
def add_item(item):
"""Fuegt ein neues Item zum Manifest hinzu. Returns (ok, new_manifest)."""
norm = _normalize_item(item)
if norm is None: return False, load_manifest()
m = load_manifest()
items = m.get("items", [])
# Dedupe per id
items = [it for it in items if it.get("id") != norm["id"]]
items.append(norm)
m["items"] = items
ok = save_manifest(m)
return ok, load_manifest()
def copy_to_assets(src_path, target_name=None):
"""Kopiert eine Datei in library/assets/. target_name optional (sonst
Original-Name). Returns relativer Pfad ('assets/foo.3dm') oder None."""
if not src_path or not os.path.isfile(src_path):
return None
ensure_library()
assets_dir = os.path.join(library_root(), "assets")
if not os.path.isdir(assets_dir):
try: os.makedirs(assets_dir)
except Exception: pass
base = target_name or os.path.basename(src_path)
target = os.path.join(assets_dir, base)
# Konflikt-Resolution: bei doppeltem Namen Nummer dran
if os.path.isfile(target):
stem, ext = os.path.splitext(base)
n = 2
while os.path.isfile(os.path.join(assets_dir, "{}_{}{}".format(stem, n, ext))):
n += 1
target = os.path.join(assets_dir, "{}_{}{}".format(stem, n, ext))
try:
import shutil
shutil.copy2(src_path, target)
rel = os.path.relpath(target, library_root())
return rel
except Exception as ex:
print("[LIBRARY] copy_to_assets:", ex)
return None
def _empty_manifest():
return {"schemaVersion": _SCHEMA_VERSION,
"name": "Dossier-Library", "items": []}