The FDO host automatically loads a set of CSS and JavaScript libraries into the sandboxed iframe that runs your plugin’s UI. You do not install or import these — they are available as browser globals the moment your render() output is mounted.
These libraries are available only in the iframe UI runtime — in your HTML output, inline scripts, and renderOnLoad() code. They are not available in your plugin backend/runtime (the init(), render(), handler, and storage code that runs in the plugin process). Do not attempt to call Notyf, hljs, ace, Split, or window.* helpers from backend code.
CSS libraries
PureCSS
PureCSS provides a minimal set of responsive CSS modules. You use it by adding class names directly to your HTML.
Grid system
<div class="pure-g">
<div class="pure-u-1-3">One third</div>
<div class="pure-u-1-3">One third</div>
<div class="pure-u-1-3">One third</div>
</div>
Responsive breakpoint variants follow the pattern pure-u-{breakpoint}-{fraction}:
<!-- Full width on mobile, half on tablet, third on desktop -->
<div class="pure-u-1 pure-u-md-1-2 pure-u-lg-1-3">Column</div>
Buttons
<button class="pure-button">Default button</button>
<button class="pure-button pure-button-primary">Primary button</button>
Forms and tables
<form class="pure-form">...</form>
<table class="pure-table pure-table-bordered">...</table>
Menus
<div class="pure-menu pure-menu-horizontal">
<ul class="pure-menu-list">
<li class="pure-menu-item"><a href="#" class="pure-menu-link">Home</a></li>
<li class="pure-menu-item"><a href="#" class="pure-menu-link">Docs</a></li>
</ul>
</div>
Highlight.js
Highlight.js provides syntax highlighting with the VS theme pre-loaded.
Wrap code in <pre><code class="language-{lang}"> and call hljs.highlightAll() in a script block:
<pre><code class="language-typescript">
const message = "Hello, World!";
class Plugin extends FDO_SDK {
init() {
this.log("Plugin initialized");
}
}
</code></pre>
<script nonce="plugin-script-inject">
hljs.highlightAll();
</script>
hljs is available as window.hljs.
Notyf
Notyf displays toast notifications. Its styles are pre-loaded; you only need to instantiate it in a script block.
const notyf = new Notyf({
duration: 3000,
position: { x: 'right', y: 'top' },
ripple: true,
dismissible: true
});
notyf.success('Operation completed successfully!');
notyf.error('Something went wrong!');
// Custom notification
notyf.open({
type: 'info',
message: 'Custom notification with any background color.',
background: '#7b1fa2',
duration: 5000
});
Notyf is available as window.Notyf.
JavaScript libraries
FontAwesome
FontAwesome provides icon sets for solid (fas), regular (far), and brands (fab) icons.
<!-- Solid icons -->
<i class="fas fa-home"></i>
<i class="fas fa-user"></i>
<i class="fas fa-check" style="color: green;"></i>
<!-- Regular icons -->
<i class="far fa-star"></i>
<i class="far fa-heart"></i>
<!-- Brand icons -->
<i class="fab fa-github"></i>
<i class="fab fa-twitter" style="color: #1da1f2;"></i>
Icons render inline and inherit font-size and color from their container.
Split Grid
Split Grid enables resizable panel layouts using CSS grid.
Set up a grid container with a gutter element, then call Split:
<style>
.split-container {
display: grid;
grid-template-columns: 1fr 10px 1fr;
height: 400px;
}
.gutter { background-color: #eee; cursor: col-resize; }
.panel { padding: 15px; overflow: auto; }
</style>
<div class="split-container">
<div class="panel">Left panel content</div>
<div class="gutter gutter-col-1"></div>
<div class="panel">Right panel content</div>
</div>
<script nonce="plugin-script-inject">
window.waitForElement('.gutter-col-1', () => {
Split({
columnGutters: [{
track: 1,
element: document.querySelector('.gutter-col-1'),
}],
columnMinSize: 100
});
});
</script>
Split is available as window.Split.
ACE Editor
ACE Editor is a full-featured browser-based code editor.
window.waitForElement('#editor', (element) => {
const editor = ace.edit("editor");
editor.setTheme("ace/theme/monokai");
editor.session.setMode("ace/mode/typescript");
editor.setValue("// Start coding...", 1);
editor.setOptions({
fontSize: "14px",
showPrintMargin: false
});
});
Use waitForElement (see below) rather than accessing the editor element immediately, since the DOM may not be ready at script evaluation time.
ace is available as window.ace.
Goober
Goober is the CSS-in-JS library used internally by the SDK’s DOM helper classes. It is exposed as window.goober, but you rarely need to call it directly — the DOM base class exposes createClassFromStyle() and createStyleKeyframe() as higher-level wrappers. See DOM Helpers for details.
Window helper functions
The FDO host injects the following functions onto window in the iframe runtime.
window.createBackendReq(type, data)
Sends a message to a named handler registered on your plugin backend and returns a Promise with the handler’s response.
// Parameters
type: string // name of the registered handler
data?: any // payload passed to the handler
// Returns
Promise<any>
try {
const result = await window.createBackendReq('getUserData', { userId: 123 });
document.getElementById('output').textContent = JSON.stringify(result);
} catch (error) {
console.error('Backend request failed:', error);
}
Register the matching handler in init():
PluginRegistry.registerHandler("getUserData", (data) => {
return { name: "Alice", id: data.userId };
});
window.waitForElement(selector, callback, timeout?)
Polls until an element matching selector appears in the DOM, then calls callback with the element. Use this for DOM-dependent initialization instead of setTimeout.
// Parameters
selector: string // CSS selector
callback: (el: Element) => void
timeout?: number // ms before giving up (default: 5000)
window.waitForElement('#editor', (element) => {
const editor = ace.edit("editor");
editor.setTheme("ace/theme/monokai");
editor.session.setMode("ace/mode/javascript");
});
window.executeInjectedScript(scriptContent)
Executes a JavaScript string in the plugin iframe context. Use this for dynamically generated scripts.
// Parameters
scriptContent: string // JavaScript source to execute
window.executeInjectedScript(`
console.log('Running in plugin context');
document.title = 'My Plugin';
`);
window.addGlobalEventListener(eventType, callback)
Attaches an event listener to window from within the iframe.
// Parameters
eventType: string // DOM event type, e.g. 'resize', 'keydown'
callback: EventListener
window.addGlobalEventListener('keydown', (event) => {
if (event.key === 'F1') {
event.preventDefault();
notyf.open({ type: 'info', message: 'Help triggered!' });
}
});
window.removeGlobalEventListener(eventType, callback)
Removes an event listener previously added with addGlobalEventListener. Pass the same function reference used when adding.
// Parameters
eventType: string
callback: EventListener
const handleResize = () => {
console.log('Resized:', window.innerWidth);
};
window.addGlobalEventListener('resize', handleResize);
// Later, when the listener is no longer needed:
window.removeGlobalEventListener('resize', handleResize);
window.applyClassToSelector(className, selector)
Adds a CSS class to the element matched by selector.
// Parameters
className: string // class name to add
selector: string // CSS selector for the target element
window.applyClassToSelector('pure-button-primary', '#my-button');
Full example
The following is adapted from examples/07-injected-libraries-demo.ts and shows several injected libraries used together in a single plugin:
import { FDO_SDK, FDOInterface, PluginMetadata } from "@anikitenko/fdo-sdk";
export default class InjectedLibrariesDemoPlugin extends FDO_SDK implements FDOInterface {
private readonly _metadata: PluginMetadata = {
name: "Injected Libraries Demo",
version: "1.0.0",
author: "FDO SDK",
description: "Demonstrates automatically injected libraries and helper functions",
icon: "lightbulb"
};
get metadata(): PluginMetadata { return this._metadata; }
init(): void {
this.log("InjectedLibrariesDemoPlugin initialized!");
}
render(): string {
return `
<div class="demo-container" style="padding: 20px;">
<h2><i class="fas fa-book"></i> Injected Libraries Demo</h2>
<!-- Pure CSS responsive grid -->
<div class="pure-g">
<div class="pure-u-1 pure-u-md-1-2 pure-u-lg-1-3">
<div style="padding: 10px; background: #e3f2fd; margin: 5px;">
<strong>Column 1</strong>
</div>
</div>
<div class="pure-u-1 pure-u-md-1-2 pure-u-lg-1-3">
<div style="padding: 10px; background: #fff3e0; margin: 5px;">
<strong>Column 2</strong>
</div>
</div>
</div>
<!-- Notyf notification buttons -->
<button class="pure-button pure-button-primary" onclick="showSuccess()">
<i class="fas fa-check"></i> Show Success
</button>
<button class="pure-button" onclick="showError()"
style="background: #d32f2f; color: white; margin-left: 10px;">
<i class="fas fa-times"></i> Show Error
</button>
<!-- Syntax highlighted code block -->
<pre><code class="language-javascript">
const message = "Hello, World!";
function greet(name) { return \`Hello, \${name}!\`; }
</code></pre>
<!-- ACE Editor -->
<div id="editor" style="height: 300px; border: 1px solid #ccc;"></div>
<!-- Backend request output -->
<div id="output" style="margin-top: 10px; padding: 10px; background: #f5f5f5;">
Output will appear here...
</div>
</div>
<script nonce="plugin-script-inject">
// Notyf
const notyf = new Notyf({
duration: 3000,
position: { x: 'right', y: 'top' }
});
function showSuccess() { notyf.success('Operation completed!'); }
function showError() { notyf.error('Something went wrong!'); }
// Highlight.js
hljs.highlightAll();
// ACE Editor
window.waitForElement('#editor', (element) => {
const editor = ace.edit("editor");
editor.setTheme("ace/theme/monokai");
editor.session.setMode("ace/mode/typescript");
editor.setValue("// Start coding here...", 1);
editor.setOptions({ fontSize: "14px", showPrintMargin: false });
});
// Backend request
async function fetchData() {
const output = document.getElementById('output');
try {
const result = await window.createBackendReq('getPluginInfo', { id: 'demo' });
output.innerHTML = JSON.stringify(result, null, 2);
} catch (e) {
output.textContent = 'Backend request failed.';
}
}
// Global event listener
window.addGlobalEventListener('keydown', (event) => {
if (event.key === 'F1') {
event.preventDefault();
notyf.open({ type: 'info', message: 'F1 Help triggered!', background: '#2196f3' });
}
});
</script>
`;
}
}
Always wrap ACE Editor and Split Grid initialization in waitForElement calls. The DOM element must exist before you can pass its ID to ace.edit() or reference it with document.querySelector.