diff --git a/README.md b/README.md index 6f5c6de..224bc9a 100644 --- a/README.md +++ b/README.md @@ -61,19 +61,19 @@ cd Tela-icon-theme - - - + + + - - - + + + - - - + + +
diff --git a/assets/bluetooth.png b/assets/screenshots/bluetooth.png similarity index 100% rename from assets/bluetooth.png rename to assets/screenshots/bluetooth.png diff --git a/assets/cliphist.png b/assets/screenshots/cliphist.png similarity index 100% rename from assets/cliphist.png rename to assets/screenshots/cliphist.png diff --git a/assets/emoji.png b/assets/screenshots/emoji.png similarity index 100% rename from assets/emoji.png rename to assets/screenshots/emoji.png diff --git a/assets/home.png b/assets/screenshots/home.png similarity index 100% rename from assets/home.png rename to assets/screenshots/home.png diff --git a/assets/logo.svg b/assets/screenshots/logo.svg similarity index 100% rename from assets/logo.svg rename to assets/screenshots/logo.svg diff --git a/assets/notification.png b/assets/screenshots/notification.png similarity index 100% rename from assets/notification.png rename to assets/screenshots/notification.png diff --git a/assets/osd.png b/assets/screenshots/osd.png similarity index 100% rename from assets/osd.png rename to assets/screenshots/osd.png diff --git a/assets/powermenu.png b/assets/screenshots/powermenu.png similarity index 100% rename from assets/powermenu.png rename to assets/screenshots/powermenu.png diff --git a/assets/shrunner.png b/assets/screenshots/shrunner.png similarity index 100% rename from assets/shrunner.png rename to assets/screenshots/shrunner.png diff --git a/assets/todo.png b/assets/screenshots/todo.png similarity index 100% rename from assets/todo.png rename to assets/screenshots/todo.png diff --git a/assets/wallpaper.png b/assets/screenshots/wallpaper.png similarity index 100% rename from assets/wallpaper.png rename to assets/screenshots/wallpaper.png diff --git a/assets/wallpaper/example-1.png b/assets/wallpaper/example-1.png new file mode 100644 index 0000000..137ceee Binary files /dev/null and b/assets/wallpaper/example-1.png differ diff --git a/assets/wallpaper/example-2.png b/assets/wallpaper/example-2.png new file mode 100644 index 0000000..6d22cf9 Binary files /dev/null and b/assets/wallpaper/example-2.png differ diff --git a/assets/wallpaper/example-3.jpg b/assets/wallpaper/example-3.jpg new file mode 100755 index 0000000..5c2fd0d Binary files /dev/null and b/assets/wallpaper/example-3.jpg differ diff --git a/assets/wallpaper/example-4.jpeg b/assets/wallpaper/example-4.jpeg new file mode 100644 index 0000000..7ca7ae6 Binary files /dev/null and b/assets/wallpaper/example-4.jpeg differ diff --git a/assets/wallpaper/example-5.jpg b/assets/wallpaper/example-5.jpg new file mode 100644 index 0000000..eced4ab Binary files /dev/null and b/assets/wallpaper/example-5.jpg differ diff --git a/config/assets/config.json b/config/assets/config.json new file mode 100644 index 0000000..2f0a891 --- /dev/null +++ b/config/assets/config.json @@ -0,0 +1 @@ +{"prefix_restart": "SHIFT ALT", "suffix_restart": "T", "prefix_bluetooth": "ALT", "suffix_bluetooth": "B", "prefix_emoji": "SUPER", "suffix_emoji": "E", "prefix_cliphist": "SUPER", "suffix_cliphist": "V", "prefix_todo": "SUPER", "suffix_todo": "T", "prefix_sh": "SUPER", "suffix_sh": "S", "prefix_walls": "SUPER", "suffix_walls": "W", "prefix_screenshotregion": "SUPER", "suffix_screenshotregion": "Z", "prefix_screenshot": "", "suffix_screenshot": "Print", "prefix_screencast": "SUPER", "suffix_screencast": "R", "prefix_launcher": "SUPER", "suffix_launcher": "D", "prefix_power": "SUPER", "suffix_power": "X", "prefix_toggle": "SUPER", "suffix_toggle": "H", "prefix_randomwallpaper": "ALT SHIFT", "suffix_randomwallpaper": "W", "wallpapers_dir": "/home/sankalp/Pictures/wallpapers"} \ No newline at end of file diff --git a/config/assets/emoji.json b/config/assets/emoji.json new file mode 100644 index 0000000..fa174ff --- /dev/null +++ b/config/assets/emoji.json @@ -0,0 +1,15509 @@ +{ + "๐Ÿ˜€": { + "name": "grinning face", + "slug": "grinning_face", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ˜ƒ": { + "name": "grinning face with big eyes", + "slug": "grinning_face_with_big_eyes", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜„": { + "name": "grinning face with smiling eyes", + "slug": "grinning_face_with_smiling_eyes", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜": { + "name": "beaming face with smiling eyes", + "slug": "beaming_face_with_smiling_eyes", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜†": { + "name": "grinning squinting face", + "slug": "grinning_squinting_face", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜…": { + "name": "grinning face with sweat", + "slug": "grinning_face_with_sweat", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿคฃ": { + "name": "rolling on the floor laughing", + "slug": "rolling_on_the_floor_laughing", + "group": "Smileys & Emotion", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿ˜‚": { + "name": "face with tears of joy", + "slug": "face_with_tears_of_joy", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ™‚": { + "name": "slightly smiling face", + "slug": "slightly_smiling_face", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ™ƒ": { + "name": "upside-down face", + "slug": "upside_down_face", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿซ ": { + "name": "melting face", + "slug": "melting_face", + "group": "Smileys & Emotion", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": false + }, + "๐Ÿ˜‰": { + "name": "winking face", + "slug": "winking_face", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜Š": { + "name": "smiling face with smiling eyes", + "slug": "smiling_face_with_smiling_eyes", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜‡": { + "name": "smiling face with halo", + "slug": "smiling_face_with_halo", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿฅฐ": { + "name": "smiling face with hearts", + "slug": "smiling_face_with_hearts", + "group": "Smileys & Emotion", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿ˜": { + "name": "smiling face with heart-eyes", + "slug": "smiling_face_with_heart_eyes", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿคฉ": { + "name": "star-struck", + "slug": "star_struck", + "group": "Smileys & Emotion", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿ˜˜": { + "name": "face blowing a kiss", + "slug": "face_blowing_a_kiss", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜—": { + "name": "kissing face", + "slug": "kissing_face", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "โ˜บ๏ธ": { + "name": "smiling face", + "slug": "smiling_face", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜š": { + "name": "kissing face with closed eyes", + "slug": "kissing_face_with_closed_eyes", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜™": { + "name": "kissing face with smiling eyes", + "slug": "kissing_face_with_smiling_eyes", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿฅฒ": { + "name": "smiling face with tear", + "slug": "smiling_face_with_tear", + "group": "Smileys & Emotion", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿ˜‹": { + "name": "face savoring food", + "slug": "face_savoring_food", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜›": { + "name": "face with tongue", + "slug": "face_with_tongue", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ˜œ": { + "name": "winking face with tongue", + "slug": "winking_face_with_tongue", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿคช": { + "name": "zany face", + "slug": "zany_face", + "group": "Smileys & Emotion", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿ˜": { + "name": "squinting face with tongue", + "slug": "squinting_face_with_tongue", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿค‘": { + "name": "money-mouth face", + "slug": "money_mouth_face", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿค—": { + "name": "smiling face with open hands", + "slug": "smiling_face_with_open_hands", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿคญ": { + "name": "face with hand over mouth", + "slug": "face_with_hand_over_mouth", + "group": "Smileys & Emotion", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿซข": { + "name": "face with open eyes and hand over mouth", + "slug": "face_with_open_eyes_and_hand_over_mouth", + "group": "Smileys & Emotion", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": false + }, + "๐Ÿซฃ": { + "name": "face with peeking eye", + "slug": "face_with_peeking_eye", + "group": "Smileys & Emotion", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": false + }, + "๐Ÿคซ": { + "name": "shushing face", + "slug": "shushing_face", + "group": "Smileys & Emotion", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿค”": { + "name": "thinking face", + "slug": "thinking_face", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿซก": { + "name": "saluting face", + "slug": "saluting_face", + "group": "Smileys & Emotion", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": false + }, + "๐Ÿค": { + "name": "zipper-mouth face", + "slug": "zipper_mouth_face", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿคจ": { + "name": "face with raised eyebrow", + "slug": "face_with_raised_eyebrow", + "group": "Smileys & Emotion", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿ˜": { + "name": "neutral face", + "slug": "neutral_face", + "group": "Smileys & Emotion", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ˜‘": { + "name": "expressionless face", + "slug": "expressionless_face", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ˜ถ": { + "name": "face without mouth", + "slug": "face_without_mouth", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿซฅ": { + "name": "dotted line face", + "slug": "dotted_line_face", + "group": "Smileys & Emotion", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": false + }, + "๐Ÿ˜ถโ€๐ŸŒซ๏ธ": { + "name": "face in clouds", + "slug": "face_in_clouds", + "group": "Smileys & Emotion", + "emoji_version": "13.1", + "unicode_version": "13.1", + "skin_tone_support": false + }, + "๐Ÿ˜": { + "name": "smirking face", + "slug": "smirking_face", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜’": { + "name": "unamused face", + "slug": "unamused_face", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ™„": { + "name": "face with rolling eyes", + "slug": "face_with_rolling_eyes", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ˜ฌ": { + "name": "grimacing face", + "slug": "grimacing_face", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ˜ฎโ€๐Ÿ’จ": { + "name": "face exhaling", + "slug": "face_exhaling", + "group": "Smileys & Emotion", + "emoji_version": "13.1", + "unicode_version": "13.1", + "skin_tone_support": false + }, + "๐Ÿคฅ": { + "name": "lying face", + "slug": "lying_face", + "group": "Smileys & Emotion", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿซจ": { + "name": "shaking face", + "slug": "shaking_face", + "group": "Smileys & Emotion", + "emoji_version": "15.0", + "unicode_version": "15.0", + "skin_tone_support": false + }, + "๐Ÿ™‚โ€โ†”๏ธ": { + "name": "head shaking horizontally", + "slug": "head_shaking_horizontally", + "group": "Smileys & Emotion", + "emoji_version": "15.1", + "unicode_version": "15.1", + "skin_tone_support": false + }, + "๐Ÿ™‚โ€โ†•๏ธ": { + "name": "head shaking vertically", + "slug": "head_shaking_vertically", + "group": "Smileys & Emotion", + "emoji_version": "15.1", + "unicode_version": "15.1", + "skin_tone_support": false + }, + "๐Ÿ˜Œ": { + "name": "relieved face", + "slug": "relieved_face", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜”": { + "name": "pensive face", + "slug": "pensive_face", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜ช": { + "name": "sleepy face", + "slug": "sleepy_face", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿคค": { + "name": "drooling face", + "slug": "drooling_face", + "group": "Smileys & Emotion", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿ˜ด": { + "name": "sleeping face", + "slug": "sleeping_face", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ˜ท": { + "name": "face with medical mask", + "slug": "face_with_medical_mask", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿค’": { + "name": "face with thermometer", + "slug": "face_with_thermometer", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿค•": { + "name": "face with head-bandage", + "slug": "face_with_head_bandage", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿคข": { + "name": "nauseated face", + "slug": "nauseated_face", + "group": "Smileys & Emotion", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿคฎ": { + "name": "face vomiting", + "slug": "face_vomiting", + "group": "Smileys & Emotion", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿคง": { + "name": "sneezing face", + "slug": "sneezing_face", + "group": "Smileys & Emotion", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿฅต": { + "name": "hot face", + "slug": "hot_face", + "group": "Smileys & Emotion", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿฅถ": { + "name": "cold face", + "slug": "cold_face", + "group": "Smileys & Emotion", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿฅด": { + "name": "woozy face", + "slug": "woozy_face", + "group": "Smileys & Emotion", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿ˜ต": { + "name": "face with crossed-out eyes", + "slug": "face_with_crossed_out_eyes", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜ตโ€๐Ÿ’ซ": { + "name": "face with spiral eyes", + "slug": "face_with_spiral_eyes", + "group": "Smileys & Emotion", + "emoji_version": "13.1", + "unicode_version": "13.1", + "skin_tone_support": false + }, + "๐Ÿคฏ": { + "name": "exploding head", + "slug": "exploding_head", + "group": "Smileys & Emotion", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿค ": { + "name": "cowboy hat face", + "slug": "cowboy_hat_face", + "group": "Smileys & Emotion", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿฅณ": { + "name": "partying face", + "slug": "partying_face", + "group": "Smileys & Emotion", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿฅธ": { + "name": "disguised face", + "slug": "disguised_face", + "group": "Smileys & Emotion", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿ˜Ž": { + "name": "smiling face with sunglasses", + "slug": "smiling_face_with_sunglasses", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿค“": { + "name": "nerd face", + "slug": "nerd_face", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿง": { + "name": "face with monocle", + "slug": "face_with_monocle", + "group": "Smileys & Emotion", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿ˜•": { + "name": "confused face", + "slug": "confused_face", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿซค": { + "name": "face with diagonal mouth", + "slug": "face_with_diagonal_mouth", + "group": "Smileys & Emotion", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": false + }, + "๐Ÿ˜Ÿ": { + "name": "worried face", + "slug": "worried_face", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ™": { + "name": "slightly frowning face", + "slug": "slightly_frowning_face", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "โ˜น๏ธ": { + "name": "frowning face", + "slug": "frowning_face", + "group": "Smileys & Emotion", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ˜ฎ": { + "name": "face with open mouth", + "slug": "face_with_open_mouth", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ˜ฏ": { + "name": "hushed face", + "slug": "hushed_face", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ˜ฒ": { + "name": "astonished face", + "slug": "astonished_face", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜ณ": { + "name": "flushed face", + "slug": "flushed_face", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฅบ": { + "name": "pleading face", + "slug": "pleading_face", + "group": "Smileys & Emotion", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿฅน": { + "name": "face holding back tears", + "slug": "face_holding_back_tears", + "group": "Smileys & Emotion", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": false + }, + "๐Ÿ˜ฆ": { + "name": "frowning face with open mouth", + "slug": "frowning_face_with_open_mouth", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ˜ง": { + "name": "anguished face", + "slug": "anguished_face", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ˜จ": { + "name": "fearful face", + "slug": "fearful_face", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜ฐ": { + "name": "anxious face with sweat", + "slug": "anxious_face_with_sweat", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜ฅ": { + "name": "sad but relieved face", + "slug": "sad_but_relieved_face", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜ข": { + "name": "crying face", + "slug": "crying_face", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜ญ": { + "name": "loudly crying face", + "slug": "loudly_crying_face", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜ฑ": { + "name": "face screaming in fear", + "slug": "face_screaming_in_fear", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜–": { + "name": "confounded face", + "slug": "confounded_face", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜ฃ": { + "name": "persevering face", + "slug": "persevering_face", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜ž": { + "name": "disappointed face", + "slug": "disappointed_face", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜“": { + "name": "downcast face with sweat", + "slug": "downcast_face_with_sweat", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜ฉ": { + "name": "weary face", + "slug": "weary_face", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜ซ": { + "name": "tired face", + "slug": "tired_face", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฅฑ": { + "name": "yawning face", + "slug": "yawning_face", + "group": "Smileys & Emotion", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿ˜ค": { + "name": "face with steam from nose", + "slug": "face_with_steam_from_nose", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜ก": { + "name": "enraged face", + "slug": "enraged_face", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜ ": { + "name": "angry face", + "slug": "angry_face", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿคฌ": { + "name": "face with symbols on mouth", + "slug": "face_with_symbols_on_mouth", + "group": "Smileys & Emotion", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿ˜ˆ": { + "name": "smiling face with horns", + "slug": "smiling_face_with_horns", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ‘ฟ": { + "name": "angry face with horns", + "slug": "angry_face_with_horns", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’€": { + "name": "skull", + "slug": "skull", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ˜ ๏ธ": { + "name": "skull and crossbones", + "slug": "skull_and_crossbones", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ’ฉ": { + "name": "pile of poo", + "slug": "pile_of_poo", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿคก": { + "name": "clown face", + "slug": "clown_face", + "group": "Smileys & Emotion", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿ‘น": { + "name": "ogre", + "slug": "ogre", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‘บ": { + "name": "goblin", + "slug": "goblin", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‘ป": { + "name": "ghost", + "slug": "ghost", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‘ฝ": { + "name": "alien", + "slug": "alien", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‘พ": { + "name": "alien monster", + "slug": "alien_monster", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿค–": { + "name": "robot", + "slug": "robot", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ˜บ": { + "name": "grinning cat", + "slug": "grinning_cat", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜ธ": { + "name": "grinning cat with smiling eyes", + "slug": "grinning_cat_with_smiling_eyes", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜น": { + "name": "cat with tears of joy", + "slug": "cat_with_tears_of_joy", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜ป": { + "name": "smiling cat with heart-eyes", + "slug": "smiling_cat_with_heart_eyes", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜ผ": { + "name": "cat with wry smile", + "slug": "cat_with_wry_smile", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜ฝ": { + "name": "kissing cat", + "slug": "kissing_cat", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ™€": { + "name": "weary cat", + "slug": "weary_cat", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜ฟ": { + "name": "crying cat", + "slug": "crying_cat", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜พ": { + "name": "pouting cat", + "slug": "pouting_cat", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ™ˆ": { + "name": "see-no-evil monkey", + "slug": "see_no_evil_monkey", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ™‰": { + "name": "hear-no-evil monkey", + "slug": "hear_no_evil_monkey", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ™Š": { + "name": "speak-no-evil monkey", + "slug": "speak_no_evil_monkey", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’Œ": { + "name": "love letter", + "slug": "love_letter", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’˜": { + "name": "heart with arrow", + "slug": "heart_with_arrow", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’": { + "name": "heart with ribbon", + "slug": "heart_with_ribbon", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’–": { + "name": "sparkling heart", + "slug": "sparkling_heart", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’—": { + "name": "growing heart", + "slug": "growing_heart", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’“": { + "name": "beating heart", + "slug": "beating_heart", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’ž": { + "name": "revolving hearts", + "slug": "revolving_hearts", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’•": { + "name": "two hearts", + "slug": "two_hearts", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’Ÿ": { + "name": "heart decoration", + "slug": "heart_decoration", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โฃ๏ธ": { + "name": "heart exclamation", + "slug": "heart_exclamation", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ’”": { + "name": "broken heart", + "slug": "broken_heart", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โค๏ธโ€๐Ÿ”ฅ": { + "name": "heart on fire", + "slug": "heart_on_fire", + "group": "Smileys & Emotion", + "emoji_version": "13.1", + "unicode_version": "13.1", + "skin_tone_support": false + }, + "โค๏ธโ€๐Ÿฉน": { + "name": "mending heart", + "slug": "mending_heart", + "group": "Smileys & Emotion", + "emoji_version": "13.1", + "unicode_version": "13.1", + "skin_tone_support": false + }, + "โค๏ธ": { + "name": "red heart", + "slug": "red_heart", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฉท": { + "name": "pink heart", + "slug": "pink_heart", + "group": "Smileys & Emotion", + "emoji_version": "15.0", + "unicode_version": "15.0", + "skin_tone_support": false + }, + "๐Ÿงก": { + "name": "orange heart", + "slug": "orange_heart", + "group": "Smileys & Emotion", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿ’›": { + "name": "yellow heart", + "slug": "yellow_heart", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’š": { + "name": "green heart", + "slug": "green_heart", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’™": { + "name": "blue heart", + "slug": "blue_heart", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฉต": { + "name": "light blue heart", + "slug": "light_blue_heart", + "group": "Smileys & Emotion", + "emoji_version": "15.0", + "unicode_version": "15.0", + "skin_tone_support": false + }, + "๐Ÿ’œ": { + "name": "purple heart", + "slug": "purple_heart", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸคŽ": { + "name": "brown heart", + "slug": "brown_heart", + "group": "Smileys & Emotion", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿ–ค": { + "name": "black heart", + "slug": "black_heart", + "group": "Smileys & Emotion", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿฉถ": { + "name": "grey heart", + "slug": "grey_heart", + "group": "Smileys & Emotion", + "emoji_version": "15.0", + "unicode_version": "15.0", + "skin_tone_support": false + }, + "๐Ÿค": { + "name": "white heart", + "slug": "white_heart", + "group": "Smileys & Emotion", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿ’‹": { + "name": "kiss mark", + "slug": "kiss_mark", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’ฏ": { + "name": "hundred points", + "slug": "hundred_points", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’ข": { + "name": "anger symbol", + "slug": "anger_symbol", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’ฅ": { + "name": "collision", + "slug": "collision", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’ซ": { + "name": "dizzy", + "slug": "dizzy", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’ฆ": { + "name": "sweat droplets", + "slug": "sweat_droplets", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’จ": { + "name": "dashing away", + "slug": "dashing_away", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ•ณ๏ธ": { + "name": "hole", + "slug": "hole", + "group": "Smileys & Emotion", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ’ฌ": { + "name": "speech balloon", + "slug": "speech_balloon", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‘๏ธโ€๐Ÿ—จ๏ธ": { + "name": "eye in speech bubble", + "slug": "eye_in_speech_bubble", + "group": "Smileys & Emotion", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ—จ๏ธ": { + "name": "left speech bubble", + "slug": "left_speech_bubble", + "group": "Smileys & Emotion", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ—ฏ๏ธ": { + "name": "right anger bubble", + "slug": "right_anger_bubble", + "group": "Smileys & Emotion", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ’ญ": { + "name": "thought balloon", + "slug": "thought_balloon", + "group": "Smileys & Emotion", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ’ค": { + "name": "ZZZ", + "slug": "zzz", + "group": "Smileys & Emotion", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‘‹": { + "name": "waving hand", + "slug": "waving_hand", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿคš": { + "name": "raised back of hand", + "slug": "raised_back_of_hand", + "group": "People & Body", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "3.0" + }, + "๐Ÿ–๏ธ": { + "name": "hand with fingers splayed", + "slug": "hand_with_fingers_splayed", + "group": "People & Body", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "โœ‹": { + "name": "raised hand", + "slug": "raised_hand", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ––": { + "name": "vulcan salute", + "slug": "vulcan_salute", + "group": "People & Body", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿซฑ": { + "name": "rightwards hand", + "slug": "rightwards_hand", + "group": "People & Body", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "14.0" + }, + "๐Ÿซฒ": { + "name": "leftwards hand", + "slug": "leftwards_hand", + "group": "People & Body", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "14.0" + }, + "๐Ÿซณ": { + "name": "palm down hand", + "slug": "palm_down_hand", + "group": "People & Body", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "14.0" + }, + "๐Ÿซด": { + "name": "palm up hand", + "slug": "palm_up_hand", + "group": "People & Body", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "14.0" + }, + "๐Ÿซท": { + "name": "leftwards pushing hand", + "slug": "leftwards_pushing_hand", + "group": "People & Body", + "emoji_version": "15.0", + "unicode_version": "15.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "15.0" + }, + "๐Ÿซธ": { + "name": "rightwards pushing hand", + "slug": "rightwards_pushing_hand", + "group": "People & Body", + "emoji_version": "15.0", + "unicode_version": "15.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "15.0" + }, + "๐Ÿ‘Œ": { + "name": "OK hand", + "slug": "ok_hand", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐ŸคŒ": { + "name": "pinched fingers", + "slug": "pinched_fingers", + "group": "People & Body", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "13.0" + }, + "๐Ÿค": { + "name": "pinching hand", + "slug": "pinching_hand", + "group": "People & Body", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.0" + }, + "โœŒ๏ธ": { + "name": "victory hand", + "slug": "victory_hand", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿคž": { + "name": "crossed fingers", + "slug": "crossed_fingers", + "group": "People & Body", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "3.0" + }, + "๐Ÿซฐ": { + "name": "hand with index finger and thumb crossed", + "slug": "hand_with_index_finger_and_thumb_crossed", + "group": "People & Body", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "14.0" + }, + "๐ŸคŸ": { + "name": "love-you gesture", + "slug": "love_you_gesture", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿค˜": { + "name": "sign of the horns", + "slug": "sign_of_the_horns", + "group": "People & Body", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿค™": { + "name": "call me hand", + "slug": "call_me_hand", + "group": "People & Body", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "3.0" + }, + "๐Ÿ‘ˆ": { + "name": "backhand index pointing left", + "slug": "backhand_index_pointing_left", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ‘‰": { + "name": "backhand index pointing right", + "slug": "backhand_index_pointing_right", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ‘†": { + "name": "backhand index pointing up", + "slug": "backhand_index_pointing_up", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ–•": { + "name": "middle finger", + "slug": "middle_finger", + "group": "People & Body", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ‘‡": { + "name": "backhand index pointing down", + "slug": "backhand_index_pointing_down", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "โ˜๏ธ": { + "name": "index pointing up", + "slug": "index_pointing_up", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿซต": { + "name": "index pointing at the viewer", + "slug": "index_pointing_at_the_viewer", + "group": "People & Body", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "14.0" + }, + "๐Ÿ‘": { + "name": "thumbs up", + "slug": "thumbs_up", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ‘Ž": { + "name": "thumbs down", + "slug": "thumbs_down", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "โœŠ": { + "name": "raised fist", + "slug": "raised_fist", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ‘Š": { + "name": "oncoming fist", + "slug": "oncoming_fist", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿค›": { + "name": "left-facing fist", + "slug": "left_facing_fist", + "group": "People & Body", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "3.0" + }, + "๐Ÿคœ": { + "name": "right-facing fist", + "slug": "right_facing_fist", + "group": "People & Body", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "3.0" + }, + "๐Ÿ‘": { + "name": "clapping hands", + "slug": "clapping_hands", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ™Œ": { + "name": "raising hands", + "slug": "raising_hands", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿซถ": { + "name": "heart hands", + "slug": "heart_hands", + "group": "People & Body", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "14.0" + }, + "๐Ÿ‘": { + "name": "open hands", + "slug": "open_hands", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿคฒ": { + "name": "palms up together", + "slug": "palms_up_together", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿค": { + "name": "handshake", + "slug": "handshake", + "group": "People & Body", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "14.0" + }, + "๐Ÿ™": { + "name": "folded hands", + "slug": "folded_hands", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "โœ๏ธ": { + "name": "writing hand", + "slug": "writing_hand", + "group": "People & Body", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ’…": { + "name": "nail polish", + "slug": "nail_polish", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿคณ": { + "name": "selfie", + "slug": "selfie", + "group": "People & Body", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "3.0" + }, + "๐Ÿ’ช": { + "name": "flexed biceps", + "slug": "flexed_biceps", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿฆพ": { + "name": "mechanical arm", + "slug": "mechanical_arm", + "group": "People & Body", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿฆฟ": { + "name": "mechanical leg", + "slug": "mechanical_leg", + "group": "People & Body", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿฆต": { + "name": "leg", + "slug": "leg", + "group": "People & Body", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "11.0" + }, + "๐Ÿฆถ": { + "name": "foot", + "slug": "foot", + "group": "People & Body", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "11.0" + }, + "๐Ÿ‘‚": { + "name": "ear", + "slug": "ear", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿฆป": { + "name": "ear with hearing aid", + "slug": "ear_with_hearing_aid", + "group": "People & Body", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.0" + }, + "๐Ÿ‘ƒ": { + "name": "nose", + "slug": "nose", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿง ": { + "name": "brain", + "slug": "brain", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿซ€": { + "name": "anatomical heart", + "slug": "anatomical_heart", + "group": "People & Body", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿซ": { + "name": "lungs", + "slug": "lungs", + "group": "People & Body", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿฆท": { + "name": "tooth", + "slug": "tooth", + "group": "People & Body", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿฆด": { + "name": "bone", + "slug": "bone", + "group": "People & Body", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿ‘€": { + "name": "eyes", + "slug": "eyes", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‘๏ธ": { + "name": "eye", + "slug": "eye", + "group": "People & Body", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ‘…": { + "name": "tongue", + "slug": "tongue", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‘„": { + "name": "mouth", + "slug": "mouth", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿซฆ": { + "name": "biting lip", + "slug": "biting_lip", + "group": "People & Body", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": false + }, + "๐Ÿ‘ถ": { + "name": "baby", + "slug": "baby", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿง’": { + "name": "child", + "slug": "child", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿ‘ฆ": { + "name": "boy", + "slug": "boy", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ‘ง": { + "name": "girl", + "slug": "girl", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿง‘": { + "name": "person", + "slug": "person", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿ‘ฑ": { + "name": "person blond hair", + "slug": "person_blond_hair", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ‘จ": { + "name": "man", + "slug": "man", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿง”": { + "name": "person beard", + "slug": "person_beard", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿง”โ€โ™‚๏ธ": { + "name": "man beard", + "slug": "man_beard", + "group": "People & Body", + "emoji_version": "13.1", + "unicode_version": "13.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "13.1" + }, + "๐Ÿง”โ€โ™€๏ธ": { + "name": "woman beard", + "slug": "woman_beard", + "group": "People & Body", + "emoji_version": "13.1", + "unicode_version": "13.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "13.1" + }, + "๐Ÿ‘จโ€๐Ÿฆฐ": { + "name": "man red hair", + "slug": "man_red_hair", + "group": "People & Body", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "11.0" + }, + "๐Ÿ‘จโ€๐Ÿฆฑ": { + "name": "man curly hair", + "slug": "man_curly_hair", + "group": "People & Body", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "11.0" + }, + "๐Ÿ‘จโ€๐Ÿฆณ": { + "name": "man white hair", + "slug": "man_white_hair", + "group": "People & Body", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "11.0" + }, + "๐Ÿ‘จโ€๐Ÿฆฒ": { + "name": "man bald", + "slug": "man_bald", + "group": "People & Body", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "11.0" + }, + "๐Ÿ‘ฉ": { + "name": "woman", + "slug": "woman", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ‘ฉโ€๐Ÿฆฐ": { + "name": "woman red hair", + "slug": "woman_red_hair", + "group": "People & Body", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "11.0" + }, + "๐Ÿง‘โ€๐Ÿฆฐ": { + "name": "person red hair", + "slug": "person_red_hair", + "group": "People & Body", + "emoji_version": "12.1", + "unicode_version": "12.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.1" + }, + "๐Ÿ‘ฉโ€๐Ÿฆฑ": { + "name": "woman curly hair", + "slug": "woman_curly_hair", + "group": "People & Body", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "11.0" + }, + "๐Ÿง‘โ€๐Ÿฆฑ": { + "name": "person curly hair", + "slug": "person_curly_hair", + "group": "People & Body", + "emoji_version": "12.1", + "unicode_version": "12.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.1" + }, + "๐Ÿ‘ฉโ€๐Ÿฆณ": { + "name": "woman white hair", + "slug": "woman_white_hair", + "group": "People & Body", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "11.0" + }, + "๐Ÿง‘โ€๐Ÿฆณ": { + "name": "person white hair", + "slug": "person_white_hair", + "group": "People & Body", + "emoji_version": "12.1", + "unicode_version": "12.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.1" + }, + "๐Ÿ‘ฉโ€๐Ÿฆฒ": { + "name": "woman bald", + "slug": "woman_bald", + "group": "People & Body", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "11.0" + }, + "๐Ÿง‘โ€๐Ÿฆฒ": { + "name": "person bald", + "slug": "person_bald", + "group": "People & Body", + "emoji_version": "12.1", + "unicode_version": "12.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.1" + }, + "๐Ÿ‘ฑโ€โ™€๏ธ": { + "name": "woman blond hair", + "slug": "woman_blond_hair", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ‘ฑโ€โ™‚๏ธ": { + "name": "man blond hair", + "slug": "man_blond_hair", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿง“": { + "name": "older person", + "slug": "older_person", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿ‘ด": { + "name": "old man", + "slug": "old_man", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ‘ต": { + "name": "old woman", + "slug": "old_woman", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ™": { + "name": "person frowning", + "slug": "person_frowning", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ™โ€โ™‚๏ธ": { + "name": "man frowning", + "slug": "man_frowning", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ™โ€โ™€๏ธ": { + "name": "woman frowning", + "slug": "woman_frowning", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ™Ž": { + "name": "person pouting", + "slug": "person_pouting", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ™Žโ€โ™‚๏ธ": { + "name": "man pouting", + "slug": "man_pouting", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ™Žโ€โ™€๏ธ": { + "name": "woman pouting", + "slug": "woman_pouting", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ™…": { + "name": "person gesturing NO", + "slug": "person_gesturing_no", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ™…โ€โ™‚๏ธ": { + "name": "man gesturing NO", + "slug": "man_gesturing_no", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ™…โ€โ™€๏ธ": { + "name": "woman gesturing NO", + "slug": "woman_gesturing_no", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ™†": { + "name": "person gesturing OK", + "slug": "person_gesturing_ok", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ™†โ€โ™‚๏ธ": { + "name": "man gesturing OK", + "slug": "man_gesturing_ok", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ™†โ€โ™€๏ธ": { + "name": "woman gesturing OK", + "slug": "woman_gesturing_ok", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ’": { + "name": "person tipping hand", + "slug": "person_tipping_hand", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ’โ€โ™‚๏ธ": { + "name": "man tipping hand", + "slug": "man_tipping_hand", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ’โ€โ™€๏ธ": { + "name": "woman tipping hand", + "slug": "woman_tipping_hand", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ™‹": { + "name": "person raising hand", + "slug": "person_raising_hand", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ™‹โ€โ™‚๏ธ": { + "name": "man raising hand", + "slug": "man_raising_hand", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ™‹โ€โ™€๏ธ": { + "name": "woman raising hand", + "slug": "woman_raising_hand", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿง": { + "name": "deaf person", + "slug": "deaf_person", + "group": "People & Body", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.0" + }, + "๐Ÿงโ€โ™‚๏ธ": { + "name": "deaf man", + "slug": "deaf_man", + "group": "People & Body", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.0" + }, + "๐Ÿงโ€โ™€๏ธ": { + "name": "deaf woman", + "slug": "deaf_woman", + "group": "People & Body", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.0" + }, + "๐Ÿ™‡": { + "name": "person bowing", + "slug": "person_bowing", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ™‡โ€โ™‚๏ธ": { + "name": "man bowing", + "slug": "man_bowing", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ™‡โ€โ™€๏ธ": { + "name": "woman bowing", + "slug": "woman_bowing", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿคฆ": { + "name": "person facepalming", + "slug": "person_facepalming", + "group": "People & Body", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "3.0" + }, + "๐Ÿคฆโ€โ™‚๏ธ": { + "name": "man facepalming", + "slug": "man_facepalming", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿคฆโ€โ™€๏ธ": { + "name": "woman facepalming", + "slug": "woman_facepalming", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿคท": { + "name": "person shrugging", + "slug": "person_shrugging", + "group": "People & Body", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "3.0" + }, + "๐Ÿคทโ€โ™‚๏ธ": { + "name": "man shrugging", + "slug": "man_shrugging", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿคทโ€โ™€๏ธ": { + "name": "woman shrugging", + "slug": "woman_shrugging", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿง‘โ€โš•๏ธ": { + "name": "health worker", + "slug": "health_worker", + "group": "People & Body", + "emoji_version": "12.1", + "unicode_version": "12.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.1" + }, + "๐Ÿ‘จโ€โš•๏ธ": { + "name": "man health worker", + "slug": "man_health_worker", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ‘ฉโ€โš•๏ธ": { + "name": "woman health worker", + "slug": "woman_health_worker", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿง‘โ€๐ŸŽ“": { + "name": "student", + "slug": "student", + "group": "People & Body", + "emoji_version": "12.1", + "unicode_version": "12.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.1" + }, + "๐Ÿ‘จโ€๐ŸŽ“": { + "name": "man student", + "slug": "man_student", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ‘ฉโ€๐ŸŽ“": { + "name": "woman student", + "slug": "woman_student", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿง‘โ€๐Ÿซ": { + "name": "teacher", + "slug": "teacher", + "group": "People & Body", + "emoji_version": "12.1", + "unicode_version": "12.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.1" + }, + "๐Ÿ‘จโ€๐Ÿซ": { + "name": "man teacher", + "slug": "man_teacher", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ‘ฉโ€๐Ÿซ": { + "name": "woman teacher", + "slug": "woman_teacher", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿง‘โ€โš–๏ธ": { + "name": "judge", + "slug": "judge", + "group": "People & Body", + "emoji_version": "12.1", + "unicode_version": "12.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.1" + }, + "๐Ÿ‘จโ€โš–๏ธ": { + "name": "man judge", + "slug": "man_judge", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ‘ฉโ€โš–๏ธ": { + "name": "woman judge", + "slug": "woman_judge", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿง‘โ€๐ŸŒพ": { + "name": "farmer", + "slug": "farmer", + "group": "People & Body", + "emoji_version": "12.1", + "unicode_version": "12.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.1" + }, + "๐Ÿ‘จโ€๐ŸŒพ": { + "name": "man farmer", + "slug": "man_farmer", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ‘ฉโ€๐ŸŒพ": { + "name": "woman farmer", + "slug": "woman_farmer", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿง‘โ€๐Ÿณ": { + "name": "cook", + "slug": "cook", + "group": "People & Body", + "emoji_version": "12.1", + "unicode_version": "12.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.1" + }, + "๐Ÿ‘จโ€๐Ÿณ": { + "name": "man cook", + "slug": "man_cook", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ‘ฉโ€๐Ÿณ": { + "name": "woman cook", + "slug": "woman_cook", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿง‘โ€๐Ÿ”ง": { + "name": "mechanic", + "slug": "mechanic", + "group": "People & Body", + "emoji_version": "12.1", + "unicode_version": "12.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.1" + }, + "๐Ÿ‘จโ€๐Ÿ”ง": { + "name": "man mechanic", + "slug": "man_mechanic", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ‘ฉโ€๐Ÿ”ง": { + "name": "woman mechanic", + "slug": "woman_mechanic", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿง‘โ€๐Ÿญ": { + "name": "factory worker", + "slug": "factory_worker", + "group": "People & Body", + "emoji_version": "12.1", + "unicode_version": "12.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.1" + }, + "๐Ÿ‘จโ€๐Ÿญ": { + "name": "man factory worker", + "slug": "man_factory_worker", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ‘ฉโ€๐Ÿญ": { + "name": "woman factory worker", + "slug": "woman_factory_worker", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿง‘โ€๐Ÿ’ผ": { + "name": "office worker", + "slug": "office_worker", + "group": "People & Body", + "emoji_version": "12.1", + "unicode_version": "12.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.1" + }, + "๐Ÿ‘จโ€๐Ÿ’ผ": { + "name": "man office worker", + "slug": "man_office_worker", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ‘ฉโ€๐Ÿ’ผ": { + "name": "woman office worker", + "slug": "woman_office_worker", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿง‘โ€๐Ÿ”ฌ": { + "name": "scientist", + "slug": "scientist", + "group": "People & Body", + "emoji_version": "12.1", + "unicode_version": "12.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.1" + }, + "๐Ÿ‘จโ€๐Ÿ”ฌ": { + "name": "man scientist", + "slug": "man_scientist", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ‘ฉโ€๐Ÿ”ฌ": { + "name": "woman scientist", + "slug": "woman_scientist", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿง‘โ€๐Ÿ’ป": { + "name": "technologist", + "slug": "technologist", + "group": "People & Body", + "emoji_version": "12.1", + "unicode_version": "12.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.1" + }, + "๐Ÿ‘จโ€๐Ÿ’ป": { + "name": "man technologist", + "slug": "man_technologist", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ‘ฉโ€๐Ÿ’ป": { + "name": "woman technologist", + "slug": "woman_technologist", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿง‘โ€๐ŸŽค": { + "name": "singer", + "slug": "singer", + "group": "People & Body", + "emoji_version": "12.1", + "unicode_version": "12.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.1" + }, + "๐Ÿ‘จโ€๐ŸŽค": { + "name": "man singer", + "slug": "man_singer", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ‘ฉโ€๐ŸŽค": { + "name": "woman singer", + "slug": "woman_singer", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿง‘โ€๐ŸŽจ": { + "name": "artist", + "slug": "artist", + "group": "People & Body", + "emoji_version": "12.1", + "unicode_version": "12.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.1" + }, + "๐Ÿ‘จโ€๐ŸŽจ": { + "name": "man artist", + "slug": "man_artist", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ‘ฉโ€๐ŸŽจ": { + "name": "woman artist", + "slug": "woman_artist", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿง‘โ€โœˆ๏ธ": { + "name": "pilot", + "slug": "pilot", + "group": "People & Body", + "emoji_version": "12.1", + "unicode_version": "12.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.1" + }, + "๐Ÿ‘จโ€โœˆ๏ธ": { + "name": "man pilot", + "slug": "man_pilot", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ‘ฉโ€โœˆ๏ธ": { + "name": "woman pilot", + "slug": "woman_pilot", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿง‘โ€๐Ÿš€": { + "name": "astronaut", + "slug": "astronaut", + "group": "People & Body", + "emoji_version": "12.1", + "unicode_version": "12.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.1" + }, + "๐Ÿ‘จโ€๐Ÿš€": { + "name": "man astronaut", + "slug": "man_astronaut", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ‘ฉโ€๐Ÿš€": { + "name": "woman astronaut", + "slug": "woman_astronaut", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿง‘โ€๐Ÿš’": { + "name": "firefighter", + "slug": "firefighter", + "group": "People & Body", + "emoji_version": "12.1", + "unicode_version": "12.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.1" + }, + "๐Ÿ‘จโ€๐Ÿš’": { + "name": "man firefighter", + "slug": "man_firefighter", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ‘ฉโ€๐Ÿš’": { + "name": "woman firefighter", + "slug": "woman_firefighter", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ‘ฎ": { + "name": "police officer", + "slug": "police_officer", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ‘ฎโ€โ™‚๏ธ": { + "name": "man police officer", + "slug": "man_police_officer", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ‘ฎโ€โ™€๏ธ": { + "name": "woman police officer", + "slug": "woman_police_officer", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ•ต๏ธ": { + "name": "detective", + "slug": "detective", + "group": "People & Body", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "2.0" + }, + "๐Ÿ•ต๏ธโ€โ™‚๏ธ": { + "name": "man detective", + "slug": "man_detective", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ•ต๏ธโ€โ™€๏ธ": { + "name": "woman detective", + "slug": "woman_detective", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ’‚": { + "name": "guard", + "slug": "guard", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ’‚โ€โ™‚๏ธ": { + "name": "man guard", + "slug": "man_guard", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ’‚โ€โ™€๏ธ": { + "name": "woman guard", + "slug": "woman_guard", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿฅท": { + "name": "ninja", + "slug": "ninja", + "group": "People & Body", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "13.0" + }, + "๐Ÿ‘ท": { + "name": "construction worker", + "slug": "construction_worker", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ‘ทโ€โ™‚๏ธ": { + "name": "man construction worker", + "slug": "man_construction_worker", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ‘ทโ€โ™€๏ธ": { + "name": "woman construction worker", + "slug": "woman_construction_worker", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿซ…": { + "name": "person with crown", + "slug": "person_with_crown", + "group": "People & Body", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "14.0" + }, + "๐Ÿคด": { + "name": "prince", + "slug": "prince", + "group": "People & Body", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "3.0" + }, + "๐Ÿ‘ธ": { + "name": "princess", + "slug": "princess", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ‘ณ": { + "name": "person wearing turban", + "slug": "person_wearing_turban", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ‘ณโ€โ™‚๏ธ": { + "name": "man wearing turban", + "slug": "man_wearing_turban", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ‘ณโ€โ™€๏ธ": { + "name": "woman wearing turban", + "slug": "woman_wearing_turban", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ‘ฒ": { + "name": "person with skullcap", + "slug": "person_with_skullcap", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿง•": { + "name": "woman with headscarf", + "slug": "woman_with_headscarf", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿคต": { + "name": "person in tuxedo", + "slug": "person_in_tuxedo", + "group": "People & Body", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "3.0" + }, + "๐Ÿคตโ€โ™‚๏ธ": { + "name": "man in tuxedo", + "slug": "man_in_tuxedo", + "group": "People & Body", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "13.0" + }, + "๐Ÿคตโ€โ™€๏ธ": { + "name": "woman in tuxedo", + "slug": "woman_in_tuxedo", + "group": "People & Body", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "13.0" + }, + "๐Ÿ‘ฐ": { + "name": "person with veil", + "slug": "person_with_veil", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ‘ฐโ€โ™‚๏ธ": { + "name": "man with veil", + "slug": "man_with_veil", + "group": "People & Body", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "13.0" + }, + "๐Ÿ‘ฐโ€โ™€๏ธ": { + "name": "woman with veil", + "slug": "woman_with_veil", + "group": "People & Body", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "13.0" + }, + "๐Ÿคฐ": { + "name": "pregnant woman", + "slug": "pregnant_woman", + "group": "People & Body", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "3.0" + }, + "๐Ÿซƒ": { + "name": "pregnant man", + "slug": "pregnant_man", + "group": "People & Body", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "14.0" + }, + "๐Ÿซ„": { + "name": "pregnant person", + "slug": "pregnant_person", + "group": "People & Body", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "14.0" + }, + "๐Ÿคฑ": { + "name": "breast-feeding", + "slug": "breast_feeding", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿ‘ฉโ€๐Ÿผ": { + "name": "woman feeding baby", + "slug": "woman_feeding_baby", + "group": "People & Body", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "13.0" + }, + "๐Ÿ‘จโ€๐Ÿผ": { + "name": "man feeding baby", + "slug": "man_feeding_baby", + "group": "People & Body", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "13.0" + }, + "๐Ÿง‘โ€๐Ÿผ": { + "name": "person feeding baby", + "slug": "person_feeding_baby", + "group": "People & Body", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "13.0" + }, + "๐Ÿ‘ผ": { + "name": "baby angel", + "slug": "baby_angel", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐ŸŽ…": { + "name": "Santa Claus", + "slug": "santa_claus", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿคถ": { + "name": "Mrs. Claus", + "slug": "mrs_claus", + "group": "People & Body", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "3.0" + }, + "๐Ÿง‘โ€๐ŸŽ„": { + "name": "mx claus", + "slug": "mx_claus", + "group": "People & Body", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "13.0" + }, + "๐Ÿฆธ": { + "name": "superhero", + "slug": "superhero", + "group": "People & Body", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "11.0" + }, + "๐Ÿฆธโ€โ™‚๏ธ": { + "name": "man superhero", + "slug": "man_superhero", + "group": "People & Body", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "11.0" + }, + "๐Ÿฆธโ€โ™€๏ธ": { + "name": "woman superhero", + "slug": "woman_superhero", + "group": "People & Body", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "11.0" + }, + "๐Ÿฆน": { + "name": "supervillain", + "slug": "supervillain", + "group": "People & Body", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "11.0" + }, + "๐Ÿฆนโ€โ™‚๏ธ": { + "name": "man supervillain", + "slug": "man_supervillain", + "group": "People & Body", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "11.0" + }, + "๐Ÿฆนโ€โ™€๏ธ": { + "name": "woman supervillain", + "slug": "woman_supervillain", + "group": "People & Body", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "11.0" + }, + "๐Ÿง™": { + "name": "mage", + "slug": "mage", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿง™โ€โ™‚๏ธ": { + "name": "man mage", + "slug": "man_mage", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿง™โ€โ™€๏ธ": { + "name": "woman mage", + "slug": "woman_mage", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿงš": { + "name": "fairy", + "slug": "fairy", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿงšโ€โ™‚๏ธ": { + "name": "man fairy", + "slug": "man_fairy", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿงšโ€โ™€๏ธ": { + "name": "woman fairy", + "slug": "woman_fairy", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿง›": { + "name": "vampire", + "slug": "vampire", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿง›โ€โ™‚๏ธ": { + "name": "man vampire", + "slug": "man_vampire", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿง›โ€โ™€๏ธ": { + "name": "woman vampire", + "slug": "woman_vampire", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿงœ": { + "name": "merperson", + "slug": "merperson", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿงœโ€โ™‚๏ธ": { + "name": "merman", + "slug": "merman", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿงœโ€โ™€๏ธ": { + "name": "mermaid", + "slug": "mermaid", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿง": { + "name": "elf", + "slug": "elf", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿงโ€โ™‚๏ธ": { + "name": "man elf", + "slug": "man_elf", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿงโ€โ™€๏ธ": { + "name": "woman elf", + "slug": "woman_elf", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿงž": { + "name": "genie", + "slug": "genie", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿงžโ€โ™‚๏ธ": { + "name": "man genie", + "slug": "man_genie", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿงžโ€โ™€๏ธ": { + "name": "woman genie", + "slug": "woman_genie", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐ŸงŸ": { + "name": "zombie", + "slug": "zombie", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐ŸงŸโ€โ™‚๏ธ": { + "name": "man zombie", + "slug": "man_zombie", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐ŸงŸโ€โ™€๏ธ": { + "name": "woman zombie", + "slug": "woman_zombie", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐ŸงŒ": { + "name": "troll", + "slug": "troll", + "group": "People & Body", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": false + }, + "๐Ÿ’†": { + "name": "person getting massage", + "slug": "person_getting_massage", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ’†โ€โ™‚๏ธ": { + "name": "man getting massage", + "slug": "man_getting_massage", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ’†โ€โ™€๏ธ": { + "name": "woman getting massage", + "slug": "woman_getting_massage", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ’‡": { + "name": "person getting haircut", + "slug": "person_getting_haircut", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ’‡โ€โ™‚๏ธ": { + "name": "man getting haircut", + "slug": "man_getting_haircut", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ’‡โ€โ™€๏ธ": { + "name": "woman getting haircut", + "slug": "woman_getting_haircut", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿšถ": { + "name": "person walking", + "slug": "person_walking", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿšถโ€โ™‚๏ธ": { + "name": "man walking", + "slug": "man_walking", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿšถโ€โ™€๏ธ": { + "name": "woman walking", + "slug": "woman_walking", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿšถโ€โžก๏ธ": { + "name": "person walking facing right", + "slug": "person_walking_facing_right", + "group": "People & Body", + "emoji_version": "15.1", + "unicode_version": "15.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "15.1" + }, + "๐Ÿšถโ€โ™€๏ธโ€โžก๏ธ": { + "name": "woman walking facing right", + "slug": "woman_walking_facing_right", + "group": "People & Body", + "emoji_version": "15.1", + "unicode_version": "15.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "15.1" + }, + "๐Ÿšถโ€โ™‚๏ธโ€โžก๏ธ": { + "name": "man walking facing right", + "slug": "man_walking_facing_right", + "group": "People & Body", + "emoji_version": "15.1", + "unicode_version": "15.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "15.1" + }, + "๐Ÿง": { + "name": "person standing", + "slug": "person_standing", + "group": "People & Body", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.0" + }, + "๐Ÿงโ€โ™‚๏ธ": { + "name": "man standing", + "slug": "man_standing", + "group": "People & Body", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.0" + }, + "๐Ÿงโ€โ™€๏ธ": { + "name": "woman standing", + "slug": "woman_standing", + "group": "People & Body", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.0" + }, + "๐ŸงŽ": { + "name": "person kneeling", + "slug": "person_kneeling", + "group": "People & Body", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.0" + }, + "๐ŸงŽโ€โ™‚๏ธ": { + "name": "man kneeling", + "slug": "man_kneeling", + "group": "People & Body", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.0" + }, + "๐ŸงŽโ€โ™€๏ธ": { + "name": "woman kneeling", + "slug": "woman_kneeling", + "group": "People & Body", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.0" + }, + "๐ŸงŽโ€โžก๏ธ": { + "name": "person kneeling facing right", + "slug": "person_kneeling_facing_right", + "group": "People & Body", + "emoji_version": "15.1", + "unicode_version": "15.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "15.1" + }, + "๐ŸงŽโ€โ™€๏ธโ€โžก๏ธ": { + "name": "woman kneeling facing right", + "slug": "woman_kneeling_facing_right", + "group": "People & Body", + "emoji_version": "15.1", + "unicode_version": "15.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "15.1" + }, + "๐ŸงŽโ€โ™‚๏ธโ€โžก๏ธ": { + "name": "man kneeling facing right", + "slug": "man_kneeling_facing_right", + "group": "People & Body", + "emoji_version": "15.1", + "unicode_version": "15.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "15.1" + }, + "๐Ÿง‘โ€๐Ÿฆฏ": { + "name": "person with white cane", + "slug": "person_with_white_cane", + "group": "People & Body", + "emoji_version": "12.1", + "unicode_version": "12.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.1" + }, + "๐Ÿง‘โ€๐Ÿฆฏโ€โžก๏ธ": { + "name": "person with white cane facing right", + "slug": "person_with_white_cane_facing_right", + "group": "People & Body", + "emoji_version": "15.1", + "unicode_version": "15.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "15.1" + }, + "๐Ÿ‘จโ€๐Ÿฆฏ": { + "name": "man with white cane", + "slug": "man_with_white_cane", + "group": "People & Body", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.0" + }, + "๐Ÿ‘จโ€๐Ÿฆฏโ€โžก๏ธ": { + "name": "man with white cane facing right", + "slug": "man_with_white_cane_facing_right", + "group": "People & Body", + "emoji_version": "15.1", + "unicode_version": "15.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "15.1" + }, + "๐Ÿ‘ฉโ€๐Ÿฆฏ": { + "name": "woman with white cane", + "slug": "woman_with_white_cane", + "group": "People & Body", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.0" + }, + "๐Ÿ‘ฉโ€๐Ÿฆฏโ€โžก๏ธ": { + "name": "woman with white cane facing right", + "slug": "woman_with_white_cane_facing_right", + "group": "People & Body", + "emoji_version": "15.1", + "unicode_version": "15.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "15.1" + }, + "๐Ÿง‘โ€๐Ÿฆผ": { + "name": "person in motorized wheelchair", + "slug": "person_in_motorized_wheelchair", + "group": "People & Body", + "emoji_version": "12.1", + "unicode_version": "12.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.1" + }, + "๐Ÿง‘โ€๐Ÿฆผโ€โžก๏ธ": { + "name": "person in motorized wheelchair facing right", + "slug": "person_in_motorized_wheelchair_facing_right", + "group": "People & Body", + "emoji_version": "15.1", + "unicode_version": "15.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "15.1" + }, + "๐Ÿ‘จโ€๐Ÿฆผ": { + "name": "man in motorized wheelchair", + "slug": "man_in_motorized_wheelchair", + "group": "People & Body", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.0" + }, + "๐Ÿ‘จโ€๐Ÿฆผโ€โžก๏ธ": { + "name": "man in motorized wheelchair facing right", + "slug": "man_in_motorized_wheelchair_facing_right", + "group": "People & Body", + "emoji_version": "15.1", + "unicode_version": "15.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "15.1" + }, + "๐Ÿ‘ฉโ€๐Ÿฆผ": { + "name": "woman in motorized wheelchair", + "slug": "woman_in_motorized_wheelchair", + "group": "People & Body", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.0" + }, + "๐Ÿ‘ฉโ€๐Ÿฆผโ€โžก๏ธ": { + "name": "woman in motorized wheelchair facing right", + "slug": "woman_in_motorized_wheelchair_facing_right", + "group": "People & Body", + "emoji_version": "15.1", + "unicode_version": "15.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "15.1" + }, + "๐Ÿง‘โ€๐Ÿฆฝ": { + "name": "person in manual wheelchair", + "slug": "person_in_manual_wheelchair", + "group": "People & Body", + "emoji_version": "12.1", + "unicode_version": "12.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.1" + }, + "๐Ÿง‘โ€๐Ÿฆฝโ€โžก๏ธ": { + "name": "person in manual wheelchair facing right", + "slug": "person_in_manual_wheelchair_facing_right", + "group": "People & Body", + "emoji_version": "15.1", + "unicode_version": "15.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "15.1" + }, + "๐Ÿ‘จโ€๐Ÿฆฝ": { + "name": "man in manual wheelchair", + "slug": "man_in_manual_wheelchair", + "group": "People & Body", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.0" + }, + "๐Ÿ‘จโ€๐Ÿฆฝโ€โžก๏ธ": { + "name": "man in manual wheelchair facing right", + "slug": "man_in_manual_wheelchair_facing_right", + "group": "People & Body", + "emoji_version": "15.1", + "unicode_version": "15.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "15.1" + }, + "๐Ÿ‘ฉโ€๐Ÿฆฝ": { + "name": "woman in manual wheelchair", + "slug": "woman_in_manual_wheelchair", + "group": "People & Body", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.0" + }, + "๐Ÿ‘ฉโ€๐Ÿฆฝโ€โžก๏ธ": { + "name": "woman in manual wheelchair facing right", + "slug": "woman_in_manual_wheelchair_facing_right", + "group": "People & Body", + "emoji_version": "15.1", + "unicode_version": "15.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "15.1" + }, + "๐Ÿƒ": { + "name": "person running", + "slug": "person_running", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿƒโ€โ™‚๏ธ": { + "name": "man running", + "slug": "man_running", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿƒโ€โ™€๏ธ": { + "name": "woman running", + "slug": "woman_running", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿƒโ€โžก๏ธ": { + "name": "person running facing right", + "slug": "person_running_facing_right", + "group": "People & Body", + "emoji_version": "15.1", + "unicode_version": "15.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "15.1" + }, + "๐Ÿƒโ€โ™€๏ธโ€โžก๏ธ": { + "name": "woman running facing right", + "slug": "woman_running_facing_right", + "group": "People & Body", + "emoji_version": "15.1", + "unicode_version": "15.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "15.1" + }, + "๐Ÿƒโ€โ™‚๏ธโ€โžก๏ธ": { + "name": "man running facing right", + "slug": "man_running_facing_right", + "group": "People & Body", + "emoji_version": "15.1", + "unicode_version": "15.1", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "15.1" + }, + "๐Ÿ’ƒ": { + "name": "woman dancing", + "slug": "woman_dancing", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ•บ": { + "name": "man dancing", + "slug": "man_dancing", + "group": "People & Body", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "3.0" + }, + "๐Ÿ•ด๏ธ": { + "name": "person in suit levitating", + "slug": "person_in_suit_levitating", + "group": "People & Body", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ‘ฏ": { + "name": "people with bunny ears", + "slug": "people_with_bunny_ears", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‘ฏโ€โ™‚๏ธ": { + "name": "men with bunny ears", + "slug": "men_with_bunny_ears", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": false + }, + "๐Ÿ‘ฏโ€โ™€๏ธ": { + "name": "women with bunny ears", + "slug": "women_with_bunny_ears", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": false + }, + "๐Ÿง–": { + "name": "person in steamy room", + "slug": "person_in_steamy_room", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿง–โ€โ™‚๏ธ": { + "name": "man in steamy room", + "slug": "man_in_steamy_room", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿง–โ€โ™€๏ธ": { + "name": "woman in steamy room", + "slug": "woman_in_steamy_room", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿง—": { + "name": "person climbing", + "slug": "person_climbing", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿง—โ€โ™‚๏ธ": { + "name": "man climbing", + "slug": "man_climbing", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿง—โ€โ™€๏ธ": { + "name": "woman climbing", + "slug": "woman_climbing", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿคบ": { + "name": "person fencing", + "slug": "person_fencing", + "group": "People & Body", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿ‡": { + "name": "horse racing", + "slug": "horse_racing", + "group": "People & Body", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "โ›ท๏ธ": { + "name": "skier", + "slug": "skier", + "group": "People & Body", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ‚": { + "name": "snowboarder", + "slug": "snowboarder", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐ŸŒ๏ธ": { + "name": "person golfing", + "slug": "person_golfing", + "group": "People & Body", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐ŸŒ๏ธโ€โ™‚๏ธ": { + "name": "man golfing", + "slug": "man_golfing", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐ŸŒ๏ธโ€โ™€๏ธ": { + "name": "woman golfing", + "slug": "woman_golfing", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ„": { + "name": "person surfing", + "slug": "person_surfing", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ„โ€โ™‚๏ธ": { + "name": "man surfing", + "slug": "man_surfing", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ„โ€โ™€๏ธ": { + "name": "woman surfing", + "slug": "woman_surfing", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿšฃ": { + "name": "person rowing boat", + "slug": "person_rowing_boat", + "group": "People & Body", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿšฃโ€โ™‚๏ธ": { + "name": "man rowing boat", + "slug": "man_rowing_boat", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿšฃโ€โ™€๏ธ": { + "name": "woman rowing boat", + "slug": "woman_rowing_boat", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐ŸŠ": { + "name": "person swimming", + "slug": "person_swimming", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐ŸŠโ€โ™‚๏ธ": { + "name": "man swimming", + "slug": "man_swimming", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐ŸŠโ€โ™€๏ธ": { + "name": "woman swimming", + "slug": "woman_swimming", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "โ›น๏ธ": { + "name": "person bouncing ball", + "slug": "person_bouncing_ball", + "group": "People & Body", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "2.0" + }, + "โ›น๏ธโ€โ™‚๏ธ": { + "name": "man bouncing ball", + "slug": "man_bouncing_ball", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "โ›น๏ธโ€โ™€๏ธ": { + "name": "woman bouncing ball", + "slug": "woman_bouncing_ball", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ‹๏ธ": { + "name": "person lifting weights", + "slug": "person_lifting_weights", + "group": "People & Body", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "2.0" + }, + "๐Ÿ‹๏ธโ€โ™‚๏ธ": { + "name": "man lifting weights", + "slug": "man_lifting_weights", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿ‹๏ธโ€โ™€๏ธ": { + "name": "woman lifting weights", + "slug": "woman_lifting_weights", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿšด": { + "name": "person biking", + "slug": "person_biking", + "group": "People & Body", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿšดโ€โ™‚๏ธ": { + "name": "man biking", + "slug": "man_biking", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿšดโ€โ™€๏ธ": { + "name": "woman biking", + "slug": "woman_biking", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿšต": { + "name": "person mountain biking", + "slug": "person_mountain_biking", + "group": "People & Body", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿšตโ€โ™‚๏ธ": { + "name": "man mountain biking", + "slug": "man_mountain_biking", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿšตโ€โ™€๏ธ": { + "name": "woman mountain biking", + "slug": "woman_mountain_biking", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿคธ": { + "name": "person cartwheeling", + "slug": "person_cartwheeling", + "group": "People & Body", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "3.0" + }, + "๐Ÿคธโ€โ™‚๏ธ": { + "name": "man cartwheeling", + "slug": "man_cartwheeling", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿคธโ€โ™€๏ธ": { + "name": "woman cartwheeling", + "slug": "woman_cartwheeling", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿคผ": { + "name": "people wrestling", + "slug": "people_wrestling", + "group": "People & Body", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿคผโ€โ™‚๏ธ": { + "name": "men wrestling", + "slug": "men_wrestling", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": false + }, + "๐Ÿคผโ€โ™€๏ธ": { + "name": "women wrestling", + "slug": "women_wrestling", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": false + }, + "๐Ÿคฝ": { + "name": "person playing water polo", + "slug": "person_playing_water_polo", + "group": "People & Body", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "3.0" + }, + "๐Ÿคฝโ€โ™‚๏ธ": { + "name": "man playing water polo", + "slug": "man_playing_water_polo", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿคฝโ€โ™€๏ธ": { + "name": "woman playing water polo", + "slug": "woman_playing_water_polo", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿคพ": { + "name": "person playing handball", + "slug": "person_playing_handball", + "group": "People & Body", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "3.0" + }, + "๐Ÿคพโ€โ™‚๏ธ": { + "name": "man playing handball", + "slug": "man_playing_handball", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿคพโ€โ™€๏ธ": { + "name": "woman playing handball", + "slug": "woman_playing_handball", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿคน": { + "name": "person juggling", + "slug": "person_juggling", + "group": "People & Body", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "3.0" + }, + "๐Ÿคนโ€โ™‚๏ธ": { + "name": "man juggling", + "slug": "man_juggling", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿคนโ€โ™€๏ธ": { + "name": "woman juggling", + "slug": "woman_juggling", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿง˜": { + "name": "person in lotus position", + "slug": "person_in_lotus_position", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿง˜โ€โ™‚๏ธ": { + "name": "man in lotus position", + "slug": "man_in_lotus_position", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿง˜โ€โ™€๏ธ": { + "name": "woman in lotus position", + "slug": "woman_in_lotus_position", + "group": "People & Body", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "5.0" + }, + "๐Ÿ›€": { + "name": "person taking bath", + "slug": "person_taking_bath", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "1.0" + }, + "๐Ÿ›Œ": { + "name": "person in bed", + "slug": "person_in_bed", + "group": "People & Body", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "4.0" + }, + "๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘": { + "name": "people holding hands", + "slug": "people_holding_hands", + "group": "People & Body", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.0" + }, + "๐Ÿ‘ญ": { + "name": "women holding hands", + "slug": "women_holding_hands", + "group": "People & Body", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.0" + }, + "๐Ÿ‘ซ": { + "name": "woman and man holding hands", + "slug": "woman_and_man_holding_hands", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.0" + }, + "๐Ÿ‘ฌ": { + "name": "men holding hands", + "slug": "men_holding_hands", + "group": "People & Body", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "12.0" + }, + "๐Ÿ’": { + "name": "kiss", + "slug": "kiss", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "13.1" + }, + "๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ": { + "name": "kiss woman, man", + "slug": "kiss_woman_man", + "group": "People & Body", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "13.1" + }, + "๐Ÿ‘จโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ": { + "name": "kiss man, man", + "slug": "kiss_man_man", + "group": "People & Body", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "13.1" + }, + "๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ": { + "name": "kiss woman, woman", + "slug": "kiss_woman_woman", + "group": "People & Body", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "13.1" + }, + "๐Ÿ’‘": { + "name": "couple with heart", + "slug": "couple_with_heart", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "13.1" + }, + "๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ‘จ": { + "name": "couple with heart woman, man", + "slug": "couple_with_heart_woman_man", + "group": "People & Body", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "13.1" + }, + "๐Ÿ‘จโ€โค๏ธโ€๐Ÿ‘จ": { + "name": "couple with heart man, man", + "slug": "couple_with_heart_man_man", + "group": "People & Body", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "13.1" + }, + "๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ‘ฉ": { + "name": "couple with heart woman, woman", + "slug": "couple_with_heart_woman_woman", + "group": "People & Body", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": true, + "skin_tone_support_unicode_version": "13.1" + }, + "๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ": { + "name": "family man, woman, boy", + "slug": "family_man_woman_boy", + "group": "People & Body", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ง": { + "name": "family man, woman, girl", + "slug": "family_man_woman_girl", + "group": "People & Body", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ": { + "name": "family man, woman, girl, boy", + "slug": "family_man_woman_girl_boy", + "group": "People & Body", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ": { + "name": "family man, woman, boy, boy", + "slug": "family_man_woman_boy_boy", + "group": "People & Body", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง": { + "name": "family man, woman, girl, girl", + "slug": "family_man_woman_girl_girl", + "group": "People & Body", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ฆ": { + "name": "family man, man, boy", + "slug": "family_man_man_boy", + "group": "People & Body", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ง": { + "name": "family man, man, girl", + "slug": "family_man_man_girl", + "group": "People & Body", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ฆ": { + "name": "family man, man, girl, boy", + "slug": "family_man_man_girl_boy", + "group": "People & Body", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ": { + "name": "family man, man, boy, boy", + "slug": "family_man_man_boy_boy", + "group": "People & Body", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ง": { + "name": "family man, man, girl, girl", + "slug": "family_man_man_girl_girl", + "group": "People & Body", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ": { + "name": "family woman, woman, boy", + "slug": "family_woman_woman_boy", + "group": "People & Body", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ง": { + "name": "family woman, woman, girl", + "slug": "family_woman_woman_girl", + "group": "People & Body", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ": { + "name": "family woman, woman, girl, boy", + "slug": "family_woman_woman_girl_boy", + "group": "People & Body", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ": { + "name": "family woman, woman, boy, boy", + "slug": "family_woman_woman_boy_boy", + "group": "People & Body", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง": { + "name": "family woman, woman, girl, girl", + "slug": "family_woman_woman_girl_girl", + "group": "People & Body", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‘จโ€๐Ÿ‘ฆ": { + "name": "family man, boy", + "slug": "family_man_boy", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": false + }, + "๐Ÿ‘จโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ": { + "name": "family man, boy, boy", + "slug": "family_man_boy_boy", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": false + }, + "๐Ÿ‘จโ€๐Ÿ‘ง": { + "name": "family man, girl", + "slug": "family_man_girl", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": false + }, + "๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ฆ": { + "name": "family man, girl, boy", + "slug": "family_man_girl_boy", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": false + }, + "๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ง": { + "name": "family man, girl, girl", + "slug": "family_man_girl_girl", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": false + }, + "๐Ÿ‘ฉโ€๐Ÿ‘ฆ": { + "name": "family woman, boy", + "slug": "family_woman_boy", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": false + }, + "๐Ÿ‘ฉโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ": { + "name": "family woman, boy, boy", + "slug": "family_woman_boy_boy", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": false + }, + "๐Ÿ‘ฉโ€๐Ÿ‘ง": { + "name": "family woman, girl", + "slug": "family_woman_girl", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": false + }, + "๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ": { + "name": "family woman, girl, boy", + "slug": "family_woman_girl_boy", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": false + }, + "๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง": { + "name": "family woman, girl, girl", + "slug": "family_woman_girl_girl", + "group": "People & Body", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": false + }, + "๐Ÿ—ฃ๏ธ": { + "name": "speaking head", + "slug": "speaking_head", + "group": "People & Body", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ‘ค": { + "name": "bust in silhouette", + "slug": "bust_in_silhouette", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‘ฅ": { + "name": "busts in silhouette", + "slug": "busts_in_silhouette", + "group": "People & Body", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿซ‚": { + "name": "people hugging", + "slug": "people_hugging", + "group": "People & Body", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿ‘ช": { + "name": "family", + "slug": "family", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿง‘โ€๐Ÿง‘โ€๐Ÿง’": { + "name": "family adult, adult, child", + "slug": "family_adult_adult_child", + "group": "People & Body", + "emoji_version": "15.1", + "unicode_version": "15.1", + "skin_tone_support": false + }, + "๐Ÿง‘โ€๐Ÿง‘โ€๐Ÿง’โ€๐Ÿง’": { + "name": "family adult, adult, child, child", + "slug": "family_adult_adult_child_child", + "group": "People & Body", + "emoji_version": "15.1", + "unicode_version": "15.1", + "skin_tone_support": false + }, + "๐Ÿง‘โ€๐Ÿง’": { + "name": "family adult, child", + "slug": "family_adult_child", + "group": "People & Body", + "emoji_version": "15.1", + "unicode_version": "15.1", + "skin_tone_support": false + }, + "๐Ÿง‘โ€๐Ÿง’โ€๐Ÿง’": { + "name": "family adult, child, child", + "slug": "family_adult_child_child", + "group": "People & Body", + "emoji_version": "15.1", + "unicode_version": "15.1", + "skin_tone_support": false + }, + "๐Ÿ‘ฃ": { + "name": "footprints", + "slug": "footprints", + "group": "People & Body", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿต": { + "name": "monkey face", + "slug": "monkey_face", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’": { + "name": "monkey", + "slug": "monkey", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฆ": { + "name": "gorilla", + "slug": "gorilla", + "group": "Animals & Nature", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿฆง": { + "name": "orangutan", + "slug": "orangutan", + "group": "Animals & Nature", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿถ": { + "name": "dog face", + "slug": "dog_face", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ•": { + "name": "dog", + "slug": "dog", + "group": "Animals & Nature", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿฆฎ": { + "name": "guide dog", + "slug": "guide_dog", + "group": "Animals & Nature", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿ•โ€๐Ÿฆบ": { + "name": "service dog", + "slug": "service_dog", + "group": "Animals & Nature", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿฉ": { + "name": "poodle", + "slug": "poodle", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿบ": { + "name": "wolf", + "slug": "wolf", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸฆŠ": { + "name": "fox", + "slug": "fox", + "group": "Animals & Nature", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿฆ": { + "name": "raccoon", + "slug": "raccoon", + "group": "Animals & Nature", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿฑ": { + "name": "cat face", + "slug": "cat_face", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿˆ": { + "name": "cat", + "slug": "cat", + "group": "Animals & Nature", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿˆโ€โฌ›": { + "name": "black cat", + "slug": "black_cat", + "group": "Animals & Nature", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿฆ": { + "name": "lion", + "slug": "lion", + "group": "Animals & Nature", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿฏ": { + "name": "tiger face", + "slug": "tiger_face", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ…": { + "name": "tiger", + "slug": "tiger", + "group": "Animals & Nature", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ†": { + "name": "leopard", + "slug": "leopard", + "group": "Animals & Nature", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿด": { + "name": "horse face", + "slug": "horse_face", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸซŽ": { + "name": "moose", + "slug": "moose", + "group": "Animals & Nature", + "emoji_version": "15.0", + "unicode_version": "15.0", + "skin_tone_support": false + }, + "๐Ÿซ": { + "name": "donkey", + "slug": "donkey", + "group": "Animals & Nature", + "emoji_version": "15.0", + "unicode_version": "15.0", + "skin_tone_support": false + }, + "๐ŸŽ": { + "name": "horse", + "slug": "horse", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฆ„": { + "name": "unicorn", + "slug": "unicorn", + "group": "Animals & Nature", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿฆ“": { + "name": "zebra", + "slug": "zebra", + "group": "Animals & Nature", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐ŸฆŒ": { + "name": "deer", + "slug": "deer", + "group": "Animals & Nature", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿฆฌ": { + "name": "bison", + "slug": "bison", + "group": "Animals & Nature", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿฎ": { + "name": "cow face", + "slug": "cow_face", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‚": { + "name": "ox", + "slug": "ox", + "group": "Animals & Nature", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿƒ": { + "name": "water buffalo", + "slug": "water_buffalo", + "group": "Animals & Nature", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ„": { + "name": "cow", + "slug": "cow", + "group": "Animals & Nature", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿท": { + "name": "pig face", + "slug": "pig_face", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ–": { + "name": "pig", + "slug": "pig", + "group": "Animals & Nature", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ—": { + "name": "boar", + "slug": "boar", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฝ": { + "name": "pig nose", + "slug": "pig_nose", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ": { + "name": "ram", + "slug": "ram", + "group": "Animals & Nature", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ‘": { + "name": "ewe", + "slug": "ewe", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ": { + "name": "goat", + "slug": "goat", + "group": "Animals & Nature", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿช": { + "name": "camel", + "slug": "camel", + "group": "Animals & Nature", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿซ": { + "name": "two-hump camel", + "slug": "two_hump_camel", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฆ™": { + "name": "llama", + "slug": "llama", + "group": "Animals & Nature", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿฆ’": { + "name": "giraffe", + "slug": "giraffe", + "group": "Animals & Nature", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿ˜": { + "name": "elephant", + "slug": "elephant", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฆฃ": { + "name": "mammoth", + "slug": "mammoth", + "group": "Animals & Nature", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿฆ": { + "name": "rhinoceros", + "slug": "rhinoceros", + "group": "Animals & Nature", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿฆ›": { + "name": "hippopotamus", + "slug": "hippopotamus", + "group": "Animals & Nature", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿญ": { + "name": "mouse face", + "slug": "mouse_face", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ": { + "name": "mouse", + "slug": "mouse", + "group": "Animals & Nature", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ€": { + "name": "rat", + "slug": "rat", + "group": "Animals & Nature", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿน": { + "name": "hamster", + "slug": "hamster", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฐ": { + "name": "rabbit face", + "slug": "rabbit_face", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‡": { + "name": "rabbit", + "slug": "rabbit", + "group": "Animals & Nature", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿฟ๏ธ": { + "name": "chipmunk", + "slug": "chipmunk", + "group": "Animals & Nature", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿฆซ": { + "name": "beaver", + "slug": "beaver", + "group": "Animals & Nature", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿฆ”": { + "name": "hedgehog", + "slug": "hedgehog", + "group": "Animals & Nature", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿฆ‡": { + "name": "bat", + "slug": "bat", + "group": "Animals & Nature", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿป": { + "name": "bear", + "slug": "bear", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿปโ€โ„๏ธ": { + "name": "polar bear", + "slug": "polar_bear", + "group": "Animals & Nature", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿจ": { + "name": "koala", + "slug": "koala", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿผ": { + "name": "panda", + "slug": "panda", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฆฅ": { + "name": "sloth", + "slug": "sloth", + "group": "Animals & Nature", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿฆฆ": { + "name": "otter", + "slug": "otter", + "group": "Animals & Nature", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿฆจ": { + "name": "skunk", + "slug": "skunk", + "group": "Animals & Nature", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿฆ˜": { + "name": "kangaroo", + "slug": "kangaroo", + "group": "Animals & Nature", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿฆก": { + "name": "badger", + "slug": "badger", + "group": "Animals & Nature", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿพ": { + "name": "paw prints", + "slug": "paw_prints", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฆƒ": { + "name": "turkey", + "slug": "turkey", + "group": "Animals & Nature", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ”": { + "name": "chicken", + "slug": "chicken", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“": { + "name": "rooster", + "slug": "rooster", + "group": "Animals & Nature", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿฃ": { + "name": "hatching chick", + "slug": "hatching_chick", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿค": { + "name": "baby chick", + "slug": "baby_chick", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฅ": { + "name": "front-facing baby chick", + "slug": "front_facing_baby_chick", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฆ": { + "name": "bird", + "slug": "bird", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿง": { + "name": "penguin", + "slug": "penguin", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ•Š๏ธ": { + "name": "dove", + "slug": "dove", + "group": "Animals & Nature", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿฆ…": { + "name": "eagle", + "slug": "eagle", + "group": "Animals & Nature", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿฆ†": { + "name": "duck", + "slug": "duck", + "group": "Animals & Nature", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿฆข": { + "name": "swan", + "slug": "swan", + "group": "Animals & Nature", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿฆ‰": { + "name": "owl", + "slug": "owl", + "group": "Animals & Nature", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿฆค": { + "name": "dodo", + "slug": "dodo", + "group": "Animals & Nature", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿชถ": { + "name": "feather", + "slug": "feather", + "group": "Animals & Nature", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿฆฉ": { + "name": "flamingo", + "slug": "flamingo", + "group": "Animals & Nature", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿฆš": { + "name": "peacock", + "slug": "peacock", + "group": "Animals & Nature", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿฆœ": { + "name": "parrot", + "slug": "parrot", + "group": "Animals & Nature", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿชฝ": { + "name": "wing", + "slug": "wing", + "group": "Animals & Nature", + "emoji_version": "15.0", + "unicode_version": "15.0", + "skin_tone_support": false + }, + "๐Ÿฆโ€โฌ›": { + "name": "black bird", + "slug": "black_bird", + "group": "Animals & Nature", + "emoji_version": "15.0", + "unicode_version": "15.0", + "skin_tone_support": false + }, + "๐Ÿชฟ": { + "name": "goose", + "slug": "goose", + "group": "Animals & Nature", + "emoji_version": "15.0", + "unicode_version": "15.0", + "skin_tone_support": false + }, + "๐Ÿฆโ€๐Ÿ”ฅ": { + "name": "phoenix", + "slug": "phoenix", + "group": "Animals & Nature", + "emoji_version": "15.1", + "unicode_version": "15.1", + "skin_tone_support": false + }, + "๐Ÿธ": { + "name": "frog", + "slug": "frog", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŠ": { + "name": "crocodile", + "slug": "crocodile", + "group": "Animals & Nature", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿข": { + "name": "turtle", + "slug": "turtle", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸฆŽ": { + "name": "lizard", + "slug": "lizard", + "group": "Animals & Nature", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿ": { + "name": "snake", + "slug": "snake", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฒ": { + "name": "dragon face", + "slug": "dragon_face", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‰": { + "name": "dragon", + "slug": "dragon", + "group": "Animals & Nature", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿฆ•": { + "name": "sauropod", + "slug": "sauropod", + "group": "Animals & Nature", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿฆ–": { + "name": "T-Rex", + "slug": "t_rex", + "group": "Animals & Nature", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿณ": { + "name": "spouting whale", + "slug": "spouting_whale", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‹": { + "name": "whale", + "slug": "whale", + "group": "Animals & Nature", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿฌ": { + "name": "dolphin", + "slug": "dolphin", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฆญ": { + "name": "seal", + "slug": "seal", + "group": "Animals & Nature", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐ŸŸ": { + "name": "fish", + "slug": "fish", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ ": { + "name": "tropical fish", + "slug": "tropical_fish", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿก": { + "name": "blowfish", + "slug": "blowfish", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฆˆ": { + "name": "shark", + "slug": "shark", + "group": "Animals & Nature", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿ™": { + "name": "octopus", + "slug": "octopus", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿš": { + "name": "spiral shell", + "slug": "spiral_shell", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿชธ": { + "name": "coral", + "slug": "coral", + "group": "Animals & Nature", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": false + }, + "๐Ÿชผ": { + "name": "jellyfish", + "slug": "jellyfish", + "group": "Animals & Nature", + "emoji_version": "15.0", + "unicode_version": "15.0", + "skin_tone_support": false + }, + "๐ŸŒ": { + "name": "snail", + "slug": "snail", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฆ‹": { + "name": "butterfly", + "slug": "butterfly", + "group": "Animals & Nature", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿ›": { + "name": "bug", + "slug": "bug", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿœ": { + "name": "ant", + "slug": "ant", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ": { + "name": "honeybee", + "slug": "honeybee", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿชฒ": { + "name": "beetle", + "slug": "beetle", + "group": "Animals & Nature", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿž": { + "name": "lady beetle", + "slug": "lady_beetle", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฆ—": { + "name": "cricket", + "slug": "cricket", + "group": "Animals & Nature", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿชณ": { + "name": "cockroach", + "slug": "cockroach", + "group": "Animals & Nature", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿ•ท๏ธ": { + "name": "spider", + "slug": "spider", + "group": "Animals & Nature", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ•ธ๏ธ": { + "name": "spider web", + "slug": "spider_web", + "group": "Animals & Nature", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿฆ‚": { + "name": "scorpion", + "slug": "scorpion", + "group": "Animals & Nature", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐ŸฆŸ": { + "name": "mosquito", + "slug": "mosquito", + "group": "Animals & Nature", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿชฐ": { + "name": "fly", + "slug": "fly", + "group": "Animals & Nature", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿชฑ": { + "name": "worm", + "slug": "worm", + "group": "Animals & Nature", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿฆ ": { + "name": "microbe", + "slug": "microbe", + "group": "Animals & Nature", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿ’": { + "name": "bouquet", + "slug": "bouquet", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒธ": { + "name": "cherry blossom", + "slug": "cherry_blossom", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’ฎ": { + "name": "white flower", + "slug": "white_flower", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿชท": { + "name": "lotus", + "slug": "lotus", + "group": "Animals & Nature", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": false + }, + "๐Ÿต๏ธ": { + "name": "rosette", + "slug": "rosette", + "group": "Animals & Nature", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸŒน": { + "name": "rose", + "slug": "rose", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฅ€": { + "name": "wilted flower", + "slug": "wilted_flower", + "group": "Animals & Nature", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐ŸŒบ": { + "name": "hibiscus", + "slug": "hibiscus", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒป": { + "name": "sunflower", + "slug": "sunflower", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒผ": { + "name": "blossom", + "slug": "blossom", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒท": { + "name": "tulip", + "slug": "tulip", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿชป": { + "name": "hyacinth", + "slug": "hyacinth", + "group": "Animals & Nature", + "emoji_version": "15.0", + "unicode_version": "15.0", + "skin_tone_support": false + }, + "๐ŸŒฑ": { + "name": "seedling", + "slug": "seedling", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿชด": { + "name": "potted plant", + "slug": "potted_plant", + "group": "Animals & Nature", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐ŸŒฒ": { + "name": "evergreen tree", + "slug": "evergreen_tree", + "group": "Animals & Nature", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐ŸŒณ": { + "name": "deciduous tree", + "slug": "deciduous_tree", + "group": "Animals & Nature", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐ŸŒด": { + "name": "palm tree", + "slug": "palm_tree", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒต": { + "name": "cactus", + "slug": "cactus", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒพ": { + "name": "sheaf of rice", + "slug": "sheaf_of_rice", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒฟ": { + "name": "herb", + "slug": "herb", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ˜˜๏ธ": { + "name": "shamrock", + "slug": "shamrock", + "group": "Animals & Nature", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ€": { + "name": "four leaf clover", + "slug": "four_leaf_clover", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ": { + "name": "maple leaf", + "slug": "maple_leaf", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‚": { + "name": "fallen leaf", + "slug": "fallen_leaf", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿƒ": { + "name": "leaf fluttering in wind", + "slug": "leaf_fluttering_in_wind", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿชน": { + "name": "empty nest", + "slug": "empty_nest", + "group": "Animals & Nature", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": false + }, + "๐Ÿชบ": { + "name": "nest with eggs", + "slug": "nest_with_eggs", + "group": "Animals & Nature", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": false + }, + "๐Ÿ„": { + "name": "mushroom", + "slug": "mushroom", + "group": "Animals & Nature", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‡": { + "name": "grapes", + "slug": "grapes", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿˆ": { + "name": "melon", + "slug": "melon", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‰": { + "name": "watermelon", + "slug": "watermelon", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŠ": { + "name": "tangerine", + "slug": "tangerine", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‹": { + "name": "lemon", + "slug": "lemon", + "group": "Food & Drink", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ‹โ€๐ŸŸฉ": { + "name": "lime", + "slug": "lime", + "group": "Food & Drink", + "emoji_version": "15.1", + "unicode_version": "15.1", + "skin_tone_support": false + }, + "๐ŸŒ": { + "name": "banana", + "slug": "banana", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ": { + "name": "pineapple", + "slug": "pineapple", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฅญ": { + "name": "mango", + "slug": "mango", + "group": "Food & Drink", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐ŸŽ": { + "name": "red apple", + "slug": "red_apple", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ": { + "name": "green apple", + "slug": "green_apple", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ": { + "name": "pear", + "slug": "pear", + "group": "Food & Drink", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ‘": { + "name": "peach", + "slug": "peach", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’": { + "name": "cherries", + "slug": "cherries", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“": { + "name": "strawberry", + "slug": "strawberry", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿซ": { + "name": "blueberries", + "slug": "blueberries", + "group": "Food & Drink", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿฅ": { + "name": "kiwi fruit", + "slug": "kiwi_fruit", + "group": "Food & Drink", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿ…": { + "name": "tomato", + "slug": "tomato", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿซ’": { + "name": "olive", + "slug": "olive", + "group": "Food & Drink", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿฅฅ": { + "name": "coconut", + "slug": "coconut", + "group": "Food & Drink", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿฅ‘": { + "name": "avocado", + "slug": "avocado", + "group": "Food & Drink", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿ†": { + "name": "eggplant", + "slug": "eggplant", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฅ”": { + "name": "potato", + "slug": "potato", + "group": "Food & Drink", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿฅ•": { + "name": "carrot", + "slug": "carrot", + "group": "Food & Drink", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐ŸŒฝ": { + "name": "ear of corn", + "slug": "ear_of_corn", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒถ๏ธ": { + "name": "hot pepper", + "slug": "hot_pepper", + "group": "Food & Drink", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿซ‘": { + "name": "bell pepper", + "slug": "bell_pepper", + "group": "Food & Drink", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿฅ’": { + "name": "cucumber", + "slug": "cucumber", + "group": "Food & Drink", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿฅฌ": { + "name": "leafy green", + "slug": "leafy_green", + "group": "Food & Drink", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿฅฆ": { + "name": "broccoli", + "slug": "broccoli", + "group": "Food & Drink", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿง„": { + "name": "garlic", + "slug": "garlic", + "group": "Food & Drink", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿง…": { + "name": "onion", + "slug": "onion", + "group": "Food & Drink", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿฅœ": { + "name": "peanuts", + "slug": "peanuts", + "group": "Food & Drink", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿซ˜": { + "name": "beans", + "slug": "beans", + "group": "Food & Drink", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": false + }, + "๐ŸŒฐ": { + "name": "chestnut", + "slug": "chestnut", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿซš": { + "name": "ginger root", + "slug": "ginger_root", + "group": "Food & Drink", + "emoji_version": "15.0", + "unicode_version": "15.0", + "skin_tone_support": false + }, + "๐Ÿซ›": { + "name": "pea pod", + "slug": "pea_pod", + "group": "Food & Drink", + "emoji_version": "15.0", + "unicode_version": "15.0", + "skin_tone_support": false + }, + "๐Ÿ„โ€๐ŸŸซ": { + "name": "brown mushroom", + "slug": "brown_mushroom", + "group": "Food & Drink", + "emoji_version": "15.1", + "unicode_version": "15.1", + "skin_tone_support": false + }, + "๐Ÿž": { + "name": "bread", + "slug": "bread", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฅ": { + "name": "croissant", + "slug": "croissant", + "group": "Food & Drink", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿฅ–": { + "name": "baguette bread", + "slug": "baguette_bread", + "group": "Food & Drink", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿซ“": { + "name": "flatbread", + "slug": "flatbread", + "group": "Food & Drink", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿฅจ": { + "name": "pretzel", + "slug": "pretzel", + "group": "Food & Drink", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿฅฏ": { + "name": "bagel", + "slug": "bagel", + "group": "Food & Drink", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿฅž": { + "name": "pancakes", + "slug": "pancakes", + "group": "Food & Drink", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿง‡": { + "name": "waffle", + "slug": "waffle", + "group": "Food & Drink", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿง€": { + "name": "cheese wedge", + "slug": "cheese_wedge", + "group": "Food & Drink", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ–": { + "name": "meat on bone", + "slug": "meat_on_bone", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ—": { + "name": "poultry leg", + "slug": "poultry_leg", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฅฉ": { + "name": "cut of meat", + "slug": "cut_of_meat", + "group": "Food & Drink", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿฅ“": { + "name": "bacon", + "slug": "bacon", + "group": "Food & Drink", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿ”": { + "name": "hamburger", + "slug": "hamburger", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŸ": { + "name": "french fries", + "slug": "french_fries", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ•": { + "name": "pizza", + "slug": "pizza", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒญ": { + "name": "hot dog", + "slug": "hot_dog", + "group": "Food & Drink", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿฅช": { + "name": "sandwich", + "slug": "sandwich", + "group": "Food & Drink", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐ŸŒฎ": { + "name": "taco", + "slug": "taco", + "group": "Food & Drink", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐ŸŒฏ": { + "name": "burrito", + "slug": "burrito", + "group": "Food & Drink", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿซ”": { + "name": "tamale", + "slug": "tamale", + "group": "Food & Drink", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿฅ™": { + "name": "stuffed flatbread", + "slug": "stuffed_flatbread", + "group": "Food & Drink", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿง†": { + "name": "falafel", + "slug": "falafel", + "group": "Food & Drink", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿฅš": { + "name": "egg", + "slug": "egg", + "group": "Food & Drink", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿณ": { + "name": "cooking", + "slug": "cooking", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฅ˜": { + "name": "shallow pan of food", + "slug": "shallow_pan_of_food", + "group": "Food & Drink", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿฒ": { + "name": "pot of food", + "slug": "pot_of_food", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿซ•": { + "name": "fondue", + "slug": "fondue", + "group": "Food & Drink", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿฅฃ": { + "name": "bowl with spoon", + "slug": "bowl_with_spoon", + "group": "Food & Drink", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿฅ—": { + "name": "green salad", + "slug": "green_salad", + "group": "Food & Drink", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿฟ": { + "name": "popcorn", + "slug": "popcorn", + "group": "Food & Drink", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿงˆ": { + "name": "butter", + "slug": "butter", + "group": "Food & Drink", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿง‚": { + "name": "salt", + "slug": "salt", + "group": "Food & Drink", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿฅซ": { + "name": "canned food", + "slug": "canned_food", + "group": "Food & Drink", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿฑ": { + "name": "bento box", + "slug": "bento_box", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ˜": { + "name": "rice cracker", + "slug": "rice_cracker", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ™": { + "name": "rice ball", + "slug": "rice_ball", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿš": { + "name": "cooked rice", + "slug": "cooked_rice", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ›": { + "name": "curry rice", + "slug": "curry_rice", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿœ": { + "name": "steaming bowl", + "slug": "steaming_bowl", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ": { + "name": "spaghetti", + "slug": "spaghetti", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ ": { + "name": "roasted sweet potato", + "slug": "roasted_sweet_potato", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿข": { + "name": "oden", + "slug": "oden", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฃ": { + "name": "sushi", + "slug": "sushi", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿค": { + "name": "fried shrimp", + "slug": "fried_shrimp", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฅ": { + "name": "fish cake with swirl", + "slug": "fish_cake_with_swirl", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฅฎ": { + "name": "moon cake", + "slug": "moon_cake", + "group": "Food & Drink", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿก": { + "name": "dango", + "slug": "dango", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸฅŸ": { + "name": "dumpling", + "slug": "dumpling", + "group": "Food & Drink", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿฅ ": { + "name": "fortune cookie", + "slug": "fortune_cookie", + "group": "Food & Drink", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿฅก": { + "name": "takeout box", + "slug": "takeout_box", + "group": "Food & Drink", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿฆ€": { + "name": "crab", + "slug": "crab", + "group": "Food & Drink", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿฆž": { + "name": "lobster", + "slug": "lobster", + "group": "Food & Drink", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿฆ": { + "name": "shrimp", + "slug": "shrimp", + "group": "Food & Drink", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿฆ‘": { + "name": "squid", + "slug": "squid", + "group": "Food & Drink", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿฆช": { + "name": "oyster", + "slug": "oyster", + "group": "Food & Drink", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿฆ": { + "name": "soft ice cream", + "slug": "soft_ice_cream", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿง": { + "name": "shaved ice", + "slug": "shaved_ice", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿจ": { + "name": "ice cream", + "slug": "ice_cream", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฉ": { + "name": "doughnut", + "slug": "doughnut", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿช": { + "name": "cookie", + "slug": "cookie", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽ‚": { + "name": "birthday cake", + "slug": "birthday_cake", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฐ": { + "name": "shortcake", + "slug": "shortcake", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿง": { + "name": "cupcake", + "slug": "cupcake", + "group": "Food & Drink", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿฅง": { + "name": "pie", + "slug": "pie", + "group": "Food & Drink", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿซ": { + "name": "chocolate bar", + "slug": "chocolate_bar", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฌ": { + "name": "candy", + "slug": "candy", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿญ": { + "name": "lollipop", + "slug": "lollipop", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฎ": { + "name": "custard", + "slug": "custard", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฏ": { + "name": "honey pot", + "slug": "honey_pot", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿผ": { + "name": "baby bottle", + "slug": "baby_bottle", + "group": "Food & Drink", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿฅ›": { + "name": "glass of milk", + "slug": "glass_of_milk", + "group": "Food & Drink", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "โ˜•": { + "name": "hot beverage", + "slug": "hot_beverage", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿซ–": { + "name": "teapot", + "slug": "teapot", + "group": "Food & Drink", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿต": { + "name": "teacup without handle", + "slug": "teacup_without_handle", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿถ": { + "name": "sake", + "slug": "sake", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿพ": { + "name": "bottle with popping cork", + "slug": "bottle_with_popping_cork", + "group": "Food & Drink", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿท": { + "name": "wine glass", + "slug": "wine_glass", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿธ": { + "name": "cocktail glass", + "slug": "cocktail_glass", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿน": { + "name": "tropical drink", + "slug": "tropical_drink", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿบ": { + "name": "beer mug", + "slug": "beer_mug", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿป": { + "name": "clinking beer mugs", + "slug": "clinking_beer_mugs", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฅ‚": { + "name": "clinking glasses", + "slug": "clinking_glasses", + "group": "Food & Drink", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿฅƒ": { + "name": "tumbler glass", + "slug": "tumbler_glass", + "group": "Food & Drink", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿซ—": { + "name": "pouring liquid", + "slug": "pouring_liquid", + "group": "Food & Drink", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": false + }, + "๐Ÿฅค": { + "name": "cup with straw", + "slug": "cup_with_straw", + "group": "Food & Drink", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿง‹": { + "name": "bubble tea", + "slug": "bubble_tea", + "group": "Food & Drink", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿงƒ": { + "name": "beverage box", + "slug": "beverage_box", + "group": "Food & Drink", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿง‰": { + "name": "mate", + "slug": "mate", + "group": "Food & Drink", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐ŸงŠ": { + "name": "ice", + "slug": "ice", + "group": "Food & Drink", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿฅข": { + "name": "chopsticks", + "slug": "chopsticks", + "group": "Food & Drink", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿฝ๏ธ": { + "name": "fork and knife with plate", + "slug": "fork_and_knife_with_plate", + "group": "Food & Drink", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿด": { + "name": "fork and knife", + "slug": "fork_and_knife", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฅ„": { + "name": "spoon", + "slug": "spoon", + "group": "Food & Drink", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿ”ช": { + "name": "kitchen knife", + "slug": "kitchen_knife", + "group": "Food & Drink", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿซ™": { + "name": "jar", + "slug": "jar", + "group": "Food & Drink", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": false + }, + "๐Ÿบ": { + "name": "amphora", + "slug": "amphora", + "group": "Food & Drink", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐ŸŒ": { + "name": "globe showing Europe-Africa", + "slug": "globe_showing_europe_africa", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸŒŽ": { + "name": "globe showing Americas", + "slug": "globe_showing_americas", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸŒ": { + "name": "globe showing Asia-Australia", + "slug": "globe_showing_asia_australia", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒ": { + "name": "globe with meridians", + "slug": "globe_with_meridians", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ—บ๏ธ": { + "name": "world map", + "slug": "world_map", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ—พ": { + "name": "map of Japan", + "slug": "map_of_japan", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿงญ": { + "name": "compass", + "slug": "compass", + "group": "Travel & Places", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿ”๏ธ": { + "name": "snow-capped mountain", + "slug": "snow_capped_mountain", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "โ›ฐ๏ธ": { + "name": "mountain", + "slug": "mountain", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸŒ‹": { + "name": "volcano", + "slug": "volcano", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ—ป": { + "name": "mount fuji", + "slug": "mount_fuji", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ•๏ธ": { + "name": "camping", + "slug": "camping", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ–๏ธ": { + "name": "beach with umbrella", + "slug": "beach_with_umbrella", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿœ๏ธ": { + "name": "desert", + "slug": "desert", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ๏ธ": { + "name": "desert island", + "slug": "desert_island", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿž๏ธ": { + "name": "national park", + "slug": "national_park", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸŸ๏ธ": { + "name": "stadium", + "slug": "stadium", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ›๏ธ": { + "name": "classical building", + "slug": "classical_building", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ—๏ธ": { + "name": "building construction", + "slug": "building_construction", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿงฑ": { + "name": "brick", + "slug": "brick", + "group": "Travel & Places", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿชจ": { + "name": "rock", + "slug": "rock", + "group": "Travel & Places", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿชต": { + "name": "wood", + "slug": "wood", + "group": "Travel & Places", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿ›–": { + "name": "hut", + "slug": "hut", + "group": "Travel & Places", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿ˜๏ธ": { + "name": "houses", + "slug": "houses", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿš๏ธ": { + "name": "derelict house", + "slug": "derelict_house", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ ": { + "name": "house", + "slug": "house", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿก": { + "name": "house with garden", + "slug": "house_with_garden", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿข": { + "name": "office building", + "slug": "office_building", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฃ": { + "name": "Japanese post office", + "slug": "japanese_post_office", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿค": { + "name": "post office", + "slug": "post_office", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿฅ": { + "name": "hospital", + "slug": "hospital", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฆ": { + "name": "bank", + "slug": "bank", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿจ": { + "name": "hotel", + "slug": "hotel", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฉ": { + "name": "love hotel", + "slug": "love_hotel", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿช": { + "name": "convenience store", + "slug": "convenience_store", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿซ": { + "name": "school", + "slug": "school", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฌ": { + "name": "department store", + "slug": "department_store", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿญ": { + "name": "factory", + "slug": "factory", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฏ": { + "name": "Japanese castle", + "slug": "japanese_castle", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฐ": { + "name": "castle", + "slug": "castle", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’’": { + "name": "wedding", + "slug": "wedding", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ—ผ": { + "name": "Tokyo tower", + "slug": "tokyo_tower", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ—ฝ": { + "name": "Statue of Liberty", + "slug": "statue_of_liberty", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ›ช": { + "name": "church", + "slug": "church", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ•Œ": { + "name": "mosque", + "slug": "mosque", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ›•": { + "name": "hindu temple", + "slug": "hindu_temple", + "group": "Travel & Places", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿ•": { + "name": "synagogue", + "slug": "synagogue", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "โ›ฉ๏ธ": { + "name": "shinto shrine", + "slug": "shinto_shrine", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ•‹": { + "name": "kaaba", + "slug": "kaaba", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "โ›ฒ": { + "name": "fountain", + "slug": "fountain", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ›บ": { + "name": "tent", + "slug": "tent", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒ": { + "name": "foggy", + "slug": "foggy", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒƒ": { + "name": "night with stars", + "slug": "night_with_stars", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ™๏ธ": { + "name": "cityscape", + "slug": "cityscape", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸŒ„": { + "name": "sunrise over mountains", + "slug": "sunrise_over_mountains", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒ…": { + "name": "sunrise", + "slug": "sunrise", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒ†": { + "name": "cityscape at dusk", + "slug": "cityscape_at_dusk", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒ‡": { + "name": "sunset", + "slug": "sunset", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒ‰": { + "name": "bridge at night", + "slug": "bridge_at_night", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ™จ๏ธ": { + "name": "hot springs", + "slug": "hot_springs", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽ ": { + "name": "carousel horse", + "slug": "carousel_horse", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ›": { + "name": "playground slide", + "slug": "playground_slide", + "group": "Travel & Places", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": false + }, + "๐ŸŽก": { + "name": "ferris wheel", + "slug": "ferris_wheel", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽข": { + "name": "roller coaster", + "slug": "roller_coaster", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’ˆ": { + "name": "barber pole", + "slug": "barber_pole", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽช": { + "name": "circus tent", + "slug": "circus_tent", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿš‚": { + "name": "locomotive", + "slug": "locomotive", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿšƒ": { + "name": "railway car", + "slug": "railway_car", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿš„": { + "name": "high-speed train", + "slug": "high_speed_train", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿš…": { + "name": "bullet train", + "slug": "bullet_train", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿš†": { + "name": "train", + "slug": "train", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿš‡": { + "name": "metro", + "slug": "metro", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿšˆ": { + "name": "light rail", + "slug": "light_rail", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿš‰": { + "name": "station", + "slug": "station", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸšŠ": { + "name": "tram", + "slug": "tram", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿš": { + "name": "monorail", + "slug": "monorail", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿšž": { + "name": "mountain railway", + "slug": "mountain_railway", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿš‹": { + "name": "tram car", + "slug": "tram_car", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐ŸšŒ": { + "name": "bus", + "slug": "bus", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿš": { + "name": "oncoming bus", + "slug": "oncoming_bus", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸšŽ": { + "name": "trolleybus", + "slug": "trolleybus", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿš": { + "name": "minibus", + "slug": "minibus", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿš‘": { + "name": "ambulance", + "slug": "ambulance", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿš’": { + "name": "fire engine", + "slug": "fire_engine", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿš“": { + "name": "police car", + "slug": "police_car", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿš”": { + "name": "oncoming police car", + "slug": "oncoming_police_car", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿš•": { + "name": "taxi", + "slug": "taxi", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿš–": { + "name": "oncoming taxi", + "slug": "oncoming_taxi", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿš—": { + "name": "automobile", + "slug": "automobile", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿš˜": { + "name": "oncoming automobile", + "slug": "oncoming_automobile", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿš™": { + "name": "sport utility vehicle", + "slug": "sport_utility_vehicle", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ›ป": { + "name": "pickup truck", + "slug": "pickup_truck", + "group": "Travel & Places", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿšš": { + "name": "delivery truck", + "slug": "delivery_truck", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿš›": { + "name": "articulated lorry", + "slug": "articulated_lorry", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿšœ": { + "name": "tractor", + "slug": "tractor", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐ŸŽ๏ธ": { + "name": "racing car", + "slug": "racing_car", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ๏ธ": { + "name": "motorcycle", + "slug": "motorcycle", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ›ต": { + "name": "motor scooter", + "slug": "motor_scooter", + "group": "Travel & Places", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿฆฝ": { + "name": "manual wheelchair", + "slug": "manual_wheelchair", + "group": "Travel & Places", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿฆผ": { + "name": "motorized wheelchair", + "slug": "motorized_wheelchair", + "group": "Travel & Places", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿ›บ": { + "name": "auto rickshaw", + "slug": "auto_rickshaw", + "group": "Travel & Places", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿšฒ": { + "name": "bicycle", + "slug": "bicycle", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ›ด": { + "name": "kick scooter", + "slug": "kick_scooter", + "group": "Travel & Places", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿ›น": { + "name": "skateboard", + "slug": "skateboard", + "group": "Travel & Places", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿ›ผ": { + "name": "roller skate", + "slug": "roller_skate", + "group": "Travel & Places", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿš": { + "name": "bus stop", + "slug": "bus_stop", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ›ฃ๏ธ": { + "name": "motorway", + "slug": "motorway", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ›ค๏ธ": { + "name": "railway track", + "slug": "railway_track", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ›ข๏ธ": { + "name": "oil drum", + "slug": "oil_drum", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "โ›ฝ": { + "name": "fuel pump", + "slug": "fuel_pump", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ›ž": { + "name": "wheel", + "slug": "wheel", + "group": "Travel & Places", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": false + }, + "๐Ÿšจ": { + "name": "police car light", + "slug": "police_car_light", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿšฅ": { + "name": "horizontal traffic light", + "slug": "horizontal_traffic_light", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿšฆ": { + "name": "vertical traffic light", + "slug": "vertical_traffic_light", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ›‘": { + "name": "stop sign", + "slug": "stop_sign", + "group": "Travel & Places", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿšง": { + "name": "construction", + "slug": "construction", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โš“": { + "name": "anchor", + "slug": "anchor", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ›Ÿ": { + "name": "ring buoy", + "slug": "ring_buoy", + "group": "Travel & Places", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": false + }, + "โ›ต": { + "name": "sailboat", + "slug": "sailboat", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ›ถ": { + "name": "canoe", + "slug": "canoe", + "group": "Travel & Places", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿšค": { + "name": "speedboat", + "slug": "speedboat", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ›ณ๏ธ": { + "name": "passenger ship", + "slug": "passenger_ship", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "โ›ด๏ธ": { + "name": "ferry", + "slug": "ferry", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ›ฅ๏ธ": { + "name": "motor boat", + "slug": "motor_boat", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿšข": { + "name": "ship", + "slug": "ship", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โœˆ๏ธ": { + "name": "airplane", + "slug": "airplane", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ›ฉ๏ธ": { + "name": "small airplane", + "slug": "small_airplane", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ›ซ": { + "name": "airplane departure", + "slug": "airplane_departure", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ›ฌ": { + "name": "airplane arrival", + "slug": "airplane_arrival", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿช‚": { + "name": "parachute", + "slug": "parachute", + "group": "Travel & Places", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿ’บ": { + "name": "seat", + "slug": "seat", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿš": { + "name": "helicopter", + "slug": "helicopter", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐ŸšŸ": { + "name": "suspension railway", + "slug": "suspension_railway", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿš ": { + "name": "mountain cableway", + "slug": "mountain_cableway", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿšก": { + "name": "aerial tramway", + "slug": "aerial_tramway", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ›ฐ๏ธ": { + "name": "satellite", + "slug": "satellite", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿš€": { + "name": "rocket", + "slug": "rocket", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ›ธ": { + "name": "flying saucer", + "slug": "flying_saucer", + "group": "Travel & Places", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿ›Ž๏ธ": { + "name": "bellhop bell", + "slug": "bellhop_bell", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿงณ": { + "name": "luggage", + "slug": "luggage", + "group": "Travel & Places", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "โŒ›": { + "name": "hourglass done", + "slug": "hourglass_done", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โณ": { + "name": "hourglass not done", + "slug": "hourglass_not_done", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โŒš": { + "name": "watch", + "slug": "watch", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โฐ": { + "name": "alarm clock", + "slug": "alarm_clock", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โฑ๏ธ": { + "name": "stopwatch", + "slug": "stopwatch", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "โฒ๏ธ": { + "name": "timer clock", + "slug": "timer_clock", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ•ฐ๏ธ": { + "name": "mantelpiece clock", + "slug": "mantelpiece_clock", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ•›": { + "name": "twelve oโ€™clock", + "slug": "twelve_o_clock", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ•ง": { + "name": "twelve-thirty", + "slug": "twelve_thirty", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ•": { + "name": "one oโ€™clock", + "slug": "one_o_clock", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ•œ": { + "name": "one-thirty", + "slug": "one_thirty", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ•‘": { + "name": "two oโ€™clock", + "slug": "two_o_clock", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ•": { + "name": "two-thirty", + "slug": "two_thirty", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ•’": { + "name": "three oโ€™clock", + "slug": "three_o_clock", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ•ž": { + "name": "three-thirty", + "slug": "three_thirty", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ•“": { + "name": "four oโ€™clock", + "slug": "four_o_clock", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ•Ÿ": { + "name": "four-thirty", + "slug": "four_thirty", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ•”": { + "name": "five oโ€™clock", + "slug": "five_o_clock", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ• ": { + "name": "five-thirty", + "slug": "five_thirty", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ••": { + "name": "six oโ€™clock", + "slug": "six_o_clock", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ•ก": { + "name": "six-thirty", + "slug": "six_thirty", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ•–": { + "name": "seven oโ€™clock", + "slug": "seven_o_clock", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ•ข": { + "name": "seven-thirty", + "slug": "seven_thirty", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ•—": { + "name": "eight oโ€™clock", + "slug": "eight_o_clock", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ•ฃ": { + "name": "eight-thirty", + "slug": "eight_thirty", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ•˜": { + "name": "nine oโ€™clock", + "slug": "nine_o_clock", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ•ค": { + "name": "nine-thirty", + "slug": "nine_thirty", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ•™": { + "name": "ten oโ€™clock", + "slug": "ten_o_clock", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ•ฅ": { + "name": "ten-thirty", + "slug": "ten_thirty", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ•š": { + "name": "eleven oโ€™clock", + "slug": "eleven_o_clock", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ•ฆ": { + "name": "eleven-thirty", + "slug": "eleven_thirty", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸŒ‘": { + "name": "new moon", + "slug": "new_moon", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒ’": { + "name": "waxing crescent moon", + "slug": "waxing_crescent_moon", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐ŸŒ“": { + "name": "first quarter moon", + "slug": "first_quarter_moon", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒ”": { + "name": "waxing gibbous moon", + "slug": "waxing_gibbous_moon", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒ•": { + "name": "full moon", + "slug": "full_moon", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒ–": { + "name": "waning gibbous moon", + "slug": "waning_gibbous_moon", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐ŸŒ—": { + "name": "last quarter moon", + "slug": "last_quarter_moon", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐ŸŒ˜": { + "name": "waning crescent moon", + "slug": "waning_crescent_moon", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐ŸŒ™": { + "name": "crescent moon", + "slug": "crescent_moon", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒš": { + "name": "new moon face", + "slug": "new_moon_face", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐ŸŒ›": { + "name": "first quarter moon face", + "slug": "first_quarter_moon_face", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒœ": { + "name": "last quarter moon face", + "slug": "last_quarter_moon_face", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸŒก๏ธ": { + "name": "thermometer", + "slug": "thermometer", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "โ˜€๏ธ": { + "name": "sun", + "slug": "sun", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒ": { + "name": "full moon face", + "slug": "full_moon_face", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐ŸŒž": { + "name": "sun with face", + "slug": "sun_with_face", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿช": { + "name": "ringed planet", + "slug": "ringed_planet", + "group": "Travel & Places", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "โญ": { + "name": "star", + "slug": "star", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒŸ": { + "name": "glowing star", + "slug": "glowing_star", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒ ": { + "name": "shooting star", + "slug": "shooting_star", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒŒ": { + "name": "milky way", + "slug": "milky_way", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ˜๏ธ": { + "name": "cloud", + "slug": "cloud", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ›…": { + "name": "sun behind cloud", + "slug": "sun_behind_cloud", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ›ˆ๏ธ": { + "name": "cloud with lightning and rain", + "slug": "cloud_with_lightning_and_rain", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸŒค๏ธ": { + "name": "sun behind small cloud", + "slug": "sun_behind_small_cloud", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸŒฅ๏ธ": { + "name": "sun behind large cloud", + "slug": "sun_behind_large_cloud", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸŒฆ๏ธ": { + "name": "sun behind rain cloud", + "slug": "sun_behind_rain_cloud", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸŒง๏ธ": { + "name": "cloud with rain", + "slug": "cloud_with_rain", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸŒจ๏ธ": { + "name": "cloud with snow", + "slug": "cloud_with_snow", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸŒฉ๏ธ": { + "name": "cloud with lightning", + "slug": "cloud_with_lightning", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸŒช๏ธ": { + "name": "tornado", + "slug": "tornado", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸŒซ๏ธ": { + "name": "fog", + "slug": "fog", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸŒฌ๏ธ": { + "name": "wind face", + "slug": "wind_face", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸŒ€": { + "name": "cyclone", + "slug": "cyclone", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒˆ": { + "name": "rainbow", + "slug": "rainbow", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒ‚": { + "name": "closed umbrella", + "slug": "closed_umbrella", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ˜‚๏ธ": { + "name": "umbrella", + "slug": "umbrella", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "โ˜”": { + "name": "umbrella with rain drops", + "slug": "umbrella_with_rain_drops", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ›ฑ๏ธ": { + "name": "umbrella on ground", + "slug": "umbrella_on_ground", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "โšก": { + "name": "high voltage", + "slug": "high_voltage", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ„๏ธ": { + "name": "snowflake", + "slug": "snowflake", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ˜ƒ๏ธ": { + "name": "snowman", + "slug": "snowman", + "group": "Travel & Places", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "โ›„": { + "name": "snowman without snow", + "slug": "snowman_without_snow", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ˜„๏ธ": { + "name": "comet", + "slug": "comet", + "group": "Travel & Places", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ”ฅ": { + "name": "fire", + "slug": "fire", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’ง": { + "name": "droplet", + "slug": "droplet", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŒŠ": { + "name": "water wave", + "slug": "water_wave", + "group": "Travel & Places", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽƒ": { + "name": "jack-o-lantern", + "slug": "jack_o_lantern", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽ„": { + "name": "Christmas tree", + "slug": "christmas_tree", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽ†": { + "name": "fireworks", + "slug": "fireworks", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽ‡": { + "name": "sparkler", + "slug": "sparkler", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿงจ": { + "name": "firecracker", + "slug": "firecracker", + "group": "Activities", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "โœจ": { + "name": "sparkles", + "slug": "sparkles", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽˆ": { + "name": "balloon", + "slug": "balloon", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽ‰": { + "name": "party popper", + "slug": "party_popper", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽŠ": { + "name": "confetti ball", + "slug": "confetti_ball", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽ‹": { + "name": "tanabata tree", + "slug": "tanabata_tree", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽ": { + "name": "pine decoration", + "slug": "pine_decoration", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽŽ": { + "name": "Japanese dolls", + "slug": "japanese_dolls", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽ": { + "name": "carp streamer", + "slug": "carp_streamer", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽ": { + "name": "wind chime", + "slug": "wind_chime", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽ‘": { + "name": "moon viewing ceremony", + "slug": "moon_viewing_ceremony", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿงง": { + "name": "red envelope", + "slug": "red_envelope", + "group": "Activities", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐ŸŽ€": { + "name": "ribbon", + "slug": "ribbon", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽ": { + "name": "wrapped gift", + "slug": "wrapped_gift", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽ—๏ธ": { + "name": "reminder ribbon", + "slug": "reminder_ribbon", + "group": "Activities", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸŽŸ๏ธ": { + "name": "admission tickets", + "slug": "admission_tickets", + "group": "Activities", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸŽซ": { + "name": "ticket", + "slug": "ticket", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽ–๏ธ": { + "name": "military medal", + "slug": "military_medal", + "group": "Activities", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ†": { + "name": "trophy", + "slug": "trophy", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ…": { + "name": "sports medal", + "slug": "sports_medal", + "group": "Activities", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿฅ‡": { + "name": "1st place medal", + "slug": "1st_place_medal", + "group": "Activities", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿฅˆ": { + "name": "2nd place medal", + "slug": "2nd_place_medal", + "group": "Activities", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿฅ‰": { + "name": "3rd place medal", + "slug": "3rd_place_medal", + "group": "Activities", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "โšฝ": { + "name": "soccer ball", + "slug": "soccer_ball", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โšพ": { + "name": "baseball", + "slug": "baseball", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸฅŽ": { + "name": "softball", + "slug": "softball", + "group": "Activities", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿ€": { + "name": "basketball", + "slug": "basketball", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ": { + "name": "volleyball", + "slug": "volleyball", + "group": "Activities", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿˆ": { + "name": "american football", + "slug": "american_football", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‰": { + "name": "rugby football", + "slug": "rugby_football", + "group": "Activities", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐ŸŽพ": { + "name": "tennis", + "slug": "tennis", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฅ": { + "name": "flying disc", + "slug": "flying_disc", + "group": "Activities", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐ŸŽณ": { + "name": "bowling", + "slug": "bowling", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ": { + "name": "cricket game", + "slug": "cricket_game", + "group": "Activities", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ‘": { + "name": "field hockey", + "slug": "field_hockey", + "group": "Activities", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ’": { + "name": "ice hockey", + "slug": "ice_hockey", + "group": "Activities", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿฅ": { + "name": "lacrosse", + "slug": "lacrosse", + "group": "Activities", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿ“": { + "name": "ping pong", + "slug": "ping_pong", + "group": "Activities", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿธ": { + "name": "badminton", + "slug": "badminton", + "group": "Activities", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐ŸฅŠ": { + "name": "boxing glove", + "slug": "boxing_glove", + "group": "Activities", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿฅ‹": { + "name": "martial arts uniform", + "slug": "martial_arts_uniform", + "group": "Activities", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿฅ…": { + "name": "goal net", + "slug": "goal_net", + "group": "Activities", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "โ›ณ": { + "name": "flag in hole", + "slug": "flag_in_hole", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ›ธ๏ธ": { + "name": "ice skate", + "slug": "ice_skate", + "group": "Activities", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸŽฃ": { + "name": "fishing pole", + "slug": "fishing_pole", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿคฟ": { + "name": "diving mask", + "slug": "diving_mask", + "group": "Activities", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐ŸŽฝ": { + "name": "running shirt", + "slug": "running_shirt", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽฟ": { + "name": "skis", + "slug": "skis", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ›ท": { + "name": "sled", + "slug": "sled", + "group": "Activities", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐ŸฅŒ": { + "name": "curling stone", + "slug": "curling_stone", + "group": "Activities", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐ŸŽฏ": { + "name": "bullseye", + "slug": "bullseye", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿช€": { + "name": "yo-yo", + "slug": "yo_yo", + "group": "Activities", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿช": { + "name": "kite", + "slug": "kite", + "group": "Activities", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿ”ซ": { + "name": "water pistol", + "slug": "water_pistol", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽฑ": { + "name": "pool 8 ball", + "slug": "pool_8_ball", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”ฎ": { + "name": "crystal ball", + "slug": "crystal_ball", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿช„": { + "name": "magic wand", + "slug": "magic_wand", + "group": "Activities", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐ŸŽฎ": { + "name": "video game", + "slug": "video_game", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ•น๏ธ": { + "name": "joystick", + "slug": "joystick", + "group": "Activities", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸŽฐ": { + "name": "slot machine", + "slug": "slot_machine", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽฒ": { + "name": "game die", + "slug": "game_die", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿงฉ": { + "name": "puzzle piece", + "slug": "puzzle_piece", + "group": "Activities", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿงธ": { + "name": "teddy bear", + "slug": "teddy_bear", + "group": "Activities", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿช…": { + "name": "piรฑata", + "slug": "pinata", + "group": "Activities", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿชฉ": { + "name": "mirror ball", + "slug": "mirror_ball", + "group": "Activities", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": false + }, + "๐Ÿช†": { + "name": "nesting dolls", + "slug": "nesting_dolls", + "group": "Activities", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "โ™ ๏ธ": { + "name": "spade suit", + "slug": "spade_suit", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ™ฅ๏ธ": { + "name": "heart suit", + "slug": "heart_suit", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ™ฆ๏ธ": { + "name": "diamond suit", + "slug": "diamond_suit", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ™ฃ๏ธ": { + "name": "club suit", + "slug": "club_suit", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ™Ÿ๏ธ": { + "name": "chess pawn", + "slug": "chess_pawn", + "group": "Activities", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿƒ": { + "name": "joker", + "slug": "joker", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ€„": { + "name": "mahjong red dragon", + "slug": "mahjong_red_dragon", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽด": { + "name": "flower playing cards", + "slug": "flower_playing_cards", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽญ": { + "name": "performing arts", + "slug": "performing_arts", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ–ผ๏ธ": { + "name": "framed picture", + "slug": "framed_picture", + "group": "Activities", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸŽจ": { + "name": "artist palette", + "slug": "artist_palette", + "group": "Activities", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿงต": { + "name": "thread", + "slug": "thread", + "group": "Activities", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿชก": { + "name": "sewing needle", + "slug": "sewing_needle", + "group": "Activities", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿงถ": { + "name": "yarn", + "slug": "yarn", + "group": "Activities", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿชข": { + "name": "knot", + "slug": "knot", + "group": "Activities", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿ‘“": { + "name": "glasses", + "slug": "glasses", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ•ถ๏ธ": { + "name": "sunglasses", + "slug": "sunglasses", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿฅฝ": { + "name": "goggles", + "slug": "goggles", + "group": "Objects", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿฅผ": { + "name": "lab coat", + "slug": "lab_coat", + "group": "Objects", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿฆบ": { + "name": "safety vest", + "slug": "safety_vest", + "group": "Objects", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿ‘”": { + "name": "necktie", + "slug": "necktie", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‘•": { + "name": "t-shirt", + "slug": "t_shirt", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‘–": { + "name": "jeans", + "slug": "jeans", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿงฃ": { + "name": "scarf", + "slug": "scarf", + "group": "Objects", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿงค": { + "name": "gloves", + "slug": "gloves", + "group": "Objects", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿงฅ": { + "name": "coat", + "slug": "coat", + "group": "Objects", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿงฆ": { + "name": "socks", + "slug": "socks", + "group": "Objects", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿ‘—": { + "name": "dress", + "slug": "dress", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‘˜": { + "name": "kimono", + "slug": "kimono", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฅป": { + "name": "sari", + "slug": "sari", + "group": "Objects", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿฉฑ": { + "name": "one-piece swimsuit", + "slug": "one_piece_swimsuit", + "group": "Objects", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿฉฒ": { + "name": "briefs", + "slug": "briefs", + "group": "Objects", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿฉณ": { + "name": "shorts", + "slug": "shorts", + "group": "Objects", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿ‘™": { + "name": "bikini", + "slug": "bikini", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‘š": { + "name": "womanโ€™s clothes", + "slug": "woman_s_clothes", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿชญ": { + "name": "folding hand fan", + "slug": "folding_hand_fan", + "group": "Objects", + "emoji_version": "15.0", + "unicode_version": "15.0", + "skin_tone_support": false + }, + "๐Ÿ‘›": { + "name": "purse", + "slug": "purse", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‘œ": { + "name": "handbag", + "slug": "handbag", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‘": { + "name": "clutch bag", + "slug": "clutch_bag", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ›๏ธ": { + "name": "shopping bags", + "slug": "shopping_bags", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸŽ’": { + "name": "backpack", + "slug": "backpack", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฉด": { + "name": "thong sandal", + "slug": "thong_sandal", + "group": "Objects", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿ‘ž": { + "name": "manโ€™s shoe", + "slug": "man_s_shoe", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‘Ÿ": { + "name": "running shoe", + "slug": "running_shoe", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฅพ": { + "name": "hiking boot", + "slug": "hiking_boot", + "group": "Objects", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿฅฟ": { + "name": "flat shoe", + "slug": "flat_shoe", + "group": "Objects", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿ‘ ": { + "name": "high-heeled shoe", + "slug": "high_heeled_shoe", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‘ก": { + "name": "womanโ€™s sandal", + "slug": "woman_s_sandal", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฉฐ": { + "name": "ballet shoes", + "slug": "ballet_shoes", + "group": "Objects", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿ‘ข": { + "name": "womanโ€™s boot", + "slug": "woman_s_boot", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿชฎ": { + "name": "hair pick", + "slug": "hair_pick", + "group": "Objects", + "emoji_version": "15.0", + "unicode_version": "15.0", + "skin_tone_support": false + }, + "๐Ÿ‘‘": { + "name": "crown", + "slug": "crown", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‘’": { + "name": "womanโ€™s hat", + "slug": "woman_s_hat", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽฉ": { + "name": "top hat", + "slug": "top_hat", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽ“": { + "name": "graduation cap", + "slug": "graduation_cap", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿงข": { + "name": "billed cap", + "slug": "billed_cap", + "group": "Objects", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿช–": { + "name": "military helmet", + "slug": "military_helmet", + "group": "Objects", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "โ›‘๏ธ": { + "name": "rescue workerโ€™s helmet", + "slug": "rescue_worker_s_helmet", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ“ฟ": { + "name": "prayer beads", + "slug": "prayer_beads", + "group": "Objects", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ’„": { + "name": "lipstick", + "slug": "lipstick", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’": { + "name": "ring", + "slug": "ring", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’Ž": { + "name": "gem stone", + "slug": "gem_stone", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”‡": { + "name": "muted speaker", + "slug": "muted_speaker", + "group": "Objects", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ”ˆ": { + "name": "speaker low volume", + "slug": "speaker_low_volume", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ”‰": { + "name": "speaker medium volume", + "slug": "speaker_medium_volume", + "group": "Objects", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ”Š": { + "name": "speaker high volume", + "slug": "speaker_high_volume", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“ข": { + "name": "loudspeaker", + "slug": "loudspeaker", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“ฃ": { + "name": "megaphone", + "slug": "megaphone", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“ฏ": { + "name": "postal horn", + "slug": "postal_horn", + "group": "Objects", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ””": { + "name": "bell", + "slug": "bell", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”•": { + "name": "bell with slash", + "slug": "bell_with_slash", + "group": "Objects", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐ŸŽผ": { + "name": "musical score", + "slug": "musical_score", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽต": { + "name": "musical note", + "slug": "musical_note", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽถ": { + "name": "musical notes", + "slug": "musical_notes", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽ™๏ธ": { + "name": "studio microphone", + "slug": "studio_microphone", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸŽš๏ธ": { + "name": "level slider", + "slug": "level_slider", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸŽ›๏ธ": { + "name": "control knobs", + "slug": "control_knobs", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸŽค": { + "name": "microphone", + "slug": "microphone", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽง": { + "name": "headphone", + "slug": "headphone", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“ป": { + "name": "radio", + "slug": "radio", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽท": { + "name": "saxophone", + "slug": "saxophone", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿช—": { + "name": "accordion", + "slug": "accordion", + "group": "Objects", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐ŸŽธ": { + "name": "guitar", + "slug": "guitar", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽน": { + "name": "musical keyboard", + "slug": "musical_keyboard", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽบ": { + "name": "trumpet", + "slug": "trumpet", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽป": { + "name": "violin", + "slug": "violin", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿช•": { + "name": "banjo", + "slug": "banjo", + "group": "Objects", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿฅ": { + "name": "drum", + "slug": "drum", + "group": "Objects", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿช˜": { + "name": "long drum", + "slug": "long_drum", + "group": "Objects", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿช‡": { + "name": "maracas", + "slug": "maracas", + "group": "Objects", + "emoji_version": "15.0", + "unicode_version": "15.0", + "skin_tone_support": false + }, + "๐Ÿชˆ": { + "name": "flute", + "slug": "flute", + "group": "Objects", + "emoji_version": "15.0", + "unicode_version": "15.0", + "skin_tone_support": false + }, + "๐Ÿ“ฑ": { + "name": "mobile phone", + "slug": "mobile_phone", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“ฒ": { + "name": "mobile phone with arrow", + "slug": "mobile_phone_with_arrow", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ˜Ž๏ธ": { + "name": "telephone", + "slug": "telephone", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“ž": { + "name": "telephone receiver", + "slug": "telephone_receiver", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“Ÿ": { + "name": "pager", + "slug": "pager", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“ ": { + "name": "fax machine", + "slug": "fax_machine", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”‹": { + "name": "battery", + "slug": "battery", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿชซ": { + "name": "low battery", + "slug": "low_battery", + "group": "Objects", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": false + }, + "๐Ÿ”Œ": { + "name": "electric plug", + "slug": "electric_plug", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’ป": { + "name": "laptop", + "slug": "laptop", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ–ฅ๏ธ": { + "name": "desktop computer", + "slug": "desktop_computer", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ–จ๏ธ": { + "name": "printer", + "slug": "printer", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "โŒจ๏ธ": { + "name": "keyboard", + "slug": "keyboard", + "group": "Objects", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ–ฑ๏ธ": { + "name": "computer mouse", + "slug": "computer_mouse", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ–ฒ๏ธ": { + "name": "trackball", + "slug": "trackball", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ’ฝ": { + "name": "computer disk", + "slug": "computer_disk", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’พ": { + "name": "floppy disk", + "slug": "floppy_disk", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’ฟ": { + "name": "optical disk", + "slug": "optical_disk", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“€": { + "name": "dvd", + "slug": "dvd", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿงฎ": { + "name": "abacus", + "slug": "abacus", + "group": "Objects", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐ŸŽฅ": { + "name": "movie camera", + "slug": "movie_camera", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽž๏ธ": { + "name": "film frames", + "slug": "film_frames", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ“ฝ๏ธ": { + "name": "film projector", + "slug": "film_projector", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐ŸŽฌ": { + "name": "clapper board", + "slug": "clapper_board", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“บ": { + "name": "television", + "slug": "television", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“ท": { + "name": "camera", + "slug": "camera", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“ธ": { + "name": "camera with flash", + "slug": "camera_with_flash", + "group": "Objects", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ“น": { + "name": "video camera", + "slug": "video_camera", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“ผ": { + "name": "videocassette", + "slug": "videocassette", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”": { + "name": "magnifying glass tilted left", + "slug": "magnifying_glass_tilted_left", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”Ž": { + "name": "magnifying glass tilted right", + "slug": "magnifying_glass_tilted_right", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ•ฏ๏ธ": { + "name": "candle", + "slug": "candle", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ’ก": { + "name": "light bulb", + "slug": "light_bulb", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”ฆ": { + "name": "flashlight", + "slug": "flashlight", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฎ": { + "name": "red paper lantern", + "slug": "red_paper_lantern", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿช”": { + "name": "diya lamp", + "slug": "diya_lamp", + "group": "Objects", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿ“”": { + "name": "notebook with decorative cover", + "slug": "notebook_with_decorative_cover", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“•": { + "name": "closed book", + "slug": "closed_book", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“–": { + "name": "open book", + "slug": "open_book", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“—": { + "name": "green book", + "slug": "green_book", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“˜": { + "name": "blue book", + "slug": "blue_book", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“™": { + "name": "orange book", + "slug": "orange_book", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“š": { + "name": "books", + "slug": "books", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ““": { + "name": "notebook", + "slug": "notebook", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“’": { + "name": "ledger", + "slug": "ledger", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“ƒ": { + "name": "page with curl", + "slug": "page_with_curl", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“œ": { + "name": "scroll", + "slug": "scroll", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“„": { + "name": "page facing up", + "slug": "page_facing_up", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“ฐ": { + "name": "newspaper", + "slug": "newspaper", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ—ž๏ธ": { + "name": "rolled-up newspaper", + "slug": "rolled_up_newspaper", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ“‘": { + "name": "bookmark tabs", + "slug": "bookmark_tabs", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”–": { + "name": "bookmark", + "slug": "bookmark", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿท๏ธ": { + "name": "label", + "slug": "label", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ’ฐ": { + "name": "money bag", + "slug": "money_bag", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿช™": { + "name": "coin", + "slug": "coin", + "group": "Objects", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿ’ด": { + "name": "yen banknote", + "slug": "yen_banknote", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’ต": { + "name": "dollar banknote", + "slug": "dollar_banknote", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’ถ": { + "name": "euro banknote", + "slug": "euro_banknote", + "group": "Objects", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ’ท": { + "name": "pound banknote", + "slug": "pound_banknote", + "group": "Objects", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ’ธ": { + "name": "money with wings", + "slug": "money_with_wings", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’ณ": { + "name": "credit card", + "slug": "credit_card", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿงพ": { + "name": "receipt", + "slug": "receipt", + "group": "Objects", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿ’น": { + "name": "chart increasing with yen", + "slug": "chart_increasing_with_yen", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โœ‰๏ธ": { + "name": "envelope", + "slug": "envelope", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“ง": { + "name": "e-mail", + "slug": "e_mail", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“จ": { + "name": "incoming envelope", + "slug": "incoming_envelope", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“ฉ": { + "name": "envelope with arrow", + "slug": "envelope_with_arrow", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“ค": { + "name": "outbox tray", + "slug": "outbox_tray", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“ฅ": { + "name": "inbox tray", + "slug": "inbox_tray", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“ฆ": { + "name": "package", + "slug": "package", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“ซ": { + "name": "closed mailbox with raised flag", + "slug": "closed_mailbox_with_raised_flag", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“ช": { + "name": "closed mailbox with lowered flag", + "slug": "closed_mailbox_with_lowered_flag", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“ฌ": { + "name": "open mailbox with raised flag", + "slug": "open_mailbox_with_raised_flag", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ“ญ": { + "name": "open mailbox with lowered flag", + "slug": "open_mailbox_with_lowered_flag", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ“ฎ": { + "name": "postbox", + "slug": "postbox", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ—ณ๏ธ": { + "name": "ballot box with ballot", + "slug": "ballot_box_with_ballot", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "โœ๏ธ": { + "name": "pencil", + "slug": "pencil", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โœ’๏ธ": { + "name": "black nib", + "slug": "black_nib", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ–‹๏ธ": { + "name": "fountain pen", + "slug": "fountain_pen", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ–Š๏ธ": { + "name": "pen", + "slug": "pen", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ–Œ๏ธ": { + "name": "paintbrush", + "slug": "paintbrush", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ–๏ธ": { + "name": "crayon", + "slug": "crayon", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ“": { + "name": "memo", + "slug": "memo", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’ผ": { + "name": "briefcase", + "slug": "briefcase", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“": { + "name": "file folder", + "slug": "file_folder", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“‚": { + "name": "open file folder", + "slug": "open_file_folder", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ—‚๏ธ": { + "name": "card index dividers", + "slug": "card_index_dividers", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ“…": { + "name": "calendar", + "slug": "calendar", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“†": { + "name": "tear-off calendar", + "slug": "tear_off_calendar", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ—’๏ธ": { + "name": "spiral notepad", + "slug": "spiral_notepad", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ—“๏ธ": { + "name": "spiral calendar", + "slug": "spiral_calendar", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ“‡": { + "name": "card index", + "slug": "card_index", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“ˆ": { + "name": "chart increasing", + "slug": "chart_increasing", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“‰": { + "name": "chart decreasing", + "slug": "chart_decreasing", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“Š": { + "name": "bar chart", + "slug": "bar_chart", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“‹": { + "name": "clipboard", + "slug": "clipboard", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“Œ": { + "name": "pushpin", + "slug": "pushpin", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“": { + "name": "round pushpin", + "slug": "round_pushpin", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“Ž": { + "name": "paperclip", + "slug": "paperclip", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ–‡๏ธ": { + "name": "linked paperclips", + "slug": "linked_paperclips", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ“": { + "name": "straight ruler", + "slug": "straight_ruler", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“": { + "name": "triangular ruler", + "slug": "triangular_ruler", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โœ‚๏ธ": { + "name": "scissors", + "slug": "scissors", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ—ƒ๏ธ": { + "name": "card file box", + "slug": "card_file_box", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ—„๏ธ": { + "name": "file cabinet", + "slug": "file_cabinet", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ—‘๏ธ": { + "name": "wastebasket", + "slug": "wastebasket", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ”’": { + "name": "locked", + "slug": "locked", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”“": { + "name": "unlocked", + "slug": "unlocked", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”": { + "name": "locked with pen", + "slug": "locked_with_pen", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”": { + "name": "locked with key", + "slug": "locked_with_key", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”‘": { + "name": "key", + "slug": "key", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ—๏ธ": { + "name": "old key", + "slug": "old_key", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ”จ": { + "name": "hammer", + "slug": "hammer", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿช“": { + "name": "axe", + "slug": "axe", + "group": "Objects", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "โ›๏ธ": { + "name": "pick", + "slug": "pick", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "โš’๏ธ": { + "name": "hammer and pick", + "slug": "hammer_and_pick", + "group": "Objects", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ› ๏ธ": { + "name": "hammer and wrench", + "slug": "hammer_and_wrench", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ—ก๏ธ": { + "name": "dagger", + "slug": "dagger", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "โš”๏ธ": { + "name": "crossed swords", + "slug": "crossed_swords", + "group": "Objects", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ’ฃ": { + "name": "bomb", + "slug": "bomb", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿชƒ": { + "name": "boomerang", + "slug": "boomerang", + "group": "Objects", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿน": { + "name": "bow and arrow", + "slug": "bow_and_arrow", + "group": "Objects", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ›ก๏ธ": { + "name": "shield", + "slug": "shield", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿชš": { + "name": "carpentry saw", + "slug": "carpentry_saw", + "group": "Objects", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿ”ง": { + "name": "wrench", + "slug": "wrench", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿช›": { + "name": "screwdriver", + "slug": "screwdriver", + "group": "Objects", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿ”ฉ": { + "name": "nut and bolt", + "slug": "nut_and_bolt", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โš™๏ธ": { + "name": "gear", + "slug": "gear", + "group": "Objects", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ—œ๏ธ": { + "name": "clamp", + "slug": "clamp", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "โš–๏ธ": { + "name": "balance scale", + "slug": "balance_scale", + "group": "Objects", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿฆฏ": { + "name": "white cane", + "slug": "white_cane", + "group": "Objects", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿ”—": { + "name": "link", + "slug": "link", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ›“๏ธโ€๐Ÿ’ฅ": { + "name": "broken chain", + "slug": "broken_chain", + "group": "Objects", + "emoji_version": "15.1", + "unicode_version": "15.1", + "skin_tone_support": false + }, + "โ›“๏ธ": { + "name": "chains", + "slug": "chains", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿช": { + "name": "hook", + "slug": "hook", + "group": "Objects", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿงฐ": { + "name": "toolbox", + "slug": "toolbox", + "group": "Objects", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿงฒ": { + "name": "magnet", + "slug": "magnet", + "group": "Objects", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿชœ": { + "name": "ladder", + "slug": "ladder", + "group": "Objects", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "โš—๏ธ": { + "name": "alembic", + "slug": "alembic", + "group": "Objects", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿงช": { + "name": "test tube", + "slug": "test_tube", + "group": "Objects", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿงซ": { + "name": "petri dish", + "slug": "petri_dish", + "group": "Objects", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿงฌ": { + "name": "dna", + "slug": "dna", + "group": "Objects", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿ”ฌ": { + "name": "microscope", + "slug": "microscope", + "group": "Objects", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ”ญ": { + "name": "telescope", + "slug": "telescope", + "group": "Objects", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ“ก": { + "name": "satellite antenna", + "slug": "satellite_antenna", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’‰": { + "name": "syringe", + "slug": "syringe", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฉธ": { + "name": "drop of blood", + "slug": "drop_of_blood", + "group": "Objects", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿ’Š": { + "name": "pill", + "slug": "pill", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿฉน": { + "name": "adhesive bandage", + "slug": "adhesive_bandage", + "group": "Objects", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿฉผ": { + "name": "crutch", + "slug": "crutch", + "group": "Objects", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": false + }, + "๐Ÿฉบ": { + "name": "stethoscope", + "slug": "stethoscope", + "group": "Objects", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿฉป": { + "name": "x-ray", + "slug": "x_ray", + "group": "Objects", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": false + }, + "๐Ÿšช": { + "name": "door", + "slug": "door", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ›—": { + "name": "elevator", + "slug": "elevator", + "group": "Objects", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿชž": { + "name": "mirror", + "slug": "mirror", + "group": "Objects", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐ŸชŸ": { + "name": "window", + "slug": "window", + "group": "Objects", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿ›๏ธ": { + "name": "bed", + "slug": "bed", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ›‹๏ธ": { + "name": "couch and lamp", + "slug": "couch_and_lamp", + "group": "Objects", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿช‘": { + "name": "chair", + "slug": "chair", + "group": "Objects", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿšฝ": { + "name": "toilet", + "slug": "toilet", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿช ": { + "name": "plunger", + "slug": "plunger", + "group": "Objects", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿšฟ": { + "name": "shower", + "slug": "shower", + "group": "Objects", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ›": { + "name": "bathtub", + "slug": "bathtub", + "group": "Objects", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿชค": { + "name": "mouse trap", + "slug": "mouse_trap", + "group": "Objects", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿช’": { + "name": "razor", + "slug": "razor", + "group": "Objects", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿงด": { + "name": "lotion bottle", + "slug": "lotion_bottle", + "group": "Objects", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿงท": { + "name": "safety pin", + "slug": "safety_pin", + "group": "Objects", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿงน": { + "name": "broom", + "slug": "broom", + "group": "Objects", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿงบ": { + "name": "basket", + "slug": "basket", + "group": "Objects", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿงป": { + "name": "roll of paper", + "slug": "roll_of_paper", + "group": "Objects", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿชฃ": { + "name": "bucket", + "slug": "bucket", + "group": "Objects", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿงผ": { + "name": "soap", + "slug": "soap", + "group": "Objects", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿซง": { + "name": "bubbles", + "slug": "bubbles", + "group": "Objects", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": false + }, + "๐Ÿชฅ": { + "name": "toothbrush", + "slug": "toothbrush", + "group": "Objects", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿงฝ": { + "name": "sponge", + "slug": "sponge", + "group": "Objects", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿงฏ": { + "name": "fire extinguisher", + "slug": "fire_extinguisher", + "group": "Objects", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿ›’": { + "name": "shopping cart", + "slug": "shopping_cart", + "group": "Objects", + "emoji_version": "3.0", + "unicode_version": "3.0", + "skin_tone_support": false + }, + "๐Ÿšฌ": { + "name": "cigarette", + "slug": "cigarette", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โšฐ๏ธ": { + "name": "coffin", + "slug": "coffin", + "group": "Objects", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿชฆ": { + "name": "headstone", + "slug": "headstone", + "group": "Objects", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "โšฑ๏ธ": { + "name": "funeral urn", + "slug": "funeral_urn", + "group": "Objects", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿงฟ": { + "name": "nazar amulet", + "slug": "nazar_amulet", + "group": "Objects", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿชฌ": { + "name": "hamsa", + "slug": "hamsa", + "group": "Objects", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": false + }, + "๐Ÿ—ฟ": { + "name": "moai", + "slug": "moai", + "group": "Objects", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿชง": { + "name": "placard", + "slug": "placard", + "group": "Objects", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿชช": { + "name": "identification card", + "slug": "identification_card", + "group": "Objects", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": false + }, + "๐Ÿง": { + "name": "ATM sign", + "slug": "atm_sign", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿšฎ": { + "name": "litter in bin sign", + "slug": "litter_in_bin_sign", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿšฐ": { + "name": "potable water", + "slug": "potable_water", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "โ™ฟ": { + "name": "wheelchair symbol", + "slug": "wheelchair_symbol", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿšน": { + "name": "menโ€™s room", + "slug": "men_s_room", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿšบ": { + "name": "womenโ€™s room", + "slug": "women_s_room", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿšป": { + "name": "restroom", + "slug": "restroom", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿšผ": { + "name": "baby symbol", + "slug": "baby_symbol", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿšพ": { + "name": "water closet", + "slug": "water_closet", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ›‚": { + "name": "passport control", + "slug": "passport_control", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ›ƒ": { + "name": "customs", + "slug": "customs", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ›„": { + "name": "baggage claim", + "slug": "baggage_claim", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ›…": { + "name": "left luggage", + "slug": "left_luggage", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "โš ๏ธ": { + "name": "warning", + "slug": "warning", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿšธ": { + "name": "children crossing", + "slug": "children_crossing", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "โ›”": { + "name": "no entry", + "slug": "no_entry", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿšซ": { + "name": "prohibited", + "slug": "prohibited", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿšณ": { + "name": "no bicycles", + "slug": "no_bicycles", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿšญ": { + "name": "no smoking", + "slug": "no_smoking", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿšฏ": { + "name": "no littering", + "slug": "no_littering", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿšฑ": { + "name": "non-potable water", + "slug": "non_potable_water", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿšท": { + "name": "no pedestrians", + "slug": "no_pedestrians", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ“ต": { + "name": "no mobile phones", + "slug": "no_mobile_phones", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ”ž": { + "name": "no one under eighteen", + "slug": "no_one_under_eighteen", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ˜ข๏ธ": { + "name": "radioactive", + "slug": "radioactive", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "โ˜ฃ๏ธ": { + "name": "biohazard", + "slug": "biohazard", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "โฌ†๏ธ": { + "name": "up arrow", + "slug": "up_arrow", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ†—๏ธ": { + "name": "up-right arrow", + "slug": "up_right_arrow", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โžก๏ธ": { + "name": "right arrow", + "slug": "right_arrow", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ†˜๏ธ": { + "name": "down-right arrow", + "slug": "down_right_arrow", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โฌ‡๏ธ": { + "name": "down arrow", + "slug": "down_arrow", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ†™๏ธ": { + "name": "down-left arrow", + "slug": "down_left_arrow", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โฌ…๏ธ": { + "name": "left arrow", + "slug": "left_arrow", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ†–๏ธ": { + "name": "up-left arrow", + "slug": "up_left_arrow", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ†•๏ธ": { + "name": "up-down arrow", + "slug": "up_down_arrow", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ†”๏ธ": { + "name": "left-right arrow", + "slug": "left_right_arrow", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ†ฉ๏ธ": { + "name": "right arrow curving left", + "slug": "right_arrow_curving_left", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ†ช๏ธ": { + "name": "left arrow curving right", + "slug": "left_arrow_curving_right", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โคด๏ธ": { + "name": "right arrow curving up", + "slug": "right_arrow_curving_up", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โคต๏ธ": { + "name": "right arrow curving down", + "slug": "right_arrow_curving_down", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”ƒ": { + "name": "clockwise vertical arrows", + "slug": "clockwise_vertical_arrows", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”„": { + "name": "counterclockwise arrows button", + "slug": "counterclockwise_arrows_button", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ”™": { + "name": "BACK arrow", + "slug": "back_arrow", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”š": { + "name": "END arrow", + "slug": "end_arrow", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”›": { + "name": "ON! arrow", + "slug": "on_arrow", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”œ": { + "name": "SOON arrow", + "slug": "soon_arrow", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”": { + "name": "TOP arrow", + "slug": "top_arrow", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ›": { + "name": "place of worship", + "slug": "place_of_worship", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "โš›๏ธ": { + "name": "atom symbol", + "slug": "atom_symbol", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ•‰๏ธ": { + "name": "om", + "slug": "om", + "group": "Symbols", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "โœก๏ธ": { + "name": "star of David", + "slug": "star_of_david", + "group": "Symbols", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "โ˜ธ๏ธ": { + "name": "wheel of dharma", + "slug": "wheel_of_dharma", + "group": "Symbols", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "โ˜ฏ๏ธ": { + "name": "yin yang", + "slug": "yin_yang", + "group": "Symbols", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "โœ๏ธ": { + "name": "latin cross", + "slug": "latin_cross", + "group": "Symbols", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "โ˜ฆ๏ธ": { + "name": "orthodox cross", + "slug": "orthodox_cross", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "โ˜ช๏ธ": { + "name": "star and crescent", + "slug": "star_and_crescent", + "group": "Symbols", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "โ˜ฎ๏ธ": { + "name": "peace symbol", + "slug": "peace_symbol", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ•Ž": { + "name": "menorah", + "slug": "menorah", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ”ฏ": { + "name": "dotted six-pointed star", + "slug": "dotted_six_pointed_star", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿชฏ": { + "name": "khanda", + "slug": "khanda", + "group": "Symbols", + "emoji_version": "15.0", + "unicode_version": "15.0", + "skin_tone_support": false + }, + "โ™ˆ": { + "name": "Aries", + "slug": "aries", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ™‰": { + "name": "Taurus", + "slug": "taurus", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ™Š": { + "name": "Gemini", + "slug": "gemini", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ™‹": { + "name": "Cancer", + "slug": "cancer", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ™Œ": { + "name": "Leo", + "slug": "leo", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ™": { + "name": "Virgo", + "slug": "virgo", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ™Ž": { + "name": "Libra", + "slug": "libra", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ™": { + "name": "Scorpio", + "slug": "scorpio", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ™": { + "name": "Sagittarius", + "slug": "sagittarius", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ™‘": { + "name": "Capricorn", + "slug": "capricorn", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ™’": { + "name": "Aquarius", + "slug": "aquarius", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ™“": { + "name": "Pisces", + "slug": "pisces", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ›Ž": { + "name": "Ophiuchus", + "slug": "ophiuchus", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”€": { + "name": "shuffle tracks button", + "slug": "shuffle_tracks_button", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ”": { + "name": "repeat button", + "slug": "repeat_button", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ”‚": { + "name": "repeat single button", + "slug": "repeat_single_button", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "โ–ถ๏ธ": { + "name": "play button", + "slug": "play_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โฉ": { + "name": "fast-forward button", + "slug": "fast_forward_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โญ๏ธ": { + "name": "next track button", + "slug": "next_track_button", + "group": "Symbols", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "โฏ๏ธ": { + "name": "play or pause button", + "slug": "play_or_pause_button", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "โ—€๏ธ": { + "name": "reverse button", + "slug": "reverse_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โช": { + "name": "fast reverse button", + "slug": "fast_reverse_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โฎ๏ธ": { + "name": "last track button", + "slug": "last_track_button", + "group": "Symbols", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿ”ผ": { + "name": "upwards button", + "slug": "upwards_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โซ": { + "name": "fast up button", + "slug": "fast_up_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”ฝ": { + "name": "downwards button", + "slug": "downwards_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โฌ": { + "name": "fast down button", + "slug": "fast_down_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โธ๏ธ": { + "name": "pause button", + "slug": "pause_button", + "group": "Symbols", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "โน๏ธ": { + "name": "stop button", + "slug": "stop_button", + "group": "Symbols", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "โบ๏ธ": { + "name": "record button", + "slug": "record_button", + "group": "Symbols", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "โ๏ธ": { + "name": "eject button", + "slug": "eject_button", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐ŸŽฆ": { + "name": "cinema", + "slug": "cinema", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”…": { + "name": "dim button", + "slug": "dim_button", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ”†": { + "name": "bright button", + "slug": "bright_button", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ“ถ": { + "name": "antenna bars", + "slug": "antenna_bars", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ›œ": { + "name": "wireless", + "slug": "wireless", + "group": "Symbols", + "emoji_version": "15.0", + "unicode_version": "15.0", + "skin_tone_support": false + }, + "๐Ÿ“ณ": { + "name": "vibration mode", + "slug": "vibration_mode", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“ด": { + "name": "mobile phone off", + "slug": "mobile_phone_off", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ™€๏ธ": { + "name": "female sign", + "slug": "female_sign", + "group": "Symbols", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": false + }, + "โ™‚๏ธ": { + "name": "male sign", + "slug": "male_sign", + "group": "Symbols", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": false + }, + "โšง๏ธ": { + "name": "transgender symbol", + "slug": "transgender_symbol", + "group": "Symbols", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "โœ–๏ธ": { + "name": "multiply", + "slug": "multiply", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โž•": { + "name": "plus", + "slug": "plus", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โž–": { + "name": "minus", + "slug": "minus", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โž—": { + "name": "divide", + "slug": "divide", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŸฐ": { + "name": "heavy equals sign", + "slug": "heavy_equals_sign", + "group": "Symbols", + "emoji_version": "14.0", + "unicode_version": "14.0", + "skin_tone_support": false + }, + "โ™พ๏ธ": { + "name": "infinity", + "slug": "infinity", + "group": "Symbols", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "โ€ผ๏ธ": { + "name": "double exclamation mark", + "slug": "double_exclamation_mark", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ‰๏ธ": { + "name": "exclamation question mark", + "slug": "exclamation_question_mark", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ“": { + "name": "red question mark", + "slug": "red_question_mark", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ”": { + "name": "white question mark", + "slug": "white_question_mark", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ•": { + "name": "white exclamation mark", + "slug": "white_exclamation_mark", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ—": { + "name": "red exclamation mark", + "slug": "red_exclamation_mark", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "ใ€ฐ๏ธ": { + "name": "wavy dash", + "slug": "wavy_dash", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’ฑ": { + "name": "currency exchange", + "slug": "currency_exchange", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’ฒ": { + "name": "heavy dollar sign", + "slug": "heavy_dollar_sign", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โš•๏ธ": { + "name": "medical symbol", + "slug": "medical_symbol", + "group": "Symbols", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": false + }, + "โ™ป๏ธ": { + "name": "recycling symbol", + "slug": "recycling_symbol", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โšœ๏ธ": { + "name": "fleur-de-lis", + "slug": "fleur_de_lis", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿ”ฑ": { + "name": "trident emblem", + "slug": "trident_emblem", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ“›": { + "name": "name badge", + "slug": "name_badge", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”ฐ": { + "name": "Japanese symbol for beginner", + "slug": "japanese_symbol_for_beginner", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โญ•": { + "name": "hollow red circle", + "slug": "hollow_red_circle", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โœ…": { + "name": "check mark button", + "slug": "check_mark_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ˜‘๏ธ": { + "name": "check box with check", + "slug": "check_box_with_check", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โœ”๏ธ": { + "name": "check mark", + "slug": "check_mark", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โŒ": { + "name": "cross mark", + "slug": "cross_mark", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โŽ": { + "name": "cross mark button", + "slug": "cross_mark_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โžฐ": { + "name": "curly loop", + "slug": "curly_loop", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โžฟ": { + "name": "double curly loop", + "slug": "double_curly_loop", + "group": "Symbols", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "ใ€ฝ๏ธ": { + "name": "part alternation mark", + "slug": "part_alternation_mark", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โœณ๏ธ": { + "name": "eight-spoked asterisk", + "slug": "eight_spoked_asterisk", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โœด๏ธ": { + "name": "eight-pointed star", + "slug": "eight_pointed_star", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ‡๏ธ": { + "name": "sparkle", + "slug": "sparkle", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "ยฉ๏ธ": { + "name": "copyright", + "slug": "copyright", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "ยฎ๏ธ": { + "name": "registered", + "slug": "registered", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ„ข๏ธ": { + "name": "trade mark", + "slug": "trade_mark", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "#๏ธโƒฃ": { + "name": "keycap #", + "slug": "keycap_number_sign", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "*๏ธโƒฃ": { + "name": "keycap *", + "slug": "keycap_asterisk", + "group": "Symbols", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "0๏ธโƒฃ": { + "name": "keycap 0", + "slug": "keycap_0", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "1๏ธโƒฃ": { + "name": "keycap 1", + "slug": "keycap_1", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "2๏ธโƒฃ": { + "name": "keycap 2", + "slug": "keycap_2", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "3๏ธโƒฃ": { + "name": "keycap 3", + "slug": "keycap_3", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "4๏ธโƒฃ": { + "name": "keycap 4", + "slug": "keycap_4", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "5๏ธโƒฃ": { + "name": "keycap 5", + "slug": "keycap_5", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "6๏ธโƒฃ": { + "name": "keycap 6", + "slug": "keycap_6", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "7๏ธโƒฃ": { + "name": "keycap 7", + "slug": "keycap_7", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "8๏ธโƒฃ": { + "name": "keycap 8", + "slug": "keycap_8", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "9๏ธโƒฃ": { + "name": "keycap 9", + "slug": "keycap_9", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”Ÿ": { + "name": "keycap 10", + "slug": "keycap_10", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ” ": { + "name": "input latin uppercase", + "slug": "input_latin_uppercase", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”ก": { + "name": "input latin lowercase", + "slug": "input_latin_lowercase", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”ข": { + "name": "input numbers", + "slug": "input_numbers", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”ฃ": { + "name": "input symbols", + "slug": "input_symbols", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”ค": { + "name": "input latin letters", + "slug": "input_latin_letters", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ…ฐ๏ธ": { + "name": "A button (blood type)", + "slug": "a_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ†Ž": { + "name": "AB button (blood type)", + "slug": "ab_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ…ฑ๏ธ": { + "name": "B button (blood type)", + "slug": "b_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ†‘": { + "name": "CL button", + "slug": "cl_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ†’": { + "name": "COOL button", + "slug": "cool_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ†“": { + "name": "FREE button", + "slug": "free_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ„น๏ธ": { + "name": "information", + "slug": "information", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ†”": { + "name": "ID button", + "slug": "id_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ“‚๏ธ": { + "name": "circled M", + "slug": "circled_m", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ†•": { + "name": "NEW button", + "slug": "new_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ†–": { + "name": "NG button", + "slug": "ng_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ…พ๏ธ": { + "name": "O button (blood type)", + "slug": "o_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ†—": { + "name": "OK button", + "slug": "ok_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ…ฟ๏ธ": { + "name": "P button", + "slug": "p_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ†˜": { + "name": "SOS button", + "slug": "sos_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ†™": { + "name": "UP! button", + "slug": "up_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ†š": { + "name": "VS button", + "slug": "vs_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿˆ": { + "name": "Japanese โ€œhereโ€ button", + "slug": "japanese_here_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿˆ‚๏ธ": { + "name": "Japanese โ€œservice chargeโ€ button", + "slug": "japanese_service_charge_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿˆท๏ธ": { + "name": "Japanese โ€œmonthly amountโ€ button", + "slug": "japanese_monthly_amount_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿˆถ": { + "name": "Japanese โ€œnot free of chargeโ€ button", + "slug": "japanese_not_free_of_charge_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿˆฏ": { + "name": "Japanese โ€œreservedโ€ button", + "slug": "japanese_reserved_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‰": { + "name": "Japanese โ€œbargainโ€ button", + "slug": "japanese_bargain_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿˆน": { + "name": "Japanese โ€œdiscountโ€ button", + "slug": "japanese_discount_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿˆš": { + "name": "Japanese โ€œfree of chargeโ€ button", + "slug": "japanese_free_of_charge_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿˆฒ": { + "name": "Japanese โ€œprohibitedโ€ button", + "slug": "japanese_prohibited_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‰‘": { + "name": "Japanese โ€œacceptableโ€ button", + "slug": "japanese_acceptable_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿˆธ": { + "name": "Japanese โ€œapplicationโ€ button", + "slug": "japanese_application_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿˆด": { + "name": "Japanese โ€œpassing gradeโ€ button", + "slug": "japanese_passing_grade_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿˆณ": { + "name": "Japanese โ€œvacancyโ€ button", + "slug": "japanese_vacancy_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "ใŠ—๏ธ": { + "name": "Japanese โ€œcongratulationsโ€ button", + "slug": "japanese_congratulations_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "ใŠ™๏ธ": { + "name": "Japanese โ€œsecretโ€ button", + "slug": "japanese_secret_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿˆบ": { + "name": "Japanese โ€œopen for businessโ€ button", + "slug": "japanese_open_for_business_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿˆต": { + "name": "Japanese โ€œno vacancyโ€ button", + "slug": "japanese_no_vacancy_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”ด": { + "name": "red circle", + "slug": "red_circle", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŸ ": { + "name": "orange circle", + "slug": "orange_circle", + "group": "Symbols", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐ŸŸก": { + "name": "yellow circle", + "slug": "yellow_circle", + "group": "Symbols", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐ŸŸข": { + "name": "green circle", + "slug": "green_circle", + "group": "Symbols", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐Ÿ”ต": { + "name": "blue circle", + "slug": "blue_circle", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŸฃ": { + "name": "purple circle", + "slug": "purple_circle", + "group": "Symbols", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐ŸŸค": { + "name": "brown circle", + "slug": "brown_circle", + "group": "Symbols", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "โšซ": { + "name": "black circle", + "slug": "black_circle", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โšช": { + "name": "white circle", + "slug": "white_circle", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŸฅ": { + "name": "red square", + "slug": "red_square", + "group": "Symbols", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐ŸŸง": { + "name": "orange square", + "slug": "orange_square", + "group": "Symbols", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐ŸŸจ": { + "name": "yellow square", + "slug": "yellow_square", + "group": "Symbols", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐ŸŸฉ": { + "name": "green square", + "slug": "green_square", + "group": "Symbols", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐ŸŸฆ": { + "name": "blue square", + "slug": "blue_square", + "group": "Symbols", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐ŸŸช": { + "name": "purple square", + "slug": "purple_square", + "group": "Symbols", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "๐ŸŸซ": { + "name": "brown square", + "slug": "brown_square", + "group": "Symbols", + "emoji_version": "12.0", + "unicode_version": "12.0", + "skin_tone_support": false + }, + "โฌ›": { + "name": "black large square", + "slug": "black_large_square", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โฌœ": { + "name": "white large square", + "slug": "white_large_square", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ—ผ๏ธ": { + "name": "black medium square", + "slug": "black_medium_square", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ—ป๏ธ": { + "name": "white medium square", + "slug": "white_medium_square", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ—พ": { + "name": "black medium-small square", + "slug": "black_medium_small_square", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ—ฝ": { + "name": "white medium-small square", + "slug": "white_medium_small_square", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ–ช๏ธ": { + "name": "black small square", + "slug": "black_small_square", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "โ–ซ๏ธ": { + "name": "white small square", + "slug": "white_small_square", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”ถ": { + "name": "large orange diamond", + "slug": "large_orange_diamond", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”ท": { + "name": "large blue diamond", + "slug": "large_blue_diamond", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”ธ": { + "name": "small orange diamond", + "slug": "small_orange_diamond", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”น": { + "name": "small blue diamond", + "slug": "small_blue_diamond", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”บ": { + "name": "red triangle pointed up", + "slug": "red_triangle_pointed_up", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”ป": { + "name": "red triangle pointed down", + "slug": "red_triangle_pointed_down", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ’ ": { + "name": "diamond with a dot", + "slug": "diamond_with_a_dot", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”˜": { + "name": "radio button", + "slug": "radio_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”ณ": { + "name": "white square button", + "slug": "white_square_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ”ฒ": { + "name": "black square button", + "slug": "black_square_button", + "group": "Symbols", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ": { + "name": "chequered flag", + "slug": "chequered_flag", + "group": "Flags", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿšฉ": { + "name": "triangular flag", + "slug": "triangular_flag", + "group": "Flags", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐ŸŽŒ": { + "name": "crossed flags", + "slug": "crossed_flags", + "group": "Flags", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿด": { + "name": "black flag", + "slug": "black_flag", + "group": "Flags", + "emoji_version": "1.0", + "unicode_version": "1.0", + "skin_tone_support": false + }, + "๐Ÿณ๏ธ": { + "name": "white flag", + "slug": "white_flag", + "group": "Flags", + "emoji_version": "0.7", + "unicode_version": "0.7", + "skin_tone_support": false + }, + "๐Ÿณ๏ธโ€๐ŸŒˆ": { + "name": "rainbow flag", + "slug": "rainbow_flag", + "group": "Flags", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": false + }, + "๐Ÿณ๏ธโ€โšง๏ธ": { + "name": "transgender flag", + "slug": "transgender_flag", + "group": "Flags", + "emoji_version": "13.0", + "unicode_version": "13.0", + "skin_tone_support": false + }, + "๐Ÿดโ€โ˜ ๏ธ": { + "name": "pirate flag", + "slug": "pirate_flag", + "group": "Flags", + "emoji_version": "11.0", + "unicode_version": "11.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฆ๐Ÿ‡จ": { + "name": "flag Ascension Island", + "slug": "flag_ascension_island", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฆ๐Ÿ‡ฉ": { + "name": "flag Andorra", + "slug": "flag_andorra", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฆ๐Ÿ‡ช": { + "name": "flag United Arab Emirates", + "slug": "flag_united_arab_emirates", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฆ๐Ÿ‡ซ": { + "name": "flag Afghanistan", + "slug": "flag_afghanistan", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฆ๐Ÿ‡ฌ": { + "name": "flag Antigua & Barbuda", + "slug": "flag_antigua_barbuda", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฆ๐Ÿ‡ฎ": { + "name": "flag Anguilla", + "slug": "flag_anguilla", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฆ๐Ÿ‡ฑ": { + "name": "flag Albania", + "slug": "flag_albania", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฆ๐Ÿ‡ฒ": { + "name": "flag Armenia", + "slug": "flag_armenia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฆ๐Ÿ‡ด": { + "name": "flag Angola", + "slug": "flag_angola", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฆ๐Ÿ‡ถ": { + "name": "flag Antarctica", + "slug": "flag_antarctica", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฆ๐Ÿ‡ท": { + "name": "flag Argentina", + "slug": "flag_argentina", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฆ๐Ÿ‡ธ": { + "name": "flag American Samoa", + "slug": "flag_american_samoa", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฆ๐Ÿ‡น": { + "name": "flag Austria", + "slug": "flag_austria", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฆ๐Ÿ‡บ": { + "name": "flag Australia", + "slug": "flag_australia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฆ๐Ÿ‡ผ": { + "name": "flag Aruba", + "slug": "flag_aruba", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฆ๐Ÿ‡ฝ": { + "name": "flag ร…land Islands", + "slug": "flag_aland_islands", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฆ๐Ÿ‡ฟ": { + "name": "flag Azerbaijan", + "slug": "flag_azerbaijan", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ง๐Ÿ‡ฆ": { + "name": "flag Bosnia & Herzegovina", + "slug": "flag_bosnia_herzegovina", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ง๐Ÿ‡ง": { + "name": "flag Barbados", + "slug": "flag_barbados", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ง๐Ÿ‡ฉ": { + "name": "flag Bangladesh", + "slug": "flag_bangladesh", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ง๐Ÿ‡ช": { + "name": "flag Belgium", + "slug": "flag_belgium", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ง๐Ÿ‡ซ": { + "name": "flag Burkina Faso", + "slug": "flag_burkina_faso", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ง๐Ÿ‡ฌ": { + "name": "flag Bulgaria", + "slug": "flag_bulgaria", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ง๐Ÿ‡ญ": { + "name": "flag Bahrain", + "slug": "flag_bahrain", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ง๐Ÿ‡ฎ": { + "name": "flag Burundi", + "slug": "flag_burundi", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ง๐Ÿ‡ฏ": { + "name": "flag Benin", + "slug": "flag_benin", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ง๐Ÿ‡ฑ": { + "name": "flag St. Barthรฉlemy", + "slug": "flag_st_barthelemy", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ง๐Ÿ‡ฒ": { + "name": "flag Bermuda", + "slug": "flag_bermuda", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ง๐Ÿ‡ณ": { + "name": "flag Brunei", + "slug": "flag_brunei", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ง๐Ÿ‡ด": { + "name": "flag Bolivia", + "slug": "flag_bolivia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ง๐Ÿ‡ถ": { + "name": "flag Caribbean Netherlands", + "slug": "flag_caribbean_netherlands", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ง๐Ÿ‡ท": { + "name": "flag Brazil", + "slug": "flag_brazil", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ง๐Ÿ‡ธ": { + "name": "flag Bahamas", + "slug": "flag_bahamas", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ง๐Ÿ‡น": { + "name": "flag Bhutan", + "slug": "flag_bhutan", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ง๐Ÿ‡ป": { + "name": "flag Bouvet Island", + "slug": "flag_bouvet_island", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ง๐Ÿ‡ผ": { + "name": "flag Botswana", + "slug": "flag_botswana", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ง๐Ÿ‡พ": { + "name": "flag Belarus", + "slug": "flag_belarus", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ง๐Ÿ‡ฟ": { + "name": "flag Belize", + "slug": "flag_belize", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡จ๐Ÿ‡ฆ": { + "name": "flag Canada", + "slug": "flag_canada", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡จ๐Ÿ‡จ": { + "name": "flag Cocos (Keeling) Islands", + "slug": "flag_cocos_islands", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡จ๐Ÿ‡ฉ": { + "name": "flag Congo - Kinshasa", + "slug": "flag_congo_kinshasa", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡จ๐Ÿ‡ซ": { + "name": "flag Central African Republic", + "slug": "flag_central_african_republic", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡จ๐Ÿ‡ฌ": { + "name": "flag Congo - Brazzaville", + "slug": "flag_congo_brazzaville", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡จ๐Ÿ‡ญ": { + "name": "flag Switzerland", + "slug": "flag_switzerland", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡จ๐Ÿ‡ฎ": { + "name": "flag Cรดte dโ€™Ivoire", + "slug": "flag_cote_d_ivoire", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡จ๐Ÿ‡ฐ": { + "name": "flag Cook Islands", + "slug": "flag_cook_islands", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡จ๐Ÿ‡ฑ": { + "name": "flag Chile", + "slug": "flag_chile", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡จ๐Ÿ‡ฒ": { + "name": "flag Cameroon", + "slug": "flag_cameroon", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡จ๐Ÿ‡ณ": { + "name": "flag China", + "slug": "flag_china", + "group": "Flags", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‡จ๐Ÿ‡ด": { + "name": "flag Colombia", + "slug": "flag_colombia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡จ๐Ÿ‡ต": { + "name": "flag Clipperton Island", + "slug": "flag_clipperton_island", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡จ๐Ÿ‡ท": { + "name": "flag Costa Rica", + "slug": "flag_costa_rica", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡จ๐Ÿ‡บ": { + "name": "flag Cuba", + "slug": "flag_cuba", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡จ๐Ÿ‡ป": { + "name": "flag Cape Verde", + "slug": "flag_cape_verde", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡จ๐Ÿ‡ผ": { + "name": "flag Curaรงao", + "slug": "flag_curacao", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡จ๐Ÿ‡ฝ": { + "name": "flag Christmas Island", + "slug": "flag_christmas_island", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡จ๐Ÿ‡พ": { + "name": "flag Cyprus", + "slug": "flag_cyprus", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡จ๐Ÿ‡ฟ": { + "name": "flag Czechia", + "slug": "flag_czechia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฉ๐Ÿ‡ช": { + "name": "flag Germany", + "slug": "flag_germany", + "group": "Flags", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‡ฉ๐Ÿ‡ฌ": { + "name": "flag Diego Garcia", + "slug": "flag_diego_garcia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฉ๐Ÿ‡ฏ": { + "name": "flag Djibouti", + "slug": "flag_djibouti", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฉ๐Ÿ‡ฐ": { + "name": "flag Denmark", + "slug": "flag_denmark", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฉ๐Ÿ‡ฒ": { + "name": "flag Dominica", + "slug": "flag_dominica", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฉ๐Ÿ‡ด": { + "name": "flag Dominican Republic", + "slug": "flag_dominican_republic", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฉ๐Ÿ‡ฟ": { + "name": "flag Algeria", + "slug": "flag_algeria", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ช๐Ÿ‡ฆ": { + "name": "flag Ceuta & Melilla", + "slug": "flag_ceuta_melilla", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ช๐Ÿ‡จ": { + "name": "flag Ecuador", + "slug": "flag_ecuador", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ช๐Ÿ‡ช": { + "name": "flag Estonia", + "slug": "flag_estonia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ช๐Ÿ‡ฌ": { + "name": "flag Egypt", + "slug": "flag_egypt", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ช๐Ÿ‡ญ": { + "name": "flag Western Sahara", + "slug": "flag_western_sahara", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ช๐Ÿ‡ท": { + "name": "flag Eritrea", + "slug": "flag_eritrea", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ช๐Ÿ‡ธ": { + "name": "flag Spain", + "slug": "flag_spain", + "group": "Flags", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‡ช๐Ÿ‡น": { + "name": "flag Ethiopia", + "slug": "flag_ethiopia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ช๐Ÿ‡บ": { + "name": "flag European Union", + "slug": "flag_european_union", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ซ๐Ÿ‡ฎ": { + "name": "flag Finland", + "slug": "flag_finland", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ซ๐Ÿ‡ฏ": { + "name": "flag Fiji", + "slug": "flag_fiji", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ซ๐Ÿ‡ฐ": { + "name": "flag Falkland Islands", + "slug": "flag_falkland_islands", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ซ๐Ÿ‡ฒ": { + "name": "flag Micronesia", + "slug": "flag_micronesia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ซ๐Ÿ‡ด": { + "name": "flag Faroe Islands", + "slug": "flag_faroe_islands", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ซ๐Ÿ‡ท": { + "name": "flag France", + "slug": "flag_france", + "group": "Flags", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‡ฌ๐Ÿ‡ฆ": { + "name": "flag Gabon", + "slug": "flag_gabon", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฌ๐Ÿ‡ง": { + "name": "flag United Kingdom", + "slug": "flag_united_kingdom", + "group": "Flags", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‡ฌ๐Ÿ‡ฉ": { + "name": "flag Grenada", + "slug": "flag_grenada", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฌ๐Ÿ‡ช": { + "name": "flag Georgia", + "slug": "flag_georgia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฌ๐Ÿ‡ซ": { + "name": "flag French Guiana", + "slug": "flag_french_guiana", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฌ๐Ÿ‡ฌ": { + "name": "flag Guernsey", + "slug": "flag_guernsey", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฌ๐Ÿ‡ญ": { + "name": "flag Ghana", + "slug": "flag_ghana", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฌ๐Ÿ‡ฎ": { + "name": "flag Gibraltar", + "slug": "flag_gibraltar", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฌ๐Ÿ‡ฑ": { + "name": "flag Greenland", + "slug": "flag_greenland", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฌ๐Ÿ‡ฒ": { + "name": "flag Gambia", + "slug": "flag_gambia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฌ๐Ÿ‡ณ": { + "name": "flag Guinea", + "slug": "flag_guinea", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฌ๐Ÿ‡ต": { + "name": "flag Guadeloupe", + "slug": "flag_guadeloupe", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฌ๐Ÿ‡ถ": { + "name": "flag Equatorial Guinea", + "slug": "flag_equatorial_guinea", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฌ๐Ÿ‡ท": { + "name": "flag Greece", + "slug": "flag_greece", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฌ๐Ÿ‡ธ": { + "name": "flag South Georgia & South Sandwich Islands", + "slug": "flag_south_georgia_south_sandwich_islands", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฌ๐Ÿ‡น": { + "name": "flag Guatemala", + "slug": "flag_guatemala", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฌ๐Ÿ‡บ": { + "name": "flag Guam", + "slug": "flag_guam", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฌ๐Ÿ‡ผ": { + "name": "flag Guinea-Bissau", + "slug": "flag_guinea_bissau", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฌ๐Ÿ‡พ": { + "name": "flag Guyana", + "slug": "flag_guyana", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ญ๐Ÿ‡ฐ": { + "name": "flag Hong Kong SAR China", + "slug": "flag_hong_kong_sar_china", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ญ๐Ÿ‡ฒ": { + "name": "flag Heard & McDonald Islands", + "slug": "flag_heard_mcdonald_islands", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ญ๐Ÿ‡ณ": { + "name": "flag Honduras", + "slug": "flag_honduras", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ญ๐Ÿ‡ท": { + "name": "flag Croatia", + "slug": "flag_croatia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ญ๐Ÿ‡น": { + "name": "flag Haiti", + "slug": "flag_haiti", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ญ๐Ÿ‡บ": { + "name": "flag Hungary", + "slug": "flag_hungary", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฎ๐Ÿ‡จ": { + "name": "flag Canary Islands", + "slug": "flag_canary_islands", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฎ๐Ÿ‡ฉ": { + "name": "flag Indonesia", + "slug": "flag_indonesia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฎ๐Ÿ‡ช": { + "name": "flag Ireland", + "slug": "flag_ireland", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฎ๐Ÿ‡ฑ": { + "name": "flag Israel", + "slug": "flag_israel", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฎ๐Ÿ‡ฒ": { + "name": "flag Isle of Man", + "slug": "flag_isle_of_man", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฎ๐Ÿ‡ณ": { + "name": "flag India", + "slug": "flag_india", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฎ๐Ÿ‡ด": { + "name": "flag British Indian Ocean Territory", + "slug": "flag_british_indian_ocean_territory", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฎ๐Ÿ‡ถ": { + "name": "flag Iraq", + "slug": "flag_iraq", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฎ๐Ÿ‡ท": { + "name": "flag Iran", + "slug": "flag_iran", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฎ๐Ÿ‡ธ": { + "name": "flag Iceland", + "slug": "flag_iceland", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฎ๐Ÿ‡น": { + "name": "flag Italy", + "slug": "flag_italy", + "group": "Flags", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‡ฏ๐Ÿ‡ช": { + "name": "flag Jersey", + "slug": "flag_jersey", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฏ๐Ÿ‡ฒ": { + "name": "flag Jamaica", + "slug": "flag_jamaica", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฏ๐Ÿ‡ด": { + "name": "flag Jordan", + "slug": "flag_jordan", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฏ๐Ÿ‡ต": { + "name": "flag Japan", + "slug": "flag_japan", + "group": "Flags", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‡ฐ๐Ÿ‡ช": { + "name": "flag Kenya", + "slug": "flag_kenya", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฐ๐Ÿ‡ฌ": { + "name": "flag Kyrgyzstan", + "slug": "flag_kyrgyzstan", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฐ๐Ÿ‡ญ": { + "name": "flag Cambodia", + "slug": "flag_cambodia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฐ๐Ÿ‡ฎ": { + "name": "flag Kiribati", + "slug": "flag_kiribati", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฐ๐Ÿ‡ฒ": { + "name": "flag Comoros", + "slug": "flag_comoros", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฐ๐Ÿ‡ณ": { + "name": "flag St. Kitts & Nevis", + "slug": "flag_st_kitts_nevis", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฐ๐Ÿ‡ต": { + "name": "flag North Korea", + "slug": "flag_north_korea", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฐ๐Ÿ‡ท": { + "name": "flag South Korea", + "slug": "flag_south_korea", + "group": "Flags", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‡ฐ๐Ÿ‡ผ": { + "name": "flag Kuwait", + "slug": "flag_kuwait", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฐ๐Ÿ‡พ": { + "name": "flag Cayman Islands", + "slug": "flag_cayman_islands", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฐ๐Ÿ‡ฟ": { + "name": "flag Kazakhstan", + "slug": "flag_kazakhstan", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฑ๐Ÿ‡ฆ": { + "name": "flag Laos", + "slug": "flag_laos", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฑ๐Ÿ‡ง": { + "name": "flag Lebanon", + "slug": "flag_lebanon", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฑ๐Ÿ‡จ": { + "name": "flag St. Lucia", + "slug": "flag_st_lucia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฑ๐Ÿ‡ฎ": { + "name": "flag Liechtenstein", + "slug": "flag_liechtenstein", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฑ๐Ÿ‡ฐ": { + "name": "flag Sri Lanka", + "slug": "flag_sri_lanka", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฑ๐Ÿ‡ท": { + "name": "flag Liberia", + "slug": "flag_liberia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฑ๐Ÿ‡ธ": { + "name": "flag Lesotho", + "slug": "flag_lesotho", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฑ๐Ÿ‡น": { + "name": "flag Lithuania", + "slug": "flag_lithuania", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฑ๐Ÿ‡บ": { + "name": "flag Luxembourg", + "slug": "flag_luxembourg", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฑ๐Ÿ‡ป": { + "name": "flag Latvia", + "slug": "flag_latvia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฑ๐Ÿ‡พ": { + "name": "flag Libya", + "slug": "flag_libya", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฒ๐Ÿ‡ฆ": { + "name": "flag Morocco", + "slug": "flag_morocco", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฒ๐Ÿ‡จ": { + "name": "flag Monaco", + "slug": "flag_monaco", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฒ๐Ÿ‡ฉ": { + "name": "flag Moldova", + "slug": "flag_moldova", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฒ๐Ÿ‡ช": { + "name": "flag Montenegro", + "slug": "flag_montenegro", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฒ๐Ÿ‡ซ": { + "name": "flag St. Martin", + "slug": "flag_st_martin", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฒ๐Ÿ‡ฌ": { + "name": "flag Madagascar", + "slug": "flag_madagascar", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฒ๐Ÿ‡ญ": { + "name": "flag Marshall Islands", + "slug": "flag_marshall_islands", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฒ๐Ÿ‡ฐ": { + "name": "flag North Macedonia", + "slug": "flag_north_macedonia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฒ๐Ÿ‡ฑ": { + "name": "flag Mali", + "slug": "flag_mali", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฒ๐Ÿ‡ฒ": { + "name": "flag Myanmar (Burma)", + "slug": "flag_myanmar", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฒ๐Ÿ‡ณ": { + "name": "flag Mongolia", + "slug": "flag_mongolia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฒ๐Ÿ‡ด": { + "name": "flag Macao SAR China", + "slug": "flag_macao_sar_china", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฒ๐Ÿ‡ต": { + "name": "flag Northern Mariana Islands", + "slug": "flag_northern_mariana_islands", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฒ๐Ÿ‡ถ": { + "name": "flag Martinique", + "slug": "flag_martinique", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฒ๐Ÿ‡ท": { + "name": "flag Mauritania", + "slug": "flag_mauritania", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฒ๐Ÿ‡ธ": { + "name": "flag Montserrat", + "slug": "flag_montserrat", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฒ๐Ÿ‡น": { + "name": "flag Malta", + "slug": "flag_malta", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฒ๐Ÿ‡บ": { + "name": "flag Mauritius", + "slug": "flag_mauritius", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฒ๐Ÿ‡ป": { + "name": "flag Maldives", + "slug": "flag_maldives", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฒ๐Ÿ‡ผ": { + "name": "flag Malawi", + "slug": "flag_malawi", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฒ๐Ÿ‡ฝ": { + "name": "flag Mexico", + "slug": "flag_mexico", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฒ๐Ÿ‡พ": { + "name": "flag Malaysia", + "slug": "flag_malaysia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฒ๐Ÿ‡ฟ": { + "name": "flag Mozambique", + "slug": "flag_mozambique", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ณ๐Ÿ‡ฆ": { + "name": "flag Namibia", + "slug": "flag_namibia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ณ๐Ÿ‡จ": { + "name": "flag New Caledonia", + "slug": "flag_new_caledonia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ณ๐Ÿ‡ช": { + "name": "flag Niger", + "slug": "flag_niger", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ณ๐Ÿ‡ซ": { + "name": "flag Norfolk Island", + "slug": "flag_norfolk_island", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ณ๐Ÿ‡ฌ": { + "name": "flag Nigeria", + "slug": "flag_nigeria", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ณ๐Ÿ‡ฎ": { + "name": "flag Nicaragua", + "slug": "flag_nicaragua", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ณ๐Ÿ‡ฑ": { + "name": "flag Netherlands", + "slug": "flag_netherlands", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ณ๐Ÿ‡ด": { + "name": "flag Norway", + "slug": "flag_norway", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ณ๐Ÿ‡ต": { + "name": "flag Nepal", + "slug": "flag_nepal", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ณ๐Ÿ‡ท": { + "name": "flag Nauru", + "slug": "flag_nauru", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ณ๐Ÿ‡บ": { + "name": "flag Niue", + "slug": "flag_niue", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ณ๐Ÿ‡ฟ": { + "name": "flag New Zealand", + "slug": "flag_new_zealand", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ด๐Ÿ‡ฒ": { + "name": "flag Oman", + "slug": "flag_oman", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ต๐Ÿ‡ฆ": { + "name": "flag Panama", + "slug": "flag_panama", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ต๐Ÿ‡ช": { + "name": "flag Peru", + "slug": "flag_peru", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ต๐Ÿ‡ซ": { + "name": "flag French Polynesia", + "slug": "flag_french_polynesia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ต๐Ÿ‡ฌ": { + "name": "flag Papua New Guinea", + "slug": "flag_papua_new_guinea", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ต๐Ÿ‡ญ": { + "name": "flag Philippines", + "slug": "flag_philippines", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ต๐Ÿ‡ฐ": { + "name": "flag Pakistan", + "slug": "flag_pakistan", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ต๐Ÿ‡ฑ": { + "name": "flag Poland", + "slug": "flag_poland", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ต๐Ÿ‡ฒ": { + "name": "flag St. Pierre & Miquelon", + "slug": "flag_st_pierre_miquelon", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ต๐Ÿ‡ณ": { + "name": "flag Pitcairn Islands", + "slug": "flag_pitcairn_islands", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ต๐Ÿ‡ท": { + "name": "flag Puerto Rico", + "slug": "flag_puerto_rico", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ต๐Ÿ‡ธ": { + "name": "flag Palestinian Territories", + "slug": "flag_palestinian_territories", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ต๐Ÿ‡น": { + "name": "flag Portugal", + "slug": "flag_portugal", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ต๐Ÿ‡ผ": { + "name": "flag Palau", + "slug": "flag_palau", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ต๐Ÿ‡พ": { + "name": "flag Paraguay", + "slug": "flag_paraguay", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ถ๐Ÿ‡ฆ": { + "name": "flag Qatar", + "slug": "flag_qatar", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ท๐Ÿ‡ช": { + "name": "flag Rรฉunion", + "slug": "flag_reunion", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ท๐Ÿ‡ด": { + "name": "flag Romania", + "slug": "flag_romania", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ท๐Ÿ‡ธ": { + "name": "flag Serbia", + "slug": "flag_serbia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ท๐Ÿ‡บ": { + "name": "flag Russia", + "slug": "flag_russia", + "group": "Flags", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‡ท๐Ÿ‡ผ": { + "name": "flag Rwanda", + "slug": "flag_rwanda", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ธ๐Ÿ‡ฆ": { + "name": "flag Saudi Arabia", + "slug": "flag_saudi_arabia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ธ๐Ÿ‡ง": { + "name": "flag Solomon Islands", + "slug": "flag_solomon_islands", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ธ๐Ÿ‡จ": { + "name": "flag Seychelles", + "slug": "flag_seychelles", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ธ๐Ÿ‡ฉ": { + "name": "flag Sudan", + "slug": "flag_sudan", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ธ๐Ÿ‡ช": { + "name": "flag Sweden", + "slug": "flag_sweden", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ธ๐Ÿ‡ฌ": { + "name": "flag Singapore", + "slug": "flag_singapore", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ธ๐Ÿ‡ญ": { + "name": "flag St. Helena", + "slug": "flag_st_helena", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ธ๐Ÿ‡ฎ": { + "name": "flag Slovenia", + "slug": "flag_slovenia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ธ๐Ÿ‡ฏ": { + "name": "flag Svalbard & Jan Mayen", + "slug": "flag_svalbard_jan_mayen", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ธ๐Ÿ‡ฐ": { + "name": "flag Slovakia", + "slug": "flag_slovakia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ธ๐Ÿ‡ฑ": { + "name": "flag Sierra Leone", + "slug": "flag_sierra_leone", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ธ๐Ÿ‡ฒ": { + "name": "flag San Marino", + "slug": "flag_san_marino", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ธ๐Ÿ‡ณ": { + "name": "flag Senegal", + "slug": "flag_senegal", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ธ๐Ÿ‡ด": { + "name": "flag Somalia", + "slug": "flag_somalia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ธ๐Ÿ‡ท": { + "name": "flag Suriname", + "slug": "flag_suriname", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ธ๐Ÿ‡ธ": { + "name": "flag South Sudan", + "slug": "flag_south_sudan", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ธ๐Ÿ‡น": { + "name": "flag Sรฃo Tomรฉ & Prรญncipe", + "slug": "flag_sao_tome_principe", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ธ๐Ÿ‡ป": { + "name": "flag El Salvador", + "slug": "flag_el_salvador", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ธ๐Ÿ‡ฝ": { + "name": "flag Sint Maarten", + "slug": "flag_sint_maarten", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ธ๐Ÿ‡พ": { + "name": "flag Syria", + "slug": "flag_syria", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ธ๐Ÿ‡ฟ": { + "name": "flag Eswatini", + "slug": "flag_eswatini", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡น๐Ÿ‡ฆ": { + "name": "flag Tristan da Cunha", + "slug": "flag_tristan_da_cunha", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡น๐Ÿ‡จ": { + "name": "flag Turks & Caicos Islands", + "slug": "flag_turks_caicos_islands", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡น๐Ÿ‡ฉ": { + "name": "flag Chad", + "slug": "flag_chad", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡น๐Ÿ‡ซ": { + "name": "flag French Southern Territories", + "slug": "flag_french_southern_territories", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡น๐Ÿ‡ฌ": { + "name": "flag Togo", + "slug": "flag_togo", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡น๐Ÿ‡ญ": { + "name": "flag Thailand", + "slug": "flag_thailand", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡น๐Ÿ‡ฏ": { + "name": "flag Tajikistan", + "slug": "flag_tajikistan", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡น๐Ÿ‡ฐ": { + "name": "flag Tokelau", + "slug": "flag_tokelau", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡น๐Ÿ‡ฑ": { + "name": "flag Timor-Leste", + "slug": "flag_timor_leste", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡น๐Ÿ‡ฒ": { + "name": "flag Turkmenistan", + "slug": "flag_turkmenistan", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡น๐Ÿ‡ณ": { + "name": "flag Tunisia", + "slug": "flag_tunisia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡น๐Ÿ‡ด": { + "name": "flag Tonga", + "slug": "flag_tonga", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡น๐Ÿ‡ท": { + "name": "flag Tรผrkiye", + "slug": "flag_turkiye", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡น๐Ÿ‡น": { + "name": "flag Trinidad & Tobago", + "slug": "flag_trinidad_tobago", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡น๐Ÿ‡ป": { + "name": "flag Tuvalu", + "slug": "flag_tuvalu", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡น๐Ÿ‡ผ": { + "name": "flag Taiwan", + "slug": "flag_taiwan", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡น๐Ÿ‡ฟ": { + "name": "flag Tanzania", + "slug": "flag_tanzania", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡บ๐Ÿ‡ฆ": { + "name": "flag Ukraine", + "slug": "flag_ukraine", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡บ๐Ÿ‡ฌ": { + "name": "flag Uganda", + "slug": "flag_uganda", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡บ๐Ÿ‡ฒ": { + "name": "flag U.S. Outlying Islands", + "slug": "flag_u_s_outlying_islands", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡บ๐Ÿ‡ณ": { + "name": "flag United Nations", + "slug": "flag_united_nations", + "group": "Flags", + "emoji_version": "4.0", + "unicode_version": "4.0", + "skin_tone_support": false + }, + "๐Ÿ‡บ๐Ÿ‡ธ": { + "name": "flag United States", + "slug": "flag_united_states", + "group": "Flags", + "emoji_version": "0.6", + "unicode_version": "0.6", + "skin_tone_support": false + }, + "๐Ÿ‡บ๐Ÿ‡พ": { + "name": "flag Uruguay", + "slug": "flag_uruguay", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡บ๐Ÿ‡ฟ": { + "name": "flag Uzbekistan", + "slug": "flag_uzbekistan", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ป๐Ÿ‡ฆ": { + "name": "flag Vatican City", + "slug": "flag_vatican_city", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ป๐Ÿ‡จ": { + "name": "flag St. Vincent & Grenadines", + "slug": "flag_st_vincent_grenadines", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ป๐Ÿ‡ช": { + "name": "flag Venezuela", + "slug": "flag_venezuela", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ป๐Ÿ‡ฌ": { + "name": "flag British Virgin Islands", + "slug": "flag_british_virgin_islands", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ป๐Ÿ‡ฎ": { + "name": "flag U.S. Virgin Islands", + "slug": "flag_u_s_virgin_islands", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ป๐Ÿ‡ณ": { + "name": "flag Vietnam", + "slug": "flag_vietnam", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ป๐Ÿ‡บ": { + "name": "flag Vanuatu", + "slug": "flag_vanuatu", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ผ๐Ÿ‡ซ": { + "name": "flag Wallis & Futuna", + "slug": "flag_wallis_futuna", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ผ๐Ÿ‡ธ": { + "name": "flag Samoa", + "slug": "flag_samoa", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฝ๐Ÿ‡ฐ": { + "name": "flag Kosovo", + "slug": "flag_kosovo", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡พ๐Ÿ‡ช": { + "name": "flag Yemen", + "slug": "flag_yemen", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡พ๐Ÿ‡น": { + "name": "flag Mayotte", + "slug": "flag_mayotte", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฟ๐Ÿ‡ฆ": { + "name": "flag South Africa", + "slug": "flag_south_africa", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฟ๐Ÿ‡ฒ": { + "name": "flag Zambia", + "slug": "flag_zambia", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿ‡ฟ๐Ÿ‡ผ": { + "name": "flag Zimbabwe", + "slug": "flag_zimbabwe", + "group": "Flags", + "emoji_version": "2.0", + "unicode_version": "2.0", + "skin_tone_support": false + }, + "๐Ÿด๓ ง๓ ข๓ ฅ๓ ฎ๓ ง๓ ฟ": { + "name": "flag England", + "slug": "flag_england", + "group": "Flags", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿด๓ ง๓ ข๓ ณ๓ ฃ๓ ด๓ ฟ": { + "name": "flag Scotland", + "slug": "flag_scotland", + "group": "Flags", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + }, + "๐Ÿด๓ ง๓ ข๓ ท๓ ฌ๓ ณ๓ ฟ": { + "name": "flag Wales", + "slug": "flag_wales", + "group": "Flags", + "emoji_version": "5.0", + "unicode_version": "5.0", + "skin_tone_support": false + } +} diff --git a/config/assets/launcher.json b/config/assets/launcher.json new file mode 100644 index 0000000..673251d --- /dev/null +++ b/config/assets/launcher.json @@ -0,0 +1,11 @@ +{ + "commands": { + ":wp": "launcher.open(\"wallpapers\")", + ":em": "launcher.open(\"emoji\")", + ":ch": "launcher.open(\"cliphist\")", + ":td": "launcher.open(\"todo\")", + ":bt": "launcher.open(\"bluetooth\")", + ":sh": "launcher.open(\"sh\")", + ":pw": "launcher.open(\"power\")" + } +} diff --git a/config/assets/settings.json b/config/assets/settings.json new file mode 100644 index 0000000..65e8d2b --- /dev/null +++ b/config/assets/settings.json @@ -0,0 +1,5 @@ +{ + "color-scheme": "dark", + "custom-color": "none", + "generation-scheme": "tonalSpot" +} diff --git a/config/config.py b/config/config.py new file mode 100644 index 0000000..0f2c399 --- /dev/null +++ b/config/config.py @@ -0,0 +1,422 @@ +import os +import json +import shutil +import gi +import subprocess +from concurrent.futures import ThreadPoolExecutor + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk + + +# Constants +SOURCE_STRING = """ +# Modus +source = ~/Modus/config/hypr/modus.conf +""" + +CONFIG_DIR = os.path.expanduser("~/Modus") +WALLPAPERS_DIR_DEFAULT = os.path.expanduser("~/Modus/assets/wallpaper") + +# Default key binding values +bind_vars = { + "prefix_restart": "SHIFT ALT", + "suffix_restart": "T", + "prefix_bluetooth": "ALT", + "suffix_bluetooth": "B", + "prefix_emoji": "SUPER", + "suffix_emoji": "E", + "prefix_cliphist": "SUPER", + "suffix_cliphist": "V", + "prefix_todo": "SUPER", + "suffix_todo": "T", + "prefix_sh": "SUPER", + "suffix_sh": "S", + "prefix_walls": "SUPER", + "suffix_walls": "W", + "prefix_screenshotregion": "SUPER", + "suffix_screenshotregion": "Z", + "prefix_screenshot": "", + "suffix_screenshot": "Print", + "prefix_screencast": "SUPER", + "suffix_screencast": "R", + "prefix_launcher": "SUPER", + "suffix_launcher": "D", + "prefix_power": "SUPER", + "suffix_power": "X", + "prefix_toggle": "SUPER", + "suffix_toggle": "H", + "prefix_randomwallpaper": "ALT SHIFT", + "suffix_randomwallpaper": "W", + "wallpapers_dir": WALLPAPERS_DIR_DEFAULT, +} + + +def deep_update(target: dict, update: dict) -> dict: + """ + Recursively update a nested dictionary with values from another dictionary. + """ + for key, value in update.items(): + if isinstance(value, dict): + target[key] = deep_update(target.get(key, {}), value) + else: + target[key] = value + return target + + +def parallel_subprocesses(commands): + """ + Run multiple subprocess commands in parallel. + """ + with ThreadPoolExecutor() as executor: + executor.map(subprocess.run, commands) + + +def setup_colors(): + subprocess.run( + [ + "python", + "-O", + os.path.expanduser("~/Modus/config/material-colors/generate.py"), + "--color", + "#0000FF", + ] + ) + + +def setup_sddm(): + commands = [ + ["sudo", "mkdir", "-p", "/etc/sddm.conf.d"], + [ + "sudo", + "cp", + os.path.expanduser("~/Modus/config/sddm/sddm.conf"), + "/etc/sddm.conf.d/", + ], + [ + "sudo", + "cp", + os.path.expanduser("~/Modus/config/sddm/sddm.conf"), + "/etc/", + ], + ["sudo", "chmod", "644", "/etc/sddm.conf.d/sddm.conf"], + ["sudo", "chmod", "644", "/etc/sddm.conf"], + ["sudo", "chmod", "-R", "755", "/usr/share/sddm/themes/corners/"], + ] + parallel_subprocesses(commands) + subprocess.run([os.path.expanduser("~/Modus/config/sddm/scripts/wallpaper.sh")]) + + +def copy_wallpapers(): + dest_wallpaper_dir = os.path.expanduser("~/Pictures/wallpaper") + src_wallpaper_dir = os.path.expanduser("~/Modus/assets/wallpaper") + if not os.path.exists(dest_wallpaper_dir): + shutil.copytree(src_wallpaper_dir, dest_wallpaper_dir, dirs_exist_ok=True) + subprocess.run( + [ + "python", + os.path.expanduser("~/Modus/config/wallpaper.py"), + "-I", + os.path.join(dest_wallpaper_dir, "example-1.jpg"), + ] + ) + + +def load_bind_vars(): + """ + Load saved key binding variables from JSON, if available. + """ + config_json = os.path.expanduser("~/Modus/config/assets/config.json") + try: + with open(config_json, "r") as f: + saved_vars = json.load(f) + bind_vars.update(saved_vars) + except FileNotFoundError: + # Use default values if no saved config exists + pass + + +def generate_hyprconf() -> str: + """ + Generate the Hypr configuration string using the current bind_vars. + """ + home = os.path.expanduser("~") + return f"""exec-once = python {home}/Modus/main.py +exec = pgrep -x "hypridle" > /dev/null || hypridle +exec = swww-daemon +exec-once = wl-paste --type text --watch cliphist store +exec-once = wl-paste --type image --watch cliphist store + +$fabricSend = fabric-cli exec modus +$scriptsDir = $HOME/Modus/config + +bind = {bind_vars["prefix_restart"]}, {bind_vars["suffix_restart"]}, exec, killall modus; python {home}/Modus/main.py # Reload Modus | Default: SUPER ALT + T +bind = {bind_vars["prefix_bluetooth"]}, {bind_vars["suffix_bluetooth"]}, exec, $fabricSend 'launcher.open("bluetooth")' # Bluetooth | Default: ALT + B +bind = {bind_vars["prefix_emoji"]}, {bind_vars["suffix_emoji"]}, exec, $fabricSend 'launcher.open("emoji")' # Emoji | Default: SUPER + E +bind = {bind_vars["prefix_cliphist"]}, {bind_vars["suffix_cliphist"]}, exec, $fabricSend 'launcher.open("cliphist")' # Cliphist | Default: SUPER + V +bind = {bind_vars["prefix_todo"]}, {bind_vars["suffix_todo"]}, exec, $fabricSend 'launcher.open("todo")' # Todo | Default: SUPER + T +bind = {bind_vars["prefix_sh"]}, {bind_vars["suffix_sh"]}, exec, $fabricSend 'launcher.open("sh")' # Sh | Default: SUPER + S +bind = {bind_vars["prefix_walls"]}, {bind_vars["suffix_walls"]}, exec, $fabricSend 'launcher.open("wallpapers")' # Wallpaper Selector | Default: SUPER + W +bind ={bind_vars["prefix_randomwallpaper"]}, {bind_vars["suffix_randomwallpaper"]}, exec, python -O $scriptsDir/wallpaper.py -R # Random Wallpaper | Default: ALT SHIFT + W +bind = {bind_vars["prefix_launcher"]}, {bind_vars["suffix_launcher"]}, exec, $fabricSend 'launcher.open("launcher")' # App Launcher | Default: SUPER + D +bind = {bind_vars["prefix_power"]}, {bind_vars["suffix_power"]}, exec, $fabricSend 'launcher.open("power")' # Power Menu | Default: SUPER + X +bind = {bind_vars["prefix_toggle"]}, {bind_vars["suffix_toggle"]}, exec, $fabricSend 'bar.toggle_hidden()' # Toggle Bar | Default: SUPER + H +bind = {bind_vars["prefix_screenshotregion"]}, {bind_vars["suffix_screenshotregion"]}, exec, $fabricSend 'sc.screenshot()' # Screenshot Region | Default: SUPER + Z +bind = {bind_vars["prefix_screenshot"]}, {bind_vars["suffix_screenshot"]}, exec, $fabricSend 'sc.screenshot(True)' # ScreenshotFullScreen | Default: Print +bind = {bind_vars["prefix_screencast"]}, {bind_vars["suffix_screencast"]}, exec, $fabricSend 'sc.screencast()' # Screencast | Default: SUPER + R + + +# Wallpapers directory: {bind_vars["wallpapers_dir"]} + +general {{ + gaps_in = 2 + gaps_out = 4 + border_size = 0 + layout = dwindle +}} + +decoration {{ + blur {{ + enabled = false + }} + rounding = 15 + active_opacity = 1 + inactive_opacity = 1 + fullscreen_opacity = 1 + + dim_inactive = true + dim_strength = 0.1 + dim_special = 0.8 + shadow {{ + enabled = false + }} +}} + +animations {{ + enabled = true + # Animation curves + bezier = linear, 0, 0, 1, 1 + bezier = md3_standard, 0.2, 0, 0, 1 + bezier = md3_decel, 0.05, 0.7, 0.1, 1 + bezier = md3_accel, 0.3, 0, 0.8, 0.15 + bezier = overshot, 0.05, 0.9, 0.1, 1.1 + bezier = crazyshot, 0.1, 1.5, 0.76, 0.92 + bezier = hyprnostretch, 0.05, 0.9, 0.1, 1.0 + bezier = menu_decel, 0.1, 1, 0, 1 + bezier = menu_accel, 0.38, 0.04, 1, 0.07 + bezier = easeInOutCirc, 0.85, 0, 0.15, 1 + bezier = easeOutCirc, 0, 0.55, 0.45, 1 + bezier = easeOutExpo, 0.16, 1, 0.3, 1 + bezier = softAcDecel, 0.26, 0.26, 0.15, 1 + bezier = md2, 0.4, 0, 0.2, 1 # use with .2s duration + # Animation configs + animation = windows, 1, 3, md3_decel, popin 60% + animation = windowsIn, 1, 3, md3_decel, popin 60% + animation = windowsOut, 1, 3, md3_accel, popin 60% + animation = border, 1, 10, default + animation = fade, 1, 3, md3_decel + animation = layers, 1, 2, md3_decel, slide + animation = layersIn, 1, 3, menu_decel, slide + animation = layersOut, 1, 1.6, menu_accel, slide + animation = fadeLayersIn, 1, 2, menu_decel + animation = fadeLayersOut, 1, 4.5, menu_accel + animation = workspaces, 1, 7, menu_decel, slide + # animation = workspaces, 1, 2.5, softAcDecel, slide + # animation = workspaces, 1, 7, menu_decel, slidefade 15% + # animation = specialWorkspace, 1, 3, md3_decel, slidefadevert 15% + animation = specialWorkspace, 1, 3, md3_decel, slidevert +}} +""" + + +def backup_and_replace(src: str, dest: str, config_name: str): + """ + Backup the existing configuration file and replace it with a new one. + """ + if os.path.exists(dest): + backup_path = dest + ".bak" + if not os.path.exists(backup_path): + shutil.copy(dest, backup_path) + print(f"{config_name} config backed up to {backup_path}") + shutil.copy(src, dest) + print(f"{config_name} config replaced from {src}") + + +class HyprConfGUI(Gtk.Window): + def __init__(self, show_lock_checkbox: bool, show_idle_checkbox: bool): + super().__init__(title="Configure Key Binds") + self.set_border_width(20) + self.set_default_size(500, 450) + self.set_resizable(False) + + self.selected_face_icon = None + + # Main container + main_vbox = Gtk.VBox(spacing=10) + self.add(main_vbox) + + # Create input entries for key bindings + self.entries = [] + bindings = [ + ("Reload Modus", "prefix_restart", "suffix_restart"), + ("Bluetooth", "prefix_bluetooth", "suffix_bluetooth"), + ("Wallpaper Selector", "prefix_walls", "suffix_walls"), + ("Random Wallpaper", "prefix_randomwallpaper", "suffix_randomwallpaper"), + ("App Launcher", "prefix_launcher", "suffix_launcher"), + ("Power Menu", "prefix_power", "suffix_power"), + ("Emoji Selector", "prefix_emoji", "suffix_emoji"), + ("Clipboard History", "prefix_cliphist", "suffix_cliphist"), + ("Screenshot", "prefix_screenshot", "suffix_screenshot"), + ("ScreenRecorder", "prefix_screencast", "suffix_screencast"), + ("Screenshot Region", "prefix_screenshotregion", "suffix_screenshotregion"), + ("Sh runner", "prefix_sh", "suffix_sh"), + ("Todo List", "prefix_todo", "suffix_todo"), + ("Toggle Bar", "prefix_toggle", "suffix_toggle"), + ] + + for label_text, prefix_key, suffix_key in bindings: + entry_box = Gtk.HBox(spacing=10) + + label = Gtk.Label(label=label_text) + entry_box.pack_start(label, False, False, 0) + + prefix_entry = Gtk.Entry() + prefix_entry.set_text(bind_vars[prefix_key]) + entry_box.pack_start(prefix_entry, True, True, 0) + + plus_label = Gtk.Label(label=" + ") + entry_box.pack_start(plus_label, False, False, 0) + + suffix_entry = Gtk.Entry() + suffix_entry.set_text(bind_vars[suffix_key]) + entry_box.pack_start(suffix_entry, True, True, 0) + + main_vbox.pack_start(entry_box, False, False, 0) + self.entries.append((prefix_key, suffix_key, prefix_entry, suffix_entry)) + + # Wallpaper directory chooser + wall_box = Gtk.HBox(spacing=10) + wall_label = Gtk.Label(label="Wallpapers Directory") + wall_box.pack_start(wall_label, False, False, 0) + self.wall_dir_chooser = Gtk.FileChooserButton( + title="Select a folder", action=Gtk.FileChooserAction.SELECT_FOLDER + ) + self.wall_dir_chooser.set_filename(bind_vars["wallpapers_dir"]) + wall_box.pack_start(self.wall_dir_chooser, True, True, 0) + main_vbox.pack_start(wall_box, False, False, 0) + + # Optional checkboxes for replacing configs + if show_lock_checkbox: + self.lock_checkbox = Gtk.CheckButton(label="Replace Hyprlock config") + self.lock_checkbox.set_active(False) + main_vbox.pack_start(self.lock_checkbox, False, False, 0) + if show_idle_checkbox: + self.idle_checkbox = Gtk.CheckButton(label="Replace Hypridle config") + self.idle_checkbox.set_active(False) + main_vbox.pack_start(self.idle_checkbox, False, False, 0) + + # Accept and Cancel buttons + button_box = Gtk.HBox(spacing=10) + cancel_btn = Gtk.Button(label="Cancel") + cancel_btn.connect("clicked", self.on_cancel) + accept_btn = Gtk.Button(label="Accept") + accept_btn.connect("clicked", self.on_accept) + button_box.pack_end(accept_btn, False, False, 0) + button_box.pack_end(cancel_btn, False, False, 0) + main_vbox.pack_start(button_box, False, False, 0) + + def on_accept(self, widget): + """ + Save the configuration and update the necessary files. + """ + # Update bind_vars from user inputs + for prefix_key, suffix_key, prefix_entry, suffix_entry in self.entries: + bind_vars[prefix_key] = prefix_entry.get_text() + bind_vars[suffix_key] = suffix_entry.get_text() + + # Update wallpaper directory + bind_vars["wallpapers_dir"] = self.wall_dir_chooser.get_filename() + + # Save the updated bind_vars to a JSON file + config_json = os.path.expanduser("~/Modus/config/assets/config.json") + os.makedirs(os.path.dirname(config_json), exist_ok=True) + with open(config_json, "w") as f: + json.dump(bind_vars, f) + + # Replace hyprlock config if requested + if hasattr(self, "lock_checkbox") and self.lock_checkbox.get_active(): + src_lock = os.path.expanduser("~/Modus/config/hypr/hyprlock.conf") + dest_lock = os.path.expanduser("~/.config/hypr/hyprlock.conf") + backup_and_replace(src_lock, dest_lock, "Hyprlock") + + # Replace hypridle config if requested + if hasattr(self, "idle_checkbox") and self.idle_checkbox.get_active(): + src_idle = os.path.expanduser("~/Modus/config/hypr/hypridle.conf") + dest_idle = os.path.expanduser("~/.config/hypr/hypridle.conf") + backup_and_replace(src_idle, dest_idle, "Hypridle") + + # Append the source string to the Hyprland config if not present + hyprland_config_path = os.path.expanduser("~/.config/hypr/hyprland.conf") + with open(hyprland_config_path, "r") as f: + content = f.read() + if SOURCE_STRING not in content: + with open(hyprland_config_path, "a") as f: + f.write(SOURCE_STRING) + + start_config() + self.destroy() + + def on_cancel(self, widget): + self.destroy() + + +def start_config(): + """ + Run final configuration steps: ensure necessary configs, write the hyprconf, and reload. + """ + copy_wallpapers() + setup_colors() + + # Write the generated hypr configuration to file + hypr_config_dir = os.path.expanduser("~/Modus/config/hypr/") + os.makedirs(hypr_config_dir, exist_ok=True) + hypr_conf_path = os.path.join(hypr_config_dir, "modus.conf") + with open(hypr_conf_path, "w") as f: + f.write(generate_hyprconf()) + + # Reload Hyprland configuration + os.system("hyprctl reload") + + +def open_config(): + """ + Entry point for opening the configuration GUI. + """ + load_bind_vars() + + # Check and copy hyprlock config if needed + dest_lock = os.path.expanduser("~/.config/hypr/hyprlock.conf") + src_lock = os.path.expanduser("~/Modus/config/hypr/hyprlock.conf") + os.makedirs(os.path.dirname(dest_lock), exist_ok=True) + show_lock_checkbox = True + if not os.path.exists(dest_lock): + shutil.copy(src_lock, dest_lock) + show_lock_checkbox = False + + # Check and copy hypridle config if needed + dest_idle = os.path.expanduser("~/.config/hypr/hypridle.conf") + src_idle = os.path.expanduser("~/Modus/config/hypr/hypridle.conf") + show_idle_checkbox = True + if not os.path.exists(dest_idle): + shutil.copy(src_idle, dest_idle) + show_idle_checkbox = False + + # Create and run the GUI + window = HyprConfGUI(show_lock_checkbox, show_idle_checkbox) + window.connect("destroy", Gtk.main_quit) + window.show_all() + Gtk.main() + + +if __name__ == "__main__": + open_config() diff --git a/config/dark-theme.sh b/config/dark-theme.sh new file mode 100755 index 0000000..fcb5461 --- /dev/null +++ b/config/dark-theme.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +# Function to get the relative path based on the current script's location +get_relative_path() { + local path=$1 + local level=$2 + local script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + + for ((i = 1; i <= level; i++)); do + script_dir="$(dirname "$script_dir")" + done + + echo "$script_dir/$path" +} + +# Assign relative paths to variables +GENERATOR=$(get_relative_path "./material-colors/generate.py") +SETTINGS_FILE=$(get_relative_path "./assets/settings.json") + +# Function to read a value from the JSON file +get_json_value() { + local key=$1 + jq -r --arg key "$key" '.[$key]' $SETTINGS_FILE +} + +# Function to update a value in the JSON file +update_json_value() { + local key=$1 + local value=$2 + jq --arg key "$key" --arg value "$value" \ + '.[$key] = $value' $SETTINGS_FILE >"$SETTINGS_FILE.tmp" && mv "$SETTINGS_FILE.tmp" $SETTINGS_FILE +} + +toggle() { + local color_scheme=$(get_json_value "color-scheme") + local generation_scheme=$(get_json_value "generation-scheme") + + if [ "$color_scheme" == "dark" ]; then + python -O $GENERATOR -R --color-scheme light --scheme $generation_scheme + update_json_value "color-scheme" "light" + elif [ "$color_scheme" == "light" ]; then + python -O $GENERATOR -R --color-scheme dark --scheme $generation_scheme + update_json_value "color-scheme" "dark" + else + echo "Unknown color scheme: $color_scheme" + fi +} + +set() { + local new_scheme=$1 + local generation_scheme=$(get_json_value "generation-scheme") + python -O $GENERATOR -R --color-scheme $new_scheme --scheme $generation_scheme + update_json_value "color-scheme" $new_scheme +} + +# Process arguments +if [[ "$1" == "--toggle" ]]; then + toggle +elif [[ "$1" == "--set" ]]; then + set $2 +else + echo "Usage: $0 --toggle | --set " +fi diff --git a/config/hypr/hypridle.conf b/config/hypr/hypridle.conf new file mode 100644 index 0000000..48bd7d7 --- /dev/null +++ b/config/hypr/hypridle.conf @@ -0,0 +1,41 @@ + +listener { + timeout = 500 # in seconds. + on-timeout = notify-send "You are idle!" # command to run when timeout has passed. + on-resume = notify-send "Welcome back!" # command to run when activity is detected after timeout has fired. +} + +general { + lock_cmd = pidof hyprlock || hyprlock # avoid starting multiple hyprlock instances. + before_sleep_cmd = loginctl lock-session # lock before suspend. + after_sleep_cmd = hyprctl dispatch dpms on # to avoid having to press a key twice to turn on the display. +} + +listener { + timeout = 150 # 2.5min. + on-timeout = brightnessctl -s set 10 # set monitor backlight to minimum, avoid 0 on OLED monitor. + on-resume = brightnessctl -r # monitor backlight restore. +} + +# turn off keyboard backlight, comment out this section if you dont have a keyboard backlight. +listener { + timeout = 150 # 2.5min. + on-timeout = brightnessctl -sd rgb:kbd_backlight set 0 # turn off keyboard backlight. + on-resume = brightnessctl -rd rgb:kbd_backlight # turn on keyboard backlight. +} + +listener { + timeout = 300 # 5min + on-timeout = loginctl lock-session # lock screen when timeout has passed +} + +listener { + timeout = 330 # 5.5min + on-timeout = hyprctl dispatch dpms off # screen off when timeout has passed + on-resume = hyprctl dispatch dpms on # screen on when activity is detected after timeout has fired. +} + +listener { + timeout = 1800 # 30min + on-timeout = systemctl suspend # suspend pc +} diff --git a/config/hypr/hyprlock.conf b/config/hypr/hyprlock.conf new file mode 100644 index 0000000..48abea1 --- /dev/null +++ b/config/hypr/hyprlock.conf @@ -0,0 +1 @@ +source = ~/.cache/material/hyprlock.conf diff --git a/config/hypr/modus.conf b/config/hypr/modus.conf new file mode 100644 index 0000000..cd7dc0d --- /dev/null +++ b/config/hypr/modus.conf @@ -0,0 +1,85 @@ +exec-once = python /home/sankalp/Modus/main.py +exec = pgrep -x "hypridle" > /dev/null || hypridle +exec = swww-daemon +exec-once = wl-paste --type text --watch cliphist store +exec-once = wl-paste --type image --watch cliphist store + +$fabricSend = fabric-cli exec modus +$scriptsDir = $HOME/Modus/config + +bind = SHIFT ALT, T, exec, killall modus; python /home/sankalp/Modus/main.py # Reload Modus | Default: SUPER ALT + T +bind = ALT, B, exec, $fabricSend 'launcher.open("bluetooth")' # Bluetooth | Default: ALT + B +bind = SUPER, E, exec, $fabricSend 'launcher.open("emoji")' # Emoji | Default: SUPER + E +bind = SUPER, V, exec, $fabricSend 'launcher.open("cliphist")' # Cliphist | Default: SUPER + V +bind = SUPER, T, exec, $fabricSend 'launcher.open("todo")' # Todo | Default: SUPER + T +bind = SUPER, S, exec, $fabricSend 'launcher.open("sh")' # Sh | Default: SUPER + S +bind = SUPER, W, exec, $fabricSend 'launcher.open("wallpapers")' # Wallpaper Selector | Default: SUPER + W +bind =ALT SHIFT, W, exec, python -O $scriptsDir/wallpaper.py -R # Random Wallpaper | Default: ALT SHIFT + W +bind = SUPER, D, exec, $fabricSend 'launcher.open("launcher")' # App Launcher | Default: SUPER + D +bind = SUPER, X, exec, $fabricSend 'launcher.open("power")' # Power Menu | Default: SUPER + X +bind = SUPER, H, exec, $fabricSend 'bar.toggle_hidden()' # Toggle Bar | Default: SUPER + H +bind = SUPER, Z, exec, $fabricSend 'sc.screenshot()' # Screenshot Region | Default: SUPER + Z +bind = , Print, exec, $fabricSend 'sc.screenshot(True)' # ScreenshotFullScreen | Default: Print +bind = SUPER, R, exec, $fabricSend 'sc.screencast()' # Screencast | Default: SUPER + R + + +# Wallpapers directory: /home/sankalp/Pictures/wallpapers + +general { + gaps_in = 2 + gaps_out = 4 + border_size = 0 + layout = dwindle +} + +decoration { + blur { + enabled = false + } + rounding = 15 + active_opacity = 1 + inactive_opacity = 1 + fullscreen_opacity = 1 + + dim_inactive = true + dim_strength = 0.1 + dim_special = 0.8 + shadow { + enabled = false + } +} + +animations { + enabled = true + # Animation curves + bezier = linear, 0, 0, 1, 1 + bezier = md3_standard, 0.2, 0, 0, 1 + bezier = md3_decel, 0.05, 0.7, 0.1, 1 + bezier = md3_accel, 0.3, 0, 0.8, 0.15 + bezier = overshot, 0.05, 0.9, 0.1, 1.1 + bezier = crazyshot, 0.1, 1.5, 0.76, 0.92 + bezier = hyprnostretch, 0.05, 0.9, 0.1, 1.0 + bezier = menu_decel, 0.1, 1, 0, 1 + bezier = menu_accel, 0.38, 0.04, 1, 0.07 + bezier = easeInOutCirc, 0.85, 0, 0.15, 1 + bezier = easeOutCirc, 0, 0.55, 0.45, 1 + bezier = easeOutExpo, 0.16, 1, 0.3, 1 + bezier = softAcDecel, 0.26, 0.26, 0.15, 1 + bezier = md2, 0.4, 0, 0.2, 1 # use with .2s duration + # Animation configs + animation = windows, 1, 3, md3_decel, popin 60% + animation = windowsIn, 1, 3, md3_decel, popin 60% + animation = windowsOut, 1, 3, md3_accel, popin 60% + animation = border, 1, 10, default + animation = fade, 1, 3, md3_decel + animation = layers, 1, 2, md3_decel, slide + animation = layersIn, 1, 3, menu_decel, slide + animation = layersOut, 1, 1.6, menu_accel, slide + animation = fadeLayersIn, 1, 2, menu_decel + animation = fadeLayersOut, 1, 4.5, menu_accel + animation = workspaces, 1, 7, menu_decel, slide + # animation = workspaces, 1, 2.5, softAcDecel, slide + # animation = workspaces, 1, 7, menu_decel, slidefade 15% + # animation = specialWorkspace, 1, 3, md3_decel, slidefadevert 15% + animation = specialWorkspace, 1, 3, md3_decel, slidevert +} diff --git a/config/material-colors/__pycache__/generate.cpython-312.opt-1.pyc b/config/material-colors/__pycache__/generate.cpython-312.opt-1.pyc new file mode 100644 index 0000000..cf26bc0 Binary files /dev/null and b/config/material-colors/__pycache__/generate.cpython-312.opt-1.pyc differ diff --git a/config/material-colors/__pycache__/generate.cpython-312.pyc b/config/material-colors/__pycache__/generate.cpython-312.pyc new file mode 100644 index 0000000..cf26bc0 Binary files /dev/null and b/config/material-colors/__pycache__/generate.cpython-312.pyc differ diff --git a/config/material-colors/__pycache__/generate.cpython-313.opt-1.pyc b/config/material-colors/__pycache__/generate.cpython-313.opt-1.pyc new file mode 100644 index 0000000..c3560d2 Binary files /dev/null and b/config/material-colors/__pycache__/generate.cpython-313.opt-1.pyc differ diff --git a/config/material-colors/__pycache__/generate.cpython-313.pyc b/config/material-colors/__pycache__/generate.cpython-313.pyc new file mode 100644 index 0000000..1a6ea6a Binary files /dev/null and b/config/material-colors/__pycache__/generate.cpython-313.pyc differ diff --git a/config/material-colors/generate.py b/config/material-colors/generate.py new file mode 100644 index 0000000..5bd382b --- /dev/null +++ b/config/material-colors/generate.py @@ -0,0 +1,487 @@ +import argparse +import hashlib +import json +import os +import pickle +import random +import re +import subprocess + +import numpy as np +from materialyoucolor.dynamiccolor.material_dynamic_colors import ( + MaterialDynamicColors, +) # type: ignore # noqa +from materialyoucolor.hct import Hct # type: ignore +from materialyoucolor.quantize import QuantizeCelebi # type: ignore +from materialyoucolor.scheme.dynamic_scheme import DynamicScheme # type: ignore # noqa +from materialyoucolor.scheme.scheme_content import SchemeContent # type: ignore # noqa +from materialyoucolor.scheme.scheme_expressive import ( + SchemeExpressive, +) # type: ignore # noqa +from materialyoucolor.scheme.scheme_fidelity import ( + SchemeFidelity, +) # type: ignore # noqa +from materialyoucolor.scheme.scheme_fruit_salad import ( + SchemeFruitSalad, +) # type: ignore # noqa +from materialyoucolor.scheme.scheme_monochrome import ( + SchemeMonochrome, +) # type: ignore # noqa +from materialyoucolor.scheme.scheme_neutral import SchemeNeutral # type: ignore # noqa +from materialyoucolor.scheme.scheme_rainbow import SchemeRainbow # type: ignore # noqa +from materialyoucolor.scheme.scheme_tonal_spot import ( + SchemeTonalSpot, +) # type: ignore # noqa +from materialyoucolor.scheme.scheme_vibrant import SchemeVibrant # type: ignore # noqa +from materialyoucolor.score.score import Score # type: ignore +from PIL import Image + +schemes = { + "tonalSpot": SchemeTonalSpot, + "expressive": SchemeExpressive, + "fruitSalad": SchemeFruitSalad, + "monochrome": SchemeMonochrome, + "rainbow": SchemeRainbow, + "vibrant": SchemeVibrant, + "neutral": SchemeNeutral, + "fidelity": SchemeFidelity, + "content": SchemeContent, +} + + +def join(*args): + return os.path.join(*args) + + +home = os.path.expanduser("~") + +script_dir = os.path.dirname(os.path.abspath(__file__)) +cache_path = f"{home}/.cache/material/" +os.makedirs(cache_path, exist_ok=True) + + +def rgb_to_hex(rgb): + return "#{:02x}{:02x}{:02x}".format(*rgb[:3]) + + +def rgba_to_rgb(rgba): + return f"{rgba[0]}, {rgba[1]}, {rgba[2]}" + + +class Color: + def __init__(self, scheme) -> None: + self.scheme = scheme + + def __call__(self, name: str): + color_name = getattr(MaterialDynamicColors, name) + if hasattr(color_name, "get_hct"): + return rgb_to_hex(color_name.get_hct(self.scheme).to_rgba()) + else: + return "#000000" + + +class ColorsCache: + def __init__( + self, + colors: DynamicScheme | dict, + wallpaper: str, + original_color: int, + contrast_level: int, + scheme_name: str, + ) -> None: + self.colors: dict[str, str] = {} + self.wallpaper = wallpaper + self.original_color = original_color + self.contrast_level = contrast_level + self.scheme_name = scheme_name + if isinstance(colors, DynamicScheme): + for color in vars(MaterialDynamicColors).keys(): + color_name = getattr(MaterialDynamicColors, color) + if hasattr(color_name, "get_hct"): + self.colors[str(color)] = rgb_to_hex( + color_name.get_hct(colors).to_rgba() + ) + else: + self.colors = colors + + +def colors_dict(cache: ColorsCache): + dict = { + "wallpaper": cache.wallpaper, + "colors": cache.colors, + "original_color": cache.original_color, + "contrast_level": cache.contrast_level, + "scheme_name": cache.scheme_name, + } + return dict + + +def get_cache_object(object: dict | str): + if isinstance(object, str): + object = dict(json.loads(object)) + colors = object["colors"] + wallpaper = object["wallpaper"] + original_color = object["original_color"] + contrast_level = object.get("contrast_level", 0) + scheme_name = object.get("scheme_name", "tonalSpot") + return ColorsCache(colors, wallpaper, original_color, contrast_level, scheme_name) + + +def get_file_list(folder_path): + file_list = [] + for file in os.listdir(folder_path): + if os.path.isfile(join(folder_path, file)): + file_list.append(join(folder_path, file)) + return file_list + + +ready_templates = { + "colors.css": "@define-color {name} {hex};\n", + "colors.scss": "${name}: {hex};\n", +} +additional = {"onBackground": "foreground"} + + +def generate_color_map(scheme: DynamicScheme): + color_map: dict[str, str] = {} + for color in vars(MaterialDynamicColors).keys(): + color_name = getattr(MaterialDynamicColors, color) + if hasattr(color_name, "get_hct"): + rgba = color_name.get_hct(scheme).to_rgba() + color_map[color.strip()] = rgb_to_hex(rgba) + return color_map + + +class ColorFormatter: + def __init__(self, scheme: DynamicScheme): + self.color_map = generate_color_map(scheme) + + def apply_transformations(self, value, transformations): + intermediate_transforms = [ + t for t in transformations if not (t.startswith("strip") or t == "rgb") + ] + final_transforms = [ + t for t in transformations if t.startswith("strip") or t == "rgb" + ] + + for transform in intermediate_transforms: + if transform.startswith("lighten"): + percent = int(re.search(r"\d+", transform).group()) + value = self.adjust_brightness(value, percent) + elif transform.startswith("darken"): + percent = int(re.search(r"\d+", transform).group()) + value = self.adjust_brightness(value, -percent) + + for transform in final_transforms: + if transform.startswith("strip"): + value = value.lstrip("#") + elif transform == "rgb": + value = self.hex_to_rgb(value) + + return value + + def adjust_brightness(self, hex_color, factor): + def min_max(v): + return min(255, max(0, v)) + + hex_color = hex_color.lstrip("#") + r, g, b = (int(hex_color[i : i + 2], 16) for i in (0, 2, 4)) + + r = min_max(int(r * (1 + factor / 100))) + g = min_max(int(g * (1 + factor / 100))) + b = min_max(int(b * (1 + factor / 100))) + + return f"#{r:02X}{g:02X}{b:02X}".lower() + + def hex_to_rgb(self, hex_color): + hex_color = hex_color.lstrip("#") + r, g, b = (int(hex_color[i : i + 2], 16) for i in (0, 2, 4)) + return f"{r},{g},{b}" + + def parse_transformations(self, transformations_str): + transformations = re.findall(r"(\w+)(?:\((\d*)\))?", transformations_str) + result = [] + for command, arg in transformations: + if command: + if arg: + result.append(f"{command}({arg})") + else: + result.append(command) + return result + + def format(self, text): + pattern = r"(<(\w+)(?:\.(.+))?>)" + matches = re.findall(pattern, text) + result = [] + last_end = 0 + + for match in matches: + full_match, key, transformations_str = match + if key in self.color_map: + result.append(text[last_end : text.find(full_match, last_end)]) + value = self.color_map[key] + if transformations_str: + transformations = self.parse_transformations(transformations_str) + value = self.apply_transformations(value, transformations) + result.append(value) + last_end = text.find(full_match, last_end) + len(full_match) + + result.append(text[last_end:]) + result = "".join(result) + pattern = r"<\\\\([^>]+)>" + + result = re.sub(pattern, r"<\1>", result) + + return result + + +def generate_templates( + folder: str, + output_folder: str, + scheme: DynamicScheme, + color_scheme: str, + wallpaper: str = "", +): + if not os.path.exists(output_folder): + os.makedirs(output_folder) + if not os.path.exists(folder): + os.makedirs(folder) + file_list = get_file_list(folder) + + for file_path in file_list: + with open(file_path) as f: + template = f.read() + formatter = ColorFormatter(scheme) + template = formatter.format(template) + template = ( + template.replace("", color_scheme) + .replace("", output_folder) + .replace("", wallpaper) + ) + new_path = join(output_folder, os.path.basename(file_path)) + with open(new_path, "w") as f: + f.write(template) + + for file in ready_templates: + _template = "" + for color in vars(MaterialDynamicColors).keys(): + color_name = getattr(MaterialDynamicColors, color) + if hasattr(color_name, "get_hct"): + rgba = color_name.get_hct(scheme).to_rgba() + hex_color = rgb_to_hex(rgba) + rgb_color = rgba_to_rgb(rgba) + new_line = ready_templates[file].format( + name=color, hex=hex_color, rgb=rgb_color + ) + _template += new_line + if color in additional: + new_line = ready_templates[file].format( + name=additional[color], hex=hex_color, rgb=rgb_color + ) + _template += new_line + + new_path = join(output_folder, os.path.basename(file)) + with open(new_path, "w") as f: + f.write(_template) + + +def run_hooks(folder: str): + if not os.path.exists(folder): + os.makedirs(folder) + file_list = get_file_list(folder) + for file_path in file_list: + file_name = os.path.basename(file_path) + if str(file_name).endswith(".py"): + subprocess.run(["python", "-O", file_path]) + elif str(file_name).endswith(".sh"): + subprocess.run(["sh", file_path]) + + +def process_image(image_path, quality=2, num_colors=128): + def get_cache_path(image_path): + global cache_path + _cache_path = join(cache_path, "cached_colors") + hash_object = hashlib.md5(image_path.encode()) + cache_filename = hash_object.hexdigest() + ".pkl" + os.makedirs(_cache_path, exist_ok=True) + return join(_cache_path, cache_filename) + + def load_from_cache(cache_path): + if os.path.exists(cache_path): + with open(cache_path, "rb") as f: + return pickle.load(f) + return None + + def save_to_cache(cache_path, data): + with open(cache_path, "wb") as f: + pickle.dump(data, f) + + cache_path = get_cache_path(image_path) + + cached_result = load_from_cache(cache_path) + if cached_result is not None: + return cached_result + + image = Image.open(image_path).convert("RGB") + + image_data = np.array(image) + pixel_array = image_data[::quality, ::quality].reshape(-1, 3) + + result = QuantizeCelebi(pixel_array, num_colors) + + color = Score.score(result)[0] + + save_to_cache(cache_path, color) + + return color + + +def main( + color_scheme: str, + image_path: str, + use_color: int | None = None, + scheme_name: str | None = None, + contrast_level: int = 0, +): + scheme_name = scheme_name or "tonalSpot" + scheme = schemes[scheme_name] + is_dark = True if color_scheme == "dark" else False + if use_color is None: + color = process_image(image_path, 4, 1024) + + else: + color = use_color + + scheme = scheme(Hct.from_int(color), is_dark, contrast_level) + # for color in vars(MaterialDynamicColors).keys(): + # color_name = getattr(MaterialDynamicColors, color) + # if hasattr(color_name, "get_hct"): + # print(color, rgb_to_hex(color_name.get_hct(scheme).to_rgba())) + + with open(join(cache_path, "colors.json"), "w") as f: + object = ColorsCache(scheme, image_path, color, contrast_level, scheme_name) + json.dump(colors_dict(object), f, indent=2) + + generate_templates( + f"{script_dir}/templates", cache_path, scheme, color_scheme, image_path + ) + generate_templates( + f"{script_dir}/templates/svg", + join(cache_path, "svg/"), + scheme, + color_scheme, + image_path, + ) + + if is_dark: + subprocess.run(["sh", join(script_dir, "gtk-material.sh")]) + else: + subprocess.run(["sh", join(script_dir, "gtk-material.sh"), "--light"]) + + run_hooks(join(script_dir, "hooks")) + + +def _argparse() -> tuple[bool, bool, str, str, str, str]: + parser = argparse.ArgumentParser() + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument("-R", help="Restore previous colorscheme.", action="store_true") + group.add_argument( + "-w", help="Use last used wallpaper for color generation.", action="store_true" + ) + group.add_argument("--image", type=str, help="Image for color scheme generation") + group.add_argument( + "--color", type=str, help="Main color for color scheme generation" + ) + parser.add_argument( + "--color-scheme", + type=str, + default="dark", + choices=["dark", "light"], + help="Color scheme variant", + ) + parser.add_argument( + "--scheme", + type=str, + default="", + choices=list(schemes.keys()), + help="Scheme for color generation", + ) + + args = parser.parse_args() + + restore_palette = args.R + use_last_used_wallpaper = args.w + image = args.image + color_scheme = args.color_scheme + use_color = args.color + scheme = args.scheme + return ( + restore_palette, + use_last_used_wallpaper, + image, + color_scheme, + use_color, + scheme, + ) + + +def _main(): + ( + restore_palette, + use_last_used_wallpaper, + image, + color_scheme, + use_color, + scheme, + ) = _argparse() + if not scheme: + scheme = None + if use_color is not None: + try: + with open(join(cache_path, "colors.json")) as f: + content = get_cache_object(f.read()) + main( + color_scheme, + image_path=content.wallpaper, + use_color=int(use_color.lstrip("#"), 16), + scheme_name=scheme, + ) + except Exception: + main( + color_scheme, + image_path="/dev/null", + use_color=int(use_color.lstrip("#"), 16), + scheme_name=scheme, + ) + + elif restore_palette: + with open(join(cache_path, "colors.json")) as f: + content = get_cache_object(f.read()) + main( + color_scheme, + image_path=content.wallpaper, + use_color=content.original_color, + scheme_name=scheme or content.scheme_name, + contrast_level=content.contrast_level, + ) + elif use_last_used_wallpaper: + with open(join(cache_path, "colors.json")) as f: + content = get_cache_object(f.read()) + main( + color_scheme, + image_path=content.wallpaper, + scheme_name=scheme or content.scheme_name, + contrast_level=content.contrast_level, + ) + else: + if os.path.isdir(image): + files = [ + f for f in os.listdir(image) if f.endswith((".png", ".jpg", ".jpeg")) + ] + if files: + image = join(image, random.choice(files)) + main(color_scheme, image_path=image, scheme_name=scheme) + + +if __name__ == "__main__": + _main() diff --git a/config/material-colors/gtk-material.sh b/config/material-colors/gtk-material.sh new file mode 100755 index 0000000..a197435 --- /dev/null +++ b/config/material-colors/gtk-material.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +dark_icon="Tela-nord-dark" +light_icon="Tela-nord-light" + +if [ "$1" == "--light" ]; then + gsettings set org.gnome.desktop.interface gtk-theme adw-gtk3 + gsettings set org.gnome.desktop.interface icon-theme "$light_icon" + gsettings set org.gnome.desktop.interface color-scheme 'prefer-light' +else + gsettings set org.gnome.desktop.interface gtk-theme adw-gtk3-dark + gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark' + gsettings set org.gnome.desktop.interface icon-theme "$dark_icon" +fi diff --git a/config/material-colors/hooks/discord.sh b/config/material-colors/hooks/discord.sh new file mode 100755 index 0000000..2ec3a3b --- /dev/null +++ b/config/material-colors/hooks/discord.sh @@ -0,0 +1,4 @@ +#!/bin/bash +material_cache="$HOME"/.cache/material/ + +sass $material_cache/material-discord.scss $material_cache/material-discord.css diff --git a/config/material-colors/hooks/sddm.sh b/config/material-colors/hooks/sddm.sh new file mode 100755 index 0000000..1528b6f --- /dev/null +++ b/config/material-colors/hooks/sddm.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +"$HOME"/dotfiles/sddm/scripts/wallpaper.sh 1>/dev/null diff --git a/config/material-colors/hooks/telegram.sh b/config/material-colors/hooks/telegram.sh new file mode 100755 index 0000000..dc001d7 --- /dev/null +++ b/config/material-colors/hooks/telegram.sh @@ -0,0 +1,13 @@ +#!/bin/bash +cache_folder="$HOME"/.cache/material +telegram_theme_cache="$HOME"/.cache/telegram-theme + +bg_color_file="$cache_folder/bg-color" +bg_color=$(cat "$bg_color_file") +magick -size 256x256 "xc:$bg_color" $telegram_theme_cache/tiled.png + +mkdir -p $telegram_theme_cache + +cp $cache_folder/colors.tdesktop-theme $telegram_theme_cache/colors.tdesktop-theme + +zip -q -j $telegram_theme_cache/pallete.tdesktop-theme $telegram_theme_cache/tiled.png $telegram_theme_cache/colors.tdesktop-theme diff --git a/config/material-colors/templates/alacritty-dark.toml b/config/material-colors/templates/alacritty-dark.toml new file mode 100644 index 0000000..3f3c701 --- /dev/null +++ b/config/material-colors/templates/alacritty-dark.toml @@ -0,0 +1,72 @@ +[colors.primary] +background = '' +foreground = '' + + +[colors.cursor] +text = '#1E1E2E' +cursor = '#F5E0DC' + +[colors.vi_mode_cursor] +text = '#1E1E2E' +cursor = '#B4BEFE' + + +[colors.search.matches] +foreground = '#1E1E2E' +background = '#A6ADC8' + +[colors.search.focused_match] +foreground = '#1E1E2E' +background = '#A6E3A1' + +[colors.footer_bar] +foreground = '#1E1E2E' +background = '#A6ADC8' + + +[colors.hints.start] +foreground = '#1E1E2E' +background = '#F9E2AF' + +[colors.hints.end] +foreground = '#1E1E2E' +background = '#A6ADC8' + + +[colors.selection] +text = '#1E1E2E' +background = '#F5E0DC' + + +[colors.normal] +black = '#45475A' +red = '#F38BA8' +green = '#A6E3A1' +yellow = '#F9E2AF' +blue = '#89B4FA' +magenta = '#F5C2E7' +cyan = '#94E2D5' +white = '#BAC2DE' + + +[colors.bright] +black = '#585B70' +red = '#F38BA8' +green = '#A6E3A1' +yellow = '#F9E2AF' +blue = '#89B4FA' +magenta = '#F5C2E7' +cyan = '#94E2D5' +white = '#A6ADC8' + + +[colors.dim] +black = '#45475A' +red = '#F38BA8' +green = '#A6E3A1' +yellow = '#F9E2AF' +blue = '#89B4FA' +magenta = '#F5C2E7' +cyan = '#94E2D5' +white = '#BAC2DE' diff --git a/config/material-colors/templates/alacritty-light.toml b/config/material-colors/templates/alacritty-light.toml new file mode 100644 index 0000000..a86bdec --- /dev/null +++ b/config/material-colors/templates/alacritty-light.toml @@ -0,0 +1,72 @@ +[colors.primary] +background = '' +foreground = '' + + +[colors.cursor] +text = '#EFF1F5' +cursor = '#DC8A78' + +[colors.vi_mode_cursor] +text = '#EFF1F5' +cursor = '#7287FD' + + +[colors.search.matches] +foreground = '#EFF1F5' +background = '#6C6F85' + +[colors.search.focused_match] +foreground = '#EFF1F5' +background = '#40A02B' + +[colors.footer_bar] +foreground = '#EFF1F5' +background = '#6C6F85' + + +[colors.hints.start] +foreground = '#EFF1F5' +background = '#DF8E1D' + +[colors.hints.end] +foreground = '#EFF1F5' +background = '#6C6F85' + + +[colors.selection] +text = '#EFF1F5' +background = '#DC8A78' + + +[colors.normal] +black = '#5C5F77' +red = '#D20F39' +green = '#40A02B' +yellow = '#DF8E1D' +blue = '#1E66F5' +magenta = '#EA76CB' +cyan = '#179299' +white = '#ACB0BE' + + +[colors.bright] +black = '#6C6F85' +red = '#D20F39' +green = '#40A02B' +yellow = '#DF8E1D' +blue = '#1E66F5' +magenta = '#EA76CB' +cyan = '#179299' +white = '#BCC0CC' + + +[colors.dim] +black = '#5C5F77' +red = '#D20F39' +green = '#40A02B' +yellow = '#DF8E1D' +blue = '#1E66F5' +magenta = '#EA76CB' +cyan = '#179299' +white = '#ACB0BE' diff --git a/config/material-colors/templates/alacritty.toml b/config/material-colors/templates/alacritty.toml new file mode 100644 index 0000000..f62d701 --- /dev/null +++ b/config/material-colors/templates/alacritty.toml @@ -0,0 +1,10 @@ +[general] +import = [ + "/alacritty-.toml" +] +[font] +normal = { family = "MesloLGS NF", style = "Regular" } +bold = { family = "MesloLGS NF", style = "Bold" } +italic = { family = "MesloLGS NF", style = "Italic" } +bold_italic = { family = "MesloLGS NF", style = "Bold Italic" } +size = 12.0 diff --git a/config/material-colors/templates/bg-color b/config/material-colors/templates/bg-color new file mode 100644 index 0000000..af5dbef --- /dev/null +++ b/config/material-colors/templates/bg-color @@ -0,0 +1 @@ + diff --git a/config/material-colors/templates/colors-hyprland.conf b/config/material-colors/templates/colors-hyprland.conf new file mode 100644 index 0000000..2766e30 --- /dev/null +++ b/config/material-colors/templates/colors-hyprland.conf @@ -0,0 +1,5 @@ +$background = rgb() +$foreground = rgb() +$border_color_active = rgb() +$border_color_inactive = rgb() +# <\\outline> diff --git a/config/material-colors/templates/colors-kitty.conf b/config/material-colors/templates/colors-kitty.conf new file mode 100644 index 0000000..eeebd86 --- /dev/null +++ b/config/material-colors/templates/colors-kitty.conf @@ -0,0 +1,27 @@ +foreground +background +cursor + +color0 +color8 + +color1 +color9 + +color2 +color10 + +color3 +color11 + +color4 +color12 + +color5 +color13 + +color6 +color14 + +color7 +color15 \ No newline at end of file diff --git a/config/material-colors/templates/colors-sddm-style.conf b/config/material-colors/templates/colors-sddm-style.conf new file mode 100644 index 0000000..d9417aa --- /dev/null +++ b/config/material-colors/templates/colors-sddm-style.conf @@ -0,0 +1,129 @@ +## Please see https://github.com/aczw/sddm-theme-corners/blob/main/CONFIG.md for +## more information about these options. Happy configuring! + +[General] + +# BgSource: string. Path to an image, used as the wallpaper. Can be placed +# in the `backgrounds/` folder for convenience (see below!) +# +# FontFamily: string. Name of the font family. You can find this with +# something like `fontconfig`, for example. +# +# FontSize: number. This value is used for everything *except* the date/time. +# +# Padding: number. How far away things should be from the edge of your screen. +# +# Radius: number. Corner radius for UI. Set to 0 to disable rounded corners. +# +# Scale: number. Adjusts the size of UI elements. Can be anything, but I +# would stick between 1 and 2 (e.g. 1.5). + +BgSource="backgrounds/CURRENTWALLPAPER" +FontFamily="Atkinson Hyperlegible" +FontSize=9 +Padding=50 +Radius=10 +Scale=1 + +# UserPictureEnabled: boolean. By default the user picture is shown. Set to false +# if you don't have/want a user picture, or you don't use the +# user selection functionality. +# +# UserBorderWidth: number. Width of the border around your picture. Set to +# 0 to remove. +# +# UserBorderColor: color. Border color around your picture. Useless if border +# width is 0. +# +# UserColor: color. Background color of the default, blank avatar. Only +# visible if you don't have your own picture. + +UserPictureEnabled=false +UserBorderWidth=5 +UserBorderColor="" +UserColor="" + +# InputColor: color. Background color of the input fields. +# +# InputTextColor: color. Color of text you enter in the fields. +# +# InputBorderWidth: number. Width of the border around the active field. Set +# to 0 to remove. +# +# InputBorderColor: color. Border color of active field. Useless if border +# width is 0. +# +# UserPlaceholderText: string. Placeholder text shown when user field is empty. +# +# PassPlaceholderText: string. Placeholder text shown when user field is empty. +# +# HidePassword: boolean. Whether to replace your entered password with dots. + +InputColor="" +InputTextColor="" +InputBorderWidth=4 +InputBorderColor="" +UserPlaceholderText="user" +PassPlaceholderText="password" +HidePassword=true + +# LoginButtonTextColor: color. Text color on the login button. +# +# LoginButtonText: string. Text displayed on the button. +# +# LoginButtonColor: color. Background color of the button. + +LoginButtonTextColor="" +LoginButtonText="Login" +LoginButtonColor="" + +# PopupColor: color. Background color of popup window. +# +# PopupActiveColor: color. Color around the currently selected entry. +# +# PopupActiveTextColor: color. Text color of the currently selected entry. +# Mainly provided for potential contrast issues. + +PopupColor="" +PopupActiveColor="" +PopupActiveTextColor="" + +# SessionButtonColor: color. Session button background color. +# +# SessionIconColor: color. Color of the icon inside the session button. +# +# PowerButtonColor: color. Power button background color. +# +# PowerIconColor: color. Color of the icon inside the power button. + +SessionButtonColor="" +SessionIconColor="" +PowerButtonColor="" +PowerIconColor="" + +# DateTimeSpacing: number. Spacing between the date and time. +# +# Date/TimeColor: color. Date/time text color. +# +# Date/TimeSize: number. Font size for the date/time. +# +# Date/TimeIsBold: boolean. Whether date/time text should be bolded. +# +# Date/TimeOpacity: number. Date/time text opacity. +# +# Date/TimeFormat: string. Change the format of how the date and time is displayed. +# Note that they use different formats. + +DateTimeSpacing=-20 + +DateColor="" +DateSize=36 +DateIsBold=false +DateOpacity=1.0 +DateFormat="dddd, MMMM d" + +TimeColor="" +TimeSize=48 +TimeIsBold=true +TimeOpacity=1.0 +TimeFormat="hh:mm AP" diff --git a/config/material-colors/templates/colors.tdesktop-theme b/config/material-colors/templates/colors.tdesktop-theme new file mode 100644 index 0000000..1dc6a05 --- /dev/null +++ b/config/material-colors/templates/colors.tdesktop-theme @@ -0,0 +1,568 @@ +// vim:ft=cfg + +name: pywal; + +// Color presets // + +secondary: ; +onSecondary: ; +outline: ; +shadow: ; + +BACKGROUND: ; +BACKGROUND_LIT_LIGHT: ; +BACKGROUND_LIGHT: ; +BACKGROUND_EXTRA_LIGHT: ; +ON_BACKGROUND: ; +ON_BACKGROUND_DARKER: ; + +ACCENT: ; +ON_ACCENT: ; +ACCENT_LIGHT: ; +ON_ACCENT_LIGHT: ; +ACCENT_DARK: ; +ON_ACCENT_DARK: ; + +ERROR: ; +ERROR_RIPPLE: #ff9180; +ON_ERROR: ; + +SUCCES: #66bb6a; +SUCCES_LIGHT: #81c784; +SUCCES_DARK: #388e3c; + +COLOR_PINK: #e016a0; // Just for testing purposes :) + +// basic +windowBg: BACKGROUND; // Window backgrounds +windowFg: ON_BACKGROUND; // Main text / chat text +windowBgOver: BACKGROUND_LIGHT; // Search field background + buttons color in context menu +windowBgRipple: BACKGROUND_EXTRA_LIGHT; // Inactive chat in chats list (press & hold) +windowFgOver: windowFg; // black: fallback for over text color +windowSubTextFg: ON_BACKGROUND_DARKER; // Chats list message preview text +windowSubTextFgOver: ON_BACKGROUND_DARKER; // Mouse over chats list message preview text +windowBoldFg: ON_BACKGROUND; // Bold font (titles in Settings, Left menu options) +windowBoldFgOver: ACCENT_LIGHT; // Mouse over chats list chat name text (+ Left menu options on hover) +windowBgActive: ACCENT; // Active button background (+ checkboxes background) +windowFgActive: ON_ACCENT; // Active selected chats list text / button text + checkboxes' "ticks" color +windowActiveTextFg: ACCENT_DARK; // Online status, links, recording cancel, etc +windowShadowFg: BACKGROUND; // Window border shadows +windowShadowFgFallback: BACKGROUND; // Notification border + +shadowFg: #00000018; // Internal border shadows +slideFadeOutBg: #0000003c; // Slide animation (chat to profile) fade out filling +slideFadeOutShadowFg: windowShadowFg; // Slide animation (chat to profile) fade out right section shadow + +imageBg: BACKGROUND; // Image background fallback (when photo size is less than minimum allowed) +imageBgTransparent: ON_BACKGROUND; // Image background when displaying an image with opacity where no opacity is needed + +//widgets +activeButtonBg: ACCENT; // Currently selected button background +activeButtonBgOver: ACCENT_LIGHT; // Mouse over currently selected button background +activeButtonBgRipple: ACCENT; // Primary button background color (and active chat) - Press & hold +activeButtonFg: ON_ACCENT; // Currently selected button text +activeButtonFgOver: ON_ACCENT_LIGHT; // Mouse over currently selected button text +activeButtonSecondaryFg: ON_BACKGROUND; // Counter color on primary button +activeButtonSecondaryFgOver: ON_BACKGROUND; // Counter color on primary button (hover) +activeLineFg: ACCENT_LIGHT; // Active input field line color (normal) +activeLineFgError: ERROR; // Active input field line color (error) + +lightButtonBg: BACKGROUND; // Secondary button background color (default) +lightButtonBgOver: BACKGROUND_LIGHT; // Secondary button background color (hover) +lightButtonBgRipple: BACKGROUND_EXTRA_LIGHT; // Secondary button background color (press & hold) +lightButtonFg: ON_BACKGROUND; // Secondary button text color +lightButtonFgOver: lightButtonFg; // Secondary button text color (hover) + +attentionButtonFg: ERROR; // Default attention button text (like confirm button on log out) +attentionButtonFgOver: ON_ERROR; // Default attention button text with mouse over +attentionButtonBgOver: ERROR; // Default attention button background with mouse over +attentionButtonBgRipple: ERROR_RIPPLE; // Default attention button ripple effect + +outlineButtonBg: BACKGROUND; // Buttons background in profiles (shared media) +outlineButtonBgOver: lightButtonBgOver; // Buttons background in profiles (hover) +outlineButtonOutlineFg: windowBgActive; // Default left outlined button left outline border +outlineButtonBgRipple: lightButtonBgRipple; // Default left outlined button ripple effect + +menuBg: BACKGROUND; // Default popup menu background +menuBgOver: windowBgOver; // Default popup menu item background with mouse over +menuBgRipple: windowBgRipple; // Default popup menu item ripple effect +menuIconFg: ON_BACKGROUND; // Icons color (left menu button, scroll down, paperclip..) +menuIconFgOver: ACCENT_DARK; // Icons color on hover (left menu button, scroll down, paperclip..) +menuSubmenuArrowFg: #373737; // Default popup menu submenu arrow icon (like in message field context menu in case of RTL system language) +menuFgDisabled: #727272; // Default popup menu item disabled text (like unavailable items in message field context menu) +menuSeparatorFg: BACKGROUND; // Default popup menu separator (like in message field context menu) + +// + +scrollBarBg: ACCENT; // Scrollbar (chats list) current position +scrollBarBgOver: ACCENT_DARK; // Mouse over scrollbar (chats list) current position +scrollBg: #0000001a; // Scrollbar (chats list) background +scrollBgOver: #0000002c; // Mouse over scrollbar (chats list) background + +smallCloseIconFg: ACCENT; // Notification close button (default) +smallCloseIconFgOver: ACCENT_LIGHT; // Notification close button (hover) + +radialFg: ON_BACKGROUND; // Default radial loader line (like in Media Viewer when loading a photo) +radialBg: #00000056; // Default radial loader background (like in Media Viewer when loading a photo) + +placeholderFg: ON_BACKGROUND_DARKER; // Inactive input box placeholder text +placeholderFgActive: ON_BACKGROUND_DARKER; // Active input box placeholder text +inputBorderFg: ACCENT; // Default input field bottom border (like in code input field when you log in and field is not focused) +filterInputBorderFg: ACCENT_DARK; // Default rounded input field border (like in chats list search field when field is focused) +filterInputInactiveBg: windowBgOver; // Default rounded input field background (like in chats list search field when field is inactive) +checkboxFg: #b3b3b3; // Default unchecked checkbox rounded rectangle (and also emoji category icons) + +sliderBgInactive: #e1eaef; // Inactive media player button (prev/next song) +sliderBgActive: windowBgActive; // Active media player button (prev/next song) + +tooltipBg: #eef2f5; // Tooltip background (hover on message sent time) +tooltipFg: #5d6c80; // Tooltip text color +tooltipBorderFg: #c9d1db; // Tooltip border color + + +// custom title bar for Windows and macOS +titleBg: BACKGROUND; // Title bar color +titleBgActive: titleBg; // Title bar color when Telegram window is focused +titleButtonBg: titleBg; // Title bar min/max/close buttons background (Windows only) +titleShadow: #00000003; // One pixel line shadow at the bottom of custom window title +titleButtonFg: BACKGROUND; // Title bar buttons(min/max/close) (Windows only) +titleButtonBgOver: BACKGROUND_LIGHT; // Title bar min/max background (hover) (Windows only) +titleButtonFgOver: ON_BACKGROUND; // Title bar min/max icons (Windows only) +titleButtonBgActive: titleButtonBg; // Custom window title minimize/maximize/restore button background when window is active (Windows only) +titleButtonFgActive: titleButtonFg; // Custom window title minimize/maximize/restore button icon when window is active (Windows only) +titleButtonBgActiveOver: titleButtonBgOver; // Custom window title minimize/maximize/restore button background with mouse over when window is active (Windows only) +titleButtonFgActiveOver: titleButtonFgOver; // Custom window title minimize/maximize/restore button icon with mouse over when window is active (Windows only) +titleButtonCloseBg: titleButtonBg; // Custom window title close button background when window is inactive (Windows only) +titleButtonCloseFg: titleButtonFg; // Custom window title close button icon when window is inactive (Windows only) +titleButtonCloseBgOver: ERROR; // Close button background (hover) +titleButtonCloseFgOver: ON_ERROR; // Close button icon color (hover) +titleButtonCloseBgActive: titleButtonCloseBg; // Custom window title close button background when window is active (Windows only) +titleButtonCloseFgActive: titleButtonCloseFg; // Custom window title close button icon when window is active (Windows only) +titleButtonCloseBgActiveOver: titleButtonCloseBgOver; // Custom window title close button background with mouse over when window is active (Windows only) +titleButtonCloseFgActiveOver: titleButtonCloseFgOver; // Custom window title close button icon with mouse over when window is active (Windows only) +titleFgActive: #3e3c3e; // Active title bar text (Mac) +titleFg: #acacac; // Inactive title bar text (Mac) + +// tray icon +trayCounterBg: ON_ERROR; // Unread messages counter background in tray (not muted, default red) +trayCounterBgMute: BACKGROUND; // Unread messages counter background in tray (muted, default gray) +trayCounterFg: ON_BACKGROUND; // Unread messages counter text (number) +trayCounterBgMacInvert: ON_BACKGROUND; // Tray icon counter background when tray icon is pressed or when dark theme of macOS is used (macOS only) +trayCounterFgMacInvert: ON_BACKGROUND; // Tray icon counter text when tray icon is pressed or when dark theme of macOS is used (macOS only) + +// layers +layerBg: #0000007f; // Box and main menu background layer fade (for example, current chat tint when forwarding message) + +cancelIconFg: menuIconFg; // Default for settings close icon and box search cancel icon +cancelIconFgOver: menuIconFgOver; // Default for settings close icon and box search cancel icon with mouse over + +// boxes +boxBg: BACKGROUND; // Popup windows background color +boxTextFg: ON_BACKGROUND; // Popup windows text color +boxTextFgGood: SUCCES; // Popup windows "good" text (e.g. "Username available") +boxTextFgError: ERROR; // Popup windows "bad" text (e.g. "Username already taken") +boxTitleFg: ACCENT; // Popup windows titles color (sessions, contacts...) +boxSearchBg: boxBg; // Box search field background (like in contacts box) +// boxSearchCancelIconFg: cancelIconFg; // (not implemented yet) Search cancel X button icon (like in contacts box) +boxSearchCancelIconFgOver: cancelIconFgOver; // (not implemented yet) Search cancel X button icon with mouse over + +boxTitleAdditionalFg: ACCENT_LIGHT; // Box title additional text (like in create group box when you see chosen members count) +boxTitleCloseFg: cancelIconFg; // Settings close icon and box search cancel icon (like in contacts box) +boxTitleCloseFgOver: cancelIconFgOver; // Settings close icon and box search cancel icon (like in contacts box) with mouse over + +membersAboutLimitFg: windowSubTextFgOver; // Text in channel members box about the limit (max 200 last members are shown) + +contactsBg: BACKGROUND; // Contacts (and some other) list background color +contactsBgOver: windowBgOver; // Contacts (and some other) list mouse over background +contactsNameFg: ON_BACKGROUND; // Contacts (and some other) box row name text +contactsStatusFg: ON_BACKGROUND_DARKER; // Contacts (and some other) box row additional text (like last seen stamp) +contactsStatusFgOver: windowSubTextFgOver; // Contacts (and some other) box row additional text (like last seen stamp) with mouse over +contactsStatusFgOnline: ACCENT_LIGHT; // Contacts (and some other) box row active additional text (like online status) + +photoCropFadeBg: layerBg; // "Out-of-bounds" area in photo cropping (avatars etc...) +photoCropPointFg: ON_BACKGROUND; // Photo crop "corners" (which you drag to crop photo) + +callArrowFg: boxTextFgGood; // Received phone call arrow (in calls list box) +callArrowMissedFg: boxTextFgError; // Missed phone call arrow (in calls list box) + +// intro +introBg: BACKGROUND; // Login screen background +introTitleFg: windowBoldFg; // Login title text +introDescriptionFg: ON_BACKGROUND_DARKER; // Login description text +introErrorFg: ERROR; // Login error text (like when providing a wrong log in code) + +introCoverTopBg: BACKGROUND; // Gradient finish at the top of the login screen +introCoverBottomBg: BACKGROUND; // Gradient start at the bottom of the login screen +introCoverIconsFg: ACCENT_DARK; // Icons and clouds colors +introCoverPlaneTrace: 69; // Plane trace +introCoverPlaneInner: ON_ACCENT; // Plane color inside +introCoverPlaneOuter: ACCENT; // Plane color outside +introCoverPlaneTop: ON_BACKGROUND; // Main plane color + +dialogsMenuIconFg: menuIconFg; // Main menu and lock telegram icon +dialogsMenuIconFgOver: menuIconFgOver; // Main menu and lock telegram icon with mouse over + +dialogsBg: BACKGROUND; // Chats list background color +dialogsNameFg: ON_BACKGROUND; // Chat list name text +dialogsChatIconFg: dialogsNameFg; // Chat list group or channel icon +dialogsDateFg: ON_BACKGROUND_DARKER; // Chats list date text +dialogsTextFg: ON_BACKGROUND_DARKER; // Chats list message preview text +dialogsTextFgService: ACCENT_LIGHT; // Chats list message preview sender name +dialogsDraftFg: ACCENT; // "Draft" word +dialogsVerifiedIconBg: windowBgActive; // Verification icon background +dialogsVerifiedIconFg: ON_ACCENT; // Verification icon check color +dialogsSendingIconFg: #c1c1c1; // Chat list sending message icon (clock) +dialogsSentIconFg: ACCENT; // Chat list sent message tick / double tick icon +dialogsUnreadBg: ACCENT; // Chats list unread counter background +dialogsUnreadBgMuted: ACCENT_DARK; // Chat list unread badge background for muted chat +dialogsUnreadFg: ON_ACCENT; // Unread counter text color +dialogsOnlineBadgeFg: ACCENT_LIGHT; // Chat list online status (for inactive item) + +dialogsBgOver: BACKGROUND_LIGHT; // Inactive chat color on hover +dialogsNameFgOver: ACCENT_LIGHT; // Chat list name text with mouse over +dialogsChatIconFgOver: dialogsNameFgOver; // Chat list group or channel icon with mouse over +dialogsDateFgOver: windowSubTextFgOver; // Chat list date text with mouse over +dialogsTextFgOver: windowSubTextFgOver; // Chat list message text with mouse over +dialogsTextFgServiceOver: dialogsTextFgService; // Mouse over chats list message preview sender name +dialogsDraftFgOver: dialogsDraftFg; // Chat list draft label with mouse over +dialogsVerifiedIconBgOver: dialogsVerifiedIconBg; // Chat list verified icon background with mouse over +dialogsVerifiedIconFgOver: dialogsVerifiedIconFg; // Chat list verified icon check with mouse over +dialogsSendingIconFgOver: dialogsSendingIconFg; // Chat list sending message icon (clock) with mouse over +dialogsSentIconFgOver: dialogsSentIconFg; // Chat list sent message tick / double tick icon with mouse over +dialogsUnreadBgOver: dialogsUnreadBg; // Chat list unread badge background for NOT muted chat with mouse over +dialogsUnreadBgMutedOver: dialogsUnreadBgMuted; // Chat list unread badge background for muted chat with mouse over +dialogsUnreadFgOver: dialogsUnreadFg; // Chat list unread badge text with mouse over + +dialogsBgActive: ACCENT; // Active chat color (in chats list) +dialogsNameFgActive: ON_ACCENT; // Active chat name text (in chats list) +dialogsChatIconFgActive: dialogsNameFgActive; // Active chat group or channel icon (in chats list) +dialogsDateFgActive: ON_ACCENT; // Active chat date color +dialogsTextFgActive: ON_ACCENT; // Active chat message preview color +dialogsTextFgServiceActive: dialogsTextFgActive;// Active chat service messages color +dialogsDraftFgActive: ON_ACCENT_LIGHT; // "Draft" word when chat is selected +dialogsVerifiedIconBgActive: dialogsTextFgActive; // Active chat verified icon background (in chats list) +dialogsVerifiedIconFgActive: dialogsBgActive; // Active chat verified icon check (in chats list) +dialogsSendingIconFgActive: ON_ACCENT_LIGHT; // Active chat sending message icon (clock) (in chats list) +dialogsSentIconFgActive: dialogsTextFgActive; // Active chat sent message tick / double tick icon (in chats list) +dialogsUnreadBgActive: dialogsTextFgActive; // Active chat unread badge background for NOT muted chat (in chats list) +dialogsUnreadBgMutedActive: dialogsDraftFgActive; // Active chat unread badge background for muted chat (in chats list) +dialogsUnreadFgActive: dialogsBgActive; // Active chat unread badge text (in chats list) + +dialogsRippleBg: windowBgRipple; +dialogsRippleBgActive: activeButtonBgRipple; + +dialogsForwardBg: dialogsBgActive; // Forwarding panel background (when forwarding messages in the smallest window size) +dialogsForwardFg: dialogsNameFgActive; // Forwarding panel text (when forwarding messages in the smallest window size) + +searchedBarBg: BACKGROUND; // Search results window background color +searchedBarFg: ACCENT_LIGHT; // Search results text color + +// history +topBarBg: BACKGROUND; // Chat header background + +emojiPanBg: BACKGROUND; // Sticker panel background +emojiPanCategories: BACKGROUND; // Sticker panel bottom bar background +emojiPanHeaderFg: ACCENT; // Sticker panel title text +emojiPanHeaderBg: BACKGROUND; // Sticker panel top bar background +stickerPanDeleteBg: #000000cc; // Delete X button background for custom sent stickers in stickers panel (legacy) +stickerPanDeleteFg: ON_BACKGROUND; // Delete X button icon for custom sent stickers in stickers panel (legacy) +stickerPreviewBg: #2f2f2fCC; // Sticker and GIF preview background (when you press and hold on a sticker) + +historyTextInFg: ON_BACKGROUND; // Incoming message text color +historyTextInFgSelected: ON_ACCENT; // Selected incoming message text or text in a selected message +historyTextOutFg: ON_BACKGROUND; // Outgoing message text color +historyTextOutFgSelected: ON_ACCENT; // Outgoing message selected text or text in a selected message +historyLinkInFg: ACCENT_LIGHT; // Incoming message link color +historyLinkInFgSelected: historyLinkInFg; // Incoming message link in a selected text or message +historyLinkOutFg: ACCENT_LIGHT; // Outgoing message link color +historyLinkOutFgSelected: historyLinkOutFg; // Outgoing message link in a selected text or message +historyFileNameInFg: historyTextInFg; // Incoming message filename text color +historyFileNameInFgSelected: historyFileNameInFg; // Incoming media filename text in a selected message +historyFileNameOutFg: historyTextOutFg; // Outgoing message filename text color +historyFileNameOutFgSelected: historyFileNameOutFg; // Outgoing media filename text in a selected message +historyOutIconFg: dialogsSentIconFg; // "Eye" and "Checks" color for outgoing messages (selected) +historyOutIconFgSelected: ACCENT_DARK; // "Eye" and "Checks" color for outgoing messages (selected) +historyIconFgInverted: ON_ACCENT; // "Checks" on stickers (outgoing) +historySendingOutIconFg: ACCENT_LIGHT; // "Clock" icon while sending file (outgoing) +historySendingInIconFg: ACCENT_LIGHT; // "Clock" icon while sending file (incoming) +historySendingInvertedIconFg: ACCENT_LIGHT; // Media sending message icon (clock) (like in sent photo) +historyCallArrowInFg: callArrowFg; // Received phone call arrow +historyCallArrowInFgSelected: callArrowFg; // Received phone call arrow in a selected message +historyCallArrowMissedInFg: callArrowMissedFg; // Missed phone call arrow +historyCallArrowMissedInFgSelected: callArrowMissedFg; // Missed phone call arrow in a selected message +historyCallArrowOutFg: historyCallArrowInFg; // Outgoing phone call arrow +historyCallArrowOutFgSelected: historyCallArrowInFgSelected; // Outgoing phone call arrow + +historyUnreadBarBg: #424444; // Unread messages bar background color +historyUnreadBarBorder: BACKGROUND; // Unread messages bar border color +historyUnreadBarFg: #ededed; // Unread messages bar text color + +historyForwardChooseBg: #0000004c; // "Choose recipient" BACKGROUND color (forwarding) +historyForwardChooseFg: BACKGROUND; // "Choose recipient" text color (forwarding) + +historyPeer1NameFg: #E53935; // Group chat sender name - Red +historyPeer1NameFgSelected: historyPeer1NameFg; // Red group member name in a selected message +historyPeer1UserpicBg: #EF9A9A; // Group chat userpic background - Red + +historyPeer2NameFg: #7CB342; // Sender name - Green +historyPeer2NameFgSelected: historyPeer2NameFg; // Green group member name in a selected message +historyPeer2UserpicBg: #C5E1A5; // Userpic background - Green + +historyPeer3NameFg: #FDD835; // Sender name - Yellow +historyPeer3NameFgSelected: historyPeer3NameFg; // Yellow group member name in a selected message +historyPeer3UserpicBg: #FFF59D; // Userpic background - Yellow + +historyPeer4NameFg: #C0CA33; // Sender name - Light green +historyPeer4NameFgSelected: historyPeer4NameFg; // Blue group member name in a selected message +historyPeer4UserpicBg: #81D4FA; // Userpic background - Blue + +historyPeer5NameFg: #7E57C2; // Sender name - Purple +historyPeer5NameFgSelected: historyPeer5NameFg; // Purple group member name in a selected message +historyPeer5UserpicBg: #B39DDB; // Userpic background - Light Purple + +historyPeer6NameFg: #D81B60; // Sender name - "Raspberry" +historyPeer6NameFgSelected: historyPeer6NameFg; // "Raspberry" group member name in a selected message +historyPeer6UserpicBg: #F48FB1; // Userpic background - Light "Raspberry" + +historyPeer7NameFg: #00ACC1; // Sender name - Cyan +historyPeer7NameFgSelected: historyPeer7NameFg; // Cyan group member name in a selected message +historyPeer7UserpicBg: #80DEEA; // Userpic background - Cyan + +historyPeer8NameFg: #FB8C00; // Sender name - Orange +historyPeer8NameFgSelected: historyPeer8NameFg; // Orange group member name in a selected message +historyPeer8UserpicBg: #FFE082; // Userpic background - Light Orange + +historyPeerUserpicFg: ON_BACKGROUND; // Default userpic initials + +// Some values are marked as (adjusted), it means they're adjusted by +// hue and saturation of the average background color if user chooses +// some other (not bundled to this color theme) background. If the +// bundled background is used those colors are not adjusted in any way. +historyScrollBarBg: ACCENT; // Scrollbar (history view) background (adjusted) +historyScrollBarBgOver: ACCENT_LIGHT; // Scrollbar (history view) background (hover) (adjusted) +historyScrollBg: BACKGROUND; // Scrollbar (history view) current position background (adjusted) +historyScrollBgOver: BACKGROUND_LIGHT; // Scrollbar (history view) current position background (hover) (adjusted) + +msgInBg: BACKGROUND_LIGHT; // Incoming message background +msgInBgSelected: ACCENT; // Selected incoming message background +msgOutBg: BACKGROUND_EXTRA_LIGHT; // Outgoing message background +msgOutBgSelected: ACCENT; // Selected outgoing message background +msgSelectOverlay: 50; // Selected media (images...) overlay +msgStickerOverlay: 50; // Selected sticker overlay +msgInServiceFg: ACCENT; // Incoming reply quoted name +msgInServiceFgSelected: ON_ACCENT; // Selected incoming reply quoted name +msgOutServiceFg: ACCENT; // Outgoing reply quoted name +msgOutServiceFgSelected: ON_ACCENT; // Selected outgoing reply quoted name +msgInShadow: BACKGROUND_LIT_LIGHT; // Incoming message box shadow +msgInShadowSelected: BACKGROUND_LIT_LIGHT; // Selected incoming message box shadow +msgOutShadow: BACKGROUND_LIT_LIGHT; // Outgoing message box shadow +msgOutShadowSelected: BACKGROUND_LIT_LIGHT; // Selected outgoing message box shadow +msgInDateFg: ON_BACKGROUND_DARKER; // Incoming message date text +msgInDateFgSelected: ON_ACCENT; // Incoming selected message date text +msgOutDateFg: ON_BACKGROUND; // Outgoing message date text +msgOutDateFgSelected: ON_ACCENT; // Outgoing selected message date text +msgServiceFg: ON_ACCENT; // Service messages text color (join, left etc) +msgServiceBg: ACCENT; // Service messages BACKGROUND color (join, left etc) (adjusted) +msgServiceBgSelected: ACCENT; // Service message selected text background (like in a service message about group title being changed) (adjusted) +msgInReplyBarColor: ACCENT; // Incoming msg: vertical bar next to reply +msgInReplyBarSelColor: ACCENT; // Incoming msg: vertical bar next to reply (selected) +msgOutReplyBarColor: ACCENT; // Outgoing msg: vertical bar next to reply +msgOutReplyBarSelColor: ACCENT; // Outgoing msg: vertical bar next to reply (selected) +msgImgReplyBarColor: msgServiceFg; // Sticker message reply outline +msgInMonoFg: ACCENT_LIGHT; // Monowidth font / incoming msg +msgOutMonoFg: ACCENT_LIGHT; // Monowidth font / outgoing msg +msgInMonoFgSelected: msgInMonoFg; // Incoming message monospace text in a selected text or message +msgOutMonoFgSelected: msgOutMonoFg; // Outgoing message monospace text in a selected text or message +msgDateImgFg: msgServiceFg; // Media message time text (like time text in a sent photo) +msgDateImgBg: ACCENT; // Media message time bubble background (like time bubble in a sent photo) or file with thumbnail download icon circle background +msgDateImgBgOver: ON_ACCENT; // Media message download icon circle background with mouse over (like file with thumbnail download icon) +msgDateImgBgSelected: ACCENT; // Selected media message time bubble background + +msgFileThumbLinkInFg: lightButtonFg; // Incoming media file message with thumbnail download / open with button text +msgFileThumbLinkInFgSelected: lightButtonFgOver; // Incoming selected media file message with thumbnail download / open with button text +msgFileThumbLinkOutFg: ACCENT; // Outgoing media file message with thumbnail download / open with button text +msgFileThumbLinkOutFgSelected: ACCENT_LIGHT; // Outgoing selected media file message with thumbnail download / open with button text +msgFileInBg: windowBgActive; // Incoming audio file download circle background +msgFileInBgOver: ACCENT_LIGHT; // Incoming audio file download circle background with mouse over +msgFileInBgSelected: ACCENT_LIGHT; // Incoming selected audio file download circle background +msgFileOutBg: ACCENT; // Outgoing audio file download circle background +msgFileOutBgOver: ACCENT_DARK; // Outgoing audio file download circle background with mouse over +msgFileOutBgSelected: ACCENT; // Outgoing selected audio file download circle background + +msgFile1Bg: #72b1df; // Blue shared links / files without image square thumbnail +msgFile1BgDark: #5c9ece; // Blue shared files without image download circle background +msgFile1BgOver: #5294c4; // Blue shared files without image download circle background with mouse over +msgFile1BgSelected: #5099d0; // Blue shared files without image download circle background if file is selected +msgFile2Bg: #61b96e; // Green shared links / shared files without image square thumbnail +msgFile2BgDark: #4da859; // Green shared files without image download circle background +msgFile2BgOver: #44a050; // Green shared files without image download circle background with mouse over +msgFile2BgSelected: #46a07e; // Green shared files without image download circle background if file is selected +msgFile3Bg: #e47272; // Red shared links / shared files without image square thumbnail +msgFile3BgDark: #cd5b5e; // Red shared files without image download circle background +msgFile3BgOver: #c35154; // Red shared files without image download circle background with mouse over +msgFile3BgSelected: #9f6a82; // Red shared files without image download circle background if file is selected +msgFile4Bg: #efc274; // Yellow shared links / shared files without image square thumbnail +msgFile4BgDark: #e6a561; // Yellow shared files without image download circle background +msgFile4BgOver: #dc9c5a; // Yellow shared files without image download circle background with mouse over +msgFile4BgSelected: #b19d84; // Yellow shared files without image download circle background if file is selected + +historyFileInIconFg: ON_ACCENT; // Incoming file without thumbnail (like audio file) download arrow icon +historyFileInIconFgSelected: ON_ACCENT; // Incoming selected file without thumbnail (like audio file) download arrow icon +historyFileInRadialFg: ON_ACCENT; // Incoming file without thumbnail (like audio file) radial download animation line +historyFileInRadialFgSelected: ON_ACCENT; // Incoming selected file without thumbnail (like audio file) radial download animation line +historyFileOutIconFg: ON_ACCENT; // Outgoing file without thumbnail (like audio file) download arrow icon +historyFileOutIconFgSelected: ON_ACCENT; // Outgoing selected file without thumbnail (like audio file) download arrow icon +historyFileOutRadialFg: ON_ACCENT; // Outgoing file without thumbnail (like audio file) radial download animation line +historyFileOutRadialFgSelected: ON_ACCENT; // Outgoing selected file without thumbnail (like audio file) radial download animation line +historyFileThumbIconFg: ON_ACCENT; // File with thumbnail (or photo / video) download arrow icon +historyFileThumbIconFgSelected: ON_ACCENT; // Selected file with thumbnail (or photo / video) download arrow icon +historyFileThumbRadialFg: ON_ACCENT; // File with thumbnail (or photo / video) radial download animation line +historyFileThumbRadialFgSelected: ON_ACCENT; // Selected file with thumbnail (or photo / video) radial download animation line + +historyVideoMessageProgressFg: historyFileThumbIconFg; // Radial playback progress in round video messages +msgWaveformInActive: windowBgActive; // Incoming Voice "heard" part +msgWaveformInActiveSelected: ACCENT_LIGHT; // Selected incoming Voice "heard" part +msgWaveformInInactive: #d4dee6; // Incoming Voice "unheard" part +msgWaveformInInactiveSelected: ON_BACKGROUND; // Selected incoming Voice "unheard" part +msgWaveformOutActive: windowBgActive; // Outgoing Voice "heard" part +msgWaveformOutActiveSelected: ACCENT_LIGHT; // Selected outgoing Voice "heard" part +msgWaveformOutInactive: ON_BACKGROUND; // Outgoing Voice "unheard" part +msgWaveformOutInactiveSelected: ON_BACKGROUND; // Selected outgoing Voice "unheard" part + +msgBotKbOverBgAdd: ACCENT_DARK; // Inline button (hover) +msgBotKbIconFg: ON_ACCENT; // Inline button icon color (URL Buttons, for example) +msgBotKbRippleBg: #00000020; // Inline button (press & hold) + +mediaInFg: msgInDateFg; // Incoming media message status text (like in file that is being downloaded) +mediaInFgSelected: msgInDateFgSelected; // Incoming selected media message status text (like in file that is being downloaded) +mediaOutFg: msgOutDateFg; // Outgoing media message status text (like in file that is being downloaded) +mediaOutFgSelected: msgOutDateFgSelected; // Outgoing selected media message status text (like in file that is being downloaded) + +youtubePlayIconBg: #ef5350ee; // Youtube play icon background (when a link to a youtube video with a webpage preview is sent) +youtubePlayIconFg: #ffcdd2; // Youtube play icon arrow (when a link to a youtube video with a webpage preview is sent) +videoPlayIconBg: #0000007f; // Other video play icon background (like when a link to a vimeo video with a webpage preview is sent) +videoPlayIconFg: ON_BACKGROUND; // Other video play icon arrow (like when a link to a vimeo video with a webpage preview is sent) +toastBg: #000000b2; // Toast notification background (like when you click on your t.me link when editing your username) +toastFg: ON_BACKGROUND; // Toast notification text (like when you click on your t.me link when editing your username) + +reportSpamBg: emojiPanHeaderBg; // Report spam panel background (like a non contact user writes your for the first time) +reportSpamFg: ON_BACKGROUND_DARKER; // Report spam panel text (when you send a report from that panel) + +historyToDownBg: BACKGROUND; // "Scroll to bottom" button background +historyToDownBgOver: windowBgOver; // "Scroll to bottom" button background (hover) +historyToDownBgRipple: windowBgRipple; // "Scroll to bottom" button background (press & hold) +historyToDownFg: menuIconFg; // "Scroll to bottom" internal icon color +historyToDownFgOver: menuIconFgOver; // "Scroll to bottom" internal icon color (hover) +historyToDownShadow: #00000040; // "Scroll to bottom" button shadow + +historyComposeAreaBg: BACKGROUND; // Text input field background +historyComposeAreaFg: ON_BACKGROUND; // Text color in "Type your message" field +historyComposeAreaFgService: msgInDateFg; // Text color in "Type your message" field when replying to a media message +historyComposeIconFg: menuIconFg; // Text input field: Paperclip, Smile, Bot command icons +historyComposeIconFgOver: ACCENT_DARK; // Text input field: Paperclip, Smile, Bot command icons (hover) +historySendIconFg: ACCENT_DARK; // Text input field: Send icon +historySendIconFgOver: ACCENT; // Text input field: Send icon (hover) +historyPinnedBg: historyComposeAreaBg; // Pinned message area background +historyReplyBg: historyComposeAreaBg; // Reply / forward / edit message area background +historyReplyIconFg: windowBgActive; // Reply / forward / edit message left icon +historyReplyCancelFg: cancelIconFg; // Reply / forward / edit message cancel button +historyReplyCancelFgOver: cancelIconFgOver; // Reply / forward / edit message cancel button with mouse over + +historyComposeButtonBg: historyComposeAreaBg; // Unblock / join channel / mute channel button background +historyComposeButtonBgOver: windowBgOver; // Unblock / join channel / mute channel button background with mouse over +historyComposeButtonBgRipple: windowBgRipple; // Unblock / join channel / mute channel button ripple effect + +// overview +overviewCheckBg: #00000040; // Shared files / links checkbox background for not selected rows when some rows are selected +overviewCheckFg: BACKGROUND; // Shared files / links checkbox icon for not selected rows when some rows are selected +overviewCheckFgActive: BACKGROUND; // Shared files / links checkbox icon for selected rows +overviewPhotoSelectOverlay: #40ace333; // Shared photos / videos / links fill for selected rows + +// profile +profileStatusFgOver: COLOR_PINK; // Group members list in group profile user last seen text with mouse over +profileVerifiedCheckBg: windowBgActive; // Profile verified check icon background +profileVerifiedCheckFg: windowFgActive; // Profile verified check icon tick +profileAdminStartFg: ACCENT_LIGHT; // Star color for group/channhel admins and moderators (look in members list) + +// settings +notificationsBoxMonitorFg: ON_BACKGROUND_DARKER;// Custom notifications settings box monitor color +notificationsBoxScreenBg: dialogsBgActive; // Custom notifications settings box monitor screen background + +notificationSampleUserpicFg: windowBgActive; // Custom notifications settings box small sample userpic placeholder +notificationSampleCloseFg: #d7d7d7; // Custom notifications settings box small sample close button placeholder +notificationSampleTextFg: #d7d7d7; // Custom notifications settings box small sample text placeholder +notificationSampleNameFg: #939393; // Custom notifications settings box small sample name placeholder + +changePhoneSimcardFrom: notificationSampleTextFg; // Change phone number box left simcard icon +changePhoneSimcardTo: notificationSampleNameFg; // Change phone number box right simcard and plane icons + +mainMenuBg: BACKGROUND; // Left menu background (lower part, not buttons) +mainMenuCoverBg: dialogsBgActive; // Left menu background (upper part) +mainMenuCoverFg: ON_BACKGROUND; // Main menu top cover text +mainMenuCloudFg: activeButtonFg; // "Cloud" icon in main menu +mainMenuCloudBg: activeButtonBgRipple; // "Cloud" icon background in main menu + +mediaPlayerBg: BACKGROUND; // Music player background +mediaPlayerActiveFg: ACCENT; // Clickable Next song/Prev song button in music player + played part color +mediaPlayerInactiveFg: #4f5151; // "Disabled" Next song/Prev song button in music player + not player part color +mediaPlayerDisabledFg: ACCENT_DARK; // Music player audio loading progress (when you're playing an audio file and switch to the previous one which is not loaded yet) + +// mediaview +mediaviewFileBg: BACKGROUND; // File rectangle background (when you view a png file in Media Viewer and go to a previous, not loaded yet, file) +mediaviewFileNameFg: ON_BACKGROUND_DARKER; // File name in file rectangle +mediaviewFileSizeFg: ON_BACKGROUND_DARKER; // File size text in file rectangle +mediaviewFileRedCornerFg: #d55959; // Red file thumbnail placeholder corner in file rectangle (for a file without thumbnail, like .pdf) +mediaviewFileYellowCornerFg: #e8a659; // Yellow file thumbnail placeholder corner in file rectangle (for a file without thumbnail, like .zip) +mediaviewFileGreenCornerFg: #49a957; // Green file thumbnail placeholder corner in file rectangle (for a file without thumbnail, like .exe) +mediaviewFileBlueCornerFg: #599dcf; // Blue file thumbnail placeholder corner in file rectangle (for a file without thumbnail, like .dmg) +mediaviewFileExtFg: activeButtonFg; // File extension text in file thumbnail placeholder in file rectangle + +mediaviewMenuBg: BACKGROUND; // Context menu in Media Viewer background +mediaviewMenuBgOver: BACKGROUND_LIGHT; // Menu items background (hover) +mediaviewMenuBgRipple: BACKGROUND_EXTRA_LIGHT; // Menu items background (press & hold) +mediaviewMenuFg: ON_BACKGROUND; // Text color in media view menu + +mediaviewBg: #222222eb; // Media Viewer background +mediaviewVideoBg: imageBg; // Media Viewer background when viewing a video in full screen +mediaviewControlBg: #0000003c; // Controls background (like next photo / previous photo) +mediaviewControlFg: ON_BACKGROUND; // Controls icon (like next photo / previous photo) +mediaviewCaptionBg: #11111180; // Caption text background (when viewing photo with caption) +mediaviewCaptionFg: mediaviewControlFg; // Caption text color +mediaviewTextLinkFg: #91d9ff; // Links color in captions +mediaviewSaveMsgBg: toastBg; // 'Something was saved to % folder' message background +mediaviewSaveMsgFg: toastFg; // 'Something was saved to % folder' message text color + +mediaviewPlaybackActive: #c7c7c7; // Video playback progress already played part +mediaviewPlaybackInactive: #252525; // Video playback progress upcoming (not played yet) part +mediaviewPlaybackActiveOver: ON_BACKGROUND; // Video playback progress already played part with mouse over +mediaviewPlaybackInactiveOver: #474747; // Video playback progress upcoming (not played yet) part with mouse over +mediaviewPlaybackProgressFg: ON_BACKGROUND; // Video playback progress text +mediaviewPlaybackIconFg: mediaviewPlaybackActive; // Video playback controls icon +mediaviewPlaybackIconFgOver: mediaviewPlaybackActiveOver; // Video playback controls icon with mouse over +mediaviewTransparentBg: ON_BACKGROUND; // Transparent filling part (when viewing a transparent .png file in Media Viewer) +mediaviewTransparentFg: #cccccc; // Another transparent filling part + +// notification +notificationBg: BACKGROUND; // Notification background + +// calls +callBg: #26282cf2; // Phone call popup background +callNameFg: #ffffff; // Phone call popup name text +callFingerprintBg: #00000066; // Phone call popup emoji fingerprint background +callStatusFg: #aaabac; // Phone call popup status text +callIconFg: #ffffff; // Phone call popup answer, hangup and mute mic icon +callAnswerBg: #64c15b; // Phone call popup answer button background +callAnswerRipple: #52b149; // Phone call popup answer button ripple effect +callAnswerBgOuter: #50eb4126; // Phone call popup answer button outer ripple effect +callHangupBg: #d75a5a; // Phone call popup hangup button background +callHangupRipple: #c04646; // Phone call popup hangup button ripple effect +callCancelBg: #ffffff; // Phone call popup line busy cancel button background +callCancelFg: #777777; // Phone call popup line busy cancel button icon +callCancelRipple: #f1f1f1; // Phone call popup line busy cancel button ripple effect +callMuteRipple: #ffffff12; // Phone call popup mute mic ripple effect + +callBarBg: dialogsBgActive; // Active phone call bar background +callBarMuteRipple: dialogsRippleBgActive; // Active phone call bar mute and hangup button ripple effect +callBarBgMuted: dialogsUnreadBgMuted; // Phone call bar with muted mic background +callBarUnmuteRipple: shadowFg; // Phone call bar with muted mic mute and hangup button ripple effect +callBarFg: dialogsNameFgActive; // Phone call bar text and icons diff --git a/config/material-colors/templates/hyprlock.conf b/config/material-colors/templates/hyprlock.conf new file mode 100644 index 0000000..12e97a0 --- /dev/null +++ b/config/material-colors/templates/hyprlock.conf @@ -0,0 +1,145 @@ +# Configuration + # (general settings) + general { + grace = 2 + hide_cursor = true + ignore_empty_input = true + text_trim = true + } + # (background config) + background { + monitor = + path = ~/.cache/current_wallpaper.png # only png supported for now + color = rgba(, 1.0) + blur_passes = 3 # 0 disables blurring + blur_size = 1 + noise = 0.01 + contrast = 0.8916 + brightness = 0.7 + vibrancy = 0.1696 + vibrancy_darkness = 0.0 + } +# + +# Password input + input-field { + monitor = + size = 225, 50 + outline_thickness = 0 + dots_size = 0.33 + dots_spacing = 0.45 + dots_center = true + dots_rounding = -1 + outer_color = rgba(0,0,0,0) + inner_color = rgba(0,0,0,0) + font_color = rgba(, 1.0) + fade_on_empty = false + fade_timeout = 1000 + font_family = Product Sans + placeholder_text = + hide_input = false + rounding = 4 + check_color = rgba(0,0,0,0) + fail_color = rgba(0,0,0,0) + fail_text = $FAIL ($ATTEMPTS) + fail_transition = 200 + capslock_color = -1 + numlock_color = -1 + bothlock_color = -1 + invert_numlock = false + swap_font_color = false + position = 0, 150 + halign = center + valign = bottom + } +# + + +# Time + # (hours) + label { + monitor = + text = cmd[update:1000] echo -e "$(date +"%H")" + color = rgba(, 1.0) + shadow_pass = 2 + shadow_size = 3 + shadow_color = rgb(0,0,0) + shadow_boost = 1.2 + font_size = 150 + font_family = Product Sans Light + position = 0, 90 + halign = center + valign = center + } + # (minutes) + label { + monitor = + text = cmd[update:1000] echo -e "$(date +"%M")" + color = rgba(, 1.0) + font_size = 150 + font_family = Product Sans Light + position = 0, -90 + halign = center + valign = center + } +# + +# Date + # (label) + label { + monitor = + text = cmd[update:1000] echo -e "$(date +"%A, %B %d")" + color = rgba(, 1.0) + font_size = 15 + font_family = Product Sans Medium + position = 100, -75 + shadow_passes = 5 + shadow_size = 10 + halign = left + valign = top + } + # (icon) + label { + monitor = + text = calendar_today + color = rgba(, 1.0) + font_size = 15 + font_family = Material Symbols Outlined + position = 75, -75 + shadow_passes = 5 + shadow_size = 10 + halign = left + valign = top + } +# + +# Input layout + # (label) + label { + monitor = + text = $LAYOUT + color = rgba(, 1.0) + font_size = 15 + font_family = Product Sans Medium + position = 100, -110 + shadow_passes = 5 + shadow_size = 10 + halign = left + valign = top + } + # (icon) + label { + monitor = + text = keyboard + color = rgba(, 1.0) + font_size = 15 + font_family = Material Symbols Outlined + position = 75, -112 + shadow_passes = 5 + shadow_size = 10 + halign = left + valign = top + } +# + + diff --git a/config/material-colors/templates/material-discord.scss b/config/material-colors/templates/material-discord.scss new file mode 100644 index 0000000..607ee99 --- /dev/null +++ b/config/material-colors/templates/material-discord.scss @@ -0,0 +1,55 @@ +@use "sass:color"; + +@import url(https://capnkitten.github.io/BetterDiscord/Themes/Material-Discord/css/source.css); +@import "/colors.scss"; + + +$color-scheme: ; + +$hue: round(color.channel($primary, "hue", $space: hsl)); +$saturation: round(color.channel($primary, "saturation", $space: hsl)); +$lightness: round(color.channel($primary, "lightness", $space: hsl)); + +* { + transition: color 0.2s ease-in-out; + transition-property: background-color, color; +} + +.theme-#{$color-scheme} { + --main-color: #{$background}; + --main-alt: #{$surfaceContainerLow}; + + --message-color: #{$surfaceContainerLowest}; + --message-color-hover: #{$surfaceContainer}; + --message-color-alt: #{$surfaceContainer}; + --attachment-color: #{$background}; + + --accent-hue: #{$hue}; + --accent-saturation: #{$saturation}; + --accent-lightness: #{$lightness}; + --accent-text-color: #{$onPrimary}; + + --main-textarea-color: #{$surfaceContainerHigh}; + --main-textarea-color-alt: #{$surfaceContainerHighest}; + --status-picker-color: #{$surfaceContainerHigh}; + + --tooltip-color: #{$surfaceContainer}; + --popout-color: #{$surfaceContainer}; + --popout-color-alt: #{$surfaceContainerLowest}; + --popout-header-border: #{$onSurface}; + --separator-color: #{$outlineVariant}; + --sidebar-panel-color: #{$surfaceContainerLowest}; + --server-color: #{$surfaceContainerLow}; +} + +[class*="botTag_"] svg path { + fill: #{$onPrimary}; +} + +[class*="repliedTextContent_"] { + padding: 0px 5px 0px 5px !important; +} + +[class*="userPopoutOuter_"] { + border-radius: 16px !important; +} diff --git a/config/material-colors/templates/svg/brightness-onPrimary.svg b/config/material-colors/templates/svg/brightness-onPrimary.svg new file mode 100644 index 0000000..faa8d91 --- /dev/null +++ b/config/material-colors/templates/svg/brightness-onPrimary.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/config/material-colors/templates/svg/dark-onPrimary.svg b/config/material-colors/templates/svg/dark-onPrimary.svg new file mode 100644 index 0000000..08a4400 --- /dev/null +++ b/config/material-colors/templates/svg/dark-onPrimary.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/config/sddm/Main.qml b/config/sddm/Main.qml new file mode 100644 index 0000000..cb7b43b --- /dev/null +++ b/config/sddm/Main.qml @@ -0,0 +1,38 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Window 2.15 + +import "./components" + +Rectangle { + id: root + + height: Screen.height + width: Screen.width + + Image { + anchors { fill: parent } + + source: config.BgSource + fillMode: Image.PreserveAspectCrop + clip: true + } + + Item { + anchors { + fill: parent + margins: config.Padding + } + + DateTimePanel { + anchors { + top: parent.top + right: parent.right + } + } + + LoginPanel { + anchors { fill: parent } + } + } +} diff --git a/config/sddm/scripts/disable.sh b/config/sddm/scripts/disable.sh new file mode 100755 index 0000000..b95c874 --- /dev/null +++ b/config/sddm/scripts/disable.sh @@ -0,0 +1,12 @@ +#!/bin/bash +figlet "Disable SDDM" +if [ -f /etc/systemd/system/display-manager.service ]; then + if gum confirm "Do you want to disable the current display manager?"; then + sudo rm /etc/systemd/system/display-manager.service + echo ":: Current display manager removed." + echo ":: Please reboot your system." + fi +else + echo ":: No Display Manager enabled." +fi +sleep 3 diff --git a/config/sddm/scripts/enable.sh b/config/sddm/scripts/enable.sh new file mode 100755 index 0000000..6383ed6 --- /dev/null +++ b/config/sddm/scripts/enable.sh @@ -0,0 +1,12 @@ +#!/bin/bash +figlet "Enable SDDM" +if [ -f /etc/systemd/system/display-manager.service ]; then + echo ":: Display Manager is already enabled." +else + if gum confirm "Do you want to enable SDDM as your display manager?"; then + sudo systemctl enable sddm.service + echo ":: Display manager SDDM has been enabled." + echo ":: Please reboot your system!" + fi +fi +sleep 3 diff --git a/config/sddm/scripts/wallpaper.sh b/config/sddm/scripts/wallpaper.sh new file mode 100755 index 0000000..f8a0b36 --- /dev/null +++ b/config/sddm/scripts/wallpaper.sh @@ -0,0 +1,31 @@ +#!/bin/bash +cache_file="$HOME/.cache/current_wallpaper" +if [ ! -d /etc/sddm.conf.d/ ]; then + sudo mkdir /etc/sddm.conf.d + echo "Folder /etc/sddm.conf.d created." +fi + +cp $HOME/dotfiles/sddm/sddm.conf /etc/sddm.conf.d/ +cp $HOME/dotfiles/sddm/sddm.conf /etc/ +if [ "$1" != "init" ]; then + echo "File /etc/sddm.conf.d/sddm.conf updated." +fi + +current_wallpaper=$(cat "$cache_file") +extension="${current_wallpaper##*.}" + +cp $current_wallpaper /usr/share/sddm/themes/corners/backgrounds/current_wallpaper.$extension + +if [ "$1" != "init" ]; then + echo "Current wallpaper copied into /usr/share/sddm/themes/corners/backgrounds/" +fi + +new_wall=$(echo $current_wallpaper | sed "s|$HOME/wallpaper/||g") +cp $HOME/.cache/material/colors-sddm-style.conf /usr/share/sddm/themes/corners/theme.conf +sed -i 's/CURRENTWALLPAPER/'"current_wallpaper.$extension"'/' /usr/share/sddm/themes/corners/theme.conf + +if [ "$1" != "init" ]; then + echo "File theme.conf updated in /usr/share/sddm/themes/corners/" + + echo "DONE! Please logout to test sddm." +fi diff --git a/config/sddm/sddm.conf b/config/sddm/sddm.conf new file mode 100644 index 0000000..e459a7a --- /dev/null +++ b/config/sddm/sddm.conf @@ -0,0 +1,138 @@ +[Autologin] +# Whether sddm should automatically log back into sessions when they exit +Relogin=false + +# Name of session file for autologin session (if empty try last logged in) +Session= + +# Username for autologin session +User= + +[General] +# Which display server should be used. +# Valid values are: x11, x11-user, wayland. Wayland support is experimental +DisplayServer=x11 + +# Comma-separated list of environment variables to be set +GreeterEnvironment= + +# Halt command +HaltCommand=/usr/bin/systemctl poweroff + +# Input method module +InputMethod= + +# Comma-separated list of Linux namespaces for user session to enter +Namespaces= + +# Initial NumLock state. Can be on, off or none. +# If property is set to none, numlock won't be changed +# NOTE: Currently ignored if autologin is enabled. +Numlock=none + +# Reboot command +RebootCommand=/usr/bin/systemctl reboot + + +[Theme] +# Current theme name +Current=corners + +# Cursor size used in the greeter +CursorSize=6 + +# Cursor theme used in the greeter +CursorTheme=Bibata-Original-Classic + +# Number of users to use as threshold +# above which avatars are disabled +# unless explicitly enabled with EnableAvatars +DisableAvatarsThreshold=7 + +# Enable display of custom user avatars +EnableAvatars=false + +# Global directory for user avatars +# The files should be named .face.icon +FacesDir=/usr/share/sddm/faces + +# Font used in the greeter +Font= + +# Theme directory path +# ThemeDir=/usr/share/sddm/themes +ThemeDir=/usr/share/sddm/themes + +[Users] +# Default $PATH for logged in users +DefaultPath=/usr/local/sbin:/usr/local/bin:/usr/bin + +# Comma-separated list of shells. +# Users with these shells as their default won't be listed +HideShells= + +# Comma-separated list of users that should not be listed +HideUsers= + +# Maximum user id for displayed users +MaximumUid=60513 + +# Minimum user id for displayed users +MinimumUid=1000 + +# Remember the session of the last successfully logged in user +RememberLastSession=true + +# Remember the last successfully logged in user +RememberLastUser=true + +# When logging in as the same user twice, restore the original session, rather than create a new one +ReuseSession=true + + +[Wayland] +# Path of the Wayland compositor to execute when starting the greeter +CompositorCommand=weston --shell=fullscreen-shell.so + +# Enable Qt's automatic high-DPI scaling +EnableHiDPI=true + +# Path to a script to execute when starting the desktop session +SessionCommand=/usr/share/sddm/scripts/wayland-session + +# Comma-separated list of directories containing available Wayland sessions +SessionDir=/usr/local/share/wayland-sessions,/usr/share/wayland-sessions + +# Path to the user session log file +SessionLogFile=.local/share/sddm/wayland-session.log + + +[X11] +# Path to a script to execute when starting the display server +DisplayCommand=/usr/share/sddm/scripts/Xsetup + +# Path to a script to execute when stopping the display server +DisplayStopCommand=/usr/share/sddm/scripts/Xstop + +# Enable Qt's automatic high-DPI scaling +EnableHiDPI=true + +# Arguments passed to the X server invocation +ServerArguments=-nolisten tcp + +# Path to X server binary +ServerPath=/usr/bin/X + +# Path to a script to execute when starting the desktop session +SessionCommand=/usr/share/sddm/scripts/Xsession + +# Comma-separated list of directories containing available X sessions +SessionDir=/usr/local/share/xsessions,/usr/share/xsessions + +# Path to the user session log file +SessionLogFile=.local/share/sddm/xorg-session.log + +# Path to Xephyr binary +XephyrPath=/usr/bin/Xephyr + + diff --git a/config/wallpaper.py b/config/wallpaper.py new file mode 100644 index 0000000..1703b5a --- /dev/null +++ b/config/wallpaper.py @@ -0,0 +1,271 @@ +import os +import sys +import importlib.util +import argparse +import fcntl +import subprocess +import asyncio +import random as _random +import colorsys +import json +import inspect + + +def get_relative_path(path: str, level: int = 1) -> str: + prev_globals = inspect.stack()[level][0].f_globals + file_var = ( + os.path.dirname(os.path.abspath(prev_globals["__file__"])) + if "__file__" in prev_globals + else os.getcwd() + ) + return os.path.join(file_var, path) + + +lock_file_path = "/tmp/wallpaper.lock" +settings_file_path = get_relative_path("../config/assets/settings.json") + + +def acquire_lock(): + global lock_file + lock_file = open(lock_file_path, "w") + try: + fcntl.flock(lock_file, fcntl.LOCK_EX | fcntl.LOCK_NB) + lock_file.write(str(os.getpid())) + lock_file.flush() + except IOError: + print("Another instance of the script is already running.") + sys.exit(1) + + +def release_lock(): + fcntl.flock(lock_file, fcntl.LOCK_UN) + lock_file.close() + os.remove(lock_file_path) + + +def hue_to_numeric_hex(hue): + hue = hue / 360.0 + rgb = colorsys.hls_to_rgb(hue, 0.5, 1.0) + hex_color_str = "#{:02x}{:02x}{:02x}".format( + int(rgb[0] * 255), int(rgb[1] * 255), int(rgb[2] * 255) + ) + numeric_hex_color = int(hex_color_str.lstrip("#"), 16) + return numeric_hex_color + + +def load_settings(): + default_settings = { + "color-scheme": "dark", + "custom-color": "none", + "generation-scheme": "tonalSpot", + } + + if not os.path.exists(settings_file_path): + os.makedirs(os.path.dirname(settings_file_path), exist_ok=True) + with open(settings_file_path, "w") as file: + json.dump(default_settings, file, indent=4) + + with open(settings_file_path, "r") as file: + return json.load(file) + + +parser = argparse.ArgumentParser() +group = parser.add_mutually_exclusive_group(required=True) +group.add_argument("-I", "--image", type=str, help="Image") +group.add_argument( + "-P", + "--prev", + help="Use last used wallpaper for color generation", + action="store_true", +) +group.add_argument( + "-R", "--random", help="Random image from folder", action="store_true" +) +parser.add_argument("-n", "--notify", help="Send notifications", action="store_true") +parser.add_argument( + "--status", type=str, help="Status file", default="/tmp/wallpaper.status" +) + +args = parser.parse_args() + +random = args.random +prev = args.prev +image = args.image +notify = args.notify +status = args.status + +HOME = os.path.expanduser("~") + +module_path = get_relative_path("./material-colors") +sys.path.append(module_path) + +module_name = "generate" +file_path = f"{module_path}/{module_name}.py" + +spec = importlib.util.spec_from_file_location(module_name, file_path) +GENERATOR = importlib.util.module_from_spec(spec) # type: ignore +spec.loader.exec_module(GENERATOR) # type: ignore + +cache_file = f"{HOME}/.cache/current_wallpaper" +square = f"{HOME}/.cache/square_wallpaper.png" +png = f"{HOME}/.cache/current_wallpaper.png" + + +def current_state(state_str: str): + with open(status, "w") as f: + f.write(state_str) + + +def send_notify(label: str, desc: str): + if not notify: + return + subprocess.run(["notify-send", label, desc]) + + +def state(name: str | None, label: str | None, desc: str | None): + if name is not None: + current_state(name) + if label is not None: + send_notify(label, desc or "") + + +def join(*args): + return os.path.join(*args) + + +async def main(): + state("init", None, None) + + settings = load_settings() + color_scheme = settings["color-scheme"] + custom_color = settings["custom-color"] + generation_scheme = settings["generation-scheme"] + swww_animation = "wipe" + wallpaper_engine = "swww" + + new_wallpaper = f"{HOME}/Pictures/wallpapers/example-1.jpg" + # new_wallpaper = get_relative_path("../assets/wallpapers/1.png") + + if random: + files = [ + f + for f in os.listdir(f"{HOME}/Pictures/wallpapers/") + if f.endswith((".png", ".jpg", ".jpeg")) + ] + if files: + new_wallpaper = join(f"{HOME}/Pictures/wallpapers", _random.choice(files)) + elif prev: + try: + with open(cache_file) as f: + new_wallpaper = f.read().strip() + except FileNotFoundError: + pass + elif image is not None: + new_wallpaper = os.path.abspath(image) + + print(f":: Wallpaper {new_wallpaper}") + + with open(cache_file, "w") as f: + f.write(new_wallpaper) + + with_image = f"with image {new_wallpaper}" + + # ----------------------------------------------------- + # Set the new wallpaper + # ----------------------------------------------------- + + transition_type = swww_animation + + state("changing", "Changing wallpaper...", with_image) + print(":: Changing wallpaper...") + + if wallpaper_engine == "swww": + print(":: Using swww") + + cursor_pos = subprocess.getoutput("hyprctl cursorpos").replace(", ", ",") + + subprocess.run( + [ + "swww", + "img", + new_wallpaper, + "--transition-bezier", + ".43,1.19,1,.4", + "--transition-fps", + "60", + "--transition-type", + transition_type, + "--transition-duration", + "0.7", + "--transition-pos", + cursor_pos, + ] + ) + + else: + print(":: Wallpaper Engine disabled") + + # ----------------------------------------------------- + # Generate colors + # ----------------------------------------------------- + state("colors", "Generating colors...", with_image) + print(":: Generate colors") + _starts_with = lambda x: str(x).startswith("-") + + GENERATOR.main( + color_scheme, + new_wallpaper, + ( + hue_to_numeric_hex(int(custom_color.lstrip("-"))) + if _starts_with(custom_color) + else (int(custom_color.lstrip("#"), 16) if custom_color != "none" else None) + ), + scheme_name=generation_scheme, + ) + + # Generate square and png versions + await asyncio.gather(square_image(new_wallpaper), png_image(new_wallpaper)) + + state("finish", "Wallpaper procedure complete!", with_image) + + +async def png_image(wallpaper: str): + if wallpaper.lower().endswith((".jpg", ".jpeg")): + state(None, "Converting jpg to png...", f"with image {wallpaper}") + print(":: Converting jpg to png...") + cmd = f"magick {wallpaper}[0] {png}" + else: + cmd = f"cp -f {wallpaper} {png}" + + process = await asyncio.create_subprocess_shell( + cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE + ) + stdout, stderr = await process.communicate() + + if process.returncode != 0: + print(f":: Error while processing image: {stderr.decode()}") + else: + print(":: JPG successfully converted to PNG!") + + +async def square_image(wallpaper): + state(None, "Creating square version...", f"with image {wallpaper}") + print(":: Creating square version") + cmd = f"magick {wallpaper}[0] -gravity Center -extent 1:1 {square}" + process = await asyncio.create_subprocess_shell( + cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE + ) + stdout, stderr = await process.communicate() + + if process.returncode != 0: + print(f":: Error while processing image: {stderr.decode()}") + else: + print(":: Square image created!") + + +if __name__ == "__main__": + acquire_lock() + try: + asyncio.run(main()) + finally: + release_lock() diff --git a/config/wayland-idle-inhibitor.py b/config/wayland-idle-inhibitor.py new file mode 100755 index 0000000..c731f67 --- /dev/null +++ b/config/wayland-idle-inhibitor.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python + +import sys +from dataclasses import dataclass +from signal import SIGINT, SIGTERM, signal +from threading import Event +import setproctitle + +from pywayland.client.display import Display # type: ignore +from pywayland.protocol.idle_inhibit_unstable_v1.zwp_idle_inhibit_manager_v1 import ( # type: ignore + ZwpIdleInhibitManagerV1, +) +from pywayland.protocol.wayland.wl_compositor import WlCompositor # type: ignore +from pywayland.protocol.wayland.wl_registry import WlRegistryProxy # type: ignore +from pywayland.protocol.wayland.wl_surface import WlSurface # type: ignore + + +@dataclass +class GlobalRegistry: + surface: WlSurface | None = None + inhibit_manager: ZwpIdleInhibitManagerV1 | None = None + + +def handle_registry_global( + wl_registry: WlRegistryProxy, id_num: int, iface_name: str, version: int +) -> None: + global_registry: GlobalRegistry = wl_registry.user_data or GlobalRegistry() + + if iface_name == "wl_compositor": + compositor = wl_registry.bind(id_num, WlCompositor, version) + global_registry.surface = compositor.create_surface() # type: ignore + elif iface_name == "zwp_idle_inhibit_manager_v1": + global_registry.inhibit_manager = wl_registry.bind( + id_num, ZwpIdleInhibitManagerV1, version + ) + + +def main() -> None: + done = Event() + signal(SIGINT, lambda _, __: done.set()) + signal(SIGTERM, lambda _, __: done.set()) + + global_registry = GlobalRegistry() + + display = Display() + display.connect() + + registry = display.get_registry() # type: ignore + registry.user_data = global_registry + registry.dispatcher["global"] = handle_registry_global + + def shutdown() -> None: + display.dispatch() + display.roundtrip() + display.disconnect() + + display.dispatch() + display.roundtrip() + + if global_registry.surface is None or global_registry.inhibit_manager is None: + print("Wayland seems not to support idle_inhibit_unstable_v1 protocol.") + shutdown() + sys.exit(1) + + inhibitor = global_registry.inhibit_manager.create_inhibitor( # type: ignore + global_registry.surface + ) + + display.dispatch() + display.roundtrip() + + print("Inhibiting idle...") + done.wait() + print("Shutting down...") + + inhibitor.destroy() + + shutdown() + + +if __name__ == "__main__": + setproctitle.setproctitle("wayland-idle-inhibitor.py") + main() diff --git a/install.sh b/install.sh index 1d421d5..d805837 100644 --- a/install.sh +++ b/install.sh @@ -25,6 +25,7 @@ PACKAGES=( python-numpy python-pywayland python-pyxdg + sddm-theme-corners-git swww swappy wl-clipboard diff --git a/main.py b/main.py new file mode 100644 index 0000000..56baf28 --- /dev/null +++ b/main.py @@ -0,0 +1,69 @@ +import os + +import setproctitle +from fabric import Application +from fabric.utils import get_relative_path, monitor_file +from loguru import logger + +from modules.bar.bar import Bar +from modules.launcher.launcher import Launcher +from modules.notification_popup import NotificationPopup +from modules.osd import OSDContainer +from modules.corners import Corners +from services import sc + +for log in [ + "fabric.hyprland.widgets", + "fabric.audio.service", + "fabric.bluetooth.service", +]: + logger.disable(log) + + +def update_main_css(): + colors_css_path = f"/home/{os.getlogin()}/.cache/material/colors.css" + main_css_path = get_relative_path("styles/main.css") + + if os.path.exists(colors_css_path): + with open(main_css_path, "r+") as css_file: + content = css_file.read() + updated_content = content.replace("@COLORS_CSS_PATH", colors_css_path) + css_file.seek(0) + css_file.write(updated_content) + css_file.truncate() + else: + logger.warning(f"[Main] colors.css not found at {colors_css_path}") + + +def apply_style(app: Application): + logger.info("[Main] Applying CSS") + app.set_stylesheet_from_file(get_relative_path("styles/main.css")) + + +if __name__ == "__main__": + update_main_css() + sc = sc + bar = Bar() + corners = Corners() + osd = OSDContainer() + notif = NotificationPopup() + launcher = Launcher() + + app = Application( + "modus", + bar, + launcher, + osd, + ) + setproctitle.setproctitle("modus") + + # Monitor CSS files for changes + css_file = monitor_file(get_relative_path("styles")) + _ = css_file.connect("changed", lambda *_: apply_style(app)) + + color_css_file = monitor_file(f"/home/{os.getlogin()}/.cache/material/colors.css") + _ = color_css_file.connect("changed", lambda *_: apply_style(app)) + + # Apply the styles and run the application + apply_style(app) + app.run() diff --git a/modules/__pycache__/corners.cpython-313.pyc b/modules/__pycache__/corners.cpython-313.pyc new file mode 100644 index 0000000..cca949f Binary files /dev/null and b/modules/__pycache__/corners.cpython-313.pyc differ diff --git a/modules/__pycache__/notification_popup.cpython-313.pyc b/modules/__pycache__/notification_popup.cpython-313.pyc new file mode 100644 index 0000000..3f45a46 Binary files /dev/null and b/modules/__pycache__/notification_popup.cpython-313.pyc differ diff --git a/modules/__pycache__/osd.cpython-313.pyc b/modules/__pycache__/osd.cpython-313.pyc new file mode 100644 index 0000000..373fc64 Binary files /dev/null and b/modules/__pycache__/osd.cpython-313.pyc differ diff --git a/modules/bar/__pycache__/bar.cpython-313.pyc b/modules/bar/__pycache__/bar.cpython-313.pyc new file mode 100644 index 0000000..2eaaca3 Binary files /dev/null and b/modules/bar/__pycache__/bar.cpython-313.pyc differ diff --git a/modules/bar/bar.py b/modules/bar/bar.py new file mode 100644 index 0000000..acd5e2c --- /dev/null +++ b/modules/bar/bar.py @@ -0,0 +1,138 @@ +from fabric.hyprland.widgets import Language +from fabric.system_tray.widgets import SystemTray +from fabric.utils import FormattedString, bulk_replace +from fabric.widgets.box import Box +from fabric.widgets.button import Button +from fabric.widgets.centerbox import CenterBox +from fabric.widgets.datetime import DateTime +from fabric.widgets.label import Label +from fabric.widgets.wayland import WaylandWindow as Window +from gi.repository import GLib +from modules.bar.components import ( + BatteryLabel, + BluetoothIndicator, + IdleIndicator, + MicrophoneIndicator, + NetworkIndicator, + PowerProfileIndicator, + SystemInfo, + TaskBar, + VolumeIndicator, + workspace, +) +from snippets import MaterialIcon +from services import sc + + +class Bar(Window): + def __init__(self): + self.workspaces = Button(child=workspace, name="workspaces") + self.language = Language( + formatter=FormattedString( + "{replace_lang(language)}", + replace_lang=lambda lang: bulk_replace( + lang, + (r".*Eng.*", r".*Nep.*"), + ("en", "np"), + regex=True, + ), + ), + ) + + self.recording_indicator = Button( + name="recording-indicator", + child=MaterialIcon("screen_record", 14), + visible=False, + on_clicked=lambda *_: sc.screencast_stop(), + ) + + sc.connect( + "recording", lambda _, status: self.on_recording_status_change(status) + ) + + self.date_time = DateTime(formatters=["%-I:%M ๓ฐงž %a %d %b"], name="datetime") + + self.battery = BatteryLabel(name="battery") + self.volume = VolumeIndicator() + self.bluetooth = BluetoothIndicator() + self.network = NetworkIndicator() + self.microphone = MicrophoneIndicator() + self.idle = IdleIndicator() + self.taskbar = TaskBar() + self.info = SystemInfo() + self.powerindicator = PowerProfileIndicator() + self.tray = SystemTray(name="tray", icon_size=16, spacing=4) + self.launcher = Button( + name="logo", + child=Label(name="logo-name", label="๓ฐฃ‡ ๓ฐซฟ๓ฐซฐ๓ฐซต"), + on_clicked=lambda *_: GLib.spawn_command_line_async( + "fabric-cli exec modus 'launcher.open(\"launcher\")'" + ), + ) + + self.applets = Box( + name="applets", + spacing=4, + orientation="h", + children=[ + self.language, + self.battery, + self.powerindicator, + self.bluetooth, + self.network, + self.volume, + self.microphone, + self.idle, + ], + ) + + self.bar = CenterBox( + name="bar", + start_children=Box( + name="start-container", + spacing=8, + orientation="h", + children=[ + self.launcher, + self.workspaces, + self.info, + ], + ), + center_children=Box( + name="center-container", + spacing=8, + orientation="h", + children=self.taskbar, + ), + end_children=Box( + name="end-container", + spacing=8, + orientation="h", + children=[ + self.recording_indicator, + self.tray, + self.applets, + self.date_time, + ], + ), + ) + super().__init__( + layer="top", + anchor="left bottom right", + exclusivity="auto", + visible=True, + child=self.bar, + ) + + self.hidden = False + + def on_recording_status_change(self, status): + print(f"Recording status changed: {status}") + self.recording_indicator.set_visible(status) + + def toggle_hidden(self): + self.hidden = not self.hidden + if self.hidden: + self.bar.add_style_class("hidden") + else: + self.bar.remove_style_class("hidden") diff --git a/modules/bar/components/__init__.py b/modules/bar/components/__init__.py new file mode 100644 index 0000000..278c35f --- /dev/null +++ b/modules/bar/components/__init__.py @@ -0,0 +1,10 @@ +from .battery import * +from .bluetooth import * +from .idle import * +from .stats import * +from .microphone import * +from .network import * +from .powerprofile import * +from .taskbar import * +from .volume import * +from .workspace import * diff --git a/modules/bar/components/__pycache__/__init__.cpython-313.pyc b/modules/bar/components/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..cf8495f Binary files /dev/null and b/modules/bar/components/__pycache__/__init__.cpython-313.pyc differ diff --git a/modules/bar/components/__pycache__/battery.cpython-313.pyc b/modules/bar/components/__pycache__/battery.cpython-313.pyc new file mode 100644 index 0000000..84586f3 Binary files /dev/null and b/modules/bar/components/__pycache__/battery.cpython-313.pyc differ diff --git a/modules/bar/components/__pycache__/bluetooth.cpython-313.pyc b/modules/bar/components/__pycache__/bluetooth.cpython-313.pyc new file mode 100644 index 0000000..25afe78 Binary files /dev/null and b/modules/bar/components/__pycache__/bluetooth.cpython-313.pyc differ diff --git a/modules/bar/components/__pycache__/idle.cpython-313.pyc b/modules/bar/components/__pycache__/idle.cpython-313.pyc new file mode 100644 index 0000000..55b894d Binary files /dev/null and b/modules/bar/components/__pycache__/idle.cpython-313.pyc differ diff --git a/modules/bar/components/__pycache__/microphone.cpython-313.pyc b/modules/bar/components/__pycache__/microphone.cpython-313.pyc new file mode 100644 index 0000000..64023d5 Binary files /dev/null and b/modules/bar/components/__pycache__/microphone.cpython-313.pyc differ diff --git a/modules/bar/components/__pycache__/network.cpython-313.pyc b/modules/bar/components/__pycache__/network.cpython-313.pyc new file mode 100644 index 0000000..af3aa99 Binary files /dev/null and b/modules/bar/components/__pycache__/network.cpython-313.pyc differ diff --git a/modules/bar/components/__pycache__/powerprofile.cpython-313.pyc b/modules/bar/components/__pycache__/powerprofile.cpython-313.pyc new file mode 100644 index 0000000..a29d50f Binary files /dev/null and b/modules/bar/components/__pycache__/powerprofile.cpython-313.pyc differ diff --git a/modules/bar/components/__pycache__/recorder.cpython-313.pyc b/modules/bar/components/__pycache__/recorder.cpython-313.pyc new file mode 100644 index 0000000..c9af69f Binary files /dev/null and b/modules/bar/components/__pycache__/recorder.cpython-313.pyc differ diff --git a/modules/bar/components/__pycache__/stats.cpython-313.pyc b/modules/bar/components/__pycache__/stats.cpython-313.pyc new file mode 100644 index 0000000..737038d Binary files /dev/null and b/modules/bar/components/__pycache__/stats.cpython-313.pyc differ diff --git a/modules/bar/components/__pycache__/taskbar.cpython-313.pyc b/modules/bar/components/__pycache__/taskbar.cpython-313.pyc new file mode 100644 index 0000000..bf0830f Binary files /dev/null and b/modules/bar/components/__pycache__/taskbar.cpython-313.pyc differ diff --git a/modules/bar/components/__pycache__/volume.cpython-313.pyc b/modules/bar/components/__pycache__/volume.cpython-313.pyc new file mode 100644 index 0000000..36fe0f6 Binary files /dev/null and b/modules/bar/components/__pycache__/volume.cpython-313.pyc differ diff --git a/modules/bar/components/__pycache__/workspace.cpython-313.pyc b/modules/bar/components/__pycache__/workspace.cpython-313.pyc new file mode 100644 index 0000000..8842c1c Binary files /dev/null and b/modules/bar/components/__pycache__/workspace.cpython-313.pyc differ diff --git a/modules/bar/components/battery.py b/modules/bar/components/battery.py new file mode 100644 index 0000000..2dd55b8 --- /dev/null +++ b/modules/bar/components/battery.py @@ -0,0 +1,68 @@ +import psutil +from fabric import Fabricator +from fabric.widgets.box import Box +from fabric.widgets.overlay import Overlay +from fabric.widgets.circularprogressbar import CircularProgressBar +from snippets import MaterialIcon + + +class BatteryLabel(Box): + ICONS_CHARGING = [ + "battery_charging_20", + "battery_charging_20", + "battery_charging_20", + "battery_charging_30", + "battery_charging_30", + "battery_charging_50", + "battery_charging_60", + "battery_charging_80", + "battery_charging_80", + "battery_charging_90", + "battery_charging_full", + ] + ICONS_NOT_CHARGING = [ + "battery_alert", + "battery_1_bar", + "battery_1_bar", + "battery_2_bar", + "battery_2_bar", + "battery_3_bar", + "battery_4_bar", + "battery_4_bar", + "battery_5_bar", + "battery_6_bar", + "battery_full", + ] + + def __init__(self, **kwargs): + super().__init__(**kwargs) + Fabricator(interval=1000, poll_from=self.update_battery_status) + + def update_battery_status(self, *_): + battery = psutil.sensors_battery() + if battery is None: + self.hide() + return + is_charging = battery.power_plugged if battery else False + icons = self.ICONS_CHARGING if is_charging else self.ICONS_NOT_CHARGING + battery_percent = round(battery.percent) + index = min(max(battery_percent // 10, 0), 10) + battery_icon = MaterialIcon(icons[index], size=16) + + self.progress_bar = CircularProgressBar( + name="progress", line_style="round", line_width=1, size=24 + ) + + self.battery_overlay = Overlay(child=self.progress_bar, overlays=battery_icon) + + self.children = self.battery_overlay + + battery_percent = round(battery.percent) + self.progress_bar.value = battery_percent / 100 + # self.show() if battery_percent < 100 else self.hide() + + self.set_tooltip_text( + f"Battery: {battery_percent}%\nStatus: {'Charging' if is_charging else 'Discharging'}" + ) + + return True diff --git a/modules/bar/components/bluetooth.py b/modules/bar/components/bluetooth.py new file mode 100644 index 0000000..ffc4fec --- /dev/null +++ b/modules/bar/components/bluetooth.py @@ -0,0 +1,34 @@ +from fabric.bluetooth import BluetoothClient +from fabric.widgets.box import Box +from snippets import MaterialIcon + + +class BluetoothIndicator(Box): + ICON_ENABLED = "bluetooth" + ICON_DISABLED = "bluetooth_disabled" + + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.bluetooth_client = BluetoothClient() + + self.bluetooth_icon = None + + self.bluetooth_client.connect("changed", self.update_bluetooth_status) + + self.update_bluetooth_status() + + def update_bluetooth_status(self, *_): + icon_label = ( + self.ICON_ENABLED if self.bluetooth_client.enabled else self.ICON_DISABLED + ) + + new_icon = MaterialIcon(icon_label, size=16) + + if self.bluetooth_icon: + self.remove(self.bluetooth_icon) + + self.bluetooth_icon = new_icon + self.add(self.bluetooth_icon) + + def on_destroy(self): + self.bluetooth_client.disconnect("changed", self.update_bluetooth_status) diff --git a/modules/bar/components/idle.py b/modules/bar/components/idle.py new file mode 100644 index 0000000..6ad444a --- /dev/null +++ b/modules/bar/components/idle.py @@ -0,0 +1,27 @@ +from fabric import Fabricator +from fabric.utils import exec_shell_command +from fabric.widgets.box import Box +from snippets import MaterialIcon + + +class IdleIndicator(Box): + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.idle_icon = self.create_idle_icon() + self.fabricator = Fabricator(interval=1000, poll_from=self.check_idle_status) + + self.children = [self.idle_icon] + + def create_idle_icon(self): + return MaterialIcon("coffee", 16) + + def check_idle_status(self, *_): + output = exec_shell_command("pidof wayland-idle-inhibitor.py") + is_idle = bool(str(output).strip()) + self.update_idle_status(is_idle) + + def update_idle_status(self, is_idle): + if is_idle: + self.idle_icon.set_visible(True) + else: + self.idle_icon.set_visible(False) diff --git a/modules/bar/components/microphone.py b/modules/bar/components/microphone.py new file mode 100644 index 0000000..a89baf7 --- /dev/null +++ b/modules/bar/components/microphone.py @@ -0,0 +1,33 @@ +from fabric.audio.service import Audio +from fabric.widgets.box import Box +from snippets import MaterialIcon + + +class MicrophoneIndicator(Box): + + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.audio_service = Audio() + self.microphone_icon = self.create_microphone_icon() + + self.audio_service.connect("microphone_changed", self.update_microphone_status) + self.update_microphone_status() + + self.children = (self.microphone_icon,) + + def create_microphone_icon(self): + return MaterialIcon("mic_off", size=16) + + def update_microphone_status(self, *args): + current_microphone = self.audio_service.microphone + + if current_microphone: + is_muted = current_microphone.muted + icon_name = "mic_off" if is_muted else "mic" + + self.microphone_icon.set_label(icon_name) + self.microphone_icon.set_visible(is_muted) + else: + self.microphone_icon.set_visible(False) + + return True diff --git a/modules/bar/components/network.py b/modules/bar/components/network.py new file mode 100644 index 0000000..3407134 --- /dev/null +++ b/modules/bar/components/network.py @@ -0,0 +1,84 @@ +import subprocess + +import psutil +from fabric import Fabricator +from fabric.widgets.box import Box +from snippets import MaterialIcon + + +class NetworkIndicator(Box): + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.update_interval = 5000 # Increased interval to 5 seconds + self.last_status = None + self.wifi_strength_cache = None + self.fabricator = Fabricator( + interval=self.update_interval, + poll_from=self.update_network_status, + ) + + def update_network_status(self, *_): + current_status = self.get_network_status() + if current_status != self.last_status: + self.update_icon(current_status) + self.last_status = current_status + return True + + def update_icon(self, current_status): + self.icon_label = MaterialIcon(current_status, size=16) + self.children = (self.icon_label,) + + def get_network_status(self): + if self.is_ethernet_connected(): + return "lan" + if self.is_wifi_connected(): + return self.get_wifi_strength_icon() + return "signal_wifi_off" + + def get_wifi_strength_icon(self): + wifi_strength = self.check_wifi_strength() + if wifi_strength is not None: + self.wifi_strength_cache = wifi_strength # Cache the result + else: + wifi_strength = self.wifi_strength_cache # Use cached value if available + + return self.wifi_signal_icon(wifi_strength) + + def wifi_signal_icon(self, strength): + if strength is None or strength <= -100: + return "signal_wifi_0_bar" + if strength > -50: + return "signal_wifi_4_bar" + if strength > -60: + return "network_wifi_3_bar" + if strength > -70: + return "network_wifi_2_bar" + return "network_wifi_1_bar" + + def check_wifi_strength(self): + try: + result = subprocess.run(["iwconfig"], capture_output=True, text=True) + for line in result.stdout.splitlines(): + if "Signal level" in line: + signal_level = line.split("Signal level=")[1].split(" ")[0] + return int(signal_level.strip().replace("dBm", "")) + return None + except Exception: + return None + + def is_wifi_connected(self): + return any( + self._is_interface_up(interface, ["wlan", "wifi"]) + for interface in psutil.net_if_addrs() + ) + + def is_ethernet_connected(self): + return any( + self._is_interface_up(interface, ["enp", "eth"]) + for interface in psutil.net_if_addrs() + ) + + def _is_interface_up(self, interface, keywords): + if any(substring in interface.lower() for substring in keywords): + return psutil.net_if_stats().get(interface, {}).isup + return False diff --git a/modules/bar/components/powerprofile.py b/modules/bar/components/powerprofile.py new file mode 100644 index 0000000..a1817cc --- /dev/null +++ b/modules/bar/components/powerprofile.py @@ -0,0 +1,44 @@ +from fabric import Fabricator +from fabric.utils import exec_shell_command +from fabric.widgets.box import Box +from snippets import MaterialIcon + +PROFILE_ICONS = { + "power-saver": "data_saver_on", + "balanced": None, + "performance": "mode_heat", +} + + +class PowerProfileIndicator(Box): + def __init__(self, **kwargs): + super().__init__(**kwargs) + + self.profile_icon = None + + self.fabricator = Fabricator(interval=1000, poll_from=self.update_power_profile) + + def update_power_profile(self, *_): + profile = self.get_power_profile() + self.update_icon(profile) + return True + + def get_power_profile(self): + result = exec_shell_command("powerprofilesctl get") + return result.strip() or "power-saver" + + def update_icon(self, profile): + icon_name = PROFILE_ICONS.get(profile) + + new_icon = MaterialIcon(icon_name, 16) if icon_name else None + + if self.profile_icon: + self.remove(self.profile_icon) + + if new_icon: + self.profile_icon = new_icon + self.add(self.profile_icon) + else: + if self.profile_icon: + self.profile_icon.hide() + self.profile_icon = None diff --git a/modules/bar/components/stats.py b/modules/bar/components/stats.py new file mode 100644 index 0000000..6471e76 --- /dev/null +++ b/modules/bar/components/stats.py @@ -0,0 +1,94 @@ +from time import sleep +import psutil +from fabric import Fabricator +from fabric.widgets.box import Box +from fabric.widgets.button import Button +from fabric.widgets.circularprogressbar import CircularProgressBar +from fabric.widgets.label import Label +from fabric.widgets.overlay import Overlay +from snippets import MaterialIcon + + +class SystemInfo(Button): + ICONS = [ + ("CPU", "settings_motion_mode"), + ("Swap", "swap_horiz"), + ("RAM", "memory_alt"), + ("Temp", "thermometer"), + ] + + def __init__(self): + super().__init__(child=self._initialize_content()) + + Fabricator( + poll_from=self._refresh_system_info, + stream=True, + on_changed=lambda fab, data: self._update_ui(data), + ) + + def _initialize_content(self): + self.box = Box(orientation="h", name="tray") + self.progress_bars = {} + self.labels = {} + self.icons = {} + + for system, icon_name in self.ICONS: + self._create_system_item(system, icon_name) + + return self.box + + def _create_system_item(self, system, icon_name): + self.progress_bars[system] = self._create_progress_bar() + self.labels[system] = self._create_label() + self.icons[system] = self._create_icon(icon_name) + + overlay = Overlay( + child=self.progress_bars[system], overlays=[self.icons[system]] + ) + self.box.pack_start(overlay, False, False, 0) + self.box.pack_start(self.labels[system], False, False, 0) + + def _create_progress_bar(self): + return CircularProgressBar( + name="progress", line_style="round", line_width=1, size=24 + ) + + def _create_label(self): + return Label(style="font-size:14px; margin: 4px;") + + def _create_icon(self, icon_name): + return MaterialIcon(icon_name, size=16) + + def _refresh_system_info(self, fab: Fabricator): + while True: + yield { + "CPU": round(psutil.cpu_percent()), + "RAM": round(psutil.virtual_memory().percent), + "Swap": round(psutil.swap_memory().percent), + "Temp": self._get_device_temperature(), + } + sleep(1) + + @staticmethod + def _get_device_temperature(): + try: + temps = psutil.sensors_temperatures() + if "coretemp" in temps: + return round(temps["coretemp"][0].current, 1) + elif "cpu_thermal" in temps: + return round(temps["cpu_thermal"][0].current, 1) + return None + except Exception: + return None + + def _update_ui(self, usages): + for system, usage in usages.items(): + if usage is None: + usage = 0 + self.labels[system].set_label("N/A") + else: + usage = int(usage) + + self.progress_bars[system].value = usage / 100 + self.labels[system].set_label(f"{usage}") + self.progress_bars[system].set_tooltip_text(f"{system} Usage: {usage}%") diff --git a/modules/bar/components/taskbar.py b/modules/bar/components/taskbar.py new file mode 100644 index 0000000..cc8af45 --- /dev/null +++ b/modules/bar/components/taskbar.py @@ -0,0 +1,154 @@ +import json +import os +from typing import TypedDict + +from fabric.hyprland.widgets import get_hyprland_connection +from fabric.utils import exec_shell_command_async +from fabric.widgets.box import Box +from fabric.widgets.button import Button +from fabric.widgets.image import Image +from gi.repository import GdkPixbuf, GLib, Gtk +from loguru import logger + + +class PagerClient(TypedDict): + title: str + initialClass: str + mapped: bool + hidden: bool + address: str + + +class TaskBar(Box): + def __init__(self, icon_size: int = 20, **kwargs): + super().__init__( + orientation="h", + spacing=7, + name="taskbar", + **kwargs, + ) + self.connection = get_hyprland_connection() + self.icon_size = icon_size + self.icon_theme = Gtk.IconTheme.get_default() + + self.set_visible(False) + + if self.connection.ready: + self.render_with_delay() + else: + self.connection.connect("event::ready", lambda *_: self.render_with_delay()) + + for event in ( + "activewindow", + "openwindow", + "closewindow", + "changefloatingmode", + ): + self.connection.connect("event::" + event, lambda *_: self.render(None)) + + def render_with_delay(self, *_): + GLib.timeout_add(101, self.render) + + def render(self, *_): + self.children = [] + + clients = self.fetch_clients() + active_window_address = self.get_active_window_address() + + visible_clients = [ + client for client in clients if client["mapped"] and not client["hidden"] + ] + + if visible_clients: + for client in visible_clients: + window_class = client["initialClass"].lower() + icon = self.bake_window_icon(window_class) + + button = Button(image=icon, tooltip_text=client["title"]) + if client["address"] == active_window_address: + button.add_style_class("activated") + button.connect( + "button-press-event", + self.on_icon_click, + client["address"], + ) + + self.add(button) + self.set_visible(True) + self.show_all() + else: + self.set_visible(False) + + self.set_visible(len(self.children) > 0) + if self.children: + self.show_all() + + def get_active_window_address(self) -> str: + active_window_info = json.loads( + self.connection.send_command("j/activewindow").reply.decode() + ) + return active_window_info.get("address", "") + + def on_icon_click(self, widget, event, address): + exec_shell_command_async( + f"hyprctl dispatch focuswindow address:{address}", lambda *_: None + ) + + def fetch_clients(self) -> list[PagerClient]: + return json.loads(self.connection.send_command("j/clients").reply.decode()) + + def bake_window_icon( + self, + window_class: str, + fallback_icon: str = "image-missing", + ) -> Image: + icon_name = self.get_icon_from_desktop_entry(window_class) + + if icon_name: + pixbuf = self.load_icon(icon_name) + else: + pixbuf = self.load_icon(window_class, fallback_icon) + + return Image(pixbuf=pixbuf, size=self.icon_size) + + def get_icon_from_desktop_entry(self, window_class: str) -> str: + for data_dir in GLib.get_system_data_dirs(): + applications_dir = os.path.join(data_dir, "applications") + + if os.path.isdir(applications_dir): + for desktop_file in os.listdir(applications_dir): + if desktop_file.endswith(".desktop"): + file_path = os.path.join(applications_dir, desktop_file) + try: + with open(file_path) as f: + app_name = None + icon_name = None + for line in f: + if line.startswith("Name="): + app_name = line.split("=", 2)[1].strip().lower() + elif line.startswith("Icon=") and app_name: + icon_name = line.split("=", 2)[1].strip() + if window_class in app_name: + return icon_name + except Exception as e: + logger.error(f"Error reading {file_path}: {e}") + return None + + def load_icon( + self, + icon_name: str, + fallback_icon: str = "image-missing", + ) -> GdkPixbuf.Pixbuf: + try: + pixbuf = self.icon_theme.load_icon( + icon_name, + self.icon_size, + Gtk.IconLookupFlags.FORCE_SIZE, + ) + except Exception: + pixbuf = self.icon_theme.load_icon( + fallback_icon, + self.icon_size, + Gtk.IconLookupFlags.FORCE_SIZE, + ) + return pixbuf diff --git a/modules/bar/components/volume.py b/modules/bar/components/volume.py new file mode 100644 index 0000000..0fed57c --- /dev/null +++ b/modules/bar/components/volume.py @@ -0,0 +1,81 @@ +from fabric import Fabricator +from fabric.utils import exec_shell_command +from fabric.widgets.box import Box +from fabric.widgets.button import Button +from snippets import MaterialIcon +from services import audio + +VOLUME_ICONS = { + "muted": "volume_off", + "low": "volume_mute", + "medium-low": "volume_down", + "medium-high": "volume_up", + "high": "volume_up", +} + + +class VolumeIndicator(Box): + def __init__(self, **kwargs): + super().__init__(**kwargs) + + self.audio_service = audio + self.volume_icon_button = self.create_volume_icon() + + self.volume_icon_button.connect("button-press-event", self.on_button_press) + self.audio_service.connect("changed", self.update_volume_status) + + Fabricator(interval=1000, poll_from=self.update_volume_status) + self.children = (self.volume_icon_button,) + + def create_volume_icon(self): + icon = MaterialIcon(VOLUME_ICONS["muted"], size=16) + button = Button(child=icon) + button.icon_widget = icon + return button + + def on_button_press(self, widget, event): + if event.button == 1: + self.toggle_mute() + elif event.button == 3: + self.open_audio_settings() + + def toggle_mute(self): + current_stream = self.audio_service.speaker + if current_stream: + current_stream.muted = not current_stream.muted + # self.update_volume_status() + + def open_audio_settings(self): + exec_shell_command("pavucontrol") + + def update_volume_status(self, *_): + current_stream = self.audio_service.speaker + if current_stream: + self.update_icon(current_stream) + + return True + + def update_icon(self, stream): + if stream.muted: + icon_name = VOLUME_ICONS["muted"] + volume_percentage = 0 + tooltip_text = "Volume Muted" + else: + volume_level = stream.volume + icon_name = self.get_volume_icon(volume_level) + volume_percentage = int(volume_level) + tooltip_text = f"Volume: {volume_percentage}%" + + self.volume_icon_button.icon_widget.set_label(icon_name) + + self.volume_icon_button.set_tooltip_text(tooltip_text) + + def get_volume_icon(self, volume_level): + if volume_level == 0: + return VOLUME_ICONS["low"] + elif volume_level <= 33: + return VOLUME_ICONS["medium-low"] + elif volume_level <= 66: + return VOLUME_ICONS["medium-high"] + else: + return VOLUME_ICONS["high"] diff --git a/modules/bar/components/workspace.py b/modules/bar/components/workspace.py new file mode 100644 index 0000000..f13372d --- /dev/null +++ b/modules/bar/components/workspace.py @@ -0,0 +1,19 @@ +import json + +from fabric.hyprland.service import Hyprland +from fabric.widgets.label import Label + +connection = Hyprland() + +workspaceData = connection.send_command("j/activeworkspace").reply +activeWorkspace = json.loads(workspaceData.decode("utf-8"))["name"] +workspace = Label(label=f"Workspace {activeWorkspace}") + + +def on_workspace(obj, signal): + global activeWorkspace + activeWorkspace = json.loads(signal.data[0]) + workspace.set_label(f"Workspace {activeWorkspace}") + + +connection.connect("event::workspace", on_workspace) diff --git a/modules/corners.py b/modules/corners.py new file mode 100644 index 0000000..10ce26d --- /dev/null +++ b/modules/corners.py @@ -0,0 +1,64 @@ +from fabric.widgets.box import Box +from fabric.widgets.wayland import WaylandWindow as Window +from fabric.widgets.shapes import Corner + + +class MyCorner(Box): + def __init__(self, corner): + super().__init__( + name="corner-container", + children=Corner( + name="corner", + orientation=corner, + size=20, + ), + ) + + +class Corners(Window): + def __init__(self): + super().__init__( + name="corners", + layer="top", + anchor="top bottom left right", + exclusivity="normal", + pass_through=True, + visible=False, + all_visible=False, + ) + + self.all_corners = Box( + name="all-corners", + orientation="v", + h_expand=True, + v_expand=True, + h_align="fill", + v_align="fill", + children=[ + Box( + name="top-corners", + orientation="h", + h_align="fill", + children=[ + MyCorner("top-left"), + Box(h_expand=True), + MyCorner("top-right"), + ], + ), + Box(v_expand=True), + Box( + name="bottom-corners", + orientation="h", + h_align="fill", + children=[ + MyCorner("bottom-left"), + Box(h_expand=True), + MyCorner("bottom-right"), + ], + ), + ], + ) + + self.add(self.all_corners) + + self.show_all() diff --git a/modules/launcher/__pycache__/launcher.cpython-313.pyc b/modules/launcher/__pycache__/launcher.cpython-313.pyc new file mode 100644 index 0000000..23b3668 Binary files /dev/null and b/modules/launcher/__pycache__/launcher.cpython-313.pyc differ diff --git a/modules/launcher/components/__init__.py b/modules/launcher/components/__init__.py new file mode 100644 index 0000000..24ba7fe --- /dev/null +++ b/modules/launcher/components/__init__.py @@ -0,0 +1,9 @@ +from .applauncher import * +from .bluetooth import * +from .cliphist import * +from .dashboard import * +from .emoji import * +from .power import * +from .sh import * +from .todo import * +from .wallpapers import * diff --git a/modules/launcher/components/__pycache__/__init__.cpython-313.pyc b/modules/launcher/components/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..1b2d266 Binary files /dev/null and b/modules/launcher/components/__pycache__/__init__.cpython-313.pyc differ diff --git a/modules/launcher/components/__pycache__/applauncher.cpython-313.pyc b/modules/launcher/components/__pycache__/applauncher.cpython-313.pyc new file mode 100644 index 0000000..4d47aaa Binary files /dev/null and b/modules/launcher/components/__pycache__/applauncher.cpython-313.pyc differ diff --git a/modules/launcher/components/__pycache__/bluetooth.cpython-313.pyc b/modules/launcher/components/__pycache__/bluetooth.cpython-313.pyc new file mode 100644 index 0000000..6ffa4bd Binary files /dev/null and b/modules/launcher/components/__pycache__/bluetooth.cpython-313.pyc differ diff --git a/modules/launcher/components/__pycache__/cliphist.cpython-313.pyc b/modules/launcher/components/__pycache__/cliphist.cpython-313.pyc new file mode 100644 index 0000000..828765b Binary files /dev/null and b/modules/launcher/components/__pycache__/cliphist.cpython-313.pyc differ diff --git a/modules/launcher/components/__pycache__/dashboard.cpython-313.pyc b/modules/launcher/components/__pycache__/dashboard.cpython-313.pyc new file mode 100644 index 0000000..ba4f55c Binary files /dev/null and b/modules/launcher/components/__pycache__/dashboard.cpython-313.pyc differ diff --git a/modules/launcher/components/__pycache__/emoji.cpython-313.pyc b/modules/launcher/components/__pycache__/emoji.cpython-313.pyc new file mode 100644 index 0000000..b9282dc Binary files /dev/null and b/modules/launcher/components/__pycache__/emoji.cpython-313.pyc differ diff --git a/modules/launcher/components/__pycache__/network.cpython-313.pyc b/modules/launcher/components/__pycache__/network.cpython-313.pyc new file mode 100644 index 0000000..113557f Binary files /dev/null and b/modules/launcher/components/__pycache__/network.cpython-313.pyc differ diff --git a/modules/launcher/components/__pycache__/power.cpython-313.pyc b/modules/launcher/components/__pycache__/power.cpython-313.pyc new file mode 100644 index 0000000..e36f763 Binary files /dev/null and b/modules/launcher/components/__pycache__/power.cpython-313.pyc differ diff --git a/modules/launcher/components/__pycache__/sh.cpython-313.pyc b/modules/launcher/components/__pycache__/sh.cpython-313.pyc new file mode 100644 index 0000000..1ed1902 Binary files /dev/null and b/modules/launcher/components/__pycache__/sh.cpython-313.pyc differ diff --git a/modules/launcher/components/__pycache__/todo.cpython-313.pyc b/modules/launcher/components/__pycache__/todo.cpython-313.pyc new file mode 100644 index 0000000..8bfe708 Binary files /dev/null and b/modules/launcher/components/__pycache__/todo.cpython-313.pyc differ diff --git a/modules/launcher/components/__pycache__/wallpapers.cpython-313.pyc b/modules/launcher/components/__pycache__/wallpapers.cpython-313.pyc new file mode 100644 index 0000000..1bc90dd Binary files /dev/null and b/modules/launcher/components/__pycache__/wallpapers.cpython-313.pyc differ diff --git a/modules/launcher/components/__pycache__/window_switcher.cpython-313.pyc b/modules/launcher/components/__pycache__/window_switcher.cpython-313.pyc new file mode 100644 index 0000000..c461453 Binary files /dev/null and b/modules/launcher/components/__pycache__/window_switcher.cpython-313.pyc differ diff --git a/modules/launcher/components/applauncher.py b/modules/launcher/components/applauncher.py new file mode 100644 index 0000000..4e755ae --- /dev/null +++ b/modules/launcher/components/applauncher.py @@ -0,0 +1,240 @@ +from collections.abc import Iterator +import operator +from fabric.utils import DesktopApp, get_desktop_applications, idle_add, remove_handler +from fabric.widgets.box import Box +from fabric.widgets.button import Button +from fabric.widgets.entry import Entry +from fabric.widgets.label import Label +from fabric.widgets.scrolledwindow import ScrolledWindow +from gi.repository import GLib, Gdk +from snippets import read_config +from fabric.widgets.image import Image + + +class AppLauncher(Box): + def __init__(self, **kwargs): + super().__init__( + name="app-launcher", + visible=False, + all_visible=False, + **kwargs, + ) + + self.launcher = kwargs["launcher"] + self.selected_index = -1 + + self._arranger_handler: int = 0 + self._all_apps = get_desktop_applications() + self.config = read_config() + + self.viewport = Box(name="viewport", spacing=4, orientation="v") + self.search_entry = Entry( + name="search-entry", + h_expand=True, + notify_text=lambda entry, *_: self.arrange_viewport(entry.get_text()), + on_activate=lambda entry, *_: self.on_search_entry_activate( + entry.get_text() + ), + on_key_press_event=self.on_search_entry_key_press, # Handle key presses + ) + self.scrolled_window = ScrolledWindow( + name="scrolled-window", + spacing=10, + h_scrollbar_policy="never", + v_scrollbar_policy="never", + child=self.viewport, + ) + + self.header_box = Box( + name="header_box", + orientation="h", + children=[self.search_entry], + ) + + self.launcher_box = Box( + name="launcher-box", + h_expand=True, + orientation="v", + children=[ + self.header_box, + self.scrolled_window, + ], + ) + + self.resize_viewport() + + self.add(self.launcher_box) + self.show_all() + + def close_launcher(self, *_): + self.viewport.children = [] + self.selected_index = -1 # Reset selection + self.launcher.close() + + def open_launcher(self): + self._all_apps = get_desktop_applications() + self.arrange_viewport() + self.show_all() + + def arrange_viewport(self, query: str = ""): + remove_handler(self._arranger_handler) if self._arranger_handler else None + self.viewport.children = [] + self.selected_index = -1 + + if not query.strip(): + return False + + filtered_apps = iter( + sorted( + [ + app + for app in self._all_apps + if query.casefold() + in ( + (app.display_name or "") + + (" " + app.name + " ") + + (app.generic_name or "") + ).casefold() + ][:6], + key=lambda app: (app.display_name or "").casefold(), + ) + ) + should_resize = operator.length_hint(filtered_apps) == len(self._all_apps) + + self._arranger_handler = idle_add( + lambda apps_iter: self.add_next_application(apps_iter) + or self.handle_arrange_complete(should_resize, query), + filtered_apps, + pin=True, + ) + + def handle_arrange_complete(self, should_resize, query): + if should_resize: + self.resize_viewport() + # Only auto-select first item if query exists + if query.strip() != "" and self.viewport.get_children(): + self.update_selection(0) + return False + + def add_next_application(self, apps_iter: Iterator[DesktopApp]): + if not (app := next(apps_iter, None)): + return False + + self.viewport.add(self.bake_application_slot(app)) + return True + + def resize_viewport(self): + self.scrolled_window.set_min_content_width( + self.viewport.get_allocation().width # type: ignore + ) + return False + + def bake_application_slot(self, app: DesktopApp, **kwargs) -> Button: + button = Button( + name="launcher-app", + child=Box( + orientation="h", + children=[ + Image( + pixbuf=app.get_icon_pixbuf(size=32), + h_align="start", + name="launcher-app-icon", + ), + Label( + label=app.display_name or "Unknown", + ellipsization="end", + v_align="center", + h_align="center", + ), + ], + ), + tooltip_text=app.description, + on_clicked=lambda *_: (app.launch(), self.close_launcher()), + **kwargs, + ) + return button + + def update_selection(self, new_index: int): + # Unselect current + if self.selected_index != -1 and self.selected_index < len( + self.viewport.get_children() + ): + current_button = self.viewport.get_children()[self.selected_index] + current_button.get_style_context().remove_class("selected") + # Select new + if new_index != -1 and new_index < len(self.viewport.get_children()): + new_button = self.viewport.get_children()[new_index] + new_button.get_style_context().add_class("selected") + self.selected_index = new_index + self.scroll_to_selected(new_button) + else: + self.selected_index = -1 + + def scroll_to_selected(self, button): + def scroll(): + adj = self.scrolled_window.get_vadjustment() + alloc = button.get_allocation() + if alloc.height == 0: + return False # Retry if allocation isn't ready + + y = alloc.y + height = alloc.height + page_size = adj.get_page_size() + current_value = adj.get_value() + + # Calculate visible boundaries + visible_top = current_value + visible_bottom = current_value + page_size + + if y < visible_top: + # Item above viewport - align to top + adj.set_value(y) + elif y + height > visible_bottom: + # Item below viewport - align to bottom + new_value = y + height - page_size + adj.set_value(new_value) + # No action if already fully visible + return False + + GLib.idle_add(scroll) + + def on_search_entry_activate(self, text): + commands = self.config.get("commands", {}) + + if text in commands: + command = commands[text] + eval(command, {"launcher": self.launcher}) # Dynamically execute command + else: + children = self.viewport.get_children() + if children: + # Only activate if we have selection or non-empty query + if text.strip() == "" and self.selected_index == -1: + return # Prevent accidental activation when empty + selected_index = self.selected_index if self.selected_index != -1 else 0 + if 0 <= selected_index < len(children): + children[selected_index].clicked() + + def on_search_entry_key_press(self, widget, event): + keyval = event.keyval + if keyval == Gdk.KEY_Down: + self.move_selection(1) + return True + elif keyval == Gdk.KEY_Up: + self.move_selection(-1) + return True + elif keyval == Gdk.KEY_Escape: + self.close_launcher() + return True + return False + + def move_selection(self, delta: int): + children = self.viewport.get_children() + if not children: + return + # Allow starting selection from nothing when empty + if self.selected_index == -1 and delta == 1: + new_index = 0 + else: + new_index = self.selected_index + delta + new_index = max(0, min(new_index, len(children) - 1)) + self.update_selection(new_index) diff --git a/modules/launcher/components/bluetooth.py b/modules/launcher/components/bluetooth.py new file mode 100644 index 0000000..aa82591 --- /dev/null +++ b/modules/launcher/components/bluetooth.py @@ -0,0 +1,132 @@ +from fabric.widgets.box import Box +from fabric.widgets.label import Label +from fabric.widgets.image import Image +from fabric.widgets.button import Button +from fabric.widgets.centerbox import CenterBox +from fabric.widgets.scrolledwindow import ScrolledWindow +from fabric.bluetooth import BluetoothClient, BluetoothDevice +from snippets import MaterialIcon + + +class BluetoothDeviceSlot(CenterBox): + def __init__(self, device: BluetoothDevice, **kwargs): + super().__init__(name="bluetooth-device", **kwargs) + self.device = device + self.device.connect("changed", self.on_changed) + self.device.connect( + "notify::closed", lambda *_: self.device.closed and self.destroy() + ) + + self.connection_icon = MaterialIcon("bluetooth_disabled", 12) + self.connect_button = Button( + name="bluetooth-connect", + label="Connect", + on_clicked=lambda *_: self.device.set_connecting(not self.device.connected), + ) + + self.start_children = [ + Box( + spacing=8, + children=[ + Image(icon_name=device.icon_name + "-symbolic", size=32), + Label(label=device.name), + self.connection_icon, + ], + ) + ] + self.end_children = self.connect_button + + self.device.emit("changed") # to update display status + + def on_changed(self, *_): + self.connection_icon.set_label( + "bluetooth" if self.device.connected else "bluetooth_disabled" + ) + + if self.device.connecting: + self.connect_button.set_label( + "Connecting..." if not self.device.connecting else "Disconnecting..." + ) + else: + self.connect_button.set_label( + "Connect" if not self.device.connected else "Disconnect" + ) + return + + +class BluetoothConnections(Box): + def __init__(self, **kwargs): + super().__init__( + name="bluetooth", + spacing=8, + orientation="vertical", + **kwargs, + ) + + self.client = BluetoothClient(on_device_added=self.on_device_added) + self.toggle_icon = MaterialIcon("bluetooth_disabled") + self.scan_icon = MaterialIcon("stop") + + self.scan_button = Button( + name="bluetooth-scan", + child=self.scan_icon, + tooltip_text="scan", + on_clicked=lambda *_: self.client.toggle_scan(), + ) + self.toggle_button = Button( + name="bluetooth-toggle", + tooltip_text="bluetooth-toggle", + child=self.toggle_icon, + on_clicked=lambda *_: self.client.toggle_power(), + ) + + self.client.connect( + "notify::enabled", + lambda *_: self.toggle_icon.set_label( + "bluetooth" if self.client.enabled else "bluetooth_disabled" + ), + ) + self.client.connect( + "notify::scanning", + lambda *_: self.scan_icon.set_label( + "stop" if self.client.scanning else "bluetooth_searching" + ), + ) + + self.paired_box = Box(spacing=2, orientation="vertical") + self.available_box = Box(spacing=2, orientation="vertical") + + self.children = [ + CenterBox( + name="bluetooth-header", + start_children=self.scan_button, + center_children=Label(name="bluetooth-text", label="Bluetooth Devices"), + end_children=self.toggle_button, + ), + Label(name="bluetooth-section", label="Paired"), + ScrolledWindow( + name="bluetooth-paired", + v_expand=True, + child=self.paired_box, + ), + Label(name="bluetooth-section", label="Available"), + ScrolledWindow( + name="bluetooth-paired", + v_expand=True, + child=self.available_box, + ), + ] + + # to run notify closures thus display the status + # without having to wait until an actual change + self.client.notify("scanning") + self.client.notify("enabled") + + def on_device_added(self, client: BluetoothClient, address: str): + if not (device := client.get_device(address)): + return + slot = BluetoothDeviceSlot(device) + + if device.paired: + return self.paired_box.add(slot) + return self.available_box.add(slot) diff --git a/modules/launcher/components/cliphist.py b/modules/launcher/components/cliphist.py new file mode 100644 index 0000000..6ccf863 --- /dev/null +++ b/modules/launcher/components/cliphist.py @@ -0,0 +1,205 @@ +import os +import subprocess +from typing import List + +from fabric import Fabricator +from fabric.utils import remove_handler +from fabric.widgets.box import Box +from fabric.widgets.button import Button +from fabric.widgets.entry import Entry +from fabric.widgets.label import Label +from fabric.widgets.scrolledwindow import ScrolledWindow +from gi.repository import GLib +from snippets import MaterialIcon + + +class Cliphist(Box): + def __init__(self, **kwargs): + super().__init__( + name="cliphist-launcher", + all_visible=False, + visible=False, + **kwargs, + ) + + self._arranger_handler = 0 + self.cliphist_manager = CliphistManager(self) + self.viewport = None + self.launcher = kwargs["launcher"] + + self.search_entry = Entry( + name="search-entry", + h_expand=True, + notify_text=lambda entry, *_: self.handle_search_input(entry.get_text()), + on_activate=lambda entry, *_: self.handle_search_input(entry.get_text()), + ) + + self.toggle_button = Button( + child=MaterialIcon("delete_forever"), + name="clear-launcher-button", + on_clicked=self.on_button_clicked, + ) + + self.header_box = Box( + name="header-box", + spacing=10, + orientation="h", + children=[self.search_entry, self.toggle_button], + ) + + self.launcher_box = Box( + name="cliphist-launcher-box", + spacing=10, + orientation="v", + h_expand=True, + children=[self.header_box], + ) + + self.add(self.launcher_box) + + def on_button_clicked(self, *_): + GLib.spawn_command_line_async("cliphist wipe") + self.cliphist_manager.update_cliphist_history() + + def open_launcher(self): + if not self.viewport: + self.viewport = Box(name="viewport", spacing=4, orientation="v") + self.scrolled_window = ScrolledWindow( + name="cliphist-history", + spacing=10, + h_scrollbar_policy="never", + child=self.viewport, + ) + self.launcher_box.add(self.scrolled_window) + + self.viewport.children = [] + self.cliphist_manager.arrange_viewport() + + self.viewport.show() + self.search_entry.grab_focus() + + def close_launcher(self): + self.launcher.close() + + def handle_search_input(self, text: str): + self.cliphist_manager.arrange_viewport(text) + + +class CliphistManager: + def __init__(self, launcher): + self.launcher = launcher + self.cliphist_history = [] + self.wl_paste_watcher = Fabricator( + poll_from="wl-paste --watch echo", stream=True, interval=-1 + ) + self.wl_paste_watcher.connect( + "changed", lambda *_: self.update_cliphist_history() + ) + + def get_clip_history(self) -> List[dict]: + try: + result = subprocess.run( + ["cliphist", "list"], capture_output=True, text=True, check=True + ) + return self._parse_clip_history(result.stdout) + except subprocess.CalledProcessError as e: + print(f"Error fetching cliphist history: {e}") + return [] + + def _parse_clip_history(self, output: str) -> List[dict]: + items = [] + for line in output.splitlines(): + if line.strip(): + parts = line.strip().split(maxsplit=1) + if len(parts) == 2: + items.append({"id": parts[0], "content": parts[1]}) + return items + + def copy_clip(self, clip_id: str): + try: + decode_result = subprocess.run( + ["cliphist", "decode", clip_id], capture_output=True, check=True + ) + subprocess.run(["wl-copy"], input=decode_result.stdout, check=True) + self.launcher.close_launcher() + except subprocess.CalledProcessError as e: + print(f"Error copying clip {clip_id}: {e}") + + def save_image_file(self, clip_id: str) -> str: + output_file = f"/tmp/cliphist-{clip_id}.png" + os.makedirs("/tmp", exist_ok=True) + + try: + decode_result = subprocess.run( + ["cliphist", "decode", clip_id], capture_output=True, check=True + ) + + with open(output_file, "wb") as f: + f.write(decode_result.stdout) + + return output_file + except (subprocess.CalledProcessError, IOError) as e: + print(f"Error saving image file for clip {clip_id}: {e}") + return "" + + def query_clips(self, query: str = "") -> List[dict]: + history = self.get_clip_history() + + if not query.strip(): + return history + + return [item for item in history if query.lower() in item["content"].lower()] + + def bake_clip_slot(self, item: dict) -> Button: + if "[[ binary data" in item["content"].lower(): + return self._create_image_button(item) + return self._create_text_button(item) + + def _create_image_button(self, item: dict) -> Button: + output_file = self.save_image_file(item["id"]) + if output_file and os.path.exists(output_file): + button = Button( + h_align="start", + name="clip-img-item", + on_clicked=lambda _, clip_id=item["id"]: self.copy_clip(clip_id), + ) + + button.set_style( + f"background-image: url('{output_file}'); background-size: cover; background-position: center;" + ) + return button + + return None + + def _create_text_button(self, item: dict) -> Button: + return Button( + child=Label( + label=item["content"][:55] + + ("..." if len(item["content"]) > 55 else ""), + h_expand=True, + v_align="center", + h_align="start", + ), + on_clicked=lambda _, clip_id=item["id"]: self.copy_clip(clip_id), + name="clip-item", + ) + + def update_cliphist_history(self): + self.cliphist_history = self.get_clip_history() + if self.launcher.viewport: + GLib.idle_add(self.arrange_viewport) + + def arrange_viewport(self, query: str = ""): + if not self.launcher.viewport: + return + + if self.launcher._arranger_handler: + remove_handler(self.launcher._arranger_handler) + + self.launcher.viewport.children = [] + filtered_clips = self.query_clips(query) + + for clip in filtered_clips: + button = self.bake_clip_slot(clip) + if button: + self.launcher.viewport.add(button) diff --git a/modules/launcher/components/custom_color_wallpapers (copy 2).py b/modules/launcher/components/custom_color_wallpapers (copy 2).py new file mode 100644 index 0000000..25c717b --- /dev/null +++ b/modules/launcher/components/custom_color_wallpapers (copy 2).py @@ -0,0 +1,357 @@ +import hashlib +import json +import os +from PIL import Image +from fabric.widgets.box import Box +from fabric.widgets.button import Button +from fabric.widgets.entry import Entry +from fabric.widgets.scrolledwindow import ScrolledWindow +from gi.repository import GdkPixbuf, GLib, Gtk, Gio +from snippets import MaterialIcon +from fabric.utils import exec_shell_command, get_relative_path +import concurrent.futures +from concurrent.futures import ThreadPoolExecutor +from fabric.widgets.label import Label + + +def is_valid_hex_color(value: str) -> bool: + """Validate if the string is a Hex color.""" + return ( + value.startswith("#") + and len(value) == 7 + and all(c in "0123456789ABCDEFabcdef" for c in value[1:]) + ) + + +def is_valid_hue(value: str) -> bool: + """Validate if the string is a numeric Hue value (0โ€“360).""" + return value.isnumeric() and 0 <= float(value) <= 360 + + +class WallpaperSelector(Box): + CACHE_DIR = os.path.expanduser("~/.cache/modus/wallpapers") + SETTINGS_FILE = os.path.expanduser("~/dotfiles/hypr/scripts/settings.json") + SCHEMES = { + "TonalSpot": "tonalSpot", + "Expressive": "expressive", + "FruitSalad": "fruitSalad", + "Monochrome": "monochrome", + "Rainbow": "rainbow", + "Vibrant": "vibrant", + "Neutral": "neutral", + "Fidelity": "fidelity", + "Content": "content", + } + + def __init__(self, **kwargs): + super().__init__(name="wallpapers", spacing=4, orientation="v", **kwargs) + self.launcher = kwargs["launcher"] + + self.wallpapers_dir = os.path.expanduser("~/Pictures/wallpapers") + os.makedirs(self.CACHE_DIR, exist_ok=True) + + self.files = [f for f in os.listdir(self.wallpapers_dir) if self._is_image(f)] + self.thumbnails = [] + self.thumbnail_queue = [] + self.executor = ThreadPoolExecutor(max_workers=4) # Shared executor + + self.viewport = Gtk.IconView() + self.viewport.set_model(Gtk.ListStore(GdkPixbuf.Pixbuf, str)) + self.viewport.set_pixbuf_column(0) + self.viewport.set_text_column(1) + self.viewport.set_item_width(0) + self.viewport.connect("item-activated", self.on_wallpaper_selected) + + self.scrolled_window = ScrolledWindow( + name="scrolled-window", + spacing=10, + h_expand=True, + v_expand=True, + child=self.viewport, + ) + + self.search_entry = Entry( + name="search-entry", + h_expand=True, + notify_text=lambda entry, *_: self.arrange_viewport(entry.get_text()), + ) + + self.scheme_dropdown = Gtk.ComboBoxText() + self.scheme_dropdown.set_name("scheme-dropdown") + self.scheme_dropdown.set_tooltip_text("Select color scheme") + + for display_name, scheme_id in sorted(self.SCHEMES.items()): + self.scheme_dropdown.append(scheme_id, display_name) + + self.scheme_dropdown.set_active_id("tonalSpot") + self.scheme_dropdown.connect("changed", self.on_scheme_changed) + + initial_icon = "light_mode" if not self.check_dark_mode_state() else "dark_mode" + self.toggle_button = Button( + child=MaterialIcon(initial_icon), + name="toggle-launcher-button", + on_clicked=self.toggle_dark_mode, + ) + self.custom_color_entry = Entry( + name="search-entry", + h_expand=True, + ) + self.custom_color_entry.connect("activate", self.on_custom_color_submitted) + + self.custom_color_box = Box( + name="custom-color-box", + spacing=10, + orientation="h", + children=[ + Label(label="Custom Color:"), + self.custom_color_entry, + ], + ) + + self.dropdown_box = Box( + name="dropdown-box", + orientation="h", + children=[ + self.scheme_dropdown, + MaterialIcon("keyboard_arrow_down"), + ], + ) + + self.header_box = Box( + name="header-box", + spacing=10, + orientation="h", + children=[ + self.search_entry, + self.dropdown_box, + self.custom_color_box, + self.toggle_button, + ], + ) + + self.add(self.header_box) + self.add(self.scrolled_window) + self._start_thumbnail_thread() + self.setup_file_monitor() + self.show_all() + + def setup_file_monitor(self): + gfile = Gio.File.new_for_path(self.wallpapers_dir) + self.file_monitor = gfile.monitor_directory(Gio.FileMonitorFlags.NONE, None) + self.file_monitor.connect("changed", self.on_directory_changed) + + def on_directory_changed(self, monitor, file, other_file, event_type): + file_name = file.get_basename() + if event_type == Gio.FileMonitorEvent.DELETED: + if file_name in self.files: + self.files.remove(file_name) + cache_path = self._get_cache_path(file_name) + if os.path.exists(cache_path): + try: + os.remove(cache_path) + except Exception as e: + print(f"Error deleting cache {cache_path}: {e}") + self.thumbnails = [(p, n) for p, n in self.thumbnails if n != file_name] + GLib.idle_add(self.arrange_viewport, self.search_entry.get_text()) + elif event_type == Gio.FileMonitorEvent.CREATED: + if self._is_image(file_name) and file_name not in self.files: + self.files.append(file_name) + self.files.sort() + self.executor.submit(self._process_file, file_name) + elif event_type == Gio.FileMonitorEvent.CHANGED: + if self._is_image(file_name) and file_name in self.files: + cache_path = self._get_cache_path(file_name) + if os.path.exists(cache_path): + try: + os.remove(cache_path) + except Exception as e: + print(f"Error deleting cache for changed file {file_name}: {e}") + self.executor.submit(self._process_file, file_name) + + def close_selector(self): + self.launcher.close() + + def arrange_viewport(self, query: str = ""): + self.viewport.get_model().clear() + filtered_thumbnails = [ + (thumb, name) + for thumb, name in self.thumbnails + if query.casefold() in name.casefold() + ] + + filtered_thumbnails.sort(key=lambda x: x[1].lower()) + + for pixbuf, file_name in filtered_thumbnails: + self.viewport.get_model().append([pixbuf, file_name]) + + def toggle_dark_mode(self, *_): + GLib.spawn_command_line_async( + f"bash {get_relative_path('../../../assets/scripts/dark-theme.sh --toggle')}" + ) + icon_name = "dark_mode" if not self.check_dark_mode_state() else "light_mode" + if self.toggle_button.get_child(): + self.toggle_button.remove(self.toggle_button.get_child()) + self.toggle_button.add(MaterialIcon(icon_name)) + self.toggle_button.show_all() + + def check_dark_mode_state(self): + result = exec_shell_command( + "gsettings get org.gnome.desktop.interface color-scheme" + ) + return result.strip().replace("'", "") == "prefer-dark" + + def on_wallpaper_selected(self, iconview, path): + model = iconview.get_model() + file_name = model[path][1] + full_path = os.path.join(self.wallpapers_dir, file_name) + selected_scheme = self.scheme_dropdown.get_active_id() + home_dir = GLib.get_home_dir() + command = ( + f"python -O {home_dir}/dotfiles/hypr/scripts/wallpaper.py -I {full_path}" + ) + GLib.spawn_command_line_async(command) + self.update_scheme(selected_scheme) + + def on_scheme_changed(self, combo): + scheme_id = combo.get_active_id() + display_name = next( + name for name, id in self.SCHEMES.items() if id == scheme_id + ) + print(f"Color scheme selected: {display_name} ({scheme_id})") + self.update_scheme(scheme_id) + + home_dir = GLib.get_home_dir() + color_generator = os.path.join(home_dir, "dotfiles/material-colors/generate.py") + try: + command = f'python -O {color_generator} -R --scheme "{scheme_id}"' + GLib.spawn_command_line_async(command) + print(f"Applied color scheme: {display_name}") + except Exception as e: + print(f"Failed to apply color scheme: {e}") + + def update_scheme(self, scheme: str): + try: + with open(self.SETTINGS_FILE, "r") as f: + settings = json.loads(f.read()) + + settings["generation-scheme"] = scheme + + with open(self.SETTINGS_FILE, "w") as f: + json.dump(settings, f, indent=2) + except Exception as error: + print(f"Failed to update generation-scheme in settings.json: {error}") + + def _start_thumbnail_thread(self): + thread = GLib.Thread.new("thumbnail-loader", self._preload_thumbnails, None) + + def _preload_thumbnails(self, _data): + futures = [ + self.executor.submit(self._process_file, file_name) + for file_name in self.files + ] + concurrent.futures.wait(futures) + GLib.idle_add(self._process_batch) + + def _process_file(self, file_name): + full_path = os.path.join(self.wallpapers_dir, file_name) + cache_path = self._get_cache_path(file_name) + if not os.path.exists(cache_path): + try: + with Image.open(full_path) as img: + img.thumbnail((96, 96), Image.Resampling.BILINEAR) + img.save(cache_path, "PNG") + except Exception as e: + print(f"Error processing {file_name}: {e}") + return + self.thumbnail_queue.append((cache_path, file_name)) + GLib.idle_add(self._process_batch) + + def _process_batch(self): + batch = self.thumbnail_queue[:10] + del self.thumbnail_queue[:10] + for cache_path, file_name in batch: + try: + pixbuf = GdkPixbuf.Pixbuf.new_from_file(cache_path) + self.thumbnails.append((pixbuf, file_name)) + self.viewport.get_model().append([pixbuf, file_name]) + except Exception as e: + print(f"Error loading thumbnail {cache_path}: {e}") + if self.thumbnail_queue: + GLib.idle_add(self._process_batch) + return False + + def _get_cache_path(self, file_name: str) -> str: + file_hash = hashlib.md5(file_name.encode("utf-8")).hexdigest() + return os.path.join(self.CACHE_DIR, f"{file_hash}.png") + + @staticmethod + def _is_image(file_name: str) -> bool: + return file_name.lower().endswith( + (".png", ".jpg", ".jpeg", ".bmp", ".gif", ".webp") + ) + + def on_custom_color_submitted(self, entry: Entry): + """Handle color submission.""" + color = entry.get_text().strip() + if is_valid_hex_color(color) or is_valid_hue(color): + color_value = ( + color if is_valid_hex_color(color) else self.hue_to_hex(float(color)) + ) + print(f"Applying custom color: {color_value}") + self.update_custom_color(color_value) + else: + print("Invalid color input. Please use a valid Hex or Hue.") + + def update_custom_color(self, color: str): + """Update the custom color in settings and regenerate theme.""" + try: + with open(self.SETTINGS_FILE, "r") as f: + settings = json.loads(f.read()) + + settings["custom-color"] = color + + with open(self.SETTINGS_FILE, "w") as f: + json.dump(settings, f, indent=2) + + home_dir = GLib.get_home_dir() + color_generator = os.path.join( + home_dir, "dotfiles/material-colors/generate.py" + ) + command = f'python -O {color_generator} --color "{color}"' + GLib.spawn_command_line_async(command) + print(f"Custom color applied: {color}") + except Exception as e: + print(f"Failed to update custom color: {e}") + + @staticmethod + def hue_to_hex(hue: float) -> str: + """Convert Hue to Hex color.""" + hue = hue / 360.0 + rgb = WallpaperSelector.hls_to_rgb(hue, 0.5, 1.0) + return f"#{int(rgb[0] * 255):02X}{int(rgb[1] * 255):02X}{int(rgb[2] * 255):02X}" + + @staticmethod + def hls_to_rgb(h: float, l: float, s: float) -> tuple: + """Convert HLS to RGB color.""" + if s == 0: + return l, l, l + + def hue2rgb(p, q, t): + if t < 0: + t += 1 + if t > 1: + t -= 1 + if t < 1 / 6: + return p + (q - p) * 6 * t + if t < 1 / 2: + return q + if t < 2 / 3: + return p + (q - p) * (2 / 3 - t) * 6 + return p + + q = l * (1 + s) if l < 0.5 else l + s - l * s + p = 2 * l - q + r = hue2rgb(p, q, h + 1 / 3) + g = hue2rgb(p, q, h) + b = hue2rgb(p, q, h - 1 / 3) + return r, g, b diff --git a/modules/launcher/components/dashboard.py b/modules/launcher/components/dashboard.py new file mode 100644 index 0000000..d266e0e --- /dev/null +++ b/modules/launcher/components/dashboard.py @@ -0,0 +1,242 @@ +from fabric.widgets.box import Box +from fabric.widgets.button import Button +from fabric.widgets.label import Label +from snippets import MaterialIcon + + +class Buttons(Box): + def __init__(self, **kwargs): + super().__init__( + name="buttons", + spacing=4, + h_align="center", + v_align="start", + h_expand=True, + v_expand=True, + visible=True, + all_visible=True, + ) + + self.network_status_button = Button( + name="network-status-button", + h_expand=True, + child=Box( + h_align="start", + v_align="center", + spacing=10, + children=[ + MaterialIcon("wifi"), + Box( + name="network-text", + orientation="v", + h_align="start", + v_align="center", + children=[ + Box( + children=[ + Label( + name="network-label", + label="Wi-Fi", + justification="left", + ), + Box(h_expand=True), + ] + ), + Box( + children=[ + Label( + name="network-ssid", + label="ARGOS_5GHz", + justification="left", + ), + Box(h_expand=True), + ] + ), + ], + ), + ], + ), + ) + + self.network_menu_button = Button( + name="network-menu-button", + child=MaterialIcon("chevron_right"), + ) + + self.network_button = Box( + name="network-button", + orientation="h", + h_align="fill", + v_align="center", + h_expand=True, + v_expand=True, + children=[ + self.network_status_button, + self.network_menu_button, + ], + ) + + self.bluetooth_status_button = Button( + name="bluetooth-status-button", + h_expand=True, + child=Box( + h_align="start", + v_align="center", + spacing=10, + children=[ + MaterialIcon("bluetooth"), + Box( + name="bluetooth-text", + orientation="v", + h_align="start", + v_align="center", + children=[ + Box( + children=[ + Label( + name="bluetooth-label", + label="Bluetooth", + justification="left", + ), + Box(h_expand=True), + ] + ), + Box( + children=[ + Label( + name="bluetooth-status", + label="Connected", + justification="left", + ), + Box(h_expand=True), + ] + ), + ], + ), + ], + ), + ) + + self.bluetooth_menu_button = Button( + name="bluetooth-menu-button", + child=MaterialIcon("chevron_right"), + ) + + self.bluetooth_button = Box( + name="bluetooth-button", + orientation="h", + h_align="fill", + v_align="center", + h_expand=True, + v_expand=True, + children=[ + self.bluetooth_status_button, + self.bluetooth_menu_button, + ], + ) + + self.night_mode_button = Button( + name="night-mode-button", + child=Box( + h_align="start", + v_align="center", + spacing=10, + children=[ + MaterialIcon("nightlight"), + Box( + name="night-mode-text", + orientation="v", + h_align="start", + v_align="center", + children=[ + Box( + children=[ + Label( + name="night-mode-label", + label="Night Mode", + justification="left", + ), + Box(h_expand=True), + ] + ), + Box( + children=[ + Label( + name="night-mode-status", + label="Enabled", + justification="left", + ), + Box(h_expand=True), + ] + ), + ], + ), + ], + ), + ) + + self.caffeine_button = Button( + name="caffeine-button", + child=Box( + h_align="start", + v_align="center", + spacing=10, + children=[ + MaterialIcon("coffee"), + Box( + name="caffeine-text", + orientation="v", + h_align="start", + v_align="center", + children=[ + Box( + children=[ + Label( + name="caffeine-label", + label="Caffeine", + justification="left", + ), + Box(h_expand=True), + ] + ), + Box( + children=[ + Label( + name="caffeine-status", + label="Enabled", + justification="left", + ), + Box(h_expand=True), + ] + ), + ], + ), + ], + ), + ) + + self.add(self.network_button) + self.add(self.bluetooth_button) + self.add(self.night_mode_button) + self.add(self.caffeine_button) + + self.show_all() + + +class Dashboard(Box): + def __init__(self, **kwargs): + super().__init__( + name="dashboard", + h_align="center", + v_align="start", + h_expand=True, + v_expand=True, + visible=True, + all_visible=True, + ) + + self.buttons = Buttons() + + self.add(self.buttons) + + self.show_all() diff --git a/modules/launcher/components/emoji.py b/modules/launcher/components/emoji.py new file mode 100644 index 0000000..731c108 --- /dev/null +++ b/modules/launcher/components/emoji.py @@ -0,0 +1,133 @@ +import subprocess +from typing import Generator, List + +import ijson +from fabric.utils import remove_handler, get_relative_path +from fabric.widgets.box import Box +from fabric.widgets.button import Button +from fabric.widgets.entry import Entry +from fabric.widgets.label import Label +from fabric.widgets.scrolledwindow import ScrolledWindow + + +class Emoji(Box): + def __init__(self, **kwargs): + super().__init__( + name="emoji-launcher", + all_visible=False, + visible=False, + **kwargs, + ) + + self.launcher = kwargs["launcher"] + self._arranger_handler: int = 0 + self.emoji_manager = EmojiManager(self) + self.viewport = None + + self.search_entry = Entry( + name="search-entry", + h_expand=True, + notify_text=lambda entry, *_: self.handle_search_input(entry.get_text()), + on_activate=lambda entry, *_: self.handle_search_input(entry.get_text()), + ) + + self.header_box = Box( + name="header-box", + spacing=10, + orientation="h", + children=[ + self.search_entry, + ], + ) + + self.launcher_box = Box( + spacing=10, + orientation="v", + h_expand=True, + children=[self.header_box], + ) + + self.add(self.launcher_box) + + def open_launcher(self): + if not self.viewport: + self.viewport = Box(name="viewport", spacing=4, orientation="v") + self.scrolled_window = ScrolledWindow( + name="scrolled-window", + spacing=10, + h_scrollbar_policy="never", + v_scrollbar_policy="never", + child=self.viewport, + ) + self.launcher_box.add(self.scrolled_window) + + self.viewport.children = [] + self.emoji_manager.arrange_viewport() + + self.viewport.show() + self.search_entry.grab_focus() + + def close_launcher(self): + self.launcher.close() + + def handle_search_input(self, text: str): + self.emoji_manager.arrange_viewport(text) + + +class EmojiManager: + def __init__(self, launcher): + self.launcher = launcher + self.emoji_file_path = get_relative_path("../../../config/assets/emoji.json") + + def load_emojis(self) -> Generator[tuple, None, None]: + try: + with open(self.emoji_file_path, "r") as file: + for emoji_str, item in ijson.kvitems(file, ""): + yield emoji_str, item["name"], item["slug"], item["group"] + except (ijson.JSONError, KeyError, OSError) as e: + print(f"Error loading emojis: {e}") + return + + def query_emojis(self, query: str) -> List[tuple]: + query = query.lower() + return [ + (emoji_str, name, slug, group) + for emoji_str, name, slug, group in self.load_emojis() + if query in name.lower() or query in slug.lower() or query in group.lower() + ][:48] + + def copy_emoji(self, emoji: tuple): + subprocess.run(["wl-copy"], input=emoji[0].encode(), check=True) + self.launcher.close_launcher() + + def bake_emoji_slot(self, emoji: tuple, **kwargs) -> Button: + return Button( + name="emoji-item", + child=Label(label=emoji[0], h_align="center"), + tooltip_text=emoji[1], + on_clicked=lambda *_: self.copy_emoji(emoji), + **kwargs, + ) + + def arrange_viewport(self, query: str = ""): + if not self.launcher.viewport: + return + + ( + remove_handler(self.launcher._arranger_handler) + if self.launcher._arranger_handler + else None + ) + self.launcher.viewport.children = [] + + filtered_emojis = self.query_emojis(query) + + row = Box(name="emoji-row", spacing=10, orientation="h") + for emoji in filtered_emojis: + row.add(self.bake_emoji_slot(emoji)) + if len(row.children) >= 12: + self.launcher.viewport.add(row) + row = Box(name="emoji-row", spacing=10, orientation="h") + + if row.children: + self.launcher.viewport.add(row) diff --git a/modules/launcher/components/power.py b/modules/launcher/components/power.py new file mode 100644 index 0000000..e00efa1 --- /dev/null +++ b/modules/launcher/components/power.py @@ -0,0 +1,88 @@ +from fabric.widgets.box import Box +from fabric.widgets.button import Button +from gi.repository import GLib + +from snippets import MaterialIcon + + +class PowerMenu(Box): + def __init__(self, **kwargs): + super().__init__( + name="powermenu", + orientation="h", + spacing=10, + v_align="center", + h_align="center", + v_expand=True, + h_expand=True, + visible=True, + **kwargs, + ) + + self.launcher = kwargs["launcher"] + + self.btn_lock = Button( + name="powermenu-button", + child=MaterialIcon("lock", 45), + on_clicked=self.lock, + ) + + self.btn_suspend = Button( + name="powermenu-button", + child=MaterialIcon("sleep", 45), + on_clicked=self.suspend, + ) + + self.btn_logout = Button( + name="powermenu-button", + child=MaterialIcon("logout", 45), + on_clicked=self.logout, + ) + + self.btn_reboot = Button( + name="powermenu-button", + child=MaterialIcon("restart_alt", 45), + on_clicked=self.reboot, + ) + + self.btn_shutdown = Button( + name="powermenu-button", + child=MaterialIcon("power_settings_new", 45), + on_clicked=self.poweroff, + ) + + self.buttons = [ + self.btn_lock, + self.btn_suspend, + self.btn_logout, + self.btn_reboot, + self.btn_shutdown, + ] + + for button in self.buttons: + self.add(button) + + self.show_all() + + def close_menu(self): + self.launcher.close() + + def lock(self, *_): + GLib.spawn_command_line_async("hyprlock --immediate") + self.close_menu() + + def suspend(self, *_): + GLib.spawn_command_line_async("systemctl suspend") + self.close_menu() + + def logout(self, *_): + GLib.spawn_command_line_async("hyprctl dispatch exit") + self.close_menu() + + def reboot(self, *_): + GLib.spawn_command_line_async("systemctl reboot") + self.close_menu() + + def poweroff(self, *_): + GLib.spawn_command_line_async("systemctl poweroff") + self.close_menu() diff --git a/modules/launcher/components/sh.py b/modules/launcher/components/sh.py new file mode 100644 index 0000000..5992ec8 --- /dev/null +++ b/modules/launcher/components/sh.py @@ -0,0 +1,138 @@ +import os +import subprocess +from typing import List +from fabric.widgets.box import Box +from fabric.widgets.button import Button +from fabric.widgets.entry import Entry +from fabric.widgets.label import Label +from fabric.widgets.scrolledwindow import ScrolledWindow +from snippets import MaterialIcon + +CACHE_DIR = os.getenv( + "XDG_CACHE_HOME", os.path.join(os.path.expanduser("~"), ".cache", "fabric") +) +BINS = os.path.join(CACHE_DIR, "binaries") + + +class Sh(Box): + def __init__(self, **kwargs): + super().__init__( + all_visible=False, + visible=False, + **kwargs, + ) + + self.launcher = kwargs["launcher"] + self._arranger_handler: int = 0 + self.shell_command_manager = ShellCommandManager(self) + self.viewport = None + + self.search_entry = Entry( + name="search-entry", + h_expand=True, + notify_text=lambda entry, *_: self.handle_search_input(entry.get_text()), + on_activate=lambda entry, *_: self.handle_search_input(entry.get_text()), + ) + + self.header_box = Box( + spacing=10, + orientation="h", + children=[self.search_entry, MaterialIcon("terminal")], + ) + + self.launcher_box = Box( + spacing=10, + orientation="v", + h_expand=True, + children=[self.header_box], + ) + + self.add(self.launcher_box) + + def open_launcher(self): + if not self.viewport: + self.viewport = Box(name="viewport", spacing=10, orientation="v") + self.scrolled_window = ScrolledWindow( + spacing=10, + h_scrollbar_policy="never", + v_scrollbar_policy="never", + child=self.viewport, + ) + self.launcher_box.add(self.scrolled_window) + + self.viewport.children = [] + self.shell_command_manager.show_shell_commands(self.viewport, "") + + self.viewport.show() + self.search_entry.grab_focus() + + def handle_search_input(self, text: str): + self.shell_command_manager.show_shell_commands(self.viewport, text) + + +class ShellCommandManager: + def __init__(self, launcher): + self.launcher = launcher + self.binaries = set() + self.reload_binaries() + + def reload_binaries(self): + paths = os.getenv("PATH", "").split(":") + self.binaries = set() + + for path in paths: + self.binaries.update(self.list_binaries_in_path(path)) + + with open(BINS, "w") as f: + f.write("\n".join(self.binaries)) + + def list_binaries_in_path(self, path): + if not os.path.exists(path): + return [] + try: + return [ + entry + for entry in os.listdir(path) + if os.access(os.path.join(path, entry), os.X_OK) + ] + except OSError: + return [] + + def show_shell_commands(self, viewport, search_query: str): + if not search_query: + return + + viewport.children = [] + + results = self.query_binaries(search_query) + self.display_results(viewport, results) + + def query_binaries(self, filter_str: str) -> List[str]: + filter_str = filter_str.lower() + filtered_bins = (cmd for cmd in self.binaries if filter_str in cmd) + return list(dict.fromkeys(filtered_bins))[:16] + + def display_results(self, viewport, results: List[str]): + for command in results: + button = Button( + child=Label( + label=command, + v_align="center", + h_align="start", + ), + on_clicked=lambda _, cmd=command: self.execute_command(cmd), + name="sh-item", + ) + viewport.add(button) + + def close_menu(self): + self.launcher.close() + + def execute_command(self, cmd: str): + try: + output = subprocess.check_output(cmd, shell=True, text=True) + print(output) + except subprocess.CalledProcessError as e: + print(e.output) + finally: + self.close_menu() diff --git a/modules/launcher/components/todo.py b/modules/launcher/components/todo.py new file mode 100644 index 0000000..7cc569b --- /dev/null +++ b/modules/launcher/components/todo.py @@ -0,0 +1,168 @@ +import json +import os + +from fabric.widgets.box import Box +from fabric.widgets.button import Button +from fabric.widgets.entry import Entry +from fabric.widgets.label import Label +from fabric.widgets.scrolledwindow import ScrolledWindow +from snippets import MaterialIcon + + +class TodoManager(Box): + def __init__(self, **kwargs): + super().__init__( + name="todo-launcher", + all_visible=False, + visible=False, + **kwargs, + ) + + self._arranger_handler = 0 + self.todo_manager = TodoListManager(self) + self.viewport = None + + self.todo_entry = Entry( + name="todo-entry", + h_expand=True, + on_activate=lambda entry, *_: self.handle_add_todo(entry.get_text()), + ) + + self.header_box = Box( + name="header-box", + spacing=10, + orientation="h", + children=[ + self.todo_entry, + Button( + name="add-button", + child=MaterialIcon("add"), + tooltip_text="Add Todo", + on_clicked=lambda *_: self.handle_add_todo( + self.todo_entry.get_text() + ), + ), + ], + ) + + self.launcher_box = Box( + name="todo-launcher-box", + spacing=10, + orientation="v", + h_expand=True, + children=[self.header_box], + ) + + self.add(self.launcher_box) + + def open_launcher(self): + if not self.viewport: + self.viewport = Box(name="viewport", spacing=4, orientation="v") + self.scrolled_window = ScrolledWindow( + name="todo-content", + spacing=10, + h_scrollbar_policy="never", + child=self.viewport, + ) + self.launcher_box.add(self.scrolled_window) + + self.viewport.children = [] + self.todo_manager.arrange_viewport() + + self.viewport.show() + self.todo_entry.grab_focus() + + def handle_add_todo(self, text: str): + if text.strip(): + self.todo_manager.add_todo(text) + self.todo_entry.set_text("") + + +class TodoListManager: + def __init__(self, launcher): + self.launcher = launcher + self.todos = [] + self.todo_file = os.path.expanduser("~/.local/share/fabric/todos.json") + self.load_todos() + + def load_todos(self): + os.makedirs(os.path.dirname(self.todo_file), exist_ok=True) + try: + if os.path.exists(self.todo_file): + with open(self.todo_file, "r") as f: + self.todos = json.load(f) + except Exception as e: + print(f"Error loading todos: {e}") + self.todos = [] + + def save_todos(self): + try: + with open(self.todo_file, "w") as f: + json.dump(self.todos, f) + except Exception as e: + print(f"Error saving todos: {e}") + + def add_todo(self, content: str): + todo = {"content": content, "completed": False, "id": len(self.todos)} + self.todos.append(todo) + self.save_todos() + self.arrange_viewport() + + def toggle_todo(self, todo_id: int): + for todo in self.todos: + if todo["id"] == todo_id: + todo["completed"] = not todo["completed"] + break + self.save_todos() + self.arrange_viewport() + + def delete_todo(self, todo_id: int): + self.todos = [todo for todo in self.todos if todo["id"] != todo_id] + self.save_todos() + self.arrange_viewport() + + def bake_todo_slot(self, todo: dict) -> Box: + todo_box = Box( + name="todo-item", + spacing=10, + orientation="h", + h_expand=True, + ) + + checkbox = Button( + name="todo-checkbox", + child=MaterialIcon( + "check_box" if todo["completed"] else "check_box_outline_blank" + ), + on_clicked=lambda _, todo_id=todo["id"]: self.toggle_todo(todo_id), + ) + + label = Label( + label=todo["content"], + h_expand=True, + v_align="center", + h_align="start", + name="todo-label-completed" if todo["completed"] else "todo-label", + ) + + delete_button = Button( + name="delete-button", + child=MaterialIcon("delete"), + on_clicked=lambda _, todo_id=todo["id"]: self.delete_todo(todo_id), + ) + + todo_box.add(checkbox) + todo_box.add(label) + todo_box.add(delete_button) + + return todo_box + + def arrange_viewport(self): + if not self.launcher.viewport: + return + + self.launcher.viewport.children = [] + + for todo in self.todos: + todo_slot = self.bake_todo_slot(todo) + self.launcher.viewport.add(todo_slot) diff --git a/modules/launcher/components/wallpapers (copy 1).py b/modules/launcher/components/wallpapers (copy 1).py new file mode 100644 index 0000000..f54c565 --- /dev/null +++ b/modules/launcher/components/wallpapers (copy 1).py @@ -0,0 +1,379 @@ +import hashlib +import json +import os +from PIL import Image +from fabric.widgets.box import Box +from fabric.widgets.button import Button +from fabric.widgets.entry import Entry +from fabric.widgets.scrolledwindow import ScrolledWindow +from gi.repository import GdkPixbuf, GLib, Gtk, Gio, Gdk +from snippets import MaterialIcon +from fabric.utils import exec_shell_command, get_relative_path +import concurrent.futures +from concurrent.futures import ThreadPoolExecutor +from fabric.widgets.label import Label + + +def is_valid_hex_color(value: str) -> bool: + """Validate if the string is a Hex color.""" + return ( + value.startswith("#") + and len(value) == 7 + and all(c in "0123456789ABCDEFabcdef" for c in value[1:]) + ) + + +def is_valid_hue(value: str) -> bool: + """Validate if the string is a numeric Hue value (0โ€“360).""" + return value.isnumeric() and 0 <= float(value) <= 360 + + +class WallpaperSelector(Box): + CACHE_DIR = os.path.expanduser("~/.cache/fabric/wallpapers") + SETTINGS_FILE = os.path.expanduser("~/dotfiles/hypr/scripts/.settings.json") + + SCHEMES = { + "TonalSpot": "tonalSpot", + "Expressive": "expressive", + "FruitSalad": "fruitSalad", + "Monochrome": "monochrome", + "Rainbow": "rainbow", + "Vibrant": "vibrant", + "Neutral": "neutral", + "Fidelity": "fidelity", + "Content": "content", + } + + def __init__(self, **kwargs): + super().__init__(name="wallpapers", spacing=5, orientation="v", **kwargs) + self.launcher = kwargs["launcher"] + + self.wallpapers_dir = os.path.expanduser("~/Pictures/wallpapers") + os.makedirs(self.CACHE_DIR, exist_ok=True) + + self.files = [f for f in os.listdir(self.wallpapers_dir) if self._is_image(f)] + self.thumbnails = [] + self.thumbnail_queue = [] + self.executor = ThreadPoolExecutor(max_workers=5) # Shared executor + + self.viewport = Gtk.IconView() + self.viewport.set_model(Gtk.ListStore(GdkPixbuf.Pixbuf, str)) + self.viewport.set_pixbuf_column(1) + self.viewport.set_text_column(2) + self.viewport.set_item_width(1) + self.viewport.connect("item-activated", self.on_wallpaper_selected) + + self.scrolled_window = ScrolledWindow( + name="scrolled-window", + spacing=11, + h_expand=True, + v_expand=True, + child=self.viewport, + ) + + self.search_entry = Entry( + name="search-entry", + h_expand=True, + notify_text=lambda entry, *_: self.arrange_viewport(entry.get_text()), + ) + + self.scheme_dropdown = Gtk.ComboBoxText() + self.scheme_dropdown.set_name("scheme-dropdown") + self.scheme_dropdown.set_tooltip_text("Select color scheme") + + for display_name, scheme_id in sorted(self.SCHEMES.items()): + self.scheme_dropdown.append(scheme_id, display_name) + + self.scheme_dropdown.set_active_id("tonalSpot") + self.scheme_dropdown.connect("changed", self.on_scheme_changed) + + initial_icon = "light_mode" if not self.check_dark_mode_state() else "dark_mode" + self.toggle_button = Button( + child=MaterialIcon(initial_icon), + name="toggle-launcher-button", + on_clicked=self.toggle_dark_mode, + ) + + self.dropdown_box = Box( + name="dropdown-box", + orientation="h", + children=[ + self.scheme_dropdown, + MaterialIcon("keyboard_arrow_down"), + ], + ) + self.custom_color_entry = Entry( + name="custom-color-entry", + h_expand=True, + # notify_text=self.on_custom_color_changed, + ) + self.custom_color_entry.connect("activate", self.on_custom_color_submitted) + + self.color_preview = Box( + name="color-preview", + ) + self.custom_color_box = Box( + name="custom-color-box", + spacing=10, + orientation="h", + children=[ + Label(label="Custom Color:", xalign=0), + self.custom_color_entry, + self.color_preview, + ], + ) + + self.header_box = Box( + name="header-box", + spacing=11, + orientation="h", + children=[ + self.search_entry, + self.dropdown_box, + self.custom_color_box, + self.toggle_button, + ], + ) + + self.add(self.header_box) + self.add(self.scrolled_window) + self._start_thumbnail_thread() + self.setup_file_monitor() + self.show_all() + + def setup_file_monitor(self): + gfile = Gio.File.new_for_path(self.wallpapers_dir) + self.file_monitor = gfile.monitor_directory(Gio.FileMonitorFlags.NONE, None) + self.file_monitor.connect("changed", self.on_directory_changed) + + def on_directory_changed(self, monitor, file, other_file, event_type): + file_name = file.get_basename() + if event_type == Gio.FileMonitorEvent.DELETED: + if file_name in self.files: + self.files.remove(file_name) + cache_path = self._get_cache_path(file_name) + if os.path.exists(cache_path): + try: + os.remove(cache_path) + except Exception as e: + print(f"Error deleting cache {cache_path}: {e}") + self.thumbnails = [(p, n) for p, n in self.thumbnails if n != file_name] + GLib.idle_add(self.arrange_viewport, self.search_entry.get_text()) + elif event_type == Gio.FileMonitorEvent.CREATED: + if self._is_image(file_name) and file_name not in self.files: + self.files.append(file_name) + self.files.sort() + self.executor.submit(self._process_file, file_name) + elif event_type == Gio.FileMonitorEvent.CHANGED: + if self._is_image(file_name) and file_name in self.files: + cache_path = self._get_cache_path(file_name) + if os.path.exists(cache_path): + try: + os.remove(cache_path) + except Exception as e: + print(f"Error deleting cache for changed file {file_name}: {e}") + self.executor.submit(self._process_file, file_name) + + def close_selector(self): + self.launcher.close() + + def arrange_viewport(self, query: str = ""): + self.viewport.get_model().clear() + filtered_thumbnails = [ + (thumb, name) + for thumb, name in self.thumbnails + if query.casefold() in name.casefold() + ] + + filtered_thumbnails.sort(key=lambda x: x[2].lower()) + + for pixbuf, file_name in filtered_thumbnails: + self.viewport.get_model().append([pixbuf, file_name]) + + def toggle_dark_mode(self, *_): + GLib.spawn_command_line_async( + f"bash {get_relative_path('../../../assets/scripts/dark-theme.sh --toggle')}" + ) + icon_name = "dark_mode" if not self.check_dark_mode_state() else "light_mode" + if self.toggle_button.get_child(): + self.toggle_button.remove(self.toggle_button.get_child()) + self.toggle_button.add(MaterialIcon(icon_name)) + self.toggle_button.show_all() + + def check_dark_mode_state(self): + result = exec_shell_command( + "gsettings get org.gnome.desktop.interface color-scheme" + ) + return result.strip().replace("'", "") == "prefer-dark" + + def on_wallpaper_selected(self, iconview, path): + model = iconview.get_model() + file_name = model[path][2] + full_path = os.path.join(self.wallpapers_dir, file_name) + selected_scheme = self.scheme_dropdown.get_active_id() + home_dir = GLib.get_home_dir() + command = ( + f"python -O {home_dir}/dotfiles/hypr/scripts/wallpaper.py -I {full_path}" + ) + GLib.spawn_command_line_async(command) + self.update_scheme(selected_scheme) + + def on_scheme_changed(self, combo): + scheme_id = combo.get_active_id() + display_name = next( + name for name, id in self.SCHEMES.items() if id == scheme_id + ) + print(f"Color scheme selected: {display_name} ({scheme_id})") + self.update_scheme(scheme_id) + + home_dir = GLib.get_home_dir() + color_generator = os.path.join(home_dir, "dotfiles/material-colors/generate.py") + try: + command = f'python -O {color_generator} -R --scheme "{scheme_id}"' + GLib.spawn_command_line_async(command) + print(f"Applied color scheme: {display_name}") + except Exception as e: + print(f"Failed to apply color scheme: {e}") + + def update_scheme(self, scheme: str): + try: + with open(self.SETTINGS_FILE, "r") as f: + settings = json.loads(f.read()) + + settings["generation-scheme"] = scheme + + with open(self.SETTINGS_FILE, "w") as f: + json.dump(settings, f, indent=3) + except Exception as error: + print(f"Failed to update generation-scheme in settings.json: {error}") + + def _start_thumbnail_thread(self): + thread = GLib.Thread.new("thumbnail-loader", self._preload_thumbnails, None) + + def _preload_thumbnails(self, _data): + futures = [ + self.executor.submit(self._process_file, file_name) + for file_name in self.files + ] + concurrent.futures.wait(futures) + GLib.idle_add(self._process_batch) + + def _process_file(self, file_name): + full_path = os.path.join(self.wallpapers_dir, file_name) + cache_path = self._get_cache_path(file_name) + if not os.path.exists(cache_path): + try: + with Image.open(full_path) as img: + img.thumbnail((96, 96), Image.Resampling.BILINEAR) + img.save(cache_path, "PNG") + except Exception as e: + print(f"Error processing {file_name}: {e}") + return + self.thumbnail_queue.append((cache_path, file_name)) + GLib.idle_add(self._process_batch) + + def _process_batch(self): + batch = self.thumbnail_queue[:10] + del self.thumbnail_queue[:10] + for cache_path, file_name in batch: + try: + pixbuf = GdkPixbuf.Pixbuf.new_from_file(cache_path) + self.thumbnails.append((pixbuf, file_name)) + self.viewport.get_model().append([pixbuf, file_name]) + except Exception as e: + print(f"Error loading thumbnail {cache_path}: {e}") + if self.thumbnail_queue: + GLib.idle_add(self._process_batch) + return False + + def _get_cache_path(self, file_name: str) -> str: + file_hash = hashlib.md5(file_name.encode("utf-8")).hexdigest() + return os.path.join(self.CACHE_DIR, f"{file_hash}.png") + + # Custom Color Functions + # def on_custom_color_changed(self, entry: Entry, *_): + # """Live preview for color entry changes.""" + # color = entry.get_text().strip() + # if is_valid_hex_color(color): + # self.color_preview.override_background_color( + # Gtk.StateFlags.NORMAL, Gdk.RGBA.from_string(color) + # ) + # elif is_valid_hue(color): + # hex_color = self.hue_to_hex(float(color)) + # self.color_preview.override_background_color( + # Gtk.StateFlags.NORMAL, Gdk.RGBA.from_string(hex_color) + # ) + # else: + # self.color_preview.override_background_color(Gtk.StateFlags.NORMAL, None) + + def on_custom_color_submitted(self, entry: Entry): + """Handle color submission.""" + color = entry.get_text().strip() + if is_valid_hex_color(color) or is_valid_hue(color): + color_value = ( + color if is_valid_hex_color(color) else self.hue_to_hex(float(color)) + ) + print(f"Applying custom color: {color_value}") + self.update_custom_color(color_value) + else: + print("Invalid color input. Please use a valid Hex or Hue.") + + def update_custom_color(self, color: str): + """Update the custom color in settings and regenerate theme.""" + try: + with open(self.SETTINGS_FILE, "r") as f: + settings = json.loads(f.read()) + + settings["custom-color"] = color + + with open(self.SETTINGS_FILE, "w") as f: + json.dump(settings, f, indent=2) + + home_dir = GLib.get_home_dir() + color_generator = os.path.join( + home_dir, "dotfiles/material-colors/generate.py" + ) + command = f'python -O {color_generator} --color "{color}"' + GLib.spawn_command_line_async(command) + print(f"Custom color applied: {color}") + except Exception as e: + print(f"Failed to update custom color: {e}") + + @staticmethod + def _is_image(file_name: str) -> bool: + return file_name.lower().endswith( + (".png", ".jpg", ".jpeg", ".bmp", ".gif", ".webp") + ) + + @staticmethod + def hue_to_hex(hue: float) -> str: + """Convert Hue to Hex color.""" + hue = hue / 360.0 + rgb = WallpaperSelector.hls_to_rgb(hue, 0.5, 1.0) + return f"#{int(rgb[0] * 255):02X}{int(rgb[1] * 255):02X}{int(rgb[2] * 255):02X}" + + @staticmethod + def hls_to_rgb(h: float, l: float, s: float) -> tuple: + """Convert HLS to RGB color.""" + if s == 0: + return l, l, l + + def hue2rgb(p, q, t): + if t < 0: + t += 1 + if t > 1: + t -= 1 + if t < 1 / 6: + return p + (q - p) * 6 * t + if t < 1 / 2: + return q + if t < 2 / 3: + return p + (q - p) * (2 / 3 - t) * 6 + return p + + q = l * (1 + s) if l < 0.5 else l + s - l * s + p = 2 * l - q + r = hue2rgb(p, q, h + 1 / 3) + g = hue2rgb(p, q, h) + b = hue2rgb(p, q, h - 1 / 3) + return r, g, b diff --git a/modules/launcher/components/wallpapers.py b/modules/launcher/components/wallpapers.py new file mode 100644 index 0000000..bf05e60 --- /dev/null +++ b/modules/launcher/components/wallpapers.py @@ -0,0 +1,264 @@ +import hashlib +import json +import os +from PIL import Image +from fabric.widgets.box import Box +from fabric.widgets.button import Button +from fabric.widgets.entry import Entry +from fabric.widgets.scrolledwindow import ScrolledWindow +from gi.repository import GdkPixbuf, GLib, Gtk, Gio +from snippets import MaterialIcon +from fabric.utils import exec_shell_command, get_relative_path +import concurrent.futures +from concurrent.futures import ThreadPoolExecutor +from snippets import WALLPAPERS_DIR + + +class WallpaperSelector(Box): + CACHE_DIR = os.path.expanduser("~/.cache/modus/wallpapers") + SETTINGS_FILE = get_relative_path("../../../config/assets/settings.json") + SCHEMES = { + "TonalSpot": "tonalSpot", + "Expressive": "expressive", + "FruitSalad": "fruitSalad", + "Monochrome": "monochrome", + "Rainbow": "rainbow", + "Vibrant": "vibrant", + "Neutral": "neutral", + "Fidelity": "fidelity", + "Content": "content", + } + + def __init__(self, **kwargs): + super().__init__(name="wallpapers", spacing=4, orientation="v", **kwargs) + self.launcher = kwargs["launcher"] + + # self.wallpapers_dir = os.path.expanduser("~/Pictures/wallpapers") + os.makedirs(self.CACHE_DIR, exist_ok=True) + + self.files = [f for f in os.listdir(WALLPAPERS_DIR) if self._is_image(f)] + self.thumbnails = [] + self.thumbnail_queue = [] + self.executor = ThreadPoolExecutor(max_workers=4) # Shared executor + + self.viewport = Gtk.IconView() + self.viewport.set_model(Gtk.ListStore(GdkPixbuf.Pixbuf, str)) + self.viewport.set_pixbuf_column(0) + self.viewport.set_text_column(1) + self.viewport.set_item_width(0) + self.viewport.connect("item-activated", self.on_wallpaper_selected) + + self.scrolled_window = ScrolledWindow( + name="scrolled-window", + spacing=10, + h_expand=True, + v_expand=True, + child=self.viewport, + ) + + self.search_entry = Entry( + name="search-entry", + h_expand=True, + notify_text=lambda entry, *_: self.arrange_viewport(entry.get_text()), + ) + + self.scheme_dropdown = Gtk.ComboBoxText() + self.scheme_dropdown.set_name("scheme-dropdown") + self.scheme_dropdown.set_tooltip_text("Select color scheme") + + for display_name, scheme_id in sorted(self.SCHEMES.items()): + self.scheme_dropdown.append(scheme_id, display_name) + + self.scheme_dropdown.set_active_id("tonalSpot") + self.scheme_dropdown.connect("changed", self.on_scheme_changed) + + # Set the initial icon based on dark mode state + initial_icon = "light_mode" if not self.check_dark_mode_state() else "dark_mode" + self.toggle_button = Button( + child=MaterialIcon(initial_icon), + name="toggle-launcher-button", + on_clicked=self.toggle_dark_mode, + ) + + self.dropdown_box = Box( + name="dropdown-box", + orientation="h", + children=[ + self.scheme_dropdown, + MaterialIcon("keyboard_arrow_down"), + ], + ) + + self.header_box = Box( + name="header-box", + spacing=10, + orientation="h", + children=[self.search_entry, self.dropdown_box, self.toggle_button], + ) + + self.add(self.header_box) + self.add(self.scrolled_window) + self._start_thumbnail_thread() + self.setup_file_monitor() + self.show_all() + + def setup_file_monitor(self): + gfile = Gio.File.new_for_path(WALLPAPERS_DIR) + self.file_monitor = gfile.monitor_directory(Gio.FileMonitorFlags.NONE, None) + self.file_monitor.connect("changed", self.on_directory_changed) + + def on_directory_changed(self, monitor, file, other_file, event_type): + file_name = file.get_basename() + if event_type == Gio.FileMonitorEvent.DELETED: + if file_name in self.files: + self.files.remove(file_name) + cache_path = self._get_cache_path(file_name) + if os.path.exists(cache_path): + try: + os.remove(cache_path) + except Exception as e: + print(f"Error deleting cache {cache_path}: {e}") + self.thumbnails = [(p, n) for p, n in self.thumbnails if n != file_name] + GLib.idle_add(self.arrange_viewport, self.search_entry.get_text()) + elif event_type == Gio.FileMonitorEvent.CREATED: + if self._is_image(file_name) and file_name not in self.files: + self.files.append(file_name) + self.files.sort() + self.executor.submit(self._process_file, file_name) + elif event_type == Gio.FileMonitorEvent.CHANGED: + if self._is_image(file_name) and file_name in self.files: + cache_path = self._get_cache_path(file_name) + if os.path.exists(cache_path): + try: + os.remove(cache_path) + except Exception as e: + print(f"Error deleting cache for changed file {file_name}: {e}") + self.executor.submit(self._process_file, file_name) + + def close_selector(self): + self.launcher.close() + + def arrange_viewport(self, query: str = ""): + self.viewport.get_model().clear() + filtered_thumbnails = [ + (thumb, name) + for thumb, name in self.thumbnails + if query.casefold() in name.casefold() + ] + + filtered_thumbnails.sort(key=lambda x: x[1].lower()) + + for pixbuf, file_name in filtered_thumbnails: + self.viewport.get_model().append([pixbuf, file_name]) + + def toggle_dark_mode(self, *_): + current_state = self.check_dark_mode_state() + new_mode = "dark" if not current_state else "light" + + # Call the shell script with the --set argument + command = f"bash {get_relative_path('../../../config/dark-theme.sh')} --set {new_mode}" + GLib.spawn_command_line_async(command) + + # Set the appropriate icon based on the mode + icon_name = "dark_mode" if new_mode == "dark" else "light_mode" + + # Update button icon + if self.toggle_button.get_child(): + self.toggle_button.remove(self.toggle_button.get_child()) + self.toggle_button.add(MaterialIcon(icon_name)) + self.toggle_button.show_all() + + def check_dark_mode_state(self): + result = exec_shell_command( + "gsettings get org.gnome.desktop.interface color-scheme" + ) + return result.strip().replace("'", "") == "prefer-dark" + + def on_wallpaper_selected(self, iconview, path): + model = iconview.get_model() + file_name = model[path][1] + full_path = os.path.join(WALLPAPERS_DIR, file_name) + selected_scheme = self.scheme_dropdown.get_active_id() + wallpaper = get_relative_path("../../../config/wallpaper.py") + command = f"python -O {wallpaper} -I {full_path}" + GLib.spawn_command_line_async(command) + self.update_scheme(selected_scheme) + + def on_scheme_changed(self, combo): + scheme_id = combo.get_active_id() + display_name = next( + name for name, id in self.SCHEMES.items() if id == scheme_id + ) + print(f"Color scheme selected: {display_name} ({scheme_id})") + self.update_scheme(scheme_id) + + color_generator = get_relative_path( + "../../../config/material-colors/generate.py" + ) + try: + command = f'python -O {color_generator} -R --scheme "{scheme_id}"' + GLib.spawn_command_line_async(command) + print(f"Applied color scheme: {display_name}") + except Exception as e: + print(f"Failed to apply color scheme: {e}") + + def update_scheme(self, scheme: str): + try: + with open(self.SETTINGS_FILE, "r") as f: + settings = json.loads(f.read()) + + settings["generation-scheme"] = scheme + + with open(self.SETTINGS_FILE, "w") as f: + json.dump(settings, f, indent=2) + except Exception as error: + print(f"Failed to update generation-scheme in settings.json: {error}") + + def _start_thumbnail_thread(self): + thread = GLib.Thread.new("thumbnail-loader", self._preload_thumbnails, None) + + def _preload_thumbnails(self, _data): + futures = [ + self.executor.submit(self._process_file, file_name) + for file_name in self.files + ] + concurrent.futures.wait(futures) + GLib.idle_add(self._process_batch) + + def _process_file(self, file_name): + full_path = os.path.join(WALLPAPERS_DIR, file_name) + cache_path = self._get_cache_path(file_name) + if not os.path.exists(cache_path): + try: + with Image.open(full_path) as img: + img.thumbnail((96, 96), Image.Resampling.BILINEAR) + img.save(cache_path, "PNG") + except Exception as e: + print(f"Error processing {file_name}: {e}") + return + self.thumbnail_queue.append((cache_path, file_name)) + GLib.idle_add(self._process_batch) + + def _process_batch(self): + batch = self.thumbnail_queue[:10] + del self.thumbnail_queue[:10] + for cache_path, file_name in batch: + try: + pixbuf = GdkPixbuf.Pixbuf.new_from_file(cache_path) + self.thumbnails.append((pixbuf, file_name)) + self.viewport.get_model().append([pixbuf, file_name]) + except Exception as e: + print(f"Error loading thumbnail {cache_path}: {e}") + if self.thumbnail_queue: + GLib.idle_add(self._process_batch) + return False + + def _get_cache_path(self, file_name: str) -> str: + file_hash = hashlib.md5(file_name.encode("utf-8")).hexdigest() + return os.path.join(self.CACHE_DIR, f"{file_hash}.png") + + @staticmethod + def _is_image(file_name: str) -> bool: + return file_name.lower().endswith( + (".png", ".jpg", ".jpeg", ".bmp", ".gif", ".webp") + ) diff --git a/modules/launcher/launcher.py b/modules/launcher/launcher.py new file mode 100644 index 0000000..b1f9320 --- /dev/null +++ b/modules/launcher/launcher.py @@ -0,0 +1,163 @@ +from fabric.widgets.centerbox import CenterBox +from fabric.widgets.stack import Stack +from fabric.widgets.box import Box +from fabric.widgets.wayland import WaylandWindow as Window +from gi.repository import Gtk +from modules.launcher.components import ( + AppLauncher, + BluetoothConnections, + Cliphist, + Emoji, + PowerMenu, + Sh, + TodoManager, + WallpaperSelector, +) + + +class Launcher(Window): + def __init__(self, **kwargs): + super().__init__( + layer="top", + anchor="center", + visible=False, + all_visible=False, + **kwargs, + ) + + self.launcher = AppLauncher(launcher=self) + self.wallpapers = WallpaperSelector(launcher=self) + self.power = PowerMenu(launcher=self) + self.emoji = Emoji(launcher=self) + self.cliphist = Cliphist(launcher=self) + self.todo = TodoManager() + self.bluetooth = BluetoothConnections() + self.sh = Sh(launcher=self) + + self.stack = Stack( + name="launcher-content", + v_expand=True, + h_expand=True, + transition_type="crossfade", + transition_duration=100, + children=[ + self.launcher, + self.wallpapers, + self.power, + self.emoji, + self.cliphist, + self.todo, + self.bluetooth, + self.sh, + ], + ) + + self.launcher_box = CenterBox( + name="launcher", + orientation="v", + start_children=self.stack, + ) + + self.add(self.launcher_box) + self.show_all() + self.hide() + self.wallpapers.viewport.hide() + self.add_keybinding("Escape", lambda *_: self.close()) + + def close(self): + self.set_keyboard_mode("none") + self.hide() + + for widget in [ + self.launcher, + self.wallpapers, + self.power, + self.emoji, + self.cliphist, + self.todo, + self.bluetooth, + self.sh, + ]: + if widget == self.wallpapers: + self.wallpapers.viewport.hide() + self.wallpapers.viewport.set_property("name", None) + + if hasattr(widget, "viewport") and widget.viewport: + widget.viewport.hide() + + for style in [ + "launcher", + "wallpapers", + "power", + "emoji", + "cliphist", + "todo", + "bluetooth", + "sh", + ]: + self.stack.remove_style_class(style) + + return True + + def open(self, widget): + self.set_keyboard_mode("exclusive") + self.show() + + widgets = { + "launcher": self.launcher, + "wallpapers": self.wallpapers, + "power": self.power, + "emoji": self.emoji, + "cliphist": self.cliphist, + "todo": self.todo, + "bluetooth": self.bluetooth, + "sh": self.sh, + } + + for w in widgets.values(): + w.hide() + for style in widgets.keys(): + self.stack.remove_style_class(style) + + if widget in widgets: + self.stack.get_style_context().add_class(widget) + self.stack.set_visible_child(widgets[widget]) + widgets[widget].show() + + # if widget != "launcher": + # self.launcher.hide() + + if widget != "wallpapers": + self.wallpapers.viewport.hide() + self.wallpapers.viewport.set_property("name", None) + + if widget == "launcher": + self.launcher.open_launcher() + self.launcher.search_entry.set_text("") + self.launcher.search_entry.grab_focus() + + elif widget == "wallpapers": + self.wallpapers.search_entry.set_text("") + self.wallpapers.search_entry.grab_focus() + self.wallpapers.viewport.show() + self.wallpapers.viewport.set_property("name", "wallpaper-icons") + + elif widget == "emoji": + self.emoji.open_launcher() + self.emoji.search_entry.set_text("") + self.emoji.search_entry.grab_focus() + + elif widget == "cliphist": + self.cliphist.open_launcher() + self.cliphist.search_entry.set_text("") + self.cliphist.search_entry.grab_focus() + + elif widget == "todo": + self.todo.open_launcher() + self.todo.todo_entry.set_text("") + self.todo.todo_entry.grab_focus() + + elif widget == "sh": + self.sh.open_launcher() + self.sh.search_entry.set_text("") + self.sh.search_entry.grab_focus() diff --git a/modules/notification_popup.py b/modules/notification_popup.py new file mode 100644 index 0000000..8835778 --- /dev/null +++ b/modules/notification_popup.py @@ -0,0 +1,303 @@ +import time + +import gi + +gi.require_version("GdkPixbuf", "2.0") + +from fabric.notifications import ( + Notification, + NotificationAction, + NotificationCloseReason, + Notifications, +) +from fabric.utils import invoke_repeater +from fabric.widgets.box import Box +from fabric.widgets.button import Button +from fabric.widgets.circularprogressbar import CircularProgressBar +from fabric.widgets.image import Image +from fabric.widgets.label import Label +from fabric.widgets.overlay import Overlay +from fabric.widgets.revealer import Revealer +from fabric.widgets.wayland import WaylandWindow as Window +from gi.repository import GdkPixbuf, GLib, GObject +from snippets import Animator, CustomImage + + +class AnimatedCircularProgressBar(CircularProgressBar): + def __init__(self, duration=0.6, bezier_curve=(0.34, 1.56, 0.64, 1.0), **kwargs): + super().__init__(**kwargs) + + self.animator = ( + Animator( + bezier_curve=bezier_curve, + duration=duration, + min_value=self.min_value, + max_value=self.value, + tick_widget=self, + notify_value=lambda p, *_: self.set_value(p.value), + ) + .build() + .play() + .unwrap() + ) + + def animate_value(self, value: float): + self.animator.pause() + self.animator.min_value = self.value + self.animator.max_value = value + self.animator.play() + return + + +class ActionButton(Button): + def __init__( + self, action: NotificationAction, action_number: int, total_actions: int + ): + self.action = action + super().__init__( + label=action.label, + h_expand=True, + on_clicked=self.on_clicked, + ) + if action_number == 0: + self.add_style_class("start-action") + elif action_number == total_actions - 1: + self.add_style_class("end-action") + else: + self.add_style_class("middle-action") + + def on_clicked(self, *_): + self.action.invoke() + self.action.parent.close("dismissed-by-user") + + +class NotificationWidget(Box): + def __init__(self, notification: Notification, **kwargs): + super().__init__( + name="notification", + spacing=8, + orientation="v", + **kwargs, + ) + + self._notification = notification + self._timer = None + self.anim_parts = 20 + self.anim_interval = self.get_timeout() / self.anim_parts + self.timeout_in_sec = self.get_timeout() / 1000 + self.time = GLib.DateTime.new_from_unix_local(time.time()).format("%m/%d") + self.connect("button-press-event", self.on_button_press) + + header_container = Box(spacing=8, orientation="h") + self.progress_timeout = AnimatedCircularProgressBar( + name="notification-circular-progress-bar", + size=30, + min_value=0, + max_value=1, + radius_color=True, + value=0, + ) + + header_container.children = ( + self.get_icon(notification.app_icon, notification.app_name, 25), + Label( + markup=str( + self._notification.summary + if self._notification.summary + else notification.app_name + ), + h_align="start", + style="font-weight: 900;", + ellipsization="end", + ), + ) + + header_container.pack_end( + Box( + v_align="start", + children=( + Overlay( + child=self.progress_timeout, + overlays=Button( + image=Image( + icon_name="window-close-symbolic", + icon_size=16, + ), + style_classes="close-button", + on_clicked=lambda *_: self._notification.close( + "dismissed-by-user" + ), + ), + ), + ), + ), + False, + False, + 0, + ) + + body_container = Box(spacing=4, orientation="h") + + if image_pixbuf := self._notification.image_pixbuf: + body_container.add( + Box( + v_expand=True, + v_align="center", + children=CustomImage( + pixbuf=image_pixbuf.scale_simple( + 64, 64, GdkPixbuf.InterpType.BILINEAR + ), + style_classes="image", + ), + ) + ) + + body_container.add( + Label( + markup=self._notification.body, + line_wrap="word-char", + v_align="start", + ellipsization="end", + max_chars_width=40, + h_align="start", + ) + ) + + actions_container = Box( + spacing=4, + orientation="h", + name="notification-action-buttons", + children=[ + ActionButton(action, i, len(self._notification.actions)) + for i, action in enumerate(self._notification.actions) + ], + h_expand=True, + ) + + self.add(header_container) + self.add(body_container) + self.add(actions_container) + + # Destroy this widget once the notification is closed + self._notification.connect( + "closed", + lambda *_: ( + parent.remove(self) if (parent := self.get_parent()) else None, # type: ignore + self.destroy(), + ), + invoke_repeater( + 5 * 1000, + lambda: self._notification.close("expired"), + initial_call=False, + ), + ) + + self.start_timer() + + def start_timer(self): + self._timer = GObject.timeout_add(200, self.update_progress) + + def update_progress(self): + self.progress_timeout.animate_value( + self.progress_timeout.value + 1 / self.anim_parts + ) + if self.progress_timeout.value >= self.timeout_in_sec: + self.progress_timeout.value = self.timeout_in_sec + self.stop_timer() + return True # Continue the timer + + def stop_timer(self): + if self._timer: + GObject.source_remove(self._timer) + self._timer = None + + def get_icon(self, app_icon, app_name, size) -> Image: + app_name = ( + app_name.lower().replace(" ", "-") + if isinstance(app_name, str) + else app_name + ) + if isinstance(app_icon, str) and app_icon.strip(): + if app_icon.startswith("file://") or app_icon.startswith("/"): + file_path = app_icon[7:] if app_icon.startswith("file://") else app_icon + try: + pixbuf = GdkPixbuf.Pixbuf.new_from_file(file_path) + scaled_pixbuf = pixbuf.scale_simple( + size, size, GdkPixbuf.InterpType.BILINEAR + ) + return Image(name="notification-icon", pixbuf=scaled_pixbuf) + except Exception as e: + print(f"DEBUG: Failed to load image from {file_path}: {e}") + + if app_icon and not app_icon.startswith(("file://", "/")): + icon_name = app_icon + elif app_name and app_name.strip() not in ["notify-send", ""]: + icon_name = app_name.strip() + else: + icon_name = "dialog-information-symbolic" + + return Image(name="notification-icon", icon_name=icon_name, icon_size=size) + + def on_button_press(self, _, event): + if event.button != 1: + (self._notification.close("dismissed-by-user"),) + + def get_timeout(self): + return ( + self._notification.timeout if self._notification.timeout != -1 else 5 * 1000 + ) + + +class NotificationRevealer(Revealer): + def __init__(self, notification: Notification, **kwargs): + self.notif_box = NotificationWidget(notification) + self._notification = notification + super().__init__( + child=Box( + style="margin: 12px;", + children=[self.notif_box], + ), + transition_duration=500, + transition_type="crossfade", + ) + + self.connect( + "notify::child-revealed", + lambda *_: self.destroy() if not self.get_child_revealed() else None, + ) + + self._notification.connect("closed", self.on_resolved) + + def on_resolved( + self, + notification: Notification, + reason: NotificationCloseReason, + ): + self.set_reveal_child(False) + + +class NotificationPopup(Window): + def __init__(self): + self._server = Notifications() + self.notifications = Box( + v_expand=True, + h_expand=True, + style="margin: 1px 0px 1px 1px;", + orientation="v", + spacing=5, + ) + self._server.connect("notification-added", self.on_new_notification) + + super().__init__( + anchor="bottom", + child=self.notifications, + layer="overlay", + all_visible=True, + visible=True, + exclusive=False, + ) + + def on_new_notification(self, fabric_notif, id): + new_box = NotificationRevealer(fabric_notif.get_notification_from_id(id)) + self.notifications.add(new_box) + new_box.set_reveal_child(True) diff --git a/modules/osd.py b/modules/osd.py new file mode 100644 index 0000000..c2764cb --- /dev/null +++ b/modules/osd.py @@ -0,0 +1,228 @@ +import time +from typing import ClassVar, Literal +from fabric.utils import invoke_repeater +from fabric.widgets.box import Box +from fabric.widgets.revealer import Revealer +from fabric.widgets.scale import Scale, ScaleMark +from fabric.widgets.wayland import WaylandWindow as Window +from gi.repository import GObject +from snippets import Animator, MaterialIcon +from services import brightness, audio + + +class AnimatedScale(Scale): + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.animator = None # Lazily initialized + + def animate_value(self, value: float): + if not self.animator: + self.animator = Animator( + bezier_curve=(0.34, 1.56, 0.64, 1.0), + duration=0.8, + min_value=self.min_value, + max_value=self.value, + tick_widget=self, + notify_value=lambda p, *_: self.set_value(p.value), + ) + self.animator.pause() + self.animator.min_value = self.value + self.animator.max_value = value + self.animator.play() + + +class BrightnessOSDContainer(Box): + def __init__(self, **kwargs): + super().__init__(**kwargs, orientation="h", spacing=12, name="osd") + self.brightness_service = brightness + self.icon = MaterialIcon(icon_name="brightness_7", size=20) + self.scale = AnimatedScale( + marks=(ScaleMark(value=i) for i in range(0, 101, 10)), + value=70, + min_value=0, + max_value=100, + increments=(1, 1), + orientation="h", + ) + + self.add(self.icon) + self.add(self.scale) + self.update_brightness() + + self.scale.connect("value-changed", lambda *_: self.update_brightness()) + self.brightness_service.connect("screen", self.on_brightness_changed) + + def update_brightness(self) -> None: + current_brightness = self.brightness_service.screen_brightness + if current_brightness != 0: + normalized_brightness = self._normalize_brightness(current_brightness) + self.scale.animate_value(normalized_brightness) + self.update_icon(normalized_brightness) + + def update_icon(self, brightness_percentage: float) -> None: + icon_name = self.get_brightness_icon(brightness_percentage) + self.icon.set_label(icon_name) + + def on_brightness_changed(self, _sender: any, value: float, *_args) -> None: + normalized_brightness = self._normalize_brightness(value) + self.scale.animate_value(normalized_brightness) + self.update_icon(normalized_brightness) + + def _normalize_brightness(self, brightness: float) -> float: + return (brightness / self.brightness_service.max_screen) * 100 + + def get_brightness_icon(self, brightness_percentage: float) -> str: + brightness_icons = [ + "brightness_1", + "brightness_2", + "brightness_3", + "brightness_4", + "brightness_5", + "brightness_6", + "brightness_7", + ] + thresholds = [ + 100 / (1.6 ** (len(brightness_icons) - i - 1)) + for i in range(len(brightness_icons)) + ] + + for i, threshold in enumerate(thresholds): + if brightness_percentage < threshold: + return brightness_icons[i] + + return brightness_icons[-1] + + +class AudioOSDContainer(Box): + __gsignals__: ClassVar[dict] = { + "volume-changed": (GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, ()), + } + + def __init__(self, **kwargs): + super().__init__(**kwargs, orientation="h", spacing=13, name="osd") + self.audio = audio + self.icon = MaterialIcon(icon_name="volume_up", size=20) + self.scale = AnimatedScale( + marks=(ScaleMark(value=i) for i in range(1, 100, 10)), + value=70, + min_value=0, + max_value=100, + increments=(1, 1), + orientation="h", + ) + + self.add(self.icon) + self.add(self.scale) + self.sync_with_audio() + + self.scale.connect("value-changed", self.on_volume_changed) + self.audio.connect("notify::speaker", self.on_audio_speaker_changed) + + def sync_with_audio(self): + if self.audio.speaker: + volume = round(self.audio.speaker.volume) + self.scale.set_value(volume) + self.update_icon(volume) + + def on_volume_changed(self, *_): + if self.audio.speaker: + volume = self.scale.value + if 0 <= volume <= 100: + self.audio.speaker.set_volume(volume) + self.update_icon(volume) + self.emit("volume-changed") + + def on_audio_speaker_changed(self, *_): + if self.audio.speaker: + self.audio.speaker.connect("notify::volume", self.update_volume) + self.update_volume() + + def update_volume(self, *_): + if self.audio.speaker and not self.is_hovered(): + volume = round(self.audio.speaker.volume) + self.scale.set_value(volume) + self.update_icon(volume) + + def update_icon(self, volume): + icon_name = self._get_audio_icon_name(volume) + self.icon.set_label(icon_name) + + def _get_audio_icon_name(self, volume: int) -> str: + if volume == 101: + return "sound_detection_loud_sound" + elif volume >= 67: + return "volume_up" + elif volume >= 34: + return "volume_down" + elif volume >= 1: + return "volume_mute" + else: + return "volume_off" + + +class OSDContainer(Window): + def __init__(self, **kwargs): + self.audio_container = AudioOSDContainer() + self.brightness_container = BrightnessOSDContainer() + + self.timeout = 1000 + + self.revealer = Revealer( + transition_type="slide-right", + transition_duration=200, + child_revealed=False, + ) + + self.main_box = Box( + orientation="v", + spacing=13, + children=[self.revealer], + ) + + super().__init__( + layer="overlay", + anchor="bottom", + child=self.main_box, + visible=False, + pass_through=True, + keyboard_mode="on-demand", + **kwargs, + ) + + self.last_activity_time = time.time() + + self.audio_container.audio.connect("notify::speaker", self.show_audio) + self.brightness_container.brightness_service.connect( + "screen", self.show_brightness + ) + self.audio_container.connect("volume-changed", self.show_audio) + + invoke_repeater(100, self.check_inactivity, initial_call=True) + + def show_audio(self, *_): + self.show_box(box_to_show="audio") + self.reset_inactivity_timer() + + def show_brightness(self, *_): + self.show_box(box_to_show="brightness") + self.reset_inactivity_timer() + + def show_box(self, box_to_show: Literal["audio", "brightness"]): + self.set_visible(True) + if box_to_show == "audio": + self.revealer.children = self.audio_container + elif box_to_show == "brightness": + self.revealer.children = self.brightness_container + self.revealer.set_reveal_child(True) + self.reset_inactivity_timer() + + def start_hide_timer(self): + self.set_visible(False) + + def reset_inactivity_timer(self): + self.last_activity_time = time.time() + + def check_inactivity(self): + if time.time() - self.last_activity_time >= (self.timeout / 1000): + self.start_hide_timer() + return True diff --git a/services/__init__.py b/services/__init__.py new file mode 100644 index 0000000..b464ce6 --- /dev/null +++ b/services/__init__.py @@ -0,0 +1,9 @@ +from fabric.audio import Audio +from .brightness import Brightness +from .screen_record import ScreenRecorder +from .wifi import * + + +brightness = Brightness() +audio = Audio() +sc = ScreenRecorder() diff --git a/services/__pycache__/__init__.cpython-313.pyc b/services/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..19785d6 Binary files /dev/null and b/services/__pycache__/__init__.cpython-313.pyc differ diff --git a/services/__pycache__/brightness.cpython-313.pyc b/services/__pycache__/brightness.cpython-313.pyc new file mode 100644 index 0000000..3875ffe Binary files /dev/null and b/services/__pycache__/brightness.cpython-313.pyc differ diff --git a/services/__pycache__/config.cpython-313.pyc b/services/__pycache__/config.cpython-313.pyc new file mode 100644 index 0000000..a8f643b Binary files /dev/null and b/services/__pycache__/config.cpython-313.pyc differ diff --git a/services/__pycache__/screen_record.cpython-313.pyc b/services/__pycache__/screen_record.cpython-313.pyc new file mode 100644 index 0000000..e7494e1 Binary files /dev/null and b/services/__pycache__/screen_record.cpython-313.pyc differ diff --git a/services/__pycache__/wifi.cpython-313.pyc b/services/__pycache__/wifi.cpython-313.pyc new file mode 100644 index 0000000..6c5ca64 Binary files /dev/null and b/services/__pycache__/wifi.cpython-313.pyc differ diff --git a/services/brightness.py b/services/brightness.py new file mode 100644 index 0000000..49e7040 --- /dev/null +++ b/services/brightness.py @@ -0,0 +1,115 @@ +import os +import subprocess + +from fabric.core.service import Property, Service, Signal +from fabric.utils import exec_shell_command, monitor_file +from gi.repository import GLib +from loguru import logger # Import loguru logger + + +# Helper function to execute brightnessctl asynchronously +def exec_brightnessctl_async(args: str): + # Executes brightnessctl command asynchronously, ensuring no resource leaks. + try: + # Use subprocess.Popen to run the command without blocking + process = subprocess.Popen( + f"brightnessctl {args}", + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + + # Wait for the process to complete + stdout, stderr = process.communicate() + + if process.returncode != 0: + logger.error(f"Error executing brightnessctl: {stderr.decode().strip()}") + else: + logger.debug( + f"brightnessctl output: {stdout.decode().strip()}" + ) # Optional: Log the output + except Exception as e: + logger.exception(f"Exception in exec_brightnessctl_async: {e}") + + +# Discover screen backlight device +try: + screen_device = str(exec_shell_command("ls -w1 /sys/class/backlight")).split("\n")[ + 0 + ] +except IndexError: + screen_device = None + + +class NoBrightnessError(ImportError): + # Raised when brightness-related dependencies are missing. + def __init__(self, *args): + super().__init__( + "Brightness control not available or missing dependencies", *args + ) + + +class Brightness(Service): + # Service to manage screen brightness levels. + + @Signal + def screen(self, value: int) -> None: + """Signal emitted when screen brightness changes.""" + pass # Implement as needed for your application + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + if not screen_device: + raise NoBrightnessError() + + # Path for screen backlight control + self.screen_backlight_path = f"/sys/class/backlight/{screen_device}" + + # Initialize maximum brightness level + self.max_screen = self._read_max_brightness(self.screen_backlight_path) + + # Monitor screen brightness file + self.screen_monitor = monitor_file(f"{self.screen_backlight_path}/brightness") + self.screen_monitor.connect( + "changed", + lambda _, file, *args: self.emit( + "screen", round(int(file.load_bytes()[0].get_data())) + ), + ) + + # Log the initialization of the service + logger.info(f"Brightness service initialized for device: {screen_device}") + + def _read_max_brightness(self, path: str) -> int: + # Reads the maximum brightness value from the specified path. + max_brightness_path = os.path.join(path, "max_brightness") + if os.path.exists(max_brightness_path): + with open(max_brightness_path) as f: + return int(f.read().strip()) + return -1 # Return -1 if file doesn't exist, indicating an error. + + @Property(int, "read-write") + def screen_brightness(self) -> int: + # Property to get or set the screen brightness. + brightness_path = os.path.join(self.screen_backlight_path, "brightness") + if os.path.exists(brightness_path): + with open(brightness_path) as f: + return int(f.read().strip()) + logger.warning(f"Brightness file does not exist: {brightness_path}") + return -1 # Return -1 if file doesn't exist, indicating error. + + @screen_brightness.setter + def screen_brightness(self, value: int): + # Setter for screen brightness property. + if not (0 <= value <= self.max_screen): + value = max(0, min(value, self.max_screen)) + + try: + exec_brightnessctl_async(f"--device '{screen_device}' set {value}") + self.emit("screen", int((value / self.max_screen) * 100)) + logger.info(f"Set screen brightness to {value} (out of {self.max_screen})") + except GLib.Error as e: + logger.error(f"Error setting screen brightness: {e.message}") + except Exception as e: + logger.exception(f"Unexpected error setting screen brightness: {e}") diff --git a/services/screen_record.py b/services/screen_record.py new file mode 100644 index 0000000..7fbf6d5 --- /dev/null +++ b/services/screen_record.py @@ -0,0 +1,142 @@ +import datetime +import subprocess + +from fabric.core.service import Property, Service, Signal +from fabric.utils import exec_shell_command, exec_shell_command_async +from gi.repository import Gio, GLib +from loguru import logger + + +class ScreenRecorder(Service): + @Signal + def recording(self, value: bool) -> None: ... + + def __init__(self, **kwargs): + self.screenshot_path = GLib.get_home_dir() + "/Pictures/Screenshots/" + self.screenrecord_path = GLib.get_home_dir() + "/Videos/Screencasting/" + + super().__init__(**kwargs) + + def screenshot(self, fullscreen=False, save_copy=True): + time = datetime.datetime.today().strftime("%Y-%m-%d_%H-%M-%S") + file_path = self.screenshot_path + str(time) + ".png" + command = ( + ["grimblast", "copysave", "screen", file_path] + if save_copy + else ["grimblast", "copyscreen"] + ) + if not fullscreen: + command[2] = "area" + try: + subprocess.run(command, check=True) + self.send_screenshot_notification( + file_path=file_path if file_path else None, + ) + except Exception as e: + logger.error(f"[SCREENSHOT] Failed to run command: {command}") + + def screencast_start(self, fullscreen=False): + if self.is_recording: + logger.error( + "[SCREENRECORD] Another instance of wf-recorder is already running" + ) + return + time = datetime.datetime.today().strftime("%Y-%m-%d_%H-%M-%S") + file_path = self.screenrecord_path + str(time) + ".mp4" + self._current_screencast_path = file_path + area = "" if fullscreen else f"-g '{exec_shell_command('slurp')}'" + command = f"wf-recorder --file={file_path} --pixel-format yuv420p {area}" + exec_shell_command_async(command) + self.emit("recording", True) + + def screencast_stop(self): + exec_shell_command_async("killall -INT wf-recorder") + self.emit("recording", False) + self.send_screencast_notification(self._current_screencast_path) + + def send_screencast_notification(self, file_path): + # TODO: Generate a thumbnail + # exec_shell_command_async(f"totem-video-thumbnailer {file_path} {FABRIC_CACHE}") + + cmd = ["notify-send"] + cmd.extend( + [ + "-A", + "files=Show in Files", + "-A", + "view=View", + "-i", + "camera-video-symbolic", + "-a", + "Fabric Screenshot Utility", + "Screencast Saved", + f"Saved Screencast at {file_path}", + ] + ) + + proc: Gio.Subprocess = Gio.Subprocess.new(cmd, Gio.SubprocessFlags.STDOUT_PIPE) + + def do_callback(process: Gio.Subprocess, task: Gio.Task): + try: + _, stdout, stderr = process.communicate_utf8_finish(task) + except Exception: + logger.error( + f"[SCREENCAST] Failed read notification action with error {stderr}" + ) + return + + match stdout.strip("\n"): + case "files": + exec_shell_command_async(f"xdg-open {self.screenrecord_path}") + case "view": + exec_shell_command_async(f"xdg-open {file_path}") + + proc.communicate_utf8_async(None, None, do_callback) + + def send_screenshot_notification(self, file_path=None): + cmd = ["notify-send"] + cmd.extend( + [ + "-A", + "files=Show in Files", + "-A", + "view=View", + "-A", + "edit=Edit", + "-i", + "camera-photo-symbolic", + "-a", + "Fabric Screenshot Utility", + "-h", + f"STRING:image-path:{file_path}", + "Screenshot Saved", + f"Saved Screenshot at {file_path}", + ] + if file_path + else ["Screenshot Sent to Clipboard"] + ) + + proc: Gio.Subprocess = Gio.Subprocess.new(cmd, Gio.SubprocessFlags.STDOUT_PIPE) + + def do_callback(process: Gio.Subprocess, task: Gio.Task): + try: + _, stdout, stderr = process.communicate_utf8_finish(task) + except Exception: + logger.error( + f"[SCREENSHOT] Failed read notification action with error {stderr}" + ) + return + + match stdout.strip("\n"): + case "files": + exec_shell_command_async(f"xdg-open {self.screenshot_path}") + case "view": + exec_shell_command_async(f"xdg-open {file_path}") + case "edit": + exec_shell_command_async(f"swappy -f {file_path}") + + proc.communicate_utf8_async(None, None, do_callback) + + @Property(bool, "readable", default_value=False) + def is_recording(self): + return False if len(exec_shell_command("pidof wf-recorder")) == 0 else True diff --git a/services/wifi.py b/services/wifi.py new file mode 100644 index 0000000..b048f23 --- /dev/null +++ b/services/wifi.py @@ -0,0 +1,321 @@ +from typing import Any, List, Literal +import gi +from fabric.core.service import Property, Service, Signal +from fabric.utils import bulk_connect +from gi.repository import Gio +from loguru import logger + +from fabric.utils import exec_shell_command_async + +from gi.repository.NM import DeviceEthernet, DeviceWifi + +try: + gi.require_version("NM", "1.0") + from gi.repository import NM +except ValueError: + logger.error("Failed to start network manager") + + +class Wifi(Service): + @Signal + def changed(self) -> None: ... + + @Signal + def enabled(self) -> bool: ... + + def __init__(self, client: NM.Client, device: NM.DeviceWifi, **kwargs): + self._client: NM.Client = client + self._device: NM.DeviceWifi = device + self._ap: NM.AccessPoint | None = None + self._ap_signal: int | None = None + super().__init__(**kwargs) + + self._client.connect( + "notify::wireless-enabled", + lambda *args: self.notifier("enabled", args), + ) + if self._device: + bulk_connect( + self._device, + { + "notify::active-access-point": lambda *args: self._activate_ap(), + "access-point-added": lambda *args: self.emit("changed"), + "access-point-removed": lambda *args: self.emit("changed"), + "state-changed": lambda *args: self.ap_update(), + }, + ) + self._activate_ap() + + def ap_update(self): + self.emit("changed") + for sn in [ + "enabled", + "internet", + "strength", + "frequency", + "access-points", + "ssid", + "state", + "icon-name", + ]: + self.notify(sn) + + def _activate_ap(self): + if self._ap: + self._ap.disconnect(self._ap_signal) + self._ap = self._device.get_active_access_point() + if not self._ap: + return + + self._ap_signal = self._ap.connect( + "notify::strength", lambda *args: self.ap_update() + ) # type: ignore + + def toggle_wifi(self): + self._client.wireless_set_enabled(not self._client.wireless_get_enabled()) + + # def set_active_ap(self, ap): + # self._device.access + + def scan(self): + self._device.request_scan_async( + None, + lambda device, result: [ + device.request_scan_finish(result), + self.emit("changed"), + ], + ) + + def notifier(self, name: str, *args): + self.notify(name) + self.emit("changed") + return + + @Property(bool, "read-write", default_value=False) + def enabled(self) -> bool: # type: ignore + return bool(self._client.wireless_get_enabled()) + + @enabled.setter + def enabled(self, value: bool): + self._client.wireless_set_enabled(value) + + @Property(int, "readable") + def strength(self): + return self._ap.get_strength() if self._ap else -1 + + @Property(str, "readable") + def icon_name(self): + if not self._ap: + return "network-wireless-disabled-symbolic" + + if self.internet == "activated": + return { + 80: "network-wireless-signal-excellent-symbolic", + 60: "network-wireless-signal-good-symbolic", + 40: "network-wireless-signal-ok-symbolic", + 20: "network-wireless-signal-weak-symbolic", + 00: "network-wireless-signal-none-symbolic", + }.get( + min(80, 20 * round(self._ap.get_strength() / 20)), + "network-wireless-no-route-symbolic", + ) + if self.internet == "activating": + return "network-wireless-acquiring-symbolic" + + return "network-wireless-offline-symbolic" + + @Property(int, "readable") + def frequency(self): + return self._ap.get_frequency() if self._ap else -1 + + @Property(int, "readable") + def internet(self): + return { + NM.ActiveConnectionState.ACTIVATED: "activated", + NM.ActiveConnectionState.ACTIVATING: "activating", + NM.ActiveConnectionState.DEACTIVATING: "deactivating", + NM.ActiveConnectionState.DEACTIVATED: "deactivated", + }.get( + self._device.get_active_connection().get_state(), + "unknown", + ) + + @Property(object, "readable") + def access_points(self) -> List[object]: + points: list[NM.AccessPoint] = self._device.get_access_points() + + def make_ap_dict(ap: NM.AccessPoint): + return { + "bssid": ap.get_bssid(), + # "address": ap.get_ + "last_seen": ap.get_last_seen(), + "ssid": NM.utils_ssid_to_utf8(ap.get_ssid().get_data()) + if ap.get_ssid() + else "Unknown", + "active-ap": self._ap, + "strength": ap.get_strength(), + "frequency": ap.get_frequency(), + "icon-name": { + 80: "network-wireless-signal-excellent-symbolic", + 60: "network-wireless-signal-good-symbolic", + 40: "network-wireless-signal-ok-symbolic", + 20: "network-wireless-signal-weak-symbolic", + 00: "network-wireless-signal-none-symbolic", + }.get( + min(80, 20 * round(ap.get_strength() / 20)), + "network-wireless-no-route-symbolic", + ), + } + + return list(map(make_ap_dict, points)) + + @Property(str, "readable") + def ssid(self): + if not self._ap: + return "Disconnected" + ssid = self._ap.get_ssid().get_data() + return NM.utils_ssid_to_utf8(ssid) if ssid else "Unknown" + + @Property(int, "readable") + def state(self): + return { + NM.DeviceState.UNMANAGED: "unmanaged", + NM.DeviceState.UNAVAILABLE: "unavailable", + NM.DeviceState.DISCONNECTED: "disconnected", + NM.DeviceState.PREPARE: "prepare", + NM.DeviceState.CONFIG: "config", + NM.DeviceState.NEED_AUTH: "need_auth", + NM.DeviceState.IP_CONFIG: "ip_config", + NM.DeviceState.IP_CHECK: "ip_check", + NM.DeviceState.SECONDARIES: "secondaries", + NM.DeviceState.ACTIVATED: "activated", + NM.DeviceState.DEACTIVATING: "deactivating", + NM.DeviceState.FAILED: "failed", + }.get(self._device.get_state(), "unknown") + + +class Ethernet(Service): + @Signal + def changed(self) -> None: ... + + @Signal + def enabled(self) -> bool: ... + + @Property(int, "readable") + def speed(self) -> int: + return self._device.get_speed() + + @Property(str, "readable") + def internet(self) -> str: + return { + NM.ActiveConnectionState.ACTIVATED: "activated", + NM.ActiveConnectionState.ACTIVATING: "activating", + NM.ActiveConnectionState.DEACTIVATING: "deactivating", + NM.ActiveConnectionState.DEACTIVATED: "deactivated", + }.get( + self._device.get_active_connection().get_state(), + "disconnected", + ) + + @Property(str, "readable") + def icon_name(self) -> str: + network = self.internet + if network == "activated": + return "network-wired-symbolic" + + elif network == "activating": + return "network-wired-acquiring-symbolic" + + elif self._device.get_connectivity != NM.ConnectivityState.FULL: + return "network-wired-no-route-symbolic" + + return "network-wired-disconnected-symbolic" + + def __init__(self, client: NM.Client, device: NM.DeviceEthernet, **kwargs) -> None: + super().__init__(**kwargs) + self._client: NM.Client = client + self._device: NM.DeviceEthernet = device + + for pn in ( + "active-connection", + "icon-name", + "internet", + "speed", + "state", + ): + self._device.connect(f"notify::{pn}", lambda *_: self.notifier(pn)) + + self._device.connect("notify::speed", lambda *_: print(_)) + + def notifier(self, pn): + self.notify(pn) + self.emit("changed") + + +class NetworkClient(Service): + @Signal + def device_ready(self) -> None: ... + + def __init__(self, **kwargs): + self._client: NM.Client | None = None + self.wifi_device: Wifi | None = None + self.ethernet_device: Ethernet | None = None + super().__init__(**kwargs) + NM.Client.new_async( + cancellable=None, + callback=self._init_network_client, + **kwargs, + ) + + def _init_network_client(self, client: NM.Client, task: Gio.Task, **kwargs): + self._client = client + wifi_device: NM.DeviceWifi | None = self._get_device(NM.DeviceType.WIFI) # type: ignore + ethernet_device: NM.DeviceEthernet | None = self._get_device( + NM.DeviceType.ETHERNET + ) + + if wifi_device: + self.wifi_device = Wifi(self._client, wifi_device) + self.emit("device-ready") + + if ethernet_device: + self.ethernet_device = Ethernet(client=self._client, device=ethernet_device) + self.emit("device-ready") + + self.notify("primary-device") + + def _get_device(self, device_type) -> Any: + devices: List[NM.Device] = self._client.get_devices() # type: ignore + return next( + ( + x + for x in devices + if x.get_device_type() == device_type + and x.get_active_connection() != None + ), + None, + ) + + def _get_primary_device(self) -> Literal["wifi", "wired"] | None: + if not self._client: + return None + return ( + "wifi" + if "wireless" + in str(self._client.get_primary_connection().get_connection_type()) + else "wired" + if "ethernet" + in str(self._client.get_primary_connection().get_connection_type()) + else None + ) + + def connect_wifi_bssid(self, bssid): + # We are using nmcli here, idk im lazy + exec_shell_command_async( + f"nmcli device wifi connect {bssid}", lambda *args: print(args) + ) + + @Property(str, "readable") + def primary_device(self) -> Literal["wifi", "wired"] | None: + return self._get_primary_device() + diff --git a/snippets/__init__.py b/snippets/__init__.py new file mode 100644 index 0000000..d4d3f96 --- /dev/null +++ b/snippets/__init__.py @@ -0,0 +1,26 @@ +from .animator import * +from .custom_image import * +from .icon import * +import json +import os +from fabric.utils import get_relative_path + + +def read_config(): + config_path = get_relative_path("../config/assets/launcher.json") + + with open(config_path, "r") as file: + # Load JSON data into a Python dictionary + data = json.load(file) + return data + + +default_wallpapers_dir = get_relative_path("../assets/wallpaper") +json_config_path = get_relative_path("../config/assets/config.json") + +if os.path.exists(json_config_path): + with open(json_config_path, "r") as f: + config = json.load(f) + WALLPAPERS_DIR = config.get("wallpapers_dir", default_wallpapers_dir) +else: + WALLPAPERS_DIR = default_wallpapers_dir diff --git a/snippets/__pycache__/__init__.cpython-313.pyc b/snippets/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..3e7b2ab Binary files /dev/null and b/snippets/__pycache__/__init__.cpython-313.pyc differ diff --git a/snippets/__pycache__/animator.cpython-313.pyc b/snippets/__pycache__/animator.cpython-313.pyc new file mode 100644 index 0000000..70b6ba5 Binary files /dev/null and b/snippets/__pycache__/animator.cpython-313.pyc differ diff --git a/snippets/__pycache__/config.cpython-313.pyc b/snippets/__pycache__/config.cpython-313.pyc new file mode 100644 index 0000000..9409492 Binary files /dev/null and b/snippets/__pycache__/config.cpython-313.pyc differ diff --git a/snippets/__pycache__/custom_image.cpython-313.pyc b/snippets/__pycache__/custom_image.cpython-313.pyc new file mode 100644 index 0000000..d5773b9 Binary files /dev/null and b/snippets/__pycache__/custom_image.cpython-313.pyc differ diff --git a/snippets/__pycache__/functions.cpython-313.pyc b/snippets/__pycache__/functions.cpython-313.pyc new file mode 100644 index 0000000..a0bd12d Binary files /dev/null and b/snippets/__pycache__/functions.cpython-313.pyc differ diff --git a/snippets/__pycache__/icon.cpython-313.pyc b/snippets/__pycache__/icon.cpython-313.pyc new file mode 100644 index 0000000..ef53c4a Binary files /dev/null and b/snippets/__pycache__/icon.cpython-313.pyc differ diff --git a/snippets/animator.py b/snippets/animator.py new file mode 100644 index 0000000..1799ae6 --- /dev/null +++ b/snippets/animator.py @@ -0,0 +1,179 @@ +from typing import cast + +from fabric import Property, Service, Signal +from gi.repository import GLib, Gtk + + +class Animator(Service): + @Signal + def finished(self) -> None: ... + + @Property(tuple[float, float, float, float], "read-write") + def bezier_curve(self) -> tuple[float, float, float, float]: + return self._bezier_curve + + @bezier_curve.setter + def bezier_curve(self, value: tuple[float, float, float, float]): + self._bezier_curve = value + return + + @Property(float, "read-write") + def value(self): + return self._value + + @value.setter + def value(self, value: float): + self._value = value + return + + @Property(float, "read-write") + def max_value(self): + return self._max_value + + @max_value.setter + def max_value(self, value: float): + self._max_value = value + return + + @Property(float, "read-write") + def min_value(self): + return self._min_value + + @min_value.setter + def min_value(self, value: float): + self._min_value = value + return + + @Property(bool, "read-write", default_value=False) + def playing(self): + return self._playing + + @playing.setter + def playing(self, value: bool): + self._playing = value + return + + @Property(bool, "read-write", default_value=False) + def repeat(self): + return self._repeat + + @repeat.setter + def repeat(self, value: bool): + self._repeat = value + return + + def __init__( + self, + bezier_curve: tuple[float, float, float, float], + duration: float, + min_value: float = 0.0, + max_value: float = 1.0, + repeat: bool = False, + tick_widget: Gtk.Widget | None = None, + **kwargs, + ): + super().__init__(**kwargs) + self._bezier_curve = (1, 0, 1, 1) + self._duration = 5 + self._value = 0.0 + self._min_value = 0.0 + self._max_value = 1.0 + self._repeat = False + + self.bezier_curve = bezier_curve + self.duration = duration + self.value = min_value + self.min_value = min_value + self.max_value = max_value + self.repeat = repeat + + self.playing = False + self._start_time = None + self._tick_handler = None + self._timeline_pos = 0 + self._tick_widget = tick_widget + + def do_get_time_now(self): + return GLib.get_monotonic_time() / 1_000_000 + + def do_lerp(self, start: float, end: float, time: float) -> float: + return start + (end - start) * time + + def do_interpolate_cubic_bezier(self, time: float) -> float: + y_points = (0, self.bezier_curve[1], self.bezier_curve[3], 1) + return ( + (1 - time) ** 3 * y_points[0] + + 3 * (1 - time) ** 2 * time * y_points[1] + + 3 * (1 - time) * time**2 * y_points[2] + + time**3 * y_points[3] + ) + + def do_ease(self, time: float) -> float: + return self.do_lerp( + self.min_value, self.max_value, self.do_interpolate_cubic_bezier(time) + ) + + def do_update_value(self, delta_time: float): + if not self.playing: + return + + elapsed_time = delta_time - cast(float, self._start_time) + + self._timeline_pos = min(1, elapsed_time / self.duration) + + self.value = self.do_ease(self._timeline_pos) + + if not self._timeline_pos >= 1: + return + + if not self.repeat: + self.value = self.max_value + self.finished() + self.pause() + return + + self._start_time = delta_time + self._timeline_pos = 0 + return + + def do_handle_tick(self, *_): + current_time = self.do_get_time_now() + self.do_update_value(current_time) + return True + + def do_remove_tick_handlers(self): + if self._tick_handler: + if self._tick_widget: + self._tick_widget.remove_tick_callback(self._tick_handler) + else: + GLib.source_remove(self._tick_handler) + self._tick_handler = None + return + + def play(self): + if self.playing: + return + + self._start_time = self.do_get_time_now() + + if not self._tick_handler: + if self._tick_widget: + self._tick_handler = self._tick_widget.add_tick_callback( + self.do_handle_tick + ) + else: + self._tick_handler = GLib.timeout_add(16, self.do_handle_tick) + + self.playing = True + return + + def pause(self): + self.playing = False + return self.do_remove_tick_handlers() + + def stop(self): + if not self._tick_handler: + self._timeline_pos = 0 + self.playing = False + return + return self.do_remove_tick_handlers() diff --git a/snippets/custom_image.py b/snippets/custom_image.py new file mode 100644 index 0000000..1aae7f6 --- /dev/null +++ b/snippets/custom_image.py @@ -0,0 +1,39 @@ +import math +from typing import cast + +import cairo +from gi.repository import Gtk + +from fabric.widgets.image import Image + + +class CustomImage(Image): + def do_render_rectangle( + self, cr: cairo.Context, width: int, height: int, radius: int = 0 + ): + cr.move_to(radius, 0) + cr.line_to(width - radius, 0) + cr.arc(width - radius, radius, radius, -(math.pi / 2), 0) + cr.line_to(width, height - radius) + cr.arc(width - radius, height - radius, radius, 0, (math.pi / 2)) + cr.line_to(radius, height) + cr.arc(radius, height - radius, radius, (math.pi / 2), math.pi) + cr.line_to(0, radius) + cr.arc(radius, radius, radius, math.pi, (3 * (math.pi / 2))) + cr.close_path() + + def do_draw(self, cr: cairo.Context): + context = self.get_style_context() + width, height = self.get_allocated_width(), self.get_allocated_height() + cr.save() + + self.do_render_rectangle( + cr, + width, + height, + cast(int, context.get_property("border-radius", Gtk.StateFlags.NORMAL)), + ) + cr.clip() + Image.do_draw(self, cr) + + cr.restore() diff --git a/snippets/icon.py b/snippets/icon.py new file mode 100644 index 0000000..86e77c0 --- /dev/null +++ b/snippets/icon.py @@ -0,0 +1,16 @@ +from fabric.widgets.label import Label + + +def MaterialIcon(icon_name: str, size: int = 24, props=None): + label_props = { + "label": str(icon_name), # Directly use the provided icon name + "name": "material_icon", + "style": f"font-size: {size}px;", # Set font family for Material Icons + "h_align": "center", # Align horizontally + "v_align": "center", # Align vertically + } + + if props: + label_props.update(props) + + return Label(**label_props) diff --git a/styles/bar.css b/styles/bar.css new file mode 100644 index 0000000..97322e8 --- /dev/null +++ b/styles/bar.css @@ -0,0 +1,184 @@ +#bar { + background-color: @surface; + padding: 3px; +} + +#bar label { + font-weight: bold; +} + +#bar.hidden { + opacity: 0; + padding: 3px; + margin-bottom: -52px; + transition: all 0.5s cubic-bezier(0.5, 0.25, 0, 1); +} + +/* + * โ”€โ”€ bar modules โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +*/ + +#start-container, +#center-container, +#end-container { + padding-top: 6px; + padding-bottom: 6px; + margin-left: 12px; + margin-right: 12px; +} + +/* + * โ”€โ”€ workspace โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +*/ + +#workspaces { + border-radius: 100px; + font-style: normal; + background: @surfaceContainer; + padding: 0px 10px 0px 10px; +} + +/* + * โ”€โ”€ Clock โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +*/ + +#datetime { + background-color: @surfaceContainer; + border-radius: 100px; + padding: 8px; +} + +/* + * โ”€โ”€ battery โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +*/ + +#battery { + padding: 0 0 0 4px; +} + +/* + * โ”€โ”€ tray โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +*/ + +#tray { + background-color: @surfaceContainer; + border-radius: 100px; + padding: 2px 10px 0px 10px; +} + +#tray > button { + margin: 0px; + padding: 0px; +} + +menu { + border: solid 1px @outlineVariant; + border-radius: 16px; + background-color: @surface; + padding: 6px; +} + +menu > menuitem { + border-radius: 10px; + padding: 6px 10px; +} + +menu > menuitem:hover { + background-color: @surfaceBright; +} + +menu > menuitem:hover > label { + color: @onSurface; +} + +/* + * โ”€โ”€ logo โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +*/ +#logo { + background-color: @surfaceContainer; + border-radius: 100px; + padding: 0px 10px 0px 10px; +} + +#logo-name { + color: darker(@primary); +} + +#logo:hover { + background-color: @surfaceBright; + box-shadow: 0px 3px 3px -2px rgba(0, 0, 0, 0.2); +} + +/* + * โ”€โ”€ applets โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +*/ + +#applets { + background-color: @surfaceContainer; + border-radius: 100px; + padding: 2px 10px 2px 10px; + margin: 0px; +} + +#applets .icon { + color: @onSurface; + font-weight: 900; +} + +#applets > button { + color: @onSurface; + margin: 0px; + padding: 0px; +} + +/* + * โ”€โ”€ taskbar โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +*/ + +#taskbar { + border-radius: 100px; + color: @onSurface; + background: @surfaceContainer; + padding-left: 4px; + padding-right: 4px; +} + +#taskbar > button.activated { + border-radius: 100px; + background: @surfaceVariant; +} + +#taskbar > button:hover { + background: @surfaceBright; +} + +#taskbar > button { + padding: 0px 4px; + margin: 4px 0px; + border-radius: 100px; + border: 0px; +} + +#taskbar > button:last-child { + margin-right: 0px; +} + +#progress { + color: lighter(@outlineVariant); + border: 1px solid @primary; +} + +#recording-indicator { + padding: 6px 12px 6px 12px; + border-radius: 99px; +} + +#recording-indicator { + animation: error 0.8s linear infinite alternate; +} + +@keyframes error { + to { + background-color: @onError; + } +} diff --git a/styles/launcher.css b/styles/launcher.css new file mode 100644 index 0000000..9daaeef --- /dev/null +++ b/styles/launcher.css @@ -0,0 +1,327 @@ +/* + * โ”€โ”€ launcher โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +*/ + +#launcher { + margin: 18px; + border: 1px solid @outlineVariant; + background-color: @surface; + border-radius: 24px; + padding: 18px; + transition: all 0.5s cubic-bezier(0.5, 0.25, 0, 1); +} + +#launcher entry { + transition: all 0.1s ease; + border-radius: 11px; + padding: 10px; + margin: 12px; + box-shadow: inset 0 0 0 1px @outlineVariant; +} + +#launcher-content.wallpapers { + padding: 16px; + min-height: 420px; + min-width: 620px; +} + +#launcer-content.cliphist { + min-height: 420px; +} + +#launcher-content.bluetooth { + min-height: 464px; + min-width: 464px; +} + +#launcher-content.power { + min-height: 52px; +} + +#launcher-content { + min-width: 420px; +} + +#app-launcher, +#wallpapers, +#todo, +#bluetooth, +#sh, +#cliphist, +#emoji, +#powermenu { + transition: all 0.25s cubic-bezier(0.5, 0.25, 0, 1); +} + +#toggle-launcher-button { + background-color: @secondaryContainer; + color: @onSecondaryContainer; + margin: 11px; + margin-left: 0px; + min-width: 54px; + min-height: 44px; + border-radius: 100%; +} + +#clear-launcher-button { + background-color: @secondaryContainer; + color: @onSecondaryContainer; + margin: 11px; + margin-left: 0px; + min-width: 44px; + min-height: 44px; + border-radius: 100%; +} + +/* + * โ”€โ”€ applauncher โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +*/ + +#launcher-app { + border-radius: 16px; + padding: 16px; + transition: padding 0.25s cubic-bezier(0.5, 0.25, 0, 1); +} + +#launcher-app:hover image, +#launcher-app:active image, +#launcher-app:selected image, +#launcher-app.selected image, +#launcher-app:focus image { + -gtk-icon-shadow: 2px 2px @primary; +} + +#launcher-app:hover, +#launcher-app:active, +#launcher-app.selected, +#launcher-app:selected, +#launcher-app:focus { + text-shadow: 2pt 2pt 2pt @primary; +} + +#launcher-app-icon { + margin-right: 8px; +} + +/* + * โ”€โ”€ wallpaper โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +*/ + +#wallpaper-icons:selected { + background-color: @surfaceBright; + border-radius: 8px; +} + +#wallpaper-icons:hover { + background-color: @surfaceBright; + border-radius: 8px; +} + +#dropdown-box { + background-color: @surface; + box-shadow: inset 0 0 0 1px @outlineVariant; + border-radius: 11px; + padding: 10px; + margin: 12px; +} + +#scheme-dropdown { + font-weight: bold; +} + +/* + * โ”€โ”€ emoji โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +*/ + +#launcher button#emoji-item { + border-radius: 11px; + background-color: transparent; + background-image: none; + box-shadow: none; + padding: 0px; +} + +#launcher button#emoji-item label { + font-size: 24px; +} + +#launcher button#emoji-item:hover, +#launcher button#emoji-item:focus { + text-shadow: 2px 2px 2px @primary; +} + +/* + * โ”€โ”€ powermenu โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +*/ + +#powermenu-button { + border-radius: 20px; + padding: 20px; +} + +#powermenu-button:hover, +#powermenu-button:focus { + background-color: @surfaceBright; +} + +#powermenu-button:active { + background-color: @primary; +} + +/* + * โ”€โ”€ cliphist โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +*/ +#cliphist-history { + min-height: 420px; +} + +#launcher button#clip-img-item { + background-size: cover; + background-position: center; + background-repeat: no-repeat; + border-radius: 8px; + min-width: 70px; + min-height: 70px; +} + +#launcher button#clip-img-item:hover, +#launcher button#clip-img-item:focus { + box-shadow: 2px 2px 2px @primary; +} + +#launcher button#clip-item { + border-radius: 11px; + background-color: transparent; + background-image: none; + box-shadow: none; + padding: 6px; +} + +#launcher button#clip-item { + font-size: 30px; +} + +#launcher button#clip-item:hover, +#launcher button#clip-item:focus { + text-shadow: 2px 2px 2px @primary; +} + +/* + * โ”€โ”€ todo โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +*/ +#todo-content { + min-height: 250px; +} + +#todo-launcher { + background-color: @surface; + padding: 10px; + border-radius: 8px; +} + +#todo-item { + padding: 8px; + border-radius: 4px; + background-color: alpha(@surface, 0.8); +} + +#todo-label { + color: @primary; +} + +#todo-label-completed { + color: alpha(@outlineVariant, 0.5); + text-decoration: line-through; +} + +#delete-button { + color: @error; +} + +/* + * โ”€โ”€ sh runner โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +*/ + +#launcher button#sh-item { + border-radius: 11px; + background-color: transparent; + background-image: none; + box-shadow: none; + padding: 0px; +} + +#launcher button#sh-item { + font-size: 24px; +} + +#launcher button#sh-item:hover, +#launcher button#sh-item:focus { + text-shadow: 2px 2px 2px @primary; +} + +/* + * โ”€โ”€ bluetooth โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +*/ + +#bluetooth-header { + background-color: @surfaceContainer; + padding: 4px; + border-radius: 20px; +} + +#bluetooth-device { + background-color: @secondaryContainer; + border-radius: 16px; + padding: 4px; + margin-bottom: 4px; +} + +#bluetooth-scan, +#bluetooth-toggle { + background-color: @secondaryContainer; + margin-left: 0px; + min-width: 44px; + min-height: 44px; + border-radius: 100%; +} + +#bluetooth-scan:hover, +#bluetooth-toggle:hover { + background-color: @surfaceBright; +} + +#bluetooth-scan label, +#bluetooth-toggle label { + font-weight: bold; +} + +#bluetooth-text, +#bluetooth-section { + font-weight: bold; +} + +#bluetooth-text { + color: @primary; +} + +#bluetooth-connection { + font-size: 20px; +} + +#bluetooth-connect { + background-color: @surface; + border-radius: 16px; + padding: 8px 16px; +} + +#bluetooth-connect:hover { + background-color: @surfaceBright; +} + +#bluetooth-paired, +#bluetooth-available { + background-color: @surfaceContainer; + padding: 4px; + border-radius: 20px; + min-height: 200px; +} diff --git a/styles/main.css b/styles/main.css new file mode 100644 index 0000000..a971d02 --- /dev/null +++ b/styles/main.css @@ -0,0 +1,69 @@ +@import url(../../.cache/material/colors.css); +@import "./bar.css"; +@import "./notification.css"; +@import "./osd.css"; +@import "./launcher.css"; + +* { + all: unset; + color: @foreground; + font-size: 16px; + font-family: "Product Sans", Roboto, "Font Awesome 6 Free", Helvetica, Arial, + sans-serif; +} + +/* + * โ”€โ”€ corner โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +*/ + +#corner { + background-color: @surface; + border-radius: 0; +} + +#corner-container { + min-width: 20px; + min-height: 20px; +} + +/* + * โ”€โ”€ tooltip โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +*/ + +tooltip { + background-color: @surfaceContainer; + border-radius: 10px; + padding: 6px 10px; + outline: none; + border: none; + box-shadow: none; + animation: tooltipShow 0.25s cubic-bezier(0.5, 0.25, 0, 1); +} + +tooltip label { + color: @onSurface; + outline: none; + border: none; + box-shadow: none; + font-weight: normal; +} + +@keyframes tooltipShow { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} + +/* + * โ”€โ”€ icon โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +*/ + +#material_icon, +#material_icon * { + all: unset; + font-family: "Material Symbols Outlined", "Material Symbols Sharp"; +} diff --git a/styles/notification.css b/styles/notification.css new file mode 100644 index 0000000..b1ba83c --- /dev/null +++ b/styles/notification.css @@ -0,0 +1,60 @@ +#notification { + padding: 16px; + border: solid 1px @outlineVariant; + border-radius: 20px; + margin-bottom: 12px; + background-color: @surfaceContainerLow; + min-width: 350px; +} + +.notification-header .summary { + font-size: 0.92rem; + font-weight: bold; +} + +.notification-body .body { + color: darker(@foreground); + font-weight: 600; + font-size: 0.92rem; +} + +#notification .close-button { + border-radius: 100px; +} + +#notification .close-button:hover { + background: alpha(@foreground, 0.2); + transition: background 0.15s ease-in-out; + border-radius: 100px; +} + +#notification-action-button button.start-action { + margin-left: 0; +} + +#notification-action-button button.end-action { + margin-right: 0; +} + +#notification-action-buttons button { + padding: 0.6rem; + font-weight: 600; + border-radius: 100px; + background-color: transparent; +} +#notification-action-buttons button:hover { + background-color: lighter(@outlineVariant); +} + +#notification-icon { + margin-right: 5px; +} + +.image { + border-radius: 30px; +} + +#notification-circular-progress-bar { + border: solid 0px @outlineVariant; + color: transparent; +} diff --git a/styles/osd.css b/styles/osd.css new file mode 100644 index 0000000..f86f4e2 --- /dev/null +++ b/styles/osd.css @@ -0,0 +1,42 @@ +#osd { + background: @surfaceContainer; + padding: 10px 4px 10px 4px; + border-radius: 14px; + margin-bottom: 16px; + min-width: 190px; +} + +#osd scale { + min-width: 150px; +} + +#osd slider { + color: @onPrimary; + background-color: @primary; + background-size: 20px 20px; + min-width: 5px; + min-height: 14px; + border-radius: 100px; + padding: 0px; + background-repeat: no-repeat; + background-position: center; + margin-left: 0px; + margin-right: 0px; + margin-top: -16px; + margin-bottom: -16px; + box-shadow: + -4px 0px 0px 1px @surfaceContainer, + 4px 0px 0px 1px @surfaceContainer; +} + +#osd trough { + background: @secondaryContainer; + min-height: 10px; + margin-right: 4px; + border-radius: 100px; +} + +#osd highlight { + border-radius: 100px 0px 0px 100px; + background: @primary; +}