Lower semantic rosters in browser clients
This commit is contained in:
parent
91277a6845
commit
698f3672e0
5 changed files with 139 additions and 3 deletions
|
|
@ -47,6 +47,12 @@ export interface EveCommandIntent {
|
|||
issuedAt: string;
|
||||
clientId: string;
|
||||
}
|
||||
export interface EveSemanticListItem {
|
||||
label: string;
|
||||
status: string;
|
||||
detail: string;
|
||||
badges: string[];
|
||||
}
|
||||
export interface EveSurfaceWorldInteraction {
|
||||
commandBoundary?: string;
|
||||
receiptSchema?: string;
|
||||
|
|
@ -145,6 +151,7 @@ export interface EveProjectedWorldEntity {
|
|||
}
|
||||
export declare function renderEveSurface(surface: EveSurfaceDocument, host: HTMLElement, options?: EveBrowserLoweringOptions): HTMLElement;
|
||||
export declare function renderEveComponent(node: EveSurfaceComponent, options?: EveBrowserLoweringOptions): HTMLElement;
|
||||
export declare function projectSemanticListItem(node: EveSurfaceComponent): EveSemanticListItem;
|
||||
export declare function projectWorldScene(node: EveSurfaceComponent): EveProjectedWorldEntity[];
|
||||
export declare function createWorldActionIntent(command: string, action: Record<string, unknown>, options?: EveBrowserLoweringOptions): EveCommandIntent;
|
||||
export declare function applyEveSurfaceStyles(styles: EveSurfaceStyles | undefined, body?: HTMLElement): void;
|
||||
|
|
|
|||
34
packages/eve-browser-lowering/dist/index.js
vendored
34
packages/eve-browser-lowering/dist/index.js
vendored
|
|
@ -294,9 +294,10 @@ export function renderEveComponent(node, options = currentOptions) {
|
|||
pane.append(renderEveComponent(child, options));
|
||||
return pane;
|
||||
}
|
||||
if (kind === "list") {
|
||||
if (kind === "list" || kind.endsWith(".roster")) {
|
||||
const list = el("section", "cultui-list");
|
||||
assignId(list, node);
|
||||
list.dataset.componentKind = kind;
|
||||
const title = stringProp(props.title, "");
|
||||
if (title)
|
||||
list.append(el("h3", "cultui-list-title", title));
|
||||
|
|
@ -304,6 +305,28 @@ export function renderEveComponent(node, options = currentOptions) {
|
|||
list.append(renderEveComponent(child, options));
|
||||
return list;
|
||||
}
|
||||
if (kind === "list.item" || kind.endsWith(".item")) {
|
||||
const semantic = projectSemanticListItem(node);
|
||||
const item = el("article", "cultui-list-item");
|
||||
assignId(item, node);
|
||||
item.dataset.componentKind = kind;
|
||||
const heading = el("div", "cultui-list-item-heading");
|
||||
heading.append(el("strong", "cultui-list-item-label", semantic.label));
|
||||
if (semantic.status)
|
||||
heading.append(el("span", "cultui-list-item-status", semantic.status));
|
||||
item.append(heading);
|
||||
if (semantic.detail)
|
||||
item.append(el("div", "cultui-list-item-detail", semantic.detail));
|
||||
if (semantic.badges.length) {
|
||||
const badgeRow = el("div", "cultui-list-item-badges");
|
||||
for (const badge of semantic.badges)
|
||||
badgeRow.append(el("span", "cultui-list-item-badge", badge));
|
||||
item.append(badgeRow);
|
||||
}
|
||||
for (const child of children)
|
||||
item.append(renderEveComponent(child, options));
|
||||
return item;
|
||||
}
|
||||
if (kind === "bar") {
|
||||
const bar = el("div", "cultui-meter");
|
||||
assignId(bar, node);
|
||||
|
|
@ -521,6 +544,15 @@ export function renderEveComponent(node, options = currentOptions) {
|
|||
fallback.append(renderEveComponent(child, options));
|
||||
return fallback;
|
||||
}
|
||||
export function projectSemanticListItem(node) {
|
||||
const props = objectProps(node.props);
|
||||
return {
|
||||
label: firstString(props.label, props.title, node.text, node.kind, "item"),
|
||||
status: firstString(props.status, ""),
|
||||
detail: firstString(props.detail, props.phase, ""),
|
||||
badges: firstString(props.badges, "").split(",").map(value => value.trim()).filter(Boolean),
|
||||
};
|
||||
}
|
||||
function applyGeneratedLayout(element, layout, style = {}) {
|
||||
const display = firstString(layout.display, "");
|
||||
if (display)
|
||||
|
|
|
|||
|
|
@ -48,6 +48,13 @@ export interface EveCommandIntent {
|
|||
clientId: string;
|
||||
}
|
||||
|
||||
export interface EveSemanticListItem {
|
||||
label: string;
|
||||
status: string;
|
||||
detail: string;
|
||||
badges: string[];
|
||||
}
|
||||
|
||||
export interface EveSurfaceWorldInteraction {
|
||||
commandBoundary?: string;
|
||||
receiptSchema?: string;
|
||||
|
|
@ -450,15 +457,35 @@ export function renderEveComponent(
|
|||
return pane;
|
||||
}
|
||||
|
||||
if (kind === "list") {
|
||||
if (kind === "list" || kind.endsWith(".roster")) {
|
||||
const list = el("section", "cultui-list");
|
||||
assignId(list, node);
|
||||
list.dataset.componentKind = kind;
|
||||
const title = stringProp(props.title, "");
|
||||
if (title) list.append(el("h3", "cultui-list-title", title));
|
||||
for (const child of children) list.append(renderEveComponent(child, options));
|
||||
return list;
|
||||
}
|
||||
|
||||
if (kind === "list.item" || kind.endsWith(".item")) {
|
||||
const semantic = projectSemanticListItem(node);
|
||||
const item = el("article", "cultui-list-item");
|
||||
assignId(item, node);
|
||||
item.dataset.componentKind = kind;
|
||||
const heading = el("div", "cultui-list-item-heading");
|
||||
heading.append(el("strong", "cultui-list-item-label", semantic.label));
|
||||
if (semantic.status) heading.append(el("span", "cultui-list-item-status", semantic.status));
|
||||
item.append(heading);
|
||||
if (semantic.detail) item.append(el("div", "cultui-list-item-detail", semantic.detail));
|
||||
if (semantic.badges.length) {
|
||||
const badgeRow = el("div", "cultui-list-item-badges");
|
||||
for (const badge of semantic.badges) badgeRow.append(el("span", "cultui-list-item-badge", badge));
|
||||
item.append(badgeRow);
|
||||
}
|
||||
for (const child of children) item.append(renderEveComponent(child, options));
|
||||
return item;
|
||||
}
|
||||
|
||||
if (kind === "bar") {
|
||||
const bar = el("div", "cultui-meter");
|
||||
assignId(bar, node);
|
||||
|
|
@ -679,6 +706,16 @@ export function renderEveComponent(
|
|||
return fallback;
|
||||
}
|
||||
|
||||
export function projectSemanticListItem(node: EveSurfaceComponent): EveSemanticListItem {
|
||||
const props = objectProps(node.props);
|
||||
return {
|
||||
label: firstString(props.label, props.title, node.text, node.kind, "item"),
|
||||
status: firstString(props.status, ""),
|
||||
detail: firstString(props.detail, props.phase, ""),
|
||||
badges: firstString(props.badges, "").split(",").map(value => value.trim()).filter(Boolean),
|
||||
};
|
||||
}
|
||||
|
||||
function applyGeneratedLayout(
|
||||
element: HTMLElement,
|
||||
layout: Record<string, unknown>,
|
||||
|
|
|
|||
|
|
@ -518,6 +518,45 @@ body[data-pixel-art="true"] .cultui-background {
|
|||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.cultui-list-item {
|
||||
display: grid;
|
||||
gap: 5px;
|
||||
min-width: 0;
|
||||
padding: 8px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 6px;
|
||||
background: var(--panel);
|
||||
}
|
||||
|
||||
.cultui-list-item-heading,
|
||||
.cultui-list-item-badges {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.cultui-list-item-label {
|
||||
min-width: 0;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.cultui-list-item-status,
|
||||
.cultui-list-item-badge {
|
||||
padding: 2px 5px;
|
||||
border: 1px solid var(--line-hot);
|
||||
border-radius: 4px;
|
||||
color: var(--quiet);
|
||||
font: 600 10px var(--font-mono);
|
||||
}
|
||||
|
||||
.cultui-list-item-detail {
|
||||
color: var(--quiet);
|
||||
font-size: 12px;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.cultui-avatar-card {
|
||||
flex: 0 0 104px;
|
||||
display: grid;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import test from "node:test";
|
||||
import { createEveCommandIntent, createWorldActionIntent, normalizeFieldsDocument, projectWorldScene, resolveRequiredPluginAdapters, selectAdvertisedSurface } from "../dist/index.js";
|
||||
import { createEveCommandIntent, createWorldActionIntent, normalizeFieldsDocument, projectSemanticListItem, projectWorldScene, resolveRequiredPluginAdapters, selectAdvertisedSurface } from "../dist/index.js";
|
||||
|
||||
test("selects the requested surface from provider authority", () => {
|
||||
const provider = {
|
||||
|
|
@ -60,6 +60,27 @@ test("projects provider world entities into a normalized tactical plane", () =>
|
|||
assert.ok(entities.every(entity => entity.yPercent >= 6 && entity.yPercent <= 94));
|
||||
});
|
||||
|
||||
test("projects unknown semantic items through the generic list contract", () => {
|
||||
const item = projectSemanticListItem({
|
||||
id: "worker.one",
|
||||
kind: "agent.item",
|
||||
props: {
|
||||
label: "Foundry Tug",
|
||||
status: "working",
|
||||
detail: "Hauling ore",
|
||||
badges: "haul, tow, explore",
|
||||
providerSpecificState: "remains provider-owned",
|
||||
},
|
||||
});
|
||||
|
||||
assert.deepEqual(item, {
|
||||
label: "Foundry Tug",
|
||||
status: "working",
|
||||
detail: "Hauling ore",
|
||||
badges: ["haul", "tow", "explore"],
|
||||
});
|
||||
});
|
||||
|
||||
test("command intents use the active surface advertisement boundary", () => {
|
||||
const intent = createEveCommandIntent("aetheria.daemon.editor.inspect", {
|
||||
action: {
|
||||
|
|
|
|||
Loading…
Reference in a new issue