@emdzej/inpax-web-provider
v0.7.1
Published
Svelte 5 UI provider + components for embedding the INPA runtime in a browser app — canvas renderer, F-key bar, dialogs, viewer modal, scriptselect picker.
Downloads
1,343
Readme
@emdzej/inpax-web-provider
Svelte 5 UI provider, browser primitives, and reusable components for
embedding the INPA runtime in a web app. Pairs with
@emdzej/inpax-interpreter, @emdzej/inpax-dispatcher,
@emdzej/inpax-ediabasx-provider, and a transport of your choice
(Web Serial via @emdzej/ediabasx-interface-serial, or a remote
WebSocket gateway via @emdzej/ediabasx-interfaces/client).
What's in the box:
UI provider
WebUIProvider— concreteUIProvidersubclass for the browser.
Components
ScreenCanvas— canvas-based renderer for the INPA screen buffer (cell grid + analog gauges + digital LEDs + sized text overlays).FKeyBar— F1–F10 input bar with native + digit-fallback key routing.MenuTitleBar— menu title display.DialogOverlay— text / number / hex / digital / connect input modals.UserBoxOverlay—userboxopcode overlay.ViewerDialog—viewopenmodal, backed byBrowserExternalProvider.ScriptSelectDialog—scriptselectpicker. Takes aloaderprop so the dialog stays host-agnostic; pair withloadScriptSelectfor the common CFGDAT-directory case.TogglelistDialog— INPA'stogglelistmulti-select picker ("Please select the objects to be controlled"). Items come from the active SCREEN's "empty" LineFunc declarations (the dispatcher harvests them viaScreenExecutor.getToggleItems()and hands them toui.togglelist). On OK the dialog encodes the picks per INPA's wire convention — bitwise-OR of the items' 9-byte masks ("0xNN;0xNN;…", default), or space-separated 1-based indices whenArgNumFlagis set. Honours bothMultipleSelectFlag(radio vs checkbox) andArgNumFlag(mask vs indices).LiveIndicator— green pulsing dot for cyclic screens (thesetscreen(handle, true)case — "this screen is on a refresh cycle").EdiabasBusyIndicator— amber pulsing dot that lights up whenever the EDIABAS bridge has anyINPAapi*async call in flight (init / end / job / fsLesen / fsLesen2). Subscribes to thebusy:changedevent onIEdiabasProvider. Complementary toLiveIndicator: green = screen-level refresh, amber = per-call ECU I/O. Both can be lit simultaneously.ScrollIndicator— pagination hint when a SCREEN has more LINE blocks than the viewport.
Browser primitives
These wrap the File System Access API (or OPFS — same handle shape) so any inpax-style web app gets the same INPA-aware behaviour without re-implementing the walks.
discoverInpaInstall(root)— walks the canonical INPA layout (EC-APPS/INPA/CFGDAT,SGDAT,EDIABAS/Ecu,EDIABAS/Bin) case-insensitively, returns the four subdirectory handles.isCompleteInstall(install)— true when CFGDAT / SGDAT / Ecu are all resolved.isFileSystemAccessSupported()— feature gate for the picker UI (Chromium-only as of 2026).listIpoFiles(dir, origin)— enumerate.ipoentries in a directory handle, sorted, with the origin label preserved.makeBrowserSgbdResolver(ecuDir)— build theloadSgbdResolvercallback Ediabas uses for both initial loads and the post-IDENT.grp → .prgvariant swap.BrowserNativeImportProvider— concreteINativeImportProviderfor browser hosts. Wires INPA's CALLE imports (kernel32INI / system / strings,api32 __apiGetConfig, …) to the picked directory handle. CallprefetchIniFiles()once during runtime setup so the synchronous CALLE dispatcher hits cache.BrowserExternalProvider— concreteIExternalProviderforviewopen/viewclose. Pairs withViewerDialog.
Scriptselect
parseScriptSelect(content)— pure parser for.ENG/.GER/.CPScatalogue files into a navigable tree.loadScriptSelect(cfgdat, filename)— case-insensitive lookup- read via a
FileSystemDirectoryHandle. Drop into<ScriptSelectDialog loader={...}>directly.
- read via a
Theme
Components read their colour palette from a Svelte context. Set it once at the root of your app:
<script>
import { setLibTheme, classicInpaTheme, darkInpaTheme } from "@emdzej/inpax-web-provider";
$effect(() => {
setLibTheme(isDark ? darkInpaTheme : classicInpaTheme);
});
</script>If no context is set, components fall back to classicInpaTheme.
Paint coalescing
ScreenCanvas accepts an onFrameReady prop — a subscribe function
that takes a callback and returns an unsubscribe. Wire it to the
runtime's cycle:complete event so paints fire on full SCREEN cycle
boundaries instead of per-cell writes. Without it the canvas falls
back to a free requestAnimationFrame loop.
Putting it together
A minimal embedding looks roughly like this (omitting cable transport
setup for brevity — that lives in @emdzej/ediabasx-interface-serial
or @emdzej/ediabasx-interfaces/client):
<script lang="ts">
import {
WebUIProvider,
BrowserExternalProvider,
BrowserNativeImportProvider,
discoverInpaInstall,
listIpoFiles,
makeBrowserSgbdResolver,
loadScriptSelect,
setLibTheme,
classicInpaTheme,
ScreenCanvas,
FKeyBar,
DialogOverlay,
ViewerDialog,
ScriptSelectDialog,
} from "@emdzej/inpax-web-provider";
// 1. Install discovery
const root = await showDirectoryPicker();
const install = await discoverInpaInstall(root);
// 2. Theme
setLibTheme(classicInpaTheme);
// 3. Providers
const ui = new WebUIProvider();
const external = new BrowserExternalProvider();
const native = new BrowserNativeImportProvider({ install });
await native.prefetchIniFiles();
// 4. Ediabas + runtime — see @emdzej/inpax-interpreter docs for the rest
// Pass `makeBrowserSgbdResolver(install.ecu)` as `loadSgbdResolver`.
</script>
<ScreenCanvas screen={ui.screenBuffer} {ui} onFrameReady={runtime.onFrame} />
<FKeyBar {ui} />
<DialogOverlay {ui} />
<ViewerDialog {external} />
<ScriptSelectDialog
{ui}
loader={(name) => loadScriptSelect(install.cfgdat!, name)}
/>Why a Svelte 5 source package
The package ships its .svelte and .ts files directly — no build
step. Consumer apps already use Vite with @sveltejs/vite-plugin-svelte
and compile per-app, so a separate compile step here would duplicate
work and produce identical output. Add @sveltejs/package later if
you need to publish standalone artifacts to npm.
Consumer tsconfig + Vite setup
Three settings the host app's tsconfig / vite config / tailwind config needs because of how the source is shipped:
tsconfig.json:verbatimModuleSyntax: false. Svelte's.svelte.tsmodule parser rejects thetypekeyword in import specifiers — TS elides type-only imports automatically when this flag is off.vite.config.ts: don't add this package tooptimizeDeps.include. Vite's pre-bundling step uses esbuild which doesn't run the Svelte plugin's TS preprocessor;.svelte.tsfiles fail to parse there. Leaving it out routes the package through the main transformation pipeline (which does include the plugin).tailwind.config.ts: scan the library's source. Tailwind's JIT only emits classes it finds in thecontentglob. Add"<path-to>/packages/web-provider/src/**/*.{ts,svelte}"to the glob so utilities used inside library components end up in the CSS bundle. (Without it, e.g.FKeyBarstacks vertically becauseflexnever made it in.)
