nandi/jolt-nativepublic Fork 0
00d32dd
Commits
Clone
git clone https://git.rickub.com/nandi/jolt-native.git
git clone ssh://git@rickub.com/nandi/jolt-native.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Draw an emoji in a line as a character, not as a tally

`:reaction` was the only node that reached the Twemoji pack, so anything
wanting a colour glyph had to ask for one — and got the pill that comes
with it. In a message that is wrong twice over: the pill and border say
somebody reacted to the line, and the node answers the pointer and opens
a tooltip naming people who were never there.

So split the picture from the tally. `:emoji` allocates the square and
paints the glyph, and stops: no frame, no count, no `mine`, no hover.
`vidya_core::emoji_icon` has drawn exactly that all along; nothing in the
tree layer had a way to ask for it. `:size` defaults to the theme's body
size rather than the caption size a chip uses, because an emoji in a
sentence has to sit level with the words either side of it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandi committed 2026-09-02T18:30:42-07:00 Browse files
00d32dd parent: a7f6202
modified crates/jolt-vidya/src/tree.rs +25 -0
@@ -68,6 +68,9 @@ pub enum Tag {
6868 Image,
6969 Avatar,
7070 Reaction,
71+ /// One emoji, drawn from the pack and nothing else: no pill, no count, no
72+ /// pointer. `Reaction` is the same glyph wearing a tally's clothes.
73+ Emoji,
7174 Status,
7275 /// A tag this backend has not grown yet, keeping the name it was created
7376 /// with so a dump answers what the caller actually asked for.
@@ -100,6 +103,7 @@ impl Tag {
100103 "image" => Self::Image,
101104 "avatar" => Self::Avatar,
102105 "reaction" => Self::Reaction,
106+ "emoji" => Self::Emoji,
103107 "status" => Self::Status,
104108 other => Self::Unknown(other.to_owned()),
105109 }
@@ -130,6 +134,7 @@ impl Tag {
130134 Self::Image => "image",
131135 Self::Avatar => "avatar",
132136 Self::Reaction => "reaction",
137+ Self::Emoji => "emoji",
133138 Self::Status => "status",
134139 Self::Unknown(name) => name,
135140 }
@@ -1352,6 +1357,26 @@ impl Tree {
13521357 }
13531358 }
13541359
1360+ Tag::Emoji => {
1361+ // A glyph the text font cannot set, drawn from the pack and
1362+ // put in the line as if it were a word. `Reaction` draws the
1363+ // same picture, but a reaction is a tally: it wears a pill, it
1364+ // answers the pointer, and it names the people in it on hover.
1365+ // An emoji in a sentence is none of those things — it is a
1366+ // character — so this allocates the square and paints, and
1367+ // stops there.
1368+ let emoji = props.str("emoji").to_owned();
1369+ let emoji = if emoji.is_empty() {
1370+ props.label().to_owned()
1371+ } else {
1372+ emoji
1373+ };
1374+ // Body size by default, because the words either side are what
1375+ // it has to sit level with.
1376+ let size = props.num("size", theme.type_scale.body as f64) as f32;
1377+ vidya_core::emoji_icon(ui, theme, &emoji, size);
1378+ }
1379+
13551380 Tag::Image => {
13561381 // Two sources, one tag: a `src` is a file decoded once and
13571382 // cached by its path, a `feed` is live pixels pushed in under
@@ -68,6 +68,9 @@ pub enum Tag {
68 Image,68 Image,
69 Avatar,69 Avatar,
70 Reaction,70 Reaction,
71+ /// One emoji, drawn from the pack and nothing else: no pill, no count, no
72+ /// pointer. `Reaction` is the same glyph wearing a tally's clothes.
73+ Emoji,
71 Status,74 Status,
72 /// A tag this backend has not grown yet, keeping the name it was created75 /// A tag this backend has not grown yet, keeping the name it was created
73 /// with so a dump answers what the caller actually asked for.76 /// with so a dump answers what the caller actually asked for.
@@ -100,6 +103,7 @@ impl Tag {
100 "image" => Self::Image,103 "image" => Self::Image,
101 "avatar" => Self::Avatar,104 "avatar" => Self::Avatar,
102 "reaction" => Self::Reaction,105 "reaction" => Self::Reaction,
106+ "emoji" => Self::Emoji,
103 "status" => Self::Status,107 "status" => Self::Status,
104 other => Self::Unknown(other.to_owned()),108 other => Self::Unknown(other.to_owned()),
105 }109 }
@@ -130,6 +134,7 @@ impl Tag {
130 Self::Image => "image",134 Self::Image => "image",
131 Self::Avatar => "avatar",135 Self::Avatar => "avatar",
132 Self::Reaction => "reaction",136 Self::Reaction => "reaction",
137+ Self::Emoji => "emoji",
133 Self::Status => "status",138 Self::Status => "status",
134 Self::Unknown(name) => name,139 Self::Unknown(name) => name,
135 }140 }
@@ -1352,6 +1357,26 @@ impl Tree {
1352 }1357 }
1353 }1358 }
1354 1359
1360+ Tag::Emoji => {
1361+ // A glyph the text font cannot set, drawn from the pack and
1362+ // put in the line as if it were a word. `Reaction` draws the
1363+ // same picture, but a reaction is a tally: it wears a pill, it
1364+ // answers the pointer, and it names the people in it on hover.
1365+ // An emoji in a sentence is none of those things — it is a
1366+ // character — so this allocates the square and paints, and
1367+ // stops there.
1368+ let emoji = props.str("emoji").to_owned();
1369+ let emoji = if emoji.is_empty() {
1370+ props.label().to_owned()
1371+ } else {
1372+ emoji
1373+ };
1374+ // Body size by default, because the words either side are what
1375+ // it has to sit level with.
1376+ let size = props.num("size", theme.type_scale.body as f64) as f32;
1377+ vidya_core::emoji_icon(ui, theme, &emoji, size);
1378+ }
1379+
1355 Tag::Image => {1380 Tag::Image => {
1356 // Two sources, one tag: a `src` is a file decoded once and1381 // Two sources, one tag: a `src` is a file decoded once and
1357 // cached by its path, a `feed` is live pixels pushed in under1382 // cached by its path, a `feed` is live pixels pushed in under