Pre-alpha. No tagged release and no upgrade path between versions. Use for evaluation and development only, not production.
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=form response.

Checklist

  • [ ] Model registered in Go ORM
  • [ ] Security CSV grants access
  • [ ] Views listed in manifest data before menus
  • [ ] Action view_mode includes your view types
  • [ ] Field name values match model fields
  • [ ] Custom widget registered before first render (via swc_entry)

See also