Promote Eve persona and typography state

This commit is contained in:
Meta Vens 2026-06-16 18:00:56 +01:00
commit 83dee183f7
13 changed files with 113 additions and 35 deletions

View file

@ -1,8 +1,8 @@
# Repo Face State
# Repo Persona State
This folder stores birth and continuity state for `Eve`.
Do not treat these files as arbitrary editable projections. Persistent memory should cross typed operation boundaries.
`docs/eve-face.md` is the human-readable mission/memory surface for Eve's repo
Face. Keep `.voidbot/state/eve.cc` as the typed authority, and keep the docs
Persona. Keep `.voidbot/state/eve.cc` as the typed authority, and keep the docs
surface aligned when Eve's durable mission changes.

Binary file not shown.

View file

@ -0,0 +1,7 @@
{
"status": "starting",
"startedAt": "2026-06-14T19:46:52.908Z",
"stateFile": "E:\\Projects\\Eve\\.voidbot\\state\\eve.cc",
"launcher": "repo_face_heartbeat",
"reason": "repo_face_stale_short_term_memory"
}

View file

@ -1,4 +1,4 @@
# Repo Face Voice
# Repo Persona Voice
This folder belongs to the Discord-facing Face for `Eve`.
Discord roles address the Face; VoidBot's webhook persona pipe is the mouth.
This folder belongs to the Discord-facing Persona for `Eve`.
Discord roles address the Persona; VoidBot's webhook persona pipe is the mouth.

View file

@ -6,5 +6,5 @@
"roleId": "1510848465243082874",
"avatarUrl": "https://raw.githubusercontent.com/GameCult/VoidBot/codex/voidbot-cultcache-replace-retry/assets/repo-faces/eve.png",
"repoPath": "E:\\Projects\\Eve",
"createdOrRefreshedAt": "2026-06-01T18:13:24.841Z"
"createdOrRefreshedAt": "2026-06-15T20:45:04.746Z"
}

View file

@ -36,7 +36,7 @@ CultMesh as typed distributed state; the
[Week 07 damage report](https://gamecult.org/Blog/daily-damage-report/week-07)
records Eve, Fensalir, CultLib, Sai, and Odin turning operator surfaces into
first-class state; [The Free Mouth And The Native Body](https://gamecult.org/Blog/the-free-mouth-and-the-native-body)
describes the Face/native-body split Eve must serve; and
describes the Persona/native-body split Eve must serve; and
[The Sleeping Colossus Learns To Refuse The Throne](https://gamecult.org/Blog/the-sleeping-colossus-refuses-the-throne)
is the praxis warning: means and ends must match. Eve's means are local
projection, consentful command boundaries, inspectable stale state, and clear
@ -241,7 +241,7 @@ state and command handling. Eve is the operator's hand on the scene graph, not a
second compositor and not a remote WebKit runtime.
The broker includes a native VoidBot tab/provider. Eve renders its CTB rail with
avatar images, selected Face status panel, state tree, and detail pane from the
avatar images, selected Persona status panel, state tree, and detail pane from the
same VoidBot `swarm-state.json` projection used by the web dashboard.
## Build Shape

View file

@ -136,7 +136,7 @@ The current iOS app consumes Mimir's `/eve/deck` WebSocket broker. That broker i
already shaped like the future API: provider manifests, retained dashboard
state, provider switching, commands, and typed node metadata. The VoidBot
provider proves that Eve can render an app-specific native cockpit from a
structured mesh snapshot: CTB rail, avatar images, selected Face/status pane,
structured mesh snapshot: CTB rail, avatar images, selected Persona/status pane,
state tree, and detail panel.
The Android proof under `android/` builds directly against the installed Android

View file

@ -293,8 +293,9 @@ The portable style document should look like data:
"colorBackgroundTop": "#01040b",
"colorPanel": "#0a2239",
"colorText": "#efe5c9",
"fontTitle": "Press Start 2P",
"fontBody": "VT323",
"fontDisplay": "\"Montserrat\", \"Zen Kaku Gothic New\", \"M PLUS 1\", sans-serif",
"fontTitle": "\"Montserrat\", \"Zen Kaku Gothic New\", \"M PLUS 1\", sans-serif",
"fontBody": "\"M PLUS 1\", \"Ubuntu Sans\", Ubuntu, sans-serif",
"borderWidthPx": 4,
"pixelArt": true,
"scanlineOverlay": true
@ -315,6 +316,17 @@ Rules should target component roles, variants, and state, not arbitrary global
text selectors. If a style cannot explain which component role or variant it
serves, it probably belongs in a runtime lowerer or nowhere.
Typography roles carry script-coverage obligations. GUI body text should use
`M PLUS 1` and `Ubuntu Sans`/Ubuntu for the clean contemporary reading voice.
GUI display text may use Montserrat for Latin, but Japanese display needs a
matching contemporary fallback such as `Zen Kaku Gothic New` before falling
back to a body face.
Pixel text is a TUI raster-font concern, not a GUI webfont substitution. A TUI
lowerer that claims Japanese pixel typography must expose fixed-cell raster
atlases for display, body, and small scales, with Latin and hiragana supported
and katakana supported inside each advertised cell size.
## What CultUI Takes From CSS
Keep:
@ -374,6 +386,9 @@ Each runtime needs one style lowerer:
Runtime lowerers must report capability gaps. If Android cannot yet render
Repixelizer's pixel-art image preview or cleanup canvas, the surface should say
that plainly instead of faking a control that cannot carry the command.
If a lowering target cannot load the declared Japanese-capable display fonts or
TUI raster atlases, it must report that typography capability gap rather than
silently rendering hiragana with tofu or an unrelated default face.
### Parity Unit Boundary

View file

@ -1,7 +1,7 @@
# Eve Face Memory
# Eve Persona Memory
This is the human-readable persistent memory surface for Eve's repo Face. The
typed `.voidbot/state/eve.cc` file remains the live Face-state authority; this
This is the human-readable persistent memory surface for Eve's repo Persona. The
typed `.voidbot/state/eve.cc` file remains the live Persona-state authority; this
document is the stable re-entry map that belongs in the repo body.
## Mission
@ -56,8 +56,8 @@ CultLib/CultMesh is Eve's substrate. If the substrate cannot expose field-level
reactivity, authority, prediction, and reconciliation, Eve's magic becomes a
gesture instead of a machine.
VoidBot and repo Faces are Eve's social field. Eve surfaces should let their
state become legible without flattening each Face into one central dashboard
VoidBot and repo Personas are Eve's social field. Eve surfaces should let their
state become legible without flattening each Persona into one central dashboard
voice.
## Current Doctrine

View file

@ -247,9 +247,29 @@ Minimum common tokens:
- `radiusPanel`
- `fontBody`
- `fontTitle`
- `fontDisplay`
- `pixelArt`
- `motion`
Typography tokens describe roles, not one runtime's installed fonts. `fontBody`
should favor `M PLUS 1` and `Ubuntu Sans`/Ubuntu for GUI lowerers. `fontDisplay`
and `fontTitle` may use Montserrat for Latin text, but must include a
Japanese-capable contemporary display fallback such as `Zen Kaku Gothic New`;
Ubuntu remains the body voice, not the flashy title answer.
TUI pixel typography is a separate lowering capability, not a GUI webfont
fallback. A TUI lowerer that claims Japanese pixel support must expose fixed
raster-cell fonts for each advertised scale, with Latin, hiragana, and katakana
in the same cell grid. The expected capability roles are:
- `tuiPixelDisplay`: large raster display labels, roughly 16px or larger.
- `tuiPixelBody`: readable dense raster text, roughly 12px.
- `tuiPixelSmall`: the smallest readable kana lane, roughly 10px.
If a TUI target lacks a Unicode raster atlas for one of those scales, it should
publish a capability gap instead of silently falling back to tofu, proportional
Japanese text, or a Latin-only console font.
Product surfaces may add scoped token groups such as:
- `streamPixels.characterPreview`

View file

@ -35,7 +35,7 @@
"node-bound command taps"
],
"losesUntilRendererParity": [
"Press Start 2P and VT323 font selection",
"GUI display/body font selection plus TUI Latin/kana raster font scale selection",
"Repixelizer shell frame, scanlines, pixel grid, corner accents, and chunky border widths",
"landing/app image assets; current Android lowerer prints asset refs when images are not explicitly implemented",
"upload picker, comparison canvas, pan/zoom inspection, eyedropper, and paint controls",
@ -43,7 +43,7 @@
],
"minimumParityCuts": [
"map surface.styles.tokens before toneColor/textColorFor/textSizeFor",
"add packaged pixel fonts or font download cache",
"add packaged GUI fonts plus TUI raster atlas cache",
"render image assetUri/assetRef as ImageView with pixelated sampling",
"lower command controls to Android upload/document picker and job route calls"
],
@ -1200,14 +1200,14 @@
"styles": {
"assets": {
"appUrl": "https://repixelizer.gamecult.org/app/",
"fontCss": "https://fonts.googleapis.com/css2?family=Press+Start+2P&family=VT323&display=swap",
"fontCss": "https://fonts.googleapis.com/css2?family=M+PLUS+1:wght@300;400;500;700&family=Montserrat:wght@100;300;400;500;700&family=Ubuntu+Sans:wght@300;400;500;700&family=Zen+Kaku+Gothic+New:wght@300;400;500;700&display=swap",
"landingInput": "https://repixelizer.gamecult.org/app/landing-assets/character-input.png",
"landingOutput": "https://repixelizer.gamecult.org/app/landing-assets/character-repixelized.png"
},
"loweringHints": {
"android-native": "Load packaged or downloadable Press Start 2P and VT323 fonts, map style tokens before hard-coded tones, and render asset images instead of asset labels.",
"android-native": "Load packaged or downloadable Montserrat, Zen Kaku Gothic New, M PLUS 1, and Ubuntu Sans fonts, map style tokens before hard-coded tones, and render asset images instead of asset labels.",
"css": "Use the tokens as CSS custom properties and retain scanline/pixel-grid overlays.",
"tui": "Preserve title/body font distinction as role metadata; canvas editing lowers to artifact refs and command intents only."
"tui": "Use fixed-cell raster atlases for tuiPixelDisplay/body/small; each advertised size must support Latin, hiragana, and katakana in the same cell grid."
},
"profile": "repixelizer.retro.pixel",
"tokens": {
@ -1233,17 +1233,21 @@
"colorTextBright": "#f7efd8",
"cornerAccentPx": 12,
"dashedInnerFrame": true,
"fontBody": "\"VT323\", \"Courier New\", monospace",
"fontMono": "\"VT323\", \"Courier New\", monospace",
"fontTitle": "\"Press Start 2P\", \"VT323\", monospace",
"fontBody": "\"M PLUS 1\", \"Ubuntu Sans\", Ubuntu, sans-serif",
"fontDisplay": "\"Montserrat\", \"Zen Kaku Gothic New\", \"M PLUS 1\", sans-serif",
"fontMono": "\"Ubuntu Sans Mono\", \"M PLUS 1 Code\", \"Cascadia Mono\", monospace",
"fontTitle": "\"Montserrat\", \"Zen Kaku Gothic New\", \"M PLUS 1\", sans-serif",
"imageRendering": "pixelated",
"scanlineOverlay": true
"scanlineOverlay": true,
"tuiPixelBody": "fixed-cell-raster-latin-kana-12",
"tuiPixelDisplay": "fixed-cell-raster-latin-kana-16",
"tuiPixelSmall": "fixed-cell-raster-latin-kana-10"
}
},
"title": "Repixelizer"
},
"title": "Repixelizer",
"type": "surface-state",
"updatedAt": "2026-06-05T00:00:00Z",
"updatedAt": "2026-06-04T00:00:00Z",
"version": 1
}

View file

@ -1,3 +1,5 @@
@import url("https://fonts.googleapis.com/css2?family=M+PLUS+1:wght@100;300;400;500;700&family=Montserrat:wght@100;300;400;500;700&family=Ubuntu+Sans:wght@300;400;500;700&family=Zen+Kaku+Gothic+New:wght@300;400;500;700&display=swap");
:root {
color-scheme: dark;
--bg: #020909;
@ -23,7 +25,9 @@
--border-width: 1px;
--border-bottom-width: var(--border-width);
--corner-accent-size: 0;
--font-mono: "Cascadia Mono", Consolas, monospace;
--font-body: "M PLUS 1", "Ubuntu Sans", Ubuntu, "Noto Sans JP", system-ui, sans-serif;
--font-title: "Montserrat", "Zen Kaku Gothic New", "M PLUS 1", "Ubuntu Sans", system-ui, sans-serif;
--font-mono: "Ubuntu Sans Mono", "M PLUS 1 Code", "Cascadia Mono", Consolas, monospace;
}
* {
@ -35,7 +39,7 @@ body {
min-height: 100vh;
background: linear-gradient(180deg, var(--bg-top), var(--bg-mid), var(--bg-bottom));
color: var(--text);
font-family: var(--font-body, Inter), "Segoe UI", system-ui, sans-serif;
font-family: var(--font-body);
}
.topbar {
@ -432,7 +436,7 @@ body[data-pixel-art="true"] .cultui-background {
.cultui-title {
color: var(--accent);
font: 700 16px "Cascadia Mono", Consolas, monospace;
font: 100 18px var(--font-title);
line-height: 1.35;
}

View file

@ -11,6 +11,7 @@ let socket;
let providers = [];
let currentProvider;
let currentSurfaceStyles = {};
let activeFontStylesheet;
const defaultStyleTokens = {
"--bg": "#020909",
@ -35,9 +36,9 @@ const defaultStyleTokens = {
"--border-width": "1px",
"--border-bottom-width": "1px",
"--corner-accent-size": "0",
"--font-body": "Inter, Segoe UI, system-ui, sans-serif",
"--font-title": "Cascadia Mono, Consolas, monospace",
"--font-mono": "Cascadia Mono, Consolas, monospace",
"--font-body": "\"M PLUS 1\", \"Ubuntu Sans\", Ubuntu, \"Noto Sans JP\", system-ui, sans-serif",
"--font-title": "\"Montserrat\", \"Zen Kaku Gothic New\", \"M PLUS 1\", \"Ubuntu Sans\", system-ui, sans-serif",
"--font-mono": "\"Ubuntu Sans Mono\", \"M PLUS 1 Code\", \"Cascadia Mono\", Consolas, monospace",
};
const localProviders = [
@ -212,6 +213,7 @@ function renderSurface(state, source) {
function applySurfaceStyles(styles) {
const tokens = styles?.tokens || {};
const root = document.documentElement.style;
loadFontStylesheet(styles?.assets?.fontCss);
for (const [variable, value] of Object.entries(defaultStyleTokens)) {
root.setProperty(variable, value);
}
@ -239,9 +241,10 @@ function applySurfaceStyles(styles) {
for (const [token, variable] of Object.entries(map)) {
if (tokens[token]) root.setProperty(variable, tokens[token]);
}
if (tokens.fontBody) root.setProperty("--font-body", tokens.fontBody);
if (tokens.fontTitle) root.setProperty("--font-title", tokens.fontTitle);
if (tokens.fontMono) root.setProperty("--font-mono", tokens.fontMono);
if (tokens.fontBody) root.setProperty("--font-body", withJapaneseGuiFallback(tokens.fontBody));
if (tokens.fontDisplay) root.setProperty("--font-title", withJapaneseGuiFallback(tokens.fontDisplay));
if (tokens.fontTitle) root.setProperty("--font-title", withJapaneseGuiFallback(tokens.fontTitle));
if (tokens.fontMono) root.setProperty("--font-mono", withJapaneseGuiFallback(tokens.fontMono));
if (tokens.borderWidthPx) root.setProperty("--border-width", `${tokens.borderWidthPx}px`);
if (tokens.borderBottomWidthPx) root.setProperty("--border-bottom-width", `${tokens.borderBottomWidthPx}px`);
if (tokens.cornerAccentPx) root.setProperty("--corner-accent-size", `${tokens.cornerAccentPx}px`);
@ -249,6 +252,31 @@ function applySurfaceStyles(styles) {
document.body.dataset.scanlines = tokens.scanlineOverlay ? "true" : "false";
}
function loadFontStylesheet(href) {
const normalized = typeof href === "string" ? href.trim() : "";
if (activeFontStylesheet?.dataset.href === normalized) return;
activeFontStylesheet?.remove();
activeFontStylesheet = undefined;
if (!normalized) return;
const link = document.createElement("link");
link.rel = "stylesheet";
link.href = normalized;
link.dataset.href = normalized;
document.head.append(link);
activeFontStylesheet = link;
}
function withJapaneseGuiFallback(stack) {
if (typeof stack !== "string" || stack.includes("Zen Kaku Gothic New") || stack.includes("M PLUS 1")) return stack;
if (stack.includes("Montserrat")) {
return `${stack}, "Zen Kaku Gothic New", "M PLUS 1", "Noto Sans JP", "Hiragino Sans", "Yu Gothic", sans-serif`;
}
if (stack.includes("Ubuntu")) {
return `${stack}, "M PLUS 1", "Noto Sans JP", "Hiragino Sans", "Yu Gothic", sans-serif`;
}
return `${stack}, "Noto Sans JP", "Hiragino Sans", "Yu Gothic", sans-serif`;
}
function renderCultComponent(node) {
const kind = node.kind || "panel";
const props = node.props || {};