mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-06-13 18:31:16 +08:00
146 lines
4.7 KiB
JavaScript
146 lines
4.7 KiB
JavaScript
// UI-only label formatting for ST-BME.
|
|
//
|
|
// Keep this module on the frontend boundary: these labels are translated for
|
|
// human-facing UI only. Do not import it from prompt/model/persistence paths.
|
|
|
|
import { normalizeMemoryScope } from "../graph/memory-scope.js";
|
|
import {
|
|
normalizeStoryTime,
|
|
normalizeStoryTimeSpan,
|
|
} from "../graph/story-timeline.js";
|
|
import { t } from "../i18n/index.js";
|
|
|
|
const UI_MEMORY_SCOPE_LAYER = Object.freeze({
|
|
OBJECTIVE: "objective",
|
|
POV: "pov",
|
|
});
|
|
|
|
const UI_MEMORY_SCOPE_OWNER_TYPE = Object.freeze({
|
|
CHARACTER: "character",
|
|
USER: "user",
|
|
});
|
|
|
|
export function uiMemoryNodeTypeClass(type) {
|
|
switch (type) {
|
|
case "pov_memory":
|
|
case "character":
|
|
return "type-character";
|
|
case "event":
|
|
return "type-event";
|
|
case "location":
|
|
return "type-location";
|
|
case "rule":
|
|
return "type-rule";
|
|
case "thread":
|
|
return "type-thread";
|
|
default:
|
|
return "type-default";
|
|
}
|
|
}
|
|
|
|
export function uiTypeLabel(type) {
|
|
const normalized = String(type || "").trim();
|
|
if (!normalized) return "—";
|
|
const key = `memory.type.${normalized}`;
|
|
const translated = t(key);
|
|
return translated === key ? normalized : translated;
|
|
}
|
|
|
|
export function normalizeOwnerUiType(ownerType = "") {
|
|
const normalized = String(ownerType || "").trim();
|
|
if (normalized === "user") return "user";
|
|
if (normalized === "character") return "character";
|
|
return "";
|
|
}
|
|
|
|
export function uiOwnerTypeLabel(ownerType = "") {
|
|
const normalizedType = normalizeOwnerUiType(ownerType);
|
|
if (normalizedType === "user") return t("scope.owner.user");
|
|
if (normalizedType === "character") return t("scope.owner.character");
|
|
return "Owner";
|
|
}
|
|
|
|
export function uiScopeBadgeText(scope) {
|
|
const normalized = normalizeMemoryScope(scope);
|
|
if (normalized.layer === UI_MEMORY_SCOPE_LAYER.POV) {
|
|
const ownerLabel = normalized.ownerName || normalized.ownerId || "POV";
|
|
return normalized.ownerType === UI_MEMORY_SCOPE_OWNER_TYPE.USER
|
|
? t("scope.badge.userPov", { owner: ownerLabel })
|
|
: t("scope.badge.characterPov", { owner: ownerLabel });
|
|
}
|
|
return normalized.regionPrimary
|
|
? t("scope.badge.objectiveRegion", { region: normalized.regionPrimary })
|
|
: t("scope.badge.objectiveGlobal");
|
|
}
|
|
|
|
export function uiBuildRegionLine(scope) {
|
|
const normalized = normalizeMemoryScope(scope);
|
|
const regionPath = Array.isArray(normalized.regionPath)
|
|
? normalized.regionPath.filter(Boolean)
|
|
: [];
|
|
const regionSecondary = Array.isArray(normalized.regionSecondary)
|
|
? normalized.regionSecondary.filter(Boolean)
|
|
: [];
|
|
const parts = [];
|
|
if (normalized.regionPrimary) {
|
|
parts.push(t("scope.region.primary", { region: normalized.regionPrimary }));
|
|
}
|
|
if (regionPath.length > 0) {
|
|
parts.push(t("scope.region.path", { path: regionPath.join(" / ") }));
|
|
}
|
|
if (regionSecondary.length > 0) {
|
|
parts.push(t("scope.region.secondary", { regions: regionSecondary.join(", ") }));
|
|
}
|
|
return parts.join(" | ");
|
|
}
|
|
|
|
export function uiDescribeStoryTimeDisplay(storyTime = {}) {
|
|
const normalized = normalizeStoryTime(storyTime);
|
|
const parts = [];
|
|
if (normalized.arc) parts.push(normalized.arc);
|
|
if (normalized.chapter) parts.push(normalized.chapter);
|
|
if (normalized.scene) parts.push(normalized.scene);
|
|
return parts.join(" / ");
|
|
}
|
|
|
|
export function uiDescribeStoryTimeSpanDisplay(storyTimeSpan = {}) {
|
|
const normalized = normalizeStoryTimeSpan(storyTimeSpan);
|
|
const label =
|
|
normalized.startLabel &&
|
|
normalized.endLabel &&
|
|
normalized.startLabel !== normalized.endLabel
|
|
? `${normalized.startLabel} → ${normalized.endLabel}`
|
|
: normalized.startLabel || normalized.endLabel || "";
|
|
|
|
if (!label) {
|
|
return normalized.mixed ? t("storyTime.mixedTime") : "";
|
|
}
|
|
return normalized.mixed ? `${label} · ${t("storyTime.mixed")}` : label;
|
|
}
|
|
|
|
export function uiDescribeNodeStoryTimeDisplay(node = {}) {
|
|
return (
|
|
uiDescribeStoryTimeDisplay(node.storyTime) ||
|
|
uiDescribeStoryTimeSpanDisplay(node.storyTimeSpan) ||
|
|
""
|
|
);
|
|
}
|
|
|
|
export function uiBuildScopeMetaText(node = {}) {
|
|
const scope = normalizeMemoryScope(node?.scope);
|
|
const parts = [];
|
|
if (scope.layer === UI_MEMORY_SCOPE_LAYER.POV) {
|
|
const ownerLabel = scope.ownerName || scope.ownerId || t("scope.owner.unnamed");
|
|
parts.push(
|
|
scope.ownerType === UI_MEMORY_SCOPE_OWNER_TYPE.USER
|
|
? t("scope.meta.userPov", { owner: ownerLabel })
|
|
: t("scope.meta.characterPov", { owner: ownerLabel }),
|
|
);
|
|
}
|
|
const regionLine = uiBuildRegionLine(scope);
|
|
if (regionLine) parts.push(regionLine);
|
|
const storyTime = uiDescribeNodeStoryTimeDisplay(node);
|
|
if (storyTime) parts.push(t("storyTime.meta", { time: storyTime }));
|
|
return parts.join(" · ");
|
|
}
|