Make RTS menu use daemon Eve surface
This commit is contained in:
parent
8a79905b99
commit
7c8a12261d
4 changed files with 108 additions and 188 deletions
|
|
@ -7,6 +7,7 @@ import {
|
|||
import type {
|
||||
AetheriaRtsApi,
|
||||
AetheriaMenuSurfaceDocument,
|
||||
AetheriaMenuSurfaceComponent,
|
||||
AuthorityStatusDocument,
|
||||
AetheriaRuntimeViewportFeedSnapshot,
|
||||
BodyView,
|
||||
|
|
@ -59,6 +60,8 @@ let refreshInFlight = false;
|
|||
let viewportFeedUnsubscribe: (() => void) | null = null;
|
||||
let eveSurfacePoll: number | null = null;
|
||||
let latestEveSurfaceKey = "";
|
||||
let activeMainMenuSurfaceId = "aetheria.main_menu.root";
|
||||
let renderDaemonEveSurfaceNow: (() => Promise<void>) | null = null;
|
||||
const mainMenuMode = window.location.hash === "#main-menu";
|
||||
const legacyViewportMode = window.location.hash === "#legacy-viewport";
|
||||
|
||||
|
|
@ -99,40 +102,6 @@ function setStatus(text: string): void {
|
|||
statusEl.textContent = text;
|
||||
}
|
||||
|
||||
async function showMainMenu(surfaceId = "aetheria.main_menu.root"): Promise<void> {
|
||||
document.body.classList.add("main-menu-mode");
|
||||
document.body.classList.remove("eve-game-mode");
|
||||
viewportFeedUnsubscribe?.();
|
||||
stopEveSurfacePoll();
|
||||
const surface = await window.aetheriaRts.mainMenuSurface({
|
||||
surfaceId,
|
||||
inGame: false,
|
||||
canOpenRuntimeInputScreen: false,
|
||||
});
|
||||
renderMainMenuSurface(surface);
|
||||
}
|
||||
|
||||
function renderMainMenuSurface(surface: AetheriaMenuSurfaceDocument): void {
|
||||
let host = document.querySelector<HTMLElement>("#main-menu-host");
|
||||
if (!host) {
|
||||
host = document.createElement("section");
|
||||
host.id = "main-menu-host";
|
||||
host.className = "main-menu-host";
|
||||
document.body.append(host);
|
||||
}
|
||||
|
||||
renderEveSurface(surface, host, {
|
||||
body: document.body,
|
||||
assetUrlResolver: resolveAetheriaAssetUrl,
|
||||
clientId: "aetheria.rts.electron",
|
||||
commandSink: intent => handleMenuCommand(intent.command, surface),
|
||||
documentResolver: resolveEveDocument,
|
||||
source: "Aetheria Electron",
|
||||
statusElement: statusEl,
|
||||
});
|
||||
wireWindowControls();
|
||||
}
|
||||
|
||||
async function showDaemonEveSurface(): Promise<void> {
|
||||
document.body.classList.remove("main-menu-mode");
|
||||
document.body.classList.add("eve-game-mode");
|
||||
|
|
@ -152,10 +121,11 @@ async function showDaemonEveSurface(): Promise<void> {
|
|||
const surface = await window.aetheriaRts.eveSurface({
|
||||
recordKey: "eve:surface:aetheria.daemon.game",
|
||||
});
|
||||
const surfaceKey = `${surface.providerId}:${surface.surface.id}:${surface.version}:${surface.updatedAtUtc}`;
|
||||
const loweredSurface = withActiveMainMenuPanel(surface, activeMainMenuSurfaceId);
|
||||
const surfaceKey = `${surface.providerId}:${surface.surface.id}:${surface.version}:${surface.updatedAtUtc}:${activeMainMenuSurfaceId}`;
|
||||
if (surfaceKey !== latestEveSurfaceKey) {
|
||||
latestEveSurfaceKey = surfaceKey;
|
||||
renderEveSurface(surface, host, {
|
||||
renderEveSurface(loweredSurface, host, {
|
||||
body: document.body,
|
||||
assetUrlResolver: resolveAetheriaAssetUrl,
|
||||
clientId: "aetheria.rts.electron",
|
||||
|
|
@ -171,6 +141,7 @@ async function showDaemonEveSurface(): Promise<void> {
|
|||
}
|
||||
};
|
||||
|
||||
renderDaemonEveSurfaceNow = renderLatest;
|
||||
await renderLatest();
|
||||
if (eveSurfacePoll == null) {
|
||||
eveSurfacePoll = window.setInterval(() => {
|
||||
|
|
@ -179,6 +150,70 @@ async function showDaemonEveSurface(): Promise<void> {
|
|||
}
|
||||
}
|
||||
|
||||
async function showMainMenuPanel(surfaceId: string): Promise<void> {
|
||||
activeMainMenuSurfaceId = normalizeMainMenuSurfaceId(surfaceId);
|
||||
latestEveSurfaceKey = "";
|
||||
if (renderDaemonEveSurfaceNow) {
|
||||
await renderDaemonEveSurfaceNow();
|
||||
return;
|
||||
}
|
||||
|
||||
await showDaemonEveSurface();
|
||||
}
|
||||
|
||||
function normalizeMainMenuSurfaceId(surfaceId: string): string {
|
||||
switch (surfaceId) {
|
||||
case "aetheria.main_menu.settings":
|
||||
case "aetheria.main_menu.player_settings":
|
||||
case "aetheria.main_menu.verse_settings":
|
||||
case "aetheria.main_menu.input_settings":
|
||||
return surfaceId;
|
||||
default:
|
||||
return "aetheria.main_menu.root";
|
||||
}
|
||||
}
|
||||
|
||||
function withActiveMainMenuPanel(
|
||||
document: AetheriaMenuSurfaceDocument,
|
||||
panelSurfaceId: string,
|
||||
): AetheriaMenuSurfaceDocument {
|
||||
return {
|
||||
...document,
|
||||
surface: {
|
||||
...document.surface,
|
||||
root: rewriteMainMenuPanelSlot(document.surface.root, panelSurfaceId),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
type MutableEveComponent = AetheriaMenuSurfaceComponent & {
|
||||
layout?: Record<string, string>;
|
||||
style?: Record<string, string>;
|
||||
};
|
||||
|
||||
function rewriteMainMenuPanelSlot(
|
||||
component: AetheriaMenuSurfaceComponent,
|
||||
panelSurfaceId: string,
|
||||
): AetheriaMenuSurfaceComponent {
|
||||
const source = component as MutableEveComponent;
|
||||
const props = source.props ?? {};
|
||||
const embeddedDocuments = source.embeddedDocuments ?? [];
|
||||
const isMainMenuSlot = props.slotId === "mainMenuPanel" ||
|
||||
embeddedDocuments.some(slot => slot.slotId === "mainMenuPanel");
|
||||
return {
|
||||
...source,
|
||||
props: isMainMenuSlot
|
||||
? { ...props, documentId: panelSurfaceId }
|
||||
: props,
|
||||
embeddedDocuments: isMainMenuSlot
|
||||
? embeddedDocuments.map(slot => slot.slotId === "mainMenuPanel"
|
||||
? { ...slot, documentId: panelSurfaceId }
|
||||
: slot)
|
||||
: source.embeddedDocuments,
|
||||
children: source.children.map(child => rewriteMainMenuPanelSlot(child, panelSurfaceId)),
|
||||
};
|
||||
}
|
||||
|
||||
async function resolveEveDocument(
|
||||
request: EveEmbeddedDocumentRequest,
|
||||
component: EveSurfaceComponent,
|
||||
|
|
@ -291,22 +326,22 @@ function handleMenuCommand(commandOrIntent: string | EveCommandIntent, surface?:
|
|||
const command = typeof commandOrIntent === "string" ? commandOrIntent : commandOrIntent.command;
|
||||
switch (command) {
|
||||
case "aetheria.main_menu.root.show_settings":
|
||||
void showMainMenu("aetheria.main_menu.settings");
|
||||
void showMainMenuPanel("aetheria.main_menu.settings");
|
||||
return;
|
||||
case "aetheria.main_menu.settings.show_player_settings":
|
||||
void showMainMenu("aetheria.main_menu.player_settings");
|
||||
void showMainMenuPanel("aetheria.main_menu.player_settings");
|
||||
return;
|
||||
case "aetheria.main_menu.settings.show_verse_settings":
|
||||
void showMainMenu("aetheria.main_menu.verse_settings");
|
||||
void showMainMenuPanel("aetheria.main_menu.verse_settings");
|
||||
return;
|
||||
case "aetheria.main_menu.settings.show_input_settings":
|
||||
void showMainMenu("aetheria.main_menu.input_settings");
|
||||
void showMainMenuPanel("aetheria.main_menu.input_settings");
|
||||
return;
|
||||
case "aetheria.main_menu.settings.back_to_settings":
|
||||
void showMainMenu("aetheria.main_menu.settings");
|
||||
void showMainMenuPanel("aetheria.main_menu.settings");
|
||||
return;
|
||||
case "aetheria.main_menu.settings.back_to_main":
|
||||
void showMainMenu("aetheria.main_menu.root");
|
||||
void showMainMenuPanel("aetheria.main_menu.root");
|
||||
return;
|
||||
default:
|
||||
setStatus(`${surface?.title ?? "Aetheria Starbridge"}: ${command || "no command"}`);
|
||||
|
|
@ -841,7 +876,7 @@ function roleText(session: StarbridgeSessionDocument): string {
|
|||
}
|
||||
|
||||
if (mainMenuMode) {
|
||||
void showMainMenu();
|
||||
void showDaemonEveSurface();
|
||||
} else if (legacyViewportMode) {
|
||||
document.body.classList.remove("main-menu-mode", "eve-game-mode");
|
||||
updateZoomLabel();
|
||||
|
|
|
|||
|
|
@ -700,34 +700,11 @@ function buildMainMenuSurface(
|
|||
surfaceId: string,
|
||||
canOpenRuntimeInputScreen: boolean,
|
||||
inGame: boolean,
|
||||
panelOnly = false,
|
||||
_panelOnly = false,
|
||||
): AetheriaMenuSurfaceDocument {
|
||||
const updatedAtUtc = new Date().toISOString();
|
||||
const activeSurfaceId = normalizeMainMenuSurfaceId(surfaceId);
|
||||
return panelOnly
|
||||
? buildMainMenuPanelSurface(activeSurfaceId, canOpenRuntimeInputScreen, inGame, updatedAtUtc)
|
||||
: buildMainMenuShellSurface(activeSurfaceId, updatedAtUtc);
|
||||
}
|
||||
|
||||
function buildMainMenuShellSurface(
|
||||
activeSurfaceId: string,
|
||||
updatedAtUtc: string,
|
||||
): AetheriaMenuSurfaceDocument {
|
||||
return surfaceDocument(
|
||||
"aetheria.main_menu.shell",
|
||||
"Aetheria Starbridge",
|
||||
updatedAtUtc,
|
||||
mainMenuCommands(),
|
||||
gravitySurface("aetheria.main_menu.shell.gravity"),
|
||||
surfaceSlot(
|
||||
"aetheria.main_menu.shell.panel",
|
||||
"mainMenuPanel",
|
||||
activeSurfaceId,
|
||||
eveSurfaceSchemaId,
|
||||
"menu.overlay",
|
||||
{ position: "absolute", top: "0", right: "0", bottom: "0", left: "0", width: "100%", height: "100%" },
|
||||
{ background: "rgba(0,0,0,0)" }),
|
||||
);
|
||||
return buildMainMenuPanelSurface(activeSurfaceId, canOpenRuntimeInputScreen, inGame, updatedAtUtc);
|
||||
}
|
||||
|
||||
function buildMainMenuPanelSurface(
|
||||
|
|
@ -858,7 +835,6 @@ function buildMainMenuPanelSurface(
|
|||
|
||||
function normalizeMainMenuSurfaceId(surfaceId: string): string {
|
||||
switch (surfaceId) {
|
||||
case "aetheria.main_menu.shell":
|
||||
case "":
|
||||
return "aetheria.main_menu.root";
|
||||
case "aetheria.main_menu.root":
|
||||
|
|
@ -872,20 +848,6 @@ function normalizeMainMenuSurfaceId(surfaceId: string): string {
|
|||
}
|
||||
}
|
||||
|
||||
function mainMenuCommands(): AetheriaMenuSurfaceCommand[] {
|
||||
return [
|
||||
command("aetheria.main_menu.root.continue", "Continue"),
|
||||
command("aetheria.main_menu.root.new_game", "New Game"),
|
||||
command("aetheria.main_menu.root.show_settings", "Settings"),
|
||||
command("aetheria.main_menu.root.quit", "Quit"),
|
||||
command("aetheria.main_menu.settings.show_player_settings", "Player Settings"),
|
||||
command("aetheria.main_menu.settings.show_verse_settings", "Verse"),
|
||||
command("aetheria.main_menu.settings.show_input_settings", "Input"),
|
||||
command("aetheria.main_menu.settings.back_to_main", "Back"),
|
||||
command("aetheria.main_menu.settings.back_to_settings", "Back"),
|
||||
];
|
||||
}
|
||||
|
||||
function surfaceDocument(
|
||||
surfaceId: string,
|
||||
title: string,
|
||||
|
|
@ -893,7 +855,6 @@ function surfaceDocument(
|
|||
commands: AetheriaMenuSurfaceCommand[],
|
||||
...children: AetheriaMenuSurfaceComponent[]
|
||||
): AetheriaMenuSurfaceDocument {
|
||||
const isMenuShell = surfaceId === "aetheria.main_menu.shell";
|
||||
return {
|
||||
providerId: "aetheria",
|
||||
providerKind: "game.menu",
|
||||
|
|
@ -907,7 +868,7 @@ function surfaceDocument(
|
|||
"surface",
|
||||
{},
|
||||
{ position: "relative", overflow: "hidden", width: "100%", height: "100vh", minHeight: "100vh" },
|
||||
{ background: isMenuShell ? "#020606" : "rgba(0,0,0,0)" },
|
||||
{ background: "rgba(0,0,0,0)" },
|
||||
...children),
|
||||
styles: [
|
||||
{ name: "font.title.family", value: "Montserrat" },
|
||||
|
|
@ -948,26 +909,6 @@ function node(
|
|||
return { id, kind, props, layout, style, children: normalizedChildren };
|
||||
}
|
||||
|
||||
function surfaceSlot(
|
||||
id: string,
|
||||
slotId: string,
|
||||
documentId: string,
|
||||
schemaId: string,
|
||||
presentationKind: string,
|
||||
layout?: Record<string, string>,
|
||||
style?: Record<string, string>,
|
||||
): AetheriaMenuSurfaceComponent {
|
||||
return {
|
||||
id,
|
||||
kind: "surface.slot",
|
||||
props: { slotId, documentId, schemaId, presentationKind },
|
||||
layout,
|
||||
style,
|
||||
embeddedDocuments: [{ slotId, documentId, schemaId, presentationKind }],
|
||||
children: [],
|
||||
};
|
||||
}
|
||||
|
||||
function text(
|
||||
id: string,
|
||||
value: string,
|
||||
|
|
@ -1016,8 +957,10 @@ function button(id: string, label: string, commandId: string): AetheriaMenuSurfa
|
|||
{ minWidth: "0", width: "220px", height: "32px", padding: "0" },
|
||||
{
|
||||
background: "rgba(0, 0, 0, 0)",
|
||||
border: "0",
|
||||
borderWidth: "0",
|
||||
borderStyle: "solid",
|
||||
borderRadius: "0",
|
||||
boxShadow: "none",
|
||||
font: "400 1.55rem/32px Ubuntu, sans-serif",
|
||||
color: "#e8fbff",
|
||||
|
|
@ -1029,43 +972,6 @@ function buttonColumn(id: string, ...children: AetheriaMenuSurfaceComponent[]):
|
|||
return node(id, "column", {}, { gap: "0.18rem", alignItems: "flex-start" }, { color: "#e8fbff" }, ...children);
|
||||
}
|
||||
|
||||
function gravitySurface(id: string): AetheriaMenuSurfaceComponent {
|
||||
return node(
|
||||
id,
|
||||
"gravity.surface",
|
||||
{
|
||||
label: "Aetheria level gravity surface",
|
||||
viewRadius: "1450",
|
||||
terrainRadius: "1450",
|
||||
terrainDepth: "-8",
|
||||
terrainDepthExponent: "1.2",
|
||||
terrainWaveFrequency: "0.6",
|
||||
simulationTimeSeconds: "0",
|
||||
samplesX: "196",
|
||||
isolines: "22",
|
||||
shader: "perlines.gravity-map.v1",
|
||||
noiseAsset: "cultmesh://aetheria/assets/textures/perlines-nebula",
|
||||
tintSplatAsset: "cultmesh://aetheria/assets/textures/tint_splat",
|
||||
bodies: [
|
||||
"terminus-sun|Sun|0|0|900|-80|3|450|10|2|cultmesh://aetheria/assets/icons/star.terminus-sun|cultmesh://aetheria/assets/textures/tint_splat",
|
||||
"anchor-moon|Planet|360|-180|260|-22|2.2|180|4|1.4|cultmesh://aetheria/assets/icons/body.anchor-moon|cultmesh://aetheria/assets/textures/tint_splat",
|
||||
"blueglass|Planet|-540|340|340|-34|2.6|150|5|1.1|cultmesh://aetheria/assets/icons/body.blueglass|cultmesh://aetheria/assets/textures/tint_splat",
|
||||
"emberhook|Planet|-680|-420|310|-28|2.1|130|4|1.3|cultmesh://aetheria/assets/icons/body.emberhook|cultmesh://aetheria/assets/textures/tint_splat",
|
||||
"vesper|Gas Giant|780|420|420|-38|2.4|210|6|0.7|cultmesh://aetheria/assets/icons/body.vesper|cultmesh://aetheria/assets/textures/tint_splat",
|
||||
"latch|Moon|-160|620|210|-18|2.8|95|3|1.6|cultmesh://aetheria/assets/icons/body.latch|cultmesh://aetheria/assets/textures/tint_splat",
|
||||
].join(";"),
|
||||
objects: [
|
||||
"anchor-station|station|Anchor Station|-220|-90|1|0|player|1|1|cultmesh://aetheria/assets/icons/station.anchor-station",
|
||||
"vanguard-one|ship|Vanguard One|-60|-40|1|0.2|player|1|1|cultmesh://aetheria/assets/icons/ship.vanguard-one",
|
||||
"wing-two|ship|Wing Two|180|120|-0.4|0.9|player|1|1|cultmesh://aetheria/assets/icons/ship.wing-two",
|
||||
"torch-three|ship|Torch Three|-160|210|0.6|-0.8|player|1|1|cultmesh://aetheria/assets/icons/ship.torch-three",
|
||||
"ash-raider|ship|Ash Raider|420|180|-0.8|-0.1|raider|0|1|cultmesh://aetheria/assets/icons/ship.ash-raider",
|
||||
"derelict-relay|station|Derelict Relay|-260|260|0|1|neutral|0|1|cultmesh://aetheria/assets/icons/station.derelict-relay",
|
||||
].join(";"),
|
||||
},
|
||||
{ position: "absolute", top: "0", right: "0", bottom: "0", left: "0", width: "100%", height: "100%" });
|
||||
}
|
||||
|
||||
function isSurfaceComponent(value: unknown): value is AetheriaMenuSurfaceComponent {
|
||||
return !!value && typeof value === "object" && "kind" in value && "props" in value && "children" in value;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,10 +96,7 @@ app.whenReady().then(async () => {
|
|||
throw new Error("AETHERIA_RTS_PEER_CULTMESH_ENDPOINTS has been removed. Configure AETHERIA_RTS_CULTMESH_URI and let Odin/CultMesh discover peer endpoints.");
|
||||
}
|
||||
|
||||
if (rendererView === "main-menu") {
|
||||
showStartup("Launching Aetheria Menu", "Lowering the CultUI main-menu surface.");
|
||||
mkdirSync(runtimeRoot, { recursive: true });
|
||||
} else if (launchLocalDaemon) {
|
||||
if (launchLocalDaemon) {
|
||||
await ensureDotnetBuild();
|
||||
showStartup("Launching Aetheria RTS", "Starting the Aetheria daemon.");
|
||||
mkdirSync(runtimeRoot, { recursive: true });
|
||||
|
|
@ -139,11 +136,11 @@ app.whenReady().then(async () => {
|
|||
"aetheria-rts-electron",
|
||||
{ publicationMode: launchLocalDaemon ? "local" : "remote" });
|
||||
|
||||
showStartup("Launching Aetheria RTS", "Waiting for the daemon CultMesh frame.");
|
||||
await rtsClient.waitForFrame(30000);
|
||||
if (rendererView === "main-menu") {
|
||||
await mainWindow.loadFile(rendererIndex, { hash: "main-menu" });
|
||||
} else {
|
||||
showStartup("Launching Aetheria RTS", "Waiting for the daemon CultMesh frame.");
|
||||
await rtsClient.waitForFrame(30000);
|
||||
await mainWindow.loadFile(rendererIndex);
|
||||
}
|
||||
if (electronSmoke) {
|
||||
|
|
|
|||
|
|
@ -367,55 +367,37 @@ namespace GameCult.Aetheria.State.Verse
|
|||
{
|
||||
return new AetheriaRuntimeSurfaceComponent(
|
||||
id,
|
||||
"column",
|
||||
new Dictionary<string, string>(StringComparer.Ordinal),
|
||||
"surface.slot",
|
||||
new Dictionary<string, string>(StringComparer.Ordinal)
|
||||
{
|
||||
["slotId"] = "mainMenuPanel",
|
||||
["documentId"] = AetheriaRuntimeMainMenuCommands.RootSurfaceId,
|
||||
["schemaId"] = "gamecult.eve.surface.v1",
|
||||
["presentationKind"] = "menu.overlay"
|
||||
},
|
||||
Array.Empty<AetheriaRuntimeSurfaceComponent>(),
|
||||
AetheriaRuntimeSurfaceStateBindings.FromProps(new Dictionary<string, string>(StringComparer.Ordinal)),
|
||||
new[]
|
||||
{
|
||||
TextNode(
|
||||
$"{id}.title",
|
||||
"AETHERIA",
|
||||
"text.title",
|
||||
Layout(("margin", "0 0 -1.35rem 0")),
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
["font"] = "100 5.3rem/0.98 Montserrat, sans-serif",
|
||||
["color"] = "rgba(232, 250, 255, 0.94)",
|
||||
["whiteSpace"] = "nowrap"
|
||||
}),
|
||||
TextNode(
|
||||
$"{id}.subtitle",
|
||||
"STARBRIDGE",
|
||||
"text.subtitle",
|
||||
Layout(("margin", "0 0 1.15rem 15.2rem")),
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
["font"] = "100 2.25rem/1 Montserrat, sans-serif",
|
||||
["color"] = "rgba(232, 250, 255, 0.88)",
|
||||
["whiteSpace"] = "nowrap"
|
||||
}),
|
||||
Node(
|
||||
$"{id}.actions",
|
||||
"column",
|
||||
Array.Empty<(string Key, string Value)>(),
|
||||
MenuButton($"{id}.continue", "Continue", "aetheria.main_menu.root.continue"),
|
||||
MenuButton($"{id}.new_game", "New Game", "aetheria.main_menu.root.new_game"),
|
||||
MenuButton($"{id}.settings", "Settings", "aetheria.main_menu.root.show_settings"),
|
||||
MenuButton($"{id}.quit", "Quit", "aetheria.main_menu.root.quit"))
|
||||
new AetheriaRuntimeEmbeddedDocumentSlot(
|
||||
"mainMenuPanel",
|
||||
AetheriaRuntimeMainMenuCommands.RootSurfaceId,
|
||||
"gamecult.eve.surface.v1",
|
||||
"menu.overlay")
|
||||
},
|
||||
AetheriaRuntimeSurfaceStateBindings.FromProps(new Dictionary<string, string>(StringComparer.Ordinal)),
|
||||
Array.Empty<AetheriaRuntimeEmbeddedDocumentSlot>(),
|
||||
Layout(
|
||||
("position", "absolute"),
|
||||
("left", "6.75rem"),
|
||||
("top", "5.75rem"),
|
||||
("width", "34rem"),
|
||||
("gap", "1rem"),
|
||||
("alignItems", "flex-start"),
|
||||
("top", "0"),
|
||||
("right", "0"),
|
||||
("bottom", "0"),
|
||||
("left", "0"),
|
||||
("width", "100%"),
|
||||
("height", "100%"),
|
||||
("zIndex", "4"),
|
||||
("pointerEvents", "auto")),
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
["color"] = "#e9fbff"
|
||||
["background"] = "rgba(0,0,0,0)"
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue