solverforge-ui
Embedded frontend components, scheduling views, backend adapters, and asset serving for SolverForge web applications.
solverforge-ui is SolverForge’s frontend component library for
constraint-optimization applications. It ships embedded assets, UI primitives,
solver lifecycle helpers, and scheduling views without requiring npm in the
runtime integration path.
What It Provides
- Drop-in components for headers, status bars, buttons, modals, tabs,
tables, footers, API guides, and toasts
- Scheduling views with both timeline rail and split-pane Gantt primitives
- Solver lifecycle helpers via
SF.createBackend(...) and
SF.createSolver(...) - Embedded asset serving under
/sf/* via
.merge(solverforge_ui::routes()) - Stable and versioned bundles for compatibility and cache-friendly
production deployments
Installation
[dependencies]
solverforge-ui = "0.3.1"
Minimal Workflow
let app = api::router(state).merge(solverforge_ui::routes()); // serves /sf/*
<link rel="stylesheet" href="/sf/sf.css" />
<script src="/sf/sf.js"></script>
<script>
var tabs = SF.createTabs({
tabs: [{ id: 'plan', content: '<div>Plan view</div>', active: true }],
});
document.body.appendChild(tabs.el);
var header = SF.createHeader({
title: 'SolverForge UI',
tabs: [{ id: 'plan', label: 'Plan', active: true }],
onTabChange: function (id) {
tabs.show(id);
},
});
document.body.prepend(header);
</script>
When To Use It
Use solverforge-ui when you want to ship SolverForge-backed web interfaces
quickly without rebuilding common UI primitives or bundling your own asset
pipeline.
It is a strong fit for:
- operations dashboards and schedule review screens
- solver result exploration and troubleshooting tools
- embedded admin UIs in Axum-, Tauri-, or static-asset-served applications
Sections
- Getting Started — Mount
/sf/*, include the bundled
assets, and wire the verified primitives into an app - Components — Core factories, return values, and unsafe HTML
opt-ins
- Scheduling Views — Timeline rail and Gantt APIs with
shipped examples
- Integration & Assets — Backend adapters, asset
serving, cache behavior, and example route contracts
External References
1 - Getting Started
Mount the bundled assets and wire verified solverforge-ui primitives into an Axum app.
Getting Started with solverforge-ui
This guide covers the verified integration path:
- add the crate
- mount
/sf/* assets with .merge(solverforge_ui::routes()) - include the bundled CSS and JS
- instantiate components plus the backend and solver helpers
Add the Dependency
[dependencies]
axum = "0.8"
solverforge-ui = "0.3.1"
Mount /sf/* Routes in Axum
use axum::{routing::get, Router};
async fn index() -> &'static str {
include_str!("../static/index.html")
}
fn app() -> Router {
Router::new()
.route("/", get(index))
.merge(solverforge_ui::routes())
}
solverforge_ui::routes() serves the embedded /sf/* assets. Your application
still owns its HTML pages and any schedule/solver API routes.
Include Required Assets in HTML
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>solverforge-ui quickstart</title>
<link
rel="stylesheet"
href="/sf/vendor/fontawesome/css/fontawesome.min.css"
/>
<link rel="stylesheet" href="/sf/vendor/fontawesome/css/solid.min.css" />
<link rel="stylesheet" href="/sf/sf.css" />
</head>
<body class="sf-app">
<script src="/sf/sf.js"></script>
<script>
var tabs = SF.createTabs({
tabs: [
{ id: 'plan', content: '<div>Plan view</div>', active: true },
{ id: 'gantt', content: '<div>Gantt view</div>' },
],
});
document.body.appendChild(tabs.el);
var backend = SF.createBackend({ type: 'axum' });
var header = SF.createHeader({
title: 'SolverForge Scheduler',
subtitle: 'by SolverForge',
tabs: [
{ id: 'plan', label: 'Plan', active: true },
{ id: 'gantt', label: 'Gantt' },
],
onTabChange: function (id) {
tabs.show(id);
},
});
document.body.prepend(header);
var statusBar = SF.createStatusBar({ header: header, constraints: [] });
header.after(statusBar.el);
var solver = SF.createSolver({
backend: backend,
statusBar: statusBar,
onUpdate: function (schedule) {
console.log('updated schedule', schedule);
},
});
</script>
</body>
</html>
Application Routes
solverforge-ui does not generate your scheduling API. The crate ships the UI
surface and a set of backend helpers. If you use
SF.createBackend({ type: 'axum' }), follow the default adapter contract
documented in Integration & Assets, or add an
application-side compatibility layer while your routes are still in transition.
Next Steps
2 - Components
Core factories, return values, and helpers in the shipped solverforge-ui surface.
Components
solverforge-ui ships a verified component surface for common scheduling and
operations UIs.
Core Factories
The current public API documents these factories:
| Factory | Returns | Description |
|---|
SF.createHeader(config) | HTMLElement | Sticky header with logo, title, nav tabs, and solve/stop/analyze actions |
SF.createStatusBar(config) | {el, bindHeader, updateScore, setSolving, updateMoves, colorDotsFromAnalysis} | Score display with constraint indicators |
SF.createButton(config) | HTMLButtonElement | Button with variant, size, icon, and shape modifiers |
SF.createModal(config) | {el, body, open, close, setBody} | Dialog with backdrop and header |
SF.createTable(config) | HTMLElement | Data table with headers and row click support |
SF.createTabs(config) | {el, show} | Tab panel container with instance-scoped tab switching |
SF.createFooter(config) | HTMLElement | Footer with links and version |
SF.createApiGuide(config) | HTMLElement | REST API documentation panel |
SF.showToast(config) | void | Auto-dismissing toast notification |
SF.showError(title, detail) | void | Error toast shorthand |
SF.showTab(tabId, root?) | void | Activate tab panels globally or within one root |
Composition Example
var tabs = SF.createTabs({
tabs: [
{ id: 'plan', content: '<div>Plan view</div>', active: true },
{ id: 'gantt', content: '<div>Gantt view</div>' },
],
});
document.body.appendChild(tabs.el);
var header = SF.createHeader({
title: 'My Scheduler',
subtitle: 'by SolverForge',
tabs: [
{ id: 'plan', label: 'Plan', active: true },
{ id: 'gantt', label: 'Gantt' },
],
onTabChange: function (id) {
tabs.show(id);
},
});
document.body.prepend(header);
var statusBar = SF.createStatusBar({ header: header, constraints: [] });
header.after(statusBar.el);
Unsafe HTML APIs
Default content is text-rendered. These opt-ins accept trusted HTML:
| Factory | Unsafe HTML field |
|---|
SF.el(tag, attrs, ...) | unsafeHtml |
SF.createModal(config) | unsafeBody |
SF.createTabs(config) | tabs[].content.unsafeHtml |
SF.createTable(config) | cells[].unsafeHtml |
SF.gantt.create(config) | unsafePopupHtml, columns[].render(task).unsafeHtml |
Escape user-provided content before interpolation. SF.escHtml(...) is the
shipped helper for that.
SF.createButton({ text: 'Solve', variant: 'success' });
SF.createButton({ text: 'Stop', variant: 'danger' });
SF.createButton({ text: 'Save', variant: 'primary' });
SF.createButton({ text: 'Cancel', variant: 'default' });
SF.createButton({ icon: 'fa-gear', variant: 'ghost', circle: true });
Useful Helpers
These helpers are documented in the current public API:
SF.score.parseHard, parseSoft, parseMedium, getComponents,
colorClassSF.colors.pick, project, resetSF.escHtml(...) for safe HTML escapingSF.el(tag, attrs, ...children) for lightweight DOM element creation
3 - Scheduling Views
Build timeline-rail and split-pane Gantt views with the shipped solverforge-ui APIs.
Scheduling Views
solverforge-ui ships two complementary scheduling view styles:
- Timeline rail for compact, lane-by-lane operator workflows
- Gantt for dense, timeline-first planning and diagnostics
Timeline Rail API
The shipped rail surface is built around:
SF.rail.createHeader(config) → HTMLElementSF.rail.createCard(config) → {el, rail, addBlock, clearBlocks, setSolving}SF.rail.addBlock(rail, config) → HTMLElementSF.rail.addChangeover(rail, config) → HTMLElement
Rail Example
var header = SF.rail.createHeader({
label: 'Resource',
labelWidth: 200,
columns: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
});
container.appendChild(header);
var card = SF.rail.createCard({
id: 'furnace-1',
name: 'FORNO 1',
labelWidth: 200,
columns: 5,
type: 'CAMERA',
typeStyle: {
bg: 'rgba(59,130,246,0.15)',
color: '#1d4ed8',
border: '1px solid rgba(59,130,246,0.3)',
},
badges: ['TEMPRA'],
gauges: [{ label: 'Temp', pct: 85, style: 'heat', text: '850/1000°C' }],
stats: [{ label: 'Jobs', value: 12 }],
});
container.appendChild(card.el);
card.addBlock({
start: 120,
end: 360,
horizon: 4800,
label: 'ODL-2847',
meta: 'Bianchi',
color: 'rgba(59,130,246,0.6)',
borderColor: '#3b82f6',
late: false,
});
SF.rail.addChangeover(card.rail, { start: 360, end: 400, horizon: 4800 });
card.setSolving(true);
Gauge styles include heat, load, and emerald. badges accepts either
plain strings or { label, style } objects.
Gantt API
SF.gantt.create(config) returns:
el, mount, setTasks, refresh, changeViewMode, highlightTask,
destroy
Gantt Example
var gantt = SF.gantt.create({
gridTitle: 'Tasks',
chartTitle: 'Schedule',
viewMode: 'Quarter Day',
splitSizes: [40, 60],
columns: [
{ key: 'name', label: 'Task', sortable: true },
{ key: 'start', label: 'Start', sortable: true },
{ key: 'end', label: 'End', sortable: true },
],
onTaskClick: function (task) {
console.log('clicked', task.id);
},
onDateChange: function (task, start, end) {
console.log('moved', task.id, start, end);
},
});
gantt.mount('my-container');
gantt.setTasks([
{
id: 'task-1',
name: 'Design review',
start: '2026-03-15 09:00',
end: '2026-03-15 10:30',
priority: 1,
projectIndex: 0,
pinned: true,
custom_class: 'project-color-0 priority-1',
dependencies: '',
},
]);
gantt.changeViewMode('Day');
gantt.highlightTask('task-1');
View modes include Quarter Day, Half Day, Day, Week, and Month.
Sortable headers are opt-in per column.
Choosing Rail vs Gantt
Use rail when operators need readable resource cards, gauges, and
changeover-aware lanes.
Use Gantt when analysts need sortable task grids, dependency arrows, and
high-density timeline review.
4 - Integration & Assets
Backend adapters, asset serving, cache behavior, and example route contracts for solverforge-ui.
Integration & Assets
This page summarizes how solverforge-ui connects frontend code to backend APIs
and how static assets are delivered.
Backend Adapters
Create adapters with SF.createBackend(...) and pass the result into
SF.createSolver(...).
Axum (default)
var backend = SF.createBackend({ type: 'axum', baseUrl: '' });
Use this when your backend exposes the default solverforge-ui scheduling
contract.
Tauri Adapter
var backend = SF.createBackend({
type: 'tauri',
invoke: window.__TAURI__.core.invoke,
listen: window.__TAURI__.event.listen,
eventName: 'solver-update',
});
Use this when solver traffic is bridged through Tauri IPC.
Generic Fetch Adapter
var backend = SF.createBackend({
type: 'fetch',
baseUrl: '/api/v1',
headers: { 'X-CSRF-Token': csrfToken },
});
Use this when your app needs a custom HTTP shape, extra headers, or a
compatibility layer during a route transition.
Default Axum Adapter Contract
The default Axum adapter expects these routes:
POST /schedulesGET /schedules/{id}GET /schedules/{id}/eventsGET /schedules/{id}/analyzeDELETE /schedules/{id}GET /demo-data/{name}
The adapter also expects createSchedule() to resolve to either:
- a plain schedule/job id string, or
- an object containing one of
id, jobId, job_id, scheduleId, or
schedule_id
If your current app still uses a legacy quickstart route shape, add an
application-side compatibility layer or use the generic fetch adapter until
the routes converge.
Solver Lifecycle
SF.createSolver(...) builds the client-side solver state machine on top of the
backend adapter.
var solver = SF.createSolver({
backend: backend,
statusBar: statusBar,
onUpdate: function (schedule) {
render(schedule);
},
});
The shipped solver helper exposes start, stop, isRunning, and getJobId.
Asset Serving Under /sf/*
solverforge_ui::routes() serves GET /sf/{*path} from the crate’s embedded
asset directory.
Common assets include:
/sf/sf.css/sf/sf.js/sf/vendor/fontawesome/css/fontawesome.min.css/sf/vendor/fontawesome/css/solid.min.css
Cache Behavior and Versioned Bundles
The crate emits both stable and versioned bundle filenames:
- stable:
/sf/sf.css, /sf/sf.js - versioned:
/sf/sf.<crate-version>.css, /sf/sf.<crate-version>.js
src/lib.rs serves them with different cache policies:
- stable bundles use
Cache-Control: public, max-age=3600 - versioned bundles use
Cache-Control: public, max-age=31536000, immutable fonts/, vendor/, and img/ assets are also served as immutable
A practical strategy is to use stable URLs during development and versioned
bundles in production or CDN environments.
Optional Modules
When the optional map module is shipped in static/sf/modules/, include it
alongside Leaflet:
<link rel="stylesheet" href="/sf/vendor/leaflet/leaflet.css" />
<script src="/sf/vendor/leaflet/leaflet.js"></script>
<link rel="stylesheet" href="/sf/modules/sf-map.css" />
<script src="/sf/modules/sf-map.js"></script>
For route geometry, travel-time, and map-data pipeline details, see the existing
solverforge-maps docs.
Non-Rust Integration Path
static/sf/ is self-contained. If you are not serving assets from Rust, copy,
submodule, or symlink it into your static-files directory so /sf/* resolves
the same way in production.