A starter template for building a TrussC addon. Click "Use this template" on
GitHub (or copy this folder), rename tcxTemplate → your addon name, and start
writing code.
An addon is a reusable library that drops into a TrussC project. Users add it with one line in
addons.make(ortrusscli addon add tcxYourAddon).
# 1. Create your repo from this template, then clone it into a TrussC install:
trusscli addon clone tcxYourAddon # (after it's published — see §6)
# ...or during development, just put the folder at <TrussC>/addons/tcxYourAddon
# 2. Build & run the bundled example:
cd addons/tcxYourAddon/example-basic
trusscli update # generates CMakePresets.json / IDE files
trusscli run # build + launchThen rename things:
| In this template | Rename to |
|---|---|
repo / folder name tcxTemplate |
tcxYourAddon |
src/tcxTemplate.{h,cpp} |
src/tcxYourAddon.{h,cpp} |
sub-namespace tcx::myaddon |
tcx::youraddon (addon name minus tcx, all lowercase — tcxDepthCamera → tcx::depthcamera) |
class tcx::myaddon::Template |
tcx::youraddon::YourClass |
addon.json fields |
your metadata |
tcxYourAddon/
├── addon.json # registry metadata (see §4)
├── README.md
├── LICENSES.md # this addon's license + any bundled deps/assets
├── src/ # ← your addon code (.h / .cpp / .mm / .m)
│ ├── tcxYourAddon.h
│ └── tcxYourAddon.cpp
├── example-basic/ # a runnable example app — a normal TrussC project
│ ├── addons.make # lists this addon
│ ├── .gitignore # standard project .gitignore (copy-out friendly)
│ ├── src/ # main.cpp + tcApp.{h,cpp}
│ └── (CMakeLists.txt / CMakePresets.json — generated by trusscli, gitignored)
├── libs/ # (optional) bundled 3rd-party libs, auto-collected
└── CMakeLists.txt # (optional) only for custom builds — see §5
Naming conventions (match the rest of TrussC):
- Repo / folder / library target:
tcxPascalCase(e.g.tcxOsc). - Public namespace:
tcx::<addonname>— each addon gets its own sub-namespace, so two addons can both define aWorld/Clientwithout clashing. Users opt in withusing namespace tcx::<addonname>;. (Framework core lives intc.)- The sub-namespace is the addon name minus the
tcxprefix, all lowercase. No abbreviation, no camelCase:tcxOsc→tcx::osc,tcxBox2d→tcx::box2d,tcxDepthCamera→tcx::depthcamera(NOTtcx::depthCameraortcx::dc). All-lowercase keeps the spelling mechanically derivable (tooling can check it) and lets case mark the part of speech, as everywhere else in TrussC:lowercase::= namespace,PascalCase= type,camelCase= function. - Keep names short inside the sub-namespace — the namespace already says whose
Clientit is, so prefertcx::mqtt::Clientovertcx::mqtt::MQTTClient(existing addons keep their historical names; apply this to new code). - Internal helpers that must live in a header go in a nested
detailnamespace; helpers in a.cppgo in an anonymous namespace. - Migrating an addon from flat
tcx::/ legacytrussc::? Keep silent compat aliases until v1.0.0 (no[[deprecated]]attribute — it would warn on idiomatic unqualified use underusing namespace):See tcxOsc for the full pattern.namespace tcx { using osc::OscMessage; } // deprecated: remove at v1.0.0 namespace trussc { using tcx::osc::OscMessage; } // deprecated: remove at v1.0.0
- The sub-namespace is the addon name minus the
- No
using namespacein headers. A using-directive in a public header (at file scope or inside your namespace) silently injects names into every consumer — and into your own namespace's lookup — so qualify explicitly:tc::Vec2,std::string. File-scopeusing namespace std; using namespace tc;in a .cpp is fine (it's TU-local, and it's the normal TrussC app style). - Files:
tcxXxx.h— thetcxprefix is required because every loaded addon'ssrc/shares one include path; a generic name likeTypes.horClient.hwill eventually collide with another addon's header. The prefix is your filename's namespace. TypesPascalCase, functionscamelCase. - Colors are
0.0–1.0floats, angles are radians, useTAU(= 2π) notPI. - Setters return
*thisfor chaining; log withtc::logNotice/logWarning/logError, notcout(stdout is reserved for the MCP channel). - MCP tools an addon registers are named
tcx_<addon>_*(e.g. tcxImGui registerstcx_imgui_click).tc_*is reserved for core-provided tools, and unprefixed names belong to the app author — never squat there.
A user lists it in their project's addons.make:
tcxYourAddon
…and TrussC's build does the rest. By default there is no CMakeLists.txt in an addon — TrussC auto-collects everything:
- compiles
src/**andlibs/*/src/**(.cpp .c .mm .m) - adds
src/,include/, andlibs/*/includeto the include path - links your addon against
TrussC - header-only addon (no sources)? It becomes an
INTERFACEtarget automatically.
So for a plain C++ addon you write zero build config. Just put headers and
sources in src/.
Metadata for the registry crawler, the website, and trusscli. Must be valid
JSON — no comments (the crawler pipes it through jq). An empty {} already
makes the repo a discoverable addon.
To be useful, fill these five — that's the whole "minimum":
{
"description": "One-line summary of what your addon does",
"author": "Your Name <you@example.com>",
"license": "MIT",
"category": "utilities",
"keywords": ["example", "template"]
}| Field | What it does |
|---|---|
description |
One-line summary in listings / trusscli addon list. |
author |
"Name" or "Name <email>". |
license |
SPDX id ("MIT", "Apache-2.0"…). Missing → shows "Unknown". |
category |
One value from the fixed set (below). Unknown/omitted → "misc". |
keywords |
Free tags for search/filtering within a category. |
Everything else is optional — add only what's relevant, roughly in this order of how often it's actually used:
| Field | What it does |
|---|---|
dependencies |
Other addons yours needs (below). Comes up fairly often. |
screenshot |
Relative path ("docs/preview.png") or absolute URL. Shown on the site. |
demo_url |
Link to a live demo (e.g. a TrussSketch page). |
version |
Self-reported version. If omitted, the crawler uses your latest git tag — preferred, so just tag releases. |
trussc_version |
TrussC version range you target, e.g. ">=0.5.0". Informational; least-used field. |
platforms |
Platforms your addon targets, e.g. ["macos","windows","linux"]. Also drives CI — the reusable workflow builds exactly these. Omit → desktop default (macos/windows/linux). web/android/ios are recognized but not built by CI yet (warning, not a failure). win/osx are accepted as aliases. |
name |
Informational only; the registry always uses the repo name. Leave it out. |
3d ai algorithms animation bridges computer-vision game graphics
gui hardware machine-learning network physics sound typography
utilities video web
Anything unknown/empty maps to misc. Don't write "misc" yourself. Use
keywords for finer sub-classification (e.g. category: "graphics" +
keywords: ["shader", "post-fx"]).
Plain string (latest from registry) or an object to pin a version:
tag/branch/commit are mutually exclusive (commit wins if you set more
than one). trusscli addon clone resolves the graph recursively and aborts on
cycles. (This is for addon→addon deps. For a third-party C/C++ library,
use FetchContent in a custom CMakeLists.txt instead — see §5.)
Add a CMakeLists.txt at the addon root only for things auto-collection
can't do: FetchContent, custom flags, code generation, platform link libs.
When present, TrussC add_subdirectory()s it and you own the target fully.
This template includes a fully-commented CMakeLists.txt.example — rename it
to CMakeLists.txt and edit. The essentials:
set(ADDON_NAME tcxYourAddon) # must equal the folder name
file(GLOB ADDON_SOURCES src/*.cpp)
add_library(${ADDON_NAME} STATIC ${ADDON_SOURCES})
target_include_directories(${ADDON_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_link_libraries(${ADDON_NAME} PUBLIC TrussC) # always link TrussCReal examples in the framework: tcxBox2d (FetchContent), tcxTls
(FetchContent + configure_file codegen + per-platform libs).
Drop it under libs/somelib/ with src/ and/or include/ subfolders — auto
collection picks those up with no CMakeLists at all. Add libs/ to
.gitignore if you fetch it instead of vendoring.
Some addons need a file to sit beside the final executable at runtime — a
Windows sensor-SDK .dll, a macOS .dylib/framework, a compiled .metallib,
a data blob. From a custom CMakeLists.txt, register it with:
tc_addon_bundle_file(<path> [MACOS_DEST <subdir>])TrussC copies it to the right place per platform, into whatever app consumes your addon — no need to know the app's target name:
- macOS: into
YourApp.app/Contents/<MACOS_DEST>(defaultResources; useFrameworksfor dylibs/frameworks). - Windows / Linux: next to the executable (so the loader finds the DLL /
.so).
# e.g. a sensor SDK DLL that must sit beside the .exe on Windows:
if(WIN32)
tc_addon_bundle_file("${CMAKE_CURRENT_SOURCE_DIR}/libs/sensor/sensor.dll")
endif()
# e.g. a generated Metal library into the app's Resources:
tc_addon_bundle_file("${CMAKE_CURRENT_BINARY_DIR}/default.metallib" MACOS_DEST Resources)Generated files work too (e.g. a metallib produced by add_custom_command) —
just add_dependencies() your addon target on the generator so the file exists
before the app's post-build copy runs. Real example: tcxSyphon compiles
Syphon's shaders to default.metallib and bundles it this way.
- macOS / iOS (Objective-C++): put
.mm/.mfiles insrc/. They're compiled on every platform, so wrap the platform-only code in#ifdef __APPLE__(orTARGET_OS_*). SeetcxIME/tcxVirtualCam. - Windows / Linux: same idea — guard with
#ifdef _WIN32/__linux__, or link platform libs in a customCMakeLists.txt(if(WIN32) … endif()). - Web (Emscripten): pure C++ generally just works. Avoid blocking I/O.
An addon can ship Android Java helpers and resources — TrussC aggregates them into the consuming app's APK automatically:
tcxYourAddon/android/
├── java/ # *.java — compiled (javac → d8) and bundled into classes.dex
├── res/ # Android resources — passed to aapt via -S
└── manifest/ # *.xml — manifest fragments the addon REQUESTS (advisory)
The app/example may also ship its own android/java, android/res, and
android/AndroidManifest.xml(.in). At build time TrussC collects the project's
dirs plus every loaded addon's dirs, so tcxMidi/android/java/*.java and
your app's own Java compile together. (Native .so code links normally on
Android regardless — this is only about the Java/.dex + res side.)
Manifest — advisory, never auto-injected. The AndroidManifest.xml belongs
to the app and stays fully under the app's control: ship
android/AndroidManifest.xml (copied as-is) or android/AndroidManifest.xml.in
(@TC_APP_PACKAGE@ / @TC_APP_LIB_NAME@ / @TC_APP_HAS_CODE@ expand via
configure_file). The default minSdkVersion is 26 — bump it in your own
manifest if your addon needs a higher API (e.g. native AMidi needs 29).
There is no manifest merger. If your addon needs a <uses-permission>,
<uses-feature>, <receiver>, or <service>, drop those lines in
android/manifest/*.xml. At configure time TrussC gathers all addon fragments
into build-android/REQUIRED_MANIFEST_SNIPPET.xml and prints a notice — the
user then copies in only the parts they want (they may deliberately drop a
permission). So: ship the fragment AND mention it in your README. Keep
<uses-feature> entries required="false" so you never block install on
devices lacking the hardware.
This template ships a tiny .github/workflows/ci.yml that calls the TrussC-org
reusable workflow:
jobs:
build:
uses: TrussC-org/ci-actions/.github/workflows/build-addon.yml@v1That's the whole file — nothing to edit after renaming the addon. It:
- auto-detects the addon name from the repo, so a renamed copy keeps working;
- builds your addon and every
example-*/directory on the platforms fromaddon.json(default: macOS / Windows / Linux — see §4); - so a fresh copy of this template goes green out of the box (it just verifies the example compiles).
The shipped on: block runs CI on every branch (branches: ['**']) plus
v* tags and main-targeted PRs — so a push never silently skips CI, and no
branch name is "magic." It's cheap (the trusscli build is cached per repo and
shared across branches). If your addon's CI is genuinely too heavy or low-value
to run on every push, narrow it to branches: [main] (opt-out).
If your addon needs a vendor SDK that isn't an apt package (a depth camera,
capture device, …), the reusable workflow can't install it for you — but it
gives you a hook. Pass a setup_script: bash that runs on every platform just
before the build, branches on $RUNNER_OS, downloads the SDK, and exports what
the build needs via $GITHUB_ENV (e.g. CMAKE_PREFIX_PATH so find_package
resolves it). Combine with addon.json "platforms" to drop unsupported OSes.
See the ci-actions README
and tcxOrbbec
for a worked example.
Behavioral tests are opt-in by the presence of a tests/ directory — this
template ships none, so nothing runs until you add one. To opt in, create a
normal TrussC console project under tests/ whose main() returns non-zero on
failure:
tcxYourAddon/
└── tests/
├── addons.make # lists this addon
└── src/main.cpp # int main() { ...assert...; return failures; }
CI then builds and runs it automatically (a non-zero exit fails the job),
skipping it entirely when absent. See
tcxPly/tests for a
complete example. (Console only, so it runs headless on all CI runners — no
window.)
No pull request needed — the registry discovers addons by GitHub topic:
- Push your repo to GitHub, public, not archived.
- Repo name must match
tcx[A-Z]…(e.g.tcxYourAddon). - Add the topic
trussc-addon(repo → About → ⚙ → Topics). - Keep
addon.jsonat the repo root (its presence is the opt-in marker). - Tag releases (
git tag v0.1.0 && git push --tags) — the crawler records the latest tag as the version.
A scheduled crawl at TrussC-org/trussc-addons
finds it within a day and regenerates the registry (registry.json on
gh-pages), which trusscli reads.
Don't add the
trussc-addontopic to a copy of this template unless it's a real, finished addon — otherwise the literaltcxTemplatewould show up in everyone's registry.
This template is MIT. Edit LICENSES.md — put your name/year on the addon's
own section, and add one ----separated section for each third-party
library you bundle/fetch and each data asset you ship (state its source and
its license). Keep the license field in addon.json set to your addon's own
SPDX id ("MIT", etc.).
Convention: the file is
LICENSES.md(plural) — addons routinely vendor deps and assets, so the file is built to list them all, not just the addon's own license.