Embed main menu panels in RTS shell
This commit is contained in:
parent
dc8a7b069d
commit
0a1403bda3
6 changed files with 212 additions and 57 deletions
|
|
@ -347,6 +347,7 @@ export type AetheriaMenuSurfaceRequest = {
|
|||
surfaceId?: string;
|
||||
inGame?: boolean;
|
||||
canOpenRuntimeInputScreen?: boolean;
|
||||
panelOnly?: boolean;
|
||||
};
|
||||
|
||||
export type AetheriaEveSurfaceRequest = {
|
||||
|
|
|
|||
|
|
@ -126,6 +126,7 @@ function renderMainMenuSurface(surface: AetheriaMenuSurfaceDocument): void {
|
|||
assetUrlResolver: resolveAetheriaAssetUrl,
|
||||
clientId: "aetheria.rts.electron",
|
||||
commandSink: intent => handleMenuCommand(intent.command, surface),
|
||||
documentResolver: resolveEveDocument,
|
||||
source: "Aetheria Electron",
|
||||
statusElement: statusEl,
|
||||
});
|
||||
|
|
@ -181,7 +182,21 @@ async function showDaemonEveSurface(): Promise<void> {
|
|||
async function resolveEveDocument(
|
||||
request: EveEmbeddedDocumentRequest,
|
||||
component: EveSurfaceComponent,
|
||||
): Promise<{ document: unknown; documentId: string; schemaId: string } | undefined> {
|
||||
): Promise<{ document?: unknown; documentId: string; schemaId: string; surface?: AetheriaMenuSurfaceDocument["surface"] } | undefined> {
|
||||
if (request.schemaId === "gamecult.eve.surface.v1" || request.slotId === "mainMenuPanel") {
|
||||
const surface = await window.aetheriaRts.mainMenuSurface({
|
||||
surfaceId: request.documentId,
|
||||
inGame: false,
|
||||
canOpenRuntimeInputScreen: false,
|
||||
panelOnly: true,
|
||||
});
|
||||
return {
|
||||
documentId: request.documentId,
|
||||
schemaId: "gamecult.eve.surface.v1",
|
||||
surface: surface.surface,
|
||||
};
|
||||
}
|
||||
|
||||
const viewport = viewportFromComponent(component);
|
||||
if (request.schemaId === "gamecult.aetheria.render_splats_viewport.v1" || request.slotId === "renderSplats") {
|
||||
return {
|
||||
|
|
@ -278,6 +293,12 @@ function handleMenuCommand(commandOrIntent: string | EveCommandIntent, surface?:
|
|||
case "aetheria.main_menu.root.show_settings":
|
||||
void showMainMenu("aetheria.main_menu.settings");
|
||||
return;
|
||||
case "aetheria.main_menu.settings.show_player_settings":
|
||||
void showMainMenu("aetheria.main_menu.player_settings");
|
||||
return;
|
||||
case "aetheria.main_menu.settings.show_verse_settings":
|
||||
void showMainMenu("aetheria.main_menu.verse_settings");
|
||||
return;
|
||||
case "aetheria.main_menu.settings.show_input_settings":
|
||||
void showMainMenu("aetheria.main_menu.input_settings");
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ export type AetheriaMenuSurfaceRequest = {
|
|||
surfaceId?: string;
|
||||
inGame?: boolean;
|
||||
canOpenRuntimeInputScreen?: boolean;
|
||||
panelOnly?: boolean;
|
||||
};
|
||||
|
||||
export type AetheriaEveSurfaceRequest = {
|
||||
|
|
@ -336,7 +337,8 @@ export class AetheriaCultMeshClient {
|
|||
return buildMainMenuSurface(
|
||||
request.surfaceId || "aetheria.main_menu.root",
|
||||
request.canOpenRuntimeInputScreen === true,
|
||||
request.inGame === true);
|
||||
request.inGame === true,
|
||||
request.panelOnly === true);
|
||||
}
|
||||
|
||||
public async eveSurface(request: AetheriaEveSurfaceRequest = {}): Promise<AetheriaMenuSurfaceDocument> {
|
||||
|
|
@ -698,8 +700,42 @@ function buildMainMenuSurface(
|
|||
surfaceId: string,
|
||||
canOpenRuntimeInputScreen: boolean,
|
||||
inGame: boolean,
|
||||
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)" }),
|
||||
);
|
||||
}
|
||||
|
||||
function buildMainMenuPanelSurface(
|
||||
surfaceId: string,
|
||||
canOpenRuntimeInputScreen: boolean,
|
||||
inGame: boolean,
|
||||
updatedAtUtc: string,
|
||||
): AetheriaMenuSurfaceDocument {
|
||||
if (surfaceId === "aetheria.main_menu.settings") {
|
||||
return surfaceDocument(
|
||||
surfaceId,
|
||||
|
|
@ -711,20 +747,25 @@ function buildMainMenuSurface(
|
|||
command("aetheria.main_menu.settings.show_input_settings", "Input"),
|
||||
command("aetheria.main_menu.settings.back_to_main", "Back"),
|
||||
],
|
||||
text("aetheria.mainMenu.settings.title", "SETTINGS", "text.title"),
|
||||
buttonColumn(
|
||||
"aetheria.mainMenu.settings.actions",
|
||||
button("aetheria.mainMenu.settings.playerSettings", "Player Settings", "aetheria.main_menu.settings.show_player_settings"),
|
||||
button("aetheria.mainMenu.settings.verse", "Verse", "aetheria.main_menu.settings.show_verse_settings"),
|
||||
button("aetheria.mainMenu.settings.input", "Input", "aetheria.main_menu.settings.show_input_settings"),
|
||||
button("aetheria.mainMenu.settings.back", "Back", "aetheria.main_menu.settings.back_to_main"),
|
||||
menuPanel(
|
||||
surfaceId,
|
||||
[
|
||||
text("aetheria.mainMenu.settings.title", "SETTINGS", "text.title", { margin: "0 0 2.4rem 0" }, mainMenuTitleStyle("4.4rem")),
|
||||
buttonColumn(
|
||||
"aetheria.mainMenu.settings.actions",
|
||||
button("aetheria.mainMenu.settings.playerSettings", "Player Settings", "aetheria.main_menu.settings.show_player_settings"),
|
||||
button("aetheria.mainMenu.settings.verse", "Verse", "aetheria.main_menu.settings.show_verse_settings"),
|
||||
button("aetheria.mainMenu.settings.input", "Input", "aetheria.main_menu.settings.show_input_settings"),
|
||||
button("aetheria.mainMenu.settings.back", "Back", "aetheria.main_menu.settings.back_to_main"),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (surfaceId === "aetheria.main_menu.input_settings") {
|
||||
const children = [
|
||||
text("aetheria.mainMenu.input.title", "INPUT", "text.title"),
|
||||
text("aetheria.mainMenu.input.title", "INPUT", "text.title", { margin: "0 0 2.2rem 0" }, mainMenuTitleStyle("4.4rem")),
|
||||
metric("aetheria.mainMenu.input.bindingOverrides", "Binding Overrides", "0"),
|
||||
metric("aetheria.mainMenu.input.actionBarInputs", "Action-Bar Inputs", "0"),
|
||||
text(
|
||||
|
|
@ -742,7 +783,47 @@ function buildMainMenuSurface(
|
|||
"Aetheria Input Settings",
|
||||
updatedAtUtc,
|
||||
[command("aetheria.main_menu.settings.back_to_settings", "Back")],
|
||||
...children);
|
||||
menuPanel(surfaceId, children));
|
||||
}
|
||||
|
||||
if (surfaceId === "aetheria.main_menu.player_settings") {
|
||||
return surfaceDocument(
|
||||
surfaceId,
|
||||
"Aetheria Player Settings",
|
||||
updatedAtUtc,
|
||||
[command("aetheria.main_menu.settings.back_to_settings", "Back")],
|
||||
menuPanel(
|
||||
surfaceId,
|
||||
[
|
||||
text("aetheria.mainMenu.player.title", "PLAYER", "text.title", { margin: "0 0 2.2rem 0" }, mainMenuTitleStyle("4.4rem")),
|
||||
metric("aetheria.mainMenu.player.temperatureUnit", "Temperature Unit", "Celsius"),
|
||||
metric("aetheria.mainMenu.player.significantDigits", "Significant Digits", "3"),
|
||||
metric("aetheria.mainMenu.player.shutdown", "Default Shutdown", "25%"),
|
||||
buttonColumn(
|
||||
"aetheria.mainMenu.player.actions",
|
||||
button("aetheria.mainMenu.player.back", "Back", "aetheria.main_menu.settings.back_to_settings"),
|
||||
),
|
||||
]));
|
||||
}
|
||||
|
||||
if (surfaceId === "aetheria.main_menu.verse_settings") {
|
||||
return surfaceDocument(
|
||||
surfaceId,
|
||||
"Aetheria Verse Settings",
|
||||
updatedAtUtc,
|
||||
[command("aetheria.main_menu.settings.back_to_settings", "Back")],
|
||||
menuPanel(
|
||||
surfaceId,
|
||||
[
|
||||
text("aetheria.mainMenu.verse.title", "VERSE", "text.title", { margin: "0 0 2.2rem 0" }, mainMenuTitleStyle("4.4rem")),
|
||||
metric("aetheria.mainMenu.verse.name", "Name", "Local Aetheria"),
|
||||
metric("aetheria.mainMenu.verse.id", "Verse", "aetheria.local"),
|
||||
metric("aetheria.mainMenu.verse.visibility", "Visibility", "local"),
|
||||
buttonColumn(
|
||||
"aetheria.mainMenu.verse.actions",
|
||||
button("aetheria.mainMenu.verse.back", "Back", "aetheria.main_menu.settings.back_to_settings"),
|
||||
),
|
||||
]));
|
||||
}
|
||||
|
||||
const actionButtons = [
|
||||
|
|
@ -761,36 +842,50 @@ function buildMainMenuSurface(
|
|||
command("aetheria.main_menu.root.show_settings", "Settings"),
|
||||
command("aetheria.main_menu.root.quit", "Quit"),
|
||||
],
|
||||
gravitySurface("aetheria.main_menu.root.gravity"),
|
||||
node(
|
||||
"aetheria.main_menu.root.menu",
|
||||
"column",
|
||||
{},
|
||||
{
|
||||
position: "relative",
|
||||
padding: "7.25rem 0 0 6.75rem",
|
||||
gap: "1.1rem",
|
||||
width: "44rem",
|
||||
maxWidth: "calc(100vw - 3rem)",
|
||||
minHeight: "100vh",
|
||||
alignItems: "flex-start",
|
||||
},
|
||||
{ color: "#e9fbff" },
|
||||
text("aetheria.main_menu.root.title", "AETHERIA", "text.title", { margin: "0 0 -1.6rem 0" }, {
|
||||
font: "100 5.9rem/0.98 Montserrat, sans-serif",
|
||||
color: "rgba(232, 250, 255, 0.94)",
|
||||
whiteSpace: "nowrap",
|
||||
}),
|
||||
text("aetheria.main_menu.root.subtitle", "STARBRIDGE", "text.subtitle", { margin: "0 0 0.35rem 16.8rem" }, {
|
||||
font: "100 2.6rem/1 Montserrat, sans-serif",
|
||||
color: "rgba(232, 250, 255, 0.9)",
|
||||
whiteSpace: "nowrap",
|
||||
}),
|
||||
buttonColumn("aetheria.main_menu.root.actions", ...actionButtons),
|
||||
),
|
||||
menuPanel(
|
||||
"aetheria.main_menu.root",
|
||||
[
|
||||
text("aetheria.main_menu.root.title", "AETHERIA", "text.title", { margin: "0 0 -1.6rem 0" }, mainMenuTitleStyle("5.9rem")),
|
||||
text("aetheria.main_menu.root.subtitle", "STARBRIDGE", "text.subtitle", { margin: "0 0 0.35rem 16.8rem" }, {
|
||||
font: "100 2.6rem/1 Montserrat, sans-serif",
|
||||
color: "rgba(232, 250, 255, 0.9)",
|
||||
whiteSpace: "nowrap",
|
||||
}),
|
||||
buttonColumn("aetheria.main_menu.root.actions", ...actionButtons),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeMainMenuSurfaceId(surfaceId: string): string {
|
||||
switch (surfaceId) {
|
||||
case "aetheria.main_menu.shell":
|
||||
case "":
|
||||
return "aetheria.main_menu.root";
|
||||
case "aetheria.main_menu.root":
|
||||
case "aetheria.main_menu.settings":
|
||||
case "aetheria.main_menu.input_settings":
|
||||
case "aetheria.main_menu.player_settings":
|
||||
case "aetheria.main_menu.verse_settings":
|
||||
return surfaceId;
|
||||
default:
|
||||
return "aetheria.main_menu.root";
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
|
|
@ -852,6 +947,26 @@ 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,
|
||||
|
|
@ -866,18 +981,44 @@ function metric(id: string, label: string, value: string): AetheriaMenuSurfaceCo
|
|||
return node(id, "metric", { label, value });
|
||||
}
|
||||
|
||||
function menuPanel(id: string, children: AetheriaMenuSurfaceComponent[]): AetheriaMenuSurfaceComponent {
|
||||
return node(
|
||||
`${id}.menu`,
|
||||
"column",
|
||||
{},
|
||||
{
|
||||
position: "relative",
|
||||
padding: "7.25rem 0 0 6.75rem",
|
||||
gap: "1.1rem",
|
||||
width: "44rem",
|
||||
maxWidth: "calc(100vw - 3rem)",
|
||||
minHeight: "100vh",
|
||||
alignItems: "flex-start",
|
||||
},
|
||||
{ color: "#e9fbff", background: "rgba(0,0,0,0)" },
|
||||
...children);
|
||||
}
|
||||
|
||||
function mainMenuTitleStyle(fontSize: string): Record<string, string> {
|
||||
return {
|
||||
font: `100 ${fontSize}/0.98 Montserrat, sans-serif`,
|
||||
color: "rgba(232, 250, 255, 0.94)",
|
||||
whiteSpace: "nowrap",
|
||||
};
|
||||
}
|
||||
|
||||
function button(id: string, label: string, commandId: string): AetheriaMenuSurfaceComponent {
|
||||
return node(
|
||||
id,
|
||||
"control.button",
|
||||
{ label, command: commandId },
|
||||
{ minWidth: "0", padding: "0.04rem 0" },
|
||||
{ minWidth: "0", width: "220px", height: "32px", padding: "0" },
|
||||
{
|
||||
background: "rgba(0, 0, 0, 0)",
|
||||
borderWidth: "0",
|
||||
borderStyle: "solid",
|
||||
boxShadow: "none",
|
||||
font: "400 1.55rem/1.2 Ubuntu, sans-serif",
|
||||
font: "400 1.55rem/32px Ubuntu, sans-serif",
|
||||
color: "#e8fbff",
|
||||
textAlign: "left",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1916,6 +1916,7 @@ export type AetheriaMenuSurfaceRequest = {
|
|||
surfaceId?: string;
|
||||
inGame?: boolean;
|
||||
canOpenRuntimeInputScreen?: boolean;
|
||||
panelOnly?: boolean;
|
||||
};
|
||||
|
||||
export type AetheriaEveSurfaceRequest = {
|
||||
|
|
|
|||
|
|
@ -485,6 +485,7 @@ export type AetheriaMenuSurfaceRequest = {
|
|||
surfaceId?: string;
|
||||
inGame?: boolean;
|
||||
canOpenRuntimeInputScreen?: boolean;
|
||||
panelOnly?: boolean;
|
||||
};
|
||||
|
||||
export type AetheriaEveSurfaceRequest = {
|
||||
|
|
@ -1154,6 +1155,7 @@ export type AetheriaMenuSurfaceRequest = {
|
|||
surfaceId?: string;
|
||||
inGame?: boolean;
|
||||
canOpenRuntimeInputScreen?: boolean;
|
||||
panelOnly?: boolean;
|
||||
};
|
||||
|
||||
export type AetheriaEveSurfaceRequest = {
|
||||
|
|
|
|||
|
|
@ -25,11 +25,7 @@ body {
|
|||
|
||||
body.main-menu-mode {
|
||||
-webkit-app-region: drag;
|
||||
background:
|
||||
radial-gradient(circle at 76% 66%, rgba(255, 213, 112, 0.74) 0, rgba(247, 121, 88, 0.36) 13%, transparent 31%),
|
||||
radial-gradient(circle at 61% 37%, rgba(240, 45, 201, 0.54) 0, rgba(176, 38, 174, 0.28) 19%, transparent 45%),
|
||||
radial-gradient(circle at 40% 45%, rgba(145, 21, 122, 0.42) 0, rgba(54, 8, 41, 0.4) 34%, transparent 62%),
|
||||
#050407;
|
||||
background: #020606;
|
||||
}
|
||||
|
||||
body.main-menu-mode .shell {
|
||||
|
|
@ -100,22 +96,15 @@ body.eve-game-mode .window-control-close:focus-visible {
|
|||
|
||||
body.main-menu-mode .main-menu-host {
|
||||
-webkit-app-region: no-drag;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
display: block;
|
||||
min-height: 100%;
|
||||
padding: 128px 96px;
|
||||
background:
|
||||
radial-gradient(ellipse at 78% 64%, rgba(255, 194, 95, 0.36) 0, rgba(255, 148, 92, 0.18) 16%, transparent 35%),
|
||||
radial-gradient(ellipse at 59% 38%, rgba(238, 56, 216, 0.28) 0, rgba(164, 37, 160, 0.2) 22%, transparent 52%),
|
||||
linear-gradient(90deg, rgba(0, 0, 0, 0.5), transparent 48%);
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
body.main-menu-mode .eve-surface {
|
||||
width: 720px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
body.main-menu-mode .eve-text-title,
|
||||
|
|
|
|||
Loading…
Reference in a new issue