Merge pull request 'feat(addon-system): create kindred-addon-sdk package (#249)' (#259) from feat/kindred-addon-sdk into main
Some checks failed
Build and Test / build (push) Failing after 1m37s
Some checks failed
Build and Test / build (push) Failing after 1m37s
Reviewed-on: #259
This commit was merged in pull request #259.
This commit is contained in:
3
mods/sdk/Init.py
Normal file
3
mods/sdk/Init.py
Normal file
@@ -0,0 +1,3 @@
|
||||
import FreeCAD
|
||||
|
||||
FreeCAD.Console.PrintLog("kindred-addon-sdk loaded\n")
|
||||
3
mods/sdk/InitGui.py
Normal file
3
mods/sdk/InitGui.py
Normal file
@@ -0,0 +1,3 @@
|
||||
import FreeCAD
|
||||
|
||||
FreeCAD.Console.PrintLog("kindred-addon-sdk GUI initialized\n")
|
||||
34
mods/sdk/kindred_sdk/__init__.py
Normal file
34
mods/sdk/kindred_sdk/__init__.py
Normal file
@@ -0,0 +1,34 @@
|
||||
# kindred-addon-sdk — stable API for Kindred Create addon integration
|
||||
|
||||
from kindred_sdk.version import SDK_VERSION
|
||||
from kindred_sdk.context import (
|
||||
register_context,
|
||||
unregister_context,
|
||||
register_overlay,
|
||||
unregister_overlay,
|
||||
inject_commands,
|
||||
current_context,
|
||||
refresh_context,
|
||||
)
|
||||
from kindred_sdk.theme import get_theme_tokens, load_palette
|
||||
from kindred_sdk.origin import register_origin, unregister_origin
|
||||
from kindred_sdk.dock import register_dock_panel
|
||||
from kindred_sdk.compat import create_version, freecad_version
|
||||
|
||||
__all__ = [
|
||||
"SDK_VERSION",
|
||||
"register_context",
|
||||
"unregister_context",
|
||||
"register_overlay",
|
||||
"unregister_overlay",
|
||||
"inject_commands",
|
||||
"current_context",
|
||||
"refresh_context",
|
||||
"get_theme_tokens",
|
||||
"load_palette",
|
||||
"register_origin",
|
||||
"unregister_origin",
|
||||
"register_dock_panel",
|
||||
"create_version",
|
||||
"freecad_version",
|
||||
]
|
||||
21
mods/sdk/kindred_sdk/compat.py
Normal file
21
mods/sdk/kindred_sdk/compat.py
Normal file
@@ -0,0 +1,21 @@
|
||||
"""Version detection utilities."""
|
||||
|
||||
import FreeCAD
|
||||
|
||||
|
||||
def create_version():
|
||||
"""Return the Kindred Create version string (e.g. ``"0.1.3"``)."""
|
||||
try:
|
||||
from version import VERSION
|
||||
|
||||
return VERSION
|
||||
except ImportError:
|
||||
return "0.0.0"
|
||||
|
||||
|
||||
def freecad_version():
|
||||
"""Return the FreeCAD base version as a tuple of strings.
|
||||
|
||||
Example: ``("1", "0", "0", "2025.01.01")``.
|
||||
"""
|
||||
return tuple(FreeCAD.Version())
|
||||
152
mods/sdk/kindred_sdk/context.py
Normal file
152
mods/sdk/kindred_sdk/context.py
Normal file
@@ -0,0 +1,152 @@
|
||||
"""Editing context and overlay registration wrappers.
|
||||
|
||||
Thin wrappers around FreeCADGui editing context bindings. If the
|
||||
underlying C++ API changes during an upstream rebase, only this module
|
||||
needs to be updated.
|
||||
"""
|
||||
|
||||
import FreeCAD
|
||||
|
||||
|
||||
def _gui():
|
||||
"""Lazy import of FreeCADGui (not available in console mode)."""
|
||||
import FreeCADGui
|
||||
|
||||
return FreeCADGui
|
||||
|
||||
|
||||
def register_context(context_id, label, color, toolbars, match, priority=50):
|
||||
"""Register an editing context.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
context_id : str
|
||||
Unique identifier (e.g. ``"myaddon.edit"``).
|
||||
label : str
|
||||
Display label template. Supports ``{name}`` placeholder.
|
||||
color : str
|
||||
Hex color for the breadcrumb (e.g. ``"#f38ba8"``).
|
||||
toolbars : list[str]
|
||||
Toolbar names to show when this context is active.
|
||||
match : callable
|
||||
Zero-argument callable returning *True* when this context is active.
|
||||
priority : int, optional
|
||||
Higher values are checked first. Default 50.
|
||||
"""
|
||||
if not isinstance(context_id, str):
|
||||
raise TypeError(f"context_id must be str, got {type(context_id).__name__}")
|
||||
if not isinstance(toolbars, list):
|
||||
raise TypeError(f"toolbars must be list, got {type(toolbars).__name__}")
|
||||
if not callable(match):
|
||||
raise TypeError("match must be callable")
|
||||
|
||||
try:
|
||||
_gui().registerEditingContext(context_id, label, color, toolbars, match, priority)
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintWarning(
|
||||
f"kindred_sdk: Failed to register context '{context_id}': {e}\n"
|
||||
)
|
||||
|
||||
|
||||
def unregister_context(context_id):
|
||||
"""Remove a previously registered editing context."""
|
||||
if not isinstance(context_id, str):
|
||||
raise TypeError(f"context_id must be str, got {type(context_id).__name__}")
|
||||
|
||||
try:
|
||||
_gui().unregisterEditingContext(context_id)
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintWarning(
|
||||
f"kindred_sdk: Failed to unregister context '{context_id}': {e}\n"
|
||||
)
|
||||
|
||||
|
||||
def register_overlay(overlay_id, toolbars, match):
|
||||
"""Register an editing overlay.
|
||||
|
||||
Overlays add toolbars to whatever context is currently active when
|
||||
*match* returns True.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
overlay_id : str
|
||||
Unique overlay identifier.
|
||||
toolbars : list[str]
|
||||
Toolbar names to append.
|
||||
match : callable
|
||||
Zero-argument callable returning *True* when the overlay applies.
|
||||
"""
|
||||
if not isinstance(overlay_id, str):
|
||||
raise TypeError(f"overlay_id must be str, got {type(overlay_id).__name__}")
|
||||
if not isinstance(toolbars, list):
|
||||
raise TypeError(f"toolbars must be list, got {type(toolbars).__name__}")
|
||||
if not callable(match):
|
||||
raise TypeError("match must be callable")
|
||||
|
||||
try:
|
||||
_gui().registerEditingOverlay(overlay_id, toolbars, match)
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintWarning(
|
||||
f"kindred_sdk: Failed to register overlay '{overlay_id}': {e}\n"
|
||||
)
|
||||
|
||||
|
||||
def unregister_overlay(overlay_id):
|
||||
"""Remove a previously registered editing overlay."""
|
||||
if not isinstance(overlay_id, str):
|
||||
raise TypeError(f"overlay_id must be str, got {type(overlay_id).__name__}")
|
||||
|
||||
try:
|
||||
_gui().unregisterEditingOverlay(overlay_id)
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintWarning(
|
||||
f"kindred_sdk: Failed to unregister overlay '{overlay_id}': {e}\n"
|
||||
)
|
||||
|
||||
|
||||
def inject_commands(context_id, toolbar_name, commands):
|
||||
"""Inject commands into a context's toolbar.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
context_id : str
|
||||
Target context identifier.
|
||||
toolbar_name : str
|
||||
Toolbar within that context.
|
||||
commands : list[str]
|
||||
Command names to add.
|
||||
"""
|
||||
if not isinstance(context_id, str):
|
||||
raise TypeError(f"context_id must be str, got {type(context_id).__name__}")
|
||||
if not isinstance(toolbar_name, str):
|
||||
raise TypeError(f"toolbar_name must be str, got {type(toolbar_name).__name__}")
|
||||
if not isinstance(commands, list):
|
||||
raise TypeError(f"commands must be list, got {type(commands).__name__}")
|
||||
|
||||
try:
|
||||
_gui().injectEditingCommands(context_id, toolbar_name, commands)
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintWarning(
|
||||
f"kindred_sdk: Failed to inject commands into '{context_id}': {e}\n"
|
||||
)
|
||||
|
||||
|
||||
def current_context():
|
||||
"""Return the current editing context as a dict.
|
||||
|
||||
Keys: ``id``, ``label``, ``color``, ``toolbars``, ``breadcrumb``,
|
||||
``breadcrumbColors``. Returns ``None`` if no context is active.
|
||||
"""
|
||||
try:
|
||||
return _gui().currentEditingContext()
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintWarning(f"kindred_sdk: Failed to get current context: {e}\n")
|
||||
return None
|
||||
|
||||
|
||||
def refresh_context():
|
||||
"""Force re-resolution and update of the editing context."""
|
||||
try:
|
||||
_gui().refreshEditingContext()
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintWarning(f"kindred_sdk: Failed to refresh context: {e}\n")
|
||||
73
mods/sdk/kindred_sdk/dock.py
Normal file
73
mods/sdk/kindred_sdk/dock.py
Normal file
@@ -0,0 +1,73 @@
|
||||
"""Deferred dock panel registration helper.
|
||||
|
||||
Replaces the manual ``QTimer.singleShot()`` + duplicate-check +
|
||||
try/except pattern used in ``src/Mod/Create/InitGui.py``.
|
||||
"""
|
||||
|
||||
import FreeCAD
|
||||
|
||||
_AREA_MAP = {
|
||||
"left": 1, # Qt.LeftDockWidgetArea
|
||||
"right": 2, # Qt.RightDockWidgetArea
|
||||
"top": 4, # Qt.TopDockWidgetArea
|
||||
"bottom": 8, # Qt.BottomDockWidgetArea
|
||||
}
|
||||
|
||||
|
||||
def register_dock_panel(object_name, title, widget_factory, area="right", delay_ms=0):
|
||||
"""Register a dock panel, optionally deferred.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
object_name : str
|
||||
Qt object name for duplicate prevention.
|
||||
title : str
|
||||
Dock widget title bar text.
|
||||
widget_factory : callable
|
||||
Zero-argument callable returning a ``QWidget``. Called only
|
||||
when the panel is actually created (after *delay_ms*).
|
||||
area : str, optional
|
||||
Dock area: ``"left"``, ``"right"``, ``"top"``, or ``"bottom"``.
|
||||
Default ``"right"``.
|
||||
delay_ms : int, optional
|
||||
Milliseconds to wait before creating the panel. Default 0
|
||||
(immediate, but still posted to the event loop).
|
||||
"""
|
||||
if not isinstance(object_name, str):
|
||||
raise TypeError(f"object_name must be str, got {type(object_name).__name__}")
|
||||
if not callable(widget_factory):
|
||||
raise TypeError("widget_factory must be callable")
|
||||
|
||||
qt_area = _AREA_MAP.get(area)
|
||||
if qt_area is None:
|
||||
raise ValueError(f"area must be one of {list(_AREA_MAP)}, got {area!r}")
|
||||
|
||||
def _create():
|
||||
try:
|
||||
from PySide import QtCore, QtWidgets
|
||||
|
||||
import FreeCADGui
|
||||
|
||||
mw = FreeCADGui.getMainWindow()
|
||||
if mw is None:
|
||||
return
|
||||
|
||||
if mw.findChild(QtWidgets.QDockWidget, object_name):
|
||||
return
|
||||
|
||||
widget = widget_factory()
|
||||
panel = QtWidgets.QDockWidget(title, mw)
|
||||
panel.setObjectName(object_name)
|
||||
panel.setWidget(widget)
|
||||
mw.addDockWidget(QtCore.Qt.DockWidgetArea(qt_area), panel)
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintLog(f"kindred_sdk: Dock panel '{object_name}' skipped: {e}\n")
|
||||
|
||||
try:
|
||||
from PySide.QtCore import QTimer
|
||||
|
||||
QTimer.singleShot(max(0, delay_ms), _create)
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintLog(
|
||||
f"kindred_sdk: Could not schedule dock panel '{object_name}': {e}\n"
|
||||
)
|
||||
42
mods/sdk/kindred_sdk/origin.py
Normal file
42
mods/sdk/kindred_sdk/origin.py
Normal file
@@ -0,0 +1,42 @@
|
||||
"""FileOrigin registration wrappers.
|
||||
|
||||
Wraps ``FreeCADGui.addOrigin()`` / ``removeOrigin()`` with validation
|
||||
and error handling. Addons implement the FileOrigin duck-typed
|
||||
interface directly (see Silo's ``SiloOrigin`` for the full contract).
|
||||
"""
|
||||
|
||||
import FreeCAD
|
||||
|
||||
_REQUIRED_METHODS = ("id", "name", "type", "ownsDocument")
|
||||
|
||||
|
||||
def _gui():
|
||||
import FreeCADGui
|
||||
|
||||
return FreeCADGui
|
||||
|
||||
|
||||
def register_origin(origin):
|
||||
"""Register a FileOrigin with FreeCADGui.
|
||||
|
||||
*origin* must be a Python object implementing at least ``id()``,
|
||||
``name()``, ``type()``, and ``ownsDocument(doc)`` methods.
|
||||
"""
|
||||
missing = [m for m in _REQUIRED_METHODS if not hasattr(origin, m)]
|
||||
if missing:
|
||||
raise TypeError(f"origin is missing required methods: {', '.join(missing)}")
|
||||
|
||||
try:
|
||||
_gui().addOrigin(origin)
|
||||
FreeCAD.Console.PrintLog(f"kindred_sdk: Registered origin '{origin.id()}'\n")
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintWarning(f"kindred_sdk: Failed to register origin: {e}\n")
|
||||
|
||||
|
||||
def unregister_origin(origin):
|
||||
"""Remove a previously registered FileOrigin."""
|
||||
try:
|
||||
_gui().removeOrigin(origin)
|
||||
FreeCAD.Console.PrintLog(f"kindred_sdk: Unregistered origin '{origin.id()}'\n")
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintWarning(f"kindred_sdk: Failed to unregister origin: {e}\n")
|
||||
46
mods/sdk/kindred_sdk/palettes/catppuccin-mocha.yaml
Normal file
46
mods/sdk/kindred_sdk/palettes/catppuccin-mocha.yaml
Normal file
@@ -0,0 +1,46 @@
|
||||
name: Catppuccin Mocha
|
||||
slug: catppuccin-mocha
|
||||
|
||||
colors:
|
||||
rosewater: "#f5e0dc"
|
||||
flamingo: "#f2cdcd"
|
||||
pink: "#f5c2e7"
|
||||
mauve: "#cba6f7"
|
||||
red: "#f38ba8"
|
||||
maroon: "#eba0ac"
|
||||
peach: "#fab387"
|
||||
yellow: "#f9e2af"
|
||||
green: "#a6e3a1"
|
||||
teal: "#94e2d5"
|
||||
sky: "#89dceb"
|
||||
sapphire: "#74c7ec"
|
||||
blue: "#89b4fa"
|
||||
lavender: "#b4befe"
|
||||
text: "#cdd6f4"
|
||||
subtext1: "#bac2de"
|
||||
subtext0: "#a6adc8"
|
||||
overlay2: "#9399b2"
|
||||
overlay1: "#7f849c"
|
||||
overlay0: "#6c7086"
|
||||
surface2: "#585b70"
|
||||
surface1: "#45475a"
|
||||
surface0: "#313244"
|
||||
base: "#1e1e2e"
|
||||
mantle: "#181825"
|
||||
crust: "#11111b"
|
||||
|
||||
roles:
|
||||
background: base
|
||||
background.toolbar: mantle
|
||||
background.darkest: crust
|
||||
foreground: text
|
||||
foreground.muted: subtext0
|
||||
foreground.subtle: overlay1
|
||||
accent.primary: mauve
|
||||
accent.info: blue
|
||||
accent.success: green
|
||||
accent.warning: yellow
|
||||
accent.error: red
|
||||
border: surface1
|
||||
selection: surface2
|
||||
input.background: surface0
|
||||
181
mods/sdk/kindred_sdk/theme.py
Normal file
181
mods/sdk/kindred_sdk/theme.py
Normal file
@@ -0,0 +1,181 @@
|
||||
"""YAML-driven theme system.
|
||||
|
||||
Loads color palettes from YAML files and provides runtime access to
|
||||
color tokens, semantic roles, QSS template formatting, and FreeCAD
|
||||
preference-pack value conversion.
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
import FreeCAD
|
||||
|
||||
|
||||
class Palette:
|
||||
"""A loaded color palette with raw tokens and semantic roles."""
|
||||
|
||||
def __init__(self, name, slug, colors, roles):
|
||||
self.name = name
|
||||
self.slug = slug
|
||||
self.colors = dict(colors)
|
||||
self.roles = {k: colors[v] for k, v in roles.items() if v in colors}
|
||||
|
||||
def get(self, key):
|
||||
"""Look up a color by role first, then by raw color name.
|
||||
|
||||
Returns the hex string or *None* if not found.
|
||||
"""
|
||||
return self.roles.get(key) or self.colors.get(key)
|
||||
|
||||
@staticmethod
|
||||
def hex_to_rgba_uint(hex_color):
|
||||
"""Convert ``#RRGGBB`` to FreeCAD's unsigned-int RGBA format.
|
||||
|
||||
>>> Palette.hex_to_rgba_uint("#cdd6f4")
|
||||
3453416703
|
||||
"""
|
||||
h = hex_color.lstrip("#")
|
||||
r = int(h[0:2], 16)
|
||||
g = int(h[2:4], 16)
|
||||
b = int(h[4:6], 16)
|
||||
a = 255
|
||||
return (r << 24) | (g << 16) | (b << 8) | a
|
||||
|
||||
def format_qss(self, template):
|
||||
"""Substitute ``{token}`` placeholders in a QSS template string.
|
||||
|
||||
Both raw color names (``{blue}``) and dotted role names
|
||||
(``{accent.primary}``) are supported. Dotted names are tried
|
||||
first so they take precedence over any same-named color.
|
||||
|
||||
Unknown tokens are left as-is.
|
||||
"""
|
||||
lookup = {}
|
||||
lookup.update(self.colors)
|
||||
# Roles use dotted names which aren't valid Python identifiers,
|
||||
# so we do regex-based substitution.
|
||||
lookup.update(self.roles)
|
||||
|
||||
def _replace(m):
|
||||
key = m.group(1)
|
||||
return lookup.get(key, m.group(0))
|
||||
|
||||
return re.sub(r"\{([a-z][a-z0-9_.]*)\}", _replace, template)
|
||||
|
||||
def __repr__(self):
|
||||
return f"Palette({self.name!r}, {len(self.colors)} colors, {len(self.roles)} roles)"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# YAML loading with fallback
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _load_yaml(path):
|
||||
"""Load a YAML file, preferring PyYAML if available."""
|
||||
try:
|
||||
import yaml
|
||||
|
||||
with open(path) as f:
|
||||
return yaml.safe_load(f)
|
||||
except ImportError:
|
||||
return _load_yaml_fallback(path)
|
||||
|
||||
|
||||
def _load_yaml_fallback(path):
|
||||
"""Minimal YAML parser for flat key-value palette files.
|
||||
|
||||
Handles the subset of YAML used by palette files: top-level keys
|
||||
with string/scalar values, and one level of nested mappings.
|
||||
"""
|
||||
data = {}
|
||||
current_section = None
|
||||
|
||||
with open(path) as f:
|
||||
for line in f:
|
||||
stripped = line.rstrip()
|
||||
|
||||
# Skip blank lines and comments
|
||||
if not stripped or stripped.startswith("#"):
|
||||
continue
|
||||
|
||||
# Detect indentation
|
||||
indent = len(line) - len(line.lstrip())
|
||||
|
||||
# Top-level key
|
||||
if indent == 0 and ":" in stripped:
|
||||
key, _, value = stripped.partition(":")
|
||||
key = key.strip()
|
||||
value = value.strip().strip('"').strip("'")
|
||||
if value:
|
||||
data[key] = value
|
||||
current_section = None
|
||||
else:
|
||||
# Start of a nested section
|
||||
current_section = key
|
||||
data[current_section] = {}
|
||||
continue
|
||||
|
||||
# Nested key (indented)
|
||||
if current_section is not None and indent > 0 and ":" in stripped:
|
||||
key, _, value = stripped.partition(":")
|
||||
key = key.strip()
|
||||
value = value.strip().strip('"').strip("'")
|
||||
data[current_section][key] = value
|
||||
|
||||
return data
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Public API
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
_cache = {}
|
||||
|
||||
_PALETTES_DIR = os.path.join(os.path.dirname(__file__), "palettes")
|
||||
|
||||
|
||||
def load_palette(name="catppuccin-mocha"):
|
||||
"""Load a named palette from the ``palettes/`` directory.
|
||||
|
||||
Results are cached; subsequent calls with the same *name* return
|
||||
the same ``Palette`` instance.
|
||||
"""
|
||||
if name in _cache:
|
||||
return _cache[name]
|
||||
|
||||
path = os.path.join(_PALETTES_DIR, f"{name}.yaml")
|
||||
if not os.path.isfile(path):
|
||||
FreeCAD.Console.PrintWarning(f"kindred_sdk: Palette file not found: {path}\n")
|
||||
return None
|
||||
|
||||
try:
|
||||
raw = _load_yaml(path)
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintWarning(f"kindred_sdk: Failed to load palette '{name}': {e}\n")
|
||||
return None
|
||||
|
||||
palette = Palette(
|
||||
name=raw.get("name", name),
|
||||
slug=raw.get("slug", name),
|
||||
colors=raw.get("colors", {}),
|
||||
roles=raw.get("roles", {}),
|
||||
)
|
||||
_cache[name] = palette
|
||||
|
||||
FreeCAD.Console.PrintLog(
|
||||
f"kindred_sdk: Loaded palette '{palette.name}' ({len(palette.colors)} colors)\n"
|
||||
)
|
||||
return palette
|
||||
|
||||
|
||||
def get_theme_tokens(name="catppuccin-mocha"):
|
||||
"""Return a dict of ``{token_name: "#hex"}`` for all colors in a palette.
|
||||
|
||||
This is a convenience shorthand for ``load_palette(name).colors``.
|
||||
Returns a copy so callers cannot mutate the cached palette.
|
||||
"""
|
||||
palette = load_palette(name)
|
||||
if palette is None:
|
||||
return {}
|
||||
return dict(palette.colors)
|
||||
1
mods/sdk/kindred_sdk/version.py
Normal file
1
mods/sdk/kindred_sdk/version.py
Normal file
@@ -0,0 +1 @@
|
||||
SDK_VERSION = "0.1.0"
|
||||
23
mods/sdk/package.xml
Normal file
23
mods/sdk/package.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<package format="1" xmlns="https://wiki.freecad.org/Package_Metadata">
|
||||
|
||||
<name>sdk</name>
|
||||
<description>Kindred Create addon SDK - stable API for addon integration</description>
|
||||
<version>0.1.0</version>
|
||||
<maintainer email="info@kindredsystems.io">Kindred Systems</maintainer>
|
||||
<license file="LICENSE">LGPL-2.1-or-later</license>
|
||||
|
||||
<content>
|
||||
<workbench>
|
||||
<classname>SdkWorkbench</classname>
|
||||
<subdirectory>./</subdirectory>
|
||||
</workbench>
|
||||
</content>
|
||||
|
||||
<kindred>
|
||||
<min_create_version>0.1.0</min_create_version>
|
||||
<load_priority>0</load_priority>
|
||||
<pure_python>true</pure_python>
|
||||
</kindred>
|
||||
|
||||
</package>
|
||||
@@ -54,3 +54,19 @@ install(
|
||||
DESTINATION
|
||||
mods/silo/silo-client
|
||||
)
|
||||
|
||||
# Install SDK
|
||||
install(
|
||||
DIRECTORY
|
||||
${CMAKE_SOURCE_DIR}/mods/sdk/kindred_sdk
|
||||
DESTINATION
|
||||
mods/sdk
|
||||
)
|
||||
install(
|
||||
FILES
|
||||
${CMAKE_SOURCE_DIR}/mods/sdk/package.xml
|
||||
${CMAKE_SOURCE_DIR}/mods/sdk/Init.py
|
||||
${CMAKE_SOURCE_DIR}/mods/sdk/InitGui.py
|
||||
DESTINATION
|
||||
mods/sdk
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user