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:
@@ -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": []}
|
||||
|
||||
Reference in New Issue
Block a user