Core DevPre-alpha
Build a module with SWC
Step-by-step recipe for an addon that ships Go models, XML views, and optionally a custom SWC field widget. Assumes you have Sumeru running from sumeru_custom_addons (see Creating an addon).
1. Scaffold the module
my_crm/
├── manifest.json
├── models/
│ └── lead.go
├── views/
│ ├── lead_views.xml
│ └── lead_menus.xml
├── security/
│ └── sys.access.csv
└── static/swc/ # optional
├── main.ts
└── esbuild.config.mjs
manifest.json:
{
"name": "my_crm",
"version": "1.0.0",
"depends": ["base"],
"data": [
"security/sys.access.csv",
"views/lead_views.xml",
"views/lead_menus.xml"
]
}
2. Define the model
package models
import "sumeru/core/sdk"
type Lead struct {
sdk.Model `sumeru:"model=my.crm.lead"`
Name sdk.String `sumeru:"required,string=Lead Name"`
Email sdk.Email `sumeru:"string=Email"`
Phone sdk.Phone `sumeru:"string=Phone"`
Active sdk.Boolean `sumeru:"default=true,string=Active"`
Priority sdk.String `sumeru:"selection=0:Low,1:Normal,2:High,string=Priority"`
}
Run make generate so models/zmodels.go registers the model.
3. Write views
views/lead_views.xml:
<sumeru>
<data>
<view id="view_my_crm_lead_list" model="my.crm.lead" type="list">
<field name="name"/>
<field name="email"/>
<field name="phone"/>
<field name="priority" widget="priority"/>
<field name="active" widget="boolean_toggle"/>
</view>
<view id="view_my_crm_lead_form" model="my.crm.lead" type="form">
<sheet>
<div class="sum_title"><h1><field name="name"/></h1></div>
<group>
<field name="email" widget="email"/>
<field name="phone" widget="phone"/>
<field name="priority" widget="priority" options="mode:stars,stars:3"/>
<field name="active" widget="boolean_toggle"/>
</group>
</sheet>
</view>
<record id="action_my_crm_leads" model="sys.action.window">
<field name="name">Leads</field>
<field name="core_model">my.crm.lead</field>
<field name="view_mode">list,form</field>
</record>
<menuitem id="menu_my_crm_root" name="My CRM" sequence="90"/>
<menuitem id="menu_my_crm_leads" name="Leads" parent="menu_my_crm_root"
action="action_my_crm_leads"/>
</data>
</sumeru>
See Field attributes and View types for all attributes.
4. Install and verify
cd sumeru_custom_addons
go run . -- -c sumeru.conf -u my_crm --stop-after-init
make run
Open My CRM → Leads. SWC renders list and form from /web/swc/workspace JSON. Save/delete/object feedback uses env.services.notification — see SWC notifications.
5. Optional — custom SWC widget
Add to manifest.json:
"swc_entry": "static/swc/main.js"
static/swc/main.ts:
import { registry } from "/static/swc/swc.js";
// SwcComponent and html are available on the global SumeruSWC bundle exports
const { SwcComponent, html } = window.SumeruSWC as typeof import("@sumeru/swc");
class ColorBadgeField extends SwcComponent {
template() {
const { field, record, readonly } = this.props as {
field: { name: string; string?: string };
record: { get: (n: string) => unknown; set: (n: string, v: unknown) => void };
readonly: boolean;
};
const val = String(record.get(field.name) ?? "");
return html`<div class="sum-field-widget sum-field-widget--row">
<label class="sum-field-label">${field.string ?? field.name}</label>
<div class="sum-field-control">
<input class="sum-field-input" type="color" value=${val || "#875a7b"}
disabled=${readonly ? "disabled" : undefined}
@input=${(ev: Event) => record.set(field.name, (ev.target as HTMLInputElement).value)} />
</div>
</div>`;
}
}
registry.category("fields").add("color_badge", ColorBadgeField);
Build with esbuild targeting ES2022 (same as core make swc). Reference in XML:
<field name="priority" widget="color_badge"/>
The kernel injects addon URLs via window.__SWC_ADDON_ENTRIES__ before SWC boots.
6. Debug
- SWC Vision: Load the browser extension from
core/swc/devtools-extension/or use the in-app debug panel. - Tests: Mirror patterns in
core/swc/tests/views/form-widgets.test.ts. - Arch JSON: Inspect
GET /web/swc/workspace?action=…&view_type=formresponse.
Checklist
- [ ] Model registered in Go ORM
- [ ] Security CSV grants access
- [ ] Views listed in manifest
databefore menus - [ ] Action
view_modeincludes your view types - [ ] Field
namevalues match model fields - [ ] Custom widget registered before first render (via
swc_entry)