3945 lines
708 KiB
JavaScript
Raw Normal View History

2023-06-29 11:55:02 +08:00
/*
THIS IS A GENERATED/BUNDLED FILE BY ROLLUP
if you want to view the source visit the plugins github repository
*/
'use strict';
var obsidian = require('obsidian');
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
class MetaData {
}
const extract = (svgString) => {
var _a, _b;
// Removes unnecessary spaces and newlines.
svgString = svgString.replace(/(\r\n|\n|\r)/gm, '');
svgString = svgString.replace(/>\s+</gm, '><');
// Create a parser for better parsing of HTML.
const parser = new DOMParser();
const svg = parser.parseFromString(svgString, 'text/html').querySelector('svg');
// Removes `width` and `height` from the `style` attribute.
if (svg.hasAttribute('style')) {
svg.style.width = '';
svg.style.height = '';
}
// Add `viewbox`, if it is not already a attribute.
if (svg.viewBox.baseVal.width === 0 && svg.viewBox.baseVal.height === 0) {
const width = (_a = svg.width.baseVal.value) !== null && _a !== void 0 ? _a : 16;
const height = (_b = svg.height.baseVal.value) !== null && _b !== void 0 ? _b : 16;
svg.viewBox.baseVal.width = width;
svg.viewBox.baseVal.height = height;
}
if (!svg.hasAttribute('fill')) {
svg.setAttribute('fill', 'currentColor');
}
svg.setAttribute('width', '16px');
svg.setAttribute('height', '16px');
return svg.outerHTML;
};
let path;
const getPath = () => {
return path;
};
const setPath = (newPath) => {
if (newPath === 'plugins/obsidian-icon-folder/icons') {
newPath = '.obsidian/plugins/obsidian-icon-folder/icons';
new obsidian.Notice(`[${MetaData.pluginName}] Due to a change in version v1.2.2, the icon pack folder changed. Please change it in the settings to not be directly in /plugins.`, 8000);
}
path = newPath;
};
const preloadedIcons = [];
let iconPacks$1 = [];
const moveIconPackDirectories = (plugin, from, to) => __awaiter(void 0, void 0, void 0, function* () {
for (let i = 0; i < iconPacks$1.length; i++) {
const iconPack = iconPacks$1[i];
const doesDirExist = yield createDirectory(plugin, iconPack.name);
if (doesDirExist) {
new obsidian.Notice(`Directory with name ${iconPack.name} already exists.`);
continue;
}
new obsidian.Notice(`Moving ${iconPack.name}...`);
for (let j = 0; j < iconPack.icons.length; j++) {
const icon = iconPack.icons[j];
if (yield plugin.app.vault.adapter.exists(`${from}/${iconPack.name}`)) {
yield plugin.app.vault.adapter.copy(`${from}/${iconPack.name}/${icon.filename}`, `${to}/${iconPack.name}/${icon.filename}`);
}
}
new obsidian.Notice(`...moved ${iconPack.name}`);
}
for (let i = 0; i < iconPacks$1.length; i++) {
const iconPack = iconPacks$1[i];
if (yield plugin.app.vault.adapter.exists(`${from}/${iconPack.name}`)) {
yield plugin.app.vault.adapter.rmdir(`${from}/${iconPack.name}`, true);
}
}
});
const createIconPackDirectory = (plugin, dir) => __awaiter(void 0, void 0, void 0, function* () {
yield createDirectory(plugin, dir);
iconPacks$1.push({ name: dir, icons: [] });
});
const deleteIconPack = (plugin, dir) => __awaiter(void 0, void 0, void 0, function* () {
iconPacks$1 = iconPacks$1.filter((iconPack) => iconPack.name !== dir);
yield plugin.app.vault.adapter.rmdir(`${path}/${dir}`, true);
});
const doesIconPackExist = (plugin, iconPackName) => {
return plugin.app.vault.adapter.exists(`${path}/${iconPackName}`);
};
const createDirectory = (plugin, dir) => __awaiter(void 0, void 0, void 0, function* () {
const doesDirExist = yield plugin.app.vault.adapter.exists(`${path}/${dir}`);
if (!doesDirExist) {
yield plugin.app.vault.adapter.mkdir(`${path}/${dir}`);
}
return doesDirExist;
});
const getNormalizedName = (s) => {
return s
.split(/[ -]|[ _]/g)
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
.join('');
};
const normalizeFileName = (plugin, oldPath) => __awaiter(void 0, void 0, void 0, function* () {
const fileName = oldPath.split('/').pop();
const newPath = oldPath.substring(0, oldPath.indexOf(fileName)) + getNormalizedName(fileName);
yield plugin.app.vault.adapter.rename(oldPath, newPath);
});
const createFile = (plugin, iconPackName, filename, content, absoluteFilename) => __awaiter(void 0, void 0, void 0, function* () {
const normalizedFilename = getNormalizedName(filename);
const exists = yield plugin.app.vault.adapter.exists(`${path}/${iconPackName}/${normalizedFilename}`);
if (exists) {
const folderSplit = absoluteFilename.split('/');
if (folderSplit.length >= 2) {
const folderName = folderSplit[folderSplit.length - 2];
const newFilename = folderName + normalizedFilename;
yield plugin.app.vault.adapter.write(`${path}/${iconPackName}/${newFilename}`, content);
console.info(`[${MetaData.pluginName}] Renamed old file ${normalizedFilename} to ${newFilename} because of duplication.`);
new obsidian.Notice(`[${MetaData.pluginName}] Renamed ${normalizedFilename} to ${newFilename} to avoid duplication.`, 8000);
}
else {
console.warn(`[${MetaData.pluginName}] Could not create icons with duplicated file names (${normalizedFilename}).`);
new obsidian.Notice(`[${MetaData.pluginName}] Could not create duplicated icon name (${normalizedFilename})`, 8000);
}
}
else {
yield plugin.app.vault.adapter.write(`${path}/${iconPackName}/${normalizedFilename}`, content);
}
});
const createDefaultDirectory = (plugin) => __awaiter(void 0, void 0, void 0, function* () {
yield createDirectory(plugin, '');
});
const getAllIconPacks = () => {
return iconPacks$1;
};
const getIconPack = (name) => {
return iconPacks$1.find((ip) => ip.name === name);
};
const getFilesInDirectory = (plugin, dir) => __awaiter(void 0, void 0, void 0, function* () {
return (yield plugin.app.vault.adapter.list(dir)).files;
});
const validIconName = /^[(A-Z)|(0-9)]/;
const svgViewboxRegex = /viewBox="([^"]*)"/g;
const svgContentRegex = /<svg.*>(.*?)<\/svg>/g;
const generateIcon = (iconPackName, iconName, content) => {
if (content.length === 0) {
return;
}
content = content.replace(/(\r\n|\n|\r)/gm, '');
content = content.replace(/>\s+</gm, '><');
const normalizedName = iconName
.split(/[ -]/g)
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
.join('');
if (!validIconName.exec(normalizedName)) {
console.log(`skipping icon with invalid name: ${iconName}`);
return null;
}
const svgViewboxMatch = content.match(svgViewboxRegex);
let svgViewbox = '';
if (svgViewboxMatch && svgViewboxMatch.length !== 0) {
svgViewbox = svgViewboxMatch[0];
}
const svgContentMatch = content.match(svgContentRegex);
const svgContent = svgContentMatch.map((val) => val.replace(/<\/?svg>/g, '').replace(/<svg.+?>/g, ''))[0];
const iconPackPrefix = createIconPackPrefix(iconPackName);
const icon = {
name: normalizedName.split('.svg')[0],
prefix: iconPackPrefix,
filename: iconName,
svgContent,
svgViewbox,
svgElement: extract(content),
};
return icon;
};
const createIconPackPrefix = (iconPackName) => {
if (iconPackName.includes('-')) {
const splitted = iconPackName.split('-');
let result = splitted[0].charAt(0).toUpperCase();
for (let i = 1; i < splitted.length; i++) {
result += splitted[i].charAt(0);
}
return result;
}
return iconPackName.charAt(0).toUpperCase() + iconPackName.charAt(1);
};
const loadUsedIcons = (plugin, icons) => __awaiter(void 0, void 0, void 0, function* () {
const iconPacks = (yield listPath(plugin)).folders.map((iconPack) => iconPack.split('/').pop());
for (let i = 0; i < icons.length; i++) {
const entry = icons[i];
if (!entry) {
continue;
}
yield loadIcon(plugin, iconPacks, entry);
}
});
const listPath = (plugin, listPath) => {
return plugin.app.vault.adapter.list(listPath !== null && listPath !== void 0 ? listPath : path);
};
const nextIdentifier = (iconName) => {
return iconName.substring(1).search(/[(A-Z)|(0-9)]/) + 1;
};
const loadIcon = (plugin, iconPacks, iconName) => __awaiter(void 0, void 0, void 0, function* () {
const nextLetter = nextIdentifier(iconName);
const prefix = iconName.substring(0, nextLetter);
const name = iconName.substring(nextLetter);
const iconPack = iconPacks.find((folder) => {
const folderPrefix = createIconPackPrefix(folder);
return prefix === folderPrefix;
});
if (!iconPack) {
new obsidian.Notice(`Seems like you do not have an icon pack installed. (${iconName})`, 5000);
return;
}
const fullPath = path + '/' + iconPack + '/' + name + '.svg';
if (!(yield plugin.app.vault.adapter.exists(fullPath))) {
console.warn(`[obsidian-icon-folder] icon with name "${name}" was not found (full path: ${fullPath}).`);
return;
}
const content = yield plugin.app.vault.adapter.read(fullPath);
const icon = generateIcon(iconPack, name, content);
preloadedIcons.push(icon);
});
const initIconPacks = (plugin) => __awaiter(void 0, void 0, void 0, function* () {
// Load all the custom generated icon packs.
const loadedIconPacks = yield plugin.app.vault.adapter.list(path);
for (let i = 0; i < loadedIconPacks.folders.length; i++) {
const folder = loadedIconPacks.folders[i];
const iconPackRegex = folder.match(new RegExp(path + '/(.*)'));
if (iconPackRegex.length > 1) {
const iconPackName = iconPackRegex[1];
const icons = yield getFilesInDirectory(plugin, folder);
const loadedIcons = [];
// Convert files into loaded svgs.
for (let j = 0; j < icons.length; j++) {
const iconNameRegex = icons[j].match(new RegExp(path + '/' + iconPackName + '/(.*)'));
const iconName = iconNameRegex[1];
const iconContent = yield plugin.app.vault.adapter.read(icons[j]);
const icon = generateIcon(iconPackName, iconName, iconContent);
if (icon) {
loadedIcons.push(icon);
}
}
iconPacks$1.push({ name: iconPackName, icons: loadedIcons });
console.log(`loaded icon pack ${iconPackName} (${loadedIcons.length})`);
}
}
});
const addIconToIconPack = (iconPackName, iconName, iconContent) => {
const icon = generateIcon(iconPackName, iconName, iconContent);
if (!icon) {
console.warn(`[obsidian-icon-folder] icon could not be generated (icon: ${iconName}, content: ${iconContent}).`);
return undefined;
}
const iconPack = iconPacks$1.find((iconPack) => iconPack.name === iconPackName);
if (!iconPack) {
console.warn(`[obsidian-icon-folder] iconpack with name "${iconPackName}" was not found.`);
return undefined;
}
iconPack.icons.push(icon);
return icon;
};
const getAllLoadedIconNames = () => {
return iconPacks$1.reduce((total, iconPack) => {
total.push(...iconPack.icons);
return total;
}, []);
};
const doesIconExists = (iconName) => {
const icons = getAllLoadedIconNames();
return icons.find((icon) => icon.name === iconName || icon.prefix + icon.name === iconName) !== undefined;
};
const getSvgFromLoadedIcon = (iconPrefix, iconName) => {
let icon = '';
let foundIcon = preloadedIcons.find((icon) => icon.prefix.toLowerCase() === iconPrefix.toLowerCase() && icon.name.toLowerCase() === iconName.toLowerCase());
if (!foundIcon) {
iconPacks$1.forEach((iconPack) => {
const icon = iconPack.icons.find((icon) => icon.prefix.toLowerCase() === iconPrefix.toLowerCase() && icon.name.toLowerCase() === iconName.toLowerCase());
if (icon) {
foundIcon = icon;
}
});
}
if (foundIcon) {
icon = foundIcon.svgElement;
}
return icon;
};
/*! Copyright Twitter Inc. and other contributors. Licensed under MIT */
var twemoji=function(){var twemoji={base:"https://twemoji.maxcdn.com/v/14.0.2/",ext:".png",size:"72x72",className:"emoji",convert:{fromCodePoint:fromCodePoint,toCodePoint:toCodePoint},onerror:function onerror(){if(this.parentNode){this.parentNode.replaceChild(createText(this.alt,false),this);}},parse:parse,replace:replace,test:test},escaper={"&":"&amp;","<":"&lt;",">":"&gt;","'":"&#39;",'"':"&quot;"},re=/(?:\ud83d\udc68\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffc-\udfff]|\ud83e\uddd1\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb\udffd-\udfff]|\ud83e\uddd1\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb\udffc\udffe\udfff]|\ud83e\uddd1\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb-\udffd\udfff]|\ud83e\uddd1\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb-\udffe]|\ud83d\udc68\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffc-\udfff]|\ud83d\udc68\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffd-\udfff]|\ud83d\udc68\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc68\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffd\udfff]|\ud83d\udc68\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffe]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffc-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffc-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffd-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb\udffd-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\
const emojiShortName = {
'😀': 'grinning face',
'😃': 'grinning face with big eyes',
'😄': 'grinning face with smiling eyes',
'😁': 'beaming face with smiling eyes',
'😆': 'grinning squinting face',
'😅': 'grinning face with sweat',
'🤣': 'rolling on the floor laughing',
'😂': 'face with tears of joy',
'🙂': 'slightly smiling face',
'🙃': 'upside-down face',
'🫠': '⊛ melting face',
'😉': 'winking face',
'😊': 'smiling face with smiling eyes',
'😇': 'smiling face with halo',
'🥰': 'smiling face with hearts',
'😍': 'smiling face with heart-eyes',
'🤩': 'star-struck',
'😘': 'face blowing a kiss',
'😗': 'kissing face',
'☺': 'smiling face',
'😚': 'kissing face with closed eyes',
'😙': 'kissing face with smiling eyes',
'🥲': 'smiling face with tear',
'😋': 'face savoring food',
'😛': 'face with tongue',
'😜': 'winking face with tongue',
'🤪': 'zany face',
'😝': 'squinting face with tongue',
'🤑': 'money-mouth face',
'🤗': 'smiling face with open hands',
'🤭': 'face with hand over mouth',
'🫢': '⊛ face with open eyes and hand over mouth',
'🫣': '⊛ face with peeking eye',
'🤫': 'shushing face',
'🤔': 'thinking face',
'🫡': '⊛ saluting face',
'🤐': 'zipper-mouth face',
'🤨': 'face with raised eyebrow',
'😐': 'neutral face',
'😑': 'expressionless face',
'😶': 'face without mouth',
'🫥': '⊛ dotted line face',
'😶‍🌫️': 'face in clouds',
'😏': 'smirking face',
'😒': 'unamused face',
'🙄': 'face with rolling eyes',
'😬': 'grimacing face',
'😮‍💨': 'face exhaling',
'🤥': 'lying face',
'😌': 'relieved face',
'😔': 'pensive face',
'😪': 'sleepy face',
'🤤': 'drooling face',
'😴': 'sleeping face',
'😷': 'face with medical mask',
'🤒': 'face with thermometer',
'🤕': 'face with head-bandage',
'🤢': 'nauseated face',
'🤮': 'face vomiting',
'🤧': 'sneezing face',
'🥵': 'hot face',
'🥶': 'cold face',
'🥴': 'woozy face',
'😵': 'face with crossed-out eyes',
'😵‍💫': 'face with spiral eyes',
'🤯': 'exploding head',
'🤠': 'cowboy hat face',
'🥳': 'partying face',
'🥸': 'disguised face',
'😎': 'smiling face with sunglasses',
'🤓': 'nerd face',
'🧐': 'face with monocle',
'😕': 'confused face',
'🫤': '⊛ face with diagonal mouth',
'😟': 'worried face',
'🙁': 'slightly frowning face',
'☹': 'frowning face',
'😮': 'face with open mouth',
'😯': 'hushed face',
'😲': 'astonished face',
'😳': 'flushed face',
'🥺': 'pleading face',
'🥹': '⊛ face holding back tears',
'😦': 'frowning face with open mouth',
'😧': 'anguished face',
'😨': 'fearful face',
'😰': 'anxious face with sweat',
'😥': 'sad but relieved face',
'😢': 'crying face',
'😭': 'loudly crying face',
'😱': 'face screaming in fear',
'😖': 'confounded face',
'😣': 'persevering face',
'😞': 'disappointed face',
'😓': 'downcast face with sweat',
'😩': 'weary face',
'😫': 'tired face',
'🥱': 'yawning face',
'😤': 'face with steam from nose',
'😡': 'pouting face',
'😠': 'angry face',
'🤬': 'face with symbols on mouth',
'😈': 'smiling face with horns',
'👿': 'angry face with horns',
'💀': 'skull',
'☠': 'skull and crossbones',
'💩': 'pile of poo',
'🤡': 'clown face',
'👹': 'ogre',
'👺': 'goblin',
'👻': 'ghost',
'👽': 'alien',
'👾': 'alien monster',
'🤖': 'robot',
'😺': 'grinning cat',
'😸': 'grinning cat with smiling eyes',
'😹': 'cat with tears of joy',
'😻': 'smiling cat with heart-eyes',
'😼': 'cat with wry smile',
'😽': 'kissing cat',
'🙀': 'weary cat',
'😿': 'crying cat',
'😾': 'pouting cat',
'🙈': 'see-no-evil monkey',
'🙉': 'hear-no-evil monkey',
'🙊': 'speak-no-evil monkey',
'💋': 'kiss mark',
'💌': 'love letter',
'💘': 'heart with arrow',
'💝': 'heart with ribbon',
'💖': 'sparkling heart',
'💗': 'growing heart',
'💓': 'beating heart',
'💞': 'revolving hearts',
'💕': 'two hearts',
'💟': 'heart decoration',
'❣': 'heart exclamation',
'💔': 'broken heart',
'❤️‍🔥': 'heart on fire',
'❤️‍🩹': 'mending heart',
'❤': 'red heart',
'🧡': 'orange heart',
'💛': 'yellow heart',
'💚': 'green heart',
'💙': 'blue heart',
'💜': 'purple heart',
'🤎': 'brown heart',
'🖤': 'black heart',
'🤍': 'white heart',
'💯': 'hundred points',
'💢': 'anger symbol',
'💥': 'collision',
'💫': 'dizzy',
'💦': 'sweat droplets',
'💨': 'dashing away',
'🕳': 'hole',
'💣': 'bomb',
'💬': 'speech balloon',
'👁️‍🗨️': 'eye in speech bubble',
'🗨': 'left speech bubble',
'🗯': 'right anger bubble',
'💭': 'thought balloon',
'💤': 'zzz',
'👋': 'waving hand',
'🤚': 'raised back of hand',
'🖐': 'hand with fingers splayed',
'✋': 'raised hand',
'🖖': 'vulcan salute',
'🫱': '⊛ rightwards hand',
'🫲': '⊛ leftwards hand',
'🫳': '⊛ palm down hand',
'🫴': '⊛ palm up hand',
'👌': 'OK hand',
'🤌': 'pinched fingers',
'🤏': 'pinching hand',
'✌': 'victory hand',
'🤞': 'crossed fingers',
'🫰': '⊛ hand with index finger and thumb crossed',
'🤟': 'love-you gesture',
'🤘': 'sign of the horns',
'🤙': 'call me hand',
'👈': 'backhand index pointing left',
'👉': 'backhand index pointing right',
'👆': 'backhand index pointing up',
'🖕': 'middle finger',
'👇': 'backhand index pointing down',
'☝': 'index pointing up',
'🫵': '⊛ index pointing at the viewer',
'👍': 'thumbs up',
'👎': 'thumbs down',
'✊': 'raised fist',
'👊': 'oncoming fist',
'🤛': 'left-facing fist',
'🤜': 'right-facing fist',
'👏': 'clapping hands',
'🙌': 'raising hands',
'🫶': '⊛ heart hands',
'👐': 'open hands',
'🤲': 'palms up together',
'🤝': 'handshake',
'🙏': 'folded hands',
'✍': 'writing hand',
'💅': 'nail polish',
'🤳': 'selfie',
'💪': 'flexed biceps',
'🦾': 'mechanical arm',
'🦿': 'mechanical leg',
'🦵': 'leg',
'🦶': 'foot',
'👂': 'ear',
'🦻': 'ear with hearing aid',
'👃': 'nose',
'🧠': 'brain',
'🫀': 'anatomical heart',
'🫁': 'lungs',
'🦷': 'tooth',
'🦴': 'bone',
'👀': 'eyes',
'👁': 'eye',
'👅': 'tongue',
'👄': 'mouth',
'🫦': '⊛ biting lip',
'👶': 'baby',
'🧒': 'child',
'👦': 'boy',
'👧': 'girl',
'🧑': 'person',
'👱': 'person: blond hair',
'👨': 'man',
'🧔': 'person: beard',
'🧔‍♂️': 'man: beard',
'🧔‍♀️': 'woman: beard',
'👨‍🦰': 'man: red hair',
'👨‍🦱': 'man: curly hair',
'👨‍🦳': 'man: white hair',
'👨‍🦲': 'man: bald',
'👩': 'woman',
'👩‍🦰': 'woman: red hair',
'🧑‍🦰': 'person: red hair',
'👩‍🦱': 'woman: curly hair',
'🧑‍🦱': 'person: curly hair',
'👩‍🦳': 'woman: white hair',
'🧑‍🦳': 'person: white hair',
'👩‍🦲': 'woman: bald',
'🧑‍🦲': 'person: bald',
'👱‍♀️': 'woman: blond hair',
'👱‍♂️': 'man: blond hair',
'🧓': 'older person',
'👴': 'old man',
'👵': 'old woman',
'🙍': 'person frowning',
'🙍‍♂️': 'man frowning',
'🙍‍♀️': 'woman frowning',
'🙎': 'person pouting',
'🙎‍♂️': 'man pouting',
'🙎‍♀️': 'woman pouting',
'🙅': 'person gesturing NO',
'🙅‍♂️': 'man gesturing NO',
'🙅‍♀️': 'woman gesturing NO',
'🙆': 'person gesturing OK',
'🙆‍♂️': 'man gesturing OK',
'🙆‍♀️': 'woman gesturing OK',
'💁': 'person tipping hand',
'💁‍♂️': 'man tipping hand',
'💁‍♀️': 'woman tipping hand',
'🙋': 'person raising hand',
'🙋‍♂️': 'man raising hand',
'🙋‍♀️': 'woman raising hand',
'🧏': 'deaf person',
'🧏‍♂️': 'deaf man',
'🧏‍♀️': 'deaf woman',
'🙇': 'person bowing',
'🙇‍♂️': 'man bowing',
'🙇‍♀️': 'woman bowing',
'🤦': 'person facepalming',
'🤦‍♂️': 'man facepalming',
'🤦‍♀️': 'woman facepalming',
'🤷': 'person shrugging',
'🤷‍♂️': 'man shrugging',
'🤷‍♀️': 'woman shrugging',
'🧑‍⚕️': 'health worker',
'👨‍⚕️': 'man health worker',
'👩‍⚕️': 'woman health worker',
'🧑‍🎓': 'student',
'👨‍🎓': 'man student',
'👩‍🎓': 'woman student',
'🧑‍🏫': 'teacher',
'👨‍🏫': 'man teacher',
'👩‍🏫': 'woman teacher',
'🧑‍⚖️': 'judge',
'👨‍⚖️': 'man judge',
'👩‍⚖️': 'woman judge',
'🧑‍🌾': 'farmer',
'👨‍🌾': 'man farmer',
'👩‍🌾': 'woman farmer',
'🧑‍🍳': 'cook',
'👨‍🍳': 'man cook',
'👩‍🍳': 'woman cook',
'🧑‍🔧': 'mechanic',
'👨‍🔧': 'man mechanic',
'👩‍🔧': 'woman mechanic',
'🧑‍🏭': 'factory worker',
'👨‍🏭': 'man factory worker',
'👩‍🏭': 'woman factory worker',
'🧑‍💼': 'office worker',
'👨‍💼': 'man office worker',
'👩‍💼': 'woman office worker',
'🧑‍🔬': 'scientist',
'👨‍🔬': 'man scientist',
'👩‍🔬': 'woman scientist',
'🧑‍💻': 'technologist',
'👨‍💻': 'man technologist',
'👩‍💻': 'woman technologist',
'🧑‍🎤': 'singer',
'👨‍🎤': 'man singer',
'👩‍🎤': 'woman singer',
'🧑‍🎨': 'artist',
'👨‍🎨': 'man artist',
'👩‍🎨': 'woman artist',
'🧑‍✈️': 'pilot',
'👨‍✈️': 'man pilot',
'👩‍✈️': 'woman pilot',
'🧑‍🚀': 'astronaut',
'👨‍🚀': 'man astronaut',
'👩‍🚀': 'woman astronaut',
'🧑‍🚒': 'firefighter',
'👨‍🚒': 'man firefighter',
'👩‍🚒': 'woman firefighter',
'👮': 'police officer',
'👮‍♂️': 'man police officer',
'👮‍♀️': 'woman police officer',
'🕵': 'detective',
'🕵️‍♂️': 'man detective',
'🕵️‍♀️': 'woman detective',
'💂': 'guard',
'💂‍♂️': 'man guard',
'💂‍♀️': 'woman guard',
'🥷': 'ninja',
'👷': 'construction worker',
'👷‍♂️': 'man construction worker',
'👷‍♀️': 'woman construction worker',
'🫅': '⊛ person with crown',
'🤴': 'prince',
'👸': 'princess',
'👳': 'person wearing turban',
'👳‍♂️': 'man wearing turban',
'👳‍♀️': 'woman wearing turban',
'👲': 'person with skullcap',
'🧕': 'woman with headscarf',
'🤵': 'person in tuxedo',
'🤵‍♂️': 'man in tuxedo',
'🤵‍♀️': 'woman in tuxedo',
'👰': 'person with veil',
'👰‍♂️': 'man with veil',
'👰‍♀️': 'woman with veil',
'🤰': 'pregnant woman',
'🫃': '⊛ pregnant man',
'🫄': '⊛ pregnant person',
'🤱': 'breast-feeding',
'👩‍🍼': 'woman feeding baby',
'👨‍🍼': 'man feeding baby',
'🧑‍🍼': 'person feeding baby',
'👼': 'baby angel',
'🎅': 'Santa Claus',
'🤶': 'Mrs. Claus',
'🧑‍🎄': 'mx claus',
'🦸': 'superhero',
'🦸‍♂️': 'man superhero',
'🦸‍♀️': 'woman superhero',
'🦹': 'supervillain',
'🦹‍♂️': 'man supervillain',
'🦹‍♀️': 'woman supervillain',
'🧙': 'mage',
'🧙‍♂️': 'man mage',
'🧙‍♀️': 'woman mage',
'🧚': 'fairy',
'🧚‍♂️': 'man fairy',
'🧚‍♀️': 'woman fairy',
'🧛': 'vampire',
'🧛‍♂️': 'man vampire',
'🧛‍♀️': 'woman vampire',
'🧜': 'merperson',
'🧜‍♂️': 'merman',
'🧜‍♀️': 'mermaid',
'🧝': 'elf',
'🧝‍♂️': 'man elf',
'🧝‍♀️': 'woman elf',
'🧞': 'genie',
'🧞‍♂️': 'man genie',
'🧞‍♀️': 'woman genie',
'🧟': 'zombie',
'🧟‍♂️': 'man zombie',
'🧟‍♀️': 'woman zombie',
'🧌': '⊛ troll',
'💆': 'person getting massage',
'💆‍♂️': 'man getting massage',
'💆‍♀️': 'woman getting massage',
'💇': 'person getting haircut',
'💇‍♂️': 'man getting haircut',
'💇‍♀️': 'woman getting haircut',
'🚶': 'person walking',
'🚶‍♂️': 'man walking',
'🚶‍♀️': 'woman walking',
'🧍': 'person standing',
'🧍‍♂️': 'man standing',
'🧍‍♀️': 'woman standing',
'🧎': 'person kneeling',
'🧎‍♂️': 'man kneeling',
'🧎‍♀️': 'woman kneeling',
'🧑‍🦯': 'person with white cane',
'👨‍🦯': 'man with white cane',
'👩‍🦯': 'woman with white cane',
'🧑‍🦼': 'person in motorized wheelchair',
'👨‍🦼': 'man in motorized wheelchair',
'👩‍🦼': 'woman in motorized wheelchair',
'🧑‍🦽': 'person in manual wheelchair',
'👨‍🦽': 'man in manual wheelchair',
'👩‍🦽': 'woman in manual wheelchair',
'🏃': 'person running',
'🏃‍♂️': 'man running',
'🏃‍♀️': 'woman running',
'💃': 'woman dancing',
'🕺': 'man dancing',
'🕴': 'person in suit levitating',
'👯': 'people with bunny ears',
'👯‍♂️': 'men with bunny ears',
'👯‍♀️': 'women with bunny ears',
'🧖': 'person in steamy room',
'🧖‍♂️': 'man in steamy room',
'🧖‍♀️': 'woman in steamy room',
'🧗': 'person climbing',
'🧗‍♂️': 'man climbing',
'🧗‍♀️': 'woman climbing',
'🤺': 'person fencing',
'🏇': 'horse racing',
'⛷': 'skier',
'🏂': 'snowboarder',
'🏌': 'person golfing',
'🏌️‍♂️': 'man golfing',
'🏌️‍♀️': 'woman golfing',
'🏄': 'person surfing',
'🏄‍♂️': 'man surfing',
'🏄‍♀️': 'woman surfing',
'🚣': 'person rowing boat',
'🚣‍♂️': 'man rowing boat',
'🚣‍♀️': 'woman rowing boat',
'🏊': 'person swimming',
'🏊‍♂️': 'man swimming',
'🏊‍♀️': 'woman swimming',
'⛹': 'person bouncing ball',
'⛹️‍♂️': 'man bouncing ball',
'⛹️‍♀️': 'woman bouncing ball',
'🏋': 'person lifting weights',
'🏋️‍♂️': 'man lifting weights',
'🏋️‍♀️': 'woman lifting weights',
'🚴': 'person biking',
'🚴‍♂️': 'man biking',
'🚴‍♀️': 'woman biking',
'🚵': 'person mountain biking',
'🚵‍♂️': 'man mountain biking',
'🚵‍♀️': 'woman mountain biking',
'🤸': 'person cartwheeling',
'🤸‍♂️': 'man cartwheeling',
'🤸‍♀️': 'woman cartwheeling',
'🤼': 'people wrestling',
'🤼‍♂️': 'men wrestling',
'🤼‍♀️': 'women wrestling',
'🤽': 'person playing water polo',
'🤽‍♂️': 'man playing water polo',
'🤽‍♀️': 'woman playing water polo',
'🤾': 'person playing handball',
'🤾‍♂️': 'man playing handball',
'🤾‍♀️': 'woman playing handball',
'🤹': 'person juggling',
'🤹‍♂️': 'man juggling',
'🤹‍♀️': 'woman juggling',
'🧘': 'person in lotus position',
'🧘‍♂️': 'man in lotus position',
'🧘‍♀️': 'woman in lotus position',
'🛀': 'person taking bath',
'🛌': 'person in bed',
'🧑‍🤝‍🧑': 'people holding hands',
'👭': 'women holding hands',
'👫': 'woman and man holding hands',
'👬': 'men holding hands',
'💏': 'kiss',
'👩‍❤️‍💋‍👨': 'kiss: woman, man',
'👨‍❤️‍💋‍👨': 'kiss: man, man',
'👩‍❤️‍💋‍👩': 'kiss: woman, woman',
'💑': 'couple with heart',
'👩‍❤️‍👨': 'couple with heart: woman, man',
'👨‍❤️‍👨': 'couple with heart: man, man',
'👩‍❤️‍👩': 'couple with heart: woman, woman',
'👪': 'family',
'👨‍👩‍👦': 'family: man, woman, boy',
'👨‍👩‍👧': 'family: man, woman, girl',
'👨‍👩‍👧‍👦': 'family: man, woman, girl, boy',
'👨‍👩‍👦‍👦': 'family: man, woman, boy, boy',
'👨‍👩‍👧‍👧': 'family: man, woman, girl, girl',
'👨‍👨‍👦': 'family: man, man, boy',
'👨‍👨‍👧': 'family: man, man, girl',
'👨‍👨‍👧‍👦': 'family: man, man, girl, boy',
'👨‍👨‍👦‍👦': 'family: man, man, boy, boy',
'👨‍👨‍👧‍👧': 'family: man, man, girl, girl',
'👩‍👩‍👦': 'family: woman, woman, boy',
'👩‍👩‍👧': 'family: woman, woman, girl',
'👩‍👩‍👧‍👦': 'family: woman, woman, girl, boy',
'👩‍👩‍👦‍👦': 'family: woman, woman, boy, boy',
'👩‍👩‍👧‍👧': 'family: woman, woman, girl, girl',
'👨‍👦': 'family: man, boy',
'👨‍👦‍👦': 'family: man, boy, boy',
'👨‍👧': 'family: man, girl',
'👨‍👧‍👦': 'family: man, girl, boy',
'👨‍👧‍👧': 'family: man, girl, girl',
'👩‍👦': 'family: woman, boy',
'👩‍👦‍👦': 'family: woman, boy, boy',
'👩‍👧': 'family: woman, girl',
'👩‍👧‍👦': 'family: woman, girl, boy',
'👩‍👧‍👧': 'family: woman, girl, girl',
'🗣': 'speaking head',
'👤': 'bust in silhouette',
'👥': 'busts in silhouette',
'🫂': 'people hugging',
'👣': 'footprints',
'🦰': 'red hair',
'🦱': 'curly hair',
'🦳': 'white hair',
'🦲': 'bald',
'🐵': 'monkey face',
'🐒': 'monkey',
'🦍': 'gorilla',
'🦧': 'orangutan',
'🐶': 'dog face',
'🐕': 'dog',
'🦮': 'guide dog',
'🐕‍🦺': 'service dog',
'🐩': 'poodle',
'🐺': 'wolf',
'🦊': 'fox',
'🦝': 'raccoon',
'🐱': 'cat face',
'🐈': 'cat',
'🐈‍⬛': 'black cat',
'🦁': 'lion',
'🐯': 'tiger face',
'🐅': 'tiger',
'🐆': 'leopard',
'🐴': 'horse face',
'🐎': 'horse',
'🦄': 'unicorn',
'🦓': 'zebra',
'🦌': 'deer',
'🦬': 'bison',
'🐮': 'cow face',
'🐂': 'ox',
'🐃': 'water buffalo',
'🐄': 'cow',
'🐷': 'pig face',
'🐖': 'pig',
'🐗': 'boar',
'🐽': 'pig nose',
'🐏': 'ram',
'🐑': 'ewe',
'🐐': 'goat',
'🐪': 'camel',
'🐫': 'two-hump camel',
'🦙': 'llama',
'🦒': 'giraffe',
'🐘': 'elephant',
'🦣': 'mammoth',
'🦏': 'rhinoceros',
'🦛': 'hippopotamus',
'🐭': 'mouse face',
'🐁': 'mouse',
'🐀': 'rat',
'🐹': 'hamster',
'🐰': 'rabbit face',
'🐇': 'rabbit',
'🐿': 'chipmunk',
'🦫': 'beaver',
'🦔': 'hedgehog',
'🦇': 'bat',
'🐻': 'bear',
'🐻‍❄️': 'polar bear',
'🐨': 'koala',
'🐼': 'panda',
'🦥': 'sloth',
'🦦': 'otter',
'🦨': 'skunk',
'🦘': 'kangaroo',
'🦡': 'badger',
'🐾': 'paw prints',
'🦃': 'turkey',
'🐔': 'chicken',
'🐓': 'rooster',
'🐣': 'hatching chick',
'🐤': 'baby chick',
'🐥': 'front-facing baby chick',
'🐦': 'bird',
'🐧': 'penguin',
'🕊': 'dove',
'🦅': 'eagle',
'🦆': 'duck',
'🦢': 'swan',
'🦉': 'owl',
'🦤': 'dodo',
'🪶': 'feather',
'🦩': 'flamingo',
'🦚': 'peacock',
'🦜': 'parrot',
'🐸': 'frog',
'🐊': 'crocodile',
'🐢': 'turtle',
'🦎': 'lizard',
'🐍': 'snake',
'🐲': 'dragon face',
'🐉': 'dragon',
'🦕': 'sauropod',
'🦖': 'T-Rex',
'🐳': 'spouting whale',
'🐋': 'whale',
'🐬': 'dolphin',
'🦭': 'seal',
'🐟': 'fish',
'🐠': 'tropical fish',
'🐡': 'blowfish',
'🦈': 'shark',
'🐙': 'octopus',
'🐚': 'spiral shell',
'🪸': '⊛ coral',
'🐌': 'snail',
'🦋': 'butterfly',
'🐛': 'bug',
'🐜': 'ant',
'🐝': 'honeybee',
'🪲': 'beetle',
'🐞': 'lady beetle',
'🦗': 'cricket',
'🪳': 'cockroach',
'🕷': 'spider',
'🕸': 'spider web',
'🦂': 'scorpion',
'🦟': 'mosquito',
'🪰': 'fly',
'🪱': 'worm',
'🦠': 'microbe',
'💐': 'bouquet',
'🌸': 'cherry blossom',
'💮': 'white flower',
'🪷': '⊛ lotus',
'🏵': 'rosette',
'🌹': 'rose',
'🥀': 'wilted flower',
'🌺': 'hibiscus',
'🌻': 'sunflower',
'🌼': 'blossom',
'🌷': 'tulip',
'🌱': 'seedling',
'🪴': 'potted plant',
'🌲': 'evergreen tree',
'🌳': 'deciduous tree',
'🌴': 'palm tree',
'🌵': 'cactus',
'🌾': 'sheaf of rice',
'🌿': 'herb',
'☘': 'shamrock',
'🍀': 'four leaf clover',
'🍁': 'maple leaf',
'🍂': 'fallen leaf',
'🍃': 'leaf fluttering in wind',
'🪹': '⊛ empty nest',
'🪺': '⊛ nest with eggs',
'🍇': 'grapes',
'🍈': 'melon',
'🍉': 'watermelon',
'🍊': 'tangerine',
'🍋': 'lemon',
'🍌': 'banana',
'🍍': 'pineapple',
'🥭': 'mango',
'🍎': 'red apple',
'🍏': 'green apple',
'🍐': 'pear',
'🍑': 'peach',
'🍒': 'cherries',
'🍓': 'strawberry',
'🫐': 'blueberries',
'🥝': 'kiwi fruit',
'🍅': 'tomato',
'🫒': 'olive',
'🥥': 'coconut',
'🥑': 'avocado',
'🍆': 'eggplant',
'🥔': 'potato',
'🥕': 'carrot',
'🌽': 'ear of corn',
'🌶': 'hot pepper',
'🫑': 'bell pepper',
'🥒': 'cucumber',
'🥬': 'leafy green',
'🥦': 'broccoli',
'🧄': 'garlic',
'🧅': 'onion',
'🍄': 'mushroom',
'🥜': 'peanuts',
'🫘': '⊛ beans',
'🌰': 'chestnut',
'🍞': 'bread',
'🥐': 'croissant',
'🥖': 'baguette bread',
'🫓': 'flatbread',
'🥨': 'pretzel',
'🥯': 'bagel',
'🥞': 'pancakes',
'🧇': 'waffle',
'🧀': 'cheese wedge',
'🍖': 'meat on bone',
'🍗': 'poultry leg',
'🥩': 'cut of meat',
'🥓': 'bacon',
'🍔': 'hamburger',
'🍟': 'french fries',
'🍕': 'pizza',
'🌭': 'hot dog',
'🥪': 'sandwich',
'🌮': 'taco',
'🌯': 'burrito',
'🫔': 'tamale',
'🥙': 'stuffed flatbread',
'🧆': 'falafel',
'🥚': 'egg',
'🍳': 'cooking',
'🥘': 'shallow pan of food',
'🍲': 'pot of food',
'🫕': 'fondue',
'🥣': 'bowl with spoon',
'🥗': 'green salad',
'🍿': 'popcorn',
'🧈': 'butter',
'🧂': 'salt',
'🥫': 'canned food',
'🍱': 'bento box',
'🍘': 'rice cracker',
'🍙': 'rice ball',
'🍚': 'cooked rice',
'🍛': 'curry rice',
'🍜': 'steaming bowl',
'🍝': 'spaghetti',
'🍠': 'roasted sweet potato',
'🍢': 'oden',
'🍣': 'sushi',
'🍤': 'fried shrimp',
'🍥': 'fish cake with swirl',
'🥮': 'moon cake',
'🍡': 'dango',
'🥟': 'dumpling',
'🥠': 'fortune cookie',
'🥡': 'takeout box',
'🦀': 'crab',
'🦞': 'lobster',
'🦐': 'shrimp',
'🦑': 'squid',
'🦪': 'oyster',
'🍦': 'soft ice cream',
'🍧': 'shaved ice',
'🍨': 'ice cream',
'🍩': 'doughnut',
'🍪': 'cookie',
'🎂': 'birthday cake',
'🍰': 'shortcake',
'🧁': 'cupcake',
'🥧': 'pie',
'🍫': 'chocolate bar',
'🍬': 'candy',
'🍭': 'lollipop',
'🍮': 'custard',
'🍯': 'honey pot',
'🍼': 'baby bottle',
'🥛': 'glass of milk',
'☕': 'hot beverage',
'🫖': 'teapot',
'🍵': 'teacup without handle',
'🍶': 'sake',
'🍾': 'bottle with popping cork',
'🍷': 'wine glass',
'🍸': 'cocktail glass',
'🍹': 'tropical drink',
'🍺': 'beer mug',
'🍻': 'clinking beer mugs',
'🥂': 'clinking glasses',
'🥃': 'tumbler glass',
'🫗': '⊛ pouring liquid',
'🥤': 'cup with straw',
'🧋': 'bubble tea',
'🧃': 'beverage box',
'🧉': 'mate',
'🧊': 'ice',
'🥢': 'chopsticks',
'🍽': 'fork and knife with plate',
'🍴': 'fork and knife',
'🥄': 'spoon',
'🔪': 'kitchen knife',
'🫙': '⊛ jar',
'🏺': 'amphora',
'🌍': 'globe showing Europe-Africa',
'🌎': 'globe showing Americas',
'🌏': 'globe showing Asia-Australia',
'🌐': 'globe with meridians',
'🗺': 'world map',
'🗾': 'map of Japan',
'🧭': 'compass',
'🏔': 'snow-capped mountain',
'⛰': 'mountain',
'🌋': 'volcano',
'🗻': 'mount fuji',
'🏕': 'camping',
'🏖': 'beach with umbrella',
'🏜': 'desert',
'🏝': 'desert island',
'🏞': 'national park',
'🏟': 'stadium',
'🏛': 'classical building',
'🏗': 'building construction',
'🧱': 'brick',
'🪨': 'rock',
'🪵': 'wood',
'🛖': 'hut',
'🏘': 'houses',
'🏚': 'derelict house',
'🏠': 'house',
'🏡': 'house with garden',
'🏢': 'office building',
'🏣': 'Japanese post office',
'🏤': 'post office',
'🏥': 'hospital',
'🏦': 'bank',
'🏨': 'hotel',
'🏩': 'love hotel',
'🏪': 'convenience store',
'🏫': 'school',
'🏬': 'department store',
'🏭': 'factory',
'🏯': 'Japanese castle',
'🏰': 'castle',
'💒': 'wedding',
'🗼': 'Tokyo tower',
'🗽': 'Statue of Liberty',
'⛪': 'church',
'🕌': 'mosque',
'🛕': 'hindu temple',
'🕍': 'synagogue',
'⛩': 'shinto shrine',
'🕋': 'kaaba',
'⛲': 'fountain',
'⛺': 'tent',
'🌁': 'foggy',
'🌃': 'night with stars',
'🏙': 'cityscape',
'🌄': 'sunrise over mountains',
'🌅': 'sunrise',
'🌆': 'cityscape at dusk',
'🌇': 'sunset',
'🌉': 'bridge at night',
'♨': 'hot springs',
'🎠': 'carousel horse',
'🛝': '⊛ playground slide',
'🎡': 'ferris wheel',
'🎢': 'roller coaster',
'💈': 'barber pole',
'🎪': 'circus tent',
'🚂': 'locomotive',
'🚃': 'railway car',
'🚄': 'high-speed train',
'🚅': 'bullet train',
'🚆': 'train',
'🚇': 'metro',
'🚈': 'light rail',
'🚉': 'station',
'🚊': 'tram',
'🚝': 'monorail',
'🚞': 'mountain railway',
'🚋': 'tram car',
'🚌': 'bus',
'🚍': 'oncoming bus',
'🚎': 'trolleybus',
'🚐': 'minibus',
'🚑': 'ambulance',
'🚒': 'fire engine',
'🚓': 'police car',
'🚔': 'oncoming police car',
'🚕': 'taxi',
'🚖': 'oncoming taxi',
'🚗': 'automobile',
'🚘': 'oncoming automobile',
'🚙': 'sport utility vehicle',
'🛻': 'pickup truck',
'🚚': 'delivery truck',
'🚛': 'articulated lorry',
'🚜': 'tractor',
'🏎': 'racing car',
'🏍': 'motorcycle',
'🛵': 'motor scooter',
'🦽': 'manual wheelchair',
'🦼': 'motorized wheelchair',
'🛺': 'auto rickshaw',
'🚲': 'bicycle',
'🛴': 'kick scooter',
'🛹': 'skateboard',
'🛼': 'roller skate',
'🚏': 'bus stop',
'🛣': 'motorway',
'🛤': 'railway track',
'🛢': 'oil drum',
'⛽': 'fuel pump',
'🛞': '⊛ wheel',
'🚨': 'police car light',
'🚥': 'horizontal traffic light',
'🚦': 'vertical traffic light',
'🛑': 'stop sign',
'🚧': 'construction',
'⚓': 'anchor',
'🛟': '⊛ ring buoy',
'⛵': 'sailboat',
'🛶': 'canoe',
'🚤': 'speedboat',
'🛳': 'passenger ship',
'⛴': 'ferry',
'🛥': 'motor boat',
'🚢': 'ship',
'✈': 'airplane',
'🛩': 'small airplane',
'🛫': 'airplane departure',
'🛬': 'airplane arrival',
'🪂': 'parachute',
'💺': 'seat',
'🚁': 'helicopter',
'🚟': 'suspension railway',
'🚠': 'mountain cableway',
'🚡': 'aerial tramway',
'🛰': 'satellite',
'🚀': 'rocket',
'🛸': 'flying saucer',
'🛎': 'bellhop bell',
'🧳': 'luggage',
'⌛': 'hourglass done',
'⏳': 'hourglass not done',
'⌚': 'watch',
'⏰': 'alarm clock',
'⏱': 'stopwatch',
'⏲': 'timer clock',
'🕰': 'mantelpiece clock',
'🕛': 'twelve oclock',
'🕧': 'twelve-thirty',
'🕐': 'one oclock',
'🕜': 'one-thirty',
'🕑': 'two oclock',
'🕝': 'two-thirty',
'🕒': 'three oclock',
'🕞': 'three-thirty',
'🕓': 'four oclock',
'🕟': 'four-thirty',
'🕔': 'five oclock',
'🕠': 'five-thirty',
'🕕': 'six oclock',
'🕡': 'six-thirty',
'🕖': 'seven oclock',
'🕢': 'seven-thirty',
'🕗': 'eight oclock',
'🕣': 'eight-thirty',
'🕘': 'nine oclock',
'🕤': 'nine-thirty',
'🕙': 'ten oclock',
'🕥': 'ten-thirty',
'🕚': 'eleven oclock',
'🕦': 'eleven-thirty',
'🌑': 'new moon',
'🌒': 'waxing crescent moon',
'🌓': 'first quarter moon',
'🌔': 'waxing gibbous moon',
'🌕': 'full moon',
'🌖': 'waning gibbous moon',
'🌗': 'last quarter moon',
'🌘': 'waning crescent moon',
'🌙': 'crescent moon',
'🌚': 'new moon face',
'🌛': 'first quarter moon face',
'🌜': 'last quarter moon face',
'🌡': 'thermometer',
'☀': 'sun',
'🌝': 'full moon face',
'🌞': 'sun with face',
'🪐': 'ringed planet',
'⭐': 'star',
'🌟': 'glowing star',
'🌠': 'shooting star',
'🌌': 'milky way',
'☁': 'cloud',
'⛅': 'sun behind cloud',
'⛈': 'cloud with lightning and rain',
'🌤': 'sun behind small cloud',
'🌥': 'sun behind large cloud',
'🌦': 'sun behind rain cloud',
'🌧': 'cloud with rain',
'🌨': 'cloud with snow',
'🌩': 'cloud with lightning',
'🌪': 'tornado',
'🌫': 'fog',
'🌬': 'wind face',
'🌀': 'cyclone',
'🌈': 'rainbow',
'🌂': 'closed umbrella',
'☂': 'umbrella',
'☔': 'umbrella with rain drops',
'⛱': 'umbrella on ground',
'⚡': 'high voltage',
'❄': 'snowflake',
'☃': 'snowman',
'⛄': 'snowman without snow',
'☄': 'comet',
'🔥': 'fire',
'💧': 'droplet',
'🌊': 'water wave',
'🎃': 'jack-o-lantern',
'🎄': 'Christmas tree',
'🎆': 'fireworks',
'🎇': 'sparkler',
'🧨': 'firecracker',
'✨': 'sparkles',
'🎈': 'balloon',
'🎉': 'party popper',
'🎊': 'confetti ball',
'🎋': 'tanabata tree',
'🎍': 'pine decoration',
'🎎': 'Japanese dolls',
'🎏': 'carp streamer',
'🎐': 'wind chime',
'🎑': 'moon viewing ceremony',
'🧧': 'red envelope',
'🎀': 'ribbon',
'🎁': 'wrapped gift',
'🎗': 'reminder ribbon',
'🎟': 'admission tickets',
'🎫': 'ticket',
'🎖': 'military medal',
'🏆': 'trophy',
'🏅': 'sports medal',
'🥇': '1st place medal',
'🥈': '2nd place medal',
'🥉': '3rd place medal',
'⚽': 'soccer ball',
'⚾': 'baseball',
'🥎': 'softball',
'🏀': 'basketball',
'🏐': 'volleyball',
'🏈': 'american football',
'🏉': 'rugby football',
'🎾': 'tennis',
'🥏': 'flying disc',
'🎳': 'bowling',
'🏏': 'cricket game',
'🏑': 'field hockey',
'🏒': 'ice hockey',
'🥍': 'lacrosse',
'🏓': 'ping pong',
'🏸': 'badminton',
'🥊': 'boxing glove',
'🥋': 'martial arts uniform',
'🥅': 'goal net',
'⛳': 'flag in hole',
'⛸': 'ice skate',
'🎣': 'fishing pole',
'🤿': 'diving mask',
'🎽': 'running shirt',
'🎿': 'skis',
'🛷': 'sled',
'🥌': 'curling stone',
'🎯': 'bullseye',
'🪀': 'yo-yo',
'🪁': 'kite',
'🎱': 'pool 8 ball',
'🔮': 'crystal ball',
'🪄': 'magic wand',
'🧿': 'nazar amulet',
'🪬': '⊛ hamsa',
'🎮': 'video game',
'🕹': 'joystick',
'🎰': 'slot machine',
'🎲': 'game die',
'🧩': 'puzzle piece',
'🧸': 'teddy bear',
'🪅': 'piñata',
'🪩': '⊛ mirror ball',
'🪆': 'nesting dolls',
'♠': 'spade suit',
'♥': 'heart suit',
'♦': 'diamond suit',
'♣': 'club suit',
'♟': 'chess pawn',
'🃏': 'joker',
'🀄': 'mahjong red dragon',
'🎴': 'flower playing cards',
'🎭': 'performing arts',
'🖼': 'framed picture',
'🎨': 'artist palette',
'🧵': 'thread',
'🪡': 'sewing needle',
'🧶': 'yarn',
'🪢': 'knot',
'👓': 'glasses',
'🕶': 'sunglasses',
'🥽': 'goggles',
'🥼': 'lab coat',
'🦺': 'safety vest',
'👔': 'necktie',
'👕': 't-shirt',
'👖': 'jeans',
'🧣': 'scarf',
'🧤': 'gloves',
'🧥': 'coat',
'🧦': 'socks',
'👗': 'dress',
'👘': 'kimono',
'🥻': 'sari',
'🩱': 'one-piece swimsuit',
'🩲': 'briefs',
'🩳': 'shorts',
'👙': 'bikini',
'👚': 'womans clothes',
'👛': 'purse',
'👜': 'handbag',
'👝': 'clutch bag',
'🛍': 'shopping bags',
'🎒': 'backpack',
'🩴': 'thong sandal',
'👞': 'mans shoe',
'👟': 'running shoe',
'🥾': 'hiking boot',
'🥿': 'flat shoe',
'👠': 'high-heeled shoe',
'👡': 'womans sandal',
'🩰': 'ballet shoes',
'👢': 'womans boot',
'👑': 'crown',
'👒': 'womans hat',
'🎩': 'top hat',
'🎓': 'graduation cap',
'🧢': 'billed cap',
'🪖': 'military helmet',
'⛑': 'rescue workers helmet',
'📿': 'prayer beads',
'💄': 'lipstick',
'💍': 'ring',
'💎': 'gem stone',
'🔇': 'muted speaker',
'🔈': 'speaker low volume',
'🔉': 'speaker medium volume',
'🔊': 'speaker high volume',
'📢': 'loudspeaker',
'📣': 'megaphone',
'📯': 'postal horn',
'🔔': 'bell',
'🔕': 'bell with slash',
'🎼': 'musical score',
'🎵': 'musical note',
'🎶': 'musical notes',
'🎙': 'studio microphone',
'🎚': 'level slider',
'🎛': 'control knobs',
'🎤': 'microphone',
'🎧': 'headphone',
'📻': 'radio',
'🎷': 'saxophone',
'🪗': 'accordion',
'🎸': 'guitar',
'🎹': 'musical keyboard',
'🎺': 'trumpet',
'🎻': 'violin',
'🪕': 'banjo',
'🥁': 'drum',
'🪘': 'long drum',
'📱': 'mobile phone',
'📲': 'mobile phone with arrow',
'☎': 'telephone',
'📞': 'telephone receiver',
'📟': 'pager',
'📠': 'fax machine',
'🔋': 'battery',
'🪫': '⊛ low battery',
'🔌': 'electric plug',
'💻': 'laptop',
'🖥': 'desktop computer',
'🖨': 'printer',
'⌨': 'keyboard',
'🖱': 'computer mouse',
'🖲': 'trackball',
'💽': 'computer disk',
'💾': 'floppy disk',
'💿': 'optical disk',
'📀': 'dvd',
'🧮': 'abacus',
'🎥': 'movie camera',
'🎞': 'film frames',
'📽': 'film projector',
'🎬': 'clapper board',
'📺': 'television',
'📷': 'camera',
'📸': 'camera with flash',
'📹': 'video camera',
'📼': 'videocassette',
'🔍': 'magnifying glass tilted left',
'🔎': 'magnifying glass tilted right',
'🕯': 'candle',
'💡': 'light bulb',
'🔦': 'flashlight',
'🏮': 'red paper lantern',
'🪔': 'diya lamp',
'📔': 'notebook with decorative cover',
'📕': 'closed book',
'📖': 'open book',
'📗': 'green book',
'📘': 'blue book',
'📙': 'orange book',
'📚': 'books',
'📓': 'notebook',
'📒': 'ledger',
'📃': 'page with curl',
'📜': 'scroll',
'📄': 'page facing up',
'📰': 'newspaper',
'🗞': 'rolled-up newspaper',
'📑': 'bookmark tabs',
'🔖': 'bookmark',
'🏷': 'label',
'💰': 'money bag',
'🪙': 'coin',
'💴': 'yen banknote',
'💵': 'dollar banknote',
'💶': 'euro banknote',
'💷': 'pound banknote',
'💸': 'money with wings',
'💳': 'credit card',
'🧾': 'receipt',
'💹': 'chart increasing with yen',
'✉': 'envelope',
'📧': 'e-mail',
'📨': 'incoming envelope',
'📩': 'envelope with arrow',
'📤': 'outbox tray',
'📥': 'inbox tray',
'📦': 'package',
'📫': 'closed mailbox with raised flag',
'📪': 'closed mailbox with lowered flag',
'📬': 'open mailbox with raised flag',
'📭': 'open mailbox with lowered flag',
'📮': 'postbox',
'🗳': 'ballot box with ballot',
'✏': 'pencil',
'✒': 'black nib',
'🖋': 'fountain pen',
'🖊': 'pen',
'🖌': 'paintbrush',
'🖍': 'crayon',
'📝': 'memo',
'💼': 'briefcase',
'📁': 'file folder',
'📂': 'open file folder',
'🗂': 'card index dividers',
'📅': 'calendar',
'📆': 'tear-off calendar',
'🗒': 'spiral notepad',
'🗓': 'spiral calendar',
'📇': 'card index',
'📈': 'chart increasing',
'📉': 'chart decreasing',
'📊': 'bar chart',
'📋': 'clipboard',
'📌': 'pushpin',
'📍': 'round pushpin',
'📎': 'paperclip',
'🖇': 'linked paperclips',
'📏': 'straight ruler',
'📐': 'triangular ruler',
'✂': 'scissors',
'🗃': 'card file box',
'🗄': 'file cabinet',
'🗑': 'wastebasket',
'🔒': 'locked',
'🔓': 'unlocked',
'🔏': 'locked with pen',
'🔐': 'locked with key',
'🔑': 'key',
'🗝': 'old key',
'🔨': 'hammer',
'🪓': 'axe',
'⛏': 'pick',
'⚒': 'hammer and pick',
'🛠': 'hammer and wrench',
'🗡': 'dagger',
'⚔': 'crossed swords',
'🔫': 'water pistol',
'🪃': 'boomerang',
'🏹': 'bow and arrow',
'🛡': 'shield',
'🪚': 'carpentry saw',
'🔧': 'wrench',
'🪛': 'screwdriver',
'🔩': 'nut and bolt',
'⚙': 'gear',
'🗜': 'clamp',
'⚖': 'balance scale',
'🦯': 'white cane',
'🔗': 'link',
'⛓': 'chains',
'🪝': 'hook',
'🧰': 'toolbox',
'🧲': 'magnet',
'🪜': 'ladder',
'⚗': 'alembic',
'🧪': 'test tube',
'🧫': 'petri dish',
'🧬': 'dna',
'🔬': 'microscope',
'🔭': 'telescope',
'📡': 'satellite antenna',
'💉': 'syringe',
'🩸': 'drop of blood',
'💊': 'pill',
'🩹': 'adhesive bandage',
'🩼': '⊛ crutch',
'🩺': 'stethoscope',
'🩻': '⊛ x-ray',
'🚪': 'door',
'🛗': 'elevator',
'🪞': 'mirror',
'🪟': 'window',
'🛏': 'bed',
'🛋': 'couch and lamp',
'🪑': 'chair',
'🚽': 'toilet',
'🪠': 'plunger',
'🚿': 'shower',
'🛁': 'bathtub',
'🪤': 'mouse trap',
'🪒': 'razor',
'🧴': 'lotion bottle',
'🧷': 'safety pin',
'🧹': 'broom',
'🧺': 'basket',
'🧻': 'roll of paper',
'🪣': 'bucket',
'🧼': 'soap',
'🫧': '⊛ bubbles',
'🪥': 'toothbrush',
'🧽': 'sponge',
'🧯': 'fire extinguisher',
'🛒': 'shopping cart',
'🚬': 'cigarette',
'⚰': 'coffin',
'🪦': 'headstone',
'⚱': 'funeral urn',
'🗿': 'moai',
'🪧': 'placard',
'🪪': '⊛ identification card',
'🏧': 'ATM sign',
'🚮': 'litter in bin sign',
'🚰': 'potable water',
'♿': 'wheelchair symbol',
'🚹': 'mens room',
'🚺': 'womens room',
'🚻': 'restroom',
'🚼': 'baby symbol',
'🚾': 'water closet',
'🛂': 'passport control',
'🛃': 'customs',
'🛄': 'baggage claim',
'🛅': 'left luggage',
'⚠': 'warning',
'🚸': 'children crossing',
'⛔': 'no entry',
'🚫': 'prohibited',
'🚳': 'no bicycles',
'🚭': 'no smoking',
'🚯': 'no littering',
'🚱': 'non-potable water',
'🚷': 'no pedestrians',
'📵': 'no mobile phones',
'🔞': 'no one under eighteen',
'☢': 'radioactive',
'☣': 'biohazard',
'⬆': 'up arrow',
'↗': 'up-right arrow',
'➡': 'right arrow',
'↘': 'down-right arrow',
'⬇': 'down arrow',
'↙': 'down-left arrow',
'⬅': 'left arrow',
'↖': 'up-left arrow',
'↕': 'up-down arrow',
'↔': 'left-right arrow',
'↩': 'right arrow curving left',
'↪': 'left arrow curving right',
'⤴': 'right arrow curving up',
'⤵': 'right arrow curving down',
'🔃': 'clockwise vertical arrows',
'🔄': 'counterclockwise arrows button',
'🔙': 'BACK arrow',
'🔚': 'END arrow',
'🔛': 'ON! arrow',
'🔜': 'SOON arrow',
'🔝': 'TOP arrow',
'🛐': 'place of worship',
'⚛': 'atom symbol',
'🕉': 'om',
'✡': 'star of David',
'☸': 'wheel of dharma',
'☯': 'yin yang',
'✝': 'latin cross',
'☦': 'orthodox cross',
'☪': 'star and crescent',
'☮': 'peace symbol',
'🕎': 'menorah',
'🔯': 'dotted six-pointed star',
'♈': 'Aries',
'♉': 'Taurus',
'♊': 'Gemini',
'♋': 'Cancer',
'♌': 'Leo',
'♍': 'Virgo',
'♎': 'Libra',
'♏': 'Scorpio',
'♐': 'Sagittarius',
'♑': 'Capricorn',
'♒': 'Aquarius',
'♓': 'Pisces',
'⛎': 'Ophiuchus',
'🔀': 'shuffle tracks button',
'🔁': 'repeat button',
'🔂': 'repeat single button',
'▶': 'play button',
'⏩': 'fast-forward button',
'⏭': 'next track button',
'⏯': 'play or pause button',
'◀': 'reverse button',
'⏪': 'fast reverse button',
'⏮': 'last track button',
'🔼': 'upwards button',
'⏫': 'fast up button',
'🔽': 'downwards button',
'⏬': 'fast down button',
'⏸': 'pause button',
'⏹': 'stop button',
'⏺': 'record button',
'⏏': 'eject button',
'🎦': 'cinema',
'🔅': 'dim button',
'🔆': 'bright button',
'📶': 'antenna bars',
'📳': 'vibration mode',
'📴': 'mobile phone off',
'♀': 'female sign',
'♂': 'male sign',
'⚧': 'transgender symbol',
'✖': 'multiply',
'': 'plus',
'': 'minus',
'➗': 'divide',
'🟰': '⊛ heavy equals sign',
'♾': 'infinity',
'‼': 'double exclamation mark',
'⁉': 'exclamation question mark',
'❓': 'red question mark',
'❔': 'white question mark',
'❕': 'white exclamation mark',
'❗': 'red exclamation mark',
'〰': 'wavy dash',
'💱': 'currency exchange',
'💲': 'heavy dollar sign',
'⚕': 'medical symbol',
'♻': 'recycling symbol',
'⚜': 'fleur-de-lis',
'🔱': 'trident emblem',
'📛': 'name badge',
'🔰': 'Japanese symbol for beginner',
'⭕': 'hollow red circle',
'✅': 'check mark button',
'☑': 'check box with check',
'✔': 'check mark',
'❌': 'cross mark',
'❎': 'cross mark button',
'➰': 'curly loop',
'➿': 'double curly loop',
'〽': 'part alternation mark',
'✳': 'eight-spoked asterisk',
'✴': 'eight-pointed star',
'❇': 'sparkle',
'©': 'copyright',
'®': 'registered',
'™': 'trade mark',
'#️⃣': 'keycap: #',
'*️⃣': 'keycap: *',
'0⃣': 'keycap: 0',
'1⃣': 'keycap: 1',
'2⃣': 'keycap: 2',
'3⃣': 'keycap: 3',
'4⃣': 'keycap: 4',
'5⃣': 'keycap: 5',
'6⃣': 'keycap: 6',
'7⃣': 'keycap: 7',
'8⃣': 'keycap: 8',
'9⃣': 'keycap: 9',
'🔟': 'keycap: 10',
'🔠': 'input latin uppercase',
'🔡': 'input latin lowercase',
'🔢': 'input numbers',
'🔣': 'input symbols',
'🔤': 'input latin letters',
'🅰': 'A button (blood type)',
'🆎': 'AB button (blood type)',
'🅱': 'B button (blood type)',
'🆑': 'CL button',
'🆒': 'COOL button',
'🆓': 'FREE button',
: 'information',
'🆔': 'ID button',
'Ⓜ': 'circled M',
'🆕': 'NEW button',
'🆖': 'NG button',
'🅾': 'O button (blood type)',
'🆗': 'OK button',
'🅿': 'P button',
'🆘': 'SOS button',
'🆙': 'UP! button',
'🆚': 'VS button',
'🈁': 'Japanese “here” button',
'🈂': 'Japanese “service charge” button',
'🈷': 'Japanese “monthly amount” button',
'🈶': 'Japanese “not free of charge” button',
'🈯': 'Japanese “reserved” button',
'🉐': 'Japanese “bargain” button',
'🈹': 'Japanese “discount” button',
'🈚': 'Japanese “free of charge” button',
'🈲': 'Japanese “prohibited” button',
'🉑': 'Japanese “acceptable” button',
'🈸': 'Japanese “application” button',
'🈴': 'Japanese “passing grade” button',
'🈳': 'Japanese “vacancy” button',
'㊗': 'Japanese “congratulations” button',
'㊙': 'Japanese “secret” button',
'🈺': 'Japanese “open for business” button',
'🈵': 'Japanese “no vacancy” button',
'🔴': 'red circle',
'🟠': 'orange circle',
'🟡': 'yellow circle',
'🟢': 'green circle',
'🔵': 'blue circle',
'🟣': 'purple circle',
'🟤': 'brown circle',
'⚫': 'black circle',
'⚪': 'white circle',
'🟥': 'red square',
'🟧': 'orange square',
'🟨': 'yellow square',
'🟩': 'green square',
'🟦': 'blue square',
'🟪': 'purple square',
'🟫': 'brown square',
'⬛': 'black large square',
'⬜': 'white large square',
'◼': 'black medium square',
'◻': 'white medium square',
'◾': 'black medium-small square',
'◽': 'white medium-small square',
'▪': 'black small square',
'▫': 'white small square',
'🔶': 'large orange diamond',
'🔷': 'large blue diamond',
'🔸': 'small orange diamond',
'🔹': 'small blue diamond',
'🔺': 'red triangle pointed up',
'🔻': 'red triangle pointed down',
'💠': 'diamond with a dot',
'🔘': 'radio button',
'🔳': 'white square button',
'🔲': 'black square button',
'🏁': 'chequered flag',
'🚩': 'triangular flag',
'🎌': 'crossed flags',
'🏴': 'black flag',
'🏳': 'white flag',
'🏳️‍🌈': 'rainbow flag',
'🏳️‍⚧️': 'transgender flag',
'🏴‍☠️': 'pirate flag',
'🇦🇨': 'flag: Ascension Island',
'🇦🇩': 'flag: Andorra',
'🇦🇪': 'flag: United Arab Emirates',
'🇦🇫': 'flag: Afghanistan',
'🇦🇬': 'flag: Antigua & Barbuda',
'🇦🇮': 'flag: Anguilla',
'🇦🇱': 'flag: Albania',
'🇦🇲': 'flag: Armenia',
'🇦🇴': 'flag: Angola',
'🇦🇶': 'flag: Antarctica',
'🇦🇷': 'flag: Argentina',
'🇦🇸': 'flag: American Samoa',
'🇦🇹': 'flag: Austria',
'🇦🇺': 'flag: Australia',
'🇦🇼': 'flag: Aruba',
'🇦🇽': 'flag: Åland Islands',
'🇦🇿': 'flag: Azerbaijan',
'🇧🇦': 'flag: Bosnia & Herzegovina',
'🇧🇧': 'flag: Barbados',
'🇧🇩': 'flag: Bangladesh',
'🇧🇪': 'flag: Belgium',
'🇧🇫': 'flag: Burkina Faso',
'🇧🇬': 'flag: Bulgaria',
'🇧🇭': 'flag: Bahrain',
'🇧🇮': 'flag: Burundi',
'🇧🇯': 'flag: Benin',
'🇧🇱': 'flag: St. Barthélemy',
'🇧🇲': 'flag: Bermuda',
'🇧🇳': 'flag: Brunei',
'🇧🇴': 'flag: Bolivia',
'🇧🇶': 'flag: Caribbean Netherlands',
'🇧🇷': 'flag: Brazil',
'🇧🇸': 'flag: Bahamas',
'🇧🇹': 'flag: Bhutan',
'🇧🇻': 'flag: Bouvet Island',
'🇧🇼': 'flag: Botswana',
'🇧🇾': 'flag: Belarus',
'🇧🇿': 'flag: Belize',
'🇨🇦': 'flag: Canada',
'🇨🇨': 'flag: Cocos (Keeling) Islands',
'🇨🇩': 'flag: Congo - Kinshasa',
'🇨🇫': 'flag: Central African Republic',
'🇨🇬': 'flag: Congo - Brazzaville',
'🇨🇭': 'flag: Switzerland',
'🇨🇮': 'flag: Côte dIvoire',
'🇨🇰': 'flag: Cook Islands',
'🇨🇱': 'flag: Chile',
'🇨🇲': 'flag: Cameroon',
'🇨🇳': 'flag: China',
'🇨🇴': 'flag: Colombia',
'🇨🇵': 'flag: Clipperton Island',
'🇨🇷': 'flag: Costa Rica',
'🇨🇺': 'flag: Cuba',
'🇨🇻': 'flag: Cape Verde',
'🇨🇼': 'flag: Curaçao',
'🇨🇽': 'flag: Christmas Island',
'🇨🇾': 'flag: Cyprus',
'🇨🇿': 'flag: Czechia',
'🇩🇪': 'flag: Germany',
'🇩🇬': 'flag: Diego Garcia',
'🇩🇯': 'flag: Djibouti',
'🇩🇰': 'flag: Denmark',
'🇩🇲': 'flag: Dominica',
'🇩🇴': 'flag: Dominican Republic',
'🇩🇿': 'flag: Algeria',
'🇪🇦': 'flag: Ceuta & Melilla',
'🇪🇨': 'flag: Ecuador',
'🇪🇪': 'flag: Estonia',
'🇪🇬': 'flag: Egypt',
'🇪🇭': 'flag: Western Sahara',
'🇪🇷': 'flag: Eritrea',
'🇪🇸': 'flag: Spain',
'🇪🇹': 'flag: Ethiopia',
'🇪🇺': 'flag: European Union',
'🇫🇮': 'flag: Finland',
'🇫🇯': 'flag: Fiji',
'🇫🇰': 'flag: Falkland Islands',
'🇫🇲': 'flag: Micronesia',
'🇫🇴': 'flag: Faroe Islands',
'🇫🇷': 'flag: France',
'🇬🇦': 'flag: Gabon',
'🇬🇧': 'flag: United Kingdom',
'🇬🇩': 'flag: Grenada',
'🇬🇪': 'flag: Georgia',
'🇬🇫': 'flag: French Guiana',
'🇬🇬': 'flag: Guernsey',
'🇬🇭': 'flag: Ghana',
'🇬🇮': 'flag: Gibraltar',
'🇬🇱': 'flag: Greenland',
'🇬🇲': 'flag: Gambia',
'🇬🇳': 'flag: Guinea',
'🇬🇵': 'flag: Guadeloupe',
'🇬🇶': 'flag: Equatorial Guinea',
'🇬🇷': 'flag: Greece',
'🇬🇸': 'flag: South Georgia & South Sandwich Islands',
'🇬🇹': 'flag: Guatemala',
'🇬🇺': 'flag: Guam',
'🇬🇼': 'flag: Guinea-Bissau',
'🇬🇾': 'flag: Guyana',
'🇭🇰': 'flag: Hong Kong SAR China',
'🇭🇲': 'flag: Heard & McDonald Islands',
'🇭🇳': 'flag: Honduras',
'🇭🇷': 'flag: Croatia',
'🇭🇹': 'flag: Haiti',
'🇭🇺': 'flag: Hungary',
'🇮🇨': 'flag: Canary Islands',
'🇮🇩': 'flag: Indonesia',
'🇮🇪': 'flag: Ireland',
'🇮🇱': 'flag: Israel',
'🇮🇲': 'flag: Isle of Man',
'🇮🇳': 'flag: India',
'🇮🇴': 'flag: British Indian Ocean Territory',
'🇮🇶': 'flag: Iraq',
'🇮🇷': 'flag: Iran',
'🇮🇸': 'flag: Iceland',
'🇮🇹': 'flag: Italy',
'🇯🇪': 'flag: Jersey',
'🇯🇲': 'flag: Jamaica',
'🇯🇴': 'flag: Jordan',
'🇯🇵': 'flag: Japan',
'🇰🇪': 'flag: Kenya',
'🇰🇬': 'flag: Kyrgyzstan',
'🇰🇭': 'flag: Cambodia',
'🇰🇮': 'flag: Kiribati',
'🇰🇲': 'flag: Comoros',
'🇰🇳': 'flag: St. Kitts & Nevis',
'🇰🇵': 'flag: North Korea',
'🇰🇷': 'flag: South Korea',
'🇰🇼': 'flag: Kuwait',
'🇰🇾': 'flag: Cayman Islands',
'🇰🇿': 'flag: Kazakhstan',
'🇱🇦': 'flag: Laos',
'🇱🇧': 'flag: Lebanon',
'🇱🇨': 'flag: St. Lucia',
'🇱🇮': 'flag: Liechtenstein',
'🇱🇰': 'flag: Sri Lanka',
'🇱🇷': 'flag: Liberia',
'🇱🇸': 'flag: Lesotho',
'🇱🇹': 'flag: Lithuania',
'🇱🇺': 'flag: Luxembourg',
'🇱🇻': 'flag: Latvia',
'🇱🇾': 'flag: Libya',
'🇲🇦': 'flag: Morocco',
'🇲🇨': 'flag: Monaco',
'🇲🇩': 'flag: Moldova',
'🇲🇪': 'flag: Montenegro',
'🇲🇫': 'flag: St. Martin',
'🇲🇬': 'flag: Madagascar',
'🇲🇭': 'flag: Marshall Islands',
'🇲🇰': 'flag: North Macedonia',
'🇲🇱': 'flag: Mali',
'🇲🇲': 'flag: Myanmar (Burma)',
'🇲🇳': 'flag: Mongolia',
'🇲🇴': 'flag: Macao SAR China',
'🇲🇵': 'flag: Northern Mariana Islands',
'🇲🇶': 'flag: Martinique',
'🇲🇷': 'flag: Mauritania',
'🇲🇸': 'flag: Montserrat',
'🇲🇹': 'flag: Malta',
'🇲🇺': 'flag: Mauritius',
'🇲🇻': 'flag: Maldives',
'🇲🇼': 'flag: Malawi',
'🇲🇽': 'flag: Mexico',
'🇲🇾': 'flag: Malaysia',
'🇲🇿': 'flag: Mozambique',
'🇳🇦': 'flag: Namibia',
'🇳🇨': 'flag: New Caledonia',
'🇳🇪': 'flag: Niger',
'🇳🇫': 'flag: Norfolk Island',
'🇳🇬': 'flag: Nigeria',
'🇳🇮': 'flag: Nicaragua',
'🇳🇱': 'flag: Netherlands',
'🇳🇴': 'flag: Norway',
'🇳🇵': 'flag: Nepal',
'🇳🇷': 'flag: Nauru',
'🇳🇺': 'flag: Niue',
'🇳🇿': 'flag: New Zealand',
'🇴🇲': 'flag: Oman',
'🇵🇦': 'flag: Panama',
'🇵🇪': 'flag: Peru',
'🇵🇫': 'flag: French Polynesia',
'🇵🇬': 'flag: Papua New Guinea',
'🇵🇭': 'flag: Philippines',
'🇵🇰': 'flag: Pakistan',
'🇵🇱': 'flag: Poland',
'🇵🇲': 'flag: St. Pierre & Miquelon',
'🇵🇳': 'flag: Pitcairn Islands',
'🇵🇷': 'flag: Puerto Rico',
'🇵🇸': 'flag: Palestinian Territories',
'🇵🇹': 'flag: Portugal',
'🇵🇼': 'flag: Palau',
'🇵🇾': 'flag: Paraguay',
'🇶🇦': 'flag: Qatar',
'🇷🇪': 'flag: Réunion',
'🇷🇴': 'flag: Romania',
'🇷🇸': 'flag: Serbia',
'🇷🇺': 'flag: Russia',
'🇷🇼': 'flag: Rwanda',
'🇸🇦': 'flag: Saudi Arabia',
'🇸🇧': 'flag: Solomon Islands',
'🇸🇨': 'flag: Seychelles',
'🇸🇩': 'flag: Sudan',
'🇸🇪': 'flag: Sweden',
'🇸🇬': 'flag: Singapore',
'🇸🇭': 'flag: St. Helena',
'🇸🇮': 'flag: Slovenia',
'🇸🇯': 'flag: Svalbard & Jan Mayen',
'🇸🇰': 'flag: Slovakia',
'🇸🇱': 'flag: Sierra Leone',
'🇸🇲': 'flag: San Marino',
'🇸🇳': 'flag: Senegal',
'🇸🇴': 'flag: Somalia',
'🇸🇷': 'flag: Suriname',
'🇸🇸': 'flag: South Sudan',
'🇸🇹': 'flag: São Tomé & Príncipe',
'🇸🇻': 'flag: El Salvador',
'🇸🇽': 'flag: Sint Maarten',
'🇸🇾': 'flag: Syria',
'🇸🇿': 'flag: Eswatini',
'🇹🇦': 'flag: Tristan da Cunha',
'🇹🇨': 'flag: Turks & Caicos Islands',
'🇹🇩': 'flag: Chad',
'🇹🇫': 'flag: French Southern Territories',
'🇹🇬': 'flag: Togo',
'🇹🇭': 'flag: Thailand',
'🇹🇯': 'flag: Tajikistan',
'🇹🇰': 'flag: Tokelau',
'🇹🇱': 'flag: Timor-Leste',
'🇹🇲': 'flag: Turkmenistan',
'🇹🇳': 'flag: Tunisia',
'🇹🇴': 'flag: Tonga',
'🇹🇷': 'flag: Turkey',
'🇹🇹': 'flag: Trinidad & Tobago',
'🇹🇻': 'flag: Tuvalu',
'🇹🇼': 'flag: Taiwan',
'🇹🇿': 'flag: Tanzania',
'🇺🇦': 'flag: Ukraine',
'🇺🇬': 'flag: Uganda',
'🇺🇲': 'flag: U.S. Outlying Islands',
'🇺🇳': 'flag: United Nations',
'🇺🇸': 'flag: United States',
'🇺🇾': 'flag: Uruguay',
'🇺🇿': 'flag: Uzbekistan',
'🇻🇦': 'flag: Vatican City',
'🇻🇨': 'flag: St. Vincent & Grenadines',
'🇻🇪': 'flag: Venezuela',
'🇻🇬': 'flag: British Virgin Islands',
'🇻🇮': 'flag: U.S. Virgin Islands',
'🇻🇳': 'flag: Vietnam',
'🇻🇺': 'flag: Vanuatu',
'🇼🇫': 'flag: Wallis & Futuna',
'🇼🇸': 'flag: Samoa',
'🇽🇰': 'flag: Kosovo',
'🇾🇪': 'flag: Yemen',
'🇾🇹': 'flag: Mayotte',
'🇿🇦': 'flag: South Africa',
'🇿🇲': 'flag: Zambia',
'🇿🇼': 'flag: Zimbabwe',
'🏴󠁧󠁢󠁥󠁮󠁧󠁿': 'flag: England',
'🏴󠁧󠁢󠁳󠁣󠁴󠁿': 'flag: Scotland',
'🏴󠁧󠁢󠁷󠁬󠁳󠁿': 'flag: Wales',
};
/**
* This function returns all enabled icons.
*
* For example: if `Remixicons Fill` and `Fontawesome Fill` is activated, it will return all these icons.
*
* @public
* @param {IconFolderPlugin} plugin - The main plugin file.
* @returns {string[]} The enabled icons.
*/
const getEnabledIcons = (plugin) => {
plugin.getSettings();
/*const icons = transformedIcons.remixIcons.filter((key) => {
return mapRemixicons(key, settings);
});
if (settings.enableFontawesomeFill) {
icons.push(...transformedIcons.faFill);
}
if (settings.enableFontawesomeLine) {
icons.push(...transformedIcons.faLine);
}
if (settings.enableFontawesomeBrands) {
icons.push(...transformedIcons.faBrands);
}
if (settings.enableDevicons) {
icons.push(...transformedIcons.deviconIcons);
}*/
return getAllLoadedIconNames();
};
/**
* This function returns the svg string with the user defined css settings.
* It handles from the settings the `margin`, `color`, and `size`.
*
* In addition, this function manipulates the passed element with the user defined setting `margin`.
*
* @public
* @param {IconFolderPlugin} plugin - The main plugin.
* @param {string} icon - The to be styled icon.
* @param {HTMLElement} el - The element that will include the margin from the user settings.
* @returns {string} The svg with the customized css settings.
*/
const customizeIconStyle = (plugin, icon, el) => {
// Allow custom font size
const widthRe = new RegExp(/width="\d+(px)?"/);
const heightRe = new RegExp(/height="\d+(px)?"/);
if (icon.match(widthRe)) {
icon = icon.replace(widthRe, `width="${plugin.getSettings().fontSize}px"`);
}
if (icon.match(heightRe)) {
icon = icon.replace(heightRe, `height="${plugin.getSettings().fontSize}px"`);
}
// Allow custom icon color.
icon = colorizeIcon(icon, plugin.getSettings().iconColor);
// Change margin of icon
const margin = plugin.getSettings().extraMargin;
const normalizedMargin = {
top: margin.top !== undefined ? margin.top : 4,
right: margin.right !== undefined ? margin.right : 4,
left: margin.left !== undefined ? margin.left : 4,
bottom: margin.bottom !== undefined ? margin.bottom : 4,
};
if (plugin.getSettings().extraMargin) {
el.style.margin = `${normalizedMargin.top}px ${normalizedMargin.right}px ${normalizedMargin.bottom}px ${normalizedMargin.left}px`;
}
return icon;
};
const colorizeIcon = (icon, c) => {
const parser = new DOMParser();
const parsedString = parser.parseFromString(icon, 'text/html');
const iconElement = parsedString.querySelector('svg');
if (iconElement) {
if (iconElement.hasAttribute('fill') && iconElement.getAttribute('fill') !== 'none') {
iconElement.setAttribute('fill', c !== null && c !== void 0 ? c : 'currentColor');
}
else if (iconElement.hasAttribute('stroke') && iconElement.getAttribute('stroke') !== 'none') {
iconElement.setAttribute('stroke', c !== null && c !== void 0 ? c : 'currentColor');
}
return iconElement.outerHTML;
}
return icon;
};
/**
* This function adds the icons to the DOM.
* For that, it will create a `div` element with the class `obsidian-icon-folder-icon` that will be customized based on the user settings.
*
* @public
* @param {IconFolderPlugin} plugin - The main plugin.
* @param {[string, string | FolderIconObject][]} data - The data that includes the icons.
* @param {WeakMap<ExplorerLeaf, boolean>} registeredFileExplorers - The already registered file explorers.
*/
const addIconsToDOM = (plugin, data, registeredFileExplorers, callback) => {
const fileExplorers = plugin.app.workspace.getLeavesOfType('file-explorer');
fileExplorers.forEach((fileExplorer) => {
if (registeredFileExplorers.has(fileExplorer.view)) {
return;
}
registeredFileExplorers.add(fileExplorer.view);
// create a map with registered file paths to have constant look up time
const registeredFilePaths = {};
data.forEach(([path]) => {
registeredFilePaths[path] = true;
});
data.forEach(([dataPath, value]) => {
const fileItem = fileExplorer.view.fileItems[dataPath];
if (fileItem) {
const titleEl = fileItem.titleEl;
const titleInnerEl = fileItem.titleInnerEl;
// needs to check because of the refreshing the plugin will duplicate all the icons
if (titleEl.children.length === 2 || titleEl.children.length === 1) {
const iconName = typeof value === 'string' ? value : value.iconName;
if (iconName) {
const existingIcon = titleEl.querySelector('.obsidian-icon-folder-icon');
if (existingIcon) {
existingIcon.remove();
}
const iconNode = titleEl.createDiv();
iconNode.classList.add('obsidian-icon-folder-icon');
insertIconToNode(plugin, iconName, iconNode);
titleEl.insertBefore(iconNode, titleInnerEl);
}
if (typeof value === 'object' && value.inheritanceIcon) {
const files = plugin.app.vault.getFiles().filter((f) => f.path.includes(dataPath));
const inheritanceIconName = value.inheritanceIcon;
files.forEach((f) => {
if (!registeredFilePaths[f.path]) {
const inheritanceFileItem = fileExplorer.view.fileItems[f.path];
const existingIcon = inheritanceFileItem.titleEl.querySelector('.obsidian-icon-folder-icon');
if (existingIcon) {
existingIcon.remove();
}
const iconNode = inheritanceFileItem.titleEl.createDiv();
iconNode.classList.add('obsidian-icon-folder-icon');
insertIconToNode(plugin, inheritanceIconName, iconNode);
inheritanceFileItem.titleEl.insertBefore(iconNode, inheritanceFileItem.titleInnerEl);
}
});
}
}
}
});
const addCustomIconRule = (rule, file) => {
const fileItem = fileExplorer.view.fileItems[file.path];
if (fileItem) {
const titleEl = fileItem.titleEl;
const titleInnerEl = fileItem.titleInnerEl;
const existingIcon = titleEl.querySelector('.obsidian-icon-folder-icon');
if (!existingIcon) {
const iconNode = titleEl.createDiv();
iconNode.classList.add('obsidian-icon-folder-icon');
insertIconToNode(plugin, rule.icon, iconNode, rule.color);
titleEl.insertBefore(iconNode, titleInnerEl);
}
}
};
// Add custom rule icons.
plugin.getSettings().rules.forEach((rule) => {
try {
// Rule is in some sort of regex.
const regex = new RegExp(rule.rule);
plugin.app.vault.getAllLoadedFiles().forEach((file) => __awaiter(void 0, void 0, void 0, function* () {
const fileType = (yield plugin.app.vault.adapter.stat(file.path)).type;
if (file.name.match(regex) && isToRuleApplicable(rule, fileType)) {
addCustomIconRule(rule, file);
}
}));
}
catch (_a) {
// Rule is not applicable to a regex format.
plugin.app.vault.getAllLoadedFiles().forEach((file) => __awaiter(void 0, void 0, void 0, function* () {
const fileType = (yield plugin.app.vault.adapter.stat(file.path)).type;
if (file.name.includes(rule.rule) && isToRuleApplicable(rule, fileType)) {
addCustomIconRule(rule, file);
}
}));
}
});
if (callback) {
callback();
}
});
};
const addInheritanceIconToFile = (plugin, registeredFileExplorers, filePath, iconName) => {
const fileExplorers = plugin.app.workspace.getLeavesOfType('file-explorer');
fileExplorers.forEach((fileExplorer) => {
if (registeredFileExplorers.has(fileExplorer.view)) {
const fileItem = fileExplorer.view.fileItems[filePath];
if (fileItem) {
const iconNode = fileItem.titleEl.createDiv();
iconNode.classList.add('obsidian-icon-folder-icon');
insertIconToNode(plugin, iconName, iconNode);
fileItem.titleEl.insertBefore(iconNode, fileItem.titleInnerEl);
}
}
});
};
/**
* This function refreshes the icon style.
* For that, it will manipulate the `innerHTML` of the icon and will customize the style.
*
* @public
* @param {IconFolderPlugin} plugin - The main plugin.
*/
const refreshIconStyle = (plugin) => {
const data = Object.entries(plugin.getData());
const fileExplorers = plugin.app.workspace.getLeavesOfType('file-explorer');
fileExplorers.forEach((fileExplorer) => {
data.forEach(([key]) => {
const fileItem = fileExplorer.view.fileItems[key];
if (fileItem) {
const titleEl = fileItem.titleEl;
const iconNode = titleEl.querySelector('.obsidian-icon-folder-icon');
iconNode.innerHTML = customizeIconStyle(plugin, iconNode.innerHTML, iconNode);
}
});
});
};
/**
* This function removes the icon node from the DOM based on the passed in path.
*
* @public
* @param {string} path - The path toe the to be removed DOM element.
*/
const removeFromDOM = (path, el) => {
const node = el !== null && el !== void 0 ? el : document.querySelector(`[data-path="${path}"]`);
if (!node) {
console.error('element with data path not found', path);
return;
}
const iconNode = node.querySelector('.obsidian-icon-folder-icon');
if (!iconNode) {
return;
}
iconNode.remove();
};
const updateIcon = (plugin, file) => {
// Try to add custom rule icons back.
plugin.getSettings().rules.forEach((rule) => __awaiter(void 0, void 0, void 0, function* () {
addCustomRuleIconsToDOM(plugin, rule, file);
}));
};
/**
* This function checks if a custom rule icon exists in the path.
*
* @param {CustomRule} rule - The custom rule that will be checked on.
* @param {string} path - The path that will be checked on.
* @returns {boolean} If the icon with the path and rule exists and should be removed.
*/
const doesCustomRuleIconExists = (rule, path) => {
const name = path.split('/').pop();
try {
// Rule is in some sort of regex.
const regex = new RegExp(rule.rule);
if (name.match(regex)) {
return true;
}
}
catch (_a) {
// Rule is not applicable to a regex format.
if (name.includes(rule.rule)) {
return true;
}
}
return false;
};
/**
* This function removes the specified rule from all the loaded files in the vault.
*
* @param {IconFolderPlugin} plugin - The main plugin.
* @param {CustomRule} rule - Specific rule that will match all loaded files.
*/
const removeCustomRuleIconsFromDOM = (plugin, rule) => {
const inheritanceFolders = Object.entries(plugin.getData()).filter(([k, v]) => k !== 'settings' && typeof v === 'object');
plugin.getRegisteredFileExplorers().forEach((explorerView) => __awaiter(void 0, void 0, void 0, function* () {
const files = Object.entries(explorerView.fileItems);
files.forEach(([path, fileItem]) => __awaiter(void 0, void 0, void 0, function* () {
const fileType = (yield plugin.app.vault.adapter.stat(path)).type;
const dataFile = typeof plugin.getData()[path] === 'object'
? plugin.getData()[path].iconName
: plugin.getData()[path];
const isInfluencedByInheritance = inheritanceFolders.find(([key]) => path.includes(key) && fileType === 'file');
const existingIcon = dataFile || isInfluencedByInheritance;
if (!existingIcon && doesCustomRuleIconExists(rule, path) && isToRuleApplicable(rule, fileType)) {
removeFromDOM(path, fileItem.titleEl);
}
}));
}));
};
const colorizeCustomRuleIcons = (plugin, rule) => {
try {
// Rule is in some sort of regex.
const regex = new RegExp(rule.rule);
plugin.app.vault.getAllLoadedFiles().forEach((file) => __awaiter(void 0, void 0, void 0, function* () {
const fileType = (yield plugin.app.vault.adapter.stat(file.path)).type;
if (file.name.match(regex) && isToRuleApplicable(rule, fileType)) {
addToDOM(plugin, file.path, rule.icon, rule.color);
}
}));
}
catch (_a) {
// Rule is not applicable to a regex format.
plugin.app.vault.getAllLoadedFiles().forEach((file) => __awaiter(void 0, void 0, void 0, function* () {
const fileType = (yield plugin.app.vault.adapter.stat(file.path)).type;
if (file.name.includes(rule.rule) && isToRuleApplicable(rule, fileType)) {
addToDOM(plugin, file.path, rule.icon, rule.color);
}
}));
}
};
const isToRuleApplicable = (rule, fileType) => {
return (rule.for === 'everything' ||
(rule.for === 'files' && fileType === 'file') ||
(rule.for === 'folders' && fileType === 'folder'));
};
/**
* This function adds to all the loaded files the icon based on the specific rule.
*
* @param {IconFolderPlugin} plugin - The main plugin.
* @param {CustomRule} rule - The custom rule for adding the icon.
* @param {TAbstractFile} file - Optional parameter if the rule should only be applied to one specific file.
*/
const addCustomRuleIconsToDOM = (plugin, rule, file) => __awaiter(void 0, void 0, void 0, function* () {
try {
// Rule is in some sort of regex.
const regex = new RegExp(rule.rule);
if (file) {
const fileType = (yield plugin.app.vault.adapter.stat(file.path)).type;
if (file.name.match(regex) && isToRuleApplicable(rule, fileType)) {
addToDOM(plugin, file.path, rule.icon, rule.color);
}
}
else {
plugin.getRegisteredFileExplorers().forEach((explorerView) => __awaiter(void 0, void 0, void 0, function* () {
const files = Object.entries(explorerView.fileItems);
files.forEach(([path, fileItem]) => __awaiter(void 0, void 0, void 0, function* () {
const fileType = (yield plugin.app.vault.adapter.stat(path)).type;
if (fileItem) {
const fileName = path.split('/').pop();
if (fileName.match(regex) && isToRuleApplicable(rule, fileType)) {
const titleEl = fileItem.titleEl;
const titleInnerEl = fileItem.titleInnerEl;
const existingIcon = titleEl.querySelector('.obsidian-icon-folder-icon');
if (!existingIcon) {
const iconNode = titleEl.createDiv();
iconNode.classList.add('obsidian-icon-folder-icon');
insertIconToNode(plugin, rule.icon, iconNode);
titleEl.insertBefore(iconNode, titleInnerEl);
}
}
}
}));
}));
}
}
catch (_a) {
// Rule is not applicable to a regex format.
if (file) {
const fileType = (yield plugin.app.vault.adapter.stat(file.path)).type;
if (file.name.includes(rule.rule) && isToRuleApplicable(rule, fileType)) {
addToDOM(plugin, file.path, rule.icon, rule.color);
}
}
else {
plugin.app.vault.getAllLoadedFiles().forEach((file) => __awaiter(void 0, void 0, void 0, function* () {
const fileType = (yield plugin.app.vault.adapter.stat(file.path)).type;
if (file.name.includes(rule.rule) && isToRuleApplicable(rule, fileType)) {
addToDOM(plugin, file.path, rule.icon, rule.color);
}
}));
}
}
});
/**
* This function adds an icon to the DOM based on a specific path.
* In addition, before added to the DOM, it will customize the icon style.
*
* @public
* @param {IconFolderPlugin} plugin - The main plugin.
* @param {string} path - The path in the DOM where the icon will be added.
* @param {string} icon - The icon that will be added to the DOM - can be an icon id or codepoint for twemoji.
*/
const addToDOM = (plugin, path, icon, color) => {
if (plugin.getData()[path]) {
removeFromDOM(path);
}
const node = document.querySelector(`[data-path="${path}"]`);
if (!node) {
console.error('element with data path not found', path);
return;
}
let titleNode = node.querySelector('.nav-folder-title-content');
if (!titleNode) {
titleNode = node.querySelector('.nav-file-title-content');
if (!titleNode) {
console.error('element with title not found');
return;
}
}
// check if there is a possible inheritance icon in the DOM
const possibleInheritanceIcon = node.querySelector('.obsidian-icon-folder-icon');
if (possibleInheritanceIcon) {
possibleInheritanceIcon.remove();
}
const iconNode = document.createElement('div');
iconNode.classList.add('obsidian-icon-folder-icon');
insertIconToNode(plugin, icon, iconNode, color);
node.insertBefore(iconNode, titleNode);
};
/**
* This function inserts a specific icon into the specified node.
*
* @param {IconFolderPlugin} plugin - The main plugin.
* @param {string} icon - The icon string (can be an icon id or a unicode for twemoji).
* @param {HTMLElement} node - The element where the icon will be inserted.
* @param color
*/
const insertIconToNode = (plugin, icon, node, color) => {
const iconNextIdentifier = nextIdentifier(icon);
const possibleIcon = getSvgFromLoadedIcon(icon.substring(0, iconNextIdentifier), icon.substring(iconNextIdentifier));
if (possibleIcon) {
let iconContent = customizeIconStyle(plugin, possibleIcon, node);
if (color) {
iconContent = colorizeIcon(possibleIcon, color);
}
node.innerHTML = iconContent;
}
else {
const emoji = twemoji.parse(icon, {
folder: 'svg',
ext: '.svg',
attributes: () => ({
width: '16px',
height: '16px',
}),
});
node.innerHTML = customizeIconStyle(plugin, emoji, node);
}
};
/**
* This function will add inheritance functionality to a specific folder.
* It will add the inheritance icon to all child files.
*
* @param {IconFolderPlugin} plugin - The main plugin.
* @param {string} folderPath - The path in the DOM where the icon will be added.
*/
const addInheritanceForFolder = (plugin, folderPath) => {
const folder = plugin.getData()[folderPath];
if (!folder || typeof folder !== 'object') {
return;
}
// add icons for all the child files
const files = plugin.app.vault.getFiles().filter((f) => f.path.includes(folderPath));
files.forEach((f) => {
if (plugin.getData()[f.path]) {
removeFromDOM(f.path);
plugin.removeFolderIcon(f.path);
}
addToDOM(plugin, f.path, folder.inheritanceIcon);
});
};
/**
* This function removes inheritance from a folder.
* It will delete all the icons in the sub files of this folder.
*
* @param {IconFolderPlugin} plugin - The main plugin.
* @param {string} folderPath - The path in the DOM where the icon will be added.
*/
const removeInheritanceForFolder = (plugin, folderPath) => {
const folder = plugin.getData()[folderPath];
if (!folder || typeof folder !== 'object') {
return;
}
// remove icons from all the child files
const files = plugin.app.vault.getFiles().filter((f) => f.path.includes(folderPath));
files.forEach((f) => {
// when the file path is not registered in the data it should remove the icon
if (!plugin.getData()[f.path]) {
removeFromDOM(f.path);
updateIcon(plugin, f);
}
});
};
const isEmoji = (str) => {
const ranges = [
'(?:[\u2700-\u27bf]|(?:\ud83c[\udde6-\uddff]){2}|[\ud800-\udbff][\udc00-\udfff]|[\u0023-\u0039]\ufe0f?\u20e3|\u3299|\u3297|\u303d|\u3030|\u24c2|\ud83c[\udd70-\udd71]|\ud83c[\udd7e-\udd7f]|\ud83c\udd8e|\ud83c[\udd91-\udd9a]|\ud83c[\udde6-\uddff]|[\ud83c[\ude01-\ude02]|\ud83c\ude1a|\ud83c\ude2f|[\ud83c[\ude32-\ude3a]|[\ud83c[\ude50-\ude51]|\u203c|\u2049|[\u25aa-\u25ab]|\u25b6|\u25c0|[\u25fb-\u25fe]|\u00a9|\u00ae|\u2122|\u2139|\ud83c\udc04|[\u2600-\u26FF]|\u2b05|\u2b06|\u2b07|\u2b1b|\u2b1c|\u2b50|\u2b55|\u231a|\u231b|\u2328|\u23cf|[\u23e9-\u23f3]|[\u23f8-\u23fa]|\ud83c\udccf|\u2934|\u2935|[\u2190-\u21ff])', // U+1F680 to U+1F6FF
];
if (str.match(ranges.join('|'))) {
return true;
}
else {
return false;
}
};
const getIconsInData = (plugin) => {
const result = [];
Object.entries(plugin.getData()).forEach(([key, value]) => {
if (key === 'settings') {
const rules = value.rules;
rules.forEach((rule) => {
if (!isEmoji(rule.icon)) {
result.push(rule.icon);
}
});
}
else if (key !== 'settings' && key !== 'migrated') {
if (typeof value === 'string' && !isEmoji(value)) {
result.push(value);
}
else if (typeof value === 'object') {
const v = value;
if (v.iconName !== null && !isEmoji(v.iconName)) {
result.push(v.iconName);
}
if (v.inheritanceIcon !== null && !isEmoji(v.inheritanceIcon)) {
result.push(v.inheritanceIcon);
}
}
}
});
return result;
};
const readFileSync = (file) => __awaiter(void 0, void 0, void 0, function* () {
const content = yield new Promise((resolve) => {
const reader = new FileReader();
reader.readAsText(file, 'UTF-8');
reader.onload = (readerEvent) => resolve(readerEvent.target.result);
});
return content;
});
const getIconByPath = (plugin, filePath) => {
var _a;
if (filePath !== 'settings' && filePath !== 'migrated') {
const value = plugin.getData()[filePath];
if (typeof value === 'string' && !isEmoji(value)) {
return value;
}
else if (typeof value === 'object') {
const v = value;
if (v.iconName !== null && !isEmoji(v.iconName)) {
return v.iconName;
}
if (v.inheritanceIcon !== null && !isEmoji(v.inheritanceIcon)) {
return v.inheritanceIcon;
}
}
}
const rules = (_a = plugin.getData()['settings']) === null || _a === void 0 ? void 0 : _a.rules;
const rule = rules.find((rule) => !isEmoji(rule.icon) && doesCustomRuleIconExists(rule, filePath));
if (rule) {
return rule.icon;
}
return undefined;
};
const getIconsWithPathInData = (plugin) => {
const result = [];
Object.entries(plugin.getData()).forEach(([key, value]) => {
if (key !== 'settings' && key !== 'migrated') {
if (typeof value === 'string') {
if (!isEmoji(value)) {
result.push({ key, value });
return;
}
}
if (typeof value === 'object') {
if (value.iconName !== null && !isEmoji(value.iconName)) {
result.push({ key, value: value.iconName });
return;
}
if (value.inheritanceIcon !== null && !isEmoji(value.inheritanceIcon)) {
result.push({ key, value: value.inheritanceIcon });
return;
}
}
}
});
return result;
};
class IconsPickerModal extends obsidian.FuzzySuggestModal {
constructor(app, plugin, path) {
super(app);
this.renderIndex = 0;
this.plugin = plugin;
this.path = path;
this.limit = 150;
const pluginRecentltyUsedItems = [...plugin.getSettings().recentlyUsedIcons];
this.recentlyUsedItems = pluginRecentltyUsedItems.reverse().filter((iconName) => {
return doesIconExists(iconName) || isEmoji(iconName);
});
this.resultContainerEl.classList.add('obsidian-icon-folder-modal');
}
onOpen() {
super.onOpen();
}
onClose() {
const { contentEl } = this;
contentEl.empty();
}
getItemText(item) {
return `${item.name} (${item.prefix})`;
}
getItems() {
const iconKeys = [];
if (this.inputEl.value.length === 0) {
this.renderIndex = 0;
this.recentlyUsedItems.forEach((iconName) => {
// Transform unicodes to twemojis.
if (isEmoji(iconName)) {
iconKeys.push({
name: emojiShortName[iconName],
prefix: 'Twemoji',
displayName: iconName,
});
return;
}
const nextLetter = nextIdentifier(iconName);
iconKeys.push({
name: iconName.substring(nextLetter),
prefix: iconName.substring(0, nextLetter),
displayName: iconName,
});
});
}
for (const icon of getEnabledIcons(this.plugin)) {
iconKeys.push({
name: icon.name,
prefix: icon.prefix,
displayName: icon.prefix + icon.name,
});
}
Object.entries(emojiShortName).forEach(([unicode, shortName]) => {
iconKeys.push({
name: shortName,
prefix: 'Twemoji',
displayName: unicode,
});
iconKeys.push({
name: unicode,
prefix: 'Twemoji',
displayName: unicode,
});
});
return iconKeys;
}
onChooseItem(item) {
if (typeof item === 'object') {
addToDOM(this.plugin, this.path, item.displayName);
}
else {
addToDOM(this.plugin, this.path, item);
}
this.plugin.addFolderIcon(this.path, item);
}
renderSuggestion(item, el) {
super.renderSuggestion(item, el);
// if (getAllIconPacks().length === 0) {
// this.resultContainerEl.style.display = 'block';
// this.resultContainerEl.innerHTML = '<div class="suggestion-empty">You need to create an icon pack.</div>';
// return;
// }
// Render subheadlines for modal.
if (this.recentlyUsedItems.length !== 0 && this.inputEl.value.length === 0) {
if (this.renderIndex === 0) {
const subheadline = this.resultContainerEl.createDiv();
subheadline.classList.add('obsidian-icon-folder-subheadline');
subheadline.innerText = 'Recently used Icons:';
this.resultContainerEl.prepend(subheadline);
}
else if (this.renderIndex === this.recentlyUsedItems.length - 1) {
const subheadline = this.resultContainerEl.createDiv();
subheadline.classList.add('obsidian-icon-folder-subheadline');
subheadline.innerText = 'All Icons:';
this.resultContainerEl.append(subheadline);
}
}
if (item.item.name !== 'default') {
if (item.item.prefix === 'Twemoji') {
el.innerHTML = `<div>${el.innerHTML}</div><div class="obsidian-icon-folder-icon-preview">${twemoji.parse(item.item.displayName)}</div>`;
}
else {
el.innerHTML = `<div>${el.innerHTML}</div><div class="obsidian-icon-folder-icon-preview">${getSvgFromLoadedIcon(item.item.prefix, item.item.name)}</div>`;
}
}
this.renderIndex++;
}
}
const DEFAULT_SETTINGS = {
migrated: false,
iconPacksPath: '.obsidian/plugins/obsidian-icon-folder/icons',
fontSize: 16,
iconColor: null,
recentlyUsedIcons: [],
recentlyUsedIconsSize: 5,
rules: [],
extraMargin: {
top: 0,
right: 4,
bottom: 0,
left: 0,
},
};
const migrationMap = [
{
oldIconPackPrefix: 'Fa',
identifier: 'Brands',
transformation: 'Fab',
},
{
oldIconPackPrefix: 'Fa',
identifier: 'Line',
transformation: 'Far',
},
{
oldIconPackPrefix: 'Fa',
identifier: 'Fill',
transformation: 'Fas',
},
];
const migrateIcons = (plugin) => {
const data = Object.assign({}, plugin.getData());
const entries = getIconsWithPathInData(plugin);
entries.forEach((entry) => {
if (entry) {
const { key, value } = entry;
const migration = migrationMap.find((migration) => value.substring(0, 2) === migration.oldIconPackPrefix && value.includes(migration.identifier));
if (migration) {
data[key] =
migration.transformation +
value.substring(migration.oldIconPackPrefix.length, value.indexOf(migration.identifier));
}
}
});
return data;
};
class IconFolderSetting {
constructor(plugin, containerEl) {
this.plugin = plugin;
this.containerEl = containerEl;
}
}
class CustomIconPackSetting extends IconFolderSetting {
constructor(plugin, containerEl, refreshDisplay) {
super(plugin, containerEl);
this.refreshDisplay = refreshDisplay;
this.dragOverElement = document.createElement('div');
this.dragOverElement.addClass('obsidian-icon-folder-dragover-el');
this.dragOverElement.style.display = 'hidden';
this.dragOverElement.innerHTML = '<p>Drop to add icon.</p>';
}
normalizeIconPackName(value) {
return value.toLowerCase().replace(/\s/g, '-');
}
preventDefaults(event) {
event.preventDefault();
event.stopPropagation();
}
highlight(el) {
clearTimeout(this.closeTimer);
if (!this.dragTargetElement) {
el.appendChild(this.dragOverElement);
el.classList.add('obsidian-icon-folder-dragover');
this.dragTargetElement = el;
}
}
unhighlight(target, el) {
if (this.dragTargetElement && this.dragTargetElement !== target) {
this.dragTargetElement.removeChild(this.dragOverElement);
this.dragTargetElement.classList.remove('obsidian-icon-folder-dragover');
this.dragTargetElement = undefined;
}
clearTimeout(this.closeTimer);
this.closeTimer = setTimeout(() => {
if (this.dragTargetElement) {
el.removeChild(this.dragOverElement);
el.classList.remove('obsidian-icon-folder-dragover');
this.dragTargetElement = undefined;
}
}, 100);
}
display() {
new obsidian.Setting(this.containerEl)
.setName('Add custom icon pack')
.setDesc('Add a custom icon pack')
.addText((text) => {
text.setPlaceholder('Your icon pack name');
this.textComponent = text;
})
.addButton((btn) => {
btn.setButtonText('Add icon pack');
btn.buttonEl.style.marginLeft = '12px';
btn.onClick(() => __awaiter(this, void 0, void 0, function* () {
const name = this.textComponent.getValue();
if (name.length === 0) {
return;
}
const normalizedName = this.normalizeIconPackName(this.textComponent.getValue());
if (yield doesIconPackExist(this.plugin, normalizedName)) {
new obsidian.Notice('Icon pack already exists.');
return;
}
yield createIconPackDirectory(this.plugin, normalizedName);
this.textComponent.setValue('');
this.refreshDisplay();
new obsidian.Notice('Icon pack successfully created.');
}));
});
getAllIconPacks().forEach((iconPack) => {
const iconPackSetting = new obsidian.Setting(this.containerEl)
.setName(iconPack.name)
.setDesc(`Total icons: ${iconPack.icons.length}`);
iconPackSetting.addButton((btn) => {
btn.setIcon('broken-link');
btn.setTooltip('Try to fix icon pack');
btn.onClick(() => __awaiter(this, void 0, void 0, function* () {
new obsidian.Notice('Try to fix icon pack...');
getIconPack(iconPack.name).icons = [];
const icons = yield getFilesInDirectory(this.plugin, `${getPath()}/${iconPack.name}`);
for (let i = 0; i < icons.length; i++) {
const filePath = icons[i];
const fileName = filePath.split('/').pop();
const file = yield this.plugin.app.vault.adapter.read(filePath);
const iconContent = file
.replace(/stroke="#fff"/g, 'stroke="currentColor"')
.replace(/fill="#fff"/g, 'fill="currentColor"');
yield this.plugin.app.vault.adapter.write(filePath, iconContent);
yield normalizeFileName(this.plugin, filePath);
addIconToIconPack(iconPack.name, fileName, iconContent);
}
new obsidian.Notice('...tried to fix icon pack');
// Refreshes the DOM.
Object.entries(this.plugin.getData()).forEach(([k, v]) => __awaiter(this, void 0, void 0, function* () {
const doesPathExist = yield this.plugin.app.vault.adapter.exists(k, true);
if (doesPathExist && typeof v === 'string') {
removeFromDOM(k);
addToDOM(this.plugin, k, v);
}
}));
}));
});
iconPackSetting.addButton((btn) => {
btn.setIcon('create-new');
btn.setTooltip('Add an icon');
btn.onClick(() => __awaiter(this, void 0, void 0, function* () {
const fileSelector = document.createElement('input');
fileSelector.setAttribute('type', 'file');
fileSelector.setAttribute('multiple', 'multiple');
fileSelector.setAttribute('accept', '.svg');
fileSelector.click();
fileSelector.onchange = (e) => __awaiter(this, void 0, void 0, function* () {
const target = e.target;
for (let i = 0; i < target.files.length; i++) {
const file = target.files[i];
const content = yield readFileSync(file);
yield createFile(this.plugin, iconPack.name, file.name, content);
addIconToIconPack(iconPack.name, file.name, content);
iconPackSetting.setDesc(`Total icons: ${iconPack.icons.length} (added: ${file.name})`);
}
new obsidian.Notice('Icons successfully added.');
});
}));
});
iconPackSetting.addButton((btn) => {
btn.setIcon('trash');
btn.setTooltip('Remove the icon pack');
btn.onClick(() => __awaiter(this, void 0, void 0, function* () {
yield deleteIconPack(this.plugin, iconPack.name);
this.refreshDisplay();
new obsidian.Notice('Icon pack successfully deleted.');
}));
});
['dragenter', 'dragover', 'dragleave', 'drop'].forEach((event) => {
iconPackSetting.settingEl.addEventListener(event, this.preventDefaults, false);
});
['dragenter', 'dragover'].forEach((event) => {
iconPackSetting.settingEl.addEventListener(event, () => this.highlight(iconPackSetting.settingEl), false);
});
['dragleave', 'drop'].forEach((event) => {
iconPackSetting.settingEl.addEventListener(event, (event) => this.unhighlight(event.currentTarget, iconPackSetting.settingEl), false);
});
iconPackSetting.settingEl.addEventListener('drop', (event) => __awaiter(this, void 0, void 0, function* () {
const files = event.dataTransfer.files;
let successful = false;
for (let i = 0; i < files.length; i++) {
const file = files[i];
if (file.type !== 'image/svg+xml') {
new obsidian.Notice(`File ${file.name} is not a XML file.`);
continue;
}
successful = true;
const content = yield readFileSync(file);
yield createFile(this.plugin, iconPack.name, file.name, content);
addIconToIconPack(iconPack.name, file.name, content);
iconPackSetting.setDesc(`Total icons: ${iconPack.icons.length} (added: ${file.name})`);
}
if (successful) {
new obsidian.Notice('Icons successfully added.');
}
}), false);
});
}
}
class CustomIconRuleSetting extends IconFolderSetting {
constructor(plugin, containerEl, app, refreshDisplay) {
super(plugin, containerEl);
this.app = app;
this.refreshDisplay = refreshDisplay;
}
display() {
new obsidian.Setting(this.containerEl)
.setName('Add icon rule')
.setDesc('Will add the icon based on the specific string.')
.addText((text) => {
text.onChange((value) => {
this.chooseIconBtn.setDisabled(value.length === 0);
this.chooseIconBtn.buttonEl.style.cursor = value.length === 0 ? 'not-allowed' : 'default';
this.chooseIconBtn.buttonEl.style.opacity = value.length === 0 ? '50%' : '100%';
});
text.setPlaceholder('regex or simple string');
this.textComponent = text;
})
.addButton((btn) => {
btn.setDisabled(true);
btn.setButtonText('Choose icon');
btn.buttonEl.style.marginLeft = '12px';
btn.buttonEl.style.cursor = 'not-allowed';
btn.buttonEl.style.opacity = '50%';
btn.onClick(() => __awaiter(this, void 0, void 0, function* () {
if (this.textComponent.getValue().length === 0) {
return;
}
const modal = new IconsPickerModal(this.app, this.plugin, '');
modal.onChooseItem = (item) => __awaiter(this, void 0, void 0, function* () {
let icon = '';
if (typeof item === 'object') {
icon = item.displayName;
}
else {
icon = item;
}
const rule = { rule: this.textComponent.getValue(), icon, for: 'everything' };
this.plugin.getSettings().rules = [...this.plugin.getSettings().rules, rule];
yield this.plugin.saveIconFolderData();
this.refreshDisplay();
new obsidian.Notice('Icon rule added.');
this.textComponent.setValue('');
yield addCustomRuleIconsToDOM(this.plugin, rule);
});
modal.open();
}));
this.chooseIconBtn = btn;
});
this.plugin.getSettings().rules.forEach((rule) => {
var _a;
const settingRuleEl = new obsidian.Setting(this.containerEl).setName(rule.rule).setDesc(`Icon: ${rule.icon}`);
const colorPicker = new obsidian.ColorComponent(settingRuleEl.controlEl)
.setValue((_a = rule.color) !== null && _a !== void 0 ? _a : '#000000')
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
rule.color = value;
yield this.plugin.saveIconFolderData();
colorizeCustomRuleIcons(this.plugin, rule);
}));
settingRuleEl.components.push(colorPicker);
settingRuleEl.addButton((btn) => {
var _a;
const isFor = (_a = rule.for) !== null && _a !== void 0 ? _a : 'everything';
if (isFor === 'folders') {
btn.setIcon('folder');
}
else if (isFor === 'files') {
btn.setIcon('document');
}
else {
btn.setIcon('documents');
}
btn.setTooltip(`Icon applicable to: ${isFor}`);
btn.onClick(() => __awaiter(this, void 0, void 0, function* () {
removeCustomRuleIconsFromDOM(this.plugin, Object.assign(Object.assign({}, rule), { for: isFor }));
if (isFor === 'folders') {
rule.for = 'everything';
}
else if (isFor === 'files') {
rule.for = 'folders';
}
else {
rule.for = 'files';
}
yield addCustomRuleIconsToDOM(this.plugin, rule);
yield this.plugin.saveIconFolderData();
this.refreshDisplay();
this.plugin.getSettings().rules.forEach((previousRule) => __awaiter(this, void 0, void 0, function* () {
yield addCustomRuleIconsToDOM(this.plugin, previousRule);
}));
}));
});
settingRuleEl.addButton((btn) => {
btn.setIcon('trash');
btn.setTooltip('Remove the custom rule');
btn.onClick(() => __awaiter(this, void 0, void 0, function* () {
const newRules = this.plugin
.getSettings()
.rules.filter((r) => rule.rule !== r.rule || rule.color !== r.color || rule.icon !== r.icon || r.for !== r.for);
this.plugin.getSettings().rules = newRules;
yield this.plugin.saveIconFolderData();
this.refreshDisplay();
new obsidian.Notice('Custom rule deleted.');
removeCustomRuleIconsFromDOM(this.plugin, rule);
const previousRules = this.plugin.getSettings().rules.filter((r) => rule.for === r.for);
previousRules.forEach((previousRule) => __awaiter(this, void 0, void 0, function* () {
yield addCustomRuleIconsToDOM(this.plugin, previousRule);
}));
}));
});
});
}
}
class ExtraMarginSetting extends IconFolderSetting {
display() {
var _a, _b;
const extraMarginSetting = new obsidian.Setting(this.containerEl)
.setName('Extra margin (in pixels)')
.setDesc('Change the margin of the icons.')
.setClass('obsidian-icon-folder-setting');
const extraMarginDropdown = new obsidian.DropdownComponent(extraMarginSetting.controlEl).addOptions({
top: 'Top',
right: 'Right',
bottom: 'Bottom',
left: 'Left',
});
const extraMarginSlider = new obsidian.SliderComponent(extraMarginSetting.controlEl)
.setLimits(-24, 24, 1)
.setDynamicTooltip()
.setValue((_b = (_a = this.plugin.getSettings().extraMargin) === null || _a === void 0 ? void 0 : _a.top) !== null && _b !== void 0 ? _b : 2)
.onChange((val) => __awaiter(this, void 0, void 0, function* () {
const dropdownValue = extraMarginDropdown.getValue();
if (this.plugin.getSettings().extraMargin) {
this.plugin.getSettings().extraMargin[dropdownValue] = val;
}
else {
this.plugin.getSettings().extraMargin = {
[dropdownValue]: val,
};
}
yield this.plugin.saveIconFolderData();
refreshIconStyle(this.plugin);
}));
extraMarginDropdown.onChange((val) => {
var _a;
if (this.plugin.getSettings().extraMargin) {
extraMarginSlider.setValue((_a = this.plugin.getSettings().extraMargin[val]) !== null && _a !== void 0 ? _a : 2);
}
else {
extraMarginSlider.setValue(2);
}
});
extraMarginSetting.components.push(extraMarginDropdown, extraMarginSlider);
}
}
class IconColorSetting extends IconFolderSetting {
display() {
var _a;
const colorCustomization = new obsidian.Setting(this.containerEl)
.setName('Icon color')
.setDesc('Change the color of the displayed icons.');
const colorPicker = new obsidian.ColorComponent(colorCustomization.controlEl)
.setValue((_a = this.plugin.getSettings().iconColor) !== null && _a !== void 0 ? _a : '#000000')
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
this.plugin.getSettings().iconColor = value;
yield this.plugin.saveIconFolderData();
refreshIconStyle(this.plugin);
}));
colorCustomization.addButton((button) => {
button
.setButtonText('Default')
.setTooltip('Set color to the default one')
.onClick(() => __awaiter(this, void 0, void 0, function* () {
colorPicker.setValue('#000000');
this.plugin.getSettings().iconColor = null;
yield this.plugin.saveIconFolderData();
refreshIconStyle(this.plugin);
}));
});
colorCustomization.components.push(colorPicker);
}
}
class IconFontSizeSetting extends IconFolderSetting {
display() {
new obsidian.Setting(this.containerEl)
.setName('Icon font size (in pixels)')
.setDesc('Change the font size of the displayed icons.')
.addSlider((slider) => {
var _a;
slider
.setLimits(10, 24, 1)
.setDynamicTooltip()
.setValue((_a = this.plugin.getSettings().fontSize) !== null && _a !== void 0 ? _a : DEFAULT_SETTINGS.fontSize)
.onChange((val) => __awaiter(this, void 0, void 0, function* () {
this.plugin.getSettings().fontSize = val;
yield this.plugin.saveIconFolderData();
refreshIconStyle(this.plugin);
}));
});
}
}
class IconPacksPathSetting extends IconFolderSetting {
display() {
const iconPacksPathSetting = new obsidian.Setting(this.containerEl)
.setName('Icon Packs folder path')
.setDesc('Change the default icon packs folder path');
iconPacksPathSetting.addText((text) => {
this.iconPacksSettingTextComp = text;
text.setValue(this.plugin.getSettings().iconPacksPath);
});
iconPacksPathSetting.addButton((btn) => {
btn.setButtonText('Save');
btn.buttonEl.style.marginLeft = '12px';
btn.onClick(() => __awaiter(this, void 0, void 0, function* () {
const newPath = this.iconPacksSettingTextComp.getValue();
const oldPath = this.plugin.getSettings().iconPacksPath;
if (oldPath === this.iconPacksSettingTextComp.getValue()) {
return;
}
new obsidian.Notice('Saving in progress...');
setPath(newPath);
yield createDefaultDirectory(this.plugin);
yield moveIconPackDirectories(this.plugin, oldPath, newPath);
this.plugin.getSettings().iconPacksPath = newPath;
yield this.plugin.saveIconFolderData();
new obsidian.Notice('...saved successfully');
}));
});
}
}
var iconPacks = {
faBrands: {
name: 'font-awesome-brands',
displayName: 'FontAwesome Brands',
path: 'fontawesome-free-6.0.0-web/svgs/brands/',
downloadLink: 'https://github.com/FortAwesome/Font-Awesome/releases/download/6.0.0/fontawesome-free-6.0.0-web.zip',
},
faRegular: {
name: 'font-awesome-regular',
displayName: 'FontAwesome Regular',
path: 'fontawesome-free-6.0.0-web/svgs/regular/',
downloadLink: 'https://github.com/FortAwesome/Font-Awesome/releases/download/6.0.0/fontawesome-free-6.0.0-web.zip',
},
faSolid: {
name: 'font-awesome-solid',
displayName: 'FontAwesome Solid',
path: 'fontawesome-free-6.0.0-web/svgs/solid/',
downloadLink: 'https://github.com/FortAwesome/Font-Awesome/releases/download/6.0.0/fontawesome-free-6.0.0-web.zip',
},
remixIcons: {
name: 'remix-icons',
displayName: 'Remix Icons',
path: '',
downloadLink: 'https://github.com/Remix-Design/RemixIcon/releases/download/v2.5.0/RemixIcon_SVG_v2.5.0.zip',
},
iconBrew: {
name: 'icon-brew',
displayName: 'Icon Brew',
path: '',
downloadLink: 'https://github.com/FlorianWoelki/obsidian-icon-folder/raw/main/iconPacks/icon-brew.zip',
},
/* https://simpleicons.org/ */
simpleIcons: {
name: 'simple-icons',
displayName: 'Simple Icons',
path: 'icons',
downloadLink: 'https://github.com/simple-icons/simple-icons/archive/refs/tags/7.15.0.zip',
},
};
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function createCommonjsModule(fn) {
var module = { exports: {} };
return fn(module, module.exports), module.exports;
}
function commonjsRequire (path) {
throw new Error('Could not dynamically require "' + path + '". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.');
}
/*!
JSZip v3.10.1 - A JavaScript class for generating and reading zip files
<http://stuartk.com/jszip>
(c) 2009-2016 Stuart Knightley <stuart [at] stuartk.com>
Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/main/LICENSE.markdown.
JSZip uses the library pako released under the MIT license :
https://github.com/nodeca/pako/blob/main/LICENSE
*/
var jszip_min = createCommonjsModule(function (module, exports) {
!function(e){module.exports=e();}(function(){return function s(a,o,h){function u(r,e){if(!o[r]){if(!a[r]){var t="function"==typeof commonjsRequire&&commonjsRequire;if(!e&&t)return t(r,!0);if(l)return l(r,!0);var n=new Error("Cannot find module '"+r+"'");throw n.code="MODULE_NOT_FOUND",n}var i=o[r]={exports:{}};a[r][0].call(i.exports,function(e){var t=a[r][1][e];return u(t||e)},i,i.exports,s,a,o,h);}return o[r].exports}for(var l="function"==typeof commonjsRequire&&commonjsRequire,e=0;e<h.length;e++)u(h[e]);return u}({1:[function(e,t,r){var d=e("./utils"),c=e("./support"),p="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";r.encode=function(e){for(var t,r,n,i,s,a,o,h=[],u=0,l=e.length,f=l,c="string"!==d.getTypeOf(e);u<e.length;)f=l-u,n=c?(t=e[u++],r=u<l?e[u++]:0,u<l?e[u++]:0):(t=e.charCodeAt(u++),r=u<l?e.charCodeAt(u++):0,u<l?e.charCodeAt(u++):0),i=t>>2,s=(3&t)<<4|r>>4,a=1<f?(15&r)<<2|n>>6:64,o=2<f?63&n:64,h.push(p.charAt(i)+p.charAt(s)+p.charAt(a)+p.charAt(o));return h.join("")},r.decode=function(e){var t,r,n,i,s,a,o=0,h=0,u="data:";if(e.substr(0,u.length)===u)throw new Error("Invalid base64 input, it looks like a data url.");var l,f=3*(e=e.replace(/[^A-Za-z0-9+/=]/g,"")).length/4;if(e.charAt(e.length-1)===p.charAt(64)&&f--,e.charAt(e.length-2)===p.charAt(64)&&f--,f%1!=0)throw new Error("Invalid base64 input, bad content length.");for(l=c.uint8array?new Uint8Array(0|f):new Array(0|f);o<e.length;)t=p.indexOf(e.charAt(o++))<<2|(i=p.indexOf(e.charAt(o++)))>>4,r=(15&i)<<4|(s=p.indexOf(e.charAt(o++)))>>2,n=(3&s)<<6|(a=p.indexOf(e.charAt(o++))),l[h++]=t,64!==s&&(l[h++]=r),64!==a&&(l[h++]=n);return l};},{"./support":30,"./utils":32}],2:[function(e,t,r){var n=e("./external"),i=e("./stream/DataWorker"),s=e("./stream/Crc32Probe"),a=e("./stream/DataLengthProbe");function o(e,t,r,n,i){this.compressedSize=e,this.uncompressedSize=t,this.crc32=r,this.compression=n,this.compressedContent=i;}o.prototype={getContentWorker:function(){var e=new i(n.Promise.resolve(this.compressedContent)).pipe(this.compression.uncompressWorker()).pipe(new a("data_length")),t=this;return e.on("end",function(){if(this.streamInfo.data_length!==t.uncompressedSize)throw new Error("Bug : uncompressed data size mismatch")}),e},getCompressedWorker:function(){return new i(n.Promise.resolve(this.compressedContent)).withStreamInfo("compressedSize",this.compressedSize).withStreamInfo("uncompressedSize",this.uncompressedSize).withStreamInfo("crc32",this.crc32).withStreamInfo("compression",this.compression)}},o.createWorkerFrom=function(e,t,r){return e.pipe(new s).pipe(new a("uncompressedSize")).pipe(t.compressWorker(r)).pipe(new a("compressedSize")).withStreamInfo("compression",t)},t.exports=o;},{"./external":6,"./stream/Crc32Probe":25,"./stream/DataLengthProbe":26,"./stream/DataWorker":27}],3:[function(e,t,r){var n=e("./stream/GenericWorker");r.STORE={magic:"\0\0",compressWorker:function(){return new n("STORE compression")},uncompressWorker:function(){return new n("STORE decompression")}},r.DEFLATE=e("./flate");},{"./flate":7,"./stream/GenericWorker":28}],4:[function(e,t,r){var n=e("./utils");var o=function(){for(var e,t=[],r=0;r<256;r++){e=r;for(var n=0;n<8;n++)e=1&e?3988292384^e>>>1:e>>>1;t[r]=e;}return t}();t.exports=function(e,t){return void 0!==e&&e.length?"string"!==n.getTypeOf(e)?function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a<s;a++)e=e>>>8^i[255&(e^t[a])];return -1^e}(0|t,e,e.length,0):function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a<s;a++)e=e>>>8^i[255&(e^t.charCodeAt(a))];return -1^e}(0|t,e,e.length,0):0};},{"./utils":32}],5:[function(e,t,r){r.base64=!1,r.binary=!1,r.dir=!1,r.createFolders=!0,r.date=null,r.compression=null,r.compressionOptions=null,r.comment=null,r.unixPermissions=null,r.dosPermissions=null;},{}],6:[function(e,t,r){var n=null;n="undefined"!=typeof Promise?Promise:e("lie"),t.exports={Promise:n};},{lie:37}],7:[function(e,t,r){var n="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Uint32Array,i=e("pako"),s=e("./utils"),a=e("./stream/GenericWorker"),o=n?"uint8array":"array";function h(e
});
const downloadZipFile = (url) => __awaiter(void 0, void 0, void 0, function* () {
const fetched = yield obsidian.requestUrl({ url });
const bytes = fetched.arrayBuffer;
return bytes;
});
const getFileFromJSZipFile = (file) => __awaiter(void 0, void 0, void 0, function* () {
const fileData = yield file.async('blob');
const filename = file.name.split('/').pop();
return new File([fileData], filename);
});
const readZipFile = (bytes, extraPath = '') => __awaiter(void 0, void 0, void 0, function* () {
const zipper = new jszip_min();
const unzippedFiles = yield zipper.loadAsync(bytes);
return Promise.resolve(unzippedFiles).then((unzipped) => {
if (!Object.keys(unzipped.files).length) {
return Promise.reject('No file was found');
}
const files = [];
const regex = new RegExp(extraPath + '(.+)\\.svg', 'g');
Object.entries(unzippedFiles.files).forEach(([_, v]) => {
const matched = v.name.match(regex);
if (!v.dir && matched && matched.length > 0) {
files.push(v);
}
});
return files;
});
});
class IconPackBrowserModal extends obsidian.FuzzySuggestModal {
constructor(app, plugin) {
super(app);
this.plugin = plugin;
this.resultContainerEl.classList.add('obsidian-icon-folder-browse-modal');
this.inputEl.placeholder = 'Select to download icon pack';
}
onAddedIconPack() { }
onOpen() {
super.onOpen();
}
onClose() {
this.contentEl.empty();
}
getItemText(item) {
const prefix = createIconPackPrefix(item.name);
return `${item.displayName} (${prefix})`;
}
getItems() {
const predefinedIconPacks = Object.values(iconPacks);
const allIconPacks = getAllIconPacks();
return predefinedIconPacks.filter((iconPack) => allIconPacks.find((ip) => iconPack.name === ip.name) === undefined);
}
onChooseItem(item, _event) {
return __awaiter(this, void 0, void 0, function* () {
new obsidian.Notice(`Adding ${item.displayName}...`);
yield createIconPackDirectory(this.plugin, item.name);
downloadZipFile(item.downloadLink).then((zipBlob) => {
readZipFile(zipBlob, item.path).then((files) => __awaiter(this, void 0, void 0, function* () {
const existingIcons = getIconsWithPathInData(this.plugin);
for (let i = 0; i < files.length; i++) {
const file = yield getFileFromJSZipFile(files[i]);
const content = yield readFileSync(file);
const icon = addIconToIconPack(item.name, file.name, content);
if (!icon) {
continue;
}
const iconName = icon.prefix + icon.name;
const existingIcon = existingIcons.find((el) => el.value === iconName);
if (existingIcon) {
const path = existingIcon.key;
const container = this.plugin.app.workspace.containerEl.querySelector(`[data-path="${path}"]`);
if (!container) {
continue;
}
const existingIconEl = container.querySelector('.obsidian-icon-folder-icon');
if (!existingIconEl) {
continue;
}
insertIconToNode(this.plugin, iconName, existingIconEl);
}
yield createFile(this.plugin, item.name, file.name, content, files[i].name); // files[i].name is the absolute path to the file.
}
new obsidian.Notice(`...${item.displayName} added`);
this.onAddedIconPack();
}));
});
});
}
renderSuggestion(item, el) {
super.renderSuggestion(item, el);
el.innerHTML = `<div>${el.innerHTML}</div>`;
}
}
class PredefinedIconPacksSetting extends IconFolderSetting {
constructor(plugin, containerEl, app, refreshDisplay) {
super(plugin, containerEl);
this.app = app;
this.refreshDisplay = refreshDisplay;
}
display() {
new obsidian.Setting(this.containerEl)
.setName('Add predefined icon pack')
.setDesc('Add an icon pack like FontAwesome or Remixicons')
.addButton((btn) => {
btn.setButtonText('Browse icon packs');
btn.onClick(() => {
const modal = new IconPackBrowserModal(this.app, this.plugin);
modal.onAddedIconPack = () => {
this.refreshDisplay();
};
modal.open();
});
});
}
}
class RecentlyUsedIconsSetting extends IconFolderSetting {
display() {
new obsidian.Setting(this.containerEl)
.setName('Recently used Icons limit')
.setDesc('Change the limit for the recently used icons displayed in the icon modal.')
.addSlider((slider) => {
var _a;
slider
.setLimits(1, 15, 1)
.setDynamicTooltip()
.setValue((_a = this.plugin.getSettings().recentlyUsedIconsSize) !== null && _a !== void 0 ? _a : DEFAULT_SETTINGS.recentlyUsedIconsSize)
.onChange((val) => __awaiter(this, void 0, void 0, function* () {
this.plugin.getSettings().recentlyUsedIconsSize = val;
yield this.plugin.checkRecentlyUsedIcons();
yield this.plugin.saveIconFolderData();
}));
});
}
}
class IconFolderSettingsTab extends obsidian.PluginSettingTab {
constructor(app, plugin) {
super(app, plugin);
this.plugin = plugin;
}
display() {
const { plugin, containerEl, app } = this;
containerEl.empty();
containerEl.createEl('h2', { text: 'Icon Folder Settings' });
new RecentlyUsedIconsSetting(plugin, containerEl).display();
new IconPacksPathSetting(plugin, containerEl).display();
containerEl.createEl('h3', { text: 'Icon Packs' });
new PredefinedIconPacksSetting(plugin, containerEl, app, () => this.display()).display();
new CustomIconPackSetting(plugin, containerEl, () => this.display()).display();
containerEl.createEl('h3', { text: 'Icon Customization' });
new IconFontSizeSetting(plugin, containerEl).display();
new IconColorSetting(plugin, containerEl).display();
new ExtraMarginSetting(plugin, containerEl).display();
containerEl.createEl('h3', { text: 'Custom Icon Rules' });
new CustomIconRuleSetting(plugin, containerEl, app, () => this.display()).display();
}
}
function around(obj, factories) {
const removers = Object.keys(factories).map(key => around1(obj, key, factories[key]));
return removers.length === 1 ? removers[0] : function () { removers.forEach(r => r()); };
}
function around1(obj, method, createWrapper) {
const original = obj[method], hadOwn = obj.hasOwnProperty(method);
let current = createWrapper(original);
// Let our wrapper inherit static props from the wrapping method,
// and the wrapping method, props from the original method
if (original)
Object.setPrototypeOf(current, original);
Object.setPrototypeOf(wrapper, current);
obj[method] = wrapper;
// Return a callback to allow safe removal
return remove;
function wrapper(...args) {
// If we have been deactivated and are no longer wrapped, remove ourselves
if (current === original && obj[method] === wrapper)
remove();
return current.apply(this, args);
}
function remove() {
// If no other patches, just do a direct removal
if (obj[method] === wrapper) {
if (hadOwn)
obj[method] = original;
else
delete obj[method];
}
if (current === original)
return;
// Else pass future calls through, and remove wrapper from the prototype chain
current = original;
Object.setPrototypeOf(wrapper, original || Function);
}
}
class InternalPluginInjector {
constructor(plugin) {
this.plugin = plugin;
}
get fileExplorers() {
return this.plugin.app.workspace.getLeavesOfType('file-explorer');
}
onMount() { }
}
class StarredInternalPlugin extends InternalPluginInjector {
constructor(plugin) {
super(plugin);
}
get starred() {
return this.plugin.app.internalPlugins.getPluginById('starred');
}
get enabled() {
return this.plugin.app.internalPlugins.getPluginById('starred').enabled;
}
get leaf() {
const leaf = this.plugin.app.workspace.getLeavesOfType('starred');
if (!leaf) {
return undefined;
}
if (leaf.length === 1) {
return leaf[0].view;
}
return undefined;
}
setIcon(filePath, node) {
const icon = getIconByPath(this.plugin, filePath);
const iconNode = node.querySelector('.nav-file-icon');
if (!iconNode || !icon) {
return;
}
insertIconToNode(this.plugin, icon, iconNode);
}
computeNodesWithPath(callback) {
const { itemLookup, containerEl } = this.leaf;
const navFileEls = containerEl.querySelectorAll('.nav-file');
navFileEls.forEach((navFileEl) => {
const lookupFile = itemLookup.get(navFileEl);
if (!lookupFile) {
return;
}
callback(navFileEl, lookupFile.path);
});
}
onMount() {
const nodesWithPath = {};
this.computeNodesWithPath((node, filePath) => {
nodesWithPath[filePath] = node;
});
Object.entries(nodesWithPath).forEach(([filePath, node]) => this.setIcon(filePath, node));
}
register() {
if (!this.plugin.app.internalPlugins.getPluginById('file-explorer').enabled) {
console.info(`[${MetaData.pluginName}/Starred] Skipping starred internal plugin registration because file-explorer is not enabled.`);
return;
}
if (!this.enabled) {
console.info(`[${MetaData.pluginName}/Starred] Skipping starred internal plugin registration because it's not enabled.`);
return;
}
const self = this;
this.plugin.register(around(this.starred.instance, {
addItem: function (next) {
return function (file) {
next.call(this, file);
self.onMount();
};
},
removeItem: function (next) {
return function (file) {
next.call(this, file);
self.onMount();
};
},
}));
}
}
class IconFolderPlugin extends obsidian.Plugin {
constructor() {
super(...arguments);
this.registeredFileExplorers = new Set();
this.modifiedInternalPlugins = [];
}
migrate() {
return __awaiter(this, void 0, void 0, function* () {
if (!this.getSettings().migrated) {
console.log('migrating icons...');
this.data = migrateIcons(this);
this.getSettings().migrated = true;
console.log('...icons migrated');
}
const extraPadding = this.getSettings().extraPadding;
if (extraPadding) {
if (extraPadding.top !== 2 || extraPadding.bottom !== 2 || extraPadding.left !== 2 || extraPadding.right !== 2) {
this.getSettings().extraMargin = extraPadding;
delete this.getSettings()['extraPadding'];
}
}
yield this.saveIconFolderData();
});
}
onload() {
return __awaiter(this, void 0, void 0, function* () {
MetaData.pluginName = this.manifest.id;
console.log(`loading ${MetaData.pluginName}`);
this.modifiedInternalPlugins.push(new StarredInternalPlugin(this));
yield this.loadIconFolderData();
setPath(this.getSettings().iconPacksPath);
yield createDefaultDirectory(this);
yield this.checkRecentlyUsedIcons();
yield this.migrate();
yield loadUsedIcons(this, getIconsInData(this));
initIconPacks(this);
this.app.workspace.onLayoutReady(() => this.handleChangeLayout());
this.registerEvent(this.app.workspace.on('layout-change', () => this.handleChangeLayout()));
this.registerEvent(this.app.workspace.on('file-menu', (menu, file) => {
const addIconMenuItem = (item) => {
item.setTitle('Change icon');
item.setIcon('hashtag');
item.onClick(() => {
const modal = new IconsPickerModal(this.app, this, file.path);
modal.open();
});
};
const removeIconMenuItem = (item) => {
item.setTitle('Remove icon');
item.setIcon('trash');
item.onClick(() => {
this.removeFolderIcon(file.path);
removeFromDOM(file.path);
updateIcon(this, file);
});
};
menu.addItem(addIconMenuItem);
const node = document.querySelector(`[data-path="${file.path}"]`);
const iconNode = node.querySelector('.obsidian-icon-folder-icon');
if (iconNode) {
menu.addItem(removeIconMenuItem);
}
const inheritIcon = (item) => {
if (typeof this.data[file.path] === 'object') {
item.setTitle('Remove inherit icon');
item.onClick(() => {
removeInheritanceForFolder(this, file.path);
this.saveInheritanceData(file.path, null);
});
}
else {
item.setTitle('Inherit icon');
item.onClick(() => {
const modal = new IconsPickerModal(this.app, this, file.path);
modal.open();
// manipulate `onChooseItem` method to get custom functionality for inheriting icons
modal.onChooseItem = (icon) => {
this.saveInheritanceData(file.path, icon);
addInheritanceForFolder(this, file.path);
};
});
}
item.setIcon('vertical-three-dots');
};
menu.addItem(inheritIcon);
}));
// deleting event
this.registerEvent(this.app.vault.on('delete', (file) => {
const path = file.path;
this.removeFolderIcon(path);
}));
// renaming event
this.registerEvent(this.app.vault.on('rename', (file, oldPath) => {
this.renameFolder(file.path, oldPath);
}));
this.addSettingTab(new IconFolderSettingsTab(this.app, this));
});
}
getSearchLeave() {
return this.app.workspace.getLeavesOfType('search')[0].view;
}
addIconsToSearch() {
console.log(this.app.workspace.getLeavesOfType('backlink'));
const searchLeaveDom = this.getSearchLeave().dom;
searchLeaveDom.children.forEach((child) => {
const file = child.file;
const collapseEl = child.collapseEl;
const existingIcon = child.containerEl.querySelector('.obsidian-icon-folder-icon');
if (existingIcon) {
existingIcon.remove();
}
const iconName = this.data[file.path];
if (iconName) {
const iconNode = child.containerEl.createDiv();
iconNode.classList.add('obsidian-icon-folder-icon');
insertIconToNode(this, this.data[file.path], iconNode);
iconNode.insertAfter(collapseEl);
}
});
}
handleChangeLayout() {
// Transform data that are objects to single strings.
const data = Object.entries(this.data);
this.modifiedInternalPlugins.forEach((internalPlugin) => {
if (internalPlugin.enabled) {
internalPlugin.onMount();
internalPlugin.register();
}
});
addIconsToDOM(this, data, this.registeredFileExplorers, () => {
//const searchLeaveDom = this.getSearchLeave().dom;
//searchLeaveDom.changed = () => this.addIconsToSearch();
// Register rename event for adding icons with custom rules to the DOM.
this.registerEvent(this.app.vault.on('rename', (file, oldPath) => {
this.getSettings().rules.forEach((rule) => __awaiter(this, void 0, void 0, function* () {
if (doesCustomRuleIconExists(rule, oldPath)) {
removeFromDOM(file.path);
}
yield addCustomRuleIconsToDOM(this, rule, file);
}));
}));
// Register create event for checking inheritance functionality.
this.registerEvent(this.app.vault.on('create', (file) => {
const inheritanceFolders = Object.entries(this.data).filter(([k, v]) => k !== 'settings' && typeof v === 'object');
if (file.parent.path === '/')
return;
inheritanceFolders.forEach(([path, obj]) => {
if (file.parent.path.includes(path)) {
addInheritanceIconToFile(this, this.registeredFileExplorers, file.path, obj.inheritanceIcon);
}
});
}));
});
}
saveInheritanceData(folderPath, icon) {
const currentValue = this.data[folderPath];
// if icon is null, it will remove the inheritance icon from the data
if (icon === null && currentValue && typeof currentValue === 'object') {
const folderObject = currentValue;
if (folderObject.iconName) {
this.data[folderPath] = folderObject.iconName;
}
else {
delete this.data[folderPath];
}
}
// icon is not null, so it will add inheritance data
else {
// check if data already exists
if (currentValue) {
// check if current value is already an icon name
if (typeof currentValue === 'string') {
this.data[folderPath] = {
iconName: currentValue,
inheritanceIcon: typeof icon === 'object' ? icon.displayName : icon,
};
}
// check if it has already a inheritance icon
else if (folderPath !== 'settings') {
this.data[folderPath] = Object.assign(Object.assign({}, currentValue), { inheritanceIcon: typeof icon === 'object' ? icon.displayName : icon });
}
}
else {
this.data[folderPath] = {
iconName: null,
inheritanceIcon: typeof icon === 'object' ? icon.displayName : icon,
};
}
}
this.saveIconFolderData();
}
onunload() {
console.log('unloading obsidian-icon-folder');
}
renameFolder(newPath, oldPath) {
if (!this.data[oldPath] || newPath === oldPath) {
return;
}
Object.defineProperty(this.data, newPath, Object.getOwnPropertyDescriptor(this.data, oldPath));
delete this.data[oldPath];
this.saveIconFolderData();
}
removeFolderIcon(path) {
if (!this.data[path]) {
return;
}
if (typeof this.data[path] === 'object') {
const currentValue = this.data[path];
this.data[path] = Object.assign(Object.assign({}, currentValue), { iconName: null });
}
else {
delete this.data[path];
}
//this.addIconsToSearch();
this.saveIconFolderData();
}
addFolderIcon(path, icon) {
const iconName = typeof icon === 'object' ? icon.displayName : icon;
this.data[path] = iconName;
if (!this.getSettings().recentlyUsedIcons.includes(iconName)) {
if (this.getSettings().recentlyUsedIcons.length >= this.getSettings().recentlyUsedIconsSize) {
this.getSettings().recentlyUsedIcons = this.getSettings().recentlyUsedIcons.slice(0, this.getSettings().recentlyUsedIconsSize - 1);
}
this.getSettings().recentlyUsedIcons.unshift(iconName);
this.checkRecentlyUsedIcons();
}
//this.addIconsToSearch();
this.saveIconFolderData();
}
getSettings() {
return this.data.settings;
}
loadIconFolderData() {
return __awaiter(this, void 0, void 0, function* () {
const data = yield this.loadData();
if (data) {
Object.entries(DEFAULT_SETTINGS).forEach(([k, v]) => {
if (!data.settings[k]) {
data.settings[k] = v;
}
});
}
this.data = Object.assign({ settings: Object.assign({}, DEFAULT_SETTINGS) }, {}, data);
});
}
saveIconFolderData() {
return __awaiter(this, void 0, void 0, function* () {
yield this.saveData(this.data);
});
}
checkRecentlyUsedIcons() {
return __awaiter(this, void 0, void 0, function* () {
if (this.getSettings().recentlyUsedIcons.length > this.getSettings().recentlyUsedIconsSize) {
this.getSettings().recentlyUsedIcons = this.getSettings().recentlyUsedIcons.slice(0, this.getSettings().recentlyUsedIconsSize);
yield this.saveIconFolderData();
}
});
}
getData() {
return this.data;
}
getRegisteredFileExplorers() {
return this.registeredFileExplorers;
}
getDataPathByValue(value) {
return Object.entries(this.data).find(([k, v]) => {
if (typeof v === 'string') {
if (value === v) {
return k;
}
}
else if (typeof v === 'object') {
v = v;
if (value === v.iconName || value === v.inheritanceIcon) {
return k;
}
}
});
}
}
module.exports = IconFolderPlugin;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZXMiOlsibm9kZV9tb2R1bGVzLy5wbnBtL0Byb2xsdXArcGx1Z2luLXR5cGVzY3JpcHRAOC41LjBfc2Jpc2t5aXlzeGhsZG1uczdybW52b2lzenUvbm9kZV9tb2R1bGVzL3RzbGliL3RzbGliLmVzNi5qcyIsInNyYy9NZXRhRGF0YS50cyIsInNyYy9zdmdFeHRyYWN0b3IudHMiLCJzcmMvaWNvblBhY2tNYW5hZ2VyLnRzIiwibm9kZV9tb2R1bGVzLy5wbnBtL3R3ZW1vamlAMTQuMC4yL25vZGVfbW9kdWxlcy90d2Vtb2ppL2Rpc3QvdHdlbW9qaS5lc20uanMiLCJzcmMvZW1vamkudHMiLCJzcmMvdXRpbC50cyIsInNyYy9pY29uc1BpY2tlck1vZGFsLnRzIiwic3JjL3NldHRpbmdzLnRzIiwic3JjL21pZ3JhdGlvbi50cyIsInNyYy9zZXR0aW5nc1RhYi9pY29uRm9sZGVyU2V0dGluZy50cyIsInNyYy9zZXR0aW5nc1RhYi9jdXN0b21JY29uUGFjay50cyIsInNyYy9zZXR0aW5nc1RhYi9jdXN0b21JY29uUnVsZS50cyIsInNyYy9zZXR0aW5nc1RhYi9leHRyYU1hcmdpbi50cyIsInNyYy9zZXR0aW5nc1RhYi9pY29uQ29sb3IudHMiLCJzcmMvc2V0dGluZ3NUYWIvaWNvbkZvbnRTaXplLnRzIiwic3JjL3NldHRpbmdzVGFiL2ljb25QYWNrc1BhdGgudHMiLCJzcmMvaWNvblBhY2tzLnRzIiwibm9kZV9tb2R1bGVzLy5wbnBtL2pzemlwQDMuMTAuMS9ub2RlX21vZHVsZXMvanN6aXAvZGlzdC9qc3ppcC5taW4uanMiLCJzcmMvemlwVXRpbC50cyIsInNyYy9pY29uUGFja0Jyb3dzZXJNb2RhbC50cyIsInNyYy9zZXR0aW5nc1RhYi9wcmVkZWZpbmVkSWNvblBhY2tzLnRzIiwic3JjL3NldHRpbmdzVGFiL3JlY2VudGx5VXNlZEljb25zLnRzIiwic3JjL3NldHRpbmdzVGFiL2luZGV4LnRzIiwibm9kZV9tb2R1bGVzLy5wbnBtL21vbmtleS1hcm91bmRAMi4zLjAvbm9kZV9tb2R1bGVzL21vbmtleS1hcm91bmQvbWpzL2luZGV4LmpzIiwic3JjL0B0eXBlcy9pbnRlcm5hbFBsdWdpbkluamVjdG9yLnRzIiwic3JjL2ludGVybmFsUGx1Z2lucy9zdGFycmVkLnRzIiwic3JjL21haW4udHMiXSwic291cmNlc0NvbnRlbnQiOm51bGwsIm5hbWVzIjpbIk5vdGljZSIsImljb25QYWNrcyIsIkZ1enp5U3VnZ2VzdE1vZGFsIiwiZW1vamkiLCJTZXR0aW5nIiwiQ29sb3JDb21wb25lbnQiLCJEcm9wZG93bkNvbXBvbmVudCIsIlNsaWRlckNvbXBvbmVudCIsInJlcXVpcmUiLCJnbG9iYWwiLCJyZXF1ZXN0VXJsIiwiSlNaaXAiLCJQbHVnaW5TZXR0aW5nVGFiIiwiUGx1Z2luIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBdURBO0FBQ08sU0FBUyxTQUFTLENBQUMsT0FBTyxFQUFFLFVBQVUsRUFBRSxDQUFDLEVBQUUsU0FBUyxFQUFFO0FBQzdELElBQUksU0FBUyxLQUFLLENBQUMsS0FBSyxFQUFFLEVBQUUsT0FBTyxLQUFLLFlBQVksQ0FBQyxHQUFHLEtBQUssR0FBRyxJQUFJLENBQUMsQ0FBQyxVQUFVLE9BQU8sRUFBRSxFQUFFLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxFQUFFO0FBQ2hILElBQUksT0FBTyxLQUFLLENBQUMsS0FBSyxDQUFDLEdBQUcsT0FBTyxDQUFDLEVBQUUsVUFBVSxPQUFPLEVBQUUsTUFBTSxFQUFFO0FBQy9ELFFBQVEsU0FBUyxTQUFTLENBQUMsS0FBSyxFQUFFLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLE9BQU8sQ0FBQyxFQUFFLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRTtBQUNuRyxRQUFRLFNBQVMsUUFBUSxDQUFDLEtBQUssRUFBRSxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLE9BQU8sQ0FBQyxFQUFFLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRTtBQUN0RyxRQUFRLFNBQVMsSUFBSSxDQUFDLE1BQU0sRUFBRSxFQUFFLE1BQU0sQ0FBQyxJQUFJLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsR0FBRyxLQUFLLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsUUFBUSxDQUFDLENBQUMsRUFBRTtBQUN0SCxRQUFRLElBQUksQ0FBQyxDQUFDLFNBQVMsR0FBRyxTQUFTLENBQUMsS0FBSyxDQUFDLE9BQU8sRUFBRSxVQUFVLElBQUksRUFBRSxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztBQUM5RSxLQUFLLENBQUMsQ0FBQztBQUNQOztBQzVFYyxNQUFPLFFBQVEsQ0FBQTtBQUU1Qjs7QUNITSxNQUFNLE9BQU8sR0FBRyxDQUFDLFNBQWlCLEtBQVk7OztJQUVuRCxTQUFTLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxnQkFBZ0IsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUNwRCxTQUFTLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsSUFBSSxDQUFDLENBQUM7O0FBRy9DLElBQUEsTUFBTSxNQUFNLEdBQUcsSUFBSSxTQUFTLEVBQUUsQ0FBQztBQUMvQixJQUFBLE1BQU0sR0FBRyxHQUFHLE1BQU0sQ0FBQyxlQUFlLENBQUMsU0FBUyxFQUFFLFdBQVcsQ0FBQyxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUMsQ0FBQzs7QUFHaEYsSUFBQSxJQUFJLEdBQUcsQ0FBQyxZQUFZLENBQUMsT0FBTyxDQUFDLEVBQUU7QUFDN0IsUUFBQSxHQUFHLENBQUMsS0FBSyxDQUFDLEtBQUssR0FBRyxFQUFFLENBQUM7QUFDckIsUUFBQSxHQUFHLENBQUMsS0FBSyxDQUFDLE1BQU0sR0FBRyxFQUFFLENBQUM7QUFDdkIsS0FBQTs7QUFHRCxJQUFBLElBQUksR0FBRyxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsS0FBSyxLQUFLLENBQUMsSUFBSSxHQUFHLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFO0FBQ3ZFLFFBQUEsTUFBTSxLQUFLLEdBQUcsQ0FBQSxFQUFBLEdBQUEsR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxNQUFJLElBQUEsSUFBQSxFQUFBLEtBQUEsS0FBQSxDQUFBLEd