├── .eslintrc.json ├── .gitignore ├── README.md ├── data.js ├── generateContent.ipynb ├── next.config.js ├── package.json ├── pages ├── _app.tsx ├── about.tsx ├── emoji │ └── [id].tsx └── index.tsx ├── pnpm-lock.yaml ├── postcss.config.js ├── public ├── favicon.ico ├── fonts │ ├── inter-v12-latin-500.woff │ ├── inter-v12-latin-500.woff2 │ ├── inter-v12-latin-600.woff │ ├── inter-v12-latin-600.woff2 │ ├── inter-v12-latin-regular.woff │ └── inter-v12-latin-regular.woff2 ├── homepage.png └── vercel.svg ├── src ├── components │ ├── autoChangeEmoji.tsx │ └── search.tsx ├── getImageURLFromGitHub.ts └── types │ └── emoji.ts ├── styles ├── Home.module.css └── globals.css ├── tailwind.config.js └── tsconfig.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | 38 | # Content initialization 39 | repo 40 | 41 | # IPyhon 42 | 43 | .ipynb_checkpoints 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Fluent UI emoji search 3 | 4 | Search and download emojis from Microsoft Fluent UI 5 | 6 | ## Features 7 | 8 | - Near realtime search 9 | - Variants of emoji also downloadable 10 | 11 | ## Usage 12 | 13 | Go to [https://fluent-ui.julienc.me](https://fluent-ui.julienc.me) 14 | 15 | ## Screenshot 16 | 17 | ![App Screenshot](#)(![](https://raw.githubusercontent.com/julien040/fluent-search/main/public/homepage.png)) 18 | 19 | ## Disclaimer 20 | 21 | All emojis used in this repository are licensed under MIT by Microsoft. 22 | 23 | [https://github.com/microsoft/fluentui-emoji.git](https://github.com/microsoft/fluentui-emoji.git) 24 | 25 | ## Develop 26 | 27 | 1. Run the Jupyter notebook to fetch last emojis 28 | 2. Run `pnpm dev` to start the dev server 29 | 3. Deploy using Vercel 30 | 31 | ## License 32 | 33 | [MIT](https://choosealicense.com/licenses/mit/) 34 | 35 | 36 | ## Authors 37 | 38 | [@julien040](https://github.com/julien040) 39 | 40 | -------------------------------------------------------------------------------- /generateContent.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "This notebook generates a JSON file that is later used by the front end.\n", 8 | "It fetches every emoji, create an entry for each one and then save it in a JSON file.\n" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "metadata": {}, 14 | "source": [ 15 | "# Initialisation" 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": 1, 21 | "metadata": {}, 22 | "outputs": [ 23 | { 24 | "name": "stdout", 25 | "output_type": "stream", 26 | "text": [ 27 | "Cloning into 'repo'...\n", 28 | "remote: Enumerating objects: 27960, done.\u001b[K\n", 29 | "remote: Counting objects: 100% (367/367), done.\u001b[K\n", 30 | "remote: Compressing objects: 100% (273/273), done.\u001b[K\n", 31 | "remote: Total 27960 (delta 42), reused 352 (delta 33), pack-reused 27593\u001b[K\n", 32 | "Receiving objects: 100% (27960/27960), 125.19 MiB | 840.00 KiB/s, done.\n", 33 | "Resolving deltas: 100% (3291/3291), done.\n", 34 | "Updating files: 100% (12041/12041), done.\n" 35 | ] 36 | } 37 | ], 38 | "source": [ 39 | "# Remove any existing content\n", 40 | "!rm -rf repo\n", 41 | "# We clone the repo in the repo folder\n", 42 | "!git clone https://github.com/microsoft/fluentui-emoji.git repo\n", 43 | "# Delete the .git folder because we don't need it\n", 44 | "!rm -rf repo/.git" 45 | ] 46 | }, 47 | { 48 | "cell_type": "markdown", 49 | "metadata": {}, 50 | "source": [ 51 | "# Transform the repo into a dictionary" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": 8, 57 | "metadata": {}, 58 | "outputs": [ 59 | { 60 | "name": "stdout", 61 | "output_type": "stream", 62 | "text": [ 63 | "File written\n" 64 | ] 65 | } 66 | ], 67 | "source": [ 68 | "\n", 69 | "import json\n", 70 | "import os\n", 71 | "\n", 72 | "\"\"\"This function will find all the emoji in a given folder\n", 73 | "\"\"\"\n", 74 | "def getAllImageOfFolder(path:str):\n", 75 | " # Get the path without \"repo\"\n", 76 | " pathForCDN = path[5:]+\"/\"\n", 77 | "\n", 78 | " \n", 79 | " result = {}\n", 80 | " # Try to get the 3D icon\n", 81 | " if os.path.exists(path+\"/3D\"):\n", 82 | " result[\"3D\"] = pathForCDN+\"3D/\"+os.listdir(path+\"/3D\")[0] #os.listdir(path+\"/3D\")[0] get the first file in the folder\n", 83 | " # Try to get the color icon\n", 84 | " if os.path.exists(path+\"/Color\"):\n", 85 | " result[\"Color\"] = pathForCDN+ \"Color/\"+os.listdir(path+\"/Color\")[0]\n", 86 | "\n", 87 | " # Try to get the Flat icon\n", 88 | " if os.path.exists(path+\"/Flat\"):\n", 89 | " result[\"Flat\"] = pathForCDN+ \"Flat/\"+os.listdir(path+\"/Flat\")[0]\n", 90 | " if os.path.exists(path+\"/High Contrast\"):\n", 91 | " result[\"High Contrast\"] = pathForCDN+\"High Contrast/\"+os.listdir(path+\"/High Contrast\")[0]\n", 92 | " return result\n", 93 | "\n", 94 | "result = {}\n", 95 | "\n", 96 | " \n", 97 | "# Iterate over the folders in the repo\n", 98 | "for folder in os.listdir('repo/assets/'):\n", 99 | " # On macos, we have a .DS_Store file\n", 100 | " if folder != \".DS_Store\" and os.path.isdir(\"repo/assets/\"+folder):\n", 101 | " data = {\n", 102 | " \"name\":folder,\n", 103 | " \"variant\":[],\n", 104 | "\n", 105 | " }\n", 106 | " # Read the metadata json file\n", 107 | " with open('repo/assets/'+folder+'/metadata.json') as json_file:\n", 108 | " metadata = json.load(json_file)\n", 109 | " # Get the emoji in unicode : 🙂\n", 110 | " data[\"glyph\"] = metadata[\"glyph\"]\n", 111 | " # Get the keywords\n", 112 | " data[\"keywords\"] = metadata[\"keywords\"]\n", 113 | "\n", 114 | "\n", 115 | " # We check if the emoji has variants. If 3D is present, it means that the emoji has no variants\n", 116 | " if os.path.isdir('repo/assets/' + folder+ \"/3D\"):\n", 117 | " # Case where the emoji has no variants\n", 118 | " data[\"variant\"].append(getAllImageOfFolder('repo/assets/' + folder))\n", 119 | "\n", 120 | " else:\n", 121 | " # Case where the emoji has variants\n", 122 | " for variant in os.listdir('repo/assets/' + folder):\n", 123 | " # We check if the variant is a folder and not a file\n", 124 | " if os.path.isdir('repo/assets/' + folder+ \"/\"+variant):\n", 125 | " data[\"variant\"].append(getAllImageOfFolder('repo/assets/' + folder+\"/\"+variant))\n", 126 | "\n", 127 | " result[folder] = data\n", 128 | "\n", 129 | "\n", 130 | "# Write the result in a json file\n", 131 | "with open('data.js', 'w') as outfile:\n", 132 | " toWrite= \"/* eslint-disable import/no-anonymous-default-export */ \\n export default \"+ json.dumps(result, indent=4) \n", 133 | " outfile.write(toWrite)\n", 134 | " print (\"File written\")\n", 135 | "\n", 136 | "# Close the file\n", 137 | "outfile.close()\n" 138 | ] 139 | } 140 | ], 141 | "metadata": { 142 | "kernelspec": { 143 | "display_name": "Python 3.10.6 64-bit", 144 | "language": "python", 145 | "name": "python3" 146 | }, 147 | "language_info": { 148 | "codemirror_mode": { 149 | "name": "ipython", 150 | "version": 3 151 | }, 152 | "file_extension": ".py", 153 | "mimetype": "text/x-python", 154 | "name": "python", 155 | "nbconvert_exporter": "python", 156 | "pygments_lexer": "ipython3", 157 | "version": "3.10.6" 158 | }, 159 | "orig_nbformat": 4, 160 | "vscode": { 161 | "interpreter": { 162 | "hash": "aee8b7b246df8f9039afb4144a1f6fd8d2ca17a180786b69acc140d282b71a49" 163 | } 164 | } 165 | }, 166 | "nbformat": 4, 167 | "nbformat_minor": 2 168 | } 169 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | swcMinify: true, 5 | } 6 | 7 | module.exports = nextConfig 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "emoji", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@types/node": "18.11.9", 13 | "@types/react": "18.0.25", 14 | "@types/react-dom": "18.0.8", 15 | "eslint": "8.27.0", 16 | "eslint-config-next": "13.0.3", 17 | "fuse.js": "^6.6.2", 18 | "next": "13.0.3", 19 | "react": "18.2.0", 20 | "react-dom": "18.2.0", 21 | "typescript": "4.8.4", 22 | "use-debounce": "^8.0.4" 23 | }, 24 | "devDependencies": { 25 | "autoprefixer": "^10.4.13", 26 | "postcss": "^8.4.19", 27 | "tailwindcss": "^3.2.4" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /pages/_app.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable @next/next/no-img-element */ 2 | import "../styles/globals.css"; 3 | import type { AppProps } from "next/app"; 4 | import Link from "next/link"; 5 | import Script from "next/script"; 6 | import * as React from "react"; 7 | import Logo from "../src/components/autoChangeEmoji"; 8 | import { SVGProps } from "react"; 9 | import Head from "next/head"; 10 | 11 | const GitHubLogo = (props: SVGProps) => ( 12 | 19 | 23 | 24 | ); 25 | 26 | function Repo() { 27 | const [stars, setStars] = React.useState(0); 28 | 29 | React.useEffect(() => { 30 | fetch("https://api.github.com/repos/julien040/fluent-search") 31 | .then((res) => res.json()) 32 | .then((data) => setStars(parseInt(data.stargazers_count))); 33 | }, []); 34 | return ( 35 |
36 | 37 | 38 |

{stars.toString()} stars

39 |
40 | ); 41 | } 42 | 43 | export default function App({ Component, pageProps }: AppProps) { 44 | return ( 45 |
46 |