1.1.1Canonical widget skeleton

You can write a widget as a single HTML file, containing Javascript and CSS, and use that by pasting it into the HTML tab of the External Widget window. For example:

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<title>My Widget</title>

<!-- Framework CSS + JS — required for all widgets -->

<link rel="stylesheet" type="text/css"

href="https://mytrader.fxbluelabs.com/css/widget-css"/>

<script src="https://mytrader.fxbluelabs.com/scripts/widget-js"></script>

<style>

/* Use framework CSS variables so the widget follows the user's theme

automatically. Never hard-code colours — always fall back to --clr-*. */

html, body {

margin: 0;

padding: 0;

height: 100%;

font-family: var(--font-family, system-ui);

font-size: var(--font-size-base, 13px);

color: var(--clr-text, #e4e4e4);

/* The framework does not publish a widget-background variable;

leaving background transparent lets the platform frame show

through correctly in every theme. */

background: transparent;

}

.widget-header {

padding-block: 10px;

padding-inline-start: 14px;

padding-inline-end: var(--widget-chrome-inset-horizontal, 40px);

border-bottom: 1px solid var(--clr-border1, rgba(255,255,255,0.08));

font-weight: 600;

}

</style>

</head>

<body>

<div class="widget-header">My Widget</div>

<div id="content"></div>

<script>

// ------------------------------------------------------------------

// 1. Create a framework instance.

// Widgets must do this explicitly; scripts and UDIXes get one

// automatically.

// ------------------------------------------------------------------

var Framework = new FXB.Framework();

// ------------------------------------------------------------------

// 2. Choose a namespace for persisted settings.

// This ID both scopes your data and authorises later writes — treat

// it as mildly sensitive. Prefer category settings (work everywhere)

// over private settings (page-container-only; silently no-op in

// floating windows).

// ------------------------------------------------------------------

var SETTINGS_ID = "my-widget-unique-id";

// ------------------------------------------------------------------

// 3. OnLoad is the ONLY safe place to start using framework APIs.

// Anything that touches Framework.Instruments, Framework.Ask, etc.

// before OnLoad fires may fail silently.

// ------------------------------------------------------------------

Framework.OnLoad = function() {

// Category settings must be loaded before they can be saved. The

// callback fires asynchronously with the stored object, or {} if

// nothing has been saved yet.

Framework.LoadCategorySettings(SETTINGS_ID, function(settings) {

settings = settings || {};

// ... restore UI state from `settings` ...

render();

});

};

// ------------------------------------------------------------------

// 4. React to runtime events. IMPORTANT: a single message can carry

// multiple update types at once (e.g. a price tick AND an order

// change). Always use Msg.is(...) with SEPARATE `if` blocks.

// Never switch on .type and never use else-if — both silently

// drop updates when more than one type is present. See §4.1.

// ------------------------------------------------------------------

Framework.OnMessage = function(Msg) {

if (!Msg) return;

if (Msg.is(FXB.MessageTypes.THEME)) {

// CSS variables and body classes update automatically; uncomment

// below only if you draw theme-dependent colours into canvas/SVG.

// redrawCanvas();

}

if (Msg.is(FXB.MessageTypes.CATEGORY_SETTINGS_CHANGE)) {

// Another instance of this widget changed settings — or we did.

// Settings are already updated in Framework.categorySettings;

// no need to call LoadCategorySettings() again.

render();

}

};

// ------------------------------------------------------------------

// 5. Persist user choices via SaveCategorySettings. All instances of

// this widget will then receive CATEGORY_SETTINGS_CHANGE.

// ------------------------------------------------------------------

function saveSettings(data) {

// Note: SaveCategorySettings() takes ONLY the data — no ID parameter.

// The ID was bound by the earlier LoadCategorySettings() call on start-up.

// Passing an ID here will silently save the wrong value with no error.

Framework.SaveCategorySettings(data);

}

function render() {

// ... redraw UI from Framework.categorySettings ...

}

</script>

</body>

</html>