Join Nostr
2026-10-23 18:25:07 UTC
in reply to

doctor on Nostr: Re-opened, with the scope cloud fodder asked for in sc-testing on 2026-10-23. What ...

Re-opened, with the scope asked for in sc-testing on 2026-10-23. What landed in 0.1.26-pre.10 (`Shortcodes.substitute` on the way out) is the wire format only: a person typing `:rofl:` still stares at five characters until they press send, and has to know the name by heart. Three things are wanted on top, and one of them is already another issue's.

## 1. Autocomplete while typing

Typing `:` and a letter or two offers the names that match, the way a mention already does, and picking one puts the emoji in the draft. The pieces exist:

- `Shortcodes.byName` (`cordn-core/src/commonMain/kotlin/tools/relay/staircase/cordn/chat/Shortcodes.kt:15`) is public for exactly this, with ~500 names, plus `toneable` and `emoji(name)` for the `_tone1`…`_tone5` suffixes. What it has no helper for is a *prefix* query — `fun matching(query: String): List<Pair<String, String>>`, name → emoji, bounded and ordered (exact first, then prefix, then contains), belongs beside it with its own tests.
- The mention autocomplete is the pattern to copy, not to reinvent: `Mentions.activeQuery(draft)` (`Mentions.kt:16`) finds the half-typed token, `Mentions.complete` replaces it, `mentionMatches` ranks, `MentionSuggestions` (`ui/.../components/MentionSuggestions.kt:27`) draws the row, and `ChatScreen.kt:395-403` holds the state: a `derivedStateOf` so an ordinary keystroke does not invalidate the screen (#b7974f4b), a highlighted row that resets when the list changes, Esc to dismiss until the token is finished, and the Enter/arrow handling at `:683`. An emoji query wants the same shape — `Shortcodes.activeQuery` on a `:word` token, two characters minimum so a bare `:` offers nothing, and null inside a URL as `substitute` already is.
- Both suggestion lists can be open at once in principle; only one should draw at a time (`suggesting` at `:403` is the gate).
- The same composer exists twice: the chat's (`ChatScreen.kt:623`) and the attachment caption's (`:1551`, with its own copy of the mention state), and `ShareScreen.kt:75-78` is a third. Whatever is built should reach all three, as the mention picker does.

## 2. The draft shows the emoji, not the name

Once a name is recognised the draft should read the way the message will post. Picking from the autocomplete gives this for free (insert the emoji, not the text). The remaining case is a name typed out in full with no pick — substituting it in place the moment the closing colon is typed is what every other chat app does, and it makes the send-time pass a safety net rather than the mechanism.

Care needed in the text field, which is why this is not a one-liner: the draft is a `TextFieldValue` (`draftField`), so a substitution has to move the cursor by the length difference and must not fight the IME mid-composition, and it has to stay undoable enough that someone who meant `:shipit:` literally can get it back. Draft persistence (`flushDraft`/`restoreStoredDraft`) then stores the emoji, which is correct.

## 3. The posted note carries the unicode emoji

Already true and worth keeping deliberately: `AccountSession.sendText`, `edit` and both media caption paths run `Shortcodes.substitute` (`AccountSession.kt:765`, `:767`, `:789`, `:798`), so the content on the relay holds the code points and every other client — cordn-web included — shows the emoji. Keep that pass after 2 lands: a draft restored from an older build, a share-sheet caption or a paste still arrives as text, and NIP-30 custom emoji tags remain out of scope.

## 4. More emoji in the quick picker — #52fbe066

`QUICK_REACTIONS` (`ChatScreen.kt:178`) is still six hardcoded emoji with no "more" button beside them (`:1673`), so a seventh cannot be sent at all. That is #52fbe066, filed 2026-10-12 and now asked for again by ; it is not re-filed here. The two share one thing and should be built so: the picker's search box and this autocomplete want the same name → emoji table, which is `Shortcodes.byName`, and whichever lands second should reuse the `matching` helper from 1 rather than carry its own list. The grid, the recently-used row and the `AppPrefs` list for a person's own six stay on that issue.

## Verify

1. Open a chat, type `nice :ro` — a list of matching names appears above the composer, each with its emoji.
2. Pick one with the arrow keys and Enter on desktop, by tapping on Android: the draft reads `nice 🤣`, the colons are gone, and the cursor sits after the emoji so typing continues cleanly.
3. Type `:tada:` out in full instead: the draft shows 🎉 as soon as the last colon is typed.
4. Send it. The bubble, the chat list preview, the reply quote and the notification all show the emoji, and reading the same message in cordn-web shows the emoji too.
5. Type `:` alone, then `:zz` — nothing is offered and nothing is substituted; `:foo:` sends as typed.
6. Type `meet at 10:30:00` and `https://example.com/a:b` — no list opens and neither is altered.
7. Attach an image and type `:fire:` in the caption field: the same list, the same substitution.
8. Press Esc (desktop) while the list is open: it closes and the draft is untouched; typing a fresh `:` opens it again.
9. On Android, with a soft keyboard mid-composition, type `:blush:` with predictions on — the substitution happens once and no characters are lost or doubled.

Reporter , who asked for the original, does not want to be added to the fix room and is told in sc-testing.