Repository navigation
feat(emoji): update emoji picker to latest version #39217
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -1,18 +1,18 @@ | ||||||||
| # Updating emojione | ||||||||
| # Updating emojis (now using emoji-toolkit) | ||||||||
|
|
||||||||
| ## Generate new category map variable for emojipicker | ||||||||
| Run | ||||||||
| Run the generator script which now pulls metadata from `emoji-toolkit` | ||||||||
|
|
||||||||
| ``` | ||||||||
| node --experimental-modules generateEmojiIndex.mjs | ||||||||
| ``` | ||||||||
|
Comment on lines
6
to
8
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add language specification to fenced code block. The code block is missing a language identifier, which helps with syntax highlighting and accessibility. 📝 Proposed fix-```
+```shell
node --experimental-modules generateEmojiIndex.mjs
```📝 Committable suggestion
Suggested change
🧰 Tools🪛 markdownlint-cli2 (0.21.0)[warning] 6-6: Fenced code blocks should have a language specified (MD040, fenced-code-language) 🤖 Prompt for AI Agents |
||||||||
|
|
||||||||
| ## Generate new percentage sprite | ||||||||
| Clone the repository https://github.com/Ranks/emojione/ and replace the file `assets/sprites/emojione.sprites.mustache` with the content | ||||||||
| of [emojione.sprites.mustache](emojione.sprites.mustache), then run at `emojione` folder: | ||||||||
| The script will download 64px PNGs from the JoyPixels CDN into `.emoji-cache` | ||||||||
| and build updated sprite sheets under `public/packages/emojione/`. | ||||||||
|
|
||||||||
| ``` | ||||||||
| grunt sprite | ||||||||
| sass --sourcemap=none assets/sprites/emojione.sprites.scss sprites.css | ||||||||
| ``` | ||||||||
| ## Sprite generation notes | ||||||||
| The old manual `grunt`/`sass` workflow is no longer required. If you need to | ||||||||
| rebuild sprites offline you can cache the PNG files yourself by running the | ||||||||
| script once with internet access; subsequent runs will reuse the cached images. | ||||||||
|
|
||||||||
| And replace the file `sprites.css` at Rocket.Chat's `/packages/rocketchat-emoji-emojione/sprites.css`. | ||||||||
| The generated CSS is written to `apps/meteor/app/emoji-emojione/client/emojione-sprites.css`. | ||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -6,31 +6,47 @@ | |||||||||||
| import fs from 'fs'; | ||||||||||||
| import nsg from 'node-sprite-generator'; | ||||||||||||
| import _ from 'underscore'; | ||||||||||||
| import gm from 'gm'; // lgtm[js/unused-local-variable] | ||||||||||||
| import path from 'path'; | ||||||||||||
|
|
||||||||||||
| const assetFolder = '../../../node_modules/emojione-assets'; | ||||||||||||
| const emojiJsonFile = `${assetFolder}/emoji.json`; | ||||||||||||
| // helper to resolve paths relative to this script file | ||||||||||||
| const scriptDir = path.dirname(new URL(import.meta.url).pathname); | ||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Prompt for AI agents |
||||||||||||
|
|
||||||||||||
| // Node 18+ has global fetch; if not available uncomment next line | ||||||||||||
| // import fetch from 'node-fetch'; | ||||||||||||
|
|
||||||||||||
| // Resolve the path to emoji-json using Node's module resolution so the | ||||||||||||
| // script can be run from any working directory. Using createRequire avoids | ||||||||||||
| // experimental JSON import assertions and works reliably in ESM. | ||||||||||||
| import { createRequire } from 'module'; | ||||||||||||
| const require = createRequire(import.meta.url); | ||||||||||||
| const emojiJsonFile = require.resolve('emoji-toolkit/emoji.json'); | ||||||||||||
|
|
||||||||||||
| // directory where we'll temporarily store downloaded PNGs when using CDN | ||||||||||||
| // keep cache scoped to the script directory so that generated files don't | ||||||||||||
| // clutter the workspace root when the script is invoked from elsewhere. | ||||||||||||
| const tmpFolder = path.join(scriptDir, '.emoji-cache'); | ||||||||||||
|
|
||||||||||||
| // ensure cache directory exists (for CDN downloads) | ||||||||||||
| fs.mkdirSync(tmpFolder, { recursive: true }); | ||||||||||||
|
|
||||||||||||
| const emojiJson = fs.readFileSync(emojiJsonFile, 'utf8'); | ||||||||||||
|
|
||||||||||||
| generateEmojiPicker(emojiJson); | ||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unhandled promise from async function call.
🐛 Proposed fix-generateEmojiPicker(emojiJson);
+generateEmojiPicker(emojiJson).catch((err) => {
+ console.error('Failed to generate emoji picker:', err);
+ process.exit(1);
+});Or wrap in an async IIFE: -generateEmojiPicker(emojiJson);
+(async () => {
+ await generateEmojiPicker(emojiJson);
+})();📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||
|
|
||||||||||||
| if (!fs.existsSync(emojiJsonFile)) { | ||||||||||||
| console.error(`${emojiJsonFile} doesn't exist.`); | ||||||||||||
| console.error("Maybe you need to run 'meteor npm install emojione-assets' or 'meteor npm install'?"); | ||||||||||||
| } else { | ||||||||||||
| const emojiJson = fs.readFileSync(emojiJsonFile); | ||||||||||||
| generateEmojiPicker(emojiJson); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| function generateEmojiPicker(data) { | ||||||||||||
| const emojiList = JSON.parse(data); | ||||||||||||
| console.log(`${Object.keys(emojiList).length} emojis found.`); | ||||||||||||
| async function generateEmojiPicker(data) { | ||||||||||||
| const emojiList = JSON.parse(data); | ||||||||||||
| console.log(`${Object.keys(emojiList).length} emojis found.`); | ||||||||||||
|
|
||||||||||||
| let toneList = []; | ||||||||||||
| let emojisByCategory = {}; | ||||||||||||
| let toneList = []; | ||||||||||||
| let emojisByCategory = {}; | ||||||||||||
|
|
||||||||||||
| _.sortBy(Object.entries(emojiList), (a) => a[1].order).forEach(([code, emoji]) => { | ||||||||||||
| if (emoji && emoji.shortname) { | ||||||||||||
| const toneIndex = emoji.shortname.indexOf('_tone'); | ||||||||||||
| if (toneIndex !== -1) { | ||||||||||||
| const tone = emoji.shortname.substr(1, toneIndex - 1); | ||||||||||||
| // iterate in order field to keep categories sorted | ||||||||||||
| _.sortBy(Object.entries(emojiList), (a) => a[1].order).forEach(([code, emoji]) => { | ||||||||||||
| if (emoji && emoji.shortname) { | ||||||||||||
| const toneIndex = emoji.shortname.indexOf('_tone'); | ||||||||||||
| if (toneIndex !== -1) { | ||||||||||||
| const tone = emoji.shortname.replace(/:/g, '').split('_tone')[0]; | ||||||||||||
| if (!toneList.includes(tone)) { | ||||||||||||
| toneList.push(tone); | ||||||||||||
| } | ||||||||||||
|
|
@@ -90,11 +106,12 @@ function generateEmojiPicker(data) { | |||||||||||
|
|
||||||||||||
| // emojisByCategory | ||||||||||||
| output += `export const emojisByCategory = {\n`; | ||||||||||||
| for (let category in emojisByCategory) { | ||||||||||||
| for (const category of Object.keys(emojisByCategory)) { | ||||||||||||
| output += `\t${category}: [\n`; | ||||||||||||
|
|
||||||||||||
| for (let emoji in emojisByCategory[category]) { | ||||||||||||
| output += `\t\t'${emojiList[emojisByCategory[category][emoji]].shortname.replace(/:/g, '')}',\n`; | ||||||||||||
| for (const code of emojisByCategory[category]) { | ||||||||||||
| const short = emojiList[code].shortname.replace(/:/g, ''); | ||||||||||||
| output += `\t\t'${short}',\n`; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| output += `\t],\n`; | ||||||||||||
|
|
@@ -113,25 +130,44 @@ function generateEmojiPicker(data) { | |||||||||||
|
|
||||||||||||
| for (let category in emojisByCategory) { | ||||||||||||
| let srcList = []; | ||||||||||||
| let diversityList = []; | ||||||||||||
| let diversityList = {}; | ||||||||||||
| const emojis = _.filter(emojiList, (x) => x.category === category); | ||||||||||||
| const spritePath = `../../../public/packages/emojione/${category}-sprites.png`; | ||||||||||||
|
|
||||||||||||
| _.each(emojis, function (emoji) { | ||||||||||||
| srcList.push(`${assetFolder}/png/64/${emoji.code_points.base}.png`); | ||||||||||||
| // derive output paths relative to this script, not the cwd | ||||||||||||
| const spritePath = path.resolve(scriptDir, '../../../public/packages/emojione', `${category}-sprites.png`); | ||||||||||||
| const cssOutputPath = path.resolve(scriptDir, '../client', `${category}-sprites.css`); | ||||||||||||
|
|
||||||||||||
| for (const emoji of emojis) { | ||||||||||||
| const code = emoji.code_points.base; | ||||||||||||
| const localPath = `${tmpFolder}/${code}.png`; | ||||||||||||
| if (!fs.existsSync(localPath)) { | ||||||||||||
| const url = `https://cdn.jsdelivr.net/npm/emoji-toolkit@latest/assets/64/${code}.png`; | ||||||||||||
| console.log(`fetching ${url}`); | ||||||||||||
| const res = await fetch(url); | ||||||||||||
| if (res.ok) { | ||||||||||||
| const buf = await res.arrayBuffer(); | ||||||||||||
| fs.writeFileSync(localPath, Buffer.from(buf)); | ||||||||||||
| } else { | ||||||||||||
| console.warn(`failed to download ${code} (${res.status})`); | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| srcList.push(localPath); | ||||||||||||
| if (emoji.diversity) { | ||||||||||||
| diversityList[emoji.code_points.base] = true; | ||||||||||||
| diversityList[code] = true; | ||||||||||||
| } | ||||||||||||
| }); | ||||||||||||
| } | ||||||||||||
| spriteCss += `@import './${category}-sprites.css';\n`; | ||||||||||||
|
|
||||||||||||
| // filter out any images that failed to download | ||||||||||||
| srcList = srcList.filter((p) => fs.existsSync(p)); | ||||||||||||
| const templateFile = path.join(scriptDir, 'emojione.tpl'); | ||||||||||||
|
|
||||||||||||
| nsg( | ||||||||||||
| { | ||||||||||||
| src: srcList, | ||||||||||||
| spritePath: spritePath, | ||||||||||||
| layout: 'packed', | ||||||||||||
| stylesheet: 'emojione.tpl', | ||||||||||||
| stylesheetPath: `../client/${category}-sprites.css`, | ||||||||||||
| stylesheet: templateFile, | ||||||||||||
| stylesheetPath: cssOutputPath, | ||||||||||||
| compositor: 'gm', | ||||||||||||
| layoutOptions: { | ||||||||||||
| scaling: 1, | ||||||||||||
|
|
@@ -175,7 +211,8 @@ function generateEmojiPicker(data) { | |||||||||||
| image-rendering: optimizeQuality; | ||||||||||||
| } | ||||||||||||
| `; | ||||||||||||
| fs.writeFileSync('../client/emojione-sprites.css', spriteCss, { | ||||||||||||
| const emojioneCssPath = path.resolve(scriptDir, '../client/emojione-sprites.css'); | ||||||||||||
| fs.writeFileSync(emojioneCssPath, spriteCss, { | ||||||||||||
| encoding: 'utf8', | ||||||||||||
| flag: 'w', | ||||||||||||
| }); | ||||||||||||
|
|
||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove practice comment from production README.
This HTML comment appears to be a test/practice addition that should not be included in the main README.
🧹 Proposed fix
📝 Committable suggestion
🤖 Prompt for AI Agents