diff --git a/Init.py b/Init.py new file mode 100644 index 0000000..dd2f119 --- /dev/null +++ b/Init.py @@ -0,0 +1,13 @@ +"""Silo addon — console initialization. + +Adds the shared silo-client package to sys.path so that +``import silo_client`` works from silo_commands.py and other modules. +""" + +import os +import sys + +_mod_dir = os.path.dirname(os.path.abspath(__file__)) +_client_dir = os.path.join(_mod_dir, "silo-client") +if os.path.isdir(_client_dir) and _client_dir not in sys.path: + sys.path.insert(0, _client_dir) diff --git a/InitGui.py b/InitGui.py new file mode 100644 index 0000000..04371b3 --- /dev/null +++ b/InitGui.py @@ -0,0 +1,265 @@ +"""Kindred Silo addon — GUI initialization. + +Registers the SiloWorkbench, Silo file origin, overlay context, +dock panels (auth + activity), document observer, and start page override. +""" + +import os + +import FreeCAD +import FreeCADGui + +FreeCAD.Console.PrintMessage("Kindred Silo InitGui.py loading...\n") + + +# --------------------------------------------------------------------------- +# Workbench +# --------------------------------------------------------------------------- + + +class SiloWorkbench(FreeCADGui.Workbench): + """Kindred Silo workbench for item database integration.""" + + MenuText = "Kindred Silo" + ToolTip = "Item database and part management for Kindred Create" + Icon = "" + + def __init__(self): + icon_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "freecad", + "resources", + "icons", + "silo.svg", + ) + if os.path.exists(icon_path): + self.__class__.Icon = icon_path + + def Initialize(self): + """Called when workbench is first activated.""" + import silo_commands + + # Register Silo as a file origin in the unified origin system + try: + import silo_origin + + silo_origin.register_silo_origin() + except Exception as e: + FreeCAD.Console.PrintWarning(f"Could not register Silo origin: {e}\n") + + # Silo origin toolbar — shown as an overlay on any context when the + # active document is Silo-tracked. Registered as Unavailable so + # EditingContextResolver controls visibility via the overlay system. + self.silo_toolbar_commands = [ + "Silo_Commit", + "Silo_Pull", + "Silo_Push", + "Separator", + "Silo_Info", + "Silo_BOM", + "Silo_Jobs", + ] + self.appendToolbar("Silo Origin", self.silo_toolbar_commands, "Unavailable") + + # Silo menu provides admin/management commands. + self.menu_commands = [ + "Silo_Info", + "Silo_BOM", + "Silo_Jobs", + "Silo_TagProjects", + "Silo_SetStatus", + "Silo_Rollback", + "Silo_SaveAsTemplate", + "Separator", + "Silo_Settings", + "Silo_Auth", + "Silo_Runners", + "Silo_StartPanel", + "Silo_Diag", + ] + + self.appendMenu("Silo", self.menu_commands) + + def Activated(self): + """Called when workbench is activated.""" + FreeCAD.Console.PrintMessage("Kindred Silo workbench activated\n") + FreeCADGui.runCommand("Silo_StartPanel", 0) + + def Deactivated(self): + pass + + def GetClassName(self): + return "Gui::PythonWorkbench" + + +FreeCADGui.addWorkbench(SiloWorkbench()) +FreeCAD.Console.PrintMessage("Silo workbench registered\n") + + +# --------------------------------------------------------------------------- +# Silo overlay context — adds "Silo Origin" toolbar to any active context +# when the current document is Silo-tracked. +# --------------------------------------------------------------------------- + + +def _register_silo_overlay(): + """Register the Silo overlay after the Silo workbench has initialised.""" + + def _silo_overlay_match(): + """Return True if the active document is Silo-tracked.""" + try: + doc = FreeCAD.ActiveDocument + if not doc: + return False + from silo_origin import get_silo_origin + + origin = get_silo_origin() + return origin.ownsDocument(doc) + except Exception: + return False + + try: + from kindred_sdk import register_overlay + + register_overlay( + "silo", # overlay id + ["Silo Origin"], # toolbar names to append + _silo_overlay_match, # match function + ) + except Exception as e: + FreeCAD.Console.PrintWarning(f"Silo overlay registration failed: {e}\n") + + +# --------------------------------------------------------------------------- +# Document observer — builds the Silo metadata tree when .kc files open. +# --------------------------------------------------------------------------- + + +def _register_silo_document_observer(): + """Register the Silo document observer for .kc tree building.""" + try: + import silo_document + + silo_document.register() + except Exception as e: + FreeCAD.Console.PrintLog(f"Silo: document observer registration skipped: {e}\n") + + +# --------------------------------------------------------------------------- +# Dock panels — auth and activity widgets via SDK +# --------------------------------------------------------------------------- + + +def _setup_silo_auth_panel(): + """Dock the Silo authentication panel in the right-hand side panel.""" + try: + from kindred_sdk import register_dock_panel + + def _factory(): + import silo_commands + + auth = silo_commands.SiloAuthDockWidget() + # Prevent GC of the auth timer by stashing on the widget + auth.widget._auth = auth + return auth.widget + + register_dock_panel("SiloDatabaseAuth", "Database Auth", _factory) + except Exception as e: + FreeCAD.Console.PrintLog(f"Silo: auth panel skipped: {e}\n") + + +def _setup_silo_activity_panel(): + """Show a dock widget with recent Silo database activity.""" + try: + from kindred_sdk import register_dock_panel + + def _factory(): + from PySide import QtWidgets + + widget = QtWidgets.QWidget() + layout = QtWidgets.QVBoxLayout(widget) + activity_list = QtWidgets.QListWidget() + layout.addWidget(activity_list) + + try: + import silo_commands + + items = silo_commands._client.list_items() + if isinstance(items, list): + for item in items[:20]: + pn = item.get("part_number", "") + desc = item.get("description", "") + updated = item.get("updated_at", "") + if updated: + updated = updated[:10] + activity_list.addItem(f"{pn} - {desc} - {updated}") + if activity_list.count() == 0: + activity_list.addItem("(No items in database)") + except Exception: + activity_list.addItem("(Unable to connect to Silo database)") + + return widget + + register_dock_panel("SiloDatabaseActivity", "Database Activity", _factory) + except Exception as e: + FreeCAD.Console.PrintLog(f"Silo: activity panel skipped: {e}\n") + + +# --------------------------------------------------------------------------- +# First-start check +# --------------------------------------------------------------------------- + + +def _check_silo_first_start(): + """Show Silo settings dialog on first startup if not yet configured.""" + try: + param = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/KindredSilo") + if not param.GetBool("FirstStartChecked", False): + param.SetBool("FirstStartChecked", True) + if not param.GetString("ApiUrl", ""): + FreeCADGui.runCommand("Silo_Settings") + except Exception as e: + FreeCAD.Console.PrintLog(f"Silo: first-start check skipped: {e}\n") + + +# --------------------------------------------------------------------------- +# Start page override — must happen before the C++ StartLauncher fires +# at ~100ms after GUI init. +# --------------------------------------------------------------------------- + +try: + import silo_start + + silo_start.register() +except Exception as e: + FreeCAD.Console.PrintWarning(f"Silo Start page override failed: {e}\n") + + +# --------------------------------------------------------------------------- +# Handle kindred:// URLs passed as command-line arguments on cold start. +# --------------------------------------------------------------------------- + + +def _handle_startup_urls(): + """Process any kindred:// URLs passed as command-line arguments.""" + import sys + + from silo_commands import handle_kindred_url + + for arg in sys.argv[1:]: + if arg.startswith("kindred://"): + handle_kindred_url(arg) + + +# --------------------------------------------------------------------------- +# Deferred setup — staggered timers for non-blocking startup +# --------------------------------------------------------------------------- + +from PySide import QtCore as _QtCore + +_QtCore.QTimer.singleShot(500, _handle_startup_urls) +_QtCore.QTimer.singleShot(600, _register_silo_document_observer) +_QtCore.QTimer.singleShot(2000, _setup_silo_auth_panel) +_QtCore.QTimer.singleShot(2500, _register_silo_overlay) +_QtCore.QTimer.singleShot(3000, _check_silo_first_start) +_QtCore.QTimer.singleShot(4000, _setup_silo_activity_panel) diff --git a/package.xml b/package.xml new file mode 100644 index 0000000..b6f856b --- /dev/null +++ b/package.xml @@ -0,0 +1,25 @@ + + + silo + PLM workbench for Kindred Create + 0.1.0 + Kindred Systems + MIT + https://git.kindred-systems.com/kindred/silo-mod + + + + SiloWorkbench + freecad + + + + + 0.1.0 + 60 + true + + sdk + + +