PrismaUI_F4 2.2.0 Examples
These examples target the current PrismaUI_F4 2.2.0 public API and the in-process Ultralight 1.4.0 backend.
They intentionally avoid the retired legacy-runtime host, remote-CDN dependencies, and unsupported Next-Gen runtime claims.
1. Minimal cooperative panel
#include "PrismaUI_F4_API.h"
static PRISMA_UI_API::IVPrismaUI10* g_api = nullptr;
static PrismaView g_view = 0;
static void ClosePanel()
{
if (!g_api || !g_api->IsValid(g_view)) return;
g_api->Unfocus(g_view);
g_api->Hide(g_view);
}
static void OnCloseRequested(const char*)
{
ClosePanel();
}
static void OnDomReady(PrismaView view)
{
g_api->RegisterJSListener(view, "requestClose", OnCloseRequested);
}
static void CreatePanel()
{
if (!g_api || (g_view && g_api->IsValid(g_view))) return;
g_view = g_api->CreateView("MyPlugin/panel.html", OnDomReady);
if (!g_view) return;
g_api->SetViewRole(g_view, PRISMA_UI_API::ViewRole::kPanel);
g_api->Hide(g_view);
}
static void OpenPanel()
{
if (!g_api || !g_api->IsValid(g_view)) return;
if (g_api->IsAnyPanelVisible(g_view)) return;
g_api->Show(g_view);
g_api->Focus(g_view, true, false);
}
<button id="close" type="button">Close</button>
<script>
document.querySelector('#close').addEventListener('click', () => {
window.requestClose();
});
</script>
2. Push structured game data to JavaScript
std::string json = R"({"hp":210,"ap":75,"name":"Sole Survivor"})";
g_api->InteropCall(g_view, "setPlayerState", json.c_str());
window.setPlayerState = function (json) {
const state = JSON.parse(json);
document.querySelector('#hp').textContent = String(state.hp);
document.querySelector('#ap').textContent = String(state.ap);
};
InteropCall is the normal choice when calling a named function. Use Invoke for one-off evaluated JavaScript.
3. Receive events from JavaScript
static void OnItemSelected(const char* value)
{
const uint32_t formId = value ? std::strtoul(value, nullptr, 16) : 0;
if (auto* form = RE::TESForm::GetFormByID(formId)) {
logger::info("selected {}", form->GetFullName());
}
}
static void OnDomReady(PrismaView view)
{
g_api->RegisterJSListener(view, "selectItem", OnItemSelected);
}
document.querySelectorAll('[data-formid]').forEach((row) => {
row.addEventListener('click', () => window.selectItem(row.dataset.formid));
});
Prisma-delivered listener callbacks are marshalled through the verified framework callback path.
4. Capture JavaScript console output
static void OnConsole(
PrismaView view,
PRISMA_UI_API::ConsoleMessageLevel level,
const char* message)
{
logger::info("[Prisma JS][{}][{}] {}",
view,
static_cast<int>(level),
message ? message : "");
}
// After the view exists:
g_api->RegisterConsoleCallback(g_view, OnConsole);
Current builds also provide the packaged F12 Ultralight inspector when DevTools are enabled. Keep console callbacks and framework logs in the debugging path.
5. Translations
RegisterTranslations is the legacy runtime JavaScript-injection path. Call it from DOM-ready before invoking page code that needs window.t.
static void OnDomReady(PrismaView view)
{
g_api->RegisterTranslations(view, "MyPlugin_F4");
g_api->Invoke(view, "window.onTranslationsReady && window.onTranslationsReady()");
}
g_view = g_api->CreateView("MyPlugin/panel.html", OnDomReady);
Translation file:
Data/Interface/Translations/MyPlugin_F4_en.txt
Page:
window.onTranslationsReady = function () {
const t = (key) => window.t?.(key) ?? key;
document.querySelector('#title').textContent = t('$UI_TITLE');
};
Do not assume window.t exists in module-level scripts that execute before DOM-ready.
6. Passive HUD plus interactive panel
static PrismaView g_hud = 0;
static PrismaView g_menu = 0;
void CreateViews()
{
g_hud = g_api->CreateView("MyPlugin/hud.html", nullptr);
g_api->SetViewRole(g_hud, PRISMA_UI_API::ViewRole::kWidget);
g_api->SetOrder(g_hud, 0);
g_menu = g_api->CreateView("MyPlugin/menu.html", OnDomReady);
g_api->SetViewRole(g_menu, PRISMA_UI_API::ViewRole::kPanel);
g_api->SetOrder(g_menu, 10);
g_api->Hide(g_menu);
}
The HUD does not make IsAnyPanelVisible() permanently true because it is declared kWidget.
7. Selective V10 overlay input
const bool hasRegions = PRISMA_UI_API::HasPrismaCapability(
PRISMA_UI_API::PrismaCapability::InputRegions);
if (hasRegions) {
const PRISMA_UI_API::InputRegion regions[] = {
{ 40, 40, 400, 240 },
{ 500, 40, 120, 48 },
};
if (g_api->SetInputRegions(g_view, regions, 2)) {
g_api->Show(g_view);
g_api->FocusOverlay(g_view, false, false);
}
}
Clear the mask with:
g_api->SetInputRegions(g_view, nullptr, 0);
8. Native networking -> Prisma view
Do required networking in native code, not in the page:
void OnNetworkMessage(std::string payload)
{
F4SE::GetTaskInterface()->AddTask([payload = std::move(payload)] {
if (!g_api || !g_api->IsValid(g_view)) return;
g_api->InteropCall(g_view, "onNetworkMessage", payload.c_str());
});
}
window.onNetworkMessage = function (json) {
const message = JSON.parse(json);
renderMessage(message);
};
This is the supported pattern for HTTP/WebSocket/server-push features.
9. ModelPreview v4
The released bridge is window.__prismaUI_modelPreview. It takes NIF paths, not FormIDs.
const preview = window.__prismaUI_modelPreview;
if (preview?.version >= 4) {
preview.onStatus((status) => {
if (!status.ok) {
console.warn('preview failed', status.error);
}
});
preview.show({
id: 'selection',
nifPath: 'Meshes/MyMod/item.nif',
formType: 'MISC',
x: 600,
y: 120,
w: 320,
h: 320,
zoom: 1.0
});
}
When the panel closes:
preview?.hide({ id: 'selection' });
See Model Preview for clipping, multi-NIF requests, pose fields, status phases, and performance rules.
10. View health diagnostics
using VH = PRISMA_UI_API::ViewHealth;
if (g_api && g_api->IsValid(g_view)) {
switch (g_api->GetViewHealth(g_view)) {
case VH::kDomReady:
case VH::kLive:
break;
case VH::kLoadFailed:
case VH::kDomReadyTimeout:
logger::error("Prisma view failed to load");
break;
case VH::kJsError:
logger::warn("Prisma view reported a JS error");
break;
default:
break;
}
}
Use PrismaUI_F4.log and RegisterConsoleCallback for the actual failure detail.
11. Vanilla menu replacement
g_api->SuppressVanillaMenu("ContainerMenu", true);
g_api->CloseVanillaMenu("ContainerMenu");
if (!g_api->IsAnyPanelVisible(g_view)) {
g_api->Show(g_view);
g_api->Focus(g_view, true, false);
}
When your replacement is disabled:
g_api->Unfocus(g_view);
g_api->Hide(g_view);
g_api->SuppressVanillaMenu("ContainerMenu", false);
The public API has no IsMenuSuppressed method. Track your own requested state if needed.
12. Runtime-safe HUD suppression
if (!g_api->SuppressHUDWidget("HUDCompass", true)) {
logger::warn("HUDCompass suppression not available on this runtime/configuration");
}
The current implementation has guarded support on OG 1.10.163 and AE 1.11.137+ where the required Address Library authority resolves.
13. Do not use these old examples
Do not copy older examples that tell you to:
- create or toggle Chrome/legacy-runtime DevTools with
CreateInspectorView; - install
retired host componentor aretired runtime directory/tree; - use
retired runtime library; - assume a separate legacy-runtime renderer process isolates JavaScript performance from the game;
- load required Google Fonts/jsDelivr resources remotely;
- support Fallout
1.10.980-1.10.984just because CommonLib can compile a slot for it; - use
SuppressActivateChoicePerkas if it currently filters choices; - use
dropDefaultTake=trueas if it currently removes the default Take row; - use
window.prismaModelPreview/window.onModelPreviewStatusor a FormID-based ModelPreview request; - assume
RegisterTranslationsmakeswindow.tavailable before DOM-ready.
Those are stale assumptions, not the current PrismaUI_F4 contract.