Compare commits

..

84 Commits

Author SHA1 Message Date
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
forbes
098f0233c4 art: update silo submodule — add missing command icons (#60)
Some checks failed
Build and Test / build (pull_request) Has been cancelled
Adds silo-tag.svg, silo-rollback.svg, silo-status.svg icons in
Catppuccin Mocha style for Silo_TagProjects, Silo_Rollback, Silo_SetStatus.

Closes #60
2026-02-08 19:35:01 -06:00
68690f3b22 Merge pull request 'fix: update silo submodule — delete_bom_entry error normalization (#59)' (#77) from fix/delete-bom-entry-request into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #77
2026-02-09 01:33:16 +00:00
087fe99324 Merge branch 'main' into fix/delete-bom-entry-request
Some checks failed
Build and Test / build (pull_request) Has been cancelled
2026-02-09 01:33:06 +00:00
7033744656 Merge pull request 'fix: update ztools submodule — robust PartDesign menu insertion (#57)' (#72) from fix/menu-insertion-fragility into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #72
2026-02-09 01:32:56 +00:00
forbes
84c1776f92 fix: update ztools submodule — robust PartDesign menu insertion (#57)
Some checks failed
Build and Test / build (pull_request) Has been cancelled
Replaces fragile chained insert operations in modifyMenuBar() with
independent append operations using PartDesign_Body as parent-locator.

Closes #57
2026-02-08 19:32:31 -06:00
f391c526cd Merge pull request 'fix: update ztools submodule — eager command registration (#52)' (#71) from fix/manipulator-timing into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #71
2026-02-09 01:28:33 +00:00
ac4f01c5c0 Merge branch 'main' into fix/manipulator-timing
Some checks failed
Build and Test / build (pull_request) Has been cancelled
2026-02-09 01:28:20 +00:00
1d2a11572d Merge pull request 'fix(theme): eliminate QSS/CFG duplication (#51)' (#70) from fix/qss-duplication into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #70
2026-02-09 01:28:07 +00:00
forbes
b3a6c9d925 merge: resolve conflicts with main (docs)
Some checks failed
Build and Test / build (pull_request) Has been cancelled
2026-02-08 19:27:52 -06:00
7c84eeb68d Merge pull request 'docs: repository cleanup and documentation update' (#50) from chore/repo-cleanup-docs into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #50
2026-02-09 01:26:31 +00:00
forbes
590cb9f6c3 fix: update silo submodule — delete_bom_entry error normalization (#59)
Some checks failed
Build and Test / build (pull_request) Has been cancelled
2026-02-08 18:30:07 -06:00
forbes
0a0ac547ef fix: update ztools submodule — eager command registration (#52)
Some checks failed
Build and Test / build (pull_request) Has been cancelled
Updates ztools to include the manipulator timing fix. Commands and the
PartDesign manipulator are now registered at module scope instead of
during ZToolsWorkbench.Initialize(), ensuring ztools buttons appear in
PartDesign regardless of workbench activation order.
2026-02-08 17:58:29 -06:00
forbes
6773ca0dfd fix(theme): eliminate QSS/CFG duplication (#51)
Some checks failed
Build and Test / build (pull_request) Has been cancelled
- Make src/Gui/Stylesheets/KindredCreate.qss the single source of truth
- Generate PreferencePacks QSS copy at build time via configure_file()
- Merge missing preference defaults into PreferencePacks cfg:
  Document (undo, autosave, backup, license), TreeView (PreSelection,
  SyncView, SyncSelection), NotificationArea, OutputWindow recording,
  General (AutoloadModule=ZToolsWorkbench)
- Delete unused resources/preferences/KindredCreate/ directory
- Delete src/Gui/PreferencePacks/KindredCreate/KindredCreate.qss from source
- Update documentation to reflect single QSS location
2026-02-08 17:41:26 -06:00
forbes
82e7362a56 docs: repository cleanup and documentation update
Some checks failed
Build and Test / build (pull_request) Has been cancelled
- Rewrite CONTRIBUTING.md with Kindred-specific contribution guide
  (branch workflow, commit conventions, submodule management, QSS procedures)
- Update README.md with Origin system, update checker, and preference defaults
- Update OVERVIEW.md with current submodule pins and date
- Update KNOWN_ISSUES.md with resolved items and new Silo features
- Update COMPONENTS.md with SSE, Activity panel, Start Panel, theme additions
- Update INTEGRATION_PLAN.md phase status (theme, Silo, build system)
- Update ARCHITECTURE.md bootstrap flow and source layout
2026-02-08 17:22:59 -06:00
cf523f1d87 Merge pull request 'fix(build): fix DlgSettingsGeneral::applyMenuIconSize visibility and namespace' (#49) from fix/build-menu-icon-size into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #49
2026-02-08 23:05:10 +00:00
forbes
4bf74cf339 fix(build): fix DlgSettingsGeneral::applyMenuIconSize visibility and namespace
Some checks failed
Build and Test / build (pull_request) Has been cancelled
- Move applyMenuIconSize to public access so StartupProcess can call it
- Add Dialog:: namespace qualifier in StartupProcess.cpp
2026-02-08 17:04:41 -06:00
3b07a0f99b Merge pull request 'chore: update silo submodule with SSE, start panel, and activity pane' (#48) from chore/update-silo-submodule into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #48
2026-02-08 22:26:36 +00:00
forbes
9cabb29824 chore: update silo submodule to latest main
Some checks failed
Build and Test / build (pull_request) Has been cancelled
New features:
- SSE reconnect with exponential backoff and terminal state handling
- Silo_Diag connection diagnostics command
- Server mode UI reflection in client
- Silo-aware start panel
- Database Activity pane with comments, interaction, and badges
2026-02-08 16:25:59 -06:00
793e6bdd49 Merge pull request 'fix(gui): UI appearance polish - Wayland scaling, menu icon size pref, dialog cleanup' (#47) from fix/ui-appearance-polish into main
Some checks failed
Build and Test / build (push) Failing after 14m32s
Reviewed-on: #47
2026-02-08 21:37:03 +00:00
b6ab14cc6f Merge branch 'main' into fix/ui-appearance-polish
Some checks failed
Build and Test / build (pull_request) Failing after 17m32s
2026-02-08 21:36:51 +00:00
da46073d68 Merge pull request 'feat: expose version to Python and add update checker (#28, #29)' (#45) from feat/update-checker into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #45
2026-02-08 21:36:40 +00:00
forbes
262dfa583d fix(defaults): tune Document, Selection, and Notification preference defaults
Some checks failed
Build and Test / build (pull_request) Failing after 12m28s
Document (fixes #37):
- Increase max undo/redo steps from 20 to 50
- Reduce auto-save interval from 15 to 5 minutes
- Increase max backup files from 1 to 3
- Set license to Other with blank URL (removes Wikipedia link)

Selection (fixes #38):
- Enable preselect-on-tree-hover (PreSelection)
- Enable auto-switch to 3D view (SyncView)
- Enable auto-expand tree item (SyncSelection)

Notification Area (fixes #40):
- Reduce max notification list from 1000 to 100
- Reduce max concurrent popups from 15 to 3
- Reduce bubble width from 800px to 400px
- Reduce max popup duration from 20s to 10s
- Reduce min popup duration from 5s to 3s
2026-02-08 13:36:57 -06:00
forbes
cc5ba638d1 fix(gui): UI appearance polish — Wayland scaling, menu icon size pref, dialog cleanup
Some checks failed
Build and Test / build (pull_request) Failing after 13m13s
- Fix hazy text on Wayland by setting QT_SCALE_FACTOR_ROUNDING_POLICY=RoundPreferFloor
  and QT_ENABLE_HIGHDPI_SCALING=1 in deb and AppImage launchers; remove forced
  QT_QPA_PLATFORM=xcb from AppImage to allow native Wayland (fixes #33)

- Add menu icon size preference selector (Small/Medium/Large/Extra large) in
  General preferences, applied via QMenu stylesheet at startup and on save (fixes #46)

- Replace verbose theme customization banner with concise 'Kindred Create Theme'
  label in UI preferences; split Overlay group into 'Panel Visibility' and
  'Overlay Interaction' sections (fixes #35)

- Remove Addon Manager branding label from General preferences, change recent
  file list default from 4 to 10 (fixes #36)
2026-02-08 13:27:22 -06:00
forbes
35302154ae feat: expose version to Python and add update checker (#28, #29)
All checks were successful
Build and Test / build (pull_request) Successful in 1h11m28s
Issue #28: Add version.py.in CMake template that injects
KINDRED_CREATE_VERSION at build time, making the Kindred Create
version available to Python code via 'from version import VERSION'.

Issue #29: Add update_checker.py that queries the Gitea releases
API on startup (10s deferred) to check for newer versions. Uses
stdlib urllib only, 5s timeout, never blocks the UI. Respects
user preferences for check interval, enable/disable, and skipped
versions. Logs results to Console for now — UI notification will
follow in issue #30.

Closes #28
Closes #29
2026-02-08 13:11:08 -06:00
forbes
be7f1d9221 fix: update silo submodule — fix origin registration import
All checks were successful
Build and Test / build (push) Successful in 1h7m24s
Silo origin was not appearing in the origin selector because
silo_origin.py used a relative import (from .silo_commands) which
fails since the directory is not a Python package. Updated submodule
includes the fix to use absolute imports.
2026-02-08 10:36:14 -06:00
forbes
10b5c9d584 fix(gui): wire Manage Origins dialog to Silo_Settings command
Some checks failed
Build and Test / build (push) Has been cancelled
Replace stub QMessageBox in Add Silo and Edit Origin buttons with
actual calls to the Silo_Settings command, so users can configure
their Silo connection from the origin manager dialog.
2026-02-08 10:31:41 -06:00
forbes
ee839c23b8 fix(ci): add error handling to release creation API calls
Some checks failed
Build and Test / build (push) Has been cancelled
Release Build / build-linux (push) Successful in 1h26m59s
Release Build / publish-release (push) Has been cancelled
Print HTTP status and response body on failure instead of crashing
with a cryptic KeyError when the Gitea API returns an error.
2026-02-08 10:22:26 -06:00
forbes
67f825c305 fix(ci): filter dereferenced tag refs from git ls-remote output
Some checks failed
Build and Test / build (push) Successful in 1h7m46s
Release Build / build-linux (push) Successful in 1h25m16s
Release Build / publish-release (push) Failing after 5m17s
2026-02-07 22:22:55 -06:00
440df2a9be Merge pull request 'fix(gui): merge Silo toolbar into File toolbar via origin system (#65)' (#27) from fix/merge-silo-toolbar into main
Some checks failed
Build and Test / build (push) Failing after 1m33s
Release Build / build-linux (push) Failing after 1m58s
Release Build / publish-release (push) Has been skipped
2026-02-08 03:29:24 +00:00
forbes
1750949afd fix(gui): merge Silo toolbar into File toolbar via origin system (#65)
Some checks failed
Build and Test / build (pull_request) Failing after 1m41s
- Remove separate Silo workbench toolbar (now redundant)
- Remove SiloMenuManipulator (Std commands already delegate to origins)
- Remove Silo_ToggleMode (origin selector handles mode switching)
- Register Silo origin at startup via Create module
- Update docs to reflect unified origin architecture
2026-02-07 21:29:05 -06:00
b2c6fc2ebc Merge pull request 'fix(silo): workbench bug fixes and submodule updates' (#26) from fix/silo-workbench-bugs into main
Some checks failed
Build and Test / build (push) Failing after 1m52s
2026-02-08 03:11:07 +00:00
forbes
d0c67455dc chore: update ztools and OndselSolver submodules
Some checks failed
Build and Test / build (pull_request) Failing after 3m25s
2026-02-07 21:01:00 -06:00
forbes
62906b0269 test(gui): add unit tests for OriginManager and LocalFileOrigin
Some checks failed
Build and Test / build (push) Has been cancelled
Add 20 GTest cases covering the Kindred Origin system:

- LocalFileOriginTest: identity methods and capability flags
- OriginManagerTest: registration lifecycle, current origin
  selection, signal emissions, and fallback behavior
- OriginManagerDocTest: document ownership via SiloItemId
  property detection, origin association, and null safety

Uses MockOrigin stub for testing OriginManager without real
PLM backends.
2026-02-07 20:56:08 -06:00
99bc7629e7 Merge pull request 'fix(gui): widen origin selector widget and update silo submodule' (#25) from fix/silo-workbench-bugs into main
All checks were successful
Build and Test / build (push) Successful in 1h3m36s
Reviewed-on: #25
2026-02-07 20:34:56 +00:00
forbes
d95c850b7b fix(gui): widen origin selector widget and update silo submodule
Some checks failed
Build and Test / build (pull_request) Has been cancelled
- Increase OriginSelectorWidget max width from 120 to 160px for longer
  origin names
- Set explicit SizePolicy::Preferred for better toolbar layout
- Update silo submodule to fix/silo-workbench-bugs with auth and menu fixes
2026-02-07 14:34:23 -06:00
1f7dae4f11 Merge pull request 'art: update kindred icon set' (#24) from art/update-kindred-icons into main
All checks were successful
Build and Test / build (push) Successful in 1h5m8s
Reviewed-on: #24
2026-02-07 16:45:30 +00:00
1d7e4e2eef Merge pull request 'fix(ci): fetch only latest tag, add patchelf dep, update docs' (#23) from docs/update-ci-and-overview into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #23
2026-02-07 16:45:20 +00:00
forbes
fe50b1595e art: update kindred icon set
Some checks failed
Build and Test / build (pull_request) Has been cancelled
Update Document, DrawStyle, Link, PartDesign, and document-* SVGs.
Remove DrawStyleHiddenLine.svg.
2026-02-07 10:44:24 -06:00
forbes
0e5a259d14 fix(ci): fetch only latest tag and add patchelf build dep
Some checks failed
Build and Test / build (pull_request) Has been cancelled
- Change both build.yml and release.yml to fetch only the latest v* tag
  via git ls-remote instead of fetching all tags
- Add patchelf as a Linux build dependency in recipe.yaml to fix
  rattler-build packaging failure
2026-02-07 10:42:48 -06:00
forbes
bfb2728f8d docs: update OVERVIEW.md and CI_CD.md to match current repo state
- Fix stale submodule URLs (gitea.kindred.internal -> git.kindred-systems.com)
- Update silo submodule name and commit, add note about repo split
- Document actual release mechanism (curl API, not release-action)
- Mark macOS/Windows builds as disabled in platform matrix
- Update ccache docs: date-based key rotation, rattler-build env forwarding
- Document disk cleanup steps and runner cleanup daemon
- Update appimagetool extraction note (FUSE unavailable in containers)
2026-02-07 09:34:41 -06:00
forbes
7bec3d5c3b fix(ci): fix release creation and artifact collection
All checks were successful
Build and Test / build (push) Successful in 1h5m11s
- Exclude appimagetool from artifact upload (FreeCAD_*.AppImage glob)
- Build release JSON payload in Python to fix false/False type error
  (shell 'false' is not valid Python; use bool with re.search instead)
2026-02-07 08:20:24 -06:00
forbes
145c29d7d6 fix(build): pin icu<76 in build deps to match host
The build environment (via qt6-main) was pulling ICU 78 headers while
the host/runtime environment had ICU 75 pinned. The compiled binary
contained icu_78 mangled symbols that fail to resolve against ICU 75
shared libs at runtime. Pin icu>=75,<76 in build deps to match.
2026-02-07 08:18:46 -06:00
forbes
8ba7b73aa8 fix(ci): fix ccache strategy and add runner cleanup
- Cache key: replace run_id (unique per build) with date-based key
  so entries are reused within the same day and rotate daily
- Skip cache save on exact hit (act_runner can't overwrite keys)
- build.sh: set CCACHE_DIR/CCACHE_BASEDIR inside rattler-build's
  isolated env so release builds actually use the cached directory
- build.sh: print ccache stats at end for diagnostics
- Add disk space cleanup step to build.yml (matching release.yml)
- Remove cross-build cache fallback in release.yml (different -O flags)
- Add runner cleanup daemon (.gitea/runner/) with systemd timer
  to purge stale cache entries, Docker data, and old workspaces
2026-02-07 08:18:00 -06:00
forbes
1e4deea130 fix(ci): add disk space cleanup steps to release workflow
Some checks failed
Build and Test / build (push) Failing after 15m24s
The runner host disk is at 95% capacity. Add cleanup steps:
- Remove pre-installed bloat (dotnet, android, boost) at start
- Clear rattler package cache and pixi build dirs between
  AppImage and .deb build steps
2026-02-06 20:01:33 -06:00
forbes
3923e2e4b9 fix(ci): replace release-action with direct Gitea API calls
Some checks failed
Build and Test / build (push) Has been cancelled
Release Build / build-linux (push) Failing after 1h25m19s
Release Build / publish-release (push) Has been skipped
The gitea.com/actions/release-action is archived and requires go to
build from source, which isn't available in the ubuntu-latest runner.
Replace with curl calls to the Gitea release API: create release,
upload assets. Handles existing releases by deleting and recreating.
2026-02-06 18:22:23 -06:00
forbes
9e29b76fbc fix(build): pin icu >=75,<76 to prevent header/lib version mismatch
All checks were successful
Build and Test / build (push) Successful in 1h18m21s
The build environment resolved ICU 78 headers while the host environment
had ICU 75 libraries (constrained by xerces-c, qt6-main, etc.). This
caused libFreeCADBase.so to contain icu_78 mangled symbols that couldn't
be found in the bundled ICU 75 libs at runtime:

  undefined symbol: _ZN6icu_7813UnicodeString8fromUTF8ENS_11StringPieceE

Pin ICU explicitly in both recipe.yaml host deps and pixi.toml to ensure
headers and libs always match.
2026-02-06 16:48:03 -06:00
forbes
88e025f1c6 docs: update stale silo/pkg/freecad paths to silo-mod layout
Some checks failed
Build and Test / build (push) Successful in 1h24m15s
Release Build / build-linux (push) Successful in 2h8m56s
Release Build / publish-release (push) Failing after 11m58s
2026-02-06 12:43:04 -06:00
forbes
772d3b3288 fix(build): update CMake install paths for silo-mod repo layout
Some checks failed
Build and Test / build (push) Has been cancelled
The silo submodule now uses silo-mod layout where the FreeCAD workbench
is at freecad/ (not pkg/freecad/). Also install the silo-client
submodule directory.
2026-02-06 12:42:02 -06:00
forbes
dfa2b73966 fix(ci): extract appimagetool for FUSE-less containers
Some checks failed
Build and Test / build (push) Has been cancelled
appimagetool is an AppImage itself and requires FUSE to self-mount.
CI containers typically don't have FUSE. Extract it with
--appimage-extract and run squashfs-root/AppRun instead.
2026-02-06 11:33:33 -06:00
forbes
056b015e78 chore: update mods/silo submodule to silo-mod initial commit
Some checks failed
Build and Test / build (push) Has been cancelled
2026-02-06 11:25:15 -06:00
6649372f7b Merge pull request 'refactor: rewire silo submodule for monorepo split' (#19) from refactor/silo-split into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #19
2026-02-06 17:23:08 +00:00
5db68dab25 Merge pull request 'docs: split REPOSITORY_STATE.md into topic files' (#18) from docs/split-repository-state into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #18
2026-02-06 17:22:42 +00:00
forbes
c59c704da3 refactor: rewire silo submodule for silo-mod split
Some checks failed
Build and Test / build (pull_request) Has been cancelled
- .gitmodules: silo.git -> silo-mod.git (FreeCAD workbench only)
- Init.py: silo/pkg/freecad -> silo/freecad (new repo layout)
- InitGui.py: same path update

The silo monorepo has been split into:
- silo-client: shared Python API client (submodule of silo-mod)
- silo-mod: FreeCAD workbench (this submodule)
- silo-calc: LibreOffice Calc extension (separate repo)
- silo: server only (no longer a Create submodule)
2026-02-06 11:15:30 -06:00
forbes
a2200b4042 docs: split REPOSITORY_STATE.md into topic files
Some checks failed
Build and Test / build (pull_request) Has been cancelled
Replace the monolithic REPOSITORY_STATE.md with four focused files:

- OVERVIEW.md: metadata, submodule pins, doc index
- ARCHITECTURE.md: bootstrap flow, source layout
- COMPONENTS.md: ztools, Silo, Origin commands, theme, icons
- KNOWN_ISSUES.md: bugs, incomplete features, next steps

Updates reflected in the split:
- Silo auth: corrected from 'not implemented' to 'local auth
  complete; LDAP/OIDC pending infrastructure'
- CSRF: corrected from 'not implemented' to 'implemented (nosurf)'
- Silo commands: 14 (was 13, added Silo_Auth)
- New Origin commands section (5 C++ commands)
- New icon infrastructure section with missing icon tracking
- New issues: Newton-Raphson convergence fix (#12), Assembly
  restore crash fix (#13), missing Silo icons (#11)
- Updated submodule pins (silo 27e112e, OndselSolver 5d1988b)
2026-02-06 10:43:34 -06:00
forbes
c858706d48 fix(gui): add silo-* icons to Qt resource file for origin commands
Some checks failed
Build and Test / build (push) Successful in 1h14m34s
Release Build / build-linux (push) Failing after 1h33m10s
Release Build / publish-release (push) Has been skipped
Copy silo-bom, silo-commit, silo-info, silo-pull, and silo-push SVG
icons from mods/silo/pkg/freecad/resources/icons/ into src/Gui/Icons/
and register them in resource.qrc so they are compiled into the binary
and discoverable by BitmapFactory at runtime.
2026-02-05 22:00:35 -06:00
forbes
724440dcb7 fix(gui): fix build errors in OriginSelectorWidget and OriginManager
Some checks failed
Build and Test / build (push) Has been cancelled
- OriginSelectorWidget: use BitmapFactoryInst::mergePixmap() instead of
  BitmapFactory::mergePixmap() since BitmapFactory is a function, not a class
- OriginManager: use Console().log() instead of Console().Log() which
  does not exist on ConsoleSingleton
2026-02-05 20:48:28 -06:00
forbes
2f594dac0a fix(gui): use Python API for viewDefaultOrientation in StdCmdNew
Some checks failed
Build and Test / build (push) Failing after 20m15s
viewDefaultOrientation is a method on View3DInventorPy (Python wrapper),
not on View3DInventorViewer (C++ viewer). Use doCommand with the Python
API, matching the pattern used in StdCmdViewHome.
2026-02-05 19:13:45 -06:00
forbes
939b81385e fix(build): pin swig <4.4 for pivy runtime compatibility
Some checks failed
Build and Test / build (push) Failing after 19m41s
SWIG 4.4.x uses runtime API version 5, which is incompatible with
conda-forge pivy 0.6.9 built with SWIG <=4.3.x (runtime version 4).
Pin to >=4.0,<4.4 to ensure matching runtime versions.
2026-02-05 18:38:19 -06:00
forbes
84b69b935b fix(build): remove non-existent SelectModule.h include in FileOrigin.cpp
Some checks failed
Build and Test / build (push) Failing after 19m45s
Release Build / publish-release (push) Has been skipped
Release Build / build-linux (push) Failing after 31m20s
SelectModule is declared in FileDialog.h which was already included.
The separate #include "SelectModule.h" referenced a header that
doesn't exist, causing a fatal build error.
2026-02-05 16:53:14 -06:00
a6e84552da feat(gui): add cross-origin detection in StdCmdSaveAs (#17)
Some checks failed
Build and Test / build (push) Failing after 12m50s
- Detect document's origin vs current (target) origin
- Route to appropriate workflow:
  - Same origin: standard SaveAs
  - Local → PLM: migration (handled by PLM origin)
  - PLM → Local: export (handled by local origin)
  - PLM → PLM: transfer (handled by target PLM origin)

This provides the infrastructure for Issue #17: Mixed origin workflows.
The actual migration/export dialogs will be implemented in the
respective origin adapters (SiloOrigin, LocalFileOrigin).
2026-02-05 14:54:36 -06:00
015df38328 feat(gui): add document-origin tracking and display in window title (#16)
Some checks failed
Build and Test / build (push) Has been cancelled
- Add originForDocument(), setDocumentOrigin(), clearDocumentOrigin()
  methods to OriginManager
- Add signalDocumentOriginChanged signal for UI updates
- Add document-to-origin tracking map in OriginManager
- Update MDIView::buildWindowTitle() to append origin suffix for
  non-local origins (e.g., 'Part001 [Silo]')

This implements Issue #16: Document origin tracking and display
2026-02-05 14:53:45 -06:00
db85277f26 feat(gui): add OriginManagerDialog for managing file origins (#15)
Some checks failed
Build and Test / build (push) Has been cancelled
- Create OriginManagerDialog with list of configured origins
- Show connection status for remote origins
- Allow setting default origin
- Add/Edit/Remove buttons (Add/Edit show placeholder for now)
- Wire up 'Manage Origins...' button in OriginSelectorWidget
- Prevent removal of built-in local origin

Part of Issue #15: Multi-instance Silo configuration UI
2026-02-05 14:51:30 -06:00
679aaec6d4 feat(gui): add unified origin commands for PLM operations (#14)
Some checks are pending
Build and Test / build (push) Has started running
- Create CommandOrigin.cpp with Origin_Commit, Origin_Pull, Origin_Push,
  Origin_Info, Origin_BOM commands
- Commands delegate to current origin's extended operations
- Commands auto-disable based on origin capabilities (supportsRevisions,
  supportsBOM, supportsPartNumbers)
- Add 'Origin Tools' toolbar with PLM operations
- Add origin commands to File menu
- Register commands via CreateOriginCommands() in Application startup

This implements Issue #14: Dynamic toolbar extension for Silo commands
2026-02-05 14:49:22 -06:00
deeb6376f7 feat(gui): add OriginSelectorWidget for file origin selection (#13)
Some checks failed
Build and Test / build (push) Has been cancelled
- Create OriginSelectorWidget class (QToolButton with dropdown menu)
- Add OriginSelectorAction to create widget in toolbars
- Add Std_Origin command registered in CommandStd.cpp
- Add widget to File toolbar (before New/Open/Save)
- Connect to OriginManager fastsignals for origin changes
- Add Catppuccin Mocha styling for the widget
- Widget shows current origin name/icon with connection status overlay

This implements Issue #13: Origin selector toolbar widget
2026-02-05 14:47:18 -06:00
103fc28bc6 ci: retrigger after pushing silo submodule
Some checks failed
Build and Test / build (push) Has been cancelled
2026-02-05 14:35:59 -06:00
79c85ed2e5 fix(gui): add interactive methods to FileOriginPython and fix Console API calls
Some checks failed
Build and Test / build (push) Failing after 2m13s
- Add openDocumentInteractive() and saveDocumentAsInteractive() to FileOriginPython
- Fix Console API: Warning -> warning, Error -> error in OriginManager.cpp
- These methods bridge Python origins to the new interactive document operations
2026-02-05 14:25:21 -06:00
38358e431d feat(gui): implement issues #10 and #12 - LocalFileOrigin and Std_* delegation
Issue #10: Local filesystem origin implementation
- Add openDocumentInteractive() method to FileOrigin interface for UI-based
  file opening (shows file dialog)
- Add saveDocumentAsInteractive() method for UI-based save as
- Implement LocalFileOrigin::openDocumentInteractive() with full file dialog
  support, filter list building, and module handler integration
- Implement LocalFileOrigin::saveDocumentAsInteractive() delegating to
  Gui::Document::saveAs()

Issue #12: Modify Std_* commands to delegate to current origin
- StdCmdNew::activated() now delegates to origin->newDocument() and sets
  up view orientation for the new document
- StdCmdOpen::activated() delegates to origin->openDocumentInteractive()
  with connection state checking for authenticated origins
- StdCmdSave::activated() uses document's owning origin (via findOwningOrigin)
  for save, falling back to saveDocumentAsInteractive if no filename
- StdCmdSaveAs::activated() delegates to origin->saveDocumentAsInteractive()
- Updated isActive() methods to check for active document

The Std_* commands now work seamlessly with both LocalFileOrigin and
SiloOrigin. When Local origin is selected, standard file dialogs appear.
When Silo origin is selected, Silo's search/creation dialogs appear.

Import and Export commands are left unchanged as they operate on document
content rather than document lifecycle.

Closes #10, Closes #12
2026-02-05 14:02:26 -06:00
5319387030 fix(ci): add --force to tag fetch, fix ccache keys, disable Windows/macOS builds
Some checks failed
Build and Test / build (push) Failing after 16m39s
Release Build / publish-release (push) Has been cancelled
Release Build / build-linux (push) Has been cancelled
- build.yml: Add --force flag to tag fetch to prevent 'would clobber
  existing tag' error when 'latest' tag already exists locally
- build.yml: Use github.run_id instead of github.sha for ccache keys
  to ensure unique keys per workflow run while still benefiting from
  restore-key prefix matching

- release.yml: Add --force flag to tag fetch commands
- release.yml: Use github.run_id for ccache keys (same reason)
- release.yml: Comment out build-macos and build-windows jobs since
  no native runners are available for these platforms
- release.yml: Update publish-release to only depend on build-linux
- release.yml: Update release notes to indicate macOS/Windows builds
  are not yet available

The ccache key strategy now works correctly with immutable caches:
- Save with unique key: ccache-{workflow}-{branch}-{run_id}
- Restore with prefix fallback: tries same branch first, then main

The macOS and Windows jobs require platform-specific tooling:
- macOS: dmgbuild, pyobjc-framework-Quartz for DMG creation
- Windows: NSIS, Visual Studio toolchain for installer creation

These cannot be easily cross-compiled from Linux. The jobs are
preserved as comments so they can be re-enabled when native runners
become available or when cross-compilation tooling is set up.
2026-02-05 13:41:07 -06:00
405e04bd3e chore: update silo submodule with origin adapter
Some checks are pending
Build and Test / build (push) Has started running
Updates silo submodule to include:
- SiloOrigin adapter implementing FileOrigin interface (#11)
- UUID tracking via SiloItemId property
- Automatic origin registration on workbench init

Refs: #11
2026-02-05 13:29:54 -06:00
7535a48ec4 feat(gui): add origin abstraction layer for unified file operations
Implements Issue #9: Origin abstraction layer

This commit introduces a foundational abstraction for document origins,
enabling FreeCAD to work with different storage backends (local filesystem,
Silo PLM, future cloud services) through a unified interface.

## Core Components

### FileOrigin Abstract Base Class (FileOrigin.h/cpp)
- Defines interface for document origin handlers
- Identity methods: id(), name(), nickname(), icon(), type()
- Workflow characteristics: tracksExternally(), requiresAuthentication()
- Capability queries: supportsRevisions(), supportsBOM(), supportsPartNumbers()
- Connection state management with fastsignals notifications
- Document identity: documentIdentity() returns UUID, documentDisplayId() for display
- Property sync: syncProperties() for bidirectional database sync
- Core operations: newDocument(), openDocument(), saveDocument(), saveDocumentAs()
- Extended PLM operations: commitDocument(), pullDocument(), pushDocument(), etc.

### LocalFileOrigin Implementation
- Default origin for local filesystem documents
- ownsDocument(): Returns true if document has NO SiloItemId property
- Wraps existing FreeCAD file operations (App::GetApplication())

### OriginManager Singleton (OriginManager.h/cpp)
- Follows WorkbenchManager pattern (instance()/destruct())
- Manages registered FileOrigin instances
- Tracks current origin selection with persistence
- Provides document-to-origin resolution via findOwningOrigin()
- Emits signals: signalOriginRegistered, signalOriginUnregistered,
  signalCurrentOriginChanged
- Preferences stored at: User parameter:BaseApp/Preferences/General/Origin

### Python Bindings (FileOriginPython.h/cpp)
- Adapts Python objects to FileOrigin C++ interface
- Enables Silo addon to implement origins in Python
- Thread-safe with Base::PyGILStateLocker
- Static addOrigin()/removeOrigin() for registration

### Python API (ApplicationPy.cpp)
- FreeCADGui.addOrigin(obj) - Register Python origin
- FreeCADGui.removeOrigin(obj) - Unregister Python origin
- FreeCADGui.getOrigin(id) - Get origin info as dict
- FreeCADGui.listOrigins() - List all registered origin IDs
- FreeCADGui.activeOrigin() - Get current origin info
- FreeCADGui.setActiveOrigin(id) - Set active origin

## Design Decisions

1. **UUID Tracking**: Documents tracked by SiloItemId (immutable UUID),
   SiloPartNumber used for human-readable display only

2. **Ownership by Properties**: Origin ownership determined by document
   properties (SiloItemId), not file path location

3. **Local Storage Always**: All documents saved locally; origins change
   workflow and identity model, not storage location

4. **Property Syncing**: syncProperties() enables bidirectional sync of
   document metadata with database (Description, SourcingType, etc.)

## Files Added
- src/Gui/FileOrigin.h
- src/Gui/FileOrigin.cpp
- src/Gui/FileOriginPython.h
- src/Gui/FileOriginPython.cpp
- src/Gui/OriginManager.h
- src/Gui/OriginManager.cpp

## Files Modified
- src/Gui/CMakeLists.txt - Added new source files
- src/Gui/Application.cpp - Initialize/destruct OriginManager
- src/Gui/ApplicationPy.h - Added Python method declarations
- src/Gui/ApplicationPy.cpp - Added Python method implementations

Refs: #9
2026-02-05 13:17:23 -06:00
1c309a0ca8 feat(icons): complete Phase 3 icon migration (#6)
All checks were successful
Build and Test / build (push) Successful in 1h13m23s
This completes the Kindred Create icon set with 191 Catppuccin Mocha themed SVG icons.

New icon categories added:
- DrawStyle icons (7): Wireframe, Shaded, FlatLines, etc.
- View orientation icons (12): Front, Rear, Top, Bottom, etc.
- Tree view icons (15): Tree_*, tree-* for document tree
- Link & Feature icons (14): Link*, Feature, Group, etc.
- Button/navigation icons (11): button_*, edit_OK, edit_Cancel
- Cursor icons (4): pan, rotate, zoom, through
- Selection icons (8): edge, face, vertex, clear-selection
- DAG view icons (4): Pass, Fail, Pending, Visible
- Std_* view/toggle icons (15): ShowObjects, HideObjects, etc.
- Utility icons (25+): Document, folder, info, Warning, etc.

All icons follow the design standards:
- 32x32 viewBox with rx=4 rounded background
- Catppuccin Mocha color palette
- Category-based color coding:
  - Blue: File operations
  - Green: Edit/Creation operations
  - Yellow/Peach: View operations
  - Mauve/Lavender: System/Settings
  - Red: Destructive actions

Closes #6
2026-02-05 12:05:58 -06:00
2d7735b4c1 feat(icons): add Phase 2 workbench icons in Catppuccin Mocha
Some checks failed
Build and Test / build (push) Has been cancelled
Add 32 workbench-related icons with consistent color coding:

Workbench Selectors (8):
- PartDesignWorkbench (Blue #89b4fa)
- SketcherWorkbench (Yellow #f9e2af)
- AssemblyWorkbench (Green #a6e3a1)
- PartWorkbench (Blue #89b4fa)
- TechDrawWorkbench (Mauve #cba6f7)
- SpreadsheetWorkbench (Sky #89dceb)
- MeshWorkbench (Pink #f5c2e7)
- DraftWorkbench (Peach #fab387)

Part Design Tools (8):
- PartDesign_Body, PartDesign_NewSketch
- PartDesign_Pad, PartDesign_Pocket, PartDesign_Revolution
- PartDesign_Hole, PartDesign_Fillet, PartDesign_Chamfer

Sketcher Tools (5):
- Sketcher_CreateLine, Sketcher_CreateRectangle
- Sketcher_CreateCircle, Sketcher_CreateArc, Sketcher_CreatePoint

Sketcher Constraints (5):
- Constraint_PointOnPoint (Coincident)
- Constraint_Horizontal, Constraint_Vertical
- Constraint_Perpendicular, Constraint_Dimension

Assembly Tools (6):
- Assembly_CreateAssembly, Assembly_InsertLink
- Assembly_CreateJointFixed, Assembly_CreateJointRevolute
- Assembly_CreateJointSlider, Assembly_CreateJointDistance

Ref #5
2026-02-05 11:36:53 -06:00
69414c5dc5 refactor(icons): update color scheme for View and System icons
Some checks are pending
Build and Test / build (push) Has started running
Change color assignments for better semantic meaning:
- View operations: Yellow (#f9e2af) / Peach (#fab387)
- System/Settings: Mauve (#cba6f7) / Lavender (#b4befe)

Updated icons:
- View: zoom-in, zoom-out, zoom-fit-best, view-refresh, view-fullscreen,
        Std_ViewHome, Std_ViewScreenShot, Std_ToggleVisibility
- System: preferences-system, help-browser, application-exit, Std_Refresh

Also updated README.md color usage documentation.
2026-02-05 11:28:44 -06:00
c28d6f92cf feat(icons): add Phase 1 core toolbar icons in Catppuccin Mocha
Some checks failed
Build and Test / build (push) Has been cancelled
Complete the core toolbar icon set with 15 additional icons:

File Operations:
- document-save-as.svg - Save As with pencil indicator
- Std_SaveAll.svg - Save All with stacked disks
- Std_Import.svg - Import with arrow into document
- Std_Export.svg - Export with arrow out of document
- document-print.svg - Printer with paper

Edit Operations:
- edit-select-all.svg - Selection rectangle with corner handles

View Operations:
- zoom-fit-best.svg - Fit to view with corner arrows
- Std_ViewHome.svg - Home view with house icon
- view-fullscreen.svg - Fullscreen expand arrows
- Std_ViewScreenShot.svg - Camera for screenshots
- Std_ToggleVisibility.svg - Eye icon for visibility
- Std_Refresh.svg - Circular refresh arrow

Common Actions:
- help-browser.svg - Question mark in circle
- application-exit.svg - Door with exit arrow
- Std_DuplicateSelection.svg - Duplicate objects

All icons follow the Catppuccin Mocha design system with:
- 32x32 viewBox
- Rounded rectangle background (surface0 #313244)
- Consistent stroke widths and color usage

Ref #4
2026-02-05 11:23:47 -06:00
224feda4ad feat(icons): add Catppuccin Mocha icon override infrastructure
Some checks failed
Build and Test / build (push) Has been cancelled
Set up the foundation for custom Kindred Create icons:

- Add kindred-icons/ directory with Catppuccin Mocha themed SVG icons
- Modify BitmapFactory to prioritize kindred-icons/ in search path
- Add CMake install rules to package icons with application
- Include documentation (README.md) with design guidelines
- Add 12 initial icons as proof of concept:
  - File: document-new, document-open, document-save
  - Edit: edit-undo, edit-redo, edit-copy, edit-cut, edit-paste, delete
  - View: zoom-in, zoom-out, view-refresh
  - System: preferences-system

All icons follow the standard template:
- 32x32 viewBox
- Rounded rectangle background (rx=4, surface0 #313244)
- Catppuccin Mocha color palette

Closes #3
2026-02-05 11:17:27 -06:00
67e5598b2e fix(build): add missing Qt and Gui includes to ThemeSelectorWidget
Some checks failed
Build and Test / build (push) Successful in 1h9m41s
Release Build / build-linux (push) Failing after 18m43s
Release Build / build-macos (arm64, macos-14) (push) Has been cancelled
Release Build / build-macos (x86_64, macos-13) (push) Has been cancelled
Release Build / build-windows (push) Has been cancelled
Release Build / publish-release (push) Has been cancelled
Add QApplication, QEvent, and Gui/Application.h includes that were
missing, causing build failures with undeclared identifiers for qApp,
QEvent::LanguageChange, and Gui::Application::Instance.
2026-02-05 10:06:00 -06:00
7431746ef0 fix(ci): support 'latest' tag for release builds
Some checks failed
Build and Test / build (push) Failing after 43m15s
Release Build / build-linux (push) Failing after 2m18s
Release Build / build-macos (arm64, macos-14) (push) Has been cancelled
Release Build / build-macos (x86_64, macos-13) (push) Has been cancelled
Release Build / build-windows (push) Has been cancelled
Release Build / publish-release (push) Has been cancelled
2026-02-05 08:07:15 -06:00
044983330c Merge pull request 'fix(ci): use shallow tag-only fetch to avoid 504 timeout' (#2) from fix/ci-tag-fetch-504 into main
Some checks failed
Build and Test / build (push) Failing after 44m23s
2026-02-04 19:30:50 +00:00
d60db282ea fix(ci): use shallow tag-only fetch to avoid 504 timeout
Some checks failed
Build and Test / build (pull_request) Failing after 41m54s
The previous `git fetch --tags` triggers full history negotiation
against the upstream FreeCAD-derived repo (~45k commits), causing
HTTP 504 gateway timeouts on the Gitea instance.

Replace with a depth=1 refspec fetch that pulls only tag refs
without negotiating reachable objects behind them. This is
sufficient for `git describe --tags --always` to resolve a
version string.
2026-02-04 13:30:34 -06:00
2a5a645ace Merge pull request 'fix(ui): clean up theme selector and migration for single-theme setup' (#1) from fix/theme-selector-migration into main
Some checks are pending
Build and Test / build (push) Has started running
2026-02-04 19:27:31 +00:00
434ae797a4 fix(ui): clean up theme selector and migration for single-theme setup
Some checks failed
Build and Test / build (pull_request) Failing after 2m30s
- Remove dead code in migrateOldTheme() that set Theme parameter for
  KindredCreate.yaml, which no longer exists in the Stylesheets build
- Remove orphaned Classic.yaml and KindredCreate.yaml parameter files
  from Stylesheets CMakeLists and from disk
- Remove unused includes in ThemeSelectorWidget.cpp (QApplication,
  QEvent, Gui/Application.h)
2026-02-04 13:26:55 -06:00
267 changed files with 8288 additions and 3620 deletions

View File

@@ -0,0 +1,11 @@
[Unit]
Description=Kindred Create CI runner disk cleanup
After=docker.service
[Service]
Type=oneshot
ExecStart=/opt/runner/cleanup.sh
Environment=CLEANUP_THRESHOLD=85
Environment=CACHE_MAX_AGE_DAYS=7
StandardOutput=append:/var/log/runner-cleanup.log
StandardError=append:/var/log/runner-cleanup.log

220
.gitea/runner/cleanup.sh Executable file
View File

@@ -0,0 +1,220 @@
#!/usr/bin/env bash
# Runner disk cleanup script for Kindred Create CI/CD
#
# Designed to run as a cron job on the pubworker host:
# */30 * * * * /path/to/cleanup.sh >> /var/log/runner-cleanup.log 2>&1
#
# Or install the systemd timer (see cleanup.timer / cleanup.service).
#
# What it cleans:
# 1. Docker: stopped containers, dangling images, build cache
# 2. act_runner action cache: keeps only the newest entry per key prefix
# 3. act_runner workspaces: removes leftover build workspaces
# 4. System: apt cache, old logs
#
# What it preserves:
# - The current runner container and its image
# - The most recent cache entry per prefix (so ccache hits still work)
# - Everything outside of known CI paths
set -euo pipefail
# ---------------------------------------------------------------------------
# Configuration -- adjust these to match your runner setup
# ---------------------------------------------------------------------------
# Disk usage threshold (percent) -- only run aggressive cleanup above this
THRESHOLD=${CLEANUP_THRESHOLD:-85}
# act_runner cache directory (default location)
CACHE_DIR=${CACHE_DIR:-/root/.cache/actcache}
# act_runner workspace directories
WORKSPACES=(
"/root/.cache/act"
"/workspace"
)
# Maximum age (days) for cache entries before unconditional deletion
CACHE_MAX_AGE_DAYS=${CACHE_MAX_AGE_DAYS:-7}
# Maximum age (days) for Docker images not used by running containers
DOCKER_IMAGE_MAX_AGE=${DOCKER_IMAGE_MAX_AGE:-48h}
# Log prefix
LOG_PREFIX="[runner-cleanup]"
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
log() { echo "$(date '+%Y-%m-%d %H:%M:%S') ${LOG_PREFIX} $*"; }
disk_usage_pct() {
df --output=pcent / | tail -1 | tr -dc '0-9'
}
bytes_to_human() {
numfmt --to=iec-i --suffix=B "$1" 2>/dev/null || echo "${1}B"
}
# ---------------------------------------------------------------------------
# Phase 1: Check if cleanup is needed
# ---------------------------------------------------------------------------
usage=$(disk_usage_pct)
log "Disk usage: ${usage}% (threshold: ${THRESHOLD}%)"
if [ "$usage" -lt "$THRESHOLD" ]; then
log "Below threshold, running light cleanup only"
AGGRESSIVE=false
else
log "Above threshold, running aggressive cleanup"
AGGRESSIVE=true
fi
# ---------------------------------------------------------------------------
# Phase 2: Docker cleanup (always runs, safe)
# ---------------------------------------------------------------------------
log "--- Docker cleanup ---"
# Remove stopped containers
stopped=$(docker ps -aq --filter status=exited --filter status=dead 2>/dev/null | wc -l)
if [ "$stopped" -gt 0 ]; then
docker rm $(docker ps -aq --filter status=exited --filter status=dead) 2>/dev/null || true
log "Removed ${stopped} stopped containers"
fi
# Remove dangling images (untagged layers)
dangling=$(docker images -q --filter dangling=true 2>/dev/null | wc -l)
if [ "$dangling" -gt 0 ]; then
docker rmi $(docker images -q --filter dangling=true) 2>/dev/null || true
log "Removed ${dangling} dangling images"
fi
# Prune build cache
docker builder prune -f --filter "until=${DOCKER_IMAGE_MAX_AGE}" 2>/dev/null || true
log "Pruned Docker build cache older than ${DOCKER_IMAGE_MAX_AGE}"
if [ "$AGGRESSIVE" = true ]; then
# Remove all images not used by running containers
running_images=$(docker ps -q 2>/dev/null | xargs -r docker inspect --format='{{.Image}}' | sort -u)
all_images=$(docker images -q 2>/dev/null | sort -u)
for img in $all_images; do
if ! echo "$running_images" | grep -q "$img"; then
docker rmi -f "$img" 2>/dev/null || true
fi
done
log "Removed unused Docker images (aggressive)"
# Prune volumes
docker volume prune -f 2>/dev/null || true
log "Pruned unused Docker volumes"
fi
# ---------------------------------------------------------------------------
# Phase 3: act_runner action cache cleanup
# ---------------------------------------------------------------------------
log "--- Action cache cleanup ---"
if [ -d "$CACHE_DIR" ]; then
before=$(du -sb "$CACHE_DIR" 2>/dev/null | cut -f1)
# Delete cache entries older than max age
find "$CACHE_DIR" -type f -mtime "+${CACHE_MAX_AGE_DAYS}" -delete 2>/dev/null || true
find "$CACHE_DIR" -type d -empty -delete 2>/dev/null || true
after=$(du -sb "$CACHE_DIR" 2>/dev/null | cut -f1)
freed=$((before - after))
log "Cache cleanup freed $(bytes_to_human $freed) (entries older than ${CACHE_MAX_AGE_DAYS}d)"
else
log "Cache directory not found: ${CACHE_DIR}"
# Try common alternative locations
for alt in /var/lib/act_runner/.cache/actcache /home/*/.cache/actcache; do
if [ -d "$alt" ]; then
log "Found cache at: $alt (update CACHE_DIR config)"
CACHE_DIR="$alt"
find "$CACHE_DIR" -type f -mtime "+${CACHE_MAX_AGE_DAYS}" -delete 2>/dev/null || true
find "$CACHE_DIR" -type d -empty -delete 2>/dev/null || true
break
fi
done
fi
# ---------------------------------------------------------------------------
# Phase 4: Workspace cleanup
# ---------------------------------------------------------------------------
log "--- Workspace cleanup ---"
for ws in "${WORKSPACES[@]}"; do
if [ -d "$ws" ]; then
# Remove workspace dirs not modified in the last 2 hours
# (active builds should be touching files continuously)
before=$(du -sb "$ws" 2>/dev/null | cut -f1)
find "$ws" -mindepth 1 -maxdepth 1 -type d -mmin +120 -exec rm -rf {} + 2>/dev/null || true
after=$(du -sb "$ws" 2>/dev/null | cut -f1)
freed=$((before - after))
if [ "$freed" -gt 0 ]; then
log "Workspace $ws: freed $(bytes_to_human $freed)"
fi
fi
done
# ---------------------------------------------------------------------------
# Phase 5: System cleanup
# ---------------------------------------------------------------------------
log "--- System cleanup ---"
# apt cache
apt-get clean 2>/dev/null || true
# Truncate large log files (keep last 1000 lines)
for logfile in /var/log/syslog /var/log/daemon.log /var/log/kern.log; do
if [ -f "$logfile" ] && [ "$(stat -c%s "$logfile" 2>/dev/null)" -gt 104857600 ]; then
tail -1000 "$logfile" > "${logfile}.tmp" && mv "${logfile}.tmp" "$logfile"
log "Truncated $logfile (was >100MB)"
fi
done
# Journal logs older than 3 days
journalctl --vacuum-time=3d 2>/dev/null || true
# ---------------------------------------------------------------------------
# Phase 6: Emergency cleanup (only if still critical)
# ---------------------------------------------------------------------------
usage=$(disk_usage_pct)
if [ "$usage" -gt 95 ]; then
log "CRITICAL: Still at ${usage}% after cleanup"
# Nuclear option: remove ALL docker data except running containers
docker system prune -af --volumes 2>/dev/null || true
log "Ran docker system prune -af --volumes"
# Clear entire action cache
if [ -d "$CACHE_DIR" ]; then
rm -rf "${CACHE_DIR:?}/"*
log "Cleared entire action cache"
fi
usage=$(disk_usage_pct)
log "After emergency cleanup: ${usage}%"
fi
# ---------------------------------------------------------------------------
# Summary
# ---------------------------------------------------------------------------
usage=$(disk_usage_pct)
log "Cleanup complete. Disk usage: ${usage}%"
# Report top space consumers for diagnostics
log "Top 10 directories under /var:"
du -sh /var/*/ 2>/dev/null | sort -rh | head -10 | while read -r line; do
log " $line"
done

View File

@@ -0,0 +1,10 @@
[Unit]
Description=Run CI runner cleanup every 30 minutes
[Timer]
OnBootSec=5min
OnUnitActiveSec=30min
Persistent=true
[Install]
WantedBy=timers.target

View File

@@ -22,6 +22,17 @@ jobs:
DEBIAN_FRONTEND: noninteractive
steps:
- name: Free disk space
run: |
echo "=== Disk usage before cleanup ==="
df -h /
rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache 2>/dev/null || true
rm -rf /usr/local/share/boost /usr/share/swift 2>/dev/null || true
apt-get autoremove -y 2>/dev/null || true
apt-get clean 2>/dev/null || true
echo "=== Disk usage after cleanup ==="
df -h /
- name: Install system prerequisites
run: |
apt-get update -qq
@@ -36,8 +47,12 @@ jobs:
submodules: recursive
fetch-depth: 1
- name: Fetch tags (for git describe)
run: git fetch --tags --no-recurse-submodules origin
- name: Fetch latest tag (for git describe)
run: |
latest_tag=$(git ls-remote --tags --sort=-v:refname origin 'refs/tags/v*' | grep -v '\^{}' | head -n1 | awk '{print $2}')
if [ -n "$latest_tag" ]; then
git fetch --no-recurse-submodules --force --depth=1 origin "+${latest_tag}:${latest_tag}"
fi
- name: Install pixi
run: |
@@ -46,12 +61,16 @@ jobs:
export PATH="$HOME/.pixi/bin:$PATH"
pixi --version
- name: Compute cache date key
id: cache-date
run: echo "date=$(date -u +%Y%m%d)" >> $GITHUB_OUTPUT
- name: Restore ccache
id: ccache-restore
uses: https://github.com/actions/cache/restore@v4
with:
path: /tmp/ccache-kindred-create
key: ccache-build-${{ github.ref_name }}-${{ github.sha }}
key: ccache-build-${{ github.ref_name }}-${{ steps.cache-date.outputs.date }}
restore-keys: |
ccache-build-${{ github.ref_name }}-
ccache-build-main-
@@ -60,6 +79,7 @@ jobs:
run: |
mkdir -p $CCACHE_DIR
pixi run ccache -z
pixi run ccache -p
- name: Configure (CMake)
run: pixi run cmake --preset conda-linux-release
@@ -71,11 +91,11 @@ jobs:
run: pixi run ccache -s
- name: Save ccache
if: always()
if: always() && steps.ccache-restore.outputs.cache-hit != 'true'
uses: https://github.com/actions/cache/save@v4
with:
path: /tmp/ccache-kindred-create
key: ccache-build-${{ github.ref_name }}-${{ github.sha }}
key: ccache-build-${{ github.ref_name }}-${{ steps.cache-date.outputs.date }}
- name: Run C++ unit tests
continue-on-error: true

View File

@@ -3,7 +3,7 @@ name: Release Build
on:
push:
tags: ["v*"]
tags: ["v*", "latest"]
workflow_dispatch:
inputs:
tag:
@@ -31,6 +31,18 @@ jobs:
DEBIAN_FRONTEND: noninteractive
steps:
- name: Free disk space
run: |
echo "=== Disk usage before cleanup ==="
df -h /
# Remove pre-installed bloat common in runner images
rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache 2>/dev/null || true
rm -rf /usr/local/share/boost /usr/share/swift 2>/dev/null || true
apt-get autoremove -y 2>/dev/null || true
apt-get clean 2>/dev/null || true
echo "=== Disk usage after cleanup ==="
df -h /
- name: Install system prerequisites
run: |
apt-get update -qq
@@ -45,8 +57,12 @@ jobs:
submodules: recursive
fetch-depth: 1
- name: Fetch tags
run: git fetch --tags --no-recurse-submodules origin
- name: Fetch latest tag (for git describe)
run: |
latest_tag=$(git ls-remote --tags --sort=-v:refname origin 'refs/tags/v*' | grep -v '\^{}' | head -n1 | awk '{print $2}')
if [ -n "$latest_tag" ]; then
git fetch --no-recurse-submodules --force --depth=1 origin "+${latest_tag}:${latest_tag}"
fi
- name: Install pixi
run: |
@@ -55,20 +71,26 @@ jobs:
export PATH="$HOME/.pixi/bin:$PATH"
pixi --version
- name: Compute cache date key
id: cache-date
run: echo "date=$(date -u +%Y%m%d)" >> $GITHUB_OUTPUT
- name: Restore ccache
id: ccache-restore
uses: https://github.com/actions/cache/restore@v4
with:
path: /tmp/ccache-kindred-create
key: ccache-release-linux-${{ github.sha }}
key: ccache-release-linux-${{ steps.cache-date.outputs.date }}
restore-keys: |
ccache-release-linux-
ccache-build-main-
- name: Prepare ccache
run: |
mkdir -p $CCACHE_DIR
# Ensure ccache is accessible to rattler-build's subprocess
export PATH="$(pixi run bash -c 'echo $PATH')"
pixi run ccache -z
pixi run ccache -p
- name: Build release package (AppImage)
working-directory: package/rattler-build
@@ -80,11 +102,19 @@ jobs:
run: pixi run ccache -s
- name: Save ccache
if: always()
if: always() && steps.ccache-restore.outputs.cache-hit != 'true'
uses: https://github.com/actions/cache/save@v4
with:
path: /tmp/ccache-kindred-create
key: ccache-release-linux-${{ github.sha }}
key: ccache-release-linux-${{ steps.cache-date.outputs.date }}
- name: Clean up intermediate build files
run: |
# Remove pixi package cache and build work dirs to free space for .deb
rm -rf package/rattler-build/.pixi/build 2>/dev/null || true
find /root/.cache/rattler -type f -delete 2>/dev/null || true
echo "=== Disk usage after cleanup ==="
df -h /
- name: Build .deb package
run: |
@@ -105,7 +135,7 @@ jobs:
with:
name: release-linux
path: |
package/rattler-build/linux/*.AppImage
package/rattler-build/linux/FreeCAD_*.AppImage
package/rattler-build/linux/*.deb
package/rattler-build/linux/*-SHA256.txt
package/rattler-build/linux/*.sha256
@@ -113,174 +143,176 @@ jobs:
# ---------------------------------------------------------------------------
# macOS: DMG (Intel + Apple Silicon)
# TODO: Re-enable when macOS runners are available or cross-compilation is set up
# ---------------------------------------------------------------------------
build-macos:
strategy:
fail-fast: false
matrix:
include:
- runner: macos-13
arch: x86_64
- runner: macos-14
arch: arm64
runs-on: ${{ matrix.runner }}
env:
CCACHE_DIR: /tmp/ccache-kindred-create
CCACHE_COMPRESS: "true"
CCACHE_COMPRESSLEVEL: "6"
CCACHE_MAXSIZE: "4G"
CCACHE_SLOPPINESS: "include_file_ctime,include_file_mtime,pch_defines,time_macros"
CCACHE_BASEDIR: ${{ github.workspace }}
BUILD_TAG: ${{ github.ref_name || inputs.tag }}
CFLAGS: "-O3"
CXXFLAGS: "-O3"
steps:
- name: Checkout repository
uses: https://github.com/actions/checkout@v4
with:
submodules: recursive
fetch-depth: 1
- name: Fetch tags
run: git fetch --tags --no-recurse-submodules origin
- name: Install pixi
run: |
curl -fsSL https://pixi.sh/install.sh | bash
echo "$HOME/.pixi/bin" >> $GITHUB_PATH
export PATH="$HOME/.pixi/bin:$PATH"
pixi --version
- name: Restore ccache
id: ccache-restore
uses: https://github.com/actions/cache/restore@v4
with:
path: /tmp/ccache-kindred-create
key: ccache-release-macos-${{ matrix.arch }}-${{ github.sha }}
restore-keys: |
ccache-release-macos-${{ matrix.arch }}-
- name: Prepare ccache
run: |
mkdir -p $CCACHE_DIR
pixi run ccache -z
- name: Build release package (DMG)
working-directory: package/rattler-build
run: |
pixi install
pixi run -e package create_bundle
- name: Show ccache statistics
run: pixi run ccache -s
- name: Save ccache
if: always()
uses: https://github.com/actions/cache/save@v4
with:
path: /tmp/ccache-kindred-create
key: ccache-release-macos-${{ matrix.arch }}-${{ github.sha }}
- name: List built artifacts
run: |
echo "=== macOS ${{ matrix.arch }} release artifacts ==="
ls -lah package/rattler-build/osx/*.dmg* 2>/dev/null || true
- name: Upload macOS artifacts
uses: https://github.com/actions/upload-artifact@v3
with:
name: release-macos-${{ matrix.arch }}
path: |
package/rattler-build/osx/*.dmg
package/rattler-build/osx/*-SHA256.txt
if-no-files-found: error
# build-macos:
# strategy:
# fail-fast: false
# matrix:
# include:
# - runner: macos-13
# arch: x86_64
# - runner: macos-14
# arch: arm64
#
# runs-on: ${{ matrix.runner }}
#
# env:
# CCACHE_DIR: /tmp/ccache-kindred-create
# CCACHE_COMPRESS: "true"
# CCACHE_COMPRESSLEVEL: "6"
# CCACHE_MAXSIZE: "4G"
# CCACHE_SLOPPINESS: "include_file_ctime,include_file_mtime,pch_defines,time_macros"
# CCACHE_BASEDIR: ${{ github.workspace }}
# BUILD_TAG: ${{ github.ref_name || inputs.tag }}
# CFLAGS: "-O3"
# CXXFLAGS: "-O3"
#
# steps:
# - name: Checkout repository
# uses: https://github.com/actions/checkout@v4
# with:
# submodules: recursive
# fetch-depth: 1
#
# - name: Fetch tags
# run: git fetch --tags --force --no-recurse-submodules origin
#
# - name: Install pixi
# run: |
# curl -fsSL https://pixi.sh/install.sh | bash
# echo "$HOME/.pixi/bin" >> $GITHUB_PATH
# export PATH="$HOME/.pixi/bin:$PATH"
# pixi --version
#
# - name: Restore ccache
# id: ccache-restore
# uses: https://github.com/actions/cache/restore@v4
# with:
# path: /tmp/ccache-kindred-create
# key: ccache-release-macos-${{ matrix.arch }}-${{ github.sha }}
# restore-keys: |
# ccache-release-macos-${{ matrix.arch }}-
#
# - name: Prepare ccache
# run: |
# mkdir -p $CCACHE_DIR
# pixi run ccache -z
#
# - name: Build release package (DMG)
# working-directory: package/rattler-build
# run: |
# pixi install
# pixi run -e package create_bundle
#
# - name: Show ccache statistics
# run: pixi run ccache -s
#
# - name: Save ccache
# if: always()
# uses: https://github.com/actions/cache/save@v4
# with:
# path: /tmp/ccache-kindred-create
# key: ccache-release-macos-${{ matrix.arch }}-${{ github.sha }}
#
# - name: List built artifacts
# run: |
# echo "=== macOS ${{ matrix.arch }} release artifacts ==="
# ls -lah package/rattler-build/osx/*.dmg* 2>/dev/null || true
#
# - name: Upload macOS artifacts
# uses: https://github.com/actions/upload-artifact@v3
# with:
# name: release-macos-${{ matrix.arch }}
# path: |
# package/rattler-build/osx/*.dmg
# package/rattler-build/osx/*-SHA256.txt
# if-no-files-found: error
# ---------------------------------------------------------------------------
# Windows: .exe installer + .7z archive
# TODO: Re-enable when Windows runners are available or cross-compilation is set up
# ---------------------------------------------------------------------------
build-windows:
runs-on: windows-latest
env:
CCACHE_DIR: C:\ccache-kindred-create
CCACHE_COMPRESS: "true"
CCACHE_COMPRESSLEVEL: "6"
CCACHE_MAXSIZE: "4G"
CCACHE_SLOPPINESS: "include_file_ctime,include_file_mtime,pch_defines,time_macros"
CCACHE_BASEDIR: ${{ github.workspace }}
BUILD_TAG: ${{ github.ref_name || inputs.tag }}
CFLAGS: "/O2"
CXXFLAGS: "/O2"
MAKE_INSTALLER: "true"
steps:
- name: Checkout repository
uses: https://github.com/actions/checkout@v4
with:
submodules: recursive
fetch-depth: 1
- name: Fetch tags
shell: bash
run: git fetch --tags --no-recurse-submodules origin
- name: Install pixi
shell: bash
run: |
curl -fsSL https://pixi.sh/install.sh | bash
echo "$HOME/.pixi/bin" >> $GITHUB_PATH
export PATH="$HOME/.pixi/bin:$PATH"
pixi --version
- name: Restore ccache
id: ccache-restore
uses: https://github.com/actions/cache/restore@v4
with:
path: C:\ccache-kindred-create
key: ccache-release-windows-${{ github.sha }}
restore-keys: |
ccache-release-windows-
- name: Build release package
shell: bash
working-directory: package/rattler-build
run: |
pixi install
pixi run -e package create_bundle
- name: Save ccache
if: always()
uses: https://github.com/actions/cache/save@v4
with:
path: C:\ccache-kindred-create
key: ccache-release-windows-${{ github.sha }}
- name: List built artifacts
shell: bash
run: |
echo "=== Windows release artifacts ==="
ls -lah package/rattler-build/windows/*.7z* 2>/dev/null || true
ls -lah package/rattler-build/windows/*.exe 2>/dev/null || true
ls -lah package/rattler-build/windows/*-SHA256.txt 2>/dev/null || true
- name: Upload Windows artifacts
uses: https://github.com/actions/upload-artifact@v3
with:
name: release-windows
path: |
package/rattler-build/windows/*.7z
package/rattler-build/windows/*.exe
package/rattler-build/windows/*-SHA256.txt
if-no-files-found: error
# build-windows:
# runs-on: windows-latest
#
# env:
# CCACHE_DIR: C:\ccache-kindred-create
# CCACHE_COMPRESS: "true"
# CCACHE_COMPRESSLEVEL: "6"
# CCACHE_MAXSIZE: "4G"
# CCACHE_SLOPPINESS: "include_file_ctime,include_file_mtime,pch_defines,time_macros"
# CCACHE_BASEDIR: ${{ github.workspace }}
# BUILD_TAG: ${{ github.ref_name || inputs.tag }}
# CFLAGS: "/O2"
# CXXFLAGS: "/O2"
# MAKE_INSTALLER: "true"
#
# steps:
# - name: Checkout repository
# uses: https://github.com/actions/checkout@v4
# with:
# submodules: recursive
# fetch-depth: 1
#
# - name: Fetch tags
# shell: bash
# run: git fetch --tags --force --no-recurse-submodules origin
#
# - name: Install pixi
# shell: bash
# run: |
# curl -fsSL https://pixi.sh/install.sh | bash
# echo "$HOME/.pixi/bin" >> $GITHUB_PATH
# export PATH="$HOME/.pixi/bin:$PATH"
# pixi --version
#
# - name: Restore ccache
# id: ccache-restore
# uses: https://github.com/actions/cache/restore@v4
# with:
# path: C:\ccache-kindred-create
# key: ccache-release-windows-${{ github.sha }}
# restore-keys: |
# ccache-release-windows-
#
# - name: Build release package
# shell: bash
# working-directory: package/rattler-build
# run: |
# pixi install
# pixi run -e package create_bundle
#
# - name: Save ccache
# if: always()
# uses: https://github.com/actions/cache/save@v4
# with:
# path: C:\ccache-kindred-create
# key: ccache-release-windows-${{ github.sha }}
#
# - name: List built artifacts
# shell: bash
# run: |
# echo "=== Windows release artifacts ==="
# ls -lah package/rattler-build/windows/*.7z* 2>/dev/null || true
# ls -lah package/rattler-build/windows/*.exe 2>/dev/null || true
# ls -lah package/rattler-build/windows/*-SHA256.txt 2>/dev/null || true
#
# - name: Upload Windows artifacts
# uses: https://github.com/actions/upload-artifact@v3
# with:
# name: release-windows
# path: |
# package/rattler-build/windows/*.7z
# package/rattler-build/windows/*.exe
# package/rattler-build/windows/*-SHA256.txt
# if-no-files-found: error
# ---------------------------------------------------------------------------
# Create Gitea release from all platform artifacts
# ---------------------------------------------------------------------------
publish-release:
needs: [build-linux, build-macos, build-windows]
needs: [build-linux] # TODO: Add build-macos, build-windows when runners are available
runs-on: ubuntu-latest
env:
@@ -313,24 +345,82 @@ jobs:
ls -lah release/
- name: Create release
uses: https://gitea.com/actions/release-action@main
with:
files: release/*
title: "Kindred Create ${{ env.BUILD_TAG }}"
body: |
## Kindred Create ${{ env.BUILD_TAG }}
env:
GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN }}
GITEA_URL: ${{ github.server_url }}
REPO: ${{ github.repository }}
run: |
TAG="${BUILD_TAG}"
### Downloads
# Build JSON payload entirely in Python to avoid shell/Python type mismatches
PAYLOAD=$(python3 -c "
import json, re
tag = '${TAG}'
prerelease = bool(re.search(r'(rc|beta|alpha)', tag))
body = '''## Kindred Create {tag}
| Platform | File |
|----------|------|
| Linux (AppImage) | `KindredCreate-*-Linux-x86_64.AppImage` |
| Linux (Debian/Ubuntu) | `kindred-create_*.deb` |
| macOS (Intel) | `KindredCreate-*-macOS-x86_64.dmg` |
| macOS (Apple Silicon) | `KindredCreate-*-macOS-arm64.dmg` |
| Windows (Installer) | `KindredCreate-*-Windows-x86_64-installer.exe` |
| Windows (Portable) | `KindredCreate-*-Windows-x86_64.7z` |
### Downloads
SHA256 checksums are provided alongside each artifact.
prerelease: ${{ contains(github.ref_name, 'rc') || contains(github.ref_name, 'beta') || contains(github.ref_name, 'alpha') }}
api_key: ${{ secrets.RELEASE_TOKEN }}
| Platform | File |
|----------|------|
| Linux (AppImage) | \`KindredCreate-*-Linux-x86_64.AppImage\` |
| Linux (Debian/Ubuntu) | \`kindred-create_*.deb\` |
*macOS and Windows builds are not yet available.*
SHA256 checksums are provided alongside each artifact.'''.format(tag=tag)
print(json.dumps({
'tag_name': tag,
'name': f'Kindred Create {tag}',
'body': body,
'prerelease': prerelease,
}))
")
# Delete existing release for this tag (if any) so we can recreate
existing=$(curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: token ${GITEA_TOKEN}" \
"${GITEA_URL}/api/v1/repos/${REPO}/releases/tags/${TAG}")
if [ "$existing" = "200" ]; then
release_id=$(curl -s \
-H "Authorization: token ${GITEA_TOKEN}" \
"${GITEA_URL}/api/v1/repos/${REPO}/releases/tags/${TAG}" | \
python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
curl -s -X DELETE \
-H "Authorization: token ${GITEA_TOKEN}" \
"${GITEA_URL}/api/v1/repos/${REPO}/releases/${release_id}"
echo "Deleted existing release ${release_id} for tag ${TAG}"
fi
# Create release
response=$(curl -s -w "\n%{http_code}" -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d "$PAYLOAD" \
"${GITEA_URL}/api/v1/repos/${REPO}/releases")
http_code=$(echo "$response" | tail -1)
body=$(echo "$response" | sed '$d')
if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then
echo "::error::Failed to create release (HTTP ${http_code}): ${body}"
exit 1
fi
release_id=$(echo "$body" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
echo "Created release ${release_id}"
# Upload assets
for file in release/*; do
filename=$(basename "$file")
echo "Uploading ${filename}..."
upload_resp=$(curl -s -w "\n%{http_code}" -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-F "attachment=@${file}" \
"${GITEA_URL}/api/v1/repos/${REPO}/releases/${release_id}/assets?name=${filename}")
upload_code=$(echo "$upload_resp" | tail -1)
if [ "$upload_code" -lt 200 ] || [ "$upload_code" -ge 300 ]; then
echo "::warning::Failed to upload ${filename} (HTTP ${upload_code})"
else
echo " done."
fi
done

2
.gitmodules vendored
View File

@@ -15,4 +15,4 @@
url = https://git.kindred-systems.com/forbes/ztools.git
[submodule "mods/silo"]
path = mods/silo
url = https://git.kindred-systems.com/kindred/silo.git
url = https://git.kindred-systems.com/kindred/silo-mod.git

View File

@@ -1,111 +1,131 @@
# FreeCAD Contribution Process (FCP)
# Contributing to Kindred Create
FreeCAD's contribution process is inspired by the Collective Code Construction Contract which itself is an evolution of the github.com Fork and Pull Model.
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.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.
## 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
```
## 0. Status
See the [README](README.md) for full build instructions.
FreeCAD is in a transition period. The following are to be regarded as GUIDELINES for contribution submission and acceptance. For historical reasons, the actual process MAY diverge from this process during the transition. Such deviations SHOULD be noted and discussed whenever possible.
## Branch and PR workflow
## 1. Goals
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.
The FreeCAD Contribution Process is expressed here with the following specific goals in mind:
## Commit messages
1. To provide transparency and fairness in the contribution process.
2. To allow contributions to be included as quickly as possible.
3. To preserve and improve the code quality while encouraging appropriate experimentation and risk-taking.
4. To minimize dependence on individual Contributors by encouraging a large pool of active Contributors.
5. To be inclusive of many viewpoints and to harness a diverse set of skills.
6. To provide an encouraging environment where Contributors learn and improve their skills.
7. To protect the free and open nature of the FreeCAD project.
Use [Conventional Commits](https://www.conventionalcommits.org/):
## 2. Fundamentals
| Prefix | Purpose |
|--------|---------|
| `feat:` | New feature |
| `fix:` | Bug fix |
| `chore:` | Maintenance, dependencies |
| `docs:` | Documentation only |
| `art:` | Icons, theme, visual assets |
1. FreeCAD uses the git distributed revision control system.
2. Source code for the main application and related subprojects is hosted on github.com in the FreeCAD organization.
3. Problems are discrete, well-defined limitations or bugs.
4. FreeCAD uses GitHub's issue-tracking system to track problems and contributions. For help requests and general discussions, use the project forum.
5. Contributions are sets of code changes that resolve a single problem.
6. FreeCAD uses the Pull Request workflow for evaluating and accepting contributions.
Examples:
- `feat: add datum point creation mode`
- `fix(gui): correct menu icon size on Wayland`
- `chore: update silo submodule`
## 3. Roles
1. "User": A member of the wider FreeCAD community who uses the software.
2. "Contributor": A person who submits a contribution that resolves a previously identified problem. Contributors do not have commit access to the repository unless they are also Maintainers. Everyone, without distinction or discrimination, SHALL have an equal right to become a Contributor.
3. "Maintainer": A person who merges contributions. Maintainers may or may not be Contributors. Their role is to enforce the process. Maintainers have commit access to the repository.
4. "Administrator": Administrators have additional authority to maintain the list of designated Maintainers.
## Code style
## 4. Licensing, Ownership, and Credit
1. FreeCAD is distributed under the Lesser General Public License, version 2, or superior (LGPL2+). Additional details can be found in the LICENSE file.
2. All contributions to FreeCAD MUST use a compatible license.
3. All contributions are owned by their authors unless assigned to another.
4. FreeCAD does not have a mandatory copyright assignment policy.
5. A Contributor who wishes to be identified in the Credits section of the application "About" dialog is responsible for identifying themselves. They should modify the Contributors file and submit a PR with a single commit for this modification only. The contributors file is found at https://github.com/FreeCAD/FreeCAD/blob/main/src/Doc/CONTRIBUTORS
6. A contributor who does not wish to assume the copyright of their contribution MAY choose to assign it to the [FreeCAD project association](https://fpa.freecad.org) by mentioning **Copyright (c) 2022 The FreeCAD project association <fpa@freecad.org>** in the file's license code block.
### C/C++
## 5. Contribution Requirements
Formatted with **clang-format** (config in `.clang-format`). Static analysis via **clang-tidy** (config in `.clang-tidy`).
1. Contributions are submitted in the form of Pull Requests (PR).
2. Maintainers and Contributors MUST have a GitHub account and SHOULD use their real names or a well-known alias.
3. If the GitHub username differs from the username on the FreeCAD Forum, effort SHOULD be taken to avoid confusion.
4. A PR SHOULD be a minimal and accurate answer to exactly one identified and agreed-on problem.
5. A PR SHOULD refrain from adding additional dependencies to the FreeCAD project unless no other option is available.
6. Code submissions MUST adhere to the code style guidelines of the project if these are defined.
7. If a PR contains multiple commits, each commit MUST compile cleanly when merged with all previous commits of the same PR. Each commit SHOULD add value to the history of the project. Checkpoint commits SHOULD be squashed.
8. A PR SHALL NOT include non-trivial code from other projects unless the Contributor is the original author of that code.
9. A PR MUST compile cleanly and pass project self-tests on all target platforms.
10. Changes that break python API used by extensions SHALL be avoided. If it is not possible to avoid breaking changes, the amount of them MUST be minimized and PR MUST clearly describe all breaking changes with clear description on how to replace no longer working solution with newer one. Contributor SHOULD search for addons that will be broken and list them in the PR.
11. Each commit message in a PR MUST succinctly explain what the commit achieves. The commit message SHALL follow the suggestions in the `git commit --help` documentation, section DISCUSSION.
12. The PR Title MUST succinctly explain what the PR achieves. The Body MAY be as detailed as needed. If a PR changes the user interface (UI), the body of the text MUST include a presentation of these UI changes, preferably with screenshots of the previous and revised state.
13. If a PR contains the work of another author (for example, if it is cherry-picked from a fork by someone other than the PR-submitter):
1. the PR description MUST contain proper attribution as the first line, for example: "This is work of XYZ cherry-picked from <link>";
2. all commits MUST have proper authorship, i.e. be authored by the original author and committed by the author of the PR;
3. if changes to cherry-picked commits are necessary they SHOULD be done as follow-up commits. If it is not possible to do so, then the modified commits MUST contain a `Co-Authored-By` trailer in their commit message.
14. A “Valid PR” is one which satisfies the above requirements.
### Python
## 6. Process
Formatted with **black** (100-character line length). Linted with **pylint** (config in `.pylintrc`).
1. Change on the project follows the pattern of accurately identifying problems and applying minimal, accurate solutions to these problems.
2. To request changes, a User logs an issue on the project GitHub issue tracker.
3. The User or Contributor SHOULD write the issue by describing the problem they face or observe. Links to the forum or other resources are permitted but the issue SHOULD be complete and accurate and SHOULD NOT require the reader to visit the forum or any other platform to understand what is being described.
4. Issue authors SHOULD strive to describe the minimum acceptable condition.
5. Issue authors SHOULD focus on User tasks and avoid comparisons to other software solutions.
6. The User or Contributor SHOULD seek consensus on the accuracy of their observation and the value of solving the problem.
7. To submit a solution to a problem, a Contributor SHALL create a pull request back to the project.
8. Contributors and Maintainers SHALL NOT commit changes directly to the target branch.
9. To discuss a proposed solution, Users MAY comment on the Pull Request in GitHub. Forum conversations regarding the solution SHOULD be discouraged and conversation redirected to the Pull Request or the related issue.
10. To accept or reject a Pull Request, a Maintainer SHALL use GitHub's interface.
11. Maintainers SHOULD NOT merge their own PRs except:
1. in exceptional cases, such as non-responsiveness from other Maintainers for an extended period.
2. If the Maintainer is also the primary developer of the workbench or subsystem.
### Pre-commit hooks
12. Maintainers SHALL merge valid PRs from other Contributors rapidly.
13. Maintainers MAY, at their discretion merge PRs that have not met all criteria to be considered valid to:
1. end fruitless discussions
2. capture toxic contributions in the historical record
3. engage with the Contributor on improving their contribution quality.
14. Maintainers SHALL NOT make value judgments on correct contributions.
15. If a PR requires significant further work before merging, the PR SHOULD be moved to draft status.
16. If a PR is complete, but should not be merged yet (for example, because it depends on another in-process PR), the "On hold" label SHOULD be applied.
17. Any Contributor who has value judgments on a PR SHOULD express these via their own PR.
18. The User who created an issue SHOULD close the issue after checking the PR is successful.
19. Maintainers SHOULD close issues that are left open without action or update for an unreasonable period.
```bash
pip install pre-commit
pre-commit install
```
## 7. Branches and Releases
This runs clang-format, black, and pylint automatically on staged files.
1. The project SHALL have one branch (“main”) that always holds the latest in-progress version and SHOULD always build.
2. The project SHALL NOT use topic branches for any reason. Personal forks MAY use topic branches.
3. To make a stable release a Maintainer SHALL tag the repository. Stable releases SHALL always be released from the repository main branch.
## Submodules
## 8. Project Administration
Kindred Create uses git submodules for addon workbenches:
1. Project Administrators are those individuals who are members of the FreeCAD Github organization and have the role of 'owner'. They have the task of administering the organization including adding and removing individuals from various teams.
2. Project Administrator is a technical role necessitated by the GitHub platform. Except for the specific exceptions listed below, the Project Administrators do not make the decision about individual team members. Rather, they carry out the collective wishes of the Maintainers team. Project Administrators will be selected from the Maintainers team by the Maintainers themselves.
3. To ensure continuity there SHALL be at least four Project Administrators at all times.
4. The project Administrators will manage the set of project Maintainers. They SHALL maintain a sufficiently large pool of Maintainers to ensure their succession and permit timely review of contributions. If the pool of Maintainers is insufficient, the Project Administrators will request that the Maintainers select additional individuals to add.
5. Contributors who have a history of successful PRs and have demonstrated continued professionalism should be invited to be Maintainers.
6. Administrators SHOULD remove Maintainers who are inactive for an extended period, or who repeatedly fail to apply this process accurately.
7. The list of Maintainers SHALL be publicly accessible and reflective of current activity on the project.
8. Administrators SHALL act expediently to protect the FreeCAD infrastructure and resources.
9. Administrators SHOULD block or ban “bad actors” who cause stress, animosity, or confusion to others in the project. This SHOULD be done after public discussion, with a chance for all parties to speak. A bad actor is someone who repeatedly ignores the rules and culture of the project, who is hostile or offensive, who impedes the productive exchange of information, and who is unable to self-correct their behavior when asked to do so by others.
| Submodule | Path | Repository |
|-----------|------|------------|
| ztools | `mods/ztools` | `git.kindred-systems.com/forbes/ztools` |
| silo-mod | `mods/silo` | `git.kindred-systems.com/kindred/silo-mod` |
To update a submodule:
```bash
cd mods/silo
git checkout main && git pull
cd ../..
git add mods/silo
git commit -m "chore: update silo submodule"
```
If you cloned without `--recursive`, initialize submodules with:
```bash
git submodule update --init --recursive
```
## Theme and QSS changes
The Catppuccin Mocha theme has **three QSS copies** that must be kept in sync:
1. `resources/preferences/KindredCreate/KindredCreate.qss` (canonical)
2. `src/Gui/Stylesheets/KindredCreate.qss`
3. `src/Gui/PreferencePacks/KindredCreate/KindredCreate.qss`
When modifying the theme, apply changes to all three files. Note that the copies have intentional differences (e.g., tree branch rendering style), so do not blindly copy between them -- apply edits individually.
See [KNOWN_ISSUES.md](docs/KNOWN_ISSUES.md) for the planned QSS consolidation.
## Preference pack
Default preferences are defined in `resources/preferences/KindredCreate/KindredCreate.cfg`. This XML file uses FreeCAD's parameter format:
```xml
<FCParamGroup Name="GroupName">
<FCBool Name="Setting" Value="1"/>
<FCInt Name="Setting" Value="42"/>
<FCText Name="Setting">value</FCText>
</FCParamGroup>
```
Changes here affect the out-of-box experience for all users.
## CI/CD
- **Build workflow** (`build.yml`): Runs on every push to `main` and on PRs. Builds in Ubuntu 24.04 container, runs C++ and Python tests.
- **Release workflow** (`release.yml`): Triggered by `v*` tags. Builds AppImage and .deb packages.
See [docs/CI_CD.md](docs/CI_CD.md) for full details.
## Architecture
For an overview of the codebase structure, bootstrap flow, and design decisions, see:
- [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) -- Bootstrap flow and source layout
- [docs/COMPONENTS.md](docs/COMPONENTS.md) -- Feature inventory
- [docs/INTEGRATION_PLAN.md](docs/INTEGRATION_PLAN.md) -- Architecture layers and roadmap
## License
All contributions must be compatible with [LGPL-2.1-or-later](LICENSE).

File diff suppressed because one or more lines are too long

61
docs/ARCHITECTURE.md Normal file
View File

@@ -0,0 +1,61 @@
# Architecture
## Bootstrap flow
```
FreeCAD startup
└─ src/Mod/Create/Init.py
└─ setup_kindred_addons()
├─ exec(mods/ztools/ztools/Init.py)
└─ exec(mods/silo/freecad/Init.py)
└─ src/Mod/Create/InitGui.py
├─ setup_kindred_workbenches()
│ ├─ exec(mods/ztools/ztools/InitGui.py)
│ │ ├─ registers ZToolsWorkbench
│ │ └─ installs _ZToolsPartDesignManipulator (global)
│ └─ exec(mods/silo/freecad/InitGui.py)
│ └─ registers SiloWorkbench
└─ Deferred setup (QTimer):
├─ 1500ms: _register_silo_origin() → registers Silo FileOrigin
├─ 2000ms: _setup_silo_auth_panel() → "Database Auth" dock
├─ 3000ms: _check_silo_first_start() → settings prompt
├─ 4000ms: _setup_silo_activity_panel() → "Database Activity" dock (SSE)
└─ 10000ms: _check_for_updates() → update checker (Gitea API)
```
## Key source layout
```
src/Mod/Create/ Kindred bootstrap module (Python)
├── Init.py Adds mods/ addon paths, loads Init.py files
├── InitGui.py Loads workbenches, installs Silo manipulators
├── version.py.in CMake template → version.py (build-time)
└── update_checker.py Checks Gitea releases API for updates
src/Gui/FileOrigin.h/.cpp FileOrigin base class + LocalFileOrigin
src/Gui/CommandOrigin.cpp Origin_Commit/Pull/Push/Info/BOM commands
src/Gui/OriginManager.h/.cpp Origin lifecycle management
src/Gui/OriginSelectorWidget.h/.cpp UI for origin selection
mods/ztools/ [submodule] ztools workbench
├── ztools/InitGui.py ZToolsWorkbench + PartDesign manipulator
├── ztools/ztools/
│ ├── commands/ Datum, pattern, pocket, assembly, spreadsheet
│ ├── datums/core.py Datum creation via Part::AttachExtension
│ └── resources/ Icons, theme utilities
└── CatppuccinMocha/ Theme preference pack (QSS)
mods/silo/ [submodule -> silo-mod.git] FreeCAD workbench
├── silo-client/ [submodule -> silo-client.git] shared API client
│ └── silo_client/ SiloClient, SiloSettings, CATEGORY_NAMES
└── freecad/ FreeCAD workbench (Python)
├── InitGui.py SiloWorkbench
├── silo_commands.py Commands + FreeCADSiloSettings adapter
└── silo_origin.py FileOrigin backend for Silo
src/Gui/Stylesheets/ QSS themes and SVG assets
src/Gui/PreferencePacks/ KindredCreate preference pack (cfg + build-time QSS)
```
See [INTEGRATION_PLAN.md](INTEGRATION_PLAN.md) for architecture layers and phase status.

View File

@@ -7,7 +7,7 @@ Kindred Create uses Gitea Actions for continuous integration and release builds.
| Workflow | Trigger | Purpose | Artifacts |
|----------|---------|---------|-----------|
| `build.yml` | Push to `main`, pull requests | Build + test | Linux tarball |
| `release.yml` | Tags matching `v*` | Multi-platform release | AppImage, .deb, .dmg, .exe, .7z |
| `release.yml` | Tags matching `v*` or `latest` | Release build | AppImage, .deb |
All builds run on public runners in dockerized mode. No host-mode or internal infrastructure is required.
@@ -34,14 +34,16 @@ Runs on every push to `main` and on pull requests. Builds the project in an Ubun
### Caching
ccache is persisted between builds using `actions/cache`. Cache keys are scoped by branch and commit SHA, with fallback to the branch key then `main`.
ccache is persisted between builds using `actions/cache`. Cache keys use a date suffix so entries rotate daily (one save per day per branch). Saves are skipped when the exact key already exists, preventing duplicate entries that fill runner storage.
```
Key: ccache-build-{branch}-{sha}
Key: ccache-build-{branch}-{YYYYMMDD}
Fallback: ccache-build-{branch}-
Fallback: ccache-build-main-
```
Release builds use a separate key namespace (`ccache-release-linux-{YYYYMMDD}`) because they compile with different optimization flags (`-O3`). The rattler-build script (`build.sh`) explicitly sets `CCACHE_DIR` and `CCACHE_BASEDIR` since rattler-build does not forward environment variables from the parent process.
ccache configuration: 4 GB max, zlib compression level 6, sloppy mode for include timestamps and PCH.
---
@@ -63,17 +65,19 @@ Tags containing `rc`, `beta`, or `alpha` are marked as pre-releases.
### Platform matrix
| Job | Runner | Container | Preset | Output |
|-----|--------|-----------|--------|--------|
| `build-linux` | `ubuntu-latest` | `ubuntu:24.04` | `conda-linux-release` | AppImage, .deb |
| `build-macos` (Intel) | `macos-13` | native | `conda-macos-release` | .dmg (x86_64) |
| `build-macos` (Apple Silicon) | `macos-14` | native | `conda-macos-release` | .dmg (arm64) |
| `build-windows` | `windows-latest` | native | `conda-windows-release` | .exe (NSIS), .7z |
| Job | Runner | Container | Preset | Output | Status |
|-----|--------|-----------|--------|--------|--------|
| `build-linux` | `ubuntu-latest` | `ubuntu:24.04` | `conda-linux-release` | AppImage, .deb | Active |
| `build-macos` (Intel) | `macos-13` | native | `conda-macos-release` | .dmg (x86_64) | Disabled |
| `build-macos` (Apple Silicon) | `macos-14` | native | `conda-macos-release` | .dmg (arm64) | Disabled |
| `build-windows` | `windows-latest` | native | `conda-windows-release` | .exe (NSIS), .7z | Disabled |
All four jobs run concurrently. After all succeed, `publish-release` collects artifacts and creates the Gitea release.
Only the Linux build is currently active. macOS and Windows jobs are defined but commented out pending runner availability or cross-compilation support. After `build-linux` succeeds, `publish-release` collects artifacts and creates the Gitea release.
### Linux build
Both workflows start with a disk cleanup step that removes pre-installed bloat (dotnet, Android SDK, etc.) to free space for the build.
Uses the rattler-build packaging pipeline:
1. `pixi install` in `package/rattler-build/`
@@ -81,9 +85,10 @@ Uses the rattler-build packaging pipeline:
3. The bundle script:
- Copies the pixi conda environment to an AppDir
- Strips unnecessary files (includes, static libs, cmake files, `__pycache__`)
- Downloads `appimagetool` and creates a squashfs AppImage (zstd compressed)
- Generates SHA256 checksums
4. `package/debian/build-deb.sh` builds a .deb from the AppDir
- Downloads `appimagetool`, extracts it with `--appimage-extract` (FUSE unavailable in containers), and runs via `squashfs-root/AppRun`
- Creates a squashfs AppImage (zstd compressed) with SHA256 checksums
4. Intermediate build files are cleaned up to free space for the .deb step
5. `package/debian/build-deb.sh` builds a .deb from the AppDir
- Installs to `/opt/kindred-create/` with wrapper scripts in `/usr/bin/`
- Sets up LD_LIBRARY_PATH, QT_PLUGIN_PATH, PYTHONPATH in wrappers
- Creates desktop entry, MIME types, AppStream metainfo
@@ -123,10 +128,15 @@ Builds natively on Windows runner:
`publish-release` runs after all platform builds succeed:
1. Downloads all artifacts from `build-linux`, `build-macos`, `build-windows`
2. Collects release files (AppImage, .deb, .dmg, .7z, .exe, checksums)
3. Creates a Gitea release via `gitea.com/actions/release-action`
4. Requires `RELEASE_TOKEN` secret with repository write permissions
1. Downloads all artifacts from completed build jobs
2. Collects release files (AppImage, .deb, checksums) into a `release/` directory
3. Deletes any existing Gitea release for the same tag (allows re-running)
4. Creates a new Gitea release via the REST API (`/api/v1/repos/{owner}/{repo}/releases`)
5. Uploads each artifact as a release attachment via the API
The release payload (tag name, body, prerelease flag) is built entirely in Python to avoid shell/Python type mismatches. Tags containing `rc`, `beta`, or `alpha` are automatically marked as pre-releases.
Requires `RELEASE_TOKEN` secret with repository write permissions.
---
@@ -174,6 +184,27 @@ container:
network: bridge
```
### Runner cleanup daemon
A cleanup script at `.gitea/runner/cleanup.sh` prevents disk exhaustion on self-hosted runners. It uses a tiered approach based on disk usage thresholds:
| Threshold | Action |
|-----------|--------|
| 70% | Docker cleanup (stopped containers, dangling images, build cache) |
| 80% | Purge act_runner cache entries older than 7 days, clean inactive workspaces |
| 90% | System cleanup (apt cache, old logs, journal vacuum to 100 MB) |
| 95% | Emergency: remove all act_runner cache entries and Docker images |
Install via the provided systemd units (`.gitea/runner/cleanup.service` and `.gitea/runner/cleanup.timer`) to run every 30 minutes:
```bash
sudo cp .gitea/runner/cleanup.sh /usr/local/bin/runner-cleanup.sh
sudo cp .gitea/runner/cleanup.service /etc/systemd/system/
sudo cp .gitea/runner/cleanup.timer /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now cleanup.timer
```
---
## Secrets
@@ -214,11 +245,12 @@ Defined in `CMakePresets.json`. Release builds use:
### ccache
Compiler cache is used across all builds to speed up incremental compilation. Cache is persisted between CI runs via `actions/cache`. Configuration:
Compiler cache is used across all builds to speed up incremental compilation. Cache is persisted between CI runs via `actions/cache` with date-based key rotation. Configuration:
- Max size: 4 GB
- Compression: zlib level 6
- Sloppy mode: include timestamps, PCH defines, time macros
- `CCACHE_BASEDIR`: set to workspace root (build workflow) or `$SRC_DIR` (rattler-build) for path normalization across runs
---
@@ -243,9 +275,12 @@ The Docker container installs only minimal dependencies. If a new dependency is
ccache misses spike when:
- The compiler version changes (pixi update)
- CMake presets change configuration flags
- The cache key doesn't match (new branch, force-pushed SHA)
- First build of the day (date-based key rotates daily)
- New branch without a prior cache (falls back to `main` cache)
Check `pixi run ccache -s` output for hit/miss ratios.
For release builds, ensure `build.sh` is correctly setting `CCACHE_DIR=/tmp/ccache-kindred-create` -- rattler-build does not forward environment variables from the workflow, so ccache config must be set inside the script.
Check `pixi run ccache -s` output (printed in the "Show ccache statistics" step) for hit/miss ratios. The "Prepare ccache" step also prints the full ccache configuration via `ccache -p`.
### Submodule checkout fails

116
docs/COMPONENTS.md Normal file
View File

@@ -0,0 +1,116 @@
# Components
## ztools workbench
**Registered commands (9):**
| Command | Function |
|---------|----------|
| `ZTools_DatumCreator` | Create datum planes, axes, points (16 modes) |
| `ZTools_DatumManager` | Manage existing datum objects |
| `ZTools_EnhancedPocket` | Flip-side pocket (cut outside sketch profile) |
| `ZTools_RotatedLinearPattern` | Linear pattern with incremental rotation |
| `ZTools_AssemblyLinearPattern` | Pattern assembly components linearly |
| `ZTools_AssemblyPolarPattern` | Pattern assembly components around axis |
| `ZTools_SpreadsheetStyle{Bold,Italic,Underline}` | Text style toggles |
| `ZTools_SpreadsheetAlign{Left,Center,Right}` | Cell alignment |
| `ZTools_Spreadsheet{BgColor,TextColor,QuickAlias}` | Colors and alias creation |
**PartDesign integration** via `_ZToolsPartDesignManipulator`:
- `ZTools_DatumCreator`, `ZTools_DatumManager` → "Part Design Helper Features" toolbar
- `ZTools_EnhancedPocket` → "Part Design Modeling Features" toolbar
- `ZTools_RotatedLinearPattern` → "Part Design Transformation Features" toolbar
- Same commands inserted into Part Design menu after `PartDesign_Boolean`
**Datum types (7):** offset_from_face, offset_from_plane, midplane, 3_points, normal_to_edge, angled, tangent_to_cylinder. All except tangent_to_cylinder use `Part::AttachExtension` for automatic parametric updates.
---
## Origin commands (C++)
The Origin abstraction (`src/Gui/FileOrigin.h`) provides a backend-agnostic interface for document storage. Commands delegate to the active `FileOrigin` implementation (currently `LocalFileOrigin` for local files, `SiloOrigin` via `mods/silo/freecad/silo_origin.py` for Silo-tracked documents).
**Registered commands (5):**
| Command | Function | Icon |
|---------|----------|------|
| `Origin_Commit` | Commit changes as a new revision | `silo-commit` |
| `Origin_Pull` | Pull a specific revision from the origin | `silo-pull` |
| `Origin_Push` | Push local changes to the origin | `silo-push` |
| `Origin_Info` | Show document information from origin | `silo-info` |
| `Origin_BOM` | Show Bill of Materials for this document | `silo-bom` |
These appear in the File menu and "Origin Tools" toolbar across all workbenches (see `src/Gui/Workbench.cpp`).
---
## Silo workbench
**Registered commands (14):**
| Command | Function |
|---------|----------|
| `Silo_New` | Create new Silo-tracked document |
| `Silo_Open` | Open file from Silo database |
| `Silo_Save` | Save to Silo (create revision) |
| `Silo_Commit` | Commit current revision |
| `Silo_Pull` | Pull latest revision from server |
| `Silo_Push` | Push local changes to server |
| `Silo_Info` | View item metadata and history |
| `Silo_BOM` | Bill of materials dialog (BOM + Where Used) |
| `Silo_TagProjects` | Assign project tags |
| `Silo_Rollback` | Rollback to previous revision |
| `Silo_SetStatus` | Set revision status (draft/review/released/obsolete) |
| `Silo_Settings` | Configure API URL, projects dir, SSL certificates |
| `Silo_Auth` | Login/logout authentication panel |
**Global integration** via the origin system in `src/Gui/`:
- File toolbar: `Std_Origin` selector widget + `Std_New`/`Std_Open`/`Std_Save` (delegate to current origin)
- Origin Tools toolbar: `Origin_Commit`/`Origin_Pull`/`Origin_Push`/`Origin_Info`/`Origin_BOM` (auto-disable by capability)
- Silo origin registered at startup by `src/Mod/Create/InitGui.py`
**Dock panels:**
- Database Auth (2000ms) -- Login/logout and API token management
- Database Activity (4000ms) -- Real-time server event feed via SSE (Server-Sent Events) with automatic reconnection and exponential backoff
- Start Panel -- In-viewport landing page with recent files and Silo integration
**Server architecture:** Go REST API (38+ routes) + PostgreSQL + MinIO S3. Authentication via local (bcrypt), LDAP, or OIDC backends. SSE endpoint for real-time event streaming. See `mods/silo/docs/` for server documentation.
**LibreOffice Calc extension** ([silo-calc](https://git.kindred-systems.com/kindred/silo-calc.git)): BOM management, item creation, and AI-assisted descriptions via OpenRouter API. Shares the same Silo REST API and auth token system via the shared [silo-client](https://git.kindred-systems.com/kindred/silo-client.git) package.
---
## Theme
**Canonical source:** `src/Gui/Stylesheets/KindredCreate.qss`
The PreferencePacks copy (`src/Gui/PreferencePacks/KindredCreate/KindredCreate.qss`) is generated at build time via `configure_file()` in `src/Gui/PreferencePacks/CMakeLists.txt`. Only the Stylesheets copy needs to be maintained.
Notable theme customizations beyond standard Catppuccin colors:
- `QGroupBox::indicator` styling to match `QCheckBox::indicator` (consistent checkbox appearance)
- `QLabel[haslink="true"]` link color (`#b4befe` Catppuccin Lavender) -- picked up by FreeCAD to set `QPalette::Link`
- Spanning-line tree branch indicators
---
## Icon infrastructure
### Qt resource icons (`src/Gui/Icons/`)
5 `silo-*` SVGs registered in `resource.qrc`, used by C++ Origin commands:
`silo-bom.svg`, `silo-commit.svg`, `silo-info.svg`, `silo-pull.svg`, `silo-push.svg`
### Silo module icons (`mods/silo/freecad/resources/icons/`)
10 SVGs loaded at runtime by the `_icon()` function in `silo_commands.py`:
`silo-auth.svg`, `silo-bom.svg`, `silo-commit.svg`, `silo-info.svg`, `silo-new.svg`, `silo-open.svg`, `silo-pull.svg`, `silo-push.svg`, `silo-save.svg`, `silo.svg`
### Missing icons
3 command icon names have no corresponding SVG file: `silo-tag`, `silo-rollback`, `silo-status`. The `_icon()` function returns an empty string for these, so `Silo_TagProjects`, `Silo_Rollback`, and `Silo_SetStatus` render without toolbar icons.
### Palette
All silo-* icons use the Catppuccin Mocha color scheme. See `kindred-icons/README.md` for palette specification and icon design standards.

File diff suppressed because one or more lines are too long

84
docs/KNOWN_ISSUES.md Normal file
View File

@@ -0,0 +1,84 @@
# Known Issues
## Issues
### Critical
1. ~~**QSS duplication.**~~ Resolved. The canonical QSS lives in `src/Gui/Stylesheets/KindredCreate.qss`. The PreferencePacks copy is now generated at build time via `configure_file()` in `src/Gui/PreferencePacks/CMakeLists.txt`. The unused `resources/preferences/KindredCreate/` directory has been removed.
2. **WorkbenchManipulator timing.** The `_ZToolsPartDesignManipulator` appends commands by name. If ZToolsWorkbench hasn't been activated when the user switches to PartDesign, the commands may not be registered. The manipulator API tolerates missing commands silently, but buttons won't appear.
3. ~~**Silo shortcut persistence.**~~ Resolved. `Silo_ToggleMode` removed; file operations now delegate to the selected origin via the unified origin system.
### High
4. **Silo authentication not production-hardened.** Local auth (bcrypt) works end-to-end. LDAP (FreeIPA) and OIDC (Keycloak) backends are coded but depend on infrastructure not yet deployed. FreeCAD client has `Silo_Auth` dock panel for login and API token management. Server has session middleware (`alexedwards/scs`), CSRF protection (`nosurf`), and role-based access control (admin/editor/viewer). Migration `009_auth.sql` adds users, api_tokens, and sessions tables.
5. **No unit tests.** Zero test coverage for ztools and Silo FreeCAD commands. Silo Go backend also lacks tests.
6. **Assembly solver datum handling is minimal.** The `findPlacement()` fix in `src/Mod/Assembly/UtilsAssembly.py` extracts placement from `obj.Shape.Faces[0]` for `PartDesign::Plane` and from shape vertex for `PartDesign::Point`. Does not handle empty shapes or non-planar datum objects.
### Medium
7. **`Silo_BOM` requires Silo-tracked document.** Depends on `SiloPartNumber` property. Unregistered documents show a warning with no registration path.
8. **PartDesign menu insertion fragility.** `_ZToolsPartDesignManipulator.modifyMenuBar()` inserts after `PartDesign_Boolean`. If upstream renames this command, insertions silently fail.
9. **tangent_to_cylinder falls back to manual placement.** TangentPlane MapMode requires a vertex reference not collected by the current UI.
10. **`delete_bom_entry()` bypasses error normalization.** Uses raw `urllib.request` instead of `SiloClient._request()`.
11. **Missing Silo icons.** Three commands reference icons that don't exist: `silo-tag.svg` (`Silo_TagProjects`), `silo-rollback.svg` (`Silo_Rollback`), `silo-status.svg` (`Silo_SetStatus`). The `_icon()` function returns an empty string, so these commands render without toolbar icons.
### Fixed (retain for reference)
12. **OndselSolver Newton-Raphson convergence.** `NewtonRaphson::isConvergedToNumericalLimit()` compared `dxNorms->at(iterNo)` to itself instead of `dxNorms->at(iterNo - 1)`. This prevented convergence detection on complex assemblies, causing solver exhaustion and "grounded object moved" warnings. Fixed in Kindred fork (`src/3rdParty/OndselSolver`). Needs upstreaming to `FreeCAD/OndselSolver`.
13. **Assembly solver crash on document restore.** `AssemblyObject::onChanged()` called `updateSolveStatus()` when the Group property changed during document restore, triggering the solver while child objects were still deserializing (SIGSEGV). Fixed with `isRestoring()` and `isPerformingTransaction()` guards at `src/Mod/Assembly/App/AssemblyObject.cpp:143`.
14. **`DlgSettingsGeneral::applyMenuIconSize` visibility.** The method was `private` but called from `StartupProcess.cpp`. Fixed by moving to `public` (PR #49). Also required `Dialog::` namespace qualifier in `StartupProcess.cpp`.
---
## Incomplete features
### Silo
| Feature | Status | Notes |
|---------|--------|-------|
| Authentication | Local auth complete | LDAP/OIDC backends coded, pending infrastructure. Auth dock panel available. |
| CSRF protection | Implemented | `nosurf` library on web form routes |
| File locking | Not implemented | Needed to prevent concurrent edits |
| Odoo ERP integration | Stub only | Returns "not yet implemented" |
| Part number date segments | Broken | `formatDate()` returns error |
| Location/inventory APIs | Tables exist, no handlers | |
| CSV import rollback | Not implemented | `bom_handlers.go` |
| SSE event streaming | Implemented | Reconnect logic with exponential backoff |
| Database Activity panel | Implemented | Dock panel showing real-time server events |
| Start panel | Implemented | In-viewport start page with recent files and Silo integration |
### ztools
| Feature | Status | Notes |
|---------|--------|-------|
| Tangent-to-cylinder attachment | Manual fallback | No vertex ref in UI |
| Angled datum live editing | Incomplete | AttachmentOffset not updated in panel |
| Assembly pattern undo | Not implemented | |
---
## Next steps
1. **Authentication hardening** -- Deploy FreeIPA and Keycloak infrastructure. End-to-end test LDAP and OIDC flows. Harden token rotation and session expiry.
2. **BOM-Assembly bridge** -- Auto-populate Silo BOM from Assembly component links on save.
3. **File locking** -- Pessimistic locks on `Silo_Open` to prevent concurrent edits. Requires server-side lock table and client-side lock display.
4. **Build system** -- CMake install rules for `mods/` submodules so packages include ztools and Silo without manual steps.
5. **Test coverage** -- Unit tests for ztools datum creation, Silo FreeCAD commands, and Go API endpoints.
6. **QSS consolidation** -- Eliminate the 3-copy QSS duplication via build-time copy or symlinks. The canonical source is `resources/preferences/KindredCreate/KindredCreate.qss`.
7. **Update notification UI** -- Display in-app notification when a new release is available (issue #30). The update checker backend is already implemented.

31
docs/OVERVIEW.md Normal file
View File

@@ -0,0 +1,31 @@
# Kindred Create
**Last updated:** 2026-02-08
**Branch:** main @ `cf523f1d87a`
**Kindred Create:** v0.1.0
**FreeCAD base:** v1.0.0
## Documentation
| Document | Contents |
|----------|----------|
| [ARCHITECTURE.md](ARCHITECTURE.md) | Bootstrap flow, source layout, submodules |
| [COMPONENTS.md](COMPONENTS.md) | ztools, Silo, Origin commands, theme, icons |
| [KNOWN_ISSUES.md](KNOWN_ISSUES.md) | Bugs, incomplete features, next steps |
| [INTEGRATION_PLAN.md](INTEGRATION_PLAN.md) | Architecture layers, integration phases |
| [CI_CD.md](CI_CD.md) | Build and release workflows |
## Submodules
| Submodule | Path | Source | Pinned commit |
|-----------|------|--------|---------------|
| ztools | `mods/ztools` | `git.kindred-systems.com/forbes/ztools` | `3298d1c` |
| silo-mod | `mods/silo` | `git.kindred-systems.com/kindred/silo-mod` | `f9924d3` |
| OndselSolver | `src/3rdParty/OndselSolver` | `git.kindred-systems.com/kindred/solver` | `fe41fa3` |
| GSL | `src/3rdParty/GSL` | `github.com/microsoft/GSL` | `756c91a` |
| AddonManager | `src/Mod/AddonManager` | `github.com/FreeCAD/AddonManager` | `01e242e` |
| googletest | `tests/lib` | `github.com/google/googletest` | `56efe39` |
The silo submodule was split from a monorepo into three repos: `silo-client` (shared Python API client), `silo-mod` (FreeCAD workbench, used as Create's submodule), and `silo-calc` (LibreOffice Calc extension). The `silo-mod` repo includes `silo-client` as its own submodule.
OndselSolver is forked from `github.com/FreeCAD/OndselSolver` to carry a Newton-Raphson convergence fix (see [KNOWN_ISSUES.md](KNOWN_ISSUES.md#12)).

View File

@@ -1,215 +0,0 @@
# Repository State
**Last updated:** 2026-02-03
**Branch:** main @ `0ef9ffcf51`
**Kindred Create:** v0.1.0
**FreeCAD base:** v1.0.0
## Submodules
| Submodule | Path | Source | Pinned commit |
|-----------|------|--------|---------------|
| ztools | `mods/ztools` | `gitea.kindred.internal/kindred/ztools-0065` | `d2f94c3` |
| silo | `mods/silo` | `gitea.kindred.internal/kindred/silo-0062` | `17a10ab` |
| OndselSolver | `src/3rdParty/OndselSolver` | `gitea.kindred.internal/kindred/ondsel` | `e32c9cd` |
| GSL | `src/3rdParty/GSL` | `github.com/microsoft/GSL` | `756c91a` |
| AddonManager | `src/Mod/AddonManager` | `github.com/FreeCAD/AddonManager` | `01e242e` |
| googletest | `tests/lib` | `github.com/google/googletest` | `56efe39` |
---
## Architecture
### Bootstrap flow
```
FreeCAD startup
└─ src/Mod/Create/Init.py
└─ setup_kindred_addons()
├─ exec(mods/ztools/ztools/Init.py)
└─ exec(mods/silo/pkg/freecad/Init.py)
└─ src/Mod/Create/InitGui.py
├─ setup_kindred_workbenches()
│ ├─ exec(mods/ztools/ztools/InitGui.py)
│ │ ├─ registers ZToolsWorkbench
│ │ └─ installs _ZToolsPartDesignManipulator (global)
│ └─ exec(mods/silo/pkg/freecad/InitGui.py)
│ └─ registers SiloWorkbench
└─ Deferred setup (QTimer):
├─ 1500ms: _setup_silo_auth_panel() → "Database Auth" dock
├─ 2000ms: _setup_silo_menu() → SiloMenuManipulator
├─ 3000ms: _check_silo_first_start() → settings prompt
└─ 4000ms: _setup_silo_activity_panel() → "Database Activity" dock
```
### Key source layout
```
src/Mod/Create/ Kindred bootstrap module (Python)
├── Init.py Adds mods/ addon paths, loads Init.py files
└── InitGui.py Loads workbenches, installs Silo manipulators
mods/ztools/ [submodule] ztools workbench
├── ztools/InitGui.py ZToolsWorkbench + PartDesign manipulator
├── ztools/ztools/
│ ├── commands/ Datum, pattern, pocket, assembly, spreadsheet
│ ├── datums/core.py Datum creation via Part::AttachExtension
│ └── resources/ Icons, theme utilities
└── CatppuccinMocha/ Theme preference pack (QSS)
mods/silo/ [submodule] Silo parts database
├── cmd/ Go server entry points
├── internal/ Go API, database, storage packages
├── pkg/freecad/ FreeCAD workbench (Python)
│ ├── InitGui.py SiloWorkbench
│ └── silo_commands.py Commands + SiloClient API
├── deployments/ Docker compose configuration
└── migrations/ PostgreSQL schema migrations
src/Gui/Stylesheets/ QSS themes and SVG assets
resources/preferences/ Canonical preference pack (KindredCreate)
```
---
## Component status
### ztools workbench
**Registered commands (9):**
| Command | Function |
|---------|----------|
| `ZTools_DatumCreator` | Create datum planes, axes, points (16 modes) |
| `ZTools_DatumManager` | Manage existing datum objects |
| `ZTools_EnhancedPocket` | Flip-side pocket (cut outside sketch profile) |
| `ZTools_RotatedLinearPattern` | Linear pattern with incremental rotation |
| `ZTools_AssemblyLinearPattern` | Pattern assembly components linearly |
| `ZTools_AssemblyPolarPattern` | Pattern assembly components around axis |
| `ZTools_SpreadsheetStyle{Bold,Italic,Underline}` | Text style toggles |
| `ZTools_SpreadsheetAlign{Left,Center,Right}` | Cell alignment |
| `ZTools_Spreadsheet{BgColor,TextColor,QuickAlias}` | Colors and alias creation |
**PartDesign integration** via `_ZToolsPartDesignManipulator`:
- `ZTools_DatumCreator`, `ZTools_DatumManager` → "Part Design Helper Features" toolbar
- `ZTools_EnhancedPocket` → "Part Design Modeling Features" toolbar
- `ZTools_RotatedLinearPattern` → "Part Design Transformation Features" toolbar
- Same commands inserted into Part Design menu after `PartDesign_Boolean`
**Datum types (7):** offset_from_face, offset_from_plane, midplane, 3_points, normal_to_edge, angled, tangent_to_cylinder. All except tangent_to_cylinder use `Part::AttachExtension` for automatic parametric updates.
### Silo workbench
**Registered commands (13):**
| Command | Function |
|---------|----------|
| `Silo_New` | Create new Silo-tracked document |
| `Silo_Open` | Open file from Silo database |
| `Silo_Save` | Save to Silo (create revision) |
| `Silo_Commit` | Commit current revision |
| `Silo_Pull` | Pull latest revision from server |
| `Silo_Push` | Push local changes to server |
| `Silo_Info` | View item metadata and history |
| `Silo_BOM` | Bill of materials dialog (BOM + Where Used) |
| `Silo_TagProjects` | Assign project tags |
| `Silo_Rollback` | Rollback to previous revision |
| `Silo_SetStatus` | Set revision status (draft/review/released/obsolete) |
| `Silo_Settings` | Configure API URL, projects dir, SSL certificates |
| `Silo_ToggleMode` | Swap Ctrl+O/S/N between FreeCAD and Silo commands |
**Global integration** via `SiloMenuManipulator` in `src/Mod/Create/InitGui.py`:
- File menu: Silo_New, Silo_Open, Silo_Save, Silo_Commit, Silo_Pull, Silo_Push, Silo_BOM
- File toolbar: Silo_ToggleMode button
**Server architecture:** Go REST API (38 routes) + PostgreSQL + MinIO. See `mods/silo/docs/REPOSITORY_STATUS.md` for route details.
### Theme
**Canonical source:** `resources/preferences/KindredCreate/KindredCreate.qss`
Four copies must stay in sync:
1. `resources/preferences/KindredCreate/KindredCreate.qss` (canonical)
2. `src/Gui/Stylesheets/KindredCreate.qss`
3. `src/Gui/PreferencePacks/KindredCreate/KindredCreate.qss`
4. `mods/ztools/CatppuccinMocha/CatppuccinMocha.qss`
---
## Known issues
### Critical
1. **QSS duplication.** Four copies of the stylesheet must be kept in sync manually. A build step or symlinks should eliminate this.
2. **WorkbenchManipulator timing.** The `_ZToolsPartDesignManipulator` appends commands by name. If ZToolsWorkbench hasn't been activated when the user switches to PartDesign, the commands may not be registered. The manipulator API tolerates missing commands silently, but buttons won't appear.
3. **Silo shortcut persistence.** `Silo_ToggleMode` stores original shortcuts in a module-level dict. If FreeCAD crashes with Silo mode on, original shortcuts are lost on next launch.
### High
4. **No authentication on Silo server.** All API endpoints are publicly accessible. Required before multi-user deployment.
5. **No unit tests.** Zero test coverage for ztools and Silo FreeCAD commands. Silo Go backend also lacks tests.
6. **Assembly solver datum handling is minimal.** The `findPlacement()` fix extracts placement from `obj.Shape.Faces[0]` for `PartDesign::Plane`. Does not handle empty shapes or non-planar datum objects.
### Medium
7. **`Silo_BOM` requires Silo-tracked document.** Depends on `SiloPartNumber` property. Unregistered documents show a warning with no registration path.
8. **PartDesign menu insertion fragility.** `_ZToolsPartDesignManipulator.modifyMenuBar()` inserts after `PartDesign_Boolean`. If upstream renames this command, insertions silently fail.
9. **tangent_to_cylinder falls back to manual placement.** TangentPlane MapMode requires a vertex reference not collected by the current UI.
10. **`delete_bom_entry()` bypasses error normalization.** Uses raw `urllib.request` instead of `SiloClient._request()`.
---
## Incomplete features
### Silo
| Feature | Status | Notes |
|---------|--------|-------|
| Authentication/authorization | Not implemented | Required for multi-user |
| File locking | Not implemented | Needed to prevent concurrent edits |
| Odoo ERP integration | Stub only | Returns "not yet implemented" |
| Part number date segments | Broken | `formatDate()` returns error |
| Location/inventory APIs | Tables exist, no handlers | |
| CSRF protection | Not implemented | Web UI only |
| CSV import rollback | Not implemented | `bom_handlers.go` |
### ztools
| Feature | Status | Notes |
|---------|--------|-------|
| Tangent-to-cylinder attachment | Manual fallback | No vertex ref in UI |
| Angled datum live editing | Incomplete | AttachmentOffset not updated in panel |
| Assembly pattern undo | Not implemented | |
### Integration plan
| Phase | Feature | Status |
|-------|---------|--------|
| 1 | Addon auto-loading | Done |
| 2 | Enhanced Pocket as C++ feature | Not started |
| 3 | Datum C++ helpers | Not started (Python approach used) |
| 4 | Theme moved to Create module | Partial (QSS synced, not relocated) |
| 5 | Silo deep integration | Done |
| 6 | Build system install rules for mods/ | Partial (CI/CD done, CMake install rules pending) |
---
## Next steps
1. **Authentication** -- LDAP/FreeIPA integration for Silo multi-user deployment. Server needs auth middleware; FreeCAD client needs credential storage.
2. **BOM-Assembly bridge** -- Auto-populate Silo BOM from Assembly component links on save.
3. **File locking** -- Pessimistic locks on `Silo_Open` to prevent concurrent edits. Requires server-side lock table and client-side lock display.
4. **Build system** -- CMake install rules for `mods/` submodules so packages include ztools and Silo without manual steps.
5. **Test coverage** -- Unit tests for ztools datum creation, Silo FreeCAD commands, and Go API endpoints.

View File

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<rect x="6" y="6" width="9" height="9" rx="1" fill="none" stroke="#89b4fa" stroke-width="1.5"/>
<rect x="17" y="6" width="9" height="9" rx="1" fill="none" stroke="#a6e3a1" stroke-width="1.5"/>
<rect x="6" y="17" width="9" height="9" rx="1" fill="none" stroke="#f9e2af" stroke-width="1.5"/>
<rect x="17" y="17" width="9" height="9" rx="1" fill="none" stroke="#cba6f7" stroke-width="1.5"/>
<line x1="21" y1="19" x2="21" y2="25" stroke="#cba6f7" stroke-width="1.5"/>
<line x1="18" y1="22" x2="24" y2="22" stroke="#cba6f7" stroke-width="1.5"/>
</svg>

After

Width:  |  Height:  |  Size: 675 B

View File

@@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 32 32"
version="1.1"
id="svg7"
sodipodi:docname="AssemblyWorkbench.svg"
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs7" />
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="25"
inkscape:cx="16"
inkscape:cy="16"
inkscape:window-width="1920"
inkscape:window-height="1011"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg7" />
<rect
x="2"
y="2"
width="28"
height="28"
rx="4"
fill="#313244"
id="rect1" />
<!-- Multiple parts assembled -->
<!-- Part 1 - cube -->
<path
d="M6 18 L6 12 L12 9 L18 12 L18 18 L12 21 Z"
fill="#45475a"
stroke="#a6e3a1"
stroke-width="1.5"
id="path1" />
<path
d="M6 12 L12 15 L18 12"
stroke="#a6e3a1"
stroke-width="1"
fill="none"
id="path2" />
<path
d="M12 15 L12 21"
stroke="#a6e3a1"
stroke-width="1"
id="path3" />
<!-- Part 2 - connected piece -->
<path
d="m 15.16,22.52 v -6 l 6,-3 6,3 v 6 l -6,3 z"
fill="#45475a"
stroke="#94e2d5"
stroke-width="1.5"
id="path4"
style="stroke:#74c7ec;stroke-opacity:1" />
<path
d="m 15.16,16.52 6,3 6,-3"
stroke="#94e2d5"
stroke-width="1"
fill="none"
id="path5"
style="stroke:#74c7ec;stroke-opacity:1" />
<path
d="m 21.16,19.52 v 6"
stroke="#94e2d5"
stroke-width="1"
id="path6"
style="stroke:#74c7ec;stroke-opacity:1" />
<path
d="m 11.11,13.318525 v -6 l 6,-3 6,3 v 6 l -6,3 z"
fill="#45475a"
stroke="#94e2d5"
stroke-width="1.5"
id="path4-6" />
<path
d="m 11.11,7.318525 6,3 6,-3"
stroke="#94e2d5"
stroke-width="1"
fill="none"
id="path5-2" />
<path
d="m 17.11,10.318525 v 6"
stroke="#94e2d5"
stroke-width="1"
id="path6-9" />
<!-- Connection indicator -->
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Multiple parts -->
<rect x="5" y="14" width="8" height="8" rx="1" fill="#45475a" stroke="#a6e3a1" stroke-width="1.5"/>
<rect x="19" y="14" width="8" height="8" rx="1" fill="#45475a" stroke="#94e2d5" stroke-width="1.5"/>
<!-- Connection line -->
<line x1="13" y1="18" x2="19" y2="18" stroke="#a6e3a1" stroke-width="2" stroke-dasharray="2,2"/>
<!-- Plus sign for create -->
<circle cx="24" cy="8" r="5" fill="#a6e3a1"/>
<path d="M24 5.5 L24 10.5 M21.5 8 L26.5 8" stroke="#1e1e2e" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 678 B

View File

@@ -0,0 +1,16 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Two parts -->
<rect x="4" y="12" width="8" height="10" rx="1" fill="#45475a" stroke="#a6e3a1" stroke-width="1.5"/>
<rect x="20" y="12" width="8" height="10" rx="1" fill="#45475a" stroke="#94e2d5" stroke-width="1.5"/>
<!-- Distance dimension -->
<line x1="12" y1="17" x2="20" y2="17" stroke="#f9e2af" stroke-width="1.5"/>
<line x1="12" y1="14" x2="12" y2="20" stroke="#f9e2af" stroke-width="1"/>
<line x1="20" y1="14" x2="20" y2="20" stroke="#f9e2af" stroke-width="1"/>
<!-- Arrows -->
<path d="M12 17 L14 15 L14 19 Z" fill="#f9e2af"/>
<path d="M20 17 L18 15 L18 19 Z" fill="#f9e2af"/>
<!-- Distance value -->
<rect x="13" y="6" width="6" height="5" rx="1" fill="#313244" stroke="#fab387" stroke-width="1"/>
<text x="16" y="10" font-family="monospace" font-size="5" fill="#cdd6f4" text-anchor="middle">d</text>
</svg>

After

Width:  |  Height:  |  Size: 976 B

View File

@@ -0,0 +1,12 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Two parts -->
<rect x="5" y="10" width="10" height="12" rx="1" fill="#45475a" stroke="#a6e3a1" stroke-width="1.5"/>
<rect x="17" y="10" width="10" height="12" rx="1" fill="#45475a" stroke="#94e2d5" stroke-width="1.5"/>
<!-- Fixed joint indicator - lock -->
<rect x="13" y="14" width="6" height="6" rx="1" fill="#f9e2af" stroke="#fab387" stroke-width="1"/>
<path d="M14 14 L14 12 A2 2 0 0 1 18 12 L18 14" fill="none" stroke="#fab387" stroke-width="1.5"/>
<!-- Ground symbol -->
<line x1="10" y1="26" x2="22" y2="26" stroke="#6c7086" stroke-width="2"/>
<line x1="12" y1="28" x2="20" y2="28" stroke="#6c7086" stroke-width="1.5"/>
</svg>

After

Width:  |  Height:  |  Size: 785 B

View File

@@ -0,0 +1,12 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Two parts connected by revolute joint -->
<rect x="4" y="12" width="10" height="8" rx="1" fill="#45475a" stroke="#a6e3a1" stroke-width="1.5"/>
<rect x="18" y="12" width="10" height="8" rx="1" fill="#45475a" stroke="#94e2d5" stroke-width="1.5"/>
<!-- Revolute joint - hinge circle -->
<circle cx="16" cy="16" r="5" fill="#313244" stroke="#f9e2af" stroke-width="2"/>
<circle cx="16" cy="16" r="2" fill="#fab387"/>
<!-- Rotation arrow -->
<path d="M22 6 A8 8 0 0 1 10 6" stroke="#fab387" stroke-width="2" fill="none" stroke-linecap="round"/>
<path d="M12 4 L10 6 L12 9" stroke="#fab387" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 820 B

View File

@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Rail/track -->
<rect x="4" y="14" width="24" height="4" rx="1" fill="#45475a" stroke="#6c7086" stroke-width="1"/>
<!-- Sliding part -->
<rect x="10" y="10" width="8" height="12" rx="1" fill="#45475a" stroke="#a6e3a1" stroke-width="1.5"/>
<!-- Slide direction arrows -->
<path d="M6 8 L4 8 L4 24 L6 24" stroke="#f9e2af" stroke-width="2" fill="none" stroke-linecap="round"/>
<path d="M26 8 L28 8 L28 24 L26 24" stroke="#f9e2af" stroke-width="2" fill="none" stroke-linecap="round"/>
<!-- Motion arrow -->
<path d="M20 16 L26 16" stroke="#94e2d5" stroke-width="2" stroke-linecap="round"/>
<path d="M23 13 L27 16 L23 19" stroke="#94e2d5" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 871 B

View File

@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Part being inserted -->
<path d="M8 20 L8 12 L14 9 L20 12 L20 20 L14 23 Z" fill="#45475a" stroke="#a6e3a1" stroke-width="1.5"/>
<path d="M8 12 L14 15 L20 12" stroke="#a6e3a1" stroke-width="1" fill="none"/>
<path d="M14 15 L14 23" stroke="#a6e3a1" stroke-width="1"/>
<!-- Arrow indicating insertion -->
<path d="M24 8 L24 18" stroke="#94e2d5" stroke-width="2.5" stroke-linecap="round"/>
<path d="M21 15 L24 19 L27 15" stroke="#94e2d5" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 668 B

View File

@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 32 32"
version="1.1"
id="svg5"
sodipodi:docname="Constraint_Dimension.svg"
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs5" />
<sodipodi:namedview
id="namedview5"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="25"
inkscape:cx="16"
inkscape:cy="19.2"
inkscape:window-width="1920"
inkscape:window-height="1011"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg5" />
<rect
x="2"
y="2"
width="28"
height="28"
rx="4"
fill="#313244"
id="rect1" />
<!-- Line being dimensioned -->
<!-- Dimension line -->
<line
x1="6"
y1="12"
x2="26"
y2="12"
stroke="#a6e3a1"
stroke-width="1.5"
id="line2" />
<!-- Extension lines -->
<line
x1="6"
y1="20"
x2="6"
y2="10"
stroke="#a6e3a1"
stroke-width="1"
id="line3" />
<line
x1="26"
y1="20"
x2="26"
y2="10"
stroke="#a6e3a1"
stroke-width="1"
id="line4" />
<!-- Arrows -->
<path
d="M6 12 L10 10 L10 14 Z"
fill="#a6e3a1"
id="path4" />
<path
d="M26 12 L22 10 L22 14 Z"
fill="#a6e3a1"
id="path5" />
<line
x1="6"
y1="20"
x2="26"
y2="20"
stroke="#f9e2af"
stroke-width="2"
stroke-linecap="round"
id="line1" />
<!-- Dimension text -->
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Horizontal line -->
<line x1="6" y1="16" x2="26" y2="16" stroke="#f9e2af" stroke-width="2.5" stroke-linecap="round"/>
<!-- End points -->
<circle cx="6" cy="16" r="2.5" fill="#fab387"/>
<circle cx="26" cy="16" r="2.5" fill="#fab387"/>
<!-- H indicator -->
<text x="16" y="10" font-family="sans-serif" font-size="8" font-weight="bold" fill="#a6e3a1" text-anchor="middle">H</text>
<!-- Constraint arrows -->
<path d="M10 22 L6 22 L6 20" stroke="#a6e3a1" stroke-width="1.5" fill="none" stroke-linecap="round"/>
<path d="M22 22 L26 22 L26 20" stroke="#a6e3a1" stroke-width="1.5" fill="none" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 772 B

View File

@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 32 32"
version="1.1"
id="svg2"
sodipodi:docname="Constraint_Perpendicular.svg"
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2" />
<sodipodi:namedview
id="namedview2"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="25"
inkscape:cx="16"
inkscape:cy="16"
inkscape:window-width="1920"
inkscape:window-height="1011"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<rect
x="2"
y="2"
width="28"
height="28"
rx="4"
fill="#313244"
id="rect1" />
<!-- Horizontal line -->
<path
d="m 15.969398,16.225639 h 6.224963 v 5.744963"
fill="none"
stroke="#a6e3a1"
stroke-width="2.99007"
id="path2"
style="stroke-width:1.85574803;stroke-dasharray:none" />
<line
x1="6"
y1="22"
x2="26"
y2="22"
stroke="#f9e2af"
stroke-width="2"
stroke-linecap="round"
id="line1" />
<!-- Vertical line -->
<line
x1="16"
y1="6"
x2="16"
y2="22"
stroke="#f9e2af"
stroke-width="2"
stroke-linecap="round"
id="line2" />
<!-- Right angle indicator -->
<!-- Intersection point -->
<circle
cx="16"
cy="22"
r="2"
fill="#fab387"
id="circle2" />
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Two lines meeting at a point -->
<line x1="6" y1="26" x2="16" y2="16" stroke="#f9e2af" stroke-width="2"/>
<line x1="26" y1="6" x2="16" y2="16" stroke="#f9e2af" stroke-width="2"/>
<!-- Coincident point -->
<circle cx="16" cy="16" r="4" fill="#a6e3a1"/>
<circle cx="16" cy="16" r="2" fill="#1e1e2e"/>
</svg>

After

Width:  |  Height:  |  Size: 451 B

View File

@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Vertical line -->
<line x1="16" y1="6" x2="16" y2="26" stroke="#f9e2af" stroke-width="2.5" stroke-linecap="round"/>
<!-- End points -->
<circle cx="16" cy="6" r="2.5" fill="#fab387"/>
<circle cx="16" cy="26" r="2.5" fill="#fab387"/>
<!-- V indicator -->
<text x="24" y="18" font-family="sans-serif" font-size="8" font-weight="bold" fill="#a6e3a1" text-anchor="middle">V</text>
<!-- Constraint arrows -->
<path d="M8 10 L8 6 L10 6" stroke="#a6e3a1" stroke-width="1.5" fill="none" stroke-linecap="round"/>
<path d="M8 22 L8 26 L10 26" stroke="#a6e3a1" stroke-width="1.5" fill="none" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 766 B

117
kindred-icons/Document.svg Normal file
View File

@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 32 32"
version="1.1"
id="svg4"
sodipodi:docname="Document.svg"
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs4">
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect5"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.8970359,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect4"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,2.25625,0,1 @ F,0,0,1,0,1.411235,0,1 @ F,0,0,1,0,1.2732549,0,1 @ F,0,0,1,0,2.25625,0,1 @ F,0,0,1,0,1.875,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
</defs>
<sodipodi:namedview
id="namedview4"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="32"
inkscape:cx="10.90625"
inkscape:cy="12.21875"
inkscape:window-width="2560"
inkscape:window-height="1371"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg4" />
<rect
width="32"
height="32"
rx="4"
fill="#313244"
id="rect1" />
<path
d="m 10.246497,6 h 8.332515 a 3.4070227,3.4070227 22.5 0 1 2.409129,0.9978938 l 2.101779,2.101779 a 3.0739092,3.0739092 67.5 0 1 0.900327,2.1735822 l 0,12.470495 A 2.25625,2.25625 135 0 1 21.733997,26 H 9.8652468 a 1.875,1.875 45 0 1 -1.875,-1.875 V 8.25625 A 2.25625,2.25625 135 0 1 10.246497,6 Z"
fill="none"
stroke="#89b4fa"
stroke-width="1.5"
id="path1"
inkscape:path-effect="#path-effect4"
inkscape:original-d="M 7.9902468,6 H 19.990247 l 4,4 V 26 H 7.9902468 Z" />
<path
d="m 19.707388,5.8623613 v 3.4443418 a 0.8970359,0.8970359 45 0 0 0.897036,0.8970359 h 3.385823"
fill="none"
stroke="#74c7ec"
stroke-width="1.61701"
id="path2"
style="stroke:#89b4fa;stroke-width:1.5;stroke-dasharray:none;stroke-opacity:1"
inkscape:path-effect="#path-effect5"
inkscape:original-d="m 19.707388,5.8623613 v 4.3413777 h 4.282859" />
<line
x1="11"
y1="14"
x2="21"
y2="14"
stroke="#74c7ec"
stroke-width="1.5"
id="line2"
style="stroke-linecap:round" />
<line
x1="11"
y1="18"
x2="21"
y2="18"
stroke="#74c7ec"
stroke-width="1.5"
id="line3"
style="stroke-linecap:round" />
<line
x1="11"
y1="22"
x2="17"
y2="22"
stroke="#74c7ec"
stroke-width="1.5"
id="line4"
style="stroke-linecap:round" />
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -0,0 +1,16 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Drafting tools - pencil and triangle -->
<!-- Triangle/Set square -->
<path d="M6 26 L6 10 L22 26 Z" fill="#45475a" stroke="#fab387" stroke-width="1.5"/>
<!-- Pencil -->
<g transform="translate(16, 4) rotate(45)">
<rect x="0" y="0" width="4" height="16" rx="0.5" fill="#f9e2af" stroke="#fab387" stroke-width="1"/>
<path d="M0 16 L2 20 L4 16 Z" fill="#585b70" stroke="#fab387" stroke-width="0.5"/>
<rect x="0" y="0" width="4" height="3" fill="#f38ba8"/>
</g>
<!-- Grid dots -->
<circle cx="10" cy="22" r="1" fill="#6c7086"/>
<circle cx="14" cy="22" r="1" fill="#6c7086"/>
<circle cx="10" cy="18" r="1" fill="#6c7086"/>
</svg>

After

Width:  |  Height:  |  Size: 790 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<ellipse cx="16" cy="16" rx="10" ry="6" fill="none" stroke="#cdd6f4" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="16" cy="16" r="3" fill="none" stroke="#cdd6f4" stroke-width="1.5"/>
</svg>

After

Width:  |  Height:  |  Size: 344 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<path d="M5.5 9.5 16 4l10.5 5.5v13L16 28 5.5 22.5z" fill="#45475a" stroke="#89b4fa" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.5 9.5 16 15l10.5-5.5M16 15v13" fill="none" stroke="#89b4fa" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 419 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<path d="M5.5 9.5 16 4l10.5 5.5v13L16 28 5.5 22.5z" fill="#585b70" stroke="#cdd6f4" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.5 9.5 16 15l10.5-5.5M16 15v13" fill="none" stroke="#cdd6f4" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 419 B

View File

@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<circle cx="16" cy="4" r="1.5" fill="#cdd6f4"/>
<circle cx="5.5" cy="9.5" r="1.5" fill="#cdd6f4"/>
<circle cx="26.5" cy="9.5" r="1.5" fill="#cdd6f4"/>
<circle cx="16" cy="15" r="1.5" fill="#cdd6f4"/>
<circle cx="5.5" cy="22.5" r="1.5" fill="#cdd6f4"/>
<circle cx="26.5" cy="22.5" r="1.5" fill="#cdd6f4"/>
<circle cx="16" cy="28" r="1.5" fill="#cdd6f4"/>
</svg>

After

Width:  |  Height:  |  Size: 491 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<path d="M5.5 9.5 16 4l10.5 5.5v13L16 28 5.5 22.5z" fill="#45475a" stroke="#cdd6f4" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.5 9.5 16 15l10.5-5.5M16 15v13" fill="none" stroke="#cdd6f4" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 419 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<path d="M5.5 9.5 16 4l10.5 5.5v13L16 28 5.5 22.5z" fill="none" stroke="#cdd6f4" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.5 9.5 16 15l10.5-5.5M16 15v13" fill="none" stroke="#cdd6f4" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 416 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<path d="M8 24 L8 12 L16 6 L24 12 L24 24 Z" fill="none" stroke="#89b4fa" stroke-width="1.5"/>
<path d="M8 12 L16 18 L24 12 M16 18 L16 6" fill="none" stroke="#74c7ec" stroke-width="1.5"/>
</svg>

After

Width:  |  Height:  |  Size: 314 B

View File

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<rect x="6" y="10" width="8" height="12" rx="1" fill="none" stroke="#a6e3a1" stroke-width="1.5"/>
<rect x="18" y="10" width="8" height="12" rx="1" fill="none" stroke="#a6e3a1" stroke-width="1.5"/>
<line x1="14" y1="14" x2="18" y2="14" stroke="#94e2d5" stroke-width="2"/>
<line x1="14" y1="18" x2="18" y2="18" stroke="#94e2d5" stroke-width="2"/>
</svg>

After

Width:  |  Height:  |  Size: 476 B

View File

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<circle cx="16" cy="16" r="8" fill="none" stroke="#89b4fa" stroke-width="1.5"/>
<circle cx="16" cy="16" r="4" fill="none" stroke="#74c7ec" stroke-width="1.5"/>
<line x1="16" y1="4" x2="16" y2="8" stroke="#74c7ec" stroke-width="1.5"/>
<line x1="16" y1="24" x2="16" y2="28" stroke="#74c7ec" stroke-width="1.5"/>
<line x1="4" y1="16" x2="8" y2="16" stroke="#74c7ec" stroke-width="1.5"/>
<line x1="24" y1="16" x2="28" y2="16" stroke="#74c7ec" stroke-width="1.5"/>
</svg>

After

Width:  |  Height:  |  Size: 595 B

5
kindred-icons/Group.svg Normal file
View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<rect x="6" y="10" width="20" height="16" rx="2" fill="none" stroke="#89b4fa" stroke-width="1.5"/>
<path d="M6 10 L6 8 C6 7 7 6 8 6 L14 6 L16 10" fill="none" stroke="#74c7ec" stroke-width="1.5"/>
</svg>

After

Width:  |  Height:  |  Size: 323 B

5
kindred-icons/InTray.svg Normal file
View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<path d="M6 16 L6 24 L26 24 L26 16 L20 16 L18 20 L14 20 L12 16 Z" fill="none" stroke="#89b4fa" stroke-width="1.5"/>
<path d="M16 6 L16 14 M12 10 L16 14 L20 10" stroke="#74c7ec" stroke-width="1.5"/>
</svg>

After

Width:  |  Height:  |  Size: 325 B

View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<path d="M6 16 L6 24 L26 24 L26 16 L20 16 L18 20 L14 20 L12 16 Z" fill="none" stroke="#89b4fa" stroke-width="1.5"/>
<circle cx="24" cy="8" r="4" fill="#f38ba8"/>
<text x="24" y="11" text-anchor="middle" font-family="sans-serif" font-size="6" font-weight="bold" fill="#313244">!</text>
</svg>

After

Width:  |  Height:  |  Size: 414 B

View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<ellipse cx="16" cy="16" rx="10" ry="6" fill="none" stroke="#585b70" stroke-width="1.5"/>
<circle cx="16" cy="16" r="3" fill="#585b70"/>
<line x1="6" y1="24" x2="26" y2="8" stroke="#f38ba8" stroke-width="2"/>
</svg>

After

Width:  |  Height:  |  Size: 338 B

55
kindred-icons/Link.svg Normal file
View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 32 32"
version="1.1"
id="svg3"
sodipodi:docname="Link.svg"
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs3" />
<sodipodi:namedview
id="namedview3"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="25.632621"
inkscape:cx="14.005591"
inkscape:cy="15.839192"
inkscape:window-width="2560"
inkscape:window-height="1371"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg3" />
<rect
width="32"
height="32"
rx="4"
fill="#313244"
id="rect1" />
<path
d="M 12.013793,20 H 8.0137931 a 4,4 0 0 1 0,-8 h 3.9999999"
fill="none"
stroke="#89b4fa"
stroke-width="1.5"
id="path1" />
<path
d="m 20.013793,12 h 4 a 4,4 0 0 1 0,8 h -4"
fill="none"
stroke="#74c7ec"
stroke-width="1.5"
id="path2"
style="stroke:#89b4fa;stroke-opacity:1" />
<path
style="fill:none;stroke:#89b4fa;stroke-width:1.77369;stroke-dasharray:none;stroke-opacity:1"
d="M 9.6965515,16 H 22.303449"
id="path3" />
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 32 32"
version="1.1"
id="svg3"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<rect
width="32"
height="32"
rx="4"
fill="#313244"
id="rect1" />
<circle
cx="10"
cy="10"
r="4"
fill="none"
stroke="#89b4fa"
stroke-width="1.5"
id="circle1" />
<circle
cx="22"
cy="10"
r="4"
fill="none"
stroke="#89b4fa"
stroke-width="1.5"
id="circle2" />
<circle
cx="10"
cy="22"
r="4"
fill="none"
stroke="#89b4fa"
stroke-width="1.5"
id="circle3" />
<circle
cx="22"
cy="22"
r="4"
fill="none"
stroke="#89b4fa"
stroke-width="1.5"
id="circle4" />
<line
x1="14"
y1="10"
x2="18"
y2="10"
stroke="#cba6f7"
stroke-width="1.5"
id="line1" />
<line
x1="10"
y1="14"
x2="10"
y2="18"
stroke="#cba6f7"
stroke-width="1.5"
id="line2" />
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 32 32"
version="1.1"
id="svg3"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<rect
width="32"
height="32"
rx="4"
fill="#313244"
id="rect1" />
<rect
x="6"
y="6"
width="10"
height="10"
rx="1.5"
fill="none"
stroke="#89b4fa"
stroke-width="1.5"
id="rect2" />
<rect
x="10"
y="10"
width="10"
height="10"
rx="1.5"
fill="#313244"
stroke="#cba6f7"
stroke-width="1.5"
id="rect3" />
<rect
x="14"
y="14"
width="10"
height="10"
rx="1.5"
fill="#313244"
stroke="#89b4fa"
stroke-width="1.5"
id="rect4" />
<circle
cx="24"
cy="8"
r="4"
fill="none"
stroke="#a6e3a1"
stroke-width="1.5"
id="circle1" />
</svg>

After

Width:  |  Height:  |  Size: 910 B

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 32 32"
version="1.1"
id="svg3"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<rect
width="32"
height="32"
rx="4"
fill="#313244"
id="rect1" />
<rect
x="8"
y="8"
width="10"
height="10"
rx="1.5"
fill="none"
stroke="#89b4fa"
stroke-width="1.5"
id="rect2" />
<path
d="M 18,18 24,24"
stroke="#89b4fa"
stroke-width="1.5"
id="path1" />
<circle
cx="24"
cy="24"
r="3"
fill="#cba6f7"
id="circle1" />
</svg>

After

Width:  |  Height:  |  Size: 636 B

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 32 32"
version="1.1"
id="svg3"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<rect
width="32"
height="32"
rx="4"
fill="#313244"
id="rect1" />
<rect
x="6"
y="10"
width="20"
height="14"
rx="2"
fill="none"
stroke="#89b4fa"
stroke-width="1.5"
id="rect2" />
<circle
cx="12"
cy="17"
r="3"
fill="none"
stroke="#cba6f7"
stroke-width="1.5"
id="circle1" />
<circle
cx="20"
cy="17"
r="3"
fill="none"
stroke="#cba6f7"
stroke-width="1.5"
id="circle2" />
<line
x1="15"
y1="17"
x2="17"
y2="17"
stroke="#89b4fa"
stroke-width="1.5"
id="line1" />
</svg>

After

Width:  |  Height:  |  Size: 838 B

View File

@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 32 32"
version="1.1"
id="svg3"
sodipodi:docname="LinkImport.svg"
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs3" />
<sodipodi:namedview
id="namedview3"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="25.632621"
inkscape:cx="14.005591"
inkscape:cy="15.800179"
inkscape:window-width="2560"
inkscape:window-height="1371"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg3" />
<rect
width="32"
height="32"
rx="4"
fill="#313244"
id="rect1" />
<path
d="M 12.013793,20 H 8.0137931 a 4,4 0 0 1 0,-8 h 3.9999999"
fill="none"
stroke="#89b4fa"
stroke-width="1.5"
id="path1" />
<path
d="m 20.013793,12 h 4 a 4,4 0 0 1 0,8 h -4"
fill="none"
stroke="#74c7ec"
stroke-width="1.5"
id="path2"
style="stroke:#89b4fa;stroke-opacity:1" />
<path
style="fill:none;stroke:#89b4fa;stroke-width:1.77369;stroke-dasharray:none;stroke-opacity:1"
d="M 9.6965515,16 H 22.303449"
id="path3" />
<circle
cx="25.513792"
cy="-9.2321157"
fill="#a6e3a1"
id="circle2"
style="stroke-width:0.999999"
transform="scale(1,-1)"
r="5" />
<path
d="M 25.51379,11.732116 V 6.7321179 m -2.499999,2.4999988 h 5"
stroke="#1e1e2e"
stroke-width="1.5"
stroke-linecap="round"
id="path3-7" />
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 32 32"
version="1.1"
id="svg3"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<rect
width="32"
height="32"
rx="4"
fill="#313244"
id="rect1" />
<circle
cx="10"
cy="10"
r="3"
fill="none"
stroke="#89b4fa"
stroke-width="1.5"
id="circle1" />
<circle
cx="22"
cy="10"
r="3"
fill="none"
stroke="#89b4fa"
stroke-width="1.5"
id="circle2" />
<circle
cx="10"
cy="20"
r="3"
fill="none"
stroke="#89b4fa"
stroke-width="1.5"
id="circle3" />
<path
d="M 24,18 24,26 18,22 Z"
fill="#a6e3a1"
id="path1" />
</svg>

After

Width:  |  Height:  |  Size: 764 B

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 32 32"
version="1.1"
id="svg3"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<rect
width="32"
height="32"
rx="4"
fill="#313244"
id="rect1" />
<rect
x="6"
y="6"
width="12"
height="12"
rx="1.5"
fill="none"
stroke="#89b4fa"
stroke-width="1.5"
id="rect2" />
<rect
x="14"
y="14"
width="12"
height="12"
rx="1.5"
fill="#313244"
stroke="#cba6f7"
stroke-width="1.5"
id="rect3" />
</svg>

After

Width:  |  Height:  |  Size: 616 B

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 32 32"
version="1.1"
id="svg3"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<rect
width="32"
height="32"
rx="4"
fill="#313244"
id="rect1" />
<circle
cx="10"
cy="16"
r="5"
fill="none"
stroke="#89b4fa"
stroke-width="1.5"
id="circle1" />
<circle
cx="22"
cy="16"
r="5"
fill="none"
stroke="#89b4fa"
stroke-width="1.5"
id="circle2" />
<path
d="M 13,12 19,12 M 19,12 17,10 M 19,12 17,14"
stroke="#a6e3a1"
stroke-width="1.5"
fill="none"
id="path1" />
<path
d="M 19,20 13,20 M 13,20 15,18 M 13,20 15,22"
stroke="#f38ba8"
stroke-width="1.5"
fill="none"
id="path2" />
</svg>

After

Width:  |  Height:  |  Size: 837 B

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 32 32"
version="1.1"
id="svg3"
sodipodi:docname="LinkSelect.svg"
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs3" />
<sodipodi:namedview
id="namedview3"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="25.632621"
inkscape:cx="14.005591"
inkscape:cy="15.800179"
inkscape:window-width="2560"
inkscape:window-height="1371"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg3" />
<rect
width="32"
height="32"
rx="4"
fill="#313244"
id="rect1" />
<path
d="M 12.013793,20 H 8.0137931 a 4,4 0 0 1 0,-8 h 3.9999999"
fill="none"
stroke="#89b4fa"
stroke-width="1.5"
id="path1" />
<path
d="m 20.013793,12 h 4 a 4,4 0 0 1 0,8 h -4"
fill="none"
stroke="#74c7ec"
stroke-width="1.5"
id="path2"
style="stroke:#89b4fa;stroke-opacity:1" />
<path
style="fill:none;stroke:#89b4fa;stroke-width:1.77369;stroke-dasharray:none;stroke-opacity:1"
d="M 9.6965515,16 H 22.303449"
id="path3" />
<rect
style="fill:none;stroke:#fab387;stroke-width:1.056;stroke-dasharray:1.05599999, 2.11199999;stroke-linejoin:round;stroke-linecap:round;stroke-dashoffset:15.41759968;stroke-opacity:1"
id="rect2"
width="28.12822"
height="14.785847"
x="1.9358902"
y="8.6070766" />
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Mesh triangles -->
<path d="M16 6 L6 24 L26 24 Z" fill="#45475a" stroke="#f5c2e7" stroke-width="1.5"/>
<!-- Internal mesh lines -->
<line x1="16" y1="6" x2="11" y2="15" stroke="#f5c2e7" stroke-width="1"/>
<line x1="16" y1="6" x2="21" y2="15" stroke="#f5c2e7" stroke-width="1"/>
<line x1="11" y1="15" x2="21" y2="15" stroke="#f5c2e7" stroke-width="1"/>
<line x1="11" y1="15" x2="6" y2="24" stroke="#f5c2e7" stroke-width="1"/>
<line x1="21" y1="15" x2="26" y2="24" stroke="#f5c2e7" stroke-width="1"/>
<line x1="11" y1="15" x2="16" y2="24" stroke="#f5c2e7" stroke-width="1"/>
<line x1="21" y1="15" x2="16" y2="24" stroke="#f5c2e7" stroke-width="1"/>
<!-- Vertices -->
<circle cx="16" cy="6" r="2" fill="#f2cdcd"/>
<circle cx="6" cy="24" r="2" fill="#f2cdcd"/>
<circle cx="26" cy="24" r="2" fill="#f2cdcd"/>
<circle cx="11" cy="15" r="1.5" fill="#f2cdcd"/>
<circle cx="21" cy="15" r="1.5" fill="#f2cdcd"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- 3D extruded shape representing Part Design -->
<path d="M8 22 L8 12 L16 8 L24 12 L24 22 L16 26 Z" fill="#45475a" stroke="#89b4fa" stroke-width="1.5"/>
<path d="M8 12 L16 16 L24 12" stroke="#89b4fa" stroke-width="1.5" fill="none"/>
<path d="M16 16 L16 26" stroke="#89b4fa" stroke-width="1.5"/>
<!-- Top face highlight -->
<path d="M8 12 L16 8 L24 12 L16 16 Z" fill="#74c7ec" fill-opacity="0.3"/>
<!-- Gear accent indicating design/engineering -->
<circle cx="16" cy="14" r="3" fill="none" stroke="#74c7ec" stroke-width="1.5"/>
</svg>

After

Width:  |  Height:  |  Size: 682 B

View File

@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- 3D body representation -->
<path d="M6 22 L6 10 L16 6 L26 10 L26 22 L16 26 Z" fill="#45475a" stroke="#89b4fa" stroke-width="2"/>
<path d="M6 10 L16 14 L26 10" stroke="#89b4fa" stroke-width="2" fill="none"/>
<path d="M16 14 L16 26" stroke="#89b4fa" stroke-width="2"/>
<!-- Top face highlight -->
<path d="M6 10 L16 6 L26 10 L16 14 Z" fill="#74c7ec" fill-opacity="0.4"/>
<!-- Origin indicator -->
<circle cx="16" cy="16" r="2" fill="#a6e3a1"/>
<line x1="16" y1="12" x2="16" y2="20" stroke="#a6e3a1" stroke-width="1"/>
<line x1="12" y1="16" x2="20" y2="16" stroke="#a6e3a1" stroke-width="1"/>
</svg>

After

Width:  |  Height:  |  Size: 750 B

View File

@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 32 32"
version="1.1"
id="svg5"
sodipodi:docname="PartDesign_Chamfer.svg"
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs5" />
<sodipodi:namedview
id="namedview5"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="22.627417"
inkscape:cx="11.136932"
inkscape:cy="18.473165"
inkscape:window-width="1920"
inkscape:window-height="1011"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg5" />
<rect
x="2"
y="2"
width="28"
height="28"
rx="4"
fill="#313244"
id="rect1" />
<!-- Block with chamfered edge -->
<path
d="M 6,24 5.9375,16.0625 11.171008,8.026064 16,6 26,10 V 24 L 16,28 Z M 20.923535,12.327039 26.006643,9.9805673 Z"
fill="#45475a"
stroke="#89b4fa"
stroke-width="1.5"
id="path1"
sodipodi:nodetypes="ccccccccccc" />
<path
d="M 16,19.88 V 28"
stroke="#89b4fa"
stroke-width="1.5"
id="path3"
sodipodi:nodetypes="cc" />
<path
d="M 5.721457,16 10.416889,7.8973687 21,12 16.132583,20.145165 Z"
fill="#a6e3a1"
fill-opacity="0.3"
id="path5"
sodipodi:nodetypes="ccccc"
style="opacity:1;fill:#a6e3a1;fill-opacity:1" />
<!-- Chamfer cut on edge -->
<!-- Chamfer face -->
<path
d="M 21.289264,12.075764 15.938527,20.29433"
stroke="#89b4fa"
stroke-width="1.5847"
id="path3-0"
sodipodi:nodetypes="cc" />
<path
d="M 11.313916,7.9020371 5.8336127,16.235844"
stroke="#89b4fa"
stroke-width="1.61498"
id="path3-0-3"
sodipodi:nodetypes="cc" />
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 32 32"
version="1.1"
id="svg5"
sodipodi:docname="PartDesign_Fillet.svg"
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs5" />
<sodipodi:namedview
id="namedview5"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="22.627417"
inkscape:cx="2.8284271"
inkscape:cy="17.412504"
inkscape:window-width="2560"
inkscape:window-height="1371"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg5" />
<rect
x="2"
y="2"
width="28"
height="28"
rx="4"
fill="#313244"
id="rect1" />
<!-- Block with chamfered edge -->
<path
d="M 6,24 6.1584709,15.885723 10.508095,8.6005883 16,6 26,10 V 24 L 16,28 Z M 20.923535,12.327039 26.006643,9.9805673 Z"
fill="#45475a"
stroke="#89b4fa"
stroke-width="1.5"
id="path1"
sodipodi:nodetypes="ccccccccccc" />
<path
d="M 16,19.88 V 28"
stroke="#89b4fa"
stroke-width="1.5"
id="path3"
sodipodi:nodetypes="cc" />
<path
d="M 5.721457,16 10.416889,7.8973687 21,12 l -3.708852,3.943415 -1.158565,4.20175 z"
fill="#a6e3a1"
fill-opacity="0.3"
id="path5"
sodipodi:nodetypes="cccccc"
style="opacity:1;fill:#a6e3a1;fill-opacity:1" />
<!-- Chamfer cut on edge -->
<!-- Chamfer face -->
<path
d="m 25.554002,10.020735 c 0,0 -2.839088,1.13525 -4.950437,2.544214 -1.652011,1.102435 -2.601422,2.423855 -3.100562,3.159378 -1.011406,1.490386 -1.47202,3.667857 -1.538841,4.165681 -0.0066,0.04949 -0.01392,7.861307 -0.01392,7.861307 m 7.653323,-14.186366"
stroke="#89b4fa"
stroke-width="1.5847"
id="path3-0"
sodipodi:nodetypes="csssc"
style="fill:none" />
<path
d="m 16.23145,6.006509 c 0,0 -3.524098,1.0247646 -5.635447,2.4337286 -1.6520107,1.1024351 -2.6014217,2.4238554 -3.1005617,3.1593784 -1.011406,1.490386 -1.47202,3.667857 -1.538841,4.165681 -0.0066,0.04949 0.2070509,8.612608 0.2070509,8.612608"
stroke="#89b4fa"
stroke-width="1.5847"
id="path3-0-6"
sodipodi:nodetypes="csssc"
style="fill:none" />
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Block with hole -->
<path d="M4 22 L4 10 L16 6 L28 10 L28 22 L16 26 Z" fill="#45475a" stroke="#89b4fa" stroke-width="1.5"/>
<path d="M4 10 L16 14 L28 10" stroke="#89b4fa" stroke-width="1.5" fill="none"/>
<path d="M16 14 L16 26" stroke="#89b4fa" stroke-width="1.5"/>
<!-- Circular hole on top -->
<ellipse cx="16" cy="11" rx="5" ry="2" fill="#1e1e2e" stroke="#f38ba8" stroke-width="1.5"/>
<!-- Hole depth indication -->
<path d="M11 11 L11 18" stroke="#f38ba8" stroke-width="1" stroke-dasharray="2,1"/>
<path d="M21 11 L21 18" stroke="#f38ba8" stroke-width="1" stroke-dasharray="2,1"/>
<ellipse cx="16" cy="18" rx="5" ry="2" fill="none" stroke="#f38ba8" stroke-width="1" stroke-dasharray="2,1"/>
</svg>

After

Width:  |  Height:  |  Size: 855 B

View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 32 32"
version="1.1"
id="svg3"
sodipodi:docname="PartDesign_NewSketch.svg"
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs3" />
<sodipodi:namedview
id="namedview3"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="36.25"
inkscape:cx="16"
inkscape:cy="16"
inkscape:window-width="2560"
inkscape:window-height="1371"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg3" />
<rect
x="2"
y="2"
width="28"
height="28"
rx="4"
fill="#313244"
id="rect1" />
<!-- Sketch plane -->
<path
d="m 4,17.158621 12,-8.0000003 12,8.0000003 -12,8 z"
fill="#45475a"
stroke="#f9e2af"
stroke-width="1.5"
id="path1" />
<!-- Grid on plane -->
<!-- Sketch geometry -->
<!-- Plus sign for "new" -->
<circle
cx="23.092838"
cy="-10.553846"
fill="#a6e3a1"
id="circle2"
style="stroke-width:0.999999"
transform="scale(1,-1)"
r="5" />
<path
d="M 23.092837,13.053846 V 8.0538483 m -2.499999,2.4999987 h 5"
stroke="#1e1e2e"
stroke-width="1.5"
stroke-linecap="round"
id="path3-7" />
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 32 32"
version="1.1"
id="svg5"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<rect
x="2"
y="2"
width="28"
height="28"
rx="4"
fill="#313244"
id="rect1" />
<!-- Base sketch profile -->
<path
d="M 6,24 6,20 14,18 26,20 26,24 14,26 Z"
fill="#45475a"
stroke="#6c7086"
stroke-width="1"
id="path1" />
<!-- Extruded body -->
<path
d="M 6,20 6,10 14,8 26,10 26,20 14,22 Z"
fill="#45475a"
stroke="#89b4fa"
stroke-width="1.5"
id="path2" />
<path
d="M 6,10 14,12 26,10"
stroke="#89b4fa"
stroke-width="1.5"
fill="none"
id="path3" />
<path
d="M 14,12 V 22"
stroke="#89b4fa"
stroke-width="1.5"
id="path4" />
<!-- Top face -->
<path
d="M 6,10 14,8 26,10 14,12 Z"
fill="#74c7ec"
fill-opacity="0.4"
id="path5" />
<!-- Extrude arrow -->
<path
d="M 20,18 V 6"
stroke="#a6e3a1"
stroke-width="2"
stroke-linecap="round"
id="path6" />
<path
d="M 17,9 20,5 23,9"
stroke="#a6e3a1"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
fill="none"
id="path7" />
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 32 32"
version="1.1"
id="svg5"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<rect
x="2"
y="2"
width="28"
height="28"
rx="4"
fill="#313244"
id="rect1" />
<!-- Solid block -->
<path
d="M 4,22 4,10 16,6 28,10 28,22 16,26 Z"
fill="#45475a"
stroke="#89b4fa"
stroke-width="1.5"
id="path1" />
<path
d="M 4,10 16,14 28,10"
stroke="#89b4fa"
stroke-width="1.5"
fill="none"
id="path2" />
<path
d="M 16,14 V 26"
stroke="#89b4fa"
stroke-width="1.5"
id="path3" />
<!-- Pocket cut-out -->
<path
d="M 10,12 16,10 22,12 22,18 16,20 10,18 Z"
fill="#1e1e2e"
stroke="#f38ba8"
stroke-width="1.5"
id="path4" />
<path
d="M 10,12 16,14 22,12"
stroke="#f38ba8"
stroke-width="1"
fill="none"
id="path5" />
<path
d="M 16,14 V 20"
stroke="#f38ba8"
stroke-width="1"
id="path6" />
<!-- Cut arrow -->
<path
d="M 16,8 V 16"
stroke="#f38ba8"
stroke-width="1.5"
stroke-linecap="round"
id="path7" />
<path
d="M 14,13 16,17 18,13"
stroke="#f38ba8"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
fill="none"
id="path8" />
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,12 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Revolved shape - vase/cone profile -->
<ellipse cx="16" cy="24" rx="10" ry="3" fill="#45475a" stroke="#89b4fa" stroke-width="1.5"/>
<path d="M6 24 Q6 12 12 8 L20 8 Q26 12 26 24" fill="#45475a" stroke="#89b4fa" stroke-width="1.5"/>
<ellipse cx="16" cy="8" rx="4" ry="1.5" fill="#74c7ec" fill-opacity="0.4" stroke="#89b4fa" stroke-width="1"/>
<!-- Axis line -->
<line x1="16" y1="4" x2="16" y2="28" stroke="#a6e3a1" stroke-width="1.5" stroke-dasharray="3,2"/>
<!-- Rotation arrow -->
<path d="M22 6 A6 6 0 0 1 22 14" stroke="#a6e3a1" stroke-width="2" fill="none" stroke-linecap="round"/>
<path d="M20 12 L22 15 L25 13" stroke="#a6e3a1" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 870 B

View File

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Simple 3D primitive - cube with boolean indication -->
<path d="M6 20 L6 10 L16 6 L26 10 L26 20 L16 24 Z" fill="#45475a" stroke="#89b4fa" stroke-width="1.5"/>
<path d="M6 10 L16 14 L26 10" stroke="#89b4fa" stroke-width="1.5" fill="none"/>
<path d="M16 14 L16 24" stroke="#89b4fa" stroke-width="1.5"/>
<!-- Boolean cut-out indication -->
<circle cx="18" cy="15" r="4" fill="#313244" stroke="#74c7ec" stroke-width="1.5" stroke-dasharray="2,1"/>
</svg>

After

Width:  |  Height:  |  Size: 595 B

121
kindred-icons/README.md Normal file
View File

@@ -0,0 +1,121 @@
# Kindred Create Icons
This directory contains custom Catppuccin Mocha themed SVG icons that override the default FreeCAD icons.
## How It Works
Icons placed in this directory are loaded **before** the default FreeCAD icons. To override an icon, simply create an SVG file with the same name as the original icon.
For example, to override `document-save.svg`, create `kindred-icons/document-save.svg`.
## Icon Design Standards
All Kindred Create icons follow these guidelines:
### Template
```svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Icon content using Catppuccin Mocha colors -->
</svg>
```
### Specifications
| Property | Value |
|----------|-------|
| ViewBox | 32x32 |
| Background | Rounded rect, rx=4, fill=`#313244` (surface0) |
| Stroke width | 1.5-2.5 for main elements |
| Style | Flat, minimal, geometric |
### Catppuccin Mocha Palette
| Color | Hex | Usage |
|-------|-----|-------|
| Base | `#1e1e2e` | Deep backgrounds |
| Mantle | `#181825` | Darker backgrounds |
| Crust | `#11111b` | Darkest backgrounds |
| Surface0 | `#313244` | **Icon backgrounds** |
| Surface1 | `#45475a` | Elevated surfaces |
| Surface2 | `#585b70` | Higher surfaces |
| Overlay0 | `#6c7086` | Subtle elements |
| Overlay1 | `#7f849c` | More visible overlays |
| Overlay2 | `#9399b2` | Prominent overlays |
| Subtext0 | `#a6adc8` | Tertiary text |
| Subtext1 | `#bac2de` | Secondary text |
| Text | `#cdd6f4` | **Primary text/strokes** |
| Lavender | `#b4befe` | Soft purple accent |
| Blue | `#89b4fa` | **File operations** |
| Sapphire | `#74c7ec` | Links, info |
| Sky | `#89dceb` | Light blue accent |
| Teal | `#94e2d5` | Success secondary |
| Green | `#a6e3a1` | **Edit operations, Creation/Success** |
| Yellow | `#f9e2af` | **View operations** |
| Peach | `#fab387` | **View accents** |
| Maroon | `#eba0ac` | Soft red |
| Red | `#f38ba8` | **Deletion/Error** |
| Mauve | `#cba6f7` | **System/Settings** |
| Lavender | `#b4befe` | **System accents** |
| Pink | `#f5c2e7` | Decorative |
| Flamingo | `#f2cdcd` | Soft accents |
| Rosewater | `#f5e0dc` | Lightest accent |
### Workbench Color Coding
Each workbench uses a distinct accent color:
| Workbench | Primary | Accent |
|-----------|---------|--------|
| Part Design | Blue `#89b4fa` | Sapphire `#74c7ec` |
| Sketcher | Yellow `#f9e2af` | Peach `#fab387` |
| Assembly | Green `#a6e3a1` | Teal `#94e2d5` |
| TechDraw | Mauve `#cba6f7` | Lavender `#b4befe` |
| Spreadsheet | Sky `#89dceb` | Sapphire `#74c7ec` |
| Mesh | Pink `#f5c2e7` | Flamingo `#f2cdcd` |
| Draft | Peach `#fab387` | Yellow `#f9e2af` |
## Adding New Icons
1. Identify the original icon name (e.g., `document-save.svg`)
2. Create your SVG using the template above
3. Save it in this directory with the exact same filename
4. The new icon will be used on next application start
## Finding Original Icon Names
Original icons are located in:
- `src/Gui/Icons/` - Core GUI icons
- `src/Mod/*/Gui/Resources/icons/` - Module-specific icons
Use the same filename to override.
## Icon Categories
The icon set covers these categories:
| Category | Count | Examples |
|----------|-------|----------|
| File Operations | 15+ | document-save, document-open, Std_Export |
| Edit Operations | 20+ | edit-undo, edit-copy, edit-paste |
| View Operations | 35+ | zoom-in, DrawStyle*, Std_View* |
| System/Settings | 15+ | preferences-system, help-browser |
| Tree View | 15 | Tree_*, tree-* |
| Link/Structure | 15+ | Link*, Feature, Group |
| Workbenches | 8 | PartDesignWorkbench, SketcherWorkbench |
| PartDesign | 8 | PartDesign_Pad, PartDesign_Pocket |
| Sketcher | 10+ | Sketcher_Create*, Constraint_* |
| Assembly | 6 | Assembly_Create*, Assembly_Insert* |
| Navigation | 10+ | button_*, cursor-* |
| Selection | 8 | *-selection, clear-selection |
| DAG View | 4 | dagView* |
**Total: 191 icons**
## Related Issues
- Epic: https://git.kindred-systems.com/kindred/create/issues/7
- Phase 1 (Core): https://git.kindred-systems.com/kindred/create/issues/4
- Phase 2 (Workbench): https://git.kindred-systems.com/kindred/create/issues/5
- Phase 3 (Complete): https://git.kindred-systems.com/kindred/create/issues/6

View File

@@ -0,0 +1,15 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Grid lines representing sketch plane -->
<line x1="6" y1="10" x2="26" y2="10" stroke="#45475a" stroke-width="1"/>
<line x1="6" y1="16" x2="26" y2="16" stroke="#45475a" stroke-width="1"/>
<line x1="6" y1="22" x2="26" y2="22" stroke="#45475a" stroke-width="1"/>
<line x1="10" y1="6" x2="10" y2="26" stroke="#45475a" stroke-width="1"/>
<line x1="16" y1="6" x2="16" y2="26" stroke="#45475a" stroke-width="1"/>
<line x1="22" y1="6" x2="22" y2="26" stroke="#45475a" stroke-width="1"/>
<!-- Sketch profile -->
<path d="M8 20 L8 12 L14 8 L24 8 L24 18 L18 24 L8 20" fill="none" stroke="#f9e2af" stroke-width="2" stroke-linejoin="round"/>
<!-- Constraint point -->
<circle cx="8" cy="12" r="2" fill="#fab387"/>
<circle cx="24" cy="8" r="2" fill="#fab387"/>
</svg>

After

Width:  |  Height:  |  Size: 911 B

View File

@@ -0,0 +1,12 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Arc -->
<path d="M6 24 A14 14 0 0 1 26 24" fill="none" stroke="#f9e2af" stroke-width="2.5" stroke-linecap="round"/>
<!-- End points -->
<circle cx="6" cy="24" r="2.5" fill="#fab387"/>
<circle cx="26" cy="24" r="2.5" fill="#fab387"/>
<!-- Center point -->
<circle cx="16" cy="24" r="2" fill="#fab387" fill-opacity="0.6"/>
<!-- Radius indicator -->
<line x1="16" y1="24" x2="16" y2="10" stroke="#fab387" stroke-width="1" stroke-dasharray="2,2"/>
</svg>

After

Width:  |  Height:  |  Size: 602 B

View File

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Circle -->
<circle cx="16" cy="16" r="10" fill="none" stroke="#f9e2af" stroke-width="2.5"/>
<!-- Center point -->
<circle cx="16" cy="16" r="2.5" fill="#fab387"/>
<!-- Radius line -->
<line x1="16" y1="16" x2="26" y2="16" stroke="#fab387" stroke-width="1.5" stroke-dasharray="2,2"/>
</svg>

After

Width:  |  Height:  |  Size: 435 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Line -->
<line x1="6" y1="26" x2="26" y2="6" stroke="#f9e2af" stroke-width="2.5" stroke-linecap="round"/>
<!-- End points -->
<circle cx="6" cy="26" r="3" fill="#fab387"/>
<circle cx="26" cy="6" r="3" fill="#fab387"/>
</svg>

After

Width:  |  Height:  |  Size: 368 B

View File

@@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Point with crosshair -->
<circle cx="16" cy="16" r="5" fill="#fab387"/>
<circle cx="16" cy="16" r="3" fill="#f9e2af"/>
<!-- Crosshair lines -->
<line x1="16" y1="6" x2="16" y2="11" stroke="#f9e2af" stroke-width="1.5"/>
<line x1="16" y1="21" x2="16" y2="26" stroke="#f9e2af" stroke-width="1.5"/>
<line x1="6" y1="16" x2="11" y2="16" stroke="#f9e2af" stroke-width="1.5"/>
<line x1="21" y1="16" x2="26" y2="16" stroke="#f9e2af" stroke-width="1.5"/>
</svg>

After

Width:  |  Height:  |  Size: 602 B

View File

@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Rectangle -->
<rect x="6" y="8" width="20" height="16" rx="1" fill="none" stroke="#f9e2af" stroke-width="2.5"/>
<!-- Corner points -->
<circle cx="6" cy="8" r="2.5" fill="#fab387"/>
<circle cx="26" cy="8" r="2.5" fill="#fab387"/>
<circle cx="6" cy="24" r="2.5" fill="#fab387"/>
<circle cx="26" cy="24" r="2.5" fill="#fab387"/>
</svg>

After

Width:  |  Height:  |  Size: 481 B

View File

@@ -0,0 +1,20 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Spreadsheet grid -->
<rect x="5" y="5" width="22" height="22" rx="1" fill="#45475a" stroke="#89dceb" stroke-width="1.5"/>
<!-- Header row -->
<rect x="5" y="5" width="22" height="5" fill="#585b70"/>
<!-- Column dividers -->
<line x1="12" y1="5" x2="12" y2="27" stroke="#6c7086" stroke-width="1"/>
<line x1="20" y1="5" x2="20" y2="27" stroke="#6c7086" stroke-width="1"/>
<!-- Row dividers -->
<line x1="5" y1="10" x2="27" y2="10" stroke="#6c7086" stroke-width="1"/>
<line x1="5" y1="16" x2="27" y2="16" stroke="#6c7086" stroke-width="1"/>
<line x1="5" y1="22" x2="27" y2="22" stroke="#6c7086" stroke-width="1"/>
<!-- Header labels -->
<text x="8" y="9" font-family="monospace" font-size="4" fill="#cdd6f4">A</text>
<text x="15" y="9" font-family="monospace" font-size="4" fill="#cdd6f4">B</text>
<text x="23" y="9" font-family="monospace" font-size="4" fill="#cdd6f4">C</text>
<!-- Cell highlight -->
<rect x="12" y="10" width="8" height="6" fill="#74c7ec" fill-opacity="0.3" stroke="#89dceb" stroke-width="1"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Original object -->
<rect x="6" y="6" width="12" height="12" rx="2" fill="#45475a" stroke="#6c7086" stroke-width="1.5"/>
<!-- Duplicate object -->
<rect x="14" y="14" width="12" height="12" rx="2" fill="#45475a" stroke="#94e2d5" stroke-width="1.5"/>
<!-- Plus badge -->
<circle cx="24" cy="8" r="5" fill="#a6e3a1"/>
<path d="M24 5.5 L24 10.5 M21.5 8 L26.5 8" stroke="#1e1e2e" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 574 B

View File

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Document -->
<path d="M8 5 L8 27 L24 27 L24 11 L18 5 Z" fill="#45475a" stroke="#89b4fa" stroke-width="1.5"/>
<path d="M18 5 L18 11 L24 11" fill="#313244" stroke="#89b4fa" stroke-width="1.5" stroke-linejoin="round"/>
<!-- Arrow pointing out of document -->
<path d="M12 16 L4 16" stroke="#fab387" stroke-width="2.5" stroke-linecap="round"/>
<path d="M7 13 L4 16 L7 19" stroke="#fab387" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 617 B

View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<ellipse cx="16" cy="16" rx="10" ry="6" fill="none" stroke="#585b70" stroke-width="1.5"/>
<circle cx="16" cy="16" r="3" fill="#585b70"/>
<line x1="6" y1="24" x2="26" y2="8" stroke="#f38ba8" stroke-width="2"/>
</svg>

After

Width:  |  Height:  |  Size: 338 B

View File

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Document -->
<path d="M8 5 L8 27 L24 27 L24 11 L18 5 Z" fill="#45475a" stroke="#89b4fa" stroke-width="1.5"/>
<path d="M18 5 L18 11 L24 11" fill="#313244" stroke="#89b4fa" stroke-width="1.5" stroke-linejoin="round"/>
<!-- Arrow pointing into document -->
<path d="M4 16 L12 16" stroke="#a6e3a1" stroke-width="2.5" stroke-linecap="round"/>
<path d="M9 13 L12 16 L9 19" stroke="#a6e3a1" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 616 B

View File

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Circular arrow -->
<path d="M16 6 A10 10 0 1 1 6 16" fill="none" stroke="#cba6f7" stroke-width="2.5" stroke-linecap="round"/>
<!-- Arrow head -->
<path d="M16 6 L13 10 L19 10 Z" fill="#b4befe"/>
</svg>

After

Width:  |  Height:  |  Size: 343 B

View File

@@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Back floppy -->
<path d="M11 4 L11 20 L25 20 L25 8 L21 4 Z" fill="#45475a" stroke="#6c7086" stroke-width="1"/>
<!-- Front floppy -->
<path d="M5 8 L5 28 L19 28 L19 12 L15 8 Z" fill="#45475a" stroke="#89b4fa" stroke-width="1.5"/>
<!-- Metal slider on front -->
<rect x="8" y="8" width="6" height="5" rx="1" fill="#1e1e2e" stroke="#6c7086" stroke-width="0.75"/>
<!-- Label area on front -->
<rect x="7" y="16" width="10" height="5" rx="1" fill="#cdd6f4"/>
<!-- Multiple indicator -->
<circle cx="24" cy="24" r="5" fill="#a6e3a1"/>
<text x="24" y="27" font-family="sans-serif" font-size="8" font-weight="bold" fill="#1e1e2e" text-anchor="middle">2</text>
</svg>

After

Width:  |  Height:  |  Size: 814 B

View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<ellipse cx="16" cy="12" rx="8" ry="5" fill="none" stroke="#f9e2af" stroke-width="1.5"/>
<circle cx="16" cy="12" r="2.5" fill="#fab387"/>
<path d="M8 20 L12 24 L24 12" fill="none" stroke="#a6e3a1" stroke-width="2"/>
</svg>

After

Width:  |  Height:  |  Size: 345 B

View File

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<circle cx="16" cy="16" r="8" fill="none" stroke="#cba6f7" stroke-width="1.5"/>
<path d="M16 8 L16 16 L22 16" fill="none" stroke="#b4befe" stroke-width="2"/>
<circle cx="16" cy="8" r="2" fill="#f38ba8"/>
<circle cx="24" cy="16" r="2" fill="#a6e3a1"/>
<circle cx="16" cy="24" r="2" fill="#89b4fa"/>
<circle cx="8" cy="16" r="2" fill="#f9e2af"/>
</svg>

After

Width:  |  Height:  |  Size: 479 B

View File

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<ellipse cx="16" cy="16" rx="10" ry="6" fill="none" stroke="#f9e2af" stroke-width="1.5"/>
<circle cx="16" cy="16" r="3" fill="#fab387"/>
<rect x="8" y="22" width="6" height="4" rx="1" fill="#a6e3a1"/>
<rect x="18" y="22" width="6" height="4" rx="1" fill="#a6e3a1"/>
</svg>

After

Width:  |  Height:  |  Size: 397 B

View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<ellipse cx="16" cy="14" rx="8" ry="5" fill="none" stroke="#f9e2af" stroke-width="1.5"/>
<circle cx="16" cy="14" r="2.5" fill="#fab387"/>
<rect x="10" y="20" width="12" height="6" rx="1" fill="none" stroke="#a6e3a1" stroke-width="1.5" stroke-dasharray="2,2"/>
</svg>

After

Width:  |  Height:  |  Size: 389 B

View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<path d="M8 20 L8 10 L16 6 L24 10 L24 20" fill="none" stroke="#89b4fa" stroke-width="1.5"/>
<line x1="4" y1="16" x2="28" y2="16" stroke="#f38ba8" stroke-width="2"/>
<path d="M8 20 L16 24 L24 20" fill="none" stroke="#89b4fa" stroke-width="1.5" stroke-dasharray="2,2"/>
</svg>

After

Width:  |  Height:  |  Size: 397 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<line x1="16" y1="6" x2="16" y2="26" stroke="#74c7ec" stroke-width="2"/>
<line x1="6" y1="16" x2="26" y2="16" stroke="#74c7ec" stroke-width="2"/>
<line x1="9" y1="9" x2="23" y2="23" stroke="#74c7ec" stroke-width="1.5"/>
<line x1="23" y1="9" x2="9" y2="23" stroke="#74c7ec" stroke-width="1.5"/>
<circle cx="16" cy="16" r="3" fill="#89dceb"/>
</svg>

After

Width:  |  Height:  |  Size: 474 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<circle cx="16" cy="16" r="8" fill="none" stroke="#f9e2af" stroke-width="1.5"/>
<path d="M16 8 L18 14 L24 16 L18 18 L16 24 L14 18 L8 16 L14 14 Z" fill="#fab387"/>
</svg>

After

Width:  |  Height:  |  Size: 290 B

View File

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<ellipse cx="12" cy="12" rx="6" ry="4" fill="none" stroke="#f9e2af" stroke-width="1.5"/>
<circle cx="12" cy="12" r="2" fill="#fab387"/>
<ellipse cx="20" cy="20" rx="6" ry="4" fill="none" stroke="#585b70" stroke-width="1.5"/>
<circle cx="20" cy="20" r="2" fill="#585b70"/>
<path d="M16 14 L16 18" stroke="#b4befe" stroke-width="1.5"/>
<path d="M14 16 L18 16" stroke="#b4befe" stroke-width="1.5"/>
</svg>

After

Width:  |  Height:  |  Size: 531 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<rect x="6" y="8" width="12" height="12" fill="#89b4fa" fill-opacity="0.5" stroke="#89b4fa" stroke-width="1.5"/>
<rect x="14" y="12" width="12" height="12" fill="#f9e2af" fill-opacity="0.5" stroke="#f9e2af" stroke-width="1.5"/>
</svg>

After

Width:  |  Height:  |  Size: 355 B

View File

@@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Eye shape -->
<path d="M4 16 Q16 6 28 16 Q16 26 4 16" fill="#45475a" stroke="#f9e2af" stroke-width="1.5"/>
<!-- Iris -->
<circle cx="16" cy="16" r="5" fill="#fab387"/>
<!-- Pupil -->
<circle cx="16" cy="16" r="2.5" fill="#1e1e2e"/>
<!-- Light reflection -->
<circle cx="14" cy="14.5" r="1" fill="#cdd6f4"/>
</svg>

After

Width:  |  Height:  |  Size: 463 B

View File

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<line x1="16" y1="6" x2="16" y2="26" stroke="#a6e3a1" stroke-width="2"/>
<line x1="6" y1="16" x2="26" y2="16" stroke="#f38ba8" stroke-width="2"/>
<line x1="8" y1="8" x2="24" y2="24" stroke="#89b4fa" stroke-width="1.5" stroke-dasharray="2,2"/>
<circle cx="16" cy="16" r="4" fill="none" stroke="#f9e2af" stroke-width="1.5"/>
</svg>

After

Width:  |  Height:  |  Size: 454 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<path d="M6 12 L16 24 L26 12 Z" fill="#fab387" stroke="#f9e2af" stroke-width="1.5"/>
<text x="16" y="18" text-anchor="middle" font-family="sans-serif" font-size="7" font-weight="bold" fill="#313244">Bo</text>
</svg>

After

Width:  |  Height:  |  Size: 336 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<path d="M4 17 L16 11 L28 17 L28 23 L16 29 L4 23 Z" fill="#f9e2af" stroke="#fab387" stroke-width="1.5"/>
<path d="M4 17 L16 23 L28 17 M16 23 L16 29" fill="none" stroke="#313244" stroke-width="1"/>
</svg>

After

Width:  |  Height:  |  Size: 324 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<rect x="8" y="8" width="16" height="16" fill="#f9e2af" stroke="#fab387" stroke-width="1.5"/>
<text x="16" y="20" text-anchor="middle" font-family="sans-serif" font-size="8" font-weight="bold" fill="#313244">F</text>
</svg>

After

Width:  |  Height:  |  Size: 344 B

View File

@@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- House shape -->
<path d="M16 6 L6 14 L6 26 L26 26 L26 14 Z" fill="#45475a" stroke="#f9e2af" stroke-width="1.5" stroke-linejoin="round"/>
<!-- Roof -->
<path d="M16 6 L4 16 L6 16 L16 8 L26 16 L28 16 Z" fill="#585b70" stroke="#f9e2af" stroke-width="1.5" stroke-linejoin="round"/>
<!-- Door -->
<rect x="13" y="18" width="6" height="8" rx="1" fill="#1e1e2e" stroke="#fab387" stroke-width="1"/>
<!-- Window -->
<rect x="19" y="14" width="4" height="4" rx="0.5" fill="#89dceb" stroke="#74c7ec" stroke-width="0.75"/>
</svg>

After

Width:  |  Height:  |  Size: 667 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<path d="M6 18 L16 12 L26 18 L26 24 L16 30 L6 24 Z" fill="#f9e2af" stroke="#fab387" stroke-width="1.5"/>
<path d="M6 18 L16 24 L26 18 M16 24 L16 30" fill="none" stroke="#313244" stroke-width="1"/>
</svg>

After

Width:  |  Height:  |  Size: 324 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<path d="M20 6 L8 16 L20 26 Z" fill="#f9e2af" stroke="#fab387" stroke-width="1.5"/>
<text x="15" y="20" text-anchor="middle" font-family="sans-serif" font-size="7" font-weight="bold" fill="#313244">L</text>
</svg>

After

Width:  |  Height:  |  Size: 334 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<rect x="8" y="8" width="16" height="16" fill="#fab387" stroke="#f9e2af" stroke-width="1.5"/>
<text x="16" y="20" text-anchor="middle" font-family="sans-serif" font-size="8" font-weight="bold" fill="#313244">B</text>
</svg>

After

Width:  |  Height:  |  Size: 344 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<path d="M12 6 L24 16 L12 26 Z" fill="#fab387" stroke="#f9e2af" stroke-width="1.5"/>
<text x="17" y="20" text-anchor="middle" font-family="sans-serif" font-size="7" font-weight="bold" fill="#313244">R</text>
</svg>

After

Width:  |  Height:  |  Size: 335 B

View File

@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="2" y="2" width="28" height="28" rx="4" fill="#313244"/>
<!-- Camera body -->
<rect x="5" y="10" width="22" height="14" rx="2" fill="#45475a" stroke="#f9e2af" stroke-width="1.5"/>
<!-- Lens -->
<circle cx="16" cy="17" r="5" fill="#1e1e2e" stroke="#fab387" stroke-width="1.5"/>
<circle cx="16" cy="17" r="3" fill="#45475a"/>
<circle cx="16" cy="17" r="1.5" fill="#f9e2af"/>
<!-- Flash -->
<rect x="20" y="12" width="4" height="2" rx="0.5" fill="#cdd6f4"/>
<!-- Viewfinder bump -->
<rect x="12" y="7" width="8" height="4" rx="1" fill="#585b70" stroke="#f9e2af" stroke-width="1"/>
</svg>

After

Width:  |  Height:  |  Size: 676 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<path d="M6 20 L16 8 L26 20 Z" fill="#f9e2af" stroke="#fab387" stroke-width="1.5"/>
<text x="16" y="19" text-anchor="middle" font-family="sans-serif" font-size="7" font-weight="bold" fill="#313244">T</text>
</svg>

After

Width:  |  Height:  |  Size: 334 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="4" fill="#313244"/>
<path d="M5 16 L16 10 L27 16 L27 24 L16 30 L5 24 Z" fill="#f9e2af" stroke="#fab387" stroke-width="1.5"/>
<path d="M5 16 L16 22 L27 16 M16 22 L16 30" fill="none" stroke="#313244" stroke-width="1"/>
</svg>

After

Width:  |  Height:  |  Size: 324 B

Some files were not shown because too many files have changed in this diff Show More