Phase 1 implementation:
- New bom_sync.py: extract cross-document App::Link components from
Assembly, resolve SiloItemId UUIDs to part numbers, diff against
server BOM, apply adds/qty updates via individual CRUD calls.
- Hook _push_bom_after_upload into Silo_Save and Silo_Commit
(same non-blocking pattern as DAG sync).
- Hook _update_manifest_revision to write revision_hash into .kc
manifest after successful upload (#277).
- Add bom_merged SSE signal + dispatch + Activity pane handler.
- Add merge_bom_json to SiloClient (forward-looking for Phase 2).
Merge rules: auto-add, auto-update qty, NEVER auto-delete removed
entries (warn only).
Refs: #276, #277
The silo server now uses filesystem storage instead of MinIO.
- Remove all MinIO references from docstrings, tooltips, and UI text
- Remove obsolete 'degraded' server mode (no separate storage service)
- Update Silo_Info display: 'File in MinIO' → 'File on Server'
- Update SiloOrigin class docstring
Replace all hardcoded 'kindred-rd' schema references with the new
configurable get_schema_name() setting. Priority: FreeCAD preference
SchemaName > SILO_SCHEMA env var > default 'kindred-rd'.
- Add get_schema_name() to FreeCADSiloSettings and _get_schema_name() helper
- Add Schema Name field to Settings dialog
- Replace raw urllib calls in schema_form.py with SiloClient methods
(get_property_schema, generate_part_number)
- Inline parse_part_number/sanitize_filename (removed from silo-client)
- Simplify category folder naming to use category code directly
- Update silo-client submodule to origin/main + configurable schema branch
Closes#28
Replace layout.addStretch() with QSizePolicy.Maximum so the
Database Auth dock panel only takes the height its content needs,
leaving more vertical space for the Database Activity panel below.
Closes#190
Resolve conflicts in silo_commands.py:
- Keep event-based activity feed (_append_activity_event, _rebuild_activity_feed)
- Adapt DAG/job SSE handlers to use _append_activity_event
- Keep _relative_time formatting for activity entries
- Include DNS diagnostic IP display from feature branch
Connects dag_updated, dag_validated, and job lifecycle signals
from SiloEventListener to the Database Activity dock widget.
- dag.updated: inserts DAG sync status (node/edge count)
- dag.validated: inserts pass/fail badge with failed count
- job.created: inserts queued job entry
- job.completed: refreshes the full activity list
- job.failed: inserts error entry
Live entries are inserted at the top of the activity list,
styled in Catppuccin Blue, capped at 50 entries.
Closeskindred/create#219
Adds _push_dag_after_upload() helper that extracts the feature DAG
and pushes it to Silo after a successful file upload.
Hooked into both Silo_Save and Silo_Commit commands. DAG sync
failures are logged as warnings and never block the save/commit.
Closeskindred/create#216
Implements extract_dag(), classify_type(), and compute_properties_hash()
for extracting feature trees from FreeCAD documents.
- classify_type: maps ~50 FreeCAD TypeIds to 8 DAG node types
- compute_properties_hash: SHA-256 of per-feature parametric inputs
- extract_dag: two-pass walk of doc.Objects producing nodes + edges
No GUI dependencies -- works in both desktop and headless mode.
Closeskindred/create#214
The CA certificate file browser hardcoded /etc/ssl/certs as fallback,
which confused users when the dialog opened to a system directory.
Default to the user's home directory instead.
- get_cad_file_path() now generates .kc paths instead of .FCStd
- find_file_by_part_number() searches .kc first, falls back to .FCStd
- search_local_files() lists both .kc and .FCStd files
Extract search-and-open UI into OpenItemWidget (open_search.py), a
plain QWidget with item_selected/cancelled signals. Silo_Open now
adds this widget as an MDI subwindow instead of running a blocking
QDialog, matching the Silo_New tab pattern.
Improvements over the old dialog:
- Non-blocking: multiple search tabs can be open simultaneously
- 500 ms debounce on search input reduces API load
- Filter checkbox changes trigger immediate re-search
Extract SchemaFormWidget from SchemaFormDialog so the creation form
can be embedded as a plain QWidget in an MDI subwindow tab. Each
Ctrl+N invocation opens a new tab alongside document tabs. On
successful creation the pre-document tab closes and the real document
opens in its place.
- SchemaFormWidget emits item_created/cancelled signals
- SchemaFormDialog preserved as thin modal wrapper for backward compat
- Inline error display replaces modal QMessageBox
- Live tab title updates from part number preview
Add 'Silo Origin' toolbar (Commit/Pull/Push/Info/BOM) registered with
Unavailable visibility. Register a Silo overlay via
FreeCADGui.registerEditingOverlay() that appends this toolbar to any
active editing context when the current document is Silo-tracked
(ownsDocument() returns True).
Consolidate PySide.QtCore imports.
When pulling an assembly from Silo, the linked component files were not
downloaded, causing FreeCAD to report 'Link not restored' errors for
every external reference.
Add _pull_dependencies() that queries the BOM API to discover child
part numbers, then downloads the latest file revision for each child
that doesn't already exist locally. Recurses into sub-assemblies.
Silo_Pull.Activated() now calls _pull_dependencies() after downloading
the assembly file and before opening it, so all PropertyXLink paths
resolve correctly.
Replace the 3-dialog chain (category → description → projects) with a
single SchemaFormDialog that fetches schema data from the Silo REST API
and builds the UI dynamically at runtime.
New dialog features:
- Two-stage category picker (domain combo → subcategory combo)
- Dynamic property fields grouped by domain and common defaults
- Collapsible form sections (Identity, Sourcing, Details, Properties)
- Live part number preview via POST /api/generate-part-number
- Item type selection (part, assembly, consumable, tool)
- Sourcing fields (type, cost, URL)
- Project tagging via multi-select list
- Widget factory: string→QLineEdit, number→QDoubleSpinBox+unit,
boolean→QCheckBox
The form mirrors the React CreateItemPane.tsx layout and uses the same
API endpoints:
- GET /api/schemas/kindred-rd (category enum values)
- GET /api/schemas/kindred-rd/properties?category={code}
- GET /api/projects
- POST /api/items (via _client.create_item)
PySide6 requires the proper enum type QtCore.Qt.WindowModal, not the
raw integer 2. The integer form was accepted by PySide2/Qt5 but raises
TypeError in PySide6.
- silo_origin.py: use Gui.Document.Modified instead of App.Document.Modified
(re-applies fix from #13 that was lost in rebase)
- silo_origin.py: add traceback logging to saveDocument error handler
- silo_commands.py: reset SSE retry counter after connections lasting >30s
so transient disconnects don't permanently kill the listener
App.Document has no IsModified() method, causing Silo_Pull to crash with
AttributeError. The correct API is to get the Gui document and check its
Modified property, consistent with the pattern used elsewhere in this file
(lines 891, 913).
Replace QWebEngineView-based start page with a rich native Qt panel that
fetches items directly from the Silo REST API. QWebEngineView is not
available on conda-forge for Qt6.
Start panel features:
- Database Items list with search (from SiloClient.list_items)
- Recent Files list from FreeCAD preferences
- Real-time Activity Feed via SSE (SiloEventListener)
- Context menu: Open in Create, Open in Browser, Copy Part Number
- Open in Browser button (QDesktopServices)
- Catppuccin Mocha dark theme styling
URL scheme support:
- handle_kindred_url() function for kindred://item/{part_number} URLs
- Startup hook in InitGui.py for cold-start URL arguments
Closes#167
Replaces the default FreeCAD Start page with a dual-mode view:
- Online: QWebEngineView loading the Silo web app
- Offline: native Qt fallback with recent files and connectivity status
The command override is registered at InitGui.py load time, before
the C++ StartLauncher fires.
- Fix SSE listener URL: _listen() used '/api/events' but _get_api_url()
already returns a URL ending in '/api', producing '/api/api/events'.
Changed to '/events' to match _test_sse().
- Replace all Command.get().Activated() calls in silo_origin.py with
FreeCADGui.runCommand(). The C++ Gui::Command wrapper returned by
Command.get() does not expose .Activated() to Python.
Replace the dead-end warning in Silo_BOM with a question dialog that
offers to register the document via Silo_New. If the user accepts and
registration succeeds, the BOM dialog opens seamlessly.
Create silo-tag.svg, silo-rollback.svg, and silo-status.svg in the
Catppuccin Mocha style matching existing silo icons. These were
referenced by _icon() but did not exist, causing the commands to
render without toolbar icons.
Enhance the Database Activity panel in SiloAuthDockWidget:
- Show latest revision number and comment inline per activity entry
- Truncate long descriptions with ellipsis, full text in tooltip
- Mark locally checked-out items with green color and "local" badge
- Double-click opens local file or triggers checkout from server
- Right-click context menu: Open in Create, Open in Browser,
Copy Part Number, View Revisions
Closes#9
Replace the infinite fixed-delay retry loop with exponential backoff
(1s, 2s, 4s, ... capped at 60s) and a max retry limit of 10.
Changes to SiloEventListener:
- Expand connection_status signal to (status, retry_count, error_message)
- Add exponential backoff: min(BASE_DELAY * 2^retries, MAX_DELAY)
- Add terminal "gave_up" state after MAX_RETRIES exhausted
- Capture and forward error messages from failed connection attempts
Changes to SiloAuthDockWidget._on_sse_status:
- Show retry count: "Reconnecting (3/10)..."
- Show "Disconnected" (red) on gave_up state
- Log each attempt to FreeCAD console (PrintWarning/PrintError)
- Set tooltip with last error message on the status label
Closes#2
Add a diagnostics command that sequentially tests:
- DNS resolution of the server hostname
- Health endpoint (/health)
- Readiness endpoint (/ready)
- Auth endpoint (/auth/me) with token
- SSE event stream (/events)
Results are printed to the FreeCAD console with PASS/FAIL status.
Closes#3
Add a SiloStartPanel dock widget that shows connection status, local
checkouts, recent silo activity, and local recent files. Automatically
shown when the Silo workbench is activated.
Sections:
- Connection status badge (green/gray dot with hostname)
- My Checkouts: scans local projects dir for .FCStd files, shows part
number and description, double-click to open
- Recent Silo Activity: fetches from server, badges items present
locally, double-click to open or checkout
- Local Recent Files: reads FreeCAD MRU list, double-click to open
Auto-refreshes every 60 seconds. Panel is reused if already open.
Closes#5
Add server mode awareness to the FreeCAD client. The mode (normal,
read-only, degraded, offline) is fetched from the /ready endpoint on
each status refresh and updated in real-time via SSE server.state events.
Changes:
- Add _server_mode global and _fetch_server_mode() helper that queries
/ready and maps response status to mode string
- Add server_mode_changed signal to SiloEventListener, handle
server.state SSE events in _dispatch()
- Add mode banner to SiloAuthDockWidget: colored bar shown when server
is not in normal mode (yellow=read-only, orange=degraded, red=offline)
- Update _refresh_status() to fetch mode and update banner
- Disable write commands (New, Save, Commit, Push) when server is not
in normal mode via IsActive() checks
Closes#4
The freecad/ directory is not a Python package (no __init__.py) — it is
added directly to sys.path by FreeCAD. The relative import
'from .silo_commands import ...' fails when silo_origin is imported as a
top-level module, causing Silo origin registration to silently fail.
Change to absolute import 'from silo_commands import ...' to match
the import style used everywhere else in the directory.
Remove the separate Silo workbench toolbar and Silo_ToggleMode command.
File operations (New/Open/Save) are now handled by the standard File
toolbar via the origin system. The Silo menu retains admin commands
(Settings, Auth, Info, BOM, TagProjects, SetStatus, Rollback).
Closes#65
- Add missing _get_auth_token() function that caused NameError in Settings
- Replace _client.auth_username/role/source() calls with _get_auth_*()
helpers (methods don't exist on SiloClient, crashed auth dock widget)
- Fix connect() in silo_origin.py to show login dialog instead of just
revealing the dock panel (was using non-existent Command.get() API)
- Separate toolbar (file ops) from menu (admin/management commands)
- Remove DEBUG logging from Silo_Save command
- Fix long line formatting in silo_origin.py
FreeCAD workbench for Silo PLM integration. Uses shared silo-client
package (submodule) for API communication.
Changes from monorepo version:
- SiloClient class removed, imported from silo_client package
- FreeCADSiloSettings adapter wraps FreeCAD.ParamGet() preferences
- Init.py adds silo-client to sys.path at startup
- All command classes and UI unchanged