The sg API
kit.js adds one global object, sg. It is how your page talks to Broadcast and to the person using it.
| Name | Does |
|---|---|
sg.pageData(input, page) |
Calls your plugin’s pageData function |
sg.call(op, args) |
Calls one bridge operation |
sg.toast(text, options) |
Shows a short message |
sg.confirm(options) |
Asks yes or no |
sg.copy(text) |
Copies text |
sg.openExternal(url) |
Opens a link outside Broadcast, after the person agrees |
sg.subscriberId() |
Reads the subscriber of a panel |
sg.SgError |
The error class |
sg.version |
"1" |
sg.pageData
Calls pageData in your dist/main.js, and gives you what it returns.
const data = await sg.pageData({ action: "refresh" });
| Value | Default | Meaning |
|---|---|---|
input |
{} |
Any JSON object. Your function gets it as input |
page |
0 |
A number your function gets as page. Use it for a page index, or ignore it |
On the plugin side:
export default definePlugin({ async pageData({ page, input }, ctx) { return { message: `You asked for ${input.action}` }; }, });
This is the main door. Use it for everything that needs secrets, writes or fetch. pageData runs with the plugin’s permissions, so check input.
sg.call
Calls one bridge operation directly. It gives the result, or it throws.
const opens = await sg.call("store.increment", { key: "opens" }); const tags = await sg.call("tags.list"); const person = await sg.call("store.get", { key: "note", subscriber_id: 12 });
Plugin Pages lists the allowed operations. A page acts as the person viewing it, and it cannot change subscribers or read secrets. For those, use pageData.
SgError
sg.call and sg.pageData throw an SgError when a call fails. It has two useful fields:
| Field | Meaning |
|---|---|
code |
A short code, such as forbidden, not_found, plugin_error or timeout |
message |
Text for you |
try { await sg.call("subscribers.list"); } catch (error) { if (error.code === "forbidden") { sg.toast("This plugin may not read subscribers.", { tone: "danger" }); } else { throw error; } }
Always handle errors. A call can fail for reasons that are not your bug, such as a slow plugin. Without a try, the page just stops, with no word to the person.
When your pageData function throws, Broadcast does not pass its message to the browser. Both code and message are then plugin_error. Read the plugin’s logs for the real message. To show a friendly message, return it as data instead of throwing:
if (!text) return { error: "Write a note first." };
sg.toast
A short message at the bottom right that goes away by itself.
sg.toast("Saved"); sg.toast("Not saved: the server did not answer", { tone: "danger", duration: 8000 });
| Option | Default | Meaning |
|---|---|---|
tone |
Dark | danger makes it red. Other tones have no effect |
duration |
4000 |
Milliseconds on screen |
It returns the message element. Text is shown as text.
Common mistake. Showing important information in a toast. It disappears. Use sg-banner.
sg.confirm
Asks a question in a dialog. It gives true or false.
const yes = await sg.confirm({ title: "Delete this poll?", message: "The answers are deleted too.", confirmLabel: "Delete poll", danger: true, }); if (!yes) return;
| Option | Default | Meaning |
|---|---|---|
title |
Are you sure? |
The question |
message |
More detail | |
confirmLabel |
Confirm |
The button that says yes |
cancelLabel |
Cancel |
The button that says no |
danger |
false |
Makes the yes button red |
The Esc key counts as no. Name the action in confirmLabel: “Delete poll” is better than “OK”.
sg.copy
Copies text. It gives true if that worked, and false if not.
const copied = await sg.copy(link); sg.toast(copied ? "Copied" : "Press Cmd+C to copy");
Call it from a click. The sandbox can refuse the clipboard. In a test with no click, it gave false. So always check the answer, or use sg-copy, which handles this for you.
sg.openExternal
Asks Broadcast to open a link outside Broadcast. Broadcast shows the person the full address, and the person chooses. It gives { opened: true } or { opened: false }.
button.addEventListener("click", async () => { const { opened } = await sg.openExternal("https://example.com/guide"); });
Call it from a click. The address must be plain https, with no user name or password, and up to 2048 characters. Otherwise it throws an SgError with one of these codes:
| Code | Meaning |
|---|---|
invalid_url |
Broadcast does not accept the address |
no_user_action |
The call did not come from a recent click inside your page |
busy |
The dialog is open, or it closed less than a second ago |
sg.subscriberId
In a subscriberPanel frame, it gives the subscriber’s ID as a string. Anywhere else, it gives null.
const id = sg.subscriberId();
Components and events
All Signal events bubble, so you can listen on a parent. The data is in event.detail.
| Event | From | detail |
|---|---|---|
sg-submit |
sg-form |
{ values } |
sg-search |
sg-search |
{ value } |
sg-change |
sg-checkbox, sg-switch |
{ checked } |
sg-change |
sg-radio-group, sg-tag-input |
{ value } |
sg-select |
sg-menu |
{ value } |
sg-close |
sg-dialog |
{ value } |
sg-tab |
sg-tabs |
{ index, value } |
sg-page |
sg-pagination |
{ page } |
sg-row-click |
sg-table |
{ row } |