/* Generated by tools/build-webui.mjs for Garage UI app. Do not edit directly. */ (function () { const runtime = window.ForgeWebUI; const GarageApp = (window.GarageApp = window.GarageApp || {}); GarageApp.runtime = runtime; window.AppRuntime = runtime; })(); (function () { const GarageApp = (window.GarageApp = window.GarageApp || {}); const defaultSession = { garageName: "Vehicle Garage", capacityUsed: 0, capacityMax: 5, nearbyCount: 0, spawnBlocked: false, spawnStatus: "Ready", }; const defaultGarage = { vehicles: [], }; const defaultNearby = { vehicles: [], }; function cloneValue(value) { return JSON.parse(JSON.stringify(value)); } function replaceObject(target, source) { Object.keys(target).forEach((key) => delete target[key]); Object.assign(target, cloneValue(source)); } GarageApp.data = { categories: [ { id: "all", label: "All" }, { id: "car", label: "Cars" }, { id: "armor", label: "Armor" }, { id: "air", label: "Air" }, { id: "naval", label: "Naval" }, { id: "other", label: "Other" }, ], session: Object.assign({}, defaultSession), garage: Object.assign({}, defaultGarage), nearby: Object.assign({}, defaultNearby), applyHydratePayload(payload) { replaceObject( this.session, Object.assign({}, defaultSession, payload?.session || {}), ); replaceObject( this.garage, Object.assign({}, defaultGarage, payload?.garage || {}), ); replaceObject( this.nearby, Object.assign({}, defaultNearby, payload?.nearby || {}), ); }, }; })(); (function () { const GarageApp = (window.GarageApp = window.GarageApp || {}); const { createSignal } = GarageApp.runtime; class GarageStore { constructor() { [this.getSelectedKind, this.setSelectedKind] = createSignal(""); [this.getSelectedId, this.setSelectedId] = createSignal(""); [this.getSearchQuery, this.setSearchQuery] = createSignal(""); [this.getCategoryFilter, this.setCategoryFilter] = createSignal("all"); [this.getPendingAction, this.setPendingAction] = createSignal(""); [this.getNotice, this.setNotice] = createSignal({ type: "", text: "", }); } getSelection() { return { id: this.getSelectedId(), kind: this.getSelectedKind(), }; } clearSelection() { this.setSelectedKind(""); this.setSelectedId(""); } select(kind, id) { this.setSelectedKind(String(kind || "")); this.setSelectedId(String(id || "")); } startAction(action) { this.setPendingAction(String(action || "")); } finishAction() { this.setPendingAction(""); } matchesSelection(entry) { if (!entry || typeof entry !== "object") { return false; } const selection = this.getSelection(); if (!selection.kind || !selection.id) { return false; } if (selection.kind === "stored") { return ( entry.entryKind === "stored" && String(entry.plate || "") === selection.id ); } if (selection.kind === "nearby") { return ( entry.entryKind === "nearby" && String(entry.netId || "") === selection.id ); } return false; } ensureSelection() { const garageVehicles = Array.isArray( GarageApp.data?.garage?.vehicles, ) ? GarageApp.data.garage.vehicles : []; const nearbyVehicles = Array.isArray( GarageApp.data?.nearby?.vehicles, ) ? GarageApp.data.nearby.vehicles : []; const hasCurrentSelection = [ ...garageVehicles, ...nearbyVehicles, ].some((entry) => this.matchesSelection(entry)); if (hasCurrentSelection) { return; } const firstStored = garageVehicles[0] || null; if (firstStored) { this.select("stored", firstStored.plate || ""); return; } const firstNearby = nearbyVehicles[0] || null; if (firstNearby) { this.select("nearby", firstNearby.netId || ""); return; } this.clearSelection(); } hydrateFromPayload() { this.finishAction(); this.ensureSelection(); } } GarageApp.store = new GarageStore(); })(); (function () { const GarageApp = (window.GarageApp = window.GarageApp || {}); const store = GarageApp.store; const bridge = window.ForgeWebUI.createBridge({ closeEvent: "garage::close", globalName: "ForgeBridge", readyEvent: "garage::ready", }); function requestClose() { return bridge.close({}); } function requestRefresh() { return bridge.send("garage::refresh", {}); } function requestRetrieve(payload) { return bridge.send("garage::vehicle::retrieve::request", payload); } function requestStore(payload) { return bridge.send("garage::vehicle::store::request", payload); } function notifyReady() { return bridge.ready({ loaded: true }); } function hydrate(payloadData) { GarageApp.data.applyHydratePayload(payloadData); store.hydrateFromPayload(payloadData); } bridge.on("garage::hydrate", hydrate); bridge.on("garage::sync", hydrate); bridge.on("garage::retrieve::success", (payloadData) => { store.finishAction(); if (GarageApp.actions) { GarageApp.actions.showNotice( "success", payloadData.message || "Vehicle retrieved from the garage.", ); } }); bridge.on("garage::retrieve::failure", (payloadData) => { store.finishAction(); if (GarageApp.actions) { GarageApp.actions.showNotice( "error", payloadData.message || "Unable to retrieve vehicle.", ); } }); bridge.on("garage::store::success", (payloadData) => { store.finishAction(); if (GarageApp.actions) { GarageApp.actions.showNotice( "success", payloadData.message || "Vehicle stored in the garage.", ); } }); bridge.on("garage::store::failure", (payloadData) => { store.finishAction(); if (GarageApp.actions) { GarageApp.actions.showNotice( "error", payloadData.message || "Unable to store vehicle.", ); } }); GarageApp.bridge = { notifyReady, receive: bridge.receive, requestClose, requestRefresh, requestRetrieve, requestStore, sendEvent: bridge.send, }; })(); (function () { const GarageApp = (window.GarageApp = window.GarageApp || {}); const store = GarageApp.store; let noticeTimer = null; function getStoredVehicles() { return Array.isArray(GarageApp.data?.garage?.vehicles) ? GarageApp.data.garage.vehicles : []; } function getNearbyVehicles() { return Array.isArray(GarageApp.data?.nearby?.vehicles) ? GarageApp.data.nearby.vehicles : []; } function getSelectedEntry() { const selection = store.getSelection(); if (selection.kind === "stored") { return ( getStoredVehicles().find( (vehicle) => String(vehicle.plate || "") === selection.id, ) || null ); } if (selection.kind === "nearby") { return ( getNearbyVehicles().find( (vehicle) => String(vehicle.netId || "") === selection.id, ) || null ); } return null; } function showNotice(type, text) { store.setNotice({ type, text }); if (noticeTimer) { clearTimeout(noticeTimer); } noticeTimer = setTimeout(() => { store.setNotice({ type: "", text: "" }); noticeTimer = null; }, 3200); } function closeGarage() { const bridge = GarageApp.bridge; if (bridge && typeof bridge.requestClose === "function") { const sent = bridge.requestClose(); if (sent) { return true; } } showNotice("error", "Garage bridge is unavailable."); return false; } function refreshGarage() { const bridge = GarageApp.bridge; if (bridge && typeof bridge.requestRefresh === "function") { const sent = bridge.requestRefresh(); if (sent) { return true; } } showNotice("error", "Garage refresh bridge is unavailable."); return false; } function applySearchQuery(value) { store.setSearchQuery(String(value || "").trim()); } function clearSearch() { store.setSearchQuery(""); } function selectCategory(categoryId) { store.setCategoryFilter(String(categoryId || "all").trim() || "all"); } function selectEntry(kind, id) { store.select(kind, id); } function requestRetrieveSelected() { const selectedEntry = getSelectedEntry(); if (!selectedEntry || selectedEntry.entryKind !== "stored") { showNotice("error", "Select a stored vehicle to retrieve."); return false; } if (GarageApp.data?.session?.spawnBlocked) { showNotice("error", "The garage spawn area is blocked."); return false; } const bridge = GarageApp.bridge; if (!bridge || typeof bridge.requestRetrieve !== "function") { showNotice("error", "Garage retrieve bridge is unavailable."); return false; } store.startAction("retrieve"); const sent = bridge.requestRetrieve({ plate: selectedEntry.plate || "", }); if (!sent) { store.finishAction(); showNotice("error", "Garage retrieve bridge is unavailable."); return false; } return true; } function requestStoreSelected() { const selectedEntry = getSelectedEntry(); if (!selectedEntry || selectedEntry.entryKind !== "nearby") { showNotice("error", "Select a nearby vehicle to store."); return false; } if (selectedEntry.isEmpty === false) { showNotice( "error", "All crew must exit the vehicle before storing it.", ); return false; } const bridge = GarageApp.bridge; if (!bridge || typeof bridge.requestStore !== "function") { showNotice("error", "Garage store bridge is unavailable."); return false; } store.startAction("store"); const sent = bridge.requestStore({ netId: selectedEntry.netId || "", }); if (!sent) { store.finishAction(); showNotice("error", "Garage store bridge is unavailable."); return false; } return true; } GarageApp.actions = { showNotice, closeGarage, refreshGarage, applySearchQuery, clearSearch, selectCategory, selectEntry, getSelectedEntry, requestRetrieveSelected, requestStoreSelected, }; })(); (function () { const GarageApp = (window.GarageApp = window.GarageApp || {}); const { h } = GarageApp.runtime; const WindowTitleBar = window.SharedUI.componentFns.WindowTitleBar; const store = GarageApp.store; const actions = GarageApp.actions; const { categories, garage, nearby, session } = GarageApp.data; function q(query, values) { const needle = String(query || "") .trim() .toLowerCase(); if (!needle) { return true; } return values.some((value) => String(value || "") .toLowerCase() .includes(needle), ); } function pct(value) { return Math.max(0, Math.min(100, Math.round(Number(value || 0) * 100))); } function categoryLabel(category) { const match = categories.find( (entry) => entry.id === String(category || "other").toLowerCase(), ); return match ? match.label : "Other"; } function distanceLabel(value) { return `${Math.round(Number(value || 0))} m`; } function plateLabel(value) { return String(value || "").trim() || "Untracked"; } function statusLabel(vehicle) { if (!vehicle) { return "-"; } if (vehicle.entryKind === "stored") { return "Stored"; } return vehicle.isEmpty === false ? "Crewed" : "Ready"; } function normalizeHitPointLabel(value) { return String(value || "") .replace(/^Hit/i, "") .replace(/([a-z])([A-Z])/g, "$1 $2") .replace(/_/g, " ") .trim(); } function sameEntry(left, right) { if (!left || !right) { return false; } return ( String(left.entryKind || "") === String(right.entryKind || "") && String(left.plate || "") === String(right.plate || "") && String(left.netId || "") === String(right.netId || "") ); } function selectedEntry(state) { if (state.selectedKind === "stored") { return ( (garage.vehicles || []).find( (vehicle) => String(vehicle.plate || "") === state.selectedId, ) || null ); } if (state.selectedKind === "nearby") { return ( (nearby.vehicles || []).find( (vehicle) => String(vehicle.netId || "") === state.selectedId, ) || null ); } return null; } function visibleVehicles(vehicles, state) { return (vehicles || []).filter((vehicle) => { if ( state.categoryFilter !== "all" && String(vehicle.category || "").toLowerCase() !== state.categoryFilter ) { return false; } return q(state.searchQuery, [ vehicle.displayName, vehicle.classname, vehicle.plate, vehicle.netId, vehicle.category, ]); }); } function stat(label, value, tone = "") { return h( "div", { className: tone ? `garage-stat-card is-${tone}` : "garage-stat-card", }, h("span", { className: "garage-stat-label" }, label), h("span", { className: "garage-stat-value" }, value), ); } function meter(label, percent, tone) { return h( "div", { className: "garage-meter" }, h( "div", { className: "garage-meter-label-row" }, h("span", { className: "garage-meter-label" }, label), h("span", { className: "garage-meter-value" }, `${percent}%`), ), h( "div", { className: "garage-meter-track" }, h("span", { className: `garage-meter-fill is-${tone}`, style: { width: `${percent}%` }, }), ), ); } function vehicleItem(vehicle, currentSelection) { const id = vehicle.entryKind === "stored" ? String(vehicle.plate || "") : String(vehicle.netId || ""); const isNearby = vehicle.entryKind === "nearby"; return h( "button", { type: "button", className: sameEntry(vehicle, currentSelection) ? "garage-vehicle-item is-selected" : "garage-vehicle-item", onClick: () => actions.selectEntry(vehicle.entryKind, id), }, h( "div", { className: "garage-vehicle-item-head" }, h( "div", { className: "garage-vehicle-copy" }, h( "span", { className: "garage-vehicle-title" }, vehicle.displayName || vehicle.classname || "Vehicle", ), h( "span", { className: "garage-vehicle-meta" }, isNearby ? `Nearby ${distanceLabel(vehicle.distance)}` : `Plate ${plateLabel(vehicle.plate)}`, ), ), h( "span", { className: isNearby && vehicle.isEmpty === false ? "garage-badge is-warning" : "garage-badge", }, isNearby ? vehicle.isEmpty === false ? "Crewed" : "Empty" : categoryLabel(vehicle.category), ), ), h( "div", { className: "garage-inline-meters" }, meter("Health", pct(vehicle.health), "health"), meter("Fuel", pct(vehicle.fuel), "fuel"), ), ); } function vehicleList(title, eyebrow, scrollId, vehicles, currentSelection) { return h( "section", { className: "garage-card garage-list-card" }, h( "div", { className: "garage-card-header" }, h( "div", null, h("span", { className: "garage-eyebrow" }, eyebrow), h("h2", { className: "garage-section-title" }, title), ), h( "span", { className: "garage-pill" }, `${vehicles.length} ${vehicles.length === 1 ? "Vehicle" : "Vehicles"}`, ), ), h( "div", { className: "garage-card-body garage-scroll-body", "data-preserve-scroll-id": scrollId, }, vehicles.length > 0 ? vehicles.map((vehicle) => vehicleItem(vehicle, currentSelection), ) : h( "div", { className: "garage-empty-state" }, h( "h3", { className: "garage-empty-title" }, "No matching vehicles", ), h( "p", { className: "garage-empty-copy" }, "Adjust the current search or category filter to view more records.", ), ), ), ); } function hitPointRows(hitPoints) { const rows = (Array.isArray(hitPoints) ? hitPoints : []) .slice() .sort( (left, right) => Number(right.value || 0) - Number(left.value || 0), ) .slice(0, 6) .filter((row) => Number(row.value || 0) > 0); if (rows.length === 0) { return h( "div", { className: "garage-empty-inline" }, "No subsystem damage reported.", ); } return h( "div", { className: "garage-hitpoint-grid" }, rows.map((row) => h( "div", { className: "garage-hitpoint-row" }, h( "div", { className: "garage-hitpoint-copy" }, h( "span", { className: "garage-hitpoint-name" }, normalizeHitPointLabel(row.name) || "Subsystem", ), row.selection ? h( "span", { className: "garage-hitpoint-selection" }, row.selection, ) : null, ), h( "span", { className: "garage-hitpoint-value" }, `${Math.round(Number(row.value || 0) * 100)}%`, ), ), ), ); } function detailPanel(currentSelection, state) { if (!currentSelection) { return h( "section", { className: "garage-card garage-detail-card" }, h( "div", { className: "garage-card-header" }, h( "div", null, h("span", { className: "garage-eyebrow" }, "Selection"), h( "h2", { className: "garage-section-title" }, "Vehicle Detail", ), ), ), h( "div", { className: "garage-card-body garage-detail-empty" }, h( "h3", { className: "garage-empty-title" }, "Select a vehicle", ), h( "p", { className: "garage-empty-copy" }, "Choose a stored record to retrieve or a nearby vehicle to store.", ), ), ); } const isStored = currentSelection.entryKind === "stored"; const pendingAction = String(state.pendingAction || ""); const isBusy = pendingAction === "retrieve" || pendingAction === "store"; const canRetrieve = isStored && !session.spawnBlocked && !isBusy; const canStore = !isStored && currentSelection.isEmpty !== false && !isBusy; return h( "section", { className: "garage-card garage-detail-card" }, h( "div", { className: "garage-card-header" }, h( "div", null, h( "span", { className: "garage-eyebrow" }, isStored ? "Stored Record" : "Nearby Vehicle", ), h( "h2", { className: "garage-section-title" }, currentSelection.displayName || currentSelection.classname || "Vehicle", ), ), h( "span", { className: currentSelection.entryKind === "nearby" && currentSelection.isEmpty === false ? "garage-badge is-warning" : "garage-badge", }, isStored ? `Plate ${plateLabel(currentSelection.plate)}` : currentSelection.isEmpty === false ? "Crewed" : "Ready", ), ), h( "div", { className: "garage-card-body garage-detail-body" }, h( "div", { className: "garage-detail-grid" }, h( "div", { className: "garage-detail-copy" }, h( "div", { className: "garage-detail-meta" }, stat( "Category", categoryLabel(currentSelection.category), ), stat( "Status", statusLabel(currentSelection), currentSelection.entryKind === "nearby" && currentSelection.isEmpty === false ? "danger" : "", ), stat( isStored ? "Record" : "Distance", isStored ? plateLabel(currentSelection.plate) : distanceLabel(currentSelection.distance), isStored ? "" : "accent", ), ), h( "div", { className: "garage-meter-stack" }, meter( "Health", pct(currentSelection.health), "health", ), meter("Fuel", pct(currentSelection.fuel), "fuel"), ), h( "div", { className: "garage-action-row" }, isStored ? h( "button", { type: "button", className: "garage-btn garage-btn-primary", disabled: !canRetrieve, onClick: () => actions.requestRetrieveSelected(), }, pendingAction === "retrieve" ? "Retrieving..." : "Retrieve Vehicle", ) : h( "button", { type: "button", className: "garage-btn garage-btn-primary", disabled: !canStore, onClick: () => actions.requestStoreSelected(), }, pendingAction === "store" ? "Storing..." : "Store Vehicle", ), h( "button", { type: "button", className: "garage-btn garage-btn-secondary", disabled: isBusy, onClick: () => actions.refreshGarage(), }, "Refresh", ), ), h( "p", { className: "garage-detail-note" }, isStored ? session.spawnBlocked ? "The garage spawn lane is currently blocked." : "Retrieve this stored vehicle into the active spawn lane." : currentSelection.isEmpty === false ? "Only empty nearby vehicles can be stored." : "Store this nearby vehicle back into persistent garage storage.", ), ), h( "div", { className: "garage-detail-subsystems" }, h( "div", { className: "garage-subsystem-header" }, h( "span", { className: "garage-eyebrow" }, "Subsystems", ), h( "span", { className: "garage-detail-caption" }, "Highest damage first", ), ), hitPointRows(currentSelection.hitPoints), ), ), ), ); } GarageApp.components = GarageApp.components || {}; GarageApp.components.App = function App() { const state = { categoryFilter: store.getCategoryFilter(), notice: store.getNotice(), pendingAction: store.getPendingAction(), searchQuery: store.getSearchQuery(), selectedId: store.getSelectedId(), selectedKind: store.getSelectedKind(), }; const currentSelection = selectedEntry(state); const storedVehicles = visibleVehicles(garage.vehicles || [], state); const nearbyVehicles = visibleVehicles(nearby.vehicles || [], state); const searchLabel = state.searchQuery ? `Search: ${state.searchQuery}` : "Live"; return h( "div", { className: "garage-shell" }, WindowTitleBar({ kicker: "FORGE Logistics", title: "Vehicle Garage", onClose: () => actions.closeGarage(), closeLabel: "Close garage interface", }), state.notice.text ? h( "div", { className: "garage-toast-stack" }, h( "div", { className: state.notice.type === "error" ? "garage-toast is-error" : "garage-toast is-success", }, state.notice.text, ), ) : null, h( "div", { className: "garage-layout" }, h( "aside", { className: "garage-sidebar" }, h( "section", { className: "garage-module" }, h( "div", { className: "garage-module-header" }, h( "div", null, h( "span", { className: "garage-eyebrow" }, "Search", ), h( "h2", { className: "garage-section-title" }, "Vehicle Records", ), ), h( "span", { className: "garage-pill" }, searchLabel, ), ), h( "div", { className: "garage-search-form" }, h("input", { id: "garage-search-input", type: "text", className: "garage-search-input", placeholder: "Search by name, plate, or category", value: state.searchQuery, }), h( "div", { className: "garage-search-actions" }, h( "button", { type: "button", className: "garage-btn garage-btn-primary", onClick: () => actions.applySearchQuery( document.getElementById( "garage-search-input", )?.value || "", ), }, "Apply Search", ), h( "button", { type: "button", className: "garage-btn garage-btn-secondary", onClick: () => actions.clearSearch(), }, "Clear", ), ), ), ), h( "section", { className: "garage-module" }, h( "div", { className: "garage-module-header" }, h( "div", null, h( "span", { className: "garage-eyebrow" }, "Filter", ), h( "h2", { className: "garage-section-title" }, "Vehicle Categories", ), ), ), h( "div", { className: "garage-category-grid" }, categories.map((category) => h( "button", { type: "button", className: state.categoryFilter === category.id ? "garage-chip is-active" : "garage-chip", onClick: () => actions.selectCategory(category.id), }, category.label, ), ), ), ), h( "section", { className: "garage-module" }, h( "div", { className: "garage-module-header" }, h( "div", null, h( "span", { className: "garage-eyebrow" }, "Status", ), h( "h2", { className: "garage-section-title" }, "Garage Summary", ), ), h( "button", { type: "button", className: "garage-btn garage-btn-secondary", disabled: Boolean(state.pendingAction), onClick: () => actions.refreshGarage(), }, "Refresh", ), ), h( "div", { className: "garage-summary-grid" }, stat( "Stored", `${session.capacityUsed}/${session.capacityMax}`, ), stat("Nearby", session.nearbyCount, "accent"), stat( "Spawn Lane", session.spawnStatus, session.spawnBlocked ? "danger" : "", ), ), ), ), h( "main", { className: "garage-main" }, h( "section", { className: "garage-panel" }, h( "div", { className: "garage-panel-header" }, h( "div", null, h( "span", { className: "garage-eyebrow" }, "Operations Bay", ), h( "h1", { className: "garage-title" }, session.garageName || "Vehicle Garage", ), ), h( "span", { className: "garage-pill" }, `${session.capacityUsed}/${session.capacityMax} Stored`, ), ), h( "div", { className: "garage-panel-intro" }, h( "p", { className: "garage-copy" }, "Retrieve stored vehicles into the active spawn lane or store nearby empty vehicles back into persistent ownership records.", ), ), h( "div", { className: "garage-dashboard" }, vehicleList( "Stored Vehicles", "Persistent Records", "garage-stored-list", storedVehicles, currentSelection, ), vehicleList( "Nearby Vehicles", "Store Window", "garage-nearby-list", nearbyVehicles, currentSelection, ), detailPanel(currentSelection, state), ), ), ), ), h( "footer", { className: "garage-footer" }, h( "div", { className: "garage-footer-block" }, h( "span", { className: "garage-footer-title" }, "Storage Capacity", ), h( "span", { className: "garage-footer-copy" }, `${session.capacityUsed} of ${session.capacityMax} vehicle slot(s) are currently occupied.`, ), ), h( "div", { className: "garage-footer-block" }, h( "span", { className: "garage-footer-title" }, "Retrieval Window", ), h( "span", { className: "garage-footer-copy" }, session.spawnBlocked ? "Spawn lane is blocked. Clear the bay before retrieving another vehicle." : "Spawn lane is clear. Stored vehicles can be retrieved immediately.", ), ), h( "div", { className: "garage-footer-block" }, h( "span", { className: "garage-footer-title" }, "Store Rules", ), h( "span", { className: "garage-footer-copy" }, "Only nearby empty vehicles can be stored. Nearby count updates from the live world state.", ), ), ), ); }; })(); (function () { const ForgeWebUI = window.ForgeWebUI; const GarageApp = window.GarageApp; const app = ForgeWebUI.createApp({ name: "garage", root: "#app", setup({ root }) { ForgeWebUI.mount(root, () => GarageApp.components.App(), { preserveScroll: true, }); if (GarageApp.bridge) { GarageApp.bridge.notifyReady(); } }, }); app.start(); })();