diff --git a/dsl/extdeps/render/ansi.dag b/dsl/extdeps/render/ansi.dag index 12a4a2ddd5c..d52312169de 100644 --- a/dsl/extdeps/render/ansi.dag +++ b/dsl/extdeps/render/ansi.dag @@ -1,36 +1,57 @@ // extdeps/render/ansi.dag — ANSI escape / SGR color realization for SemanticColor. // -// Anchor: https://en.wikipedia.org/wiki/ANSI_escape_code +// Anchor: https://ecma-international.org/publications-and-standards/standards/ecma-48/ +// (ISO/IEC 6429 Select Graphic Rendition CSI sequences: ESC [ m) // -// Upstream: ECMA-48 (ISO/IEC 6429) / ANSI X3.64 — control sequences for character -// imputation. The cited escape strings are Select Graphic Rendition (SGR) CSI -// sequences (`ESC [ … m`); 256-color foreground uses `38;5;` (ISO 8613-6 -// indexed palette). Real spec names kept in the `code` field. +// Provenance: gunb.ai/pkg/fermi/colors.go — project palette-index choices per SemanticColor. // // P2 split: std.symbols owns the agnostic SemanticColor concept; the cited SGR -// escape sequences live here. Project palette index choices per semantic role -// align with gunb.ai/pkg/fermi/colors.go for consistent appearance. +// realization lives here. module extdeps.render.ansi import std.symbols { SemanticColor } +// ECMA-48 CSI framing — emitted once via emit_sgr_csi, not repeated per mapping row. +data csi_esc: String = "\x1b[" +data csi_end: String = "m" + +// ISO 8613-6 / xterm 256-color palette index (0..255). +type PaletteIndex = Int + +type SgrStyle + = SgrReset {} + | SgrDim {} + | SgrIndexedForeground { index: PaletteIndex } + type AnsiMapping { color: SemanticColor - code: String + style: SgrStyle } data ansi_mappings: List = [ - { color: Default, code: "\x1b[0m" }, - { color: Success, code: "\x1b[38;5;34m" }, - { color: Warning, code: "\x1b[38;5;208m" }, - { color: Error, code: "\x1b[38;5;196m" }, - { color: Info, code: "\x1b[38;5;39m" }, - { color: Dim, code: "\x1b[2m" }, - { color: Active, code: "\x1b[38;5;208m" }, - { color: Accent, code: "\x1b[38;5;75m" } + { color: Default, style: SgrReset {} }, + { color: Success, style: SgrIndexedForeground { index: 34 } }, + { color: Warning, style: SgrIndexedForeground { index: 208 } }, + { color: Error, style: SgrIndexedForeground { index: 196 } }, + { color: Info, style: SgrIndexedForeground { index: 39 } }, + { color: Dim, style: SgrDim {} }, + { color: Active, style: SgrIndexedForeground { index: 208 } }, + { color: Accent, style: SgrIndexedForeground { index: 75 } } ] +fn format_sgr_params(style: SgrStyle) -> String { + match style { + SgrReset {} => "0" + SgrDim {} => "2" + SgrIndexedForeground { index: n } => concat("38;5;", to_string(n)) + } +} + +fn emit_sgr_csi(style: SgrStyle) -> String { + concat(concat(csi_esc, format_sgr_params(style: style)), csi_end) +} + // Lookup via fold + structural == (filter+field-equality hits a v1 interpreter bug). fn ansi_code(c: SemanticColor) -> String? { fold(ansi_mappings, @@ -39,7 +60,7 @@ fn ansi_code(c: SemanticColor) -> String? { Present { value: _ } => acc Absent => if m.color == c { - Present { value: m.code } + Present { value: emit_sgr_csi(style: m.style) } } else { none } diff --git a/dsl/extdeps/render/ascii_glyph.dag b/dsl/extdeps/render/ascii_glyph.dag new file mode 100644 index 00000000000..4409b64f035 --- /dev/null +++ b/dsl/extdeps/render/ascii_glyph.dag @@ -0,0 +1,18 @@ +// extdeps/render/ascii_glyph.dag — ASCII-tier glyph carrier (ECMA-6 repertoire). +// +// Anchor: https://www.ecma-international.org/publications-and-standards/standards/ecma-6/ +// (ECMA-6 / ISO 646 7-bit coded character set; RFC 20 is the IETF mirror) +// +// dissolve-on: Q-Regex repertoire refinement — ASCII printable 0x20–0x7E. + +module extdeps.render.ascii_glyph + +type AsciiGlyph = String where brand("AsciiGlyph") + +fn ascii_glyph(text: String) -> AsciiGlyph { + text +} + +fn ascii_glyph_text(g: AsciiGlyph) -> String { + g +} diff --git a/dsl/extdeps/render/emoji_glyph.dag b/dsl/extdeps/render/emoji_glyph.dag new file mode 100644 index 00000000000..0d006db7816 --- /dev/null +++ b/dsl/extdeps/render/emoji_glyph.dag @@ -0,0 +1,17 @@ +// extdeps/render/emoji_glyph.dag — Emoji-tier glyph carrier (UTS #51). +// +// Anchor: https://www.unicode.org/reports/tr51/ (UTS #51 Unicode Emoji) +// +// dissolve-on: Q-Regex UTS #51 emoji-sequence validity. + +module extdeps.render.emoji_glyph + +type EmojiGlyph = String where brand("EmojiGlyph") + +fn emoji_glyph(text: String) -> EmojiGlyph { + text +} + +fn emoji_glyph_text(g: EmojiGlyph) -> String { + g +} diff --git a/dsl/extdeps/render/glyphs.dag b/dsl/extdeps/render/glyphs.dag index 0e01385ef5c..ae9e393bc35 100644 --- a/dsl/extdeps/render/glyphs.dag +++ b/dsl/extdeps/render/glyphs.dag @@ -2,11 +2,21 @@ // // Project visual vocabulary: three-tier encodings (Emoji / Unicode / Ascii) per // SymbolId. Dispatch lives in extdeps/, not std/ (DESIGN.md §3): std.symbols -// owns only the agnostic glyph identity and SymbolEntry shape. +// owns only the agnostic glyph identity vocabulary (SymbolId, Tier, SemanticColor). module extdeps.render.glyphs -import std.symbols { SemanticColor, SymbolEntry, SymbolId, Tier } +import extdeps.render.ascii_glyph { ascii_glyph } +import extdeps.render.emoji_glyph { emoji_glyph } +import extdeps.render.unicode_glyph { unicode_glyph } +import extdeps.render.tier_glyphs { TierGlyphs, glyph_at, glyphs_for_tiers } +import std.symbols { SemanticColor, SymbolId, Tier } + +type SymbolEntry { + id: SymbolId + glyphs: TierGlyphs + color: SemanticColor +} // --- Standard symbol set --- // @@ -16,60 +26,60 @@ import std.symbols { SemanticColor, SymbolEntry, SymbolId, Tier } data standard_symbols: List = [ // Node lifecycle // NOTE: emoji/unicode tiers collapsed to ASCII -- restore when tokenizer handles UTF-8 - { id: NodePending, emoji: "[ ]", unicode: "o", ascii: "[ ]", color: Dim }, - { id: NodeRunning, emoji: "[~]", unicode: "O", ascii: "[~]", color: Active }, - { id: NodeCompleted, emoji: "[x]", unicode: "*", ascii: "[x]", color: Success }, - { id: NodeFailed, emoji: "[!]", unicode: "x", ascii: "[!]", color: Error }, - { id: NodeSkipped, emoji: "[-]", unicode: "o", ascii: "[-]", color: Dim }, - { id: NodeIntercepted, emoji: "[m]", unicode: "<>", ascii: "[m]", color: Info }, + { id: NodePending, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[ ]"), unicode: unicode_glyph(text: "o"), ascii: ascii_glyph(text: "[ ]")), color: Dim }, + { id: NodeRunning, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[~]"), unicode: unicode_glyph(text: "O"), ascii: ascii_glyph(text: "[~]")), color: Active }, + { id: NodeCompleted, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[x]"), unicode: unicode_glyph(text: "*"), ascii: ascii_glyph(text: "[x]")), color: Success }, + { id: NodeFailed, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[!]"), unicode: unicode_glyph(text: "x"), ascii: ascii_glyph(text: "[!]")), color: Error }, + { id: NodeSkipped, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[-]"), unicode: unicode_glyph(text: "o"), ascii: ascii_glyph(text: "[-]")), color: Dim }, + { id: NodeIntercepted, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[m]"), unicode: unicode_glyph(text: "<>"), ascii: ascii_glyph(text: "[m]")), color: Info }, // Edge states - { id: EdgeIdle, emoji: "-", unicode: "-", ascii: "-", color: Dim }, - { id: EdgeFlowing, emoji: "=", unicode: "=", ascii: "=", color: Accent }, - { id: EdgeDone, emoji: "-", unicode: "-", ascii: "-", color: Success }, - { id: EdgeDead, emoji: ".", unicode: ".", ascii: ".", color: Dim }, + { id: EdgeIdle, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "-"), unicode: unicode_glyph(text: "-"), ascii: ascii_glyph(text: "-")), color: Dim }, + { id: EdgeFlowing, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "="), unicode: unicode_glyph(text: "="), ascii: ascii_glyph(text: "=")), color: Accent }, + { id: EdgeDone, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "-"), unicode: unicode_glyph(text: "-"), ascii: ascii_glyph(text: "-")), color: Success }, + { id: EdgeDead, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "."), unicode: unicode_glyph(text: "."), ascii: ascii_glyph(text: ".")), color: Dim }, // DAG phases - { id: DagNotStarted, emoji: "[ ]", unicode: "[ ]", ascii: "[ ]", color: Dim }, - { id: DagRunning, emoji: "[>]", unicode: ">", ascii: "[>]", color: Active }, - { id: DagCompleted, emoji: "[X]", unicode: "#", ascii: "[X]", color: Success }, - { id: DagFailed, emoji: "[!]", unicode: "#", ascii: "[!]", color: Error }, + { id: DagNotStarted, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[ ]"), unicode: unicode_glyph(text: "[ ]"), ascii: ascii_glyph(text: "[ ]")), color: Dim }, + { id: DagRunning, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[>]"), unicode: unicode_glyph(text: ">"), ascii: ascii_glyph(text: "[>]")), color: Active }, + { id: DagCompleted, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[X]"), unicode: unicode_glyph(text: "#"), ascii: ascii_glyph(text: "[X]")), color: Success }, + { id: DagFailed, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[!]"), unicode: unicode_glyph(text: "#"), ascii: ascii_glyph(text: "[!]")), color: Error }, // Structural - { id: BoundaryMarker, emoji: "[B]", unicode: "<>", ascii: "[B]", color: Info }, + { id: BoundaryMarker, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[B]"), unicode: unicode_glyph(text: "<>"), ascii: ascii_glyph(text: "[B]")), color: Info }, // Spinners (ASCII fallback -- restore braille when tokenizer handles UTF-8) - { id: Spinner0, emoji: "|", unicode: "|", ascii: "|", color: Active }, - { id: Spinner1, emoji: "/", unicode: "/", ascii: "/", color: Active }, - { id: Spinner2, emoji: "-", unicode: "-", ascii: "-", color: Active }, - { id: Spinner3, emoji: "\\", unicode: "\\", ascii: "\\", color: Active }, - { id: Spinner4, emoji: "|", unicode: "|", ascii: "|", color: Active }, - { id: Spinner5, emoji: "/", unicode: "/", ascii: "/", color: Active }, - { id: Spinner6, emoji: "-", unicode: "-", ascii: "-", color: Active }, - { id: Spinner7, emoji: "\\", unicode: "\\", ascii: "\\", color: Active }, - { id: Spinner8, emoji: "|", unicode: "|", ascii: "|", color: Active }, - { id: Spinner9, emoji: "/", unicode: "/", ascii: "/", color: Active }, + { id: Spinner0, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "|"), unicode: unicode_glyph(text: "|"), ascii: ascii_glyph(text: "|")), color: Active }, + { id: Spinner1, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "/"), unicode: unicode_glyph(text: "/"), ascii: ascii_glyph(text: "/")), color: Active }, + { id: Spinner2, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "-"), unicode: unicode_glyph(text: "-"), ascii: ascii_glyph(text: "-")), color: Active }, + { id: Spinner3, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "\\"), unicode: unicode_glyph(text: "\\"), ascii: ascii_glyph(text: "\\")), color: Active }, + { id: Spinner4, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "|"), unicode: unicode_glyph(text: "|"), ascii: ascii_glyph(text: "|")), color: Active }, + { id: Spinner5, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "/"), unicode: unicode_glyph(text: "/"), ascii: ascii_glyph(text: "/")), color: Active }, + { id: Spinner6, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "-"), unicode: unicode_glyph(text: "-"), ascii: ascii_glyph(text: "-")), color: Active }, + { id: Spinner7, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "\\"), unicode: unicode_glyph(text: "\\"), ascii: ascii_glyph(text: "\\")), color: Active }, + { id: Spinner8, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "|"), unicode: unicode_glyph(text: "|"), ascii: ascii_glyph(text: "|")), color: Active }, + { id: Spinner9, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "/"), unicode: unicode_glyph(text: "/"), ascii: ascii_glyph(text: "/")), color: Active }, // Status indicators - { id: StatusSuccess, emoji: "OK", unicode: "v", ascii: "OK", color: Success }, - { id: StatusFailure, emoji: "FAIL", unicode: "x", ascii: "FAIL", color: Error }, - { id: StatusWarning, emoji: "WARN", unicode: "!", ascii: "WARN", color: Warning }, - { id: StatusInfo, emoji: "INFO", unicode: "i", ascii: "INFO", color: Info }, + { id: StatusSuccess, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "OK"), unicode: unicode_glyph(text: "v"), ascii: ascii_glyph(text: "OK")), color: Success }, + { id: StatusFailure, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "FAIL"), unicode: unicode_glyph(text: "x"), ascii: ascii_glyph(text: "FAIL")), color: Error }, + { id: StatusWarning, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "WARN"), unicode: unicode_glyph(text: "!"), ascii: ascii_glyph(text: "WARN")), color: Warning }, + { id: StatusInfo, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "INFO"), unicode: unicode_glyph(text: "i"), ascii: ascii_glyph(text: "INFO")), color: Info }, // Data type indicators - { id: DataList, emoji: "[L]", unicode: "=", ascii: "[L]", color: Default }, - { id: DataMap, emoji: "[M]", unicode: "[+]", ascii: "[M]", color: Default }, - { id: DataSecret, emoji: "[*]", unicode: "*", ascii: "[*]", color: Warning }, - { id: DataUrl, emoji: "[U]", unicode: ">", ascii: "[U]", color: Info }, - { id: DataTimer, emoji: "[T]", unicode: "[T]", ascii: "[T]", color: Dim }, + { id: DataList, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[L]"), unicode: unicode_glyph(text: "="), ascii: ascii_glyph(text: "[L]")), color: Default }, + { id: DataMap, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[M]"), unicode: unicode_glyph(text: "[+]"), ascii: ascii_glyph(text: "[M]")), color: Default }, + { id: DataSecret, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[*]"), unicode: unicode_glyph(text: "*"), ascii: ascii_glyph(text: "[*]")), color: Warning }, + { id: DataUrl, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[U]"), unicode: unicode_glyph(text: ">"), ascii: ascii_glyph(text: "[U]")), color: Info }, + { id: DataTimer, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[T]"), unicode: unicode_glyph(text: "[T]"), ascii: ascii_glyph(text: "[T]")), color: Dim }, // Connectors - { id: ConnectorHorizontal, emoji: "--", unicode: "--", ascii: "--", color: Dim }, - { id: ConnectorVertical, emoji: "|", unicode: "|", ascii: "|", color: Dim }, - { id: ConnectorTeeDown, emoji: "+", unicode: "+", ascii: "+", color: Dim }, - { id: ConnectorTeeUp, emoji: "+", unicode: "+", ascii: "+", color: Dim }, - { id: ConnectorCornerBottomLeft, emoji: "+", unicode: "+", ascii: "+", color: Dim }, - { id: ConnectorCornerTopLeft, emoji: "+", unicode: "+", ascii: "+", color: Dim } + { id: ConnectorHorizontal, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "--"), unicode: unicode_glyph(text: "--"), ascii: ascii_glyph(text: "--")), color: Dim }, + { id: ConnectorVertical, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "|"), unicode: unicode_glyph(text: "|"), ascii: ascii_glyph(text: "|")), color: Dim }, + { id: ConnectorTeeDown, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "+"), unicode: unicode_glyph(text: "+"), ascii: ascii_glyph(text: "+")), color: Dim }, + { id: ConnectorTeeUp, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "+"), unicode: unicode_glyph(text: "+"), ascii: ascii_glyph(text: "+")), color: Dim }, + { id: ConnectorCornerBottomLeft, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "+"), unicode: unicode_glyph(text: "+"), ascii: ascii_glyph(text: "+")), color: Dim }, + { id: ConnectorCornerTopLeft, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "+"), unicode: unicode_glyph(text: "+"), ascii: ascii_glyph(text: "+")), color: Dim } ] // Lookup via fold + structural == (filter+field-equality hits a v1 interpreter bug). @@ -90,12 +100,7 @@ fn lookup_symbol_entry(id: SymbolId) -> SymbolEntry? { fn resolve_symbol(id: SymbolId, tier: Tier) -> String? { match lookup_symbol_entry(id: id) { - Present { value: entry } => - match tier { - Emoji => Present { value: entry.emoji } - Unicode => Present { value: entry.unicode } - Ascii => Present { value: entry.ascii } - } + Present { value: entry } => Present { value: glyph_at(glyphs: entry.glyphs, tier: tier) } Absent => none } } diff --git a/dsl/extdeps/render/terminal.dag b/dsl/extdeps/render/terminal.dag index 97028504c92..6efafe8b1ef 100644 --- a/dsl/extdeps/render/terminal.dag +++ b/dsl/extdeps/render/terminal.dag @@ -1,5 +1,7 @@ // extdeps/render/terminal.dag — Terminal width composition over glyph realization. // +// Anchor: https://www.unicode.org/reports/tr11/ (UAX #11 East Asian Width; sibling to std.unicode) +// // Width-constrained frame/line composition depends on resolve_symbol (encoding-tier // dispatch in extdeps.render.glyphs). Lives in extdeps so std.render stays // realization-free (DESIGN.md §3). diff --git a/dsl/extdeps/render/tier_glyphs.dag b/dsl/extdeps/render/tier_glyphs.dag new file mode 100644 index 00000000000..499303425f6 --- /dev/null +++ b/dsl/extdeps/render/tier_glyphs.dag @@ -0,0 +1,29 @@ +// extdeps/render/tier_glyphs.dag — Total Tier→glyph product (one field per variant). +// +// Each field is a distinct encoding authority (ascii_glyph / unicode_glyph / emoji_glyph). +// Illegal states (missing/duplicate tier) are unrepresentable. + +module extdeps.render.tier_glyphs + +import extdeps.render.ascii_glyph { AsciiGlyph, ascii_glyph_text } +import extdeps.render.emoji_glyph { EmojiGlyph, emoji_glyph_text } +import extdeps.render.unicode_glyph { UnicodeGlyph, unicode_glyph_text } +import std.symbols { Tier } + +type TierGlyphs { + ascii: AsciiGlyph + unicode: UnicodeGlyph + emoji: EmojiGlyph +} + +fn glyphs_for_tiers(emoji: EmojiGlyph, unicode: UnicodeGlyph, ascii: AsciiGlyph) -> TierGlyphs { + { emoji: emoji, unicode: unicode, ascii: ascii } +} + +fn glyph_at(glyphs: TierGlyphs, tier: Tier) -> String { + match tier { + Emoji => emoji_glyph_text(g: glyphs.emoji) + Unicode => unicode_glyph_text(g: glyphs.unicode) + Ascii => ascii_glyph_text(g: glyphs.ascii) + } +} diff --git a/dsl/extdeps/render/unicode_glyph.dag b/dsl/extdeps/render/unicode_glyph.dag new file mode 100644 index 00000000000..a20190af3da --- /dev/null +++ b/dsl/extdeps/render/unicode_glyph.dag @@ -0,0 +1,26 @@ +// extdeps/render/unicode_glyph.dag — Unicode-tier glyph carrier. +// +// Anchor: https://www.unicode.org/versions/latest/ +// (Unicode Standard — same authority as std.unicode UCD 15.x; no parallel model) +// +// dissolve-on: Q-Regex repertoire refinement — cited blocks (U+2500 Box Drawing, +// U+2800 Braille Patterns, …). + +module extdeps.render.unicode_glyph + +import std.unicode { string_display_width } + +type UnicodeGlyph = String where brand("UnicodeGlyph") + +fn unicode_glyph(text: String) -> UnicodeGlyph { + text +} + +fn unicode_glyph_text(g: UnicodeGlyph) -> String { + g +} + +// Display-width dispatch for Unicode-tier glyphs defers to std.unicode (UAX #11). +fn unicode_glyph_display_width(g: UnicodeGlyph) -> Int { + string_display_width(s: unicode_glyph_text(g: g)) +} diff --git a/dsl/std/symbols.dag b/dsl/std/symbols.dag index 257ef749a7d..85f7035b986 100644 --- a/dsl/std/symbols.dag +++ b/dsl/std/symbols.dag @@ -23,8 +23,9 @@ type SemanticColor = Default | Success | Warning | Error | Info | Dim | Active | // --- Encoding tier --- // -// Ordered from richest to most compatible: -// Emoji > Unicode > Ascii +// Project interface: ordered richness (Emoji > Unicode > Ascii). +// Not an external authority — tier dispatch contract lives in std; cited +// repertoire per tier is anchored in extdeps/render/glyphs.dag. type Tier = Emoji | Unicode | Ascii @@ -84,16 +85,3 @@ type SymbolId | ConnectorTeeUp | ConnectorCornerBottomLeft | ConnectorCornerTopLeft - -// --- Symbol entry --- -// -// A single symbol with all three tier encodings and a semantic color. -// The cited realization rows live in extdeps/render/glyphs.dag. - -type SymbolEntry { - id: SymbolId - emoji: String - unicode: String - ascii: String - color: SemanticColor -} diff --git a/dsl/test/claim/render_glyphs_ansi_witness_test.dag b/dsl/test/claim/render_glyphs_ansi_witness_test.dag index a79f7cd8ce1..bc0b8621aa5 100644 --- a/dsl/test/claim/render_glyphs_ansi_witness_test.dag +++ b/dsl/test/claim/render_glyphs_ansi_witness_test.dag @@ -11,10 +11,14 @@ module test.claim.render_glyphs_ansi_witness import extdeps.render.ansi { ansi_code } +import extdeps.render.ascii_glyph { ascii_glyph } +import extdeps.render.emoji_glyph { emoji_glyph } import extdeps.render.glyphs { resolve_symbol, symbol_color } -import std.symbols { Ascii, Error, NodeCompleted, NodeFailed, Success, Unicode } +import extdeps.render.tier_glyphs { glyph_at, glyphs_for_tiers } +import extdeps.render.unicode_glyph { unicode_glyph } +import std.symbols { Ascii, Emoji, Error, NodeCompleted, NodeFailed, Success, Unicode } -// -- Glyph tier dispatch ----------------------------------------------------- +// -- Glyph tier dispatch (standard_symbols table) ---------------------------- fn w_completed_ascii() -> Bool { match resolve_symbol(id: NodeCompleted, tier: Ascii) { Present { value: s } => s == "[x]" @@ -33,6 +37,19 @@ fn w_completed_unicode_differs_from_ascii() -> Bool { } } +// -- Behavioral glyph_at witness (distinct text per tier; not brand-reliant) - +// Perturbing glyph_at to project the wrong field makes this RED. +fn w_glyph_at_selects_matching_tier_text() -> Bool { + let g = glyphs_for_tiers( + emoji: emoji_glyph(text: "E"), + unicode: unicode_glyph(text: "U"), + ascii: ascii_glyph(text: "A") + ) + glyph_at(glyphs: g, tier: Ascii) == "A" && + glyph_at(glyphs: g, tier: Unicode) == "U" && + glyph_at(glyphs: g, tier: Emoji) == "E" +} + fn w_failed_color_is_error() -> Bool { match symbol_color(id: NodeFailed) { Present { value: c } => @@ -56,6 +73,7 @@ fn w_success_ansi_is_256_green() -> Bool { test fn render_glyphs_ansi_witnesses() -> Bool { w_completed_ascii() && w_completed_unicode_differs_from_ascii() && + w_glyph_at_selects_matching_tier_text() && w_failed_color_is_error() && w_success_ansi_is_256_green() }