Desktop Widgets
Desktop widgets are web components injected into the Hiclaro dashboard as a self-contained JavaScript bundle.
Bundle contract
The desktop client fetches widgets/desktop/bundle.js, injects it into the page as a <script> tag, and then waits for each declared tag to be registered via customElements.define. The bundle must satisfy the following constraints:
Self-contained IIFE. The bundle must be a single immediately-invoked function expression that calls
customElements.define(tag, class extends HTMLElement {…})for every widget it ships. No external imports, no dynamicimport(), no lazy chunks, no CDN dependencies.DOM properties, not attributes.
widgetApi,instanceConfig, andpluginIdare set on the element as JavaScript properties after it is appended to the DOM. They are complex objects or strings — do not try to read them as HTML attributes. React to them viasetaccessors.pluginIdis a string containing the UUID of the plugin that owns this widget, injected by the host so that widgets can filtergetAllAssets()without hardcoding an install-time identifier.Inline all CSS. External stylesheets are not loaded. Use a Shadow DOM (
this.attachShadow({ mode: 'open' })) with a<style>tag inside, or inject aCSSStyleSheetvia the Constructable Stylesheets API. Shadow DOM also isolates your widget's styles from the host page.Inline images and fonts. External asset URLs are not reachable in the Electron context. Encode small assets as data URIs, or fetch them at runtime via
widgetApiif they must be served from your plugin's backend.Allow
app://in CORS. In the Electron desktop app the renderer origin isapp://rather thanhttp://localhost. If your widget makes direct HTTP calls to your plugin's own service backend, that backend must includeapp://in its CORS allowed origins.Clean up in
disconnectedCallback. Unsubscribe fromwidgetApi.subscribeTelemetryand clear any timers or event listeners when the element is removed from the DOM.
Vanilla JS example
A minimal sensor widget using plain JavaScript and Shadow DOM. Build this with any bundler (esbuild, rollup, webpack) targeting a single IIFE output file.
React example
You can build desktop widgets with React by wrapping a React component inside a custom element. Use your bundler to compile the source into a self-contained IIFE — React and ReactDOM must be bundled into the output file, not treated as externals.
The custom element acts as the bridge: it creates a DOM container in connectedCallback, mounts a React root into it, and re-renders whenever widgetApi or instanceConfig are set as DOM properties. disconnectedCallback unmounts the root to avoid memory leaks.
WidgetApi
The widgetApi object is set on your custom element as a DOM property before or shortly after connectedCallback. Always guard against it being undefined on first render and subscribe in the set widgetApi setter.