From 04835c3629dfb28a3b50d8c79107041bba52a18d Mon Sep 17 00:00:00 2001 From: forbes Date: Sat, 14 Feb 2026 10:57:46 -0600 Subject: [PATCH] =?UTF-8?q?feat:=20.kc=20Layer=201=20=E2=80=94=20manifest?= =?UTF-8?q?=20auto-creation=20and=20platform=20file=20associations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit kc_format.py: - Auto-create silo/manifest.json with default fields (UUID, timestamps, username) when saving a .kc file that lacks one - Update modified_at timestamp on each save - KC_VERSION = 1.0 Platform integration: - kindred-create.desktop: add application/x-kindred-create MIME type - kindred-create.xml: register .kc glob patterns with dedicated MIME type (application/x-kindred-create), separate from .fcstd type --- resources/kindred-create.desktop | 2 +- resources/kindred-create.xml | 11 +++++- src/Mod/Create/kc_format.py | 63 +++++++++++++++++++++++++++----- 3 files changed, 63 insertions(+), 13 deletions(-) diff --git a/resources/kindred-create.desktop b/resources/kindred-create.desktop index 6432edaf03..85c0038732 100644 --- a/resources/kindred-create.desktop +++ b/resources/kindred-create.desktop @@ -7,7 +7,7 @@ Icon=kindred-create Terminal=false Type=Application Categories=Graphics;Science;Engineering; -MimeType=application/x-extension-fcstd;x-scheme-handler/kindred; +MimeType=application/x-extension-fcstd;application/x-kindred-create;x-scheme-handler/kindred; Keywords=CAD;3D;modeling;engineering;design;parametric; StartupNotify=true StartupWMClass=KindredCreate diff --git a/resources/kindred-create.xml b/resources/kindred-create.xml index 8f310e4097..652dfca9e6 100644 --- a/resources/kindred-create.xml +++ b/resources/kindred-create.xml @@ -1,11 +1,18 @@ - Kindred Create Document - Kindred Create Document + FreeCAD Document + FreeCAD Document + + Kindred Create Document + Kindred Create Document + + + + diff --git a/src/Mod/Create/kc_format.py b/src/Mod/Create/kc_format.py index 974c4a7076..71e5ae68d6 100644 --- a/src/Mod/Create/kc_format.py +++ b/src/Mod/Create/kc_format.py @@ -1,18 +1,40 @@ """ -kc_format.py — .kc file format round-trip preservation. +kc_format.py — .kc file format support. -Caches silo/ ZIP entries before FreeCAD's C++ save rewrites the ZIP -from scratch, then re-injects them after save completes. +Handles two responsibilities: +1. Round-trip preservation: caches silo/ ZIP entries before FreeCAD's C++ + save rewrites the ZIP from scratch, then re-injects them after save. +2. Manifest auto-creation: ensures every .kc file has silo/manifest.json. """ +import json import os +import uuid import zipfile +from datetime import datetime, timezone import FreeCAD # Cache: filepath -> {entry_name: bytes} _silo_cache = {} +KC_VERSION = "1.0" + + +def _default_manifest(): + """Generate a default silo/manifest.json for new .kc files.""" + now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") + username = os.environ.get("USER", os.environ.get("USERNAME", "unknown")) + return { + "kc_version": KC_VERSION, + "silo_instance": None, + "part_uuid": str(uuid.uuid4()), + "revision_hash": None, + "created_at": now, + "modified_at": now, + "created_by": username, + } + class _KcFormatObserver: """Document observer that preserves silo/ entries across saves.""" @@ -35,22 +57,43 @@ class _KcFormatObserver: pass def slotFinishSaveDocument(self, doc, filename): - """After save: re-inject cached silo/ entries into the .kc ZIP.""" + """After save: re-inject cached silo/ entries and ensure manifest.""" if not filename.lower().endswith(".kc"): _silo_cache.pop(filename, None) return entries = _silo_cache.pop(filename, None) - if not entries: - return try: with zipfile.ZipFile(filename, "a") as zf: existing = set(zf.namelist()) - for name, data in entries.items(): - if name not in existing: - zf.writestr(name, data) + # Re-inject cached silo/ entries + if entries: + for name, data in entries.items(): + if name not in existing: + zf.writestr(name, data) + existing.add(name) + # Ensure silo/manifest.json exists + if "silo/manifest.json" not in existing: + manifest = _default_manifest() + zf.writestr( + "silo/manifest.json", + json.dumps(manifest, indent=2) + "\n", + ) + else: + # Update modified_at timestamp + raw = zf.read("silo/manifest.json") + manifest = json.loads(raw) + now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") + if manifest.get("modified_at") != now: + manifest["modified_at"] = now + # ZipFile append mode can't overwrite; write new entry + # (last duplicate wins in most ZIP readers) + zf.writestr( + "silo/manifest.json", + json.dumps(manifest, indent=2) + "\n", + ) except Exception as e: FreeCAD.Console.PrintWarning( - f"kc_format: failed to preserve silo/ entries: {e}\n" + f"kc_format: failed to update .kc silo/ entries: {e}\n" )