diff --git a/assets/content-style.css b/assets/content-style.css index 6260584..4d76c00 100644 --- a/assets/content-style.css +++ b/assets/content-style.css @@ -55,3 +55,13 @@ img[ src *= evergreen_tree ] { font-size: 5rem; font-variation-settings: 'FILL' 1; } + +.emoji { + font-family: "Noto Color Emoji", emoji; + font-size: 5rem; +} + +.emoji-svg svg { + height: 8rem; + width: 8rem; +} diff --git a/assets/index.css b/assets/index.css index 52b2ed7..b119984 100644 --- a/assets/index.css +++ b/assets/index.css @@ -59,3 +59,17 @@ img[ src *= evergreen_tree ] { font-size: 5rem; font-variation-settings: 'FILL' 1; } + +.emoji { + font-family: "Noto Color Emoji", emoji; + font-size: 5rem; +} + +.emoji-svg svg { + height: 8rem; + width: 8rem; +} + +.error { + color: darkred; +} diff --git a/build/emoji-svg-js.js b/build/emoji-svg-js.js new file mode 100644 index 0000000..c2ab93f --- /dev/null +++ b/build/emoji-svg-js.js @@ -0,0 +1,65 @@ +/** + * Build the `lib/emoji-svg.js` JavaScript from SVG files in the `noto-emoji` Git repo. + * + * © NDF, 07-Sep-2024. + * @see https://github.com/googlefonts/noto-emoji + */ + +const fs = require('fs').promises; +const { resolve } = require('path'); +const { EMOJI } = require('../lib/Icons.js'); + +const outputJsPath = resolve(__dirname, '..', 'lib', 'emoji-svg.js'); + +const SVG = EMOJI.map(async (it, idx) => { + const xml = await fs.readFile(svgFilePath(it.codepoint), 'utf8'); + + const svg = trimSvgXml(xml); + + return { id: it.code, svg }; +}); + +console.log('SVG emoji count:', SVG.length); + +Promise.all(SVG).then((svgData) => { + const svgJson = JSON.stringify(svgData, null, 2); + + return fs.writeFile(outputJsPath, jsFileTemplate(svgJson)); +}) + .then(() => console.log('File emoji-svg.js written OK.')); + +/* + Utility functions. +*/ + +function trimSvgXml (xml) { + return xml.replace(/<\?xml[^>]+\?>\n/, '').replace(/\n/, '') + .replace(' xmlns:xlink="http://www.w3.org/1999/xlink"', '') + .replace(/ version="\d.\d"/, '').replace(/ id="Layer_\d"/, '') + .replace(/\t/g, '').replace(/\n/g, ''); +} + +function codePoint (codepoint) { + return codepoint.replace('U+', '').toLowerCase(); +} + +function svgFileName (codepoint) { + return `emoji_u${codePoint(codepoint)}.svg`; +} + +function svgFilePath (codepoint) { + return resolve(__dirname, '..', 'noto-emoji', 'svg', svgFileName(codepoint)); +} + +function jsFileTemplate (jsonSvgData) { + return `/* Auto-generated. */ + +/* eslint-disable quotes, quote-props */ + +export const SVG = ${jsonSvgData}; + +export default SVG; + +/* eslint-enable */ +`; +} diff --git a/build/manifest.js b/build/manifest.js index 344644e..2a75314 100644 --- a/build/manifest.js +++ b/build/manifest.js @@ -14,6 +14,7 @@ const IS_GECKO = process.env.UA === 'gecko'; const FILE_PATH = resolve(__dirname, '..', 'manifest.json'); const SERVICE_WORKER = 'lib/service-worker.js'; +// const BACKGROUND_PAGE = 'pages/background.html'; const TEMPLATE = { manifest_version: 3, @@ -41,6 +42,8 @@ const TEMPLATE = { 'http://*/*' ], + // content_security_policy: "script-src 'self'; font-src 'self' https://fonts.gstatic.com/; upgrade-insecure-requests;", + background: { // scripts: ['lib/service-worker.js'], // service_worker: 'lib/service-worker.js' @@ -64,7 +67,10 @@ const TEMPLATE = { matches: ['https://*/*'], resources: [ 'assets/content-style.css', - 'assets/evergreen_tree.svg' + // 'assets/evergreen_tree.svg', + // 'assets/emoji.svg', + 'lib/emoji-svg.js', + 'lib/Icons.js' ] } ] @@ -82,10 +88,12 @@ const GECKO = { const MANIFEST = IS_GECKO ? { ...TEMPLATE, ...GECKO } : TEMPLATE; if (IS_GECKO) { + // MANIFEST.background.page = BACKGROUND_PAGE; MANIFEST.background.scripts = [SERVICE_WORKER]; } else { // Chromium-specific. MANIFEST.background.service_worker = SERVICE_WORKER; + MANIFEST.background.type = 'module'; MANIFEST.permissions.push('offscreen'); } diff --git a/lib/Icons.js b/lib/Icons.js new file mode 100644 index 0000000..ff686be --- /dev/null +++ b/lib/Icons.js @@ -0,0 +1,114 @@ +/** + * + * @see https://codepen.io/nfreear/pen/abgRwdY + * @see https://emojipedia.org/evergreen-tree#technical + */ + +const emojiSvgJs = './emoji-svg.js'; + +export const EMOJI = [ + { + name: 'Tree', + phrase: 'Grow a tree!', + emoji: '🌲', + code: 'evergreen_tree', // Shortcode: `:evergreen_tree:` + codepoint: 'U+1F332' + }, { + name: 'Bean', + phrase: 'Collect a bean!', + emoji: '🫘', + code: 'beans', + codepoint: 'U+1FAD8' + }, { + name: 'Chocolate', + phrase: 'Eat chocolate!', + emoji: '🍫', + code: 'chocolate_bar', + codepoint: 'U+1F36B' + }, { + name: 'Hot drink', + phrase: 'Have a hot drink!', + emoji: '☕', + code: 'hot_beverage', + codepoint: 'U+2615' + }, /* { + name: 'Yarn', + phrase: 'Do crafts!', + emoji: '🧶', + code: 'yarn', + codepoint: 'U+1F9F6' + }, */ { + name: 'Star', + phrase: 'Collect a star!', + emoji: '⭐', + code: 'star', + codepoint: 'U+2B50' + }, { + name: 'Walking', + phrase: 'Take a break!', + emoji: '🚶', + code: 'person_walking', + codepoint: 'U+1F6B6' + } +]; + +const APP_ICONS = [ + { + name: 'Cog', // Settings/ Options. + emoji: '⚙️', + code: 'gear' + }, { + name: 'Timer', + emoji: '⏱️', + code: 'stopwatch', + codepoint: 'U+23F1 U+FE0F' + } +]; + +export class Icons { + get emoji () { return EMOJI; } + get appIcons () { return APP_ICONS; } + + get default () { return this.find('evergreen_tree'); } + get cog () { return this.findAppIcon('gear'); } + get timer () { return this.findAppIcon('stopwatch'); } + + getSelectOptions () { + return this.emoji.map((it) => { + const { code, emoji, name } = it; + return ``; + }); + } + + find (byCode) { + return this.emoji.find((it) => it.code === byCode); + } + + findAppIcon (byCode) { + return this.appIcons.find(it => it.code === byCode); + } + + findEmojiSVG (byId) { + const found = this._SVG.find(it => it.id === byId); + if (!found) { + // throw new Error(`SVG emoji not found: "${byId}"`); + console.error(`Error: SVG emoji not found: "${byId}"`); + return `