Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

README.md

4.59 Kb · 104 lines

gno.land/p/moul/kit/ui/v0

The shared display vocabulary for r/moul realms: the small set of rendering decisions every realm was making on its own, made once.

First package of the p/moul/kit/* layer (moul/gno-contracts#151). kit composes the existing packages, it does not replace them.

Why it exists

Measured across 118 realm directories in this repo:

Realm dirs importing zero p/moul/* 79 of 118
Realm lines inside Render / render* 5,909 of 23,276 (25.4%)
Lines in duplicated private helpers 857 across 126 functions
Copies of shortAddr 11, with four different truncation rules
Copies of escapeInline 8, all a byte-identical seven-pair replacer

The shortAddr number is the one that matters: the same account rendered differently depending on which realm you opened. That is not duplication, it is four answers to one question.

What is here, and what is deliberately not

This package does not re-export markdown primitives. Headings, bold, lists, code blocks and links already have an owner in p/moul/md; import that alongside. What lives here is only what had no owner and was therefore copy-pasted.

 1import (
 2    "gno.land/p/moul/kit/ui/v0"
 3    "gno.land/p/moul/md/v0"
 4)
 5
 6func Render(path string) string {
 7    t := ui.NewTable("#", "Player", "Score")
 8    for i, p := range players {
 9        t.Row(strconv.Itoa(i+1)+ui.Podium(i), ui.Addr(p.addr), ui.Cell(p.label))
10    }
11    return md.H1("Leaderboard") + t.OrEmpty("Nobody has played yet.")
12}
Addr(a) `g1manfre…dlf5`, the one address format
AddrFull(a) the full address, in backticks
AddrText(a) shortened, no backticks, for a link title
AddrOf(s) Addr for an address already in string form (an avl key)
Short(s), ShortN(s, head, tail) the same rule for any string: a URL, a handle, a commitment hash
Inline(s), Cell(s) escape user text, delegated to p/nt/markdown/sanitize
Excerpt(s, width) a preview of user prose: cut to width runes, then escape. That order, because escaping first and cutting second strands a backslash
Action(title, fn, args...) a clickable call, instead of prose telling the reader to type a function name
ActionIn(pkgPath, title, fn, args...) the same, against another realm
NewTable(headers...), .Row(...), .String(), .OrEmpty(msg) GFM tables
Empty(msg) the italic placeholder where a list would be
Podium(rank) 🥇🥈🥉, or "" past third
Join(sep, parts...) concatenate, skipping empty sections

The escaping contract

Table cells and Action titles are markdown, not plain text.

  • Anything that came from a user goes through Cell (inside a table) or Inline (anywhere else) before it reaches this package.
  • Output of Addr, AddrFull, AddrOf and Podium is already safe and must not be escaped again.

Table renders the GFM table itself rather than delegating to p/moul/mdtable, which unconditionally rewrites | to | in every cell. Stacked on Cell, which already emits the GFM escape \|, that double-escapes into a stray backslash (a\|b, found while porting guestbook). One escaping stage is the only way to get this right, and it has to be the stage that knows whether the text is user input.

Action also escapes its title, which p/moul/helplink does not (it carries an // XXX: escape title where this would go).

Design rule

The safe, conventional thing must be the shortest thing to type. A realm author reaching for the obvious call has to land on the correct behaviour; that is the only mechanism that stops these helpers from being rewritten a twelfth time.


Part of moul/gno-contracts — moul's versioned gno.land contracts. See the repository for the full catalog, build/test tooling, and usage.

Dependency graph:

gno.land/p/moul/kit/ui/v0 dependency graph

⚠️ Disclaimer: provided as-is, without warranty; not security-audited. Full disclaimer: DISCLAIMER.