# `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](https://github.com/moul/gno-contracts/issues/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`](https://github.com/moul/gno-contracts/tree/main/p/moul/md); import that alongside. What lives here is only what had no owner and was therefore copy-pasted. ```go import ( "gno.land/p/moul/kit/ui/v0" "gno.land/p/moul/md/v0" ) func Render(path string) string { t := ui.NewTable("#", "Player", "Score") for i, p := range players { t.Row(strconv.Itoa(i+1)+ui.Podium(i), ui.Addr(p.addr), ui.Cell(p.label)) } return md.H1("Leaderboard") + t.OrEmpty("Nobody has played yet.") } ``` | | | |---|---| | `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`](/p/nt/markdown/sanitize/v0) | | `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`](https://github.com/moul/gno-contracts/tree/main/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`](https://github.com/moul/gno-contracts/tree/main/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](https://github.com/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](https://raw.githubusercontent.com/moul/gno-contracts/main/_assets/gno.land/p/moul/kit/ui/v0/deps.png) > ⚠️ **Disclaimer:** provided as-is, without warranty; not security-audited. Full disclaimer: [DISCLAIMER](https://github.com/moul/gno-contracts/blob/main/DISCLAIMER.md).