-
-
Notifications
You must be signed in to change notification settings - Fork 110
gSender Plugins ‐ Getting Started
gSender plugins are small web apps that run inside gSender (in an iframe). You build the UI yourself — plain JavaScript, React, or anything that compiles to static files — and talk to gSender through the Plugin SDK.
You don’t need to fork gSender. A plugin is:
- A
gsender-plugin.jsonmanifest - A built
ui/folder with anindex.htmlentry - Optional use of
@sienci/gsender-plugin-sdk
- Node.js (for installing deps and building with Vite)
- A recent gSender build that includes the plugin system
- Basic web skills (HTML/JS is enough to start)
The gSender repo ships examples under plugins/:
| Template | Best if you want… |
|---|---|
example-hello/ |
Plain JS, learn the bridge quickly |
react-ts-app/ |
React + TypeScript hooks |
example-viewer/ |
Embedded G-code preview |
basic-cam/ |
Full CAM-style tool (forms → G-code → preview → load to job) |
Recommendation: start with example-hello or react-ts-app, then graduate to basic-cam when you need a real tool.
cp -R plugins/example-hello plugins/my-tool
cd plugins/my-toolOpen gsender-plugin.json and make it yours:
{
"id": "com.example.my-tool",
"name": "My Tool",
"description": "A short blurb shown on the Tools page.",
"version": "0.1.0",
"engine": ">=1.6.0",
"ui": {
"entry": "ui/index.html",
"contributions": [
{
"slot": "tools-page",
"route": "my-tool",
"label": "My Tool"
}
]
},
"capabilities": {
"requestTypes": ["gcode:load:to:visualizer"],
"topics": ["workspace"],
"allowedFunctions": ["gcode", "useWorkspaceState", "GCodeVisualizer"]
}
}Important fields:
-
id— unique reverse-DNS id (com.yourorg.plugin-name). Usecom.sienci.*only for official Sienci plugins. -
route/label— how it appears under Tools -
capabilities— what bridge access the plugin is allowed to use (see below)
The capabilities block is the plugin’s access list. gSender uses it to decide which bridge calls and live subscriptions the plugin may use — anything not listed is denied.
This block will be populated during the plugin import process. For development testing, you need to fill it in yourself.
"capabilities": {
"requestTypes": ["gcode:load:to:visualizer"],
"topics": ["workspace"],
"allowedFunctions": ["gcode", "useWorkspaceState", "GCodeVisualizer"]
}| Field | What it controls |
|---|---|
requestTypes |
One-shot bridge RPCs the plugin can call (e.g. gcode:load:to:visualizer, machine:get:context) |
topics |
Live subscriptions the plugin can open (workspace, redux) |
allowedFunctions |
SDK symbols the plugin is expected to use (e.g. gcode, useWorkspaceState) — used for scanning / review |
This table lists the request types that will be required depending on what symbols you have used from the SDK.
| SDK Import | Request Type |
|---|---|
gsender |
All Request Types |
machine |
machine:get:context, machine:command
|
gcode |
gcode:load:to:visualizer |
workspace |
workspace:get:state |
getWorkspaceState |
workspace:get:state |
redux |
redux:get:state |
getReduxState |
redux:get:state |
getSelector |
redux:get:state |
This table lists the topics that will be required depending on what symbols you have used from the SDK.
| SDK Import | Topic |
|---|---|
subscribeWorkspaceState |
workspace |
subscribeSelector |
redux |
useWorkspaceState |
workspace |
useTypedSelector |
redux |
In order for your plugin to work, you will need to import the vite plugin from the SDK into your vite.config. Add it to the plugins section like so:
import gsenderPlugin from "@sienci/gsender-plugin-sdk/vite";
export default defineConfig({
plugins: [gsenderPlugin()],
[...]
});npm install
npm run buildThat writes the production bundle to ui/ (this folder is build output — rebuild after code changes).
Developing against the gSender repo
With npm run electron:hot / NODE_ENV=development, gSender loads plugins from the repo’s plugins/ folder automatically. After adding a new plugin folder, restart the server once so its route is mounted.
Installing into a normal gSender app
Copy the whole plugin folder (including built ui/) into the plugins directory:
| OS | Location |
|---|---|
| macOS | ~/Library/Application Support/gSender/plugins/ |
| Windows | %APPDATA%\gSender\plugins\ |
| Linux | ~/.config/gSender/plugins/ |
Exact path is also shown under Tools → Plugins. Then restart gSender and enable the plugin if needed.
Go to Tools → your plugin’s card/label. You should see your UI in the panel.
Install the SDK in your plugin:
npm install @sienci/gsender-plugin-sdk(In the monorepo examples this is often file:../../packages/plugin-sdk.)
import {
gsender,
subscribeWorkspaceState,
subscribeSelector,
} from "@sienci/gsender-plugin-sdk";
// One-shot: machine context
const ctx = await gsender.machine.getContext();
// Live workspace (units, profile, etc.)
subscribeWorkspaceState((workspace) => {
console.log(workspace?.units);
});
// Live Redux slice
subscribeSelector(
(state) => state.connection?.isConnected ?? false,
(connected) => console.log("connected:", connected),
);
// Load G-code into gSender’s main job/visualizer
await gsender.gcode.loadToVisualizer(gcode, "my-part.nc");import { gsender } from "@sienci/gsender-plugin-sdk";
import {
useWorkspaceState,
useTypedSelector,
} from "@sienci/gsender-plugin-sdk/react";
const workspace = useWorkspaceState();
const isConnected = useTypedSelector((s) => s.connection?.isConnected);npm install @sienci/gviewer threeimport { GCodeViewer } from "@sienci/gsender-plugin-sdk/viewer";
const viewer = new GCodeViewer({
id: "preview",
container: document.getElementById("preview"),
});
await viewer.loadFromText(gcode);
viewer.focusToModel();Give the container a fixed height and position: relative; overflow: hidden so the canvas doesn’t overflow the iframe.
- Run gSender in development.
- In the plugin folder:
npm run build -- --watch- Edit source → Vite rebuilds
ui/→ the open plugin iframe reloads.
Tips:
- New plugin folders need a server restart once.
- Edits to an already-loaded plugin hot-reload.
- Style dark mode with
html.dark(or Tailwinddark:class strategy). gSender setshtml.darkon the iframe — don’t rely onprefers-color-schemealone.
my-tool/
gsender-plugin.json # required
package.json
vite.config.js # base: "./", outDir: "ui"
index.html # Vite entry
src/
main.js # or App.tsx
style.css
ui/ # build output (do not hand-edit)
Vite config essentials:
export default {
base: "./",
build: { outDir: "ui", emptyOutDir: true },
};Relative base matters because plugins are served from a subpath, not the site root.
- Unique
id(not colliding with another plugin) - Clear
name,description,version -
ui.entrypoints atui/index.htmland that file exists after build -
capabilitiesmatch what you actually use - Built
ui/included when distributing (zip the folder afternpm run build) - Works with gSender light and dark themes
| Resource | Purpose |
|---|---|
plugins/example-hello |
Minimal bridge demo |
plugins/react-ts-app |
React hooks demo |
plugins/example-viewer |
Preview + load-to-job |
plugins/basic-cam |
End-to-end CAM reference |
@sienci/gsender-plugin-sdk README |
Full API surface |
| Tools → Plugins in the app | Install path, enable/disable |
A gSender plugin is a small SPA that gSender hosts locally; the SDK is your only door into machine state, live UI data, and loading jobs — keep all CNC logic and UI in the plugin, and call the host through the bridge.