Skip to content

feat(markdown): replace GitHub emoji shortcodes inline#617

Merged
rainxchzed merged 1 commit into
mainfrom
feat/markdown-emoji-shortcodes
May 16, 2026
Merged

feat(markdown): replace GitHub emoji shortcodes inline#617
rainxchzed merged 1 commit into
mainfrom
feat/markdown-emoji-shortcodes

Conversation

@rainxchzed
Copy link
Copy Markdown
Member

@rainxchzed rainxchzed commented May 16, 2026

`:rocket:` → 🚀, `:tada:` → 🎉, ~250 common shortcodes total. Preprocessor pass runs after HTML normalization and skips fenced code blocks so legitimate `:foo:` syntax in code isn't clobbered. Unknown shortcodes pass through as literal text.

Test plan

  • Compile clean
  • Device: open a README with emoji shortcodes (most awesome-* lists). Verify they render as unicode emojis.

Summary by CodeRabbit

  • New Features

    • Emoji shortcodes in markdown (README, release notes and other rendered docs) now convert to emoji characters (:rocket: → 🚀, :tada: → 🎉, plus ~250 others), preserving code blocks.
  • Documentation

    • Updated "What’s New" release notes across 14+ languages to announce emoji shortcode rendering.

Review Change Stack

Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rainxchzed has reached the 50-review limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 16, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8ca722a2-3dca-4d6d-853f-03611085c46b

📥 Commits

Reviewing files that changed from the base of the PR and between 187086f and aec46b3.

📒 Files selected for processing (15)
  • core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/EmojiShortcodes.kt
  • core/presentation/src/commonMain/composeResources/files/whatsnew/18.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/ar/18.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/bn/18.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/es/18.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/fr/18.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/hi/18.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/it/18.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/ja/18.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/ko/18.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/pl/18.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/ru/18.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/tr/18.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/zh-CN/18.json
  • feature/details/data/src/commonMain/kotlin/zed/rainxch/details/data/utils/preprocessMarkdown.kt

Walkthrough

Adds a Kotlin EmojiShortcodes utility that replaces GitHub-style :shortcode: tokens with Unicode emoji outside fenced code blocks, wires it into markdown preprocessing as Phase 15, and updates localized release notes to document the feature.

Changes

Emoji Shortcode Rendering Feature

Layer / File(s) Summary
EmojiShortcodes utility implementation
core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/EmojiShortcodes.kt
Defines a private shortcode→emoji TABLE, a PATTERN for :token: matching, public render(input: String), replaceInText() performing lookups, and splitOutCodeRegions() which preserves triple-backtick code fences during replacements.
Markdown preprocessing integration
feature/details/data/src/commonMain/kotlin/zed/rainxch/details/data/utils/preprocessMarkdown.kt
Calls EmojiShortcodes.render(processed) as Phase 15 immediately before final trim/return to render shortcode tokens into emoji.
Release notes across locales
core/presentation/src/commonMain/composeResources/files/whatsnew/{18,ar/18,bn/18,es/18,fr/18,hi/18,it/18,ja/18,ko/18,pl/18,ru/18,tr/18,zh-CN/18}.json
Adds an IMRPOVED bullet describing emoji shortcode rendering in README/release notes with examples (:rocket: → 🚀, :tada: → 🎉) and an approximate count of supported shortcuts.

Sequence Diagram

sequenceDiagram
  participant User as User/Content
  participant PreProcess as preprocessMarkdown()
  participant Shortcodes as EmojiShortcodes.render()
  participant Split as splitOutCodeRegions()
  participant Replace as replaceInText()
  participant Output as Rendered Markdown
  User->>PreProcess: supply markdown with :rocket:
  PreProcess->>Shortcodes: Phase 15: render emoji
  Shortcodes->>Split: detect ``` code fences
  Split->>Replace: text chunks (exclude code)
  Replace->>Replace: lookup :rocket: → 🚀 via TABLE
  Shortcodes->>Output: reconstructed markdown
  Output->>User: returned markdown with 🚀
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

A rabbit hops through emoji lands 🐰,
Where :rocket: rockets into the sky 🚀,
:tada: sparkles in a joyful spree 🎉,
Code fences keep secrets safe and sound,
Shortcodes bloom — two-fifty-plus found ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(markdown): replace GitHub emoji shortcodes inline' accurately summarizes the main change: introducing GitHub emoji shortcode replacement in markdown preprocessing.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/markdown-emoji-shortcodes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/EmojiShortcodes.kt (1)

4-8: ⚡ Quick win

Reduce non-essential inline comments to match Kotlin file guidelines.

This file introduces many narrative section comments; keep only comments for non-obvious invariants/workarounds.

As per coding guidelines, "Do not add KDoc or inline comments unless explicitly requested; only add inline comments for non-obvious invariants, tricky concurrency, or workarounds".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/EmojiShortcodes.kt`
around lines 4 - 8, The top-of-file narrative comments in EmojiShortcodes.kt are
too verbose; remove or condense them so only non-obvious invariants,
workarounds, or essential notes remain. Specifically, edit the header comment
block above the emoji table/definitions in the EmojiShortcodes object (or
top-level declarations) to delete narrative lines about selection rationale and
bundle size, or convert them into a single short sentence explaining the subset
decision; keep only comments that document tricky invariants or necessary
behavior. Ensure no additional KDoc or inline commentary is added elsewhere in
the file unless it documents a non-obvious invariant or workaround.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/EmojiShortcodes.kt`:
- Around line 361-383: The current split logic treats every triple-backtick
substring as a fence, misclassifying inline backticks; replace the
substring-based parsing with a line-oriented detector in splitOutCodeRegions so
fences are only recognized when they occur on their own (up to 3 leading spaces)
using a regex like ^\s{0,3}```.*$; iterate lines, accumulate non-fence lines
into a buffer and flush into Chunk(buffer, isCode=current) when encountering a
fence line, add the fence line itself as a Chunk(isCode=true) (preserve trailing
newlines between lines), toggle inCode on fence lines, and return the collected
List<Chunk> so emoji replacement only ignores true markdown code blocks.

In `@core/presentation/src/commonMain/composeResources/files/whatsnew/18.json`:
- Line 32: The whatsnew JSON entry string "Emoji shortcodes in README and
release notes — :rocket: now renders as 🚀, :tada: as 🎉, and ~250 others.
Common dev/status icons covered out of the box." has an inaccurate shortcode
count; update that text to reflect the implemented ~330 shortcodes (e.g.,
replace "~250 others" with "~330 others" or "around 330 others") so the entry
accurately matches the implementation.

In `@core/presentation/src/commonMain/composeResources/files/whatsnew/ja/18.json`:
- Line 32: The Japanese string "README とリリースノートで絵文字ショートコードに対応 — :rocket: は
🚀、:tada: は 🎉、合計約 250 種類。" is semantically inconsistent with the English "~250
others"; update this Japanese sentence to mirror the final English phrasing and
corrected count (e.g., replace "合計約 250 種類" with the equivalent Japanese of
"~250 others" like "その他約250種類" or with the corrected total count such as
"合計約252種類") so the locales convey the same meaning.

---

Nitpick comments:
In
`@core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/EmojiShortcodes.kt`:
- Around line 4-8: The top-of-file narrative comments in EmojiShortcodes.kt are
too verbose; remove or condense them so only non-obvious invariants,
workarounds, or essential notes remain. Specifically, edit the header comment
block above the emoji table/definitions in the EmojiShortcodes object (or
top-level declarations) to delete narrative lines about selection rationale and
bundle size, or convert them into a single short sentence explaining the subset
decision; keep only comments that document tricky invariants or necessary
behavior. Ensure no additional KDoc or inline commentary is added elsewhere in
the file unless it documents a non-obvious invariant or workaround.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0d1efd48-b0c8-4ae2-8bc6-948454dc9a77

📥 Commits

Reviewing files that changed from the base of the PR and between a98ac82 and 187086f.

📒 Files selected for processing (15)
  • core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/EmojiShortcodes.kt
  • core/presentation/src/commonMain/composeResources/files/whatsnew/18.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/ar/18.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/bn/18.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/es/18.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/fr/18.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/hi/18.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/it/18.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/ja/18.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/ko/18.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/pl/18.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/ru/18.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/tr/18.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/zh-CN/18.json
  • feature/details/data/src/commonMain/kotlin/zed/rainxch/details/data/utils/preprocessMarkdown.kt

Comment on lines +361 to +383
val fence = "```"
if (!input.contains(fence)) return listOf(Chunk(input, isCode = false))
val out = mutableListOf<Chunk>()
var i = 0
var inCode = false
while (i < input.length) {
val next = input.indexOf(fence, startIndex = i)
if (next < 0) {
out += Chunk(input.substring(i), isCode = inCode)
break
}
// Take everything up to and including the fence into the
// current region — the fence itself is part of the code
// block when we're transitioning out, so always include
// 3 chars in the "code" side.
if (inCode) {
out += Chunk(input.substring(i, next + fence.length), isCode = true)
} else {
out += Chunk(input.substring(i, next), isCode = false)
out += Chunk(fence, isCode = true)
}
i = next + fence.length
inCode = !inCode
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fence parsing toggles on any ``` occurrence, not actual markdown fence lines.

Current logic treats every triple-backtick substring as a fence delimiter. That can mis-split regions when backticks appear in normal text/content, causing incorrect emoji replacement scope.

Proposed direction (line-based fence detection)
-    private fun splitOutCodeRegions(input: String): List<Chunk> {
-        val fence = "```"
-        if (!input.contains(fence)) return listOf(Chunk(input, isCode = false))
-        val out = mutableListOf<Chunk>()
-        var i = 0
-        var inCode = false
-        while (i < input.length) {
-            val next = input.indexOf(fence, startIndex = i)
-            if (next < 0) {
-                out += Chunk(input.substring(i), isCode = inCode)
-                break
-            }
-            if (inCode) {
-                out += Chunk(input.substring(i, next + fence.length), isCode = true)
-            } else {
-                out += Chunk(input.substring(i, next), isCode = false)
-                out += Chunk(fence, isCode = true)
-            }
-            i = next + fence.length
-            inCode = !inCode
-        }
-        return out
-    }
+    private fun splitOutCodeRegions(input: String): List<Chunk> {
+        val lines = input.split('\n')
+        if (lines.none { it.matches(Regex("""^\s{0,3}```.*$""")) }) {
+            return listOf(Chunk(input, isCode = false))
+        }
+        val out = mutableListOf<Chunk>()
+        val buffer = StringBuilder()
+        var inCode = false
+
+        fun flush() {
+            if (buffer.isNotEmpty()) {
+                out += Chunk(buffer.toString(), isCode = inCode)
+                buffer.clear()
+            }
+        }
+
+        lines.forEachIndexed { idx, line ->
+            val isFenceLine = line.matches(Regex("""^\s{0,3}```.*$"""))
+            if (isFenceLine) {
+                flush()
+                out += Chunk(line + if (idx != lines.lastIndex) "\n" else "", isCode = true)
+                inCode = !inCode
+            } else {
+                buffer.append(line)
+                if (idx != lines.lastIndex) buffer.append('\n')
+            }
+        }
+        flush()
+        return out
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
val fence = "```"
if (!input.contains(fence)) return listOf(Chunk(input, isCode = false))
val out = mutableListOf<Chunk>()
var i = 0
var inCode = false
while (i < input.length) {
val next = input.indexOf(fence, startIndex = i)
if (next < 0) {
out += Chunk(input.substring(i), isCode = inCode)
break
}
// Take everything up to and including the fence into the
// current region — the fence itself is part of the code
// block when we're transitioning out, so always include
// 3 chars in the "code" side.
if (inCode) {
out += Chunk(input.substring(i, next + fence.length), isCode = true)
} else {
out += Chunk(input.substring(i, next), isCode = false)
out += Chunk(fence, isCode = true)
}
i = next + fence.length
inCode = !inCode
private fun splitOutCodeRegions(input: String): List<Chunk> {
val lines = input.split('\n')
if (lines.none { it.matches(Regex("""^\s{0,3}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/EmojiShortcodes.kt`
around lines 361 - 383, The current split logic treats every triple-backtick
substring as a fence, misclassifying inline backticks; replace the
substring-based parsing with a line-oriented detector in splitOutCodeRegions so
fences are only recognized when they occur on their own (up to 3 leading spaces)
using a regex like ^\s{0,3}```.*$; iterate lines, accumulate non-fence lines
into a buffer and flush into Chunk(buffer, isCode=current) when encountering a
fence line, add the fence line itself as a Chunk(isCode=true) (preserve trailing
newlines between lines), toggle inCode on fence lines, and return the collected
List<Chunk> so emoji replacement only ignores true markdown code blocks.

"Manually linking apps — sorted by installer source (F-Droid / Obtainium first, Play Store and system updates last) with a chip showing each app's source.",
"Manual link now suggests matching GitHub repos automatically — pick an app, get ranked candidates, tap to link. Manual URL entry still available."
"Manual link now suggests matching GitHub repos automatically — pick an app, get ranked candidates, tap to link. Manual URL entry still available.",
"Emoji shortcodes in README and release notes — :rocket: now renders as 🚀, :tada: as 🎉, and ~250 others. Common dev/status icons covered out of the box."
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Count the actual number of emoji shortcodes in the implementation

# Search for the shortcode map definition in EmojiShortcodes
ast-grep --pattern 'mapOf(
  $$$
)'  | rg -c ':\w+:' || echo "Pattern not found, trying alternative search"

# Alternative: search for shortcode entries in the file
rg -o '"\w+"\s+to\s+".' core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/EmojiShortcodes.kt | wc -l

Repository: OpenHub-Store/GitHub-Store

Length of output: 78


Update emoji shortcode count to reflect actual implementation.

The JSON file states "~250 others" but verification reveals the implementation contains approximately 330 shortcodes total, not ~250. Update the text to accurately reflect this count.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@core/presentation/src/commonMain/composeResources/files/whatsnew/18.json` at
line 32, The whatsnew JSON entry string "Emoji shortcodes in README and release
notes — :rocket: now renders as 🚀, :tada: as 🎉, and ~250 others. Common
dev/status icons covered out of the box." has an inaccurate shortcode count;
update that text to reflect the implemented ~330 shortcodes (e.g., replace "~250
others" with "~330 others" or "around 330 others") so the entry accurately
matches the implementation.

"手動リンク — インストーラ別に並び替え(F-Droid / Obtainium が先、Play ストアやシステム更新は最後)し、各アプリの入手元をチップで表示。",
"手動リンクが一致する GitHub リポを自動提案 — アプリを選ぶとランク付き候補が出る。タップでリンク。手動 URL 入力も引き続き利用可能。"
"手動リンクが一致する GitHub リポを自動提案 — アプリを選ぶとランク付き候補が出る。タップでリンク。手動 URL 入力も引き続き利用可能。",
"README とリリースノートで絵文字ショートコードに対応 — :rocket: は 🚀、:tada: は 🎉、合計約 250 種類。"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Semantic discrepancy with English version.

The Japanese text says "合計約 250 種類" (approximately 250 total types), but the English version says "~250 others" (implying 252+ total including :rocket: and :tada:). This creates an inconsistency across locales.

If the English version is corrected per the count verification issue, ensure the Japanese translation aligns with the final phrasing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@core/presentation/src/commonMain/composeResources/files/whatsnew/ja/18.json`
at line 32, The Japanese string "README とリリースノートで絵文字ショートコードに対応 — :rocket: は
🚀、:tada: は 🎉、合計約 250 種類。" is semantically inconsistent with the English "~250
others"; update this Japanese sentence to mirror the final English phrasing and
corrected count (e.g., replace "合計約 250 種類" with the equivalent Japanese of
"~250 others" like "その他約250種類" or with the corrected total count such as
"合計約252種類") so the locales convey the same meaning.

@rainxchzed rainxchzed force-pushed the feat/markdown-emoji-shortcodes branch from 187086f to aec46b3 Compare May 16, 2026 15:35
Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rainxchzed has reached the 50-review limit for trial accounts. To continue receiving code reviews, upgrade your plan.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant