This repository has been archived on 2026-04-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
core/src/js/sw/sdk/actions.js
Jeffrey Smith 22c70034c6 Feat v0.2.4: SDK Topbar, Schedules surface, manifest icons, UserMenu cleanup
Shell navigation:
- sw.shell.Topbar — composable nav bar (title + slot + bell + user menu)
- Topbar CSS in sw-shell.css, wired into SDK via dynamic import

Schedules surface (packages/schedules/):
- Wraps kernel /api/v1/schedules CRUD + run + logs
- Table view with cron badge, human-readable preview, enable toggle
- Create/edit dialog with live cron-to-english preview

Manifest icons:
- icon field in manifest.json (emoji string)
- Surfaces API returns icon from package manifest
- UserMenu renders per-surface icons

UserMenu cleanup:
- Removed dead Chat/Notes/Projects hardcoded links
- Menu now driven by /api/v1/surfaces API (installed surfaces only)
- Core surfaces filtered via CORE_IDS set

Bug fixes:
- isAdmin() in can.js now checks surface.admin.access RBAC grant
  instead of deprecated user.role column (v0.2.0 regression)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 10:16:37 +00:00

71 lines
2.1 KiB
JavaScript

// ==========================================
// Switchboard Core — SDK: Action Registry
// ==========================================
// Named actions that extensions expose for invocation.
//
// Factory: createActions(emitFn)
// ==========================================
/**
* Create the action registry.
*
* @param {Function} emitFn — events.emit for notifications
* @returns {object} actions
*/
export function createActions(emitFn) {
/** @type {Map<string, {handler:Function, label?:string, icon?:string}>} */
const _registry = new Map();
return {
/**
* Register a named action.
* @param {string} id — unique action id (e.g. 'tasks.create')
* @param {object} def — { handler, label?, icon? }
* @returns {Function} unregister
*/
register(id, { handler, label, icon }) {
_registry.set(id, { handler, label, icon });
emitFn('action.registered', { id, label }, { localOnly: true });
return () => this.unregister(id);
},
/**
* Remove an action.
*/
unregister(id) {
if (_registry.delete(id)) {
emitFn('action.unregistered', { id }, { localOnly: true });
}
},
/**
* Execute an action by id.
* @param {string} id
* @param {...any} args — passed to handler
* @returns {Promise<any>}
*/
async run(id, ...args) {
const entry = _registry.get(id);
if (!entry) throw new Error(`[sw.actions] Unknown action: ${id}`);
const result = await entry.handler(...args);
emitFn('action.executed', { id }, { localOnly: true });
return result;
},
/**
* List registered action metadata.
* @returns {Array<{id:string, label?:string, icon?:string}>}
*/
list() {
return [..._registry.entries()].map(([id, { label, icon }]) => ({ id, label, icon }));
},
/**
* Check if an action is registered.
*/
has(id) {
return _registry.has(id);
},
};
}