-
Notifications
You must be signed in to change notification settings - Fork 0
JavaScript API
Mount a widget into an element on your own page. Use this when the widget has to sit in your DOM: when your page controls it, when you need several instances, or when an iframe is in the way. If you just want a picture on a page, Embedding with iframe is less work.
Each widget is one self-contained ES module. It has no runtime dependencies, ships no CSS file, and injects the few styles it needs into document.head when it mounts.
<div id="tree" style="position: relative; width: 100%; height: 420px;"></div>
<script type="module">
import { mountAdvancements } from "https://mcwidgets.kaban.sh/advancements/viewer.js";
await mountAdvancements(document.getElementById("tree"), {
bundleUrl: "https://example.com/mc/advancements/bundle.json",
});
</script><div id="craft" style="position: relative; width: 100%; height: 220px;"></div>
<script type="module">
import { mountItemGui } from "https://mcwidgets.kaban.sh/itemgui/itemgui.js";
await mountItemGui(document.getElementById("craft"), {
bundleUrl: "https://mcwidgets.kaban.sh/bundles/itemgui-demo/bundle.json",
gui: "crafting_table",
items: {
crafting1: "diamond",
crafting2: "diamond",
crafting3: "diamond",
crafting5: "stick",
crafting8: "stick",
result: "diamond_sword",
},
});
</script>items also accepts the string form used by the items query parameter, so "crafting1=diamond,result=diamond_sword" is equivalent for those two slots.
Give the container an explicit width and height. The widget fills it and picks the largest whole-number GUI scale, 1 to 4, that fits. It never draws at a fractional scale.
Give the container position: relative as well. Errors are drawn as an absolutely positioned panel that replaces the container's children, and without a positioned ancestor that panel escapes to the nearest one.
Resizing is handled for you. The widget observes the container and re-lays out, so a container sized in percentages or with flexbox works.
mountAdvancements and mountItemGui return a promise that resolves once the bundle is loaded and the first frame is on screen. The resolved value is the widget instance.
On failure they do both things: they draw an error panel into the container, and they reject. Catch the rejection if you want to react in code, but you do not have to render anything yourself.
try {
const widget = await mountAdvancements(el, { bundleUrl });
} catch (err) {
console.error("advancement embed failed", err);
}The panel names the failing URL and lists what to check. Troubleshooting covers what each message means.
Call destroy() on the instance. It removes the widget's DOM and detaches its resize and input listeners. Always call it before you remove the container, or the observers outlive the element.
const widget = await mountItemGui(el, options);
// later
widget.destroy();In a component framework, mount in the effect that runs after the element exists and destroy in its cleanup:
useEffect(() => {
let widget;
let cancelled = false;
mountAdvancements(ref.current, options).then((w) => {
if (cancelled) w.destroy();
else widget = w;
});
return () => {
cancelled = true;
widget?.destroy();
};
}, []);The cancelled flag matters because mounting is asynchronous. A component that unmounts during the fetch would otherwise leave a live widget behind.
The module is served with Access-Control-Allow-Origin: *, so importing it from another origin works.
Your bundle.json is fetched from the page's origin, because that is where the module runs. Same-origin bundles need no headers. A bundle on another host must send Access-Control-Allow-Origin. Icons and textures load as images and need no header either way.
The widget resolves its textures and its font relative to its own module URL, so importing from https://mcwidgets.kaban.sh/advancements/viewer.js finds them at https://mcwidgets.kaban.sh/advancements/assets/. Nothing to configure.
Serve the widget yourself and the same rule applies to your copy:
import { mountAdvancements } from "/vendor/mcwidgets/advancements/viewer.js";That looks for /vendor/mcwidgets/advancements/assets/. Keep the module and its assets/ directory together and it is correct. Self-hosting produces exactly that layout.
One case breaks the rule. If you run the widget through your own bundler, the module ends up inside your build output and its assets do not follow. Set assetBase to wherever you copied them:
await mountAdvancements(el, {
bundleUrl: "/mc/advancements/bundle.json",
assetBase: "/static/mcwidgets/advancements/assets/",
});Embedding
Reference
Building bundles
Running it yourself