Compare commits

...

28 Commits

Author SHA1 Message Date
9cf5b162bb fix(ci): use Docker network hostname for docs runner checkout
The act_runner and gitea containers share the git_default Docker
network. Use http://gitea:3000 (container DNS name) instead of
localhost, which resolves to the runner container itself.
2026-02-09 08:57:58 -06:00
23fd2593af Merge pull request 'fix(ci): use localhost:3000 for docs runner checkout' (#111) from fix/docs-checkout-localhost into main
Some checks failed
Deploy Docs / build-and-deploy (push) Failing after 0s
Build and Test / build (push) Failing after 1m47s
Reviewed-on: #111
2026-02-09 14:47:00 +00:00
c25f1b62b8 fix(ci): use localhost:3000 for docs runner checkout
Some checks failed
Build and Test / build (pull_request) Failing after 1m38s
The docs runner is on the same host as Gitea, so clone via
http://localhost:3000 instead of the public HTTPS URL which
fails to connect (port 443 unreachable from the runner).
2026-02-09 08:46:42 -06:00
10ae616f08 Merge pull request 'fix(ci): add docs workflow file to paths trigger' (#110) from fix/docs-workflow-paths into main
Some checks failed
Deploy Docs / build-and-deploy (push) Failing after 0s
Build and Test / build (push) Has been cancelled
Reviewed-on: #110
2026-02-09 14:43:59 +00:00
d6ad0fb43d fix(ci): add docs workflow file to paths trigger
Some checks failed
Build and Test / build (pull_request) Failing after 1m38s
Changes to .gitea/workflows/docs.yml itself should also trigger
a docs rebuild so workflow fixes are immediately testable.
2026-02-09 08:43:32 -06:00
bbee39431d Merge pull request 'fix(ci): replace actions/checkout with git clone in docs workflow' (#108) from fix/docs-workflow-no-node into main
Some checks failed
Build and Test / build (push) Failing after 2m50s
Reviewed-on: #108
2026-02-09 14:23:39 +00:00
3c0b14b862 Merge branch 'main' into fix/docs-workflow-no-node
Some checks failed
Build and Test / build (pull_request) Has been cancelled
2026-02-09 14:23:27 +00:00
1f793a6b71 Merge pull request 'docs: populate initial content from project context' (#107) from docs/populate-content into main
Some checks failed
Build and Test / build (push) Has been cancelled
Deploy Docs / build-and-deploy (push) Failing after 3s
Reviewed-on: #107
2026-02-09 14:22:44 +00:00
65a2bb98db fix(ci): replace actions/checkout with git clone in docs workflow
Some checks failed
Build and Test / build (pull_request) Has been cancelled
The docs runner is a bare host without Node.js, so actions/checkout@v4
fails with 'Cannot find: node in PATH'. Replace with a plain git
fetch/checkout that works without Node.
2026-02-09 08:20:38 -06:00
c47eb26312 docs: populate initial content from project context (#104)
Some checks failed
Build and Test / build (pull_request) Has been cancelled
Expand documentation pages with real content sourced from the codebase:

- guide/ztools.md: full 16 datum mode reference, all 24+ command classes,
  PartDesign injection table, internal properties, known gaps
- guide/silo.md: all 14 commands with shortcuts, origin capability table,
  architecture diagram, migration inventory, deployment configs, schema docs
- reference/configuration.md: full Silo parameter table, keyboard shortcuts,
  update checker params, theme settings, build config, server config
- introduction.md: added origin system, FreeCAD relationship section,
  expanded theme description
2026-02-09 08:18:21 -06:00
769072fb9d Merge pull request 'docs: initialize mdBook structure with stub pages and deployment workflow' (#105) from docs/mdbook-setup into main
Some checks failed
Deploy Docs / build-and-deploy (push) Failing after 11s
Build and Test / build (push) Has been cancelled
Reviewed-on: #105
2026-02-09 14:02:57 +00:00
64bde8d97a docs: initialize mdBook structure with stub pages and deployment workflow
Some checks failed
Build and Test / build (pull_request) Has been cancelled
- Add docs/book.toml with coal theme, repo link, and custom CSS
- Add docs/src/SUMMARY.md with navigation structure
- Create 16 content pages across guide/, architecture/, development/, reference/
- Add docs/theme/kindred.css with minimal sidebar overrides
- Add .gitea/workflows/docs.yml for auto-deploy on push to main
- Add docs/book/ to .gitignore

Pages are populated with real content from the codebase where possible.
Remaining pages have TODO markers for future content.
2026-02-09 07:59:01 -06:00
a9de667914 Merge pull request 'fix: update silo submodule — SSE URL and origin command fixes (#84)' (#85) from fix/silo-sse-and-origin into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #85
2026-02-09 04:55:15 +00:00
forbes
353efe51e2 fix: update silo submodule — SSE URL and origin command fixes (#84)
Some checks failed
Build and Test / build (pull_request) Has been cancelled
- Fix SSE listener double /api/ URL causing reconnection loop
- Replace Command.get().Activated() with runCommand() in SiloOrigin

Closes #84
2026-02-08 22:54:49 -06:00
6d828a6459 Merge pull request 'fix(assembly): extend findPlacement() datum and origin handling (#55)' (#83) from fix/assembly-datum-placement into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #83
2026-02-09 02:11:50 +00:00
forbes
b7374d7b1f fix(assembly): extend findPlacement() datum and origin handling (#55)
Some checks failed
Build and Test / build (pull_request) Has been cancelled
Add support for missing datum and origin object types in the Assembly
solver's findPlacement() function:

- App::Plane: handle XY/XZ/YZ origin planes with correct rotations
- App::Point: handle origin point (identity placement)
- PartDesign::Line: extract edge midpoint and direction from shape
- PartDesign::Point: extract vertex position from shape geometry
  instead of returning raw obj.Placement
- Add obj.Shape.isNull() validation to all PartDesign datum branches
- PartDesign::Plane falls back to obj.Placement on invalid shape
  instead of falling through to App.Placement()

Closes #55
2026-02-08 19:53:49 -06:00
7ef7ce1dfc Merge pull request 'fix(theme): QGroupBox indicator, hyperlink color, and report defaults (#41, #43, #44)' (#82) from fix/qss-theme-polish into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #82
2026-02-09 01:39:48 +00:00
7411508b36 Merge branch 'main' into fix/qss-theme-polish
Some checks failed
Build and Test / build (pull_request) Has been cancelled
2026-02-09 01:39:39 +00:00
766f9b491c Merge pull request 'fix: update ztools submodule — parametric tangent-to-cylinder datums (#58)' (#81) from fix/tangent-cylinder-attachment into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #81
2026-02-09 01:39:23 +00:00
ad34cffcd6 Merge branch 'main' into fix/tangent-cylinder-attachment
Some checks failed
Build and Test / build (pull_request) Has been cancelled
2026-02-09 01:39:13 +00:00
cecd4988a8 Merge pull request 'fix(UX): update silo submodule — BOM registration path (#56)' (#80) from fix/bom-registration-path into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #80
2026-02-09 01:39:03 +00:00
forbes
977fa3c934 fix(theme): QGroupBox indicator, hyperlink color, and report defaults
Some checks failed
Build and Test / build (pull_request) Has been cancelled
- Add QGroupBox::indicator styles matching QCheckBox::indicator for
  consistent checkbox appearance in checkable group boxes (#44)
- Add QLabel[haslink] color rule using Catppuccin lavender (#b4befe)
  so hyperlinks render correctly in the dark theme (#43)
- Add checkLogging and checkShowReportViewOnError defaults to the
  KindredCreate preference pack (#41)

Closes #41, closes #43, closes #44
2026-02-08 19:38:22 -06:00
forbes
f0f4c068d2 fix: update ztools submodule — parametric tangent-to-cylinder datums (#58)
Some checks failed
Build and Test / build (pull_request) Has been cancelled
Uses TangentPlane MapMode with a derived vertex reference so
tangent-to-cylinder datums auto-update when cylinder geometry changes.

Closes #58
2026-02-08 19:38:18 -06:00
forbes
5f43b534d9 fix(UX): update silo submodule — BOM registration path (#56)
Some checks failed
Build and Test / build (pull_request) Has been cancelled
Replaces dead-end warning in Silo_BOM with a question dialog that
offers to register the document via Silo_New before continuing.

Closes #56
2026-02-08 19:38:18 -06:00
599d4b23a8 Merge pull request 'fix: update ztools submodule — angled datum live editing (#66)' (#79) from fix/angled-datum-edit into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #79
2026-02-09 01:37:18 +00:00
d136ee3ebe Merge branch 'main' into fix/angled-datum-edit
Some checks failed
Build and Test / build (pull_request) Has been cancelled
2026-02-09 01:37:07 +00:00
561d634934 Merge pull request 'art: update silo submodule — add missing command icons (#60)' (#78) from fix/missing-silo-icons into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #78
2026-02-09 01:36:57 +00:00
forbes
eb57f80e72 fix: update ztools submodule — angled datum live editing (#66)
Some checks failed
Build and Test / build (pull_request) Has been cancelled
Updates AttachmentOffset.Rotation during angled datum editing so the
datum moves in real-time as the angle spinner changes. Adds
_resolve_source_refs() helper and tangent_cylinder manual fallback.

Closes #66
2026-02-08 19:35:14 -06:00
26 changed files with 1396 additions and 19 deletions

39
.gitea/workflows/docs.yml Normal file
View File

@@ -0,0 +1,39 @@
name: Deploy Docs
on:
push:
branches: [main]
paths:
- "docs/**"
- ".gitea/workflows/docs.yml"
jobs:
build-and-deploy:
runs-on: docs
steps:
- name: Checkout
run: |
REPO_URL="http://gitea:3000/kindred/create.git"
if [ -d .git ]; then
git fetch "$REPO_URL" main
git checkout -f FETCH_HEAD
else
git clone --depth 1 --branch main "$REPO_URL" .
fi
- name: Install mdBook
run: |
if ! command -v mdbook &>/dev/null; then
MDBOOK_VERSION="v0.5.2"
wget -q -O mdbook.tar.gz "https://github.com/rust-lang/mdBook/releases/download/${MDBOOK_VERSION}/mdbook-${MDBOOK_VERSION}-x86_64-unknown-linux-musl.tar.gz"
tar -xzf mdbook.tar.gz -C /usr/local/bin
rm mdbook.tar.gz
fi
- name: Build mdBook
run: mdbook build docs/
- name: Deploy
run: |
rm -rf /opt/git/docs/book/*
cp -r docs/book/* /opt/git/docs/book/

3
.gitignore vendored
View File

@@ -71,3 +71,6 @@ files_to_translate.txt
# pixi environments
.pixi
*.egg-info
# mdBook build output
docs/book/

17
docs/book.toml Normal file
View File

@@ -0,0 +1,17 @@
[book]
title = "Kindred Create Documentation"
authors = ["Kindred Systems LLC"]
language = "en"
multilingual = false
src = "src"
[build]
build-dir = "book"
[output.html]
default-theme = "coal"
preferred-dark-theme = "coal"
git-repository-url = "https://git.kindred-systems.com/kindred/create"
git-repository-icon = "fa-code-branch"
additional-css = ["theme/kindred.css"]
no-section-label = false

33
docs/src/SUMMARY.md Normal file
View File

@@ -0,0 +1,33 @@
# Summary
[Introduction](./introduction.md)
---
# User Guide
- [Getting Started](./guide/getting-started.md)
- [Installation](./guide/installation.md)
- [Building from Source](./guide/building.md)
- [Workbenches](./guide/workbenches.md)
- [ztools](./guide/ztools.md)
- [Silo](./guide/silo.md)
# Architecture
- [Overview](./architecture/overview.md)
- [Python as Source of Truth](./architecture/python-source-of-truth.md)
- [Silo Server](./architecture/silo-server.md)
- [OndselSolver](./architecture/ondsel-solver.md)
# Development
- [Contributing](./development/contributing.md)
- [Code Quality](./development/code-quality.md)
- [Repository Structure](./development/repo-structure.md)
- [Build System](./development/build-system.md)
# Reference
- [Configuration](./reference/configuration.md)
- [Glossary](./reference/glossary.md)

View File

@@ -0,0 +1,27 @@
# OndselSolver
OndselSolver is the assembly constraint solver used by FreeCAD's Assembly workbench. Kindred Create vendors a fork of the solver as a git submodule.
- **Path:** `src/3rdParty/OndselSolver/`
- **Source:** `git.kindred-systems.com/kindred/solver` (Kindred fork)
## How it works
The solver uses a **Lagrangian constraint formulation** to resolve assembly constraints (mates, joints, fixed positions). Given a set of parts with geometric constraints between them, it computes positions and orientations that satisfy all constraints simultaneously.
The Assembly workbench (`src/Mod/Assembly/`) calls the solver whenever constraints are added or modified. Kindred Create has patches to `Assembly/` that extend `findPlacement()` for better datum and origin handling.
## Why a fork
The solver is forked from the upstream Ondsel project for:
- **Pinned stability** — the submodule is pinned to a known-good commit
- **Potential modifications** — the fork allows Kindred-specific patches if needed
- **Availability** — hosted on Kindred's Gitea instance for reliable access
## Future: GNN solver
There are plans to explore a Graph Neural Network (GNN) approach to constraint solving that could complement or supplement the Lagrangian solver for specific use cases. This is not yet implemented.
## Related: GSL
The `src/3rdParty/GSL/` submodule is Microsoft's Guidelines Support Library (`github.com/microsoft/GSL`), providing C++ core guidelines utilities like `gsl::span` and `gsl::not_null`. It is a build dependency, not related to the constraint solver.

View File

@@ -0,0 +1,78 @@
# Architecture Overview
Kindred Create is structured as a thin integration layer on top of FreeCAD. The design follows three principles:
1. **Minimal core modifications** — prefer submodule addons over patching FreeCAD internals
2. **Graceful degradation** — Create runs without ztools or Silo if submodules are missing
3. **Pure Python addons** — workbenches follow FreeCAD's standard addon pattern
## Three-layer model
```
┌─────────────────────────────────┐
│ FreeCAD Documents (.FCStd) │ Python source of truth
│ Workbench logic (Python) │
├─────────────────────────────────┤
│ PostgreSQL │ Silo metadata, revisions, BOM
├─────────────────────────────────┤
│ MinIO (S3-compatible) │ Binary file storage cache
└─────────────────────────────────┘
```
FreeCAD documents are the authoritative representation of CAD data. Silo's PostgreSQL database stores metadata (part numbers, revisions, BOM relationships) and MinIO stores the binary `.FCStd` files. The FreeCAD workbench synchronizes between local files and the server.
## Source layout
```
create/
├── src/App/ # Core application (C++)
├── src/Base/ # Foundation classes (C++)
├── src/Gui/ # GUI framework (C++ + Qt6 + QSS)
│ ├── Stylesheets/ # KindredCreate.qss theme
│ ├── PreferencePacks/ # Theme preference pack
│ ├── Icons/ # silo-*.svg origin icons
│ ├── FileOrigin.* # Abstract file origin interface
│ └── OriginManager.* # Origin lifecycle management
├── src/Mod/ # ~37 FreeCAD modules
│ ├── Create/ # Kindred bootstrap module
│ ├── Assembly/ # Assembly workbench (Kindred patches)
│ ├── PartDesign/ # Part Design (stock + ztools injection)
│ └── ... # Other stock FreeCAD modules
├── mods/
│ ├── ztools/ # Datum/pattern/pocket workbench (submodule)
│ └── silo/ # Parts database workbench (submodule)
└── src/3rdParty/
├── OndselSolver/ # Assembly solver (submodule)
└── GSL/ # Guidelines Support Library (submodule)
```
## Bootstrap sequence
1. FreeCAD core initializes, discovers `src/Mod/Create/`
2. `Init.py` runs `setup_kindred_addons()` — adds `mods/ztools/ztools` and `mods/silo/freecad` to `sys.path`, executes their `Init.py`
3. GUI phase: `InitGui.py` runs `setup_kindred_workbenches()` — executes addon `InitGui.py` files to register workbenches
4. Deferred QTimer cascade:
- **1500ms** — Register Silo as a file origin
- **2000ms** — Dock the Silo auth panel
- **3000ms** — Check for Silo first-start configuration
- **4000ms** — Dock the Silo activity panel
- **10000ms** — Check for application updates
The QTimer cascade exists because FreeCAD's startup is not fully synchronous — Silo registration must wait for the GUI framework to be ready.
## Origin system
The origin system is Kindred's primary addition to FreeCAD's GUI layer:
- **`FileOrigin`** — abstract C++ interface for file backends
- **`LocalFileOrigin`** — default implementation (local filesystem)
- **`SiloOrigin`** — Silo database backend (registered by the Python addon)
- **`OriginManager`** — manages origin lifecycle, switching, capability queries
- **`OriginSelectorWidget`** — dropdown in the File toolbar
- **`CommandOrigin.cpp`** — Commit / Pull / Push / Info / BOM commands that delegate to the active origin
## Module interaction
- **ztools** injects commands into PartDesign via `_ZToolsPartDesignManipulator`
- **Silo** registers as a `FileOrigin` backend via `silo_origin.register_silo_origin()`
- **Create module** is glue only — no feature code, just bootstrap and version management

View File

@@ -0,0 +1,26 @@
# Python as Source of Truth
In Kindred Create's architecture, FreeCAD documents (`.FCStd` files) are the authoritative representation of all CAD data. The Silo database and MinIO storage are caches and metadata indexes — they do not define the model.
## How it works
An `.FCStd` file is a ZIP archive containing:
- XML documents describing the parametric model tree
- BREP geometry files for each shape
- Thumbnail images
- Embedded spreadsheets and metadata
When a user runs **Commit**, the workbench uploads the entire `.FCStd` file to MinIO and records metadata (part number, revision, timestamp, commit message) in PostgreSQL. When a user runs **Pull**, the workbench downloads the `.FCStd` from MinIO and opens it locally.
## Why this design
- **No data loss** — the complete model is always in the `.FCStd` file, never split across systems
- **Offline capability** — engineers can work without a server connection
- **FreeCAD compatibility** — files are standard FreeCAD documents, openable in stock FreeCAD
- **Simple sync model** — the unit of transfer is always a whole file, avoiding merge conflicts in binary geometry
## Trade-offs
- **Large files** — `.FCStd` files can grow large with complex assemblies; every commit stores the full file
- **No partial sync** — you cannot pull a single feature or component; it is all or nothing
- **Conflict resolution** — two users editing the same file must resolve conflicts manually (last-commit-wins by default)

View File

@@ -0,0 +1,50 @@
# Silo Server
The Silo server is a Go REST API that provides the backend for the Silo workbench. It manages part numbers, revisions, bills of materials, and file storage for engineering teams.
## Components
```
silo/
├── cmd/
│ ├── silo/ # CLI tool
│ └── silod/ # API server
├── internal/
│ ├── api/ # HTTP handlers, routes, templates
│ ├── config/ # Configuration loading
│ ├── db/ # PostgreSQL access
│ ├── migration/ # Property migration utilities
│ ├── partnum/ # Part number generation
│ ├── schema/ # YAML schema parsing
│ └── storage/ # MinIO file storage
├── migrations/ # Database migration SQL scripts
├── schemas/ # Part numbering schema definitions (YAML)
└── deployments/ # Docker Compose and systemd configs
```
## Stack
- **Go** REST API with 38+ routes
- **PostgreSQL** for metadata, revisions, BOM relationships
- **MinIO** (S3-compatible) for binary `.FCStd` file storage
- **LDAP / OIDC** for authentication
- **SSE** (Server-Sent Events) for real-time activity feed
## Key features
- **Configurable part number generation** via YAML schemas
- **Revision tracking** with append-only history
- **BOM management** with reference designators and alternates
- **Physical inventory** tracking with hierarchical locations
## Database migrations
Migrations live in `migrations/` as numbered SQL scripts (e.g., `001_initial.sql`). Apply them sequentially against the database:
```bash
psql -h psql.kindred.internal -U silo -d silo -f migrations/001_initial.sql
```
## Deployment
See `mods/silo/deployments/` for Docker Compose and systemd configurations. A typical deployment runs the Go server alongside PostgreSQL and MinIO containers.

View File

@@ -0,0 +1,72 @@
# Build System
Kindred Create uses **CMake** for build configuration, **pixi** (conda-based) for dependency management and task running, and **ccache** for compilation caching.
## Overview
- **CMake** >= 3.22.0
- **Ninja** generator (via conda presets)
- **pixi** manages all dependencies — compilers, Qt6, OpenCASCADE, Python, etc.
- **ccache** with 4 GB max, zlib compression level 6, sloppy mode
- **mold** linker on Linux for faster link times
## CMake configuration
The root `CMakeLists.txt` defines:
- **Kindred Create version:** `0.1.0` (via `KINDRED_CREATE_VERSION`)
- **FreeCAD base version:** `1.0.0` (via `FREECAD_VERSION`)
- CMake policy settings for compatibility
- ccache auto-detection
- Submodule dependency checks
- Library setup: yaml-cpp, fmt, ICU
### Version injection
The version flows from CMake to Python via `configure_file()`:
```
CMakeLists.txt (KINDRED_CREATE_VERSION = "0.1.0")
→ src/Mod/Create/version.py.in (template)
→ build/*/Mod/Create/version.py (generated)
→ update_checker.py (imports VERSION)
```
## CMake presets
Defined in `CMakePresets.json`:
| Preset | Platform | Build type |
|--------|----------|------------|
| `conda-linux-debug` | Linux | Debug |
| `conda-linux-release` | Linux | Release |
| `conda-macos-debug` | macOS | Debug |
| `conda-macos-release` | macOS | Release |
| `conda-windows-debug` | Windows | Debug |
| `conda-windows-release` | Windows | Release |
All presets inherit from a hidden `common` base and a hidden `conda` base (Ninja generator, conda toolchain).
## cMake/ helper modules
The `cMake/` directory contains CMake helper macros inherited from FreeCAD:
- **FreeCAD_Helpers** — macros for building FreeCAD modules
- Platform detection modules
- Dependency finding modules (Find*.cmake)
## Dependencies
Core dependencies managed by pixi (from `pixi.toml`):
| Category | Packages |
|----------|----------|
| Build | cmake, ninja, swig, compilers (clang/gcc) |
| CAD kernel | occt 7.8, coin3d, opencamlib, pythonocc-core |
| UI | Qt6 6.8, PySide6, pyside6 |
| Math | eigen, numpy, scipy, sympy |
| Data | hdf5, vtk, smesh, ifcopenshell |
| Python | 3.11 (< 3.12), pip, freecad-stubs |
Platform-specific extras:
- **Linux:** clang, kernel-headers, mesa, X11, libspnav
- **macOS:** sed
- **Windows:** pthreads-win32

View File

@@ -0,0 +1,40 @@
# Code Quality
## Formatting and linting
### C/C++
- **Formatter:** clang-format (config in `.clang-format`)
- **Static analysis:** clang-tidy (config in `.clang-tidy`)
### Python
- **Formatter:** black with 100-character line length
- **Linter:** pylint (config in `.pylintrc`)
## Pre-commit hooks
The repository uses [pre-commit](https://pre-commit.com/) to run formatters and linters automatically on staged files:
```bash
pip install pre-commit
pre-commit install
```
Configured hooks (`.pre-commit-config.yaml`):
- `trailing-whitespace` — remove trailing whitespace
- `end-of-file-fixer` — ensure files end with a newline
- `check-yaml` — validate YAML syntax
- `check-added-large-files` — prevent accidental large file commits
- `mixed-line-ending` — normalize line endings
- `black` — Python formatting (100 char lines)
- `clang-format` — C/C++ formatting
## Scope
Pre-commit hooks are configured to run on specific directories:
- `src/Base/`, `src/Gui/`, `src/Main/`, `src/Tools/`
- `src/Mod/Assembly/`, `src/Mod/BIM/`, `src/Mod/CAM/`, `src/Mod/Draft/`, `src/Mod/Fem/`, and other stock modules
- `tests/src/`
Excluded: generated files, vendored libraries (`QSint/`, `Quarter/`, `3Dconnexion/navlib`), and binary formats.

View File

@@ -0,0 +1,52 @@
# Contributing
Kindred Create is maintained at [git.kindred-systems.com/kindred/create](https://git.kindred-systems.com/kindred/create). Contributions are submitted as pull requests against the `main` branch.
## Getting started
```bash
git clone --recursive ssh://git@git.kindred-systems.com:2222/kindred/create.git
cd create
pixi run configure
pixi run build
pixi run freecad
```
See [Building from Source](../guide/building.md) for the full development setup.
## Branch and PR workflow
1. Create a feature branch from `main`:
```bash
git checkout -b feat/my-feature main
```
2. Make your changes, commit with conventional commit messages (see below).
3. Push and open a pull request against `main`.
4. CI builds and tests run automatically on all PRs.
## Commit messages
Use [Conventional Commits](https://www.conventionalcommits.org/):
| Prefix | Purpose |
|--------|---------|
| `feat:` | New feature |
| `fix:` | Bug fix |
| `chore:` | Maintenance, dependencies |
| `docs:` | Documentation only |
| `art:` | Icons, theme, visual assets |
Scope is optional but encouraged:
- `feat(ztools): add datum point creation mode`
- `fix(gui): correct menu icon size on Wayland`
- `chore: update silo submodule`
## Reporting issues
Report issues at the [issue tracker](https://git.kindred-systems.com/kindred/create/issues). When reporting:
1. Note whether the issue involves Kindred Create additions (ztools, Silo, theme) or base FreeCAD
2. Include version info from **Help > About FreeCAD > Copy to clipboard**
3. Provide reproduction steps and attach example files (FCStd as ZIP) if applicable
For base FreeCAD issues, also check the [FreeCAD issue tracker](https://github.com/FreeCAD/FreeCAD/issues).

View File

@@ -0,0 +1,73 @@
# Repository Structure
```
create/
├── src/
│ ├── App/ # Core application (C++)
│ ├── Base/ # Base classes (C++)
│ ├── Gui/ # GUI framework and stylesheets (C++)
│ ├── Main/ # Application entry points
│ ├── Mod/ # FreeCAD modules (~37)
│ │ ├── Create/ # Kindred bootstrap module
│ │ ├── Assembly/ # Assembly workbench (Kindred patches)
│ │ ├── PartDesign/ # Part Design workbench
│ │ ├── Sketcher/ # Sketcher workbench
│ │ ├── AddonManager/ # Addon manager (submodule)
│ │ └── ... # Other stock FreeCAD modules
│ └── 3rdParty/
│ ├── OndselSolver/ # Assembly solver (submodule)
│ └── GSL/ # Guidelines Support Library (submodule)
├── mods/ # Kindred addon workbenches (submodules)
│ ├── ztools/ # ztools workbench
│ └── silo/ # Silo parts database
├── kindred-icons/ # SVG icon library (~200 icons)
├── resources/ # Branding, desktop integration
│ ├── branding/ # Logo, splash, icon generation scripts
│ └── icons/ # Platform icons (.ico, .icns, hicolor)
├── package/ # Packaging scripts
│ ├── debian/ # Debian package
│ ├── ubuntu/ # Ubuntu-specific
│ ├── fedora/ # RPM package
│ ├── rattler-build/ # Cross-platform bundles (AppImage, DMG, NSIS)
│ └── WindowsInstaller/ # NSIS installer definition
├── .gitea/workflows/ # CI/CD pipelines
│ ├── build.yml # Build + test on push/PR
│ └── release.yml # Release on tag push
├── tests/ # Test suite
│ ├── src/ # C++ test sources
│ └── lib/ # Google Test framework (submodule)
├── cMake/ # CMake helper modules
├── docs/ # Documentation (this book)
├── tools/ # Dev utilities (build, lint, profile)
├── contrib/ # IDE configs (VSCode, CLion, debugger)
├── data/ # Example and test data
├── CMakeLists.txt # Root build configuration
├── CMakePresets.json # Platform build presets
├── pixi.toml # Pixi environment and tasks
├── CONTRIBUTING.md # Contribution guide
├── README.md # Project overview
├── LICENSE # LGPL-2.1-or-later
└── .pre-commit-config.yaml # Code quality hooks
```
## Git submodules
| Submodule | Path | Source | Purpose |
|-----------|------|--------|---------|
| ztools | `mods/ztools` | `git.kindred-systems.com/forbes/ztools` | Unified workbench |
| silo-mod | `mods/silo` | `git.kindred-systems.com/kindred/silo-mod` | Parts database |
| OndselSolver | `src/3rdParty/OndselSolver` | `git.kindred-systems.com/kindred/solver` | Assembly solver |
| GSL | `src/3rdParty/GSL` | `github.com/microsoft/GSL` | C++ guidelines library |
| AddonManager | `src/Mod/AddonManager` | `github.com/FreeCAD/AddonManager` | Extension manager |
| googletest | `tests/lib` | `github.com/google/googletest` | Test framework |
## Key files
| File | Purpose |
|------|---------|
| `src/Mod/Create/Init.py` | Console-phase bootstrap — loads addons |
| `src/Mod/Create/InitGui.py` | GUI-phase bootstrap — registers workbenches, deferred setup |
| `src/Gui/FileOrigin.h` | Abstract file origin interface (Kindred addition) |
| `src/Gui/Stylesheets/KindredCreate.qss` | Catppuccin Mocha theme |
| `pixi.toml` | Build tasks and dependencies |
| `CMakeLists.txt` | Root CMake configuration |

107
docs/src/guide/building.md Normal file
View File

@@ -0,0 +1,107 @@
# Building from Source
## Prerequisites
- **git** with submodule support
- **[pixi](https://pixi.sh)** — conda-based dependency manager and task runner
Pixi handles all other dependencies (CMake, compilers, Qt6, OpenCASCADE, etc.).
## Clone
```bash
git clone --recursive ssh://git@git.kindred-systems.com:2222/kindred/create.git
cd create
```
If cloned without `--recursive`:
```bash
git submodule update --init --recursive
```
The repository includes six submodules:
| Submodule | Path | Source |
|-----------|------|--------|
| ztools | `mods/ztools` | `git.kindred-systems.com/forbes/ztools` |
| silo-mod | `mods/silo` | `git.kindred-systems.com/kindred/silo-mod` |
| OndselSolver | `src/3rdParty/OndselSolver` | `git.kindred-systems.com/kindred/solver` |
| GSL | `src/3rdParty/GSL` | `github.com/microsoft/GSL` |
| AddonManager | `src/Mod/AddonManager` | `github.com/FreeCAD/AddonManager` |
| googletest | `tests/lib` | `github.com/google/googletest` |
## Build
```bash
pixi run configure
pixi run build
pixi run install
pixi run freecad
```
By default these target the **debug** configuration. For release builds:
```bash
pixi run configure-release
pixi run build-release
pixi run install-release
pixi run freecad-release
```
## All pixi tasks
| Task | Description |
|------|-------------|
| `initialize` | `git submodule update --init --recursive` |
| `configure` | CMake configure (debug) |
| `configure-debug` | CMake configure with debug preset |
| `configure-release` | CMake configure with release preset |
| `build` | Build (debug) |
| `build-debug` | `cmake --build build/debug` |
| `build-release` | `cmake --build build/release` |
| `install` | Install (debug) |
| `install-debug` | `cmake --install build/debug` |
| `install-release` | `cmake --install build/release` |
| `test` | Run tests (debug) |
| `test-debug` | `ctest --test-dir build/debug` |
| `test-release` | `ctest --test-dir build/release` |
| `freecad` | Launch FreeCAD (debug) |
| `freecad-debug` | `build/debug/bin/FreeCAD` |
| `freecad-release` | `build/release/bin/FreeCAD` |
## CMake presets
The project provides presets in `CMakePresets.json` for each platform and build type:
- `conda-linux-debug` / `conda-linux-release`
- `conda-macos-debug` / `conda-macos-release`
- `conda-windows-debug` / `conda-windows-release`
All presets inherit from a `common` base that enables `CMAKE_EXPORT_COMPILE_COMMANDS` and configures job pools. The `conda` presets use the Ninja generator and pick up compiler paths from the pixi environment.
## Platform notes
**Linux:** Uses clang from conda-forge. Requires kernel-headers, mesa, X11, and libspnav (all provided by pixi). Uses the mold linker for faster link times.
**macOS:** Minimal extra dependencies — pixi provides nearly everything. Tested on both Intel and Apple Silicon.
**Windows:** Requires pthreads-win32 and MSVC. The conda preset configures the MSVC toolchain automatically when run inside a pixi shell.
## Caching
The build uses **ccache** for compilation caching:
- Maximum cache size: 4 GB
- Compression: zlib level 6
- Sloppiness mode enabled for faster cache hits
ccache is auto-detected by CMake at configure time.
## Common problems
**Submodules not initialized:** If you see missing file errors for ztools or Silo, run `pixi run initialize` or `git submodule update --init --recursive`.
**Pixi not found:** Install pixi from <https://pixi.sh>.
**ccache full:** Clear with `ccache -C` or increase the limit in your ccache config.
**Preset not found:** Ensure you are running CMake from within a pixi shell (`pixi shell`) so that conda environment variables are set.

View File

@@ -0,0 +1,41 @@
# Getting Started
Kindred Create can be installed from prebuilt packages or built from source. This section covers both paths.
## Quick start (prebuilt)
Download the latest release from the [releases page](https://git.kindred-systems.com/kindred/create/releases).
**Debian/Ubuntu:**
```bash
sudo apt install ./kindred-create_*.deb
```
**AppImage:**
```bash
chmod +x KindredCreate-*.AppImage
./KindredCreate-*.AppImage
```
## Quick start (from source)
```bash
git clone --recursive ssh://git@git.kindred-systems.com:2222/kindred/create.git
cd create
pixi run configure
pixi run build
pixi run freecad
```
See [Installation](./installation.md) for prebuilt package details and [Building from Source](./building.md) for the full development setup.
## First run
On first launch, Kindred Create:
1. Loads the **ztools** and **Silo** workbenches automatically via the Create bootstrap module
2. Opens the **ZToolsWorkbench** as the default workbench
3. Prompts for Silo server configuration if not yet set up
4. Checks for application updates in the background (after ~10 seconds)
If the Silo server is not available, Kindred Create operates normally with local file operations. The Silo features activate once a server is configured.

View File

@@ -0,0 +1,49 @@
# Installation
## Prebuilt packages
Download the latest release from the [releases page](https://git.kindred-systems.com/kindred/create/releases).
### Debian / Ubuntu
```bash
sudo apt install ./kindred-create_*.deb
```
### AppImage (any Linux)
```bash
chmod +x KindredCreate-*.AppImage
./KindredCreate-*.AppImage
```
The AppImage is a self-contained bundle using squashfs with zstd compression. No installation required.
### macOS
> macOS builds are planned but not yet available in CI. Build from source for now.
### Windows
> Windows builds are planned but not yet available in CI. Build from source for now.
## Verifying your installation
Launch Kindred Create and check the console output (View > Report View) for:
```
Create: Loaded ztools Init.py
Create: Loaded silo Init.py
Create module initialized
```
This confirms the bootstrap module loaded both workbenches. If Silo is not configured, you will see a settings prompt on first launch.
## Uninstalling
**Debian/Ubuntu:**
```bash
sudo apt remove kindred-create
```
**AppImage:** Delete the `.AppImage` file. No system files are modified.

204
docs/src/guide/silo.md Normal file
View File

@@ -0,0 +1,204 @@
# Silo
Silo is an item database and part management system for Kindred Create. It provides revision-controlled storage for CAD files, configurable part number generation, BOM management, and team collaboration.
- **Submodule path:** `mods/silo/`
- **Source:** `git.kindred-systems.com/kindred/silo-mod`
## Architecture
Silo has three components:
```
┌──────────────────────┐ ┌──────────────┐
│ FreeCAD Workbench │────▶│ Go REST API │
│ (Python commands) │ │ (silod) │
└──────────────────────┘ └──────┬───────┘
│ │
│ silo-client │
│ (shared API lib) │
│ ┌─────┴─────┐
│ │ │
│ PostgreSQL MinIO
│ (metadata) (files)
Local .FCStd files
```
- **Go REST API server** (`cmd/silod/`) — 38+ routes, backed by PostgreSQL and MinIO
- **FreeCAD workbench** (`freecad/`) — Python commands integrated into Kindred Create
- **Shared API client** (`silo-client/`) — Python library used by the workbench (nested submodule)
The silo-mod repository was split from a monorepo into three repos: `silo-client` (shared Python API client), `silo-mod` (FreeCAD workbench), and `silo-calc` (LibreOffice Calc extension).
## Workbench commands
### Document lifecycle
| Command | Shortcut | Description |
|---------|----------|-------------|
| `Silo_New` | Ctrl+N | Register a new part — select category, generate part number from schema, optional project tagging |
| `Silo_Open` | Ctrl+O | Search and open items — combined dialog querying both the database and local files |
| `Silo_Save` | Ctrl+S | Save locally to canonical path, collect document properties, upload to MinIO as auto-revision |
| `Silo_Commit` | Ctrl+Shift+S | Save as a new revision with a user-provided comment |
| `Silo_Pull` | — | Download from MinIO with revision selection, conflict detection, and progress tracking |
| `Silo_Push` | — | Batch upload — finds local files not yet synced to the server, compares timestamps |
### Information and management
| Command | Description |
|---------|-------------|
| `Silo_Info` | Show item metadata, project tags, and revision history table with status and labels |
| `Silo_BOM` | Two-tab view: BOM (children) and Where Used (parents). Add, edit, remove entries with quantity and unit tracking |
| `Silo_TagProjects` | Multi-select dialog for assigning project tags to items |
| `Silo_Rollback` | Select a previous revision and create a new revision from that point with optional comment |
| `Silo_SetStatus` | Change revision lifecycle status: draft → review → released → obsolete |
### Administration
| Command | Description |
|---------|-------------|
| `Silo_Settings` | Full settings UI — API URL, SSL verify, custom CA cert, API token management, authentication status |
| `Silo_Auth` | Session-based login: `/login``/api/auth/me``/api/auth/tokens`; stores API token in preferences |
| `Silo_ToggleMode` | Switch between Silo workbench and other workbenches (menu only) |
## Origin integration
Silo registers as a **file origin** via the `FileOrigin` interface in `src/Gui/`. The `SiloOrigin` class in `silo_origin.py` implements:
| Capability | Value |
|------------|-------|
| `id` | `"silo"` |
| `name` | `"Kindred Silo"` |
| `type` | PLM (1) |
| `tracksExternally` | true |
| `requiresAuthentication` | true |
| `supportsRevisions` | true |
| `supportsBOM` | true |
| `supportsPartNumbers` | true |
| `supportsAssemblies` | true |
The origin delegates to the workbench commands for all operations (new, open, save, commit, pull, push, info, BOM). Registration happens via a deferred QTimer (1500ms after startup) in `src/Mod/Create/InitGui.py`.
## Configuration
### FreeCAD parameters
Stored in `User parameter:BaseApp/Preferences/Mod/KindredSilo`:
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `ApiUrl` | String | (empty) | Silo server URL |
| `SslVerify` | Bool | true | Verify SSL certificates |
| `CaCertPath` | String | (empty) | Path to custom CA certificate |
| `ApiToken` | String | (empty) | Stored authentication token |
| `FirstStartChecked` | Bool | false | Whether first-start prompt has been shown |
| `ProjectsDir` | String | `~/projects` | Local directory for checked-out files |
### Environment variables
| Variable | Default | Description |
|----------|---------|-------------|
| `SILO_API_URL` | `http://localhost:8080/api` | Override for server API endpoint |
| `SILO_PROJECTS_DIR` | `~/projects` | Override for local projects directory |
## Server setup
### Quick start
```bash
# Database setup (apply migrations sequentially)
psql -h psql.kindred.internal -U silo -d silo -f migrations/001_initial.sql
# ... through 010_item_extended_fields.sql
# Configure
cp config.example.yaml config.yaml
# Edit config.yaml with your database, MinIO, and auth settings
# Run server
go run ./cmd/silod
```
### Production deployment
Production configs live in `mods/silo/deployments/`:
```
deployments/
├── config.prod.yaml # Database, MinIO, auth settings
├── docker-compose.prod.yaml # Production container orchestration
├── docker-compose.yaml # Development Docker Compose
└── systemd/
├── silod.env.example # Service environment template
└── silod.service # systemd unit file
```
The systemd service runs as user `silo` with security hardening (`ProtectSystem=strict`, `NoNewPrivileges`, `PrivateTmp`). Config at `/etc/silo/config.yaml`, binary at `/opt/silo/bin/silod`.
### Server stack
- **Go** REST API with 38+ routes
- **PostgreSQL** for metadata, revisions, BOM relationships
- **MinIO** (S3-compatible) for binary `.FCStd` file storage
- **LDAP / OIDC** for authentication
- **SSE** (Server-Sent Events) for real-time activity feed
## Database migrations
Migrations live in `mods/silo/migrations/` as numbered SQL scripts:
| Migration | Purpose |
|-----------|---------|
| `001_initial.sql` | Core schema — items, revisions, properties |
| `002_sequence_by_name.sql` | Part number sequence generation |
| `003_remove_material.sql` | Property cleanup |
| `004_cad_sync_state.sql` | CAD file sync tracking |
| `005_property_schema_version.sql` | Schema versioning for properties |
| `006_project_tags.sql` | Project-to-item relationships |
| `007_revision_status.sql` | Revision lifecycle status tracking |
| `008_odoo_integration.sql` | ERP integration preparation |
| `009_auth.sql` | User authentication tables |
| `010_item_extended_fields.sql` | Extended item metadata |
Apply sequentially: `psql -f migrations/001_initial.sql`, then `002`, etc. There is no automated migration runner — apply manually against the database.
## Part numbering schemas
Part number generation is configured via YAML schemas in `mods/silo/schemas/`:
- `kindred-rd.yaml` — Primary R&D part numbering schema with category codes, sequence segments, and validation rules
- `kindred-locations.yaml` — Location hierarchy schema for physical inventory tracking
## Directory structure
```
mods/silo/
├── cmd/
│ ├── silo/ # CLI tool
│ └── silod/ # API server
├── internal/
│ ├── api/ # HTTP handlers, routes, templates
│ ├── config/ # Configuration loading
│ ├── db/ # PostgreSQL access
│ ├── migration/ # Property migration utilities
│ ├── partnum/ # Part number generation
│ ├── schema/ # YAML schema parsing
│ └── storage/ # MinIO file storage
├── freecad/
│ ├── InitGui.py # SiloWorkbench registration
│ ├── silo_commands.py # 14 commands + dock widgets
│ ├── silo_origin.py # FileOrigin backend
│ └── resources/icons/ # 10 silo-*.svg icons
├── silo-client/ # Shared Python API client (nested submodule)
│ └── silo_client/
│ ├── client.py # SiloClient HTTP wrapper
│ └── settings.py # SiloSettings config management
├── migrations/ # 10 numbered SQL scripts
├── schemas/ # Part numbering YAML schemas
└── deployments/ # Docker Compose + systemd configs
```
## Further reading
- `mods/silo/README.md` — server quickstart and CLI usage
- `mods/silo/ROADMAP.md` — strategic roadmap (6 phases, Q2 2026 → Q4 2027)

View File

@@ -0,0 +1,23 @@
# Workbenches
Kindred Create ships two custom workbenches on top of FreeCAD's standard set.
## ztools
A unified workbench that consolidates part design, assembly, and sketcher tools into a single interface. It is the **default workbench** when Kindred Create launches.
ztools commands are also injected into the PartDesign workbench menus and toolbars via a manipulator mechanism, so they are accessible even when working in stock PartDesign.
See the [ztools guide](./ztools.md) for details.
## Silo
A parts database workbench for managing CAD files, part numbers, revisions, and bills of materials across teams. Silo commands (New, Open, Save, Commit, Pull, Push, Info, BOM) are integrated into the File menu and toolbar across **all** workbenches via the origin system.
Silo requires a running server instance. On first launch, Kindred Create prompts for server configuration.
See the [Silo guide](./silo.md) for details.
## Stock FreeCAD workbenches
All standard FreeCAD workbenches are available: PartDesign, Sketcher, Assembly, TechDraw, Draft, BIM, CAM, FEM, Mesh, Spreadsheet, and others. Kindred Create does not remove or disable any stock functionality.

133
docs/src/guide/ztools.md Normal file
View File

@@ -0,0 +1,133 @@
# ztools
ztools is a pure-Python FreeCAD workbench that consolidates part design, assembly, and sketcher tools into a single unified interface. It is the **default workbench** when Kindred Create launches.
- **Submodule path:** `mods/ztools/`
- **Source:** `git.kindred-systems.com/forbes/ztools`
- **Stats:** 6,400+ lines of code, 24+ command classes, 33 custom icons, 17 toolbars
## Commands
### Datum Creator (`ZTools_DatumCreator`)
Creates datum geometry (planes, axes, points) with 16 creation modes. The task panel auto-detects the geometry type from your selection and offers appropriate modes. Supports custom naming, spreadsheet linking, and body- or document-level creation.
### Datum Manager (`ZTools_DatumManager`)
Manages existing datums. (Stub — planned for Phase 1, Q1 2026.)
### Enhanced Pocket (`ZTools_EnhancedPocket`)
Extends FreeCAD's Pocket feature with **Flip Side to Cut** — a SOLIDWORKS-style feature that removes material *outside* the sketch profile rather than inside. Uses a Boolean Common operation internally. Supports all standard pocket types: Dimension, Through All, To First, Up To Face, Two Dimensions. Taper angle is supported for standard pockets (disabled for flipped).
### Rotated Linear Pattern (`ZTools_RotatedLinearPattern`)
Creates a linear pattern with incremental rotation per instance. Configure direction, spacing, number of occurrences, and cumulative or per-instance rotation. Source components are automatically hidden.
### Assembly Linear Pattern (`ZTools_AssemblyLinearPattern`)
Creates linear patterns of assembly components. Supports multi-component selection, direction vectors, total length or spacing modes, and creation as Links (recommended) or copies. Auto-detects the parent assembly.
### Assembly Polar Pattern (`ZTools_AssemblyPolarPattern`)
Creates polar (circular) patterns of assembly components. Supports custom or preset axes (X/Y/Z), full circle or custom angle, center point definition, and creation as Links or copies.
### Spreadsheet Formatting (9 commands)
| Command | Action |
|---------|--------|
| `ZTools_SpreadsheetStyleBold` | Toggle bold |
| `ZTools_SpreadsheetStyleItalic` | Toggle italic |
| `ZTools_SpreadsheetStyleUnderline` | Toggle underline |
| `ZTools_SpreadsheetAlignLeft` | Left align |
| `ZTools_SpreadsheetAlignCenter` | Center align |
| `ZTools_SpreadsheetAlignRight` | Right align |
| `ZTools_SpreadsheetBgColor` | Background color picker |
| `ZTools_SpreadsheetTextColor` | Text color picker |
| `ZTools_SpreadsheetQuickAlias` | Auto-create aliases from row/column labels |
## Datum creation modes
### Planes (7 modes)
| Mode | Description | Input |
|------|-------------|-------|
| Offset from Face | Offsets a planar face along its normal | Face + distance (mm) |
| Offset from Plane | Offsets an existing datum plane | Datum plane + distance (mm) |
| Midplane | Plane halfway between two parallel faces | Two parallel faces |
| 3 Points | Plane through three non-collinear points | Three vertices |
| Normal to Edge | Plane perpendicular to an edge at a parameter location | Edge + parameter (0.01.0) |
| Angled | Rotates a plane about an edge by a specified angle | Face + edge + angle (degrees) |
| Tangent to Cylinder | Plane tangent to a cylindrical face at an angular position | Cylindrical face + angle (degrees) |
### Axes (4 modes)
| Mode | Description | Input |
|------|-------------|-------|
| 2 Points | Axis through two points | Two vertices |
| From Edge | Axis along a linear edge | Linear edge |
| Cylinder Center | Axis along the centerline of a cylinder | Cylindrical face |
| Plane Intersection | Axis at the intersection of two planes | Two non-parallel planes |
### Points (5 modes)
| Mode | Description | Input |
|------|-------------|-------|
| At Vertex | Point at a vertex location | Vertex |
| XYZ Coordinates | Point at explicit coordinates | x, y, z (mm) |
| On Edge | Point at a location along an edge | Edge + parameter (0.01.0) |
| Face Center | Point at the center of mass of a face | Face |
| Circle Center | Point at the center of a circular or arc edge | Circular edge |
## PartDesign injection
ztools registers a `_ZToolsPartDesignManipulator` that hooks into the PartDesign workbench at startup. This injects the following commands into PartDesign's toolbars and menus:
| PartDesign toolbar | Injected command |
|--------------------|-----------------|
| Part Design Helper Features | `ZTools_DatumCreator`, `ZTools_DatumManager` |
| Part Design Modeling Features | `ZTools_EnhancedPocket` |
| Part Design Transformation Features | `ZTools_RotatedLinearPattern` |
The manipulator is registered in `InitGui.py` when the Create bootstrap module loads addon workbenches.
## Directory structure
```
mods/ztools/
├── ztools/ztools/
│ ├── InitGui.py # Workbench registration + manipulator
│ ├── Init.py # Console initialization
│ ├── commands/
│ │ ├── datum_commands.py # DatumCreator + DatumManager
│ │ ├── datum_viewprovider.py # ViewProvider + edit panel
│ │ ├── pocket_commands.py # EnhancedPocket + FlippedPocket
│ │ ├── pattern_commands.py # RotatedLinearPattern
│ │ ├── assembly_pattern_commands.py # Linear + Polar assembly patterns
│ │ └── spreadsheet_commands.py # 9 formatting commands
│ ├── datums/
│ │ └── core.py # 16 datum creation functions
│ └── resources/ # Icons and theme
└── CatppuccinMocha/ # Theme preference pack
```
## Internal properties
ztools stores metadata on feature objects using these properties (preserved for backward compatibility):
| Property | Purpose |
|----------|---------|
| `ZTools_Type` | Feature type identifier |
| `ZTools_Params` | JSON creation parameters |
| `ZTools_SourceRefs` | JSON source geometry references |
## Known gaps
- Datum Manager is a stub — full implementation planned for Q1 2026
- Datum parameter changes don't recalculate from source geometry yet
- Enhanced Pocket taper angle is disabled for flipped pockets
## Further reading
- `mods/ztools/KINDRED_INTEGRATION.md` — integration architecture and migration options
- `mods/ztools/ROADMAP.md` — phased development plan (Q1Q4 2026)

37
docs/src/introduction.md Normal file
View File

@@ -0,0 +1,37 @@
# Kindred Create
Kindred Create is a fork of [FreeCAD](https://www.freecad.org) 1.0+ that adds integrated tooling for professional engineering workflows. It ships custom workbenches and a dark theme on top of FreeCAD's parametric modeling core.
- **License:** LGPL 2.1+
- **Organization:** [Kindred Systems LLC](https://www.kindred-systems.com)
- **Build system:** CMake + [pixi](https://pixi.sh)
- **Current version:** Kindred Create v0.1.0 (FreeCAD base v1.0.0)
## Key features
**[ztools](./guide/ztools.md)** — A unified workbench that consolidates part design, assembly, and sketcher tools into a single interface. Adds custom datum creation (planes, axes, points with 16 creation modes), pattern tools for assemblies, an enhanced pocket with flip-side cutting, and spreadsheet formatting commands.
**[Silo](./guide/silo.md)** — A parts database system for managing CAD files, part numbers, revisions, and bills of materials across teams. Includes a Go REST API server backed by PostgreSQL and MinIO, with FreeCAD commands for opening, saving, and syncing files directly from the application.
**Catppuccin Mocha theme** — A dark theme applied across the entire application, including the 3D viewport, sketch editor, spreadsheet view, and tree view. Uses spanning-line branch indicators instead of disclosure arrows, with tuned preference defaults for document handling, selection behavior, and notifications.
**Origin system** — A pluggable file backend abstraction. The origin selector in the File toolbar lets you switch between local filesystem operations and Silo database operations. Silo commands (Commit, Pull, Push, Info, BOM) are available across all workbenches when Silo is the active origin.
**Update checker** — On startup, Kindred Create checks the Gitea releases API for newer versions and logs the result. Configurable check interval and skip-version preferences.
## How it relates to FreeCAD
Kindred Create is a fork/distribution of FreeCAD 1.0+. The design minimizes core modifications — custom functionality is delivered through submodule addons (ztools, Silo) that follow FreeCAD's standard workbench pattern. If the addon submodules are missing, Kindred Create still functions as a themed FreeCAD.
The primary additions to FreeCAD's core are:
- The **origin system** (`FileOrigin` interface in `src/Gui/`) for pluggable file backends
- The **Create bootstrap module** (`src/Mod/Create/`) that loads addons at startup
- The **Catppuccin Mocha theme** (`KindredCreate.qss`) and preference pack
- Patches to **Assembly** (`findPlacement()` datum/origin handling)
## Links
- **Source:** <https://git.kindred-systems.com/kindred/create>
- **Downloads:** <https://git.kindred-systems.com/kindred/create/releases>
- **Issue tracker:** <https://git.kindred-systems.com/kindred/create/issues>
- **Website:** <https://www.kindred-systems.com/create>

View File

@@ -0,0 +1,117 @@
# Configuration
## Silo workbench
### FreeCAD parameters
Stored in `User parameter:BaseApp/Preferences/Mod/KindredSilo`:
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `ApiUrl` | String | (empty) | Silo server API endpoint URL |
| `SslVerify` | Bool | true | Verify SSL certificates when connecting to server |
| `CaCertPath` | String | (empty) | Path to custom CA certificate for self-signed certs |
| `ApiToken` | String | (empty) | Stored authentication token (set by `Silo_Auth`) |
| `FirstStartChecked` | Bool | false | Whether the first-start settings prompt has been shown |
| `ProjectsDir` | String | `~/projects` | Local directory for checked-out CAD files |
### Environment variables
These override the FreeCAD parameter values when set:
| Variable | Default | Description |
|----------|---------|-------------|
| `SILO_API_URL` | `http://localhost:8080/api` | Silo server API endpoint |
| `SILO_PROJECTS_DIR` | `~/projects` | Local directory for checked-out files |
### Keyboard shortcuts
Recommended shortcuts (prompted on first workbench activation):
| Shortcut | Command |
|----------|---------|
| Ctrl+O | `Silo_Open` — Search and open items |
| Ctrl+N | `Silo_New` — Create new item |
| Ctrl+S | `Silo_Save` — Save locally and upload |
| Ctrl+Shift+S | `Silo_Commit` — Save with revision comment |
## Update checker
Stored in `User parameter:BaseApp/Preferences/Mod/KindredCreate/Update`:
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `CheckEnabled` | Bool | true | Enable or disable update checks |
| `CheckIntervalDays` | Int | 1 | Minimum days between checks |
| `LastCheckTimestamp` | String | (empty) | ISO 8601 timestamp of last successful check |
| `SkippedVersion` | String | (empty) | Version the user chose to skip |
The checker queries:
```
https://git.kindred-systems.com/api/v1/repos/kindred/create/releases?limit=10
```
It compares the current version (injected at build time via `version.py.in`) against the latest non-draft, non-prerelease tag. The `latest` rolling tag is ignored. Checks run 10 seconds after GUI startup.
To disable: set `CheckEnabled` to `false` in FreeCAD preferences, or set `CheckIntervalDays` to `0` for on-demand only.
## Theme
The default theme is **Catppuccin Mocha** applied via `KindredCreate.qss`.
| Setting | Location |
|---------|----------|
| Canonical stylesheet | `src/Gui/Stylesheets/KindredCreate.qss` |
| Preference pack | `src/Gui/PreferencePacks/KindredCreate/` |
| Default theme name | `coal` (in mdBook docs) / `KindredCreate` (in app) |
To switch themes: **Edit > Preferences > General > Stylesheet** and select a different `.qss` file.
The preference pack is synced from the canonical stylesheet at build time via CMake's `configure_file()`. Edits should be made to the canonical file, not the preference pack copy.
## Build configuration
### Version constants
Defined in the root `CMakeLists.txt`:
| Constant | Value | Description |
|----------|-------|-------------|
| `KINDRED_CREATE_VERSION` | `0.1.0` | Kindred Create version |
| `FREECAD_VERSION` | `1.0.0` | FreeCAD base version |
These are injected into `src/Mod/Create/version.py` at build time via `version.py.in`.
### CMake presets
Defined in `CMakePresets.json`:
| Preset | Platform | Build type |
|--------|----------|------------|
| `conda-linux-debug` | Linux | Debug |
| `conda-linux-release` | Linux | Release |
| `conda-macos-debug` | macOS | Debug |
| `conda-macos-release` | macOS | Release |
| `conda-windows-debug` | Windows | Debug |
| `conda-windows-release` | Windows | Release |
### ccache
| Setting | Value |
|---------|-------|
| Max size | 4 GB |
| Compression | zlib level 6 |
| Sloppiness | `include_file_ctime,include_file_mtime,pch_defines,time_macros` |
ccache is auto-detected by CMake at configure time. Clear with `ccache -C`.
## Silo server
Server configuration is defined in YAML. See `mods/silo/deployments/config.prod.yaml` for production settings and `mods/silo/config.example.yaml` for all available options.
Key sections:
- **database** — PostgreSQL connection string
- **storage** — MinIO endpoint, bucket, access keys
- **auth** — LDAP/OIDC provider settings
- **server** — Listen address, TLS, CORS
- **schemas** — Path to part numbering YAML schemas

View File

@@ -0,0 +1,53 @@
# Glossary
## Terms
**BOM** — Bill of Materials. A structured list of components in an assembly with part numbers, quantities, and reference designators.
**Catppuccin Mocha** — A dark color palette used for Kindred Create's theme. Part of the [Catppuccin](https://github.com/catppuccin/catppuccin) color scheme family.
**Datum** — Reference geometry (plane, axis, or point) used as a construction aid for modeling. Not a physical shape — used to position features relative to abstract references.
**FCStd** — FreeCAD's standard document format. A ZIP archive containing XML model trees, BREP geometry, thumbnails, and embedded data.
**FileOrigin** — Abstract C++ interface in `src/Gui/` that defines a pluggable file backend. Implementations: `LocalFileOrigin` (filesystem) and `SiloOrigin` (database).
**FreeCAD** — Open-source parametric 3D CAD platform. Kindred Create is based on FreeCAD v1.0.0. Website: <https://www.freecad.org>
**Kindred Create** — The full application: a FreeCAD fork plus Kindred's addon workbenches, theme, and tooling.
**Manipulator** — FreeCAD mechanism for injecting commands from one workbench into another's menus and toolbars. Used by ztools to add commands to PartDesign.
**MinIO** — S3-compatible object storage server. Used by Silo to store binary `.FCStd` files.
**OndselSolver** — Lagrangian constraint solver for the Assembly workbench. Vendored as a submodule from a Kindred fork.
**Origin** — In Kindred Create context: the pluggable file backend system. In FreeCAD context: the coordinate system origin (X/Y/Z axes and planes) of a Part or Body.
**pixi** — Conda-based dependency manager and task runner. Used for all build operations. Website: <https://pixi.sh>
**Preference pack** — FreeCAD mechanism for bundling theme settings, preferences, and stylesheets into an installable package.
**QSS** — Qt Style Sheet. A CSS-like language for styling Qt widgets. Kindred Create's theme is defined in `KindredCreate.qss`.
**rattler-build** — Cross-platform package build tool from the conda ecosystem. Used to create AppImage, DMG, and NSIS installer bundles.
**Silo** — Kindred's parts database system. Consists of a Go server, FreeCAD workbench, and shared Python client library.
**SSE** — Server-Sent Events. HTTP-based protocol for real-time server-to-client notifications. Used by Silo for the activity feed.
**Workbench** — FreeCAD's plugin/module system. Each workbench provides a set of tools, menus, and toolbars for a specific task domain.
**ztools** — Kindred's unified workbench combining Part Design, Assembly, and Sketcher tools with custom datum creation and assembly patterns.
## Repository URLs
| Repository | URL |
|------------|-----|
| Kindred Create | <https://git.kindred-systems.com/kindred/create> |
| ztools | <https://git.kindred-systems.com/forbes/ztools> |
| silo-mod | <https://git.kindred-systems.com/kindred/silo-mod> |
| OndselSolver | <https://git.kindred-systems.com/kindred/solver> |
| GSL | <https://github.com/microsoft/GSL> |
| AddonManager | <https://github.com/FreeCAD/AddonManager> |
| googletest | <https://github.com/google/googletest> |

8
docs/theme/kindred.css vendored Normal file
View File

@@ -0,0 +1,8 @@
/* Kindred Create docs - minor overrides for coal theme */
:root {
--sidebar-width: 280px;
}
.sidebar .sidebar-scrollbox {
padding: 10px 15px;
}

View File

@@ -744,6 +744,33 @@ QGroupBox::title {
background-color: #1e1e2e;
}
QGroupBox::indicator {
width: 18px;
height: 18px;
border: 2px solid #585b70;
border-radius: 4px;
background-color: #313244;
}
QGroupBox::indicator:hover {
border-color: #cba6f7;
}
QGroupBox::indicator:checked {
background-color: #cba6f7;
border-color: #cba6f7;
}
QGroupBox::indicator:checked:disabled {
background-color: #6c7086;
border-color: #6c7086;
}
QGroupBox::indicator:disabled {
background-color: #181825;
border-color: #45475a;
}
/* =============================================================================
Tree View
============================================================================= */
@@ -985,6 +1012,11 @@ QLabel:disabled {
color: #6c7086;
}
/* Hyperlinks — sets QPalette::Link via Application.cpp haslink mechanism */
QLabel[haslink="true"] {
color: #b4befe;
}
/* =============================================================================
Frames
============================================================================= */

View File

@@ -49,7 +49,9 @@ def activePartOrAssembly():
def activeAssembly():
active_assembly = activePartOrAssembly()
if active_assembly is not None and active_assembly.isDerivedFrom("Assembly::AssemblyObject"):
if active_assembly is not None and active_assembly.isDerivedFrom(
"Assembly::AssemblyObject"
):
if active_assembly.ViewObject.isInEditMode():
return active_assembly
@@ -59,7 +61,9 @@ def activeAssembly():
def activePart():
active_part = activePartOrAssembly()
if active_part is not None and not active_part.isDerivedFrom("Assembly::AssemblyObject"):
if active_part is not None and not active_part.isDerivedFrom(
"Assembly::AssemblyObject"
):
return active_part
return None
@@ -120,7 +124,9 @@ def number_of_components_in(assembly):
def isLink(obj):
# If element count is not 0, then its a link group in which case the Link
# is a container and it's the LinkElement that is linking to external doc.
return (obj.TypeId == "App::Link" and obj.ElementCount == 0) or obj.TypeId == "App::LinkElement"
return (
obj.TypeId == "App::Link" and obj.ElementCount == 0
) or obj.TypeId == "App::LinkElement"
def isLinkGroup(obj):
@@ -375,7 +381,9 @@ def getGlobalPlacement(ref, targetObj=None):
if not isRefValid(ref, 1):
return App.Placement()
if targetObj is None: # If no targetObj is given, we consider it's the getObject(ref)
if (
targetObj is None
): # If no targetObj is given, we consider it's the getObject(ref)
targetObj = getObject(ref)
if targetObj is None:
return App.Placement()
@@ -520,11 +528,17 @@ def findElementClosestVertex(ref, mousePos):
for i, edge in enumerate(edges):
curve = edge.Curve
if curve.TypeId == "Part::GeomCircle" or curve.TypeId == "Part::GeomEllipse":
if (
curve.TypeId == "Part::GeomCircle"
or curve.TypeId == "Part::GeomEllipse"
):
center_points.append(curve.Location)
center_points_edge_indexes.append(i)
elif _type == "Part::GeomCylinder" and curve.TypeId == "Part::GeomBSplineCurve":
elif (
_type == "Part::GeomCylinder"
and curve.TypeId == "Part::GeomBSplineCurve"
):
# handle special case of 2 cylinder intersecting.
for j, facej in enumerate(obj.Shape.Faces):
surfacej = facej.Surface
@@ -553,7 +567,9 @@ def findElementClosestVertex(ref, mousePos):
if _type == "Part::GeomCylinder" or _type == "Part::GeomCone":
centerOfG = face.CenterOfGravity - surface.Center
centerPoint = surface.Center + centerOfG
centerPoint = centerPoint + App.Vector().projectToLine(centerOfG, surface.Axis)
centerPoint = centerPoint + App.Vector().projectToLine(
centerOfG, surface.Axis
)
face_points.append(centerPoint)
else:
face_points.append(face.CenterOfGravity)
@@ -623,7 +639,8 @@ def color_from_unsigned(c):
def getJointsOfType(asm, jointTypes):
if not (
asm.isDerivedFrom("Assembly::AssemblyObject") or asm.isDerivedFrom("Assembly::AssemblyLink")
asm.isDerivedFrom("Assembly::AssemblyObject")
or asm.isDerivedFrom("Assembly::AssemblyLink")
):
return []
@@ -763,7 +780,9 @@ def getSubMovingParts(obj, partsAsSolid):
if isLink(obj):
linked_obj = obj.getLinkedObject()
if linked_obj.isDerivedFrom("App::Part") or linked_obj.isDerivedFrom("Part::Feature"):
if linked_obj.isDerivedFrom("App::Part") or linked_obj.isDerivedFrom(
"Part::Feature"
):
return [obj]
return []
@@ -996,7 +1015,7 @@ def findPlacement(ref, ignoreVertex=False):
vtx = getElementName(ref[1][1])
if not elt or not vtx:
# case of whole parts such as PartDesign::Body or App/PartDesign::CordinateSystem/Point/Line/Plane.
# Origin objects (App::Line, App::Plane, App::Point)
if obj.TypeId == "App::Line":
if obj.Role == "X_Axis":
return App.Placement(App.Vector(), App.Rotation(0.5, 0.5, 0.5, 0.5))
@@ -1005,9 +1024,25 @@ def findPlacement(ref, ignoreVertex=False):
if obj.Role == "Z_Axis":
return App.Placement(App.Vector(), App.Rotation(-0.5, 0.5, -0.5, 0.5))
# PartDesign datum planes (including ZTools datums like ZPlane_Mid, ZPlane_Offset)
if obj.TypeId == "App::Plane":
if obj.Role == "XY_Plane":
return App.Placement()
if obj.Role == "XZ_Plane":
return App.Placement(
App.Vector(), App.Rotation(App.Vector(1, 0, 0), -90)
)
if obj.Role == "YZ_Plane":
return App.Placement(
App.Vector(), App.Rotation(App.Vector(0, 1, 0), 90)
)
return App.Placement()
if obj.TypeId == "App::Point":
return App.Placement()
# PartDesign datum planes
if obj.isDerivedFrom("PartDesign::Plane"):
if hasattr(obj, "Shape") and obj.Shape.Faces:
if hasattr(obj, "Shape") and not obj.Shape.isNull() and obj.Shape.Faces:
face = obj.Shape.Faces[0]
surface = face.Surface
plc = App.Placement()
@@ -1015,9 +1050,28 @@ def findPlacement(ref, ignoreVertex=False):
if hasattr(surface, "Rotation") and surface.Rotation is not None:
plc.Rotation = App.Rotation(surface.Rotation)
return obj.Placement.inverse() * plc
return obj.Placement
# PartDesign datum lines
if obj.isDerivedFrom("PartDesign::Line"):
if hasattr(obj, "Shape") and not obj.Shape.isNull() and obj.Shape.Edges:
edge = obj.Shape.Edges[0]
points = getPointsFromVertexes(edge.Vertexes)
mid = (points[0] + points[1]) * 0.5
direction = round_vector(edge.Curve.Direction)
plane = Part.Plane(App.Vector(), direction)
plc = App.Placement()
plc.Base = mid
plc.Rotation = App.Rotation(plane.Rotation)
return obj.Placement.inverse() * plc
return obj.Placement
# PartDesign datum points
if obj.isDerivedFrom("PartDesign::Point"):
if hasattr(obj, "Shape") and not obj.Shape.isNull() and obj.Shape.Vertexes:
plc = App.Placement()
plc.Base = obj.Shape.Vertexes[0].Point
return obj.Placement.inverse() * plc
return obj.Placement
return App.Placement()
@@ -1080,9 +1134,14 @@ def findPlacement(ref, ignoreVertex=False):
if surface.TypeId == "Part::GeomCylinder":
centerOfG = face.CenterOfGravity - surface.Center
centerPoint = surface.Center + centerOfG
centerPoint = centerPoint + App.Vector().projectToLine(centerOfG, surface.Axis)
centerPoint = centerPoint + App.Vector().projectToLine(
centerOfG, surface.Axis
)
plc.Base = centerPoint
elif surface.TypeId == "Part::GeomTorus" or surface.TypeId == "Part::GeomSphere":
elif (
surface.TypeId == "Part::GeomTorus"
or surface.TypeId == "Part::GeomSphere"
):
plc.Base = surface.Center
elif surface.TypeId == "Part::GeomCone":
plc.Base = surface.Apex
@@ -1100,7 +1159,8 @@ def findPlacement(ref, ignoreVertex=False):
plc.Base = (center_point.x, center_point.y, center_point.z)
elif (
surface.TypeId == "Part::GeomCylinder" and curve.TypeId == "Part::GeomBSplineCurve"
surface.TypeId == "Part::GeomCylinder"
and curve.TypeId == "Part::GeomBSplineCurve"
):
# handle special case of 2 cylinder intersecting.
plc.Base = findCylindersIntersection(obj, surface, edge, elt_index)
@@ -1394,13 +1454,16 @@ def generatePropertySettings(documentObject):
commands.append(f"obj.{propertyName} = {propertyValue:.5f}")
elif propertyType == "App::PropertyInt" or propertyType == "App::PropertyBool":
commands.append(f"obj.{propertyName} = {propertyValue}")
elif propertyType == "App::PropertyString" or propertyType == "App::PropertyEnumeration":
elif (
propertyType == "App::PropertyString"
or propertyType == "App::PropertyEnumeration"
):
commands.append(f'obj.{propertyName} = "{propertyValue}"')
elif propertyType == "App::PropertyPlacement":
commands.append(
f"obj.{propertyName} = App.Placement("
f"App.Vector({propertyValue.Base.x:.5f},{propertyValue.Base.y:.5f},{propertyValue.Base.z:.5f}),"
f"App.Rotation(*{[round(n,5) for n in propertyValue.Rotation.getYawPitchRoll()]}))"
f"App.Rotation(*{[round(n, 5) for n in propertyValue.Rotation.getYawPitchRoll()]}))"
)
elif propertyType == "App::PropertyXLinkSubHidden":
commands.append(