Embeds the original DOOM (1993) shareware as a playable Mendix widget via js-dos DOSBox emulator. Built with Gleam + glendix bindings, without JSX.
gleam run -m glendix/install # Install dependencies (Gleam deps + npm)
gleam run -m glendix/build # Production build (.mpk output)
gleam run -m glendix/dev # Dev server (HMR, port 3000)
gleam run -m glendix/start # Link with Mendix test project
gleam run -m glendix/release # Release build
gleam run -m glendix/lint # Run ESLint
gleam run -m glendix/lint_fix # ESLint auto-fix
gleam run -m glendix/marketplace # Search/download Marketplace widgets
gleam run -m glendix/define # Widget property definition TUI editor
gleam test # Run tests
gleam format # Format codeIMPORTANT: Breaking these rules will break the build or compromise the architecture.
- Do not write JSX/JS files directly. All widget logic and UI must be written in Gleam
- Do not write FFI files (.mjs) in the widget project. React/Mendix FFI is provided by the glendix package
- Do not manually manage bridge JS files (src/*.js). glendix auto-generates/deletes them at build time
- Use
redraw/redraw_domfor React bindings. glendix v3.0 delegates React elements, hooks, and events to theredrawecosystem. Do NOT use the removedglendix/reactmodule - Do not use
lustreelement types for the widget return value. The widget must returnredraw.Element. Useglendix/lustrebridge functions (use_tea,use_simple,render) to convert lustre elements - Do not add
react/react-domtodependencies.pluggable-widgets-toolsprovides them. Useoverrides/resolutionswith exact versions (no caret) to pin React 19 - Editor config must not use Gleam Lists. Studio Pro runs via Jint (.NET JS engine) which crashes on Gleam List literals. Use comma-separated Strings instead
- The Gleam compilation output path (
build/dev/javascript/{gleam.toml name}/) must match the Rollup input path - Mendix widget names allow only alphabetic characters (a-zA-Z)
- Format with
gleam format - Use English comments
- Do not manually edit compiled JS output (
build/)
Widget entry point signature: pub fn widget(props: JsProps) -> Element — identical to a React functional component. Element is from redraw.
v3.0 dependency structure:
User code
├── redraw ← React hooks, components, fragment
├── redraw_dom ← HTML/SVG tags, attributes, events
├── lustre ← TEA update/view (optional)
└── glendix
├── mendix ← Mendix API types + props access
├── interop ← External JS components → redraw.Element
├── lustre ← Lustre Element → redraw.Element bridge
├── widget ← .mpk widget components
├── binding ← bindings.json external React components
├── classic ← Classic (Dojo) widgets
└── js/* ← JS interop escape hatch
Two rendering paths (both return redraw.Element, freely composable):
| Path | State | View | Best for |
|---|---|---|---|
| redraw (direct React) | redraw.use_state, redraw.use_reducer |
redraw/dom/html |
Simple UI, Mendix value display/edit |
| lustre (TEA bridge) | update function (pure) |
lustre/element/html |
Complex state machines, TEA pattern |
Project structure:
src/mendix_doom.gleam— Main widget entry point (callsgame.render())src/editor_config.gleam— Studio Pro property panel configurationsrc/editor_preview.gleam— Studio Pro design view preview (static DOOM placeholder)src/components/game.gleam— Core DOOM game component (js-dos integration, DOSBox emulation)src/Doom.xml— Widget property definitions. Adding<property>triggers automatic type generation by the build toolsrc/package.xml— Mendix package manifestsrc/assets/doom.jsdos— Embedded DOOM shareware bundle for js-dossrc/ui/Doom.css— Widget stylingrollup.config.mjs— Custom Rollup config (copies doom.jsdos asset to build output)widgets/— .mpk widget file bindings (used viaglendix/widget)
src/*.gleam → gleam build → build/dev/javascript/**/*.mjs → Bridge JS (auto-generated) → Rollup → dist/**/*.mpk
Rollup also copies src/assets/doom.jsdos into the output so the DOOM bundle is included in the .mpk.
- Widget ID:
ggobp.doom.Doom packagePath: "ggobp"inpackage.jsondetermines the deployment pathneedsEntityContext="false"→ Does not require Mendix data contextofflineCapable="false"→ Requires network (js-dos CDN).mpkoutput:dist/directory- Test project:
./tests/testProject - DOOM bundle served at:
/widgets/ggobp/doom/doom.jsdos
- Mendix props (
JsProps) are accessed viamendix.get_prop/mendix.get_string_prop/mendix.get_prop_requiredetc. - Mendix complex types (
EditableValue,ActionValue,ListValue) are opaque types with FFI accessors - JS
undefined↔ GleamOptionconversion is handled automatically at the FFI boundary - HTML attributes use the Attribute list API:
[attribute.class("x"), event.on_click(handler)] - Gleam tuples
#(a, b)= JS[a, b]— directly compatible withuseStatereturn values - js-dos v8 is loaded dynamically from CDN (
https://v8.js-dos.com/latest/)
For detailed glendix API and Gleam syntax, see:
- docs/glendix_guide.md — Complete glendix v3.0 guide (redraw/lustre rendering, Mendix API, external components, editor config, JS interop, practical patterns, troubleshooting)
- docs/gleam_language_tour.md — Gleam syntax reference (types, pattern matching, FFI, use keyword, etc.)
docs.mendix.com is not accessible. Use GitHub raw sources:
- Pluggable Widgets API:
https://github.com/mendix/docs/blob/development/content/en/docs/apidocs-mxsdk/apidocs/pluggable-widgets/ - Build tools source:
https://github.com/mendix/widgets-tools - Official widget examples:
https://github.com/mendix/web-widgets