feat(06-05): the 8-glyph committed maneuver set + glyphs.ts + the 18 guidance strings keys

- 8 hand-authored 32x32 SVGs per the UI-SPEC geometry table (3px stroke, square caps, orthogonal/45° only; uturn = machined staple, no arc)
- glyphs.ts: GLYPH_KINDS/GLYPHS via Vite ?raw (committed files are the single source) + svgHost — the ONE sanctioned innerHTML site, a static import value
- strings.ts: 18 ShellStrings keys appended in en + ms, values verbatim from the 06-UI-SPEC copywriting contract
- copy-gate: COPY1 floor 40→58 + COPY8 pins all 18 guidance keys verbatim in both locales
This commit is contained in:
araharah-executor
2026-10-24 14:08:35 +08:00
parent 46acbf4a53
commit 0023ab3086
11 changed files with 232 additions and 2 deletions
+71
View File
@@ -0,0 +1,71 @@
/*
* glyphs.ts — the 8-glyph committed maneuver set (06-UI-SPEC "The 8-glyph set").
* ------------------------------------------------------------------------------
* The committed SVG files under ./glyphs/ are the SINGLE SOURCE: this module
* imports them verbatim with Vite's `?raw` and never re-states their geometry.
* The same files rasterize once to 256×256 PNGs for the chat turn-cards
* (maintainer `build-glyphs.mjs`, plan 07) — one geometry, two renderings.
* In-app the raw SVG renders inline: 32×32 on the banner's arrow plate,
* 24×24 in the maneuver list. Strokes ride `currentColor` (signal-orange on
* the banner plate, matte-black/outline per list row state) — the PNG build
* injects the token literals; nothing here hardcodes a color (DSGN-01).
*
* svgHost is the shell's ONE sanctioned innerHTML site: the template parses
* a STATIC import value (GLYPHS[kind], committed at build time), never a
* name-derived or runtime string — the surface gate whitelists exactly this
* line (guidance-surface structural case).
*/
import depart from "./glyphs/depart.svg?raw";
import straight from "./glyphs/straight.svg?raw";
import left from "./glyphs/left.svg?raw";
import right from "./glyphs/right.svg?raw";
import slightLeft from "./glyphs/slight-left.svg?raw";
import slightRight from "./glyphs/slight-right.svg?raw";
import uturn from "./glyphs/uturn.svg?raw";
import arrive from "./glyphs/arrive.svg?raw";
/** The maneuver vocabulary the UI-SPEC commits (8 kinds, trip-order names —
* RouteStep.kind maps onto these in the controller). */
export type GlyphKind =
| "depart"
| "straight"
| "left"
| "right"
| "slight-left"
| "slight-right"
| "uturn"
| "arrive";
/** The 8 kinds in committed order (depart → arrive). */
export const GLYPH_KINDS: readonly GlyphKind[] = [
"depart",
"straight",
"left",
"right",
"slight-left",
"slight-right",
"uturn",
"arrive",
];
/** kind → raw SVG string of the committed file (the single source). */
export const GLYPHS: Record<GlyphKind, string> = {
depart,
straight,
left,
right,
"slight-left": slightLeft,
"slight-right": slightRight,
uturn,
arrive,
};
/** Parse a committed glyph into a live inline-SVG element. The caller sizes
* the host (banner 32×32, list 24×24); color rides currentColor. */
export function svgHost(kind: GlyphKind): SVGSVGElement {
const template = document.createElement("template");
// THE one sanctioned innerHTML site: a static import value — never a
// name-derived or runtime string (the surface gate's exact whitelist).
template.innerHTML = GLYPHS[kind];
return template.content.firstElementChild as SVGSVGElement;
}