diff --git a/scripts/lib/setup_venv.py b/scripts/lib/setup_venv.py index 2e8e2f8844..4bacba06e5 100644 --- a/scripts/lib/setup_venv.py +++ b/scripts/lib/setup_venv.py @@ -28,6 +28,7 @@ VENV_DEPENDENCIES = [ "jq", # No longer used in production (clean me up later) "libsasl2-dev", # For building python-ldap from source "libvips", # For thumbnailing + "libvips-tools", ] COMMON_YUM_VENV_DEPENDENCIES = [ @@ -46,6 +47,7 @@ COMMON_YUM_VENV_DEPENDENCIES = [ "openssl-devel", "jq", "vips", # For thumbnailing + "vips-tools", ] REDHAT_VENV_DEPENDENCIES = [ diff --git a/tools/setup/emoji/build_emoji b/tools/setup/emoji/build_emoji index e42df4125b..3ef92d912c 100755 --- a/tools/setup/emoji/build_emoji +++ b/tools/setup/emoji/build_emoji @@ -4,6 +4,7 @@ # works. import os import shutil +import subprocess import sys from collections.abc import Iterator, Sequence from typing import Any @@ -49,7 +50,7 @@ div.emoji, span.emoji {{ display: inline-block; - background-image: url(~emoji-datasource-{emojiset}/img/{alt_name}/sheets-256/64.png); + background-image: url(../emoji/{emojiset}.webp); background-size: {background_size}; background-repeat: no-repeat; @@ -116,15 +117,16 @@ def main() -> None: # /srv/zulip-emoji-cache/*/web gets copied to ZULIP_PATH/web/generated # These must not be symlinked so webpack can resolve module references. - TARGET_EMOJI_STYLES = os.path.join(ZULIP_PATH, "web", "generated", "emoji-styles") - os.makedirs(TARGET_EMOJI_STYLES, exist_ok=True) - to_remove = set(os.listdir(TARGET_EMOJI_STYLES)) - source_emoji_dump = os.path.join(emoji_cache_path, "web", "emoji-styles") - for filename in os.listdir(source_emoji_dump): - shutil.copy2(os.path.join(source_emoji_dump, filename), TARGET_EMOJI_STYLES) - to_remove.discard(filename) - for filename in to_remove: - os.remove(os.path.join(TARGET_EMOJI_STYLES, filename)) + for subdir in ("emoji", "emoji-styles"): + target_dir = os.path.join(ZULIP_PATH, "web", "generated", subdir) + os.makedirs(target_dir, exist_ok=True) + to_remove = set(os.listdir(target_dir)) + source_emoji_dump = os.path.join(emoji_cache_path, "web", subdir) + for filename in os.listdir(source_emoji_dump): + shutil.copy2(os.path.join(source_emoji_dump, filename), target_dir) + to_remove.discard(filename) + for filename in to_remove: + os.remove(os.path.join(target_dir, filename)) def percent(f: float) -> str: @@ -334,6 +336,26 @@ def setup_emoji_farms(cache_path: str, emoji_data: list[dict[str, Any]]) -> None generate_sprite_css_files(cache_path, emoji_data, emojiset, alt_name, fallback_emoji_data) + print(f"Converting {emojiset} sheet to webp...") + TARGET_EMOJI_SHEETS = os.path.join(cache_path, "web", "emoji") + os.makedirs(TARGET_EMOJI_SHEETS, exist_ok=True) + + sheet_src = os.path.join( + NODE_MODULES_PATH, + f"emoji-datasource-{emojiset}", + "img", + alt_name, + "sheets-256", + "64.png", + ) + sheet_dst = os.path.join(TARGET_EMOJI_SHEETS, f"{emojiset}.webp") + # From libwebp: [Q is] between 0 and 100. For lossy, 0 gives + # the smallest size and 100 the largest. For lossless, this + # parameter is the amount of effort put into the + # compression: 0 is the fastest but gives larger files + # compared to the slowest, but best, 100. + subprocess.check_call(["vips", "copy", sheet_src, f"{sheet_dst}[lossless=true,Q=100]"]) + # Set up standard emoji sets. for emojiset in ["google", "twitter"]: setup_emoji_farm(emojiset, emoji_data) diff --git a/web/.gitignore b/web/.gitignore index f3873c8bf6..3ea477d029 100644 --- a/web/.gitignore +++ b/web/.gitignore @@ -1,4 +1,5 @@ # From emoji +/generated/emoji /generated/emoji-styles # From passing pygments data to the frontend /generated/pygments_data.json diff --git a/web/src/assets.d.ts b/web/src/assets.d.ts index 53b34255fe..9a7c16f75e 100644 --- a/web/src/assets.d.ts +++ b/web/src/assets.d.ts @@ -13,6 +13,11 @@ declare module "*.png" { export default url; } +declare module "*.webp" { + const url: string; + export default url; +} + // Declare the style loader for CSS files. This is used in the // `import` statements in the `emojisets.ts` file. declare module "!style-loader?*" { diff --git a/web/src/emojisets.ts b/web/src/emojisets.ts index 6abe738c68..c5760c2668 100644 --- a/web/src/emojisets.ts +++ b/web/src/emojisets.ts @@ -1,8 +1,7 @@ -import google_sheet from "emoji-datasource-google/img/google/sheets-256/64.png"; -import google_blob_sheet from "emoji-datasource-google-blob/img/google/sheets-256/64.png"; -import twitter_sheet from "emoji-datasource-twitter/img/twitter/sheets-256/64.png"; - import octopus_url from "../../static/generated/emoji/images-google-64/1f419.png"; +import google_blob_sheet from "../generated/emoji/google-blob.webp"; +import google_sheet from "../generated/emoji/google.webp"; +import twitter_sheet from "../generated/emoji/twitter.webp"; import * as blueslip from "./blueslip"; import {user_settings} from "./user_settings"; diff --git a/web/tests/copy_and_paste.test.js b/web/tests/copy_and_paste.test.js index ed6e0d6a94..a8263a1b8b 100644 --- a/web/tests/copy_and_paste.test.js +++ b/web/tests/copy_and_paste.test.js @@ -147,7 +147,7 @@ run_test("paste_handler_converter", () => { // Emojis input = - 'emojis: :smile: :family_man_woman_girl:'; + 'emojis: :smile: :family_man_woman_girl:'; assert.equal( copy_and_paste.paste_handler_converter(input), "emojis: :smile: :family_man_woman_girl:", diff --git a/web/webpack.config.ts b/web/webpack.config.ts index 3e7b8e261f..9c8a79526e 100644 --- a/web/webpack.config.ts +++ b/web/webpack.config.ts @@ -192,7 +192,7 @@ const config = ( }, // load fonts and files { - test: /\.(eot|jpg|svg|ttf|otf|png|woff2?)$/, + test: /\.(eot|jpg|svg|ttf|otf|png|webp|woff2?)$/, type: "asset/resource", }, ],