/ *
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' ) ;
var view = require ( '@codemirror/view' ) ;
var state = require ( '@codemirror/state' ) ;
var language = require ( '@codemirror/language' ) ;
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
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 ( ) ) ;
} ) ;
}
typeof SuppressedError === "function" ? SuppressedError : function ( error , suppressed , message ) {
var e = new Error ( message ) ;
return e . name = "SuppressedError" , e . error = error , e . suppressed = suppressed , e ;
} ;
// This library file does not include any other dependency and is a standalone file that
// only include utility functions for manipulating or extracting svg information.
/ * *
* Extracts an SVG string from a given input string and returns a cleaned up and
* formatted SVG string .
* @ param svgString SVG string to extract from .
* @ returns Cleaned up and formatted SVG string .
* /
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' ) ;
}
const possibleTitle = svg . querySelector ( 'title' ) ;
if ( possibleTitle ) {
possibleTitle . remove ( ) ;
}
svg . setAttribute ( 'width' , '16px' ) ;
svg . setAttribute ( 'height' , '16px' ) ;
return svg . outerHTML ;
} ;
/ * *
* Sets the font size of an SVG string by modifying its width and / or height attributes .
* The font size will be always set in pixels .
* @ param svgString SVG string to modify .
* @ param fontSize Font size in pixels to set .
* @ returns Modified SVG string .
* /
const setFontSize = ( svgString , fontSize ) => {
const widthRe = new RegExp ( /width="[\d.]+(px)?"/ ) ;
const heightRe = new RegExp ( /height="[\d.]+(px)?"/ ) ;
if ( svgString . match ( widthRe ) ) {
svgString = svgString . replace ( widthRe , ` width=" ${ fontSize } px" ` ) ;
}
if ( svgString . match ( heightRe ) ) {
svgString = svgString . replace ( heightRe , ` height=" ${ fontSize } px" ` ) ;
}
return svgString ;
} ;
/ * *
* Replaces the fill or stroke color of an SVG string with a given color .
* @ param svgString SVG string to modify .
* @ param color Color to set . Defaults to 'currentColor' .
* @ returns The modified SVG string .
* /
const colorize = ( svgString , color ) => {
if ( ! color ) {
color = 'currentColor' ;
}
const parser = new DOMParser ( ) ;
// Tries to parse the string into a HTML node.
const parsedNode = parser . parseFromString ( svgString , 'text/html' ) ;
const svg = parsedNode . querySelector ( 'svg' ) ;
if ( svg ) {
if ( svg . hasAttribute ( 'fill' ) && svg . getAttribute ( 'fill' ) !== 'none' ) {
svg . setAttribute ( 'fill' , color ) ;
}
else if ( svg . hasAttribute ( 'stroke' ) &&
svg . getAttribute ( 'stroke' ) !== 'none' ) {
svg . setAttribute ( 'stroke' , color ) ;
}
return svg . outerHTML ;
}
return svgString ;
} ;
var svg = {
extract ,
colorize ,
setFontSize ,
} ;
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : { } ;
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.' ) ;
}
var jszip _min = { exports : { } } ;
/ * !
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
* /
jszip _min . exports ;
( 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 (
} ( jszip _min , jszip _min . exports ) ) ;
var jszip _minExports = jszip _min . exports ;
/ * *
* Download a zip file from a url and return the bytes of the file as an ArrayBuffer .
* @ param url String url of the zip file to download .
* @ returns ArrayBuffer of the zip file .
* /
const downloadZipFile = ( url ) => _ _awaiter ( void 0 , void 0 , void 0 , function * ( ) {
const fetched = yield obsidian . requestUrl ( { url } ) ;
const bytes = fetched . arrayBuffer ;
return bytes ;
} ) ;
/ * *
* Transforms a JSZip file into a File object .
* @ param file JSZip file to transform .
* @ returns File object of the JSZip file .
* /
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 ) ;
} ) ;
/ * *
* Read a zip file and return the files inside it .
* @ param bytes ArrayBuffer of the zip file .
* @ param extraPath String path to filter the files inside the zip file . This can be used
* to set an extra path ( like a directory inside the zip file ) to filter the files .
* @ returns Array of loaded files inside the zip file .
* /
const readZipFile = ( bytes _1 , ... args _1 ) => _ _awaiter ( void 0 , [ bytes _1 , ... args _1 ] , void 0 , function * ( bytes , extraPath = '' ) {
const unzippedFiles = yield jszip _minExports . loadAsync ( bytes ) ;
return Promise . resolve ( unzippedFiles ) . then ( ( unzipped ) => {
if ( ! Object . keys ( unzipped . files ) . length ) {
return Promise . reject ( 'No file was found' ) ;
}
const files = [ ] ;
// Regex for retrieving the files inside the zip file or inside the directory of a
// zip file.
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 ;
} ) ;
} ) ;
const PLUGIN _NAME = 'iconize' ;
const TITLE _ICON _CLASS = 'iconize-title-icon' ;
/ * *
* The name of the attribute that is used to store the icon name in the node .
* The value of this attributes contains the prefix and the name of the icon .
* /
const ICON _ATTRIBUTE _NAME = 'data-icon' ;
var config = {
PLUGIN _NAME ,
TITLE _ICON _CLASS ,
ICON _ATTRIBUTE _NAME ,
} ;
const iconPacks$1 = {
faBrands : {
name : 'font-awesome-brands' ,
displayName : 'FontAwesome Brands' ,
path : 'fontawesome-free-6.5.1-web/svgs/brands/' ,
downloadLink : 'https://github.com/FortAwesome/Font-Awesome/releases/download/6.5.1/fontawesome-free-6.5.1-web.zip' ,
} ,
faRegular : {
name : 'font-awesome-regular' ,
displayName : 'FontAwesome Regular' ,
path : 'fontawesome-free-6.5.1-web/svgs/regular/' ,
downloadLink : 'https://github.com/FortAwesome/Font-Awesome/releases/download/6.5.1/fontawesome-free-6.5.1-web.zip' ,
} ,
faSolid : {
name : 'font-awesome-solid' ,
displayName : 'FontAwesome Solid' ,
path : 'fontawesome-free-6.5.1-web/svgs/solid/' ,
downloadLink : 'https://github.com/FortAwesome/Font-Awesome/releases/download/6.5.1/fontawesome-free-6.5.1-web.zip' ,
} ,
remixIcons : {
name : 'remix-icons' ,
displayName : 'Remix Icons' ,
path : '' ,
downloadLink : 'https://github.com/Remix-Design/RemixIcon/releases/download/v4.2.0/RemixIcon_Svg_v4.2.0.zip' ,
} ,
iconBrew : {
name : 'icon-brew' ,
displayName : 'Icon Brew' ,
path : '' ,
downloadLink : 'https://github.com/FlorianWoelki/obsidian-iconize/raw/main/iconPacks/icon-brew.zip' ,
} ,
/** @source https://simpleicons.org/ */
simpleIcons : {
name : 'simple-icons' ,
displayName : 'Simple Icons' ,
path : 'simple-icons-11.10.0/icons/' ,
downloadLink : 'https://github.com/simple-icons/simple-icons/archive/refs/tags/11.10.0.zip' ,
} ,
lucide : {
name : 'lucide-icons' ,
displayName : 'Lucide' ,
path : '' ,
downloadLink : 'https://github.com/lucide-icons/lucide/releases/download/0.363.0/lucide-icons-0.363.0.zip' ,
} ,
tablerIcons : {
name : 'tabler-icons' ,
displayName : 'Tabler Icons' ,
path : 'svg' ,
downloadLink : 'https://github.com/tabler/tabler-icons/releases/download/v3.1.0/tabler-icons-3.1.0.zip' ,
} ,
/** @source https://boxicons.com/ */
boxicons : {
name : 'boxicons' ,
displayName : 'Boxicons' ,
path : 'svg' ,
downloadLink : 'https://github.com/FlorianWoelki/obsidian-iconize/raw/main/iconPacks/boxicons.zip' ,
} ,
/** @source http://nagoshiashumari.github.io/Rpg-Awesome/ */
rpgAwesome : {
name : 'rpg-awesome' ,
displayName : 'RPG Awesome' ,
path : '' ,
downloadLink : 'https://github.com/FlorianWoelki/obsidian-iconize/raw/main/iconPacks/rpg-awesome.zip' ,
} ,
/** @source https://coolicons.cool/ */
coolicons : {
name : 'coolicons' ,
displayName : 'Coolicons' ,
path : 'cooliocns SVG' ,
downloadLink : 'https://github.com/krystonschwarze/coolicons/releases/download/v4.1/coolicons.v4.1.zip' ,
} ,
/** @source https://feathericons.com/ */
feathericons : {
name : 'feather-icons' ,
displayName : 'Feather Icons' ,
path : 'feather-4.29.1/icons/' ,
downloadLink : 'https://github.com/feathericons/feather/archive/refs/tags/v4.29.1.zip' ,
} ,
/** @source https://github.com/primer/octicons */
octicons : {
name : 'octicons' ,
displayName : 'Octicons' ,
path : 'octicons-19.8.0/icons/' ,
downloadLink : 'https://github.com/primer/octicons/archive/refs/tags/v19.8.0.zip' ,
} ,
} ;
/ * *
* Returns a possible path to the icon pack .
* @ param name String of the icon pack name .
* @ returns String of the path to the icon pack or undefined if the icon pack does not
* exist .
* /
const getExtraPath = ( iconPackName ) => {
var _a ;
const path = ( _a = Object . values ( iconPacks$1 ) . find ( ( iconPack ) => iconPack . name === iconPackName ) ) === null || _a === void 0 ? void 0 : _a . path ;
return ( path === null || path === void 0 ? void 0 : path . length ) === 0 ? undefined : path ;
} ;
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 ( ` [ ${ config . PLUGIN _NAME } ] 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 ;
} ;
let preloadedIcons = [ ] ;
const getPreloadedIcons = ( ) => {
return preloadedIcons ;
} ;
const resetPreloadedIcons = ( ) => {
preloadedIcons = [ ] ;
} ;
let iconPacks = [ ] ;
const moveIconPackDirectories = ( plugin , from , to ) => _ _awaiter ( void 0 , void 0 , void 0 , function * ( ) {
// Tries to move all icon packs to the new folder.
for ( let i = 0 ; i < iconPacks . length ; i ++ ) {
const iconPack = iconPacks [ i ] ;
if ( yield plugin . app . vault . adapter . exists ( ` ${ from } / ${ iconPack . name } ` ) ) {
// Tries to create a new directory in the new path.
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 } ... ` ) ;
// Move the zip file.
if ( yield plugin . app . vault . adapter . exists ( ` ${ from } / ${ iconPack . name } .zip ` ) ) {
yield plugin . app . vault . adapter . copy ( ` ${ from } / ${ iconPack . name } .zip ` , ` ${ to } / ${ iconPack . name } .zip ` ) ;
}
// Move all other files inside of the iconpack directory.
const filesInDirectory = yield getFilesInDirectory ( plugin , ` ${ from } / ${ iconPack . name } ` ) ;
for ( const file of filesInDirectory ) {
const fileName = file . split ( '/' ) . pop ( ) ;
yield plugin . app . vault . adapter . copy ( ` ${ from } / ${ iconPack . name } / ${ fileName } ` , ` ${ to } / ${ iconPack . name } / ${ fileName } ` ) ;
}
new obsidian . Notice ( ` ...moved ${ iconPack . name } ` ) ;
}
// Removes all the existing icon packs in the `from` directory.
for ( let i = 0 ; i < iconPacks . length ; i ++ ) {
const iconPack = iconPacks [ i ] ;
if ( yield plugin . app . vault . adapter . exists ( ` ${ from } / ${ iconPack . name } ` ) ) {
yield plugin . app . vault . adapter . rmdir ( ` ${ from } / ${ iconPack . name } ` , true ) ;
}
}
// Remove root directory that contains all the icon packs.
if ( ! to . startsWith ( from ) ) {
yield plugin . app . vault . adapter . rmdir ( ` ${ from } ` , true ) ;
}
} ) ;
const createCustomIconPackDirectory = ( plugin , dir ) => _ _awaiter ( void 0 , void 0 , void 0 , function * ( ) {
yield createDirectory ( plugin , dir ) ;
const prefix = createIconPackPrefix ( dir ) ;
iconPacks . push ( { name : dir , icons : [ ] , prefix , custom : true } ) ;
} ) ;
const deleteIconPack = ( plugin , dir ) => _ _awaiter ( void 0 , void 0 , void 0 , function * ( ) {
iconPacks = iconPacks . filter ( ( iconPack ) => iconPack . name !== dir ) ;
// Check for the icon pack directory and delete it.
if ( yield plugin . app . vault . adapter . exists ( ` ${ path } / ${ dir } ` ) ) {
yield plugin . app . vault . adapter . rmdir ( ` ${ path } / ${ dir } ` , true ) ;
}
// Check for the icon pack zip file and delete it.
if ( yield plugin . app . vault . adapter . exists ( ` ${ path } / ${ dir } .zip ` ) ) {
yield plugin . app . vault . adapter . remove ( ` ${ path } / ${ dir } .zip ` ) ;
}
} ) ;
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 ( '' ) ;
} ;
// export const normalizeFileName = async (plugin: Plugin, oldPath: string) => {
// const fileName = oldPath.split('/').pop();
// const newPath = oldPath.substring(0, oldPath.indexOf(fileName)) + getNormalizedName(fileName);
// await plugin.app.vault.adapter.rename(oldPath, newPath);
// };
const createZipFile = ( plugin , filename , buffer ) => _ _awaiter ( void 0 , void 0 , void 0 , function * ( ) {
yield plugin . app . vault . adapter . writeBinary ( ` ${ path } / ${ filename } ` , buffer ) ;
} ) ;
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 ( ` [ ${ config . PLUGIN _NAME } ] Renamed old file ${ normalizedFilename } to ${ newFilename } because of duplication. ` ) ;
new obsidian . Notice ( ` [ ${ config . PLUGIN _NAME } ] Renamed ${ normalizedFilename } to ${ newFilename } to avoid duplication. ` , 8000 ) ;
}
else {
console . warn ( ` [ ${ config . PLUGIN _NAME } ] Could not create icons with duplicated file names ( ${ normalizedFilename } ). ` ) ;
new obsidian . Notice ( ` [ ${ config . PLUGIN _NAME } ] 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 ;
} ;
const getFilesInDirectory = ( plugin , dir ) => _ _awaiter ( void 0 , void 0 , void 0 , function * ( ) {
if ( ! ( yield plugin . app . vault . adapter . exists ( dir ) ) ) {
return [ ] ;
}
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 . charAt ( 0 ) . toUpperCase ( ) + iconName . substring ( 1 ) ;
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 ) ;
if ( ! svgContentMatch ) {
console . log ( ` skipping icon with invalid svg content: ${ content } ` ) ;
return null ;
}
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 ,
iconPackName ,
filename : iconName ,
svgContent ,
svgViewbox ,
svgElement : svg . 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 ) . toLowerCase ( ) ;
}
return result ;
}
return ( iconPackName . charAt ( 0 ) . toUpperCase ( ) + iconPackName . charAt ( 1 ) . toLowerCase ( ) ) ;
} ;
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 getIconPackNameByPrefix = ( prefix ) => {
var _a ;
return ( _a = iconPacks . find ( ( iconPack ) => iconPack . prefix === prefix ) ) === null || _a === void 0 ? void 0 : _a . name ;
} ;
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 ) {
// Ignore because background check automatically adds the icons and icon pack
// directories.
if ( ! plugin . getSettings ( ) . iconsBackgroundCheckEnabled ) {
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 ( ` [iconize] 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 * ( ) {
// Remove the beginning slash because paths which start with `/` are the same as without
// a slash.
if ( path . startsWith ( '/' ) ) {
path = path . slice ( 1 ) ;
}
const loadedIconPacks = yield plugin . app . vault . adapter . list ( path ) ;
// Extract all zip files which will be downloaded icon packs.
const zipFiles = { } ;
for ( let i = 0 ; i < loadedIconPacks . files . length ; i ++ ) {
const fileName = loadedIconPacks . files [ i ] ;
if ( fileName . endsWith ( '.zip' ) ) {
const arrayBuffer = yield plugin . app . vault . adapter . readBinary ( fileName ) ;
const files = yield readZipFile ( arrayBuffer ) ;
const iconPackName = fileName . split ( '/' ) . pop ( ) . split ( '.zip' ) [ 0 ] ;
zipFiles [ iconPackName ] = files ;
}
}
// Check for custom-made icon packs.
for ( let i = 0 ; i < loadedIconPacks . folders . length ; i ++ ) {
const folderName = loadedIconPacks . folders [ i ] . split ( '/' ) . pop ( ) ;
// Continue if the icon pack does have a zip file.
if ( zipFiles [ folderName ] ) {
continue ;
}
const files = yield getFilesInDirectory ( plugin , ` ${ path } / ${ folderName } ` ) ;
const loadedIcons = [ ] ;
// Convert files into loaded svgs.
for ( let j = 0 ; j < files . length ; j ++ ) {
const iconNameRegex = files [ j ] . match ( new RegExp ( path + '/' + folderName + '/(.*)' ) ) ;
const iconName = getNormalizedName ( iconNameRegex [ 1 ] ) ;
const iconContent = yield plugin . app . vault . adapter . read ( files [ j ] ) ;
const icon = generateIcon ( folderName , iconName , iconContent ) ;
if ( icon ) {
loadedIcons . push ( icon ) ;
}
}
const prefix = createIconPackPrefix ( folderName ) ;
console . log ( folderName , loadedIcons ) ;
iconPacks . push ( {
name : folderName ,
icons : loadedIcons ,
prefix ,
custom : true ,
} ) ;
console . log ( ` loaded icon pack ${ folderName } ( ${ loadedIcons . length } ) ` ) ;
}
// Extract all files from the zip files.
for ( const zipFile in zipFiles ) {
const files = zipFiles [ zipFile ] ;
const loadedIcons = yield getLoadedIconsFromZipFile ( zipFile , files ) ;
const prefix = createIconPackPrefix ( zipFile ) ;
iconPacks . push ( {
name : zipFile ,
icons : loadedIcons ,
prefix ,
custom : false ,
} ) ;
console . log ( ` loaded icon pack ${ zipFile } ( ${ loadedIcons . length } ) ` ) ;
}
} ) ;
const getLoadedIconsFromZipFile = ( iconPackName , files ) => _ _awaiter ( void 0 , void 0 , void 0 , function * ( ) {
const loadedIcons = [ ] ;
const extraPath = getExtraPath ( iconPackName ) ;
for ( let j = 0 ; j < files . length ; j ++ ) {
// Checks if the icon pack has an extra path. Also ignores files which do not start
// with the extra path.
if ( extraPath && ! files [ j ] . name . startsWith ( extraPath ) ) {
continue ;
}
const file = yield getFileFromJSZipFile ( files [ j ] ) ;
const iconContent = yield file . text ( ) ;
const iconName = getNormalizedName ( file . name ) ;
const icon = generateIcon ( iconPackName , iconName , iconContent ) ;
if ( icon ) {
loadedIcons . push ( icon ) ;
}
}
return loadedIcons ;
} ) ;
const addIconToIconPack = ( iconPackName , iconName , iconContent ) => {
// Normalize the icon name to remove `-` or `_` in the name.
iconName = getNormalizedName ( iconName ) ;
const icon = generateIcon ( iconPackName , iconName , iconContent ) ;
if ( ! icon ) {
console . warn ( ` [iconize] icon could not be generated (icon: ${ iconName } , content: ${ iconContent } ). ` ) ;
return undefined ;
}
const iconPack = iconPacks . find ( ( iconPack ) => iconPack . name === iconPackName ) ;
if ( ! iconPack ) {
console . warn ( ` [iconize] iconpack with name " ${ iconPackName } " was not found. ` ) ;
return undefined ;
}
iconPack . icons . push ( icon ) ;
return icon ;
} ;
const removeIconFromIconPackDirectory = ( plugin , iconPackName , iconName ) => {
const iconPack = iconPacks . find ( ( iconPack ) => iconPack . name === iconPackName ) ;
// Checks if icon pack is custom-made.
if ( ! iconPack . custom ) {
return plugin . app . vault . adapter . rmdir ( ` ${ path } / ${ iconPackName } / ${ iconName } .svg ` , true ) ;
}
} ;
const extractIconToIconPack = ( plugin , icon , iconContent ) => _ _awaiter ( void 0 , void 0 , void 0 , function * ( ) {
const doesIconPackDirExist = yield plugin . app . vault . adapter . exists ( ` ${ path } / ${ icon . iconPackName } ` ) ;
if ( ! doesIconPackDirExist ) {
yield plugin . app . vault . adapter . mkdir ( ` ${ path } / ${ icon . iconPackName } ` ) ;
}
const doesIconFileExists = yield plugin . app . vault . adapter . exists ( ` ${ path } / ${ icon . iconPackName } / ${ icon . name } .svg ` ) ;
if ( ! doesIconFileExists ) {
yield createFile ( plugin , icon . iconPackName , ` ${ icon . name } .svg ` , iconContent ) ;
}
} ) ;
const getAllLoadedIconNames = ( ) => {
return iconPacks . reduce ( ( total , iconPack ) => {
total . push ( ... iconPack . icons ) ;
return total ;
} , [ ] ) ;
} ;
const registerIconPack = ( name , arrayBuffer ) => _ _awaiter ( void 0 , void 0 , void 0 , function * ( ) {
const files = yield readZipFile ( arrayBuffer ) ;
const loadedIcons = yield getLoadedIconsFromZipFile ( name , files ) ;
const prefix = createIconPackPrefix ( name ) ;
iconPacks . push ( { name , icons : loadedIcons , prefix , custom : false } ) ;
console . log ( ` loaded icon pack ${ name } ( ${ loadedIcons . length } ) ` ) ;
} ) ;
const doesIconExists = ( iconName ) => {
const icons = getAllLoadedIconNames ( ) ;
return ( icons . find ( ( icon ) => icon . name === iconName || icon . prefix + icon . name === iconName ) !== undefined ) ;
} ;
const getIconFromIconPack = ( iconPackName , iconPrefix , iconName ) => {
const foundIcon = preloadedIcons . find ( ( icon ) => icon . prefix . toLowerCase ( ) === iconPrefix . toLowerCase ( ) &&
icon . name . toLowerCase ( ) === iconName . toLowerCase ( ) ) ;
if ( foundIcon ) {
return foundIcon ;
}
const iconPack = iconPacks . find ( ( iconPack ) => iconPack . name === iconPackName ) ;
if ( ! iconPack ) {
return undefined ;
}
return iconPack . icons . find ( ( icon ) => getNormalizedName ( icon . name ) === iconName ) ;
} ;
const getSvgFromLoadedIcon = ( iconPrefix , iconName ) => {
let icon = '' ;
let foundIcon = preloadedIcons . find ( ( icon ) => icon . prefix . toLowerCase ( ) === iconPrefix . toLowerCase ( ) &&
icon . name . toLowerCase ( ) === iconName . toLowerCase ( ) ) ;
if ( ! foundIcon ) {
iconPacks . forEach ( ( iconPack ) => {
const icon = iconPack . icons . find ( ( icon ) => {
return ( icon . prefix . toLowerCase ( ) === iconPrefix . toLowerCase ( ) &&
getNormalizedName ( 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://cdn.jsdelivr.net/gh/jdecked/twemoji@15.1.0/assets/" , 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 = { "&" : "&" , "<" : "<" , ">" : ">" , "'" : "'" , '"' : """ } , re = / ( ? : \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c \ u d f f b \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 8 b \ u 2 0 0 d \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c [ \ u d f f b - \ u d f f f ] | \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c \ u d f f c \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 8 b \ u 2 0 0 d \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c [ \ u d f f b - \ u d f f f ] | \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c \ u d f f d \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 8 b \ u 2 0 0 d \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c [ \ u d f f b - \ u d f f f ] | \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c \ u d f f e \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 8 b \ u 2 0 0 d \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c [ \ u d f f b - \ u d f f f ] | \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c \ u d f f f \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 8 b \ u 2 0 0 d \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c [ \ u d f f b - \ u d f f f ] | \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c \ u d f f b \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 8 b \ u 2 0 0 d \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c [ \ u d f f b - \ u d f f f ] | \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c \ u d f f b \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 8 b \ u 2 0 0 d \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c [ \ u d f f b - \ u d f f f ] | \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c \ u d f f c \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 8 b \ u 2 0 0 d \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c [ \ u d f f b - \ u d f f f ] | \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c \ u d f f c \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 8 b \ u 2 0 0 d \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c [ \ u d f f b - \ u d f f f ] | \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c \ u d f f d \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 8 b \ u 2 0 0 d \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c [ \ u d f f b - \ u d f f f ] | \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c \ u d f f d \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 8 b \ u 2 0 0 d \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c [ \ u d f f b - \ u d f f f ] | \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c \ u d f f e \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 8 b \ u 2 0 0 d \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c [ \ u d f f b - \ u d f f f ] | \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c \ u d f f e \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 8 b \ u 2 0 0 d \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c [ \ u d f f b - \ u d f f f ] | \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c \ u d f f f \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 8 b \ u 2 0 0 d \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c [ \ u d f f b - \ u d f f f ] | \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c \ u d f f f \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 8 b \ u 2 0 0 d \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c [ \ u d f f b - \ u d f f f ] | \ u d 8 3 e \ u d d d 1 \ u d 8 3 c \ u d f f b \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 8 b \ u 2 0 0 d \ u d 8 3 e \ u d d d 1 \ u d 8 3 c [ \ u d f f c - \ u d f f f ] | \ u d 8 3 e \ u d d d 1 \ u d 8 3 c \ u d f f c \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 8 b \ u 2 0 0 d \ u d 8 3 e \ u d d d 1 \ u d 8 3 c [ \ u d f f b \ u d f f d - \ u d f f f ] | \ u d 8 3 e \ u d d d 1 \ u d 8 3 c \ u d f f d \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 8 b \ u 2 0 0 d \ u d 8 3 e \ u d d d 1 \ u d 8 3 c [ \ u d f f b \ u d f f c \ u d f f e \ u d f f f ] | \ u d 8 3 e \ u d d d 1 \ u d 8 3 c \ u d f f e \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 8 b \ u 2 0 0 d \ u d 8 3 e \ u d d d 1 \ u d 8 3 c [ \ u d f f b - \ u d f f d \ u d f f f ] | \ u d 8 3 e \ u d d d 1 \ u d 8 3 c \ u d f f f \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 8 b \ u 2 0 0 d \ u d 8 3 e \ u d d d 1 \ u d 8 3 c [ \ u d f f b - \ u d f f e ] | \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c \ u d f f b \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c [ \ u d f f b - \ u d f f f ] | \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c \ u d f f b \ u 2 0 0 d \ u d 8 3 e \ u d d 1 d \ u 2 0 0 d \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c [ \ u d f f c - \ u d f f f ] | \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c \ u d f f c \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c [ \ u d f f b - \ u d f f f ] | \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c \ u d f f c \ u 2 0 0 d \ u d 8 3 e \ u d d 1 d \ u 2 0 0 d \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c [ \ u d f f b \ u d f f d - \ u d f f f ] | \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c \ u d f f d \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c [ \ u d f f b - \ u d f f f ] | \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c \ u d f f d \ u 2 0 0 d \ u d 8 3 e \ u d d 1 d \ u 2 0 0 d \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c [ \ u d f f b \ u d f f c \ u d f f e \ u d f f f ] | \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c \ u d f f e \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c [ \ u d f f b - \ u d f f f ] | \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c \ u d f f e \ u 2 0 0 d \ u d 8 3 e \ u d d 1 d \ u 2 0 0 d \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c [ \ u d f f b - \ u d f f d \ u d f f f ] | \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c \ u d f f f \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c [ \ u d f f b - \ u d f f f ] | \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c \ u d f f f \ u 2 0 0 d \ u d 8 3 e \ u d d 1 d \ u 2 0 0 d \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c [ \ u d f f b - \ u d f f e ] | \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c \ u d f f b \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c [ \ u d f f b - \ u d f f f ] | \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c \ u d f f b \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c [ \ u d f f b - \ u d f f f ] | \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c \ u d f f b \ u 2 0 0 d \ u d 8 3 e \ u d d 1 d \ u 2 0 0 d \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c [ \ u d f f c - \ u d f f f ] | \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c \ u d f f b \ u 2 0 0 d \ u d 8 3 e \ u d d 1 d \ u 2 0 0 d \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c [ \ u d f f c - \ u d f f f ] | \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c \ u d f f c \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c [ \ u d f f b - \ u d f f f ] | \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c \ u d f f c \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c [ \ u d f f b - \ u d f f f ] | \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c \ u d f f c \ u 2 0 0 d \ u d 8 3 e \ u d d 1 d \ u 2 0 0 d \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c [ \ u d f f b \ u d f f d - \ u d f f f ] | \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c \ u d f f c \ u 2 0 0 d \ u d 8 3 e \ u d d 1 d \ u 2 0 0 d \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c [ \ u d f f b \ u d f f d - \ u d f f f ] | \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c \ u d f f d \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0 0 d \ u d 8 3 d \ u d c 6 8 \ u d 8 3 c [ \ u d f f b - \ u d f f f ] | \ u d 8 3 d \ u d c 6 9 \ u d 8 3 c \ u d f f d \ u 2 0 0 d \ u 2 7 6 4 \ u f e 0 f \ u 2 0
const getRegex = ( ) => {
return new RegExp ( / [ # * 0 - 9 ] \ u F E 0 F ? \ u 2 0 E 3 | [ \ x A 9 \ x A E \ u 2 0 3 C \ u 2 0 4 9 \ u 2 1 2 2 \ u 2 1 3 9 \ u 2 1 9 4 - \ u 2 1 9 9 \ u 2 1 A 9 \ u 2 1 A A \ u 2 3 1 A \ u 2 3 1 B \ u 2 3 2 8 \ u 2 3 C F \ u 2 3 E D - \ u 2 3 E F \ u 2 3 F 1 \ u 2 3 F 2 \ u 2 3 F 8 - \ u 2 3 F A \ u 2 4 C 2 \ u 2 5 A A \ u 2 5 A B \ u 2 5 B 6 \ u 2 5 C 0 \ u 2 5 F B \ u 2 5 F C \ u 2 5 F E \ u 2 6 0 0 - \ u 2 6 0 4 \ u 2 6 0 E \ u 2 6 1 1 \ u 2 6 1 4 \ u 2 6 1 5 \ u 2 6 1 8 \ u 2 6 2 0 \ u 2 6 2 2 \ u 2 6 2 3 \ u 2 6 2 6 \ u 2 6 2 A \ u 2 6 2 E \ u 2 6 2 F \ u 2 6 3 8 - \ u 2 6 3 A \ u 2 6 4 0 \ u 2 6 4 2 \ u 2 6 4 8 - \ u 2 6 5 3 \ u 2 6 5 F \ u 2 6 6 0 \ u 2 6 6 3 \ u 2 6 6 5 \ u 2 6 6 6 \ u 2 6 6 8 \ u 2 6 7 B \ u 2 6 7 E \ u 2 6 7 F \ u 2 6 9 2 \ u 2 6 9 4 - \ u 2 6 9 7 \ u 2 6 9 9 \ u 2 6 9 B \ u 2 6 9 C \ u 2 6 A 0 \ u 2 6 A 7 \ u 2 6 A A \ u 2 6 B 0 \ u 2 6 B 1 \ u 2 6 B D \ u 2 6 B E \ u 2 6 C 4 \ u 2 6 C 8 \ u 2 6 C F \ u 2 6 D 1 \ u 2 6 E 9 \ u 2 6 F 0 - \ u 2 6 F 5 \ u 2 6 F 7 \ u 2 6 F 8 \ u 2 6 F A \ u 2 7 0 2 \ u 2 7 0 8 \ u 2 7 0 9 \ u 2 7 0 F \ u 2 7 1 2 \ u 2 7 1 4 \ u 2 7 1 6 \ u 2 7 1 D \ u 2 7 2 1 \ u 2 7 3 3 \ u 2 7 3 4 \ u 2 7 4 4 \ u 2 7 4 7 \ u 2 7 5 7 \ u 2 7 6 3 \ u 2 7 A 1 \ u 2 9 3 4 \ u 2 9 3 5 \ u 2 B 0 5 - \ u 2 B 0 7 \ u 2 B 1 B \ u 2 B 1 C \ u 2 B 5 5 \ u 3 0 3 0 \ u 3 0 3 D \ u 3 2 9 7 \ u 3 2 9 9 ] \ u F E 0 F ? | [ \ u 2 6 1 D \ u 2 7 0 C \ u 2 7 0 D ] ( ? : \ u F E 0 F | \ u D 8 3 C [ \ u D F F B - \ u D F F F ] ) ? | [ \ u 2 7 0 A \ u 2 7 0 B ] ( ? : \ u D 8 3 C [ \ u D F F B - \ u D F F F ] ) ? | [ \ u 2 3 E 9 - \ u 2 3 E C \ u 2 3 F 0 \ u 2 3 F 3 \ u 2 5 F D \ u 2 6 9 3 \ u 2 6 A 1 \ u 2 6 A B \ u 2 6 C 5 \ u 2 6 C E \ u 2 6 D 4 \ u 2 6 E A \ u 2 6 F D \ u 2 7 0 5 \ u 2 7 2 8 \ u 2 7 4 C \ u 2 7 4 E \ u 2 7 5 3 - \ u 2 7 5 5 \ u 2 7 9 5 - \ u 2 7 9 7 \ u 2 7 B 0 \ u 2 7 B F \ u 2 B 5 0 ] | \ u 2 6 D 3 \ u F E 0 F ? ( ? : \ u 2 0 0 D \ u D 8 3 D \ u D C A 5 ) ? | \ u 2 6 F 9 ( ? : \ u F E 0 F | \ u D 8 3 C [ \ u D F F B - \ u D F F F ] ) ? ( ? : \ u 2 0 0 D [ \ u 2 6 4 0 \ u 2 6 4 2 ] \ u F E 0 F ? ) ? | \ u 2 7 6 4 \ u F E 0 F ? ( ? : \ u 2 0 0 D ( ? : \ u D 8 3 D \ u D D 2 5 | \ u D 8 3 E \ u D E 7 9 ) ) ? | \ u D 8 3 C ( ? : [ \ u D C 0 4 \ u D D 7 0 \ u D D 7 1 \ u D D 7 E \ u D D 7 F \ u D E 0 2 \ u D E 3 7 \ u D F 2 1 \ u D F 2 4 - \ u D F 2 C \ u D F 3 6 \ u D F 7 D \ u D F 9 6 \ u D F 9 7 \ u D F 9 9 - \ u D F 9 B \ u D F 9 E \ u D F 9 F \ u D F C D \ u D F C E \ u D F D 4 - \ u D F D F \ u D F F 5 \ u D F F 7 ] \ u F E 0 F ? | [ \ u D F 8 5 \ u D F C 2 \ u D F C 7 ] ( ? : \ u D 8 3 C [ \ u D F F B - \ u D F F F ] ) ? | [ \ u D F C 4 \ u D F C A ] ( ? : \ u D 8 3 C [ \ u D F F B - \ u D F F F ] ) ? ( ? : \ u 2 0 0 D [ \ u 2 6 4 0 \ u 2 6 4 2 ] \ u F E 0 F ? ) ? | [ \ u D F C B \ u D F C C ] ( ? : \ u F E 0 F | \ u D 8 3 C [ \ u D F F B - \ u D F F F ] ) ? ( ? : \ u 2 0 0 D [ \ u 2 6 4 0 \ u 2 6 4 2 ] \ u F E 0 F ? ) ? | [ \ u D C C F \ u D D 8 E \ u D D 9 1 - \ u D D 9 A \ u D E 0 1 \ u D E 1 A \ u D E 2 F \ u D E 3 2 - \ u D E 3 6 \ u D E 3 8 - \ u D E 3 A \ u D E 5 0 \ u D E 5 1 \ u D F 0 0 - \ u D F 2 0 \ u D F 2 D - \ u D F 3 5 \ u D F 3 7 - \ u D F 4 3 \ u D F 4 5 - \ u D F 4 A \ u D F 4 C - \ u D F 7 C \ u D F 7 E - \ u D F 8 4 \ u D F 8 6 - \ u D F 9 3 \ u D F A 0 - \ u D F C 1 \ u D F C 5 \ u D F C 6 \ u D F C 8 \ u D F C 9 \ u D F C F - \ u D F D 3 \ u D F E 0 - \ u D F F 0 \ u D F F 8 - \ u D F F F ] | \ u D D E 6 \ u D 8 3 C [ \ u D D E 8 - \ u D D E C \ u D D E E \ u D D F 1 \ u D D F 2 \ u D D F 4 \ u D D F 6 - \ u D D F A \ u D D F C \ u D D F D \ u D D F F ] | \ u D D E 7 \ u D 8 3 C [ \ u D D E 6 \ u D D E 7 \ u D D E 9 - \ u D D E F \ u D D F 1 - \ u D D F 4 \ u D D F 6 - \ u D D F 9 \ u D D F B \ u D D F C \ u D D F E \ u D D F F ] | \ u D D E 8 \ u D 8 3 C [ \ u D D E 6 \ u D D E 8 \ u D D E 9 \ u D D E B - \ u D D E E \ u D D F 0 - \ u D D F 5 \ u D D F 7 \ u D D F A - \ u D D F F ] | \ u D D E 9 \ u D 8 3 C [ \ u D D E A \ u D D E C \ u D D E F \ u D D F 0 \ u D D F 2 \ u D D F 4 \ u D D F F ] | \ u D D E A \ u D 8 3 C [ \ u D D E 6 \ u D D E 8 \ u D D E A \ u D D E C \ u D D E D \ u D D F 7 - \ u D D F A ] | \ u D D E B \ u D 8 3 C [ \ u D D E E - \ u D D F 0 \ u D D F 2 \ u D D F 4 \ u D D F 7 ] | \ u D D E C \ u D 8 3 C [ \ u D D E 6 \ u D D E 7 \ u D D E 9 - \ u D D E E \ u D D F 1 - \ u D D F 3 \ u D D F 5 - \ u D D F A \ u D D F C \ u D D F E ] | \ u D D E D \ u D 8 3 C [ \ u D D F 0 \ u D D F 2 \ u D D F 3 \ u D D F 7 \ u D D F 9 \ u D D F A ] | \ u D D E E \ u D 8 3 C [ \ u D D E 8 - \ u D D E A \ u D D F 1 - \ u D D F 4 \ u D D F 6 - \ u D D F 9 ] | \ u D D E F \ u D 8 3 C [ \ u D D E A \ u D D F 2 \ u D D F 4 \ u D D F 5 ] | \ u D D F 0 \ u D 8 3 C [ \ u D D E A \ u D D E C - \ u D D E E \ u D D F 2 \ u D D F 3 \ u D D F 5 \ u D D F 7 \ u D D F C \ u D D F E \ u D D F F ] | \ u D D F 1 \ u D 8 3 C [ \ u D D E 6 - \ u D D E 8 \ u D D E E \ u D D F 0 \ u D D F 7 - \ u D D F B \ u D D F E ] | \ u D D F 2 \ u D 8 3 C [ \ u D D E 6 \ u D D E 8 - \ u D D E D \ u D D F 0 - \ u D D F F ] | \ u D D F 3 \ u D 8 3 C [ \ u D D E 6 \ u D D E 8 \ u D D E A - \ u D D E C \ u D D E E \ u D D F 1 \ u D D F 4 \ u D D F 5 \ u D D F 7 \ u D D F A \ u D D F F ] | \ u D D F 4 \ u D 8 3 C \ u D D F 2 | \ u D D F 5 \ u D 8 3 C [ \ u D D E 6 \ u D D E A - \ u D D E D \ u D D F 0 - \ u D D F 3 \ u D D F 7 - \ u D D F 9 \ u D D F C \ u D D F E ] | \ u D D F 6 \ u D 8 3 C \ u D D E 6 | \ u D D F 7 \ u D 8 3 C [ \ u D D E A \ u D D F 4 \ u D D F 8 \ u D D F A \ u D D F C ] | \ u D D F 8 \ u D 8 3 C [ \ u D D E 6 - \ u D D E A \ u D D E C - \ u D D F 4 \ u D D F 7 - \ u D D F 9 \ u D D F B \ u D D F D - \ u D D F F ] | \ u D D F 9 \ u D 8 3 C [ \ u D D E 6 \ u D D E 8 \ u D D E 9 \ u D D E B - \ u D D E D \ u D D E F - \ u D D F 4 \ u D D F 7 \ u D D F 9 \ u D D F B \ u D D F C \ u D D F F ] | \ u D D F A \ u D 8 3 C [ \ u D D E 6 \ u D D E C \ u D D F 2 \ u D D F 3 \ u D D F 8 \ u D D F E \ u D D F F ] | \ u D D F B \ u D 8 3 C [ \ u D D E 6 \ u D D E 8 \ u D D E A \ u D D E C \ u D D E E \ u D D F 3 \ u D D F A ] | \ u D D F C \ u D 8 3 C [ \ u D D E B \ u D D F 8 ] | \ u D D F D \ u D 8 3 C \ u D D F 0 | \ u D D F E \ u D 8 3 C [ \ u D D E A \ u D D F 9 ] | \ u D D F F \ u D 8 3 C [ \ u D D E 6 \ u D D F 2 \ u D D F C ] | \ u D F 4 4 ( ? : \ u 2 0 0 D \ u D 8 3 D \ u D F E B ) ? | \ u D F 4 B ( ? : \ u 2 0 0 D \ u D 8 3 D \ u D F E 9 ) ? | \ u D F C 3 ( ? : \ u D 8 3 C [ \ u D F F B - \ u D F F F ] ) ? ( ? : \ u 2 0 0 D ( ? : [ \ u 2 6 4 0 \ u 2 6 4 2 ] \ u F E 0 F ? ( ? : \ u 2 0 0 D \ u 2 7 A 1 \ u F E 0 F ? ) ? | \ u 2 7 A 1 \ u F E 0 F ? ) ) ? | \ u D F F 3 \ u F E 0 F ? ( ? : \ u 2 0 0 D ( ? : \ u 2 6 A 7 \ u F E 0 F ? | \ u D 8 3 C \ u D F 0 8 ) ) ? | \ u D F F 4 ( ? : \ u 2 0 0 D \ u 2 6 2 0 \ u F E 0 F ? | \ u D B 4 0 \ u D C 6 7 \ u D B 4 0 \ u D C 6 2 \ u D B 4 0 ( ? : \ u D C 6 5 \ u D B 4 0 \ u D C 6 E \ u D B 4 0 \ u D C 6 7 | \ u D C 7 3 \ u D B 4 0 \ u D C 6 3 \ u D B 4 0 \ u D C 7 4 | \ u D C 7 7 \ u D B 4 0 \ u D C 6 C \ u D B 4 0 \ u D C 7 3 ) \ u D B 4 0 \ u D C 7 F ) ? ) | \ u D 8 3 D ( ? : [ \ u D C 3 F \ u D C F D \ u D D 4 9 \ u D D 4 A \ u D D 6 F \ u D D 7 0 \ u D D 7 3 \ u D D 7 6 - \ u D D 7 9 \ u D D 8 7 \ u D D 8 A - \ u D D 8 D \ u D D A 5 \ u D D A 8 \ u D D B 1 \ u D D B 2 \ u D D B C \ u D D C 2 - \ u D D C 4 \ u D D D 1 - \ u D D D 3 \ u D D D C - \ u D D D E \ u D D E 1 \ u D D E 3 \ u D D E 8 \ u D D E F \ u D D F 3 \ u D D F A \ u D E C B \ u D E C D - \ u D E C F \ u D E E 0 - \ u D E E 5 \ u D E E 9 \ u D E F 0 \ u D E F 3 ] \ u F E 0 F ? | [ \ u D C 4 2 \ u D C 4 3 \ u D C 4 6 - \ u D C 5 0 \ u D C 6 6 \ u D C 6 7 \ u D C 6 B - \ u D C 6 D \ u D C 7 2 \ u D C 7 4 - \ u D C 7 6 \ u D C 7 8 \ u D C 7 C \ u D C 8 3 \ u D C 8 5 \ u D C 8 F \ u D C 9 1 \ u D C A A \ u D D 7 A \ u D D 9 5 \ u D D 9 6 \ u D E 4 C \ u D E 4 F \ u D E C 0 \ u D E C C ] ( ? : \ u D 8 3 C [ \ u D F F B - \ u D F F F ] ) ? | [ \ u D C 6 E \ u D C 7 0 \ u D C 7 1 \ u D C 7 3 \ u D C 7 7 \ u D C 8 1 \ u D C 8 2 \ u D C 8 6 \ u D C 8 7 \ u D E 4 5 - \ u D E 4 7 \ u D E 4 B \ u D E 4 D \ u D E 4 E \ u D E A 3 \ u D E B 4 \ u D E B 5 ] ( ? : \ u D
} ;
const shortNames = {
'😀' : '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 o’ clock' ,
'🕧' : 'twelve-thirty' ,
'🕐' : 'one o’ clock' ,
'🕜' : 'one-thirty' ,
'🕑' : 'two o’ clock' ,
'🕝' : 'two-thirty' ,
'🕒' : 'three o’ clock' ,
'🕞' : 'three-thirty' ,
'🕓' : 'four o’ clock' ,
'🕟' : 'four-thirty' ,
'🕔' : 'five o’ clock' ,
'🕠' : 'five-thirty' ,
'🕕' : 'six o’ clock' ,
'🕡' : 'six-thirty' ,
'🕖' : 'seven o’ clock' ,
'🕢' : 'seven-thirty' ,
'🕗' : 'eight o’ clock' ,
'🕣' : 'eight-thirty' ,
'🕘' : 'nine o’ clock' ,
'🕤' : 'nine-thirty' ,
'🕙' : 'ten o’ clock' ,
'🕥' : 'ten-thirty' ,
'🕚' : 'eleven o’ clock' ,
'🕦' : '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' ,
'👚' : 'woman’ s clothes' ,
'👛' : 'purse' ,
'👜' : 'handbag' ,
'👝' : 'clutch bag' ,
'🛍' : 'shopping bags' ,
'🎒' : 'backpack' ,
'🩴' : 'thong sandal' ,
'👞' : 'man’ s shoe' ,
'👟' : 'running shoe' ,
'🥾' : 'hiking boot' ,
'🥿' : 'flat shoe' ,
'👠' : 'high-heeled shoe' ,
'👡' : 'woman’ s sandal' ,
'🩰' : 'ballet shoes' ,
'👢' : 'woman’ s boot' ,
'👑' : 'crown' ,
'👒' : 'woman’ s hat' ,
'🎩' : 'top hat' ,
'🎓' : 'graduation cap' ,
'🧢' : 'billed cap' ,
'🪖' : 'military helmet' ,
'⛑' : 'rescue worker’ s 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' ,
'🚹' : 'men’ s room' ,
'🚺' : 'women’ s 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 d’ Ivoire' ,
'🇨🇰' : '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' ,
} ;
const isEmoji = ( str ) => {
const regex = getRegex ( ) ;
const emojiMatches = str . match ( regex ) ;
const emojiString = emojiMatches ? emojiMatches . join ( '' ) : '' ;
return ! /\d/ . test ( str ) && emojiString === str ;
} ;
const parseEmoji = ( style , str , size = 16 ) => {
switch ( style ) {
case 'twemoji' :
return twemoji . parse ( str , {
folder : 'svg' ,
ext : '.svg' ,
attributes : ( ) => ( {
width : ` ${ size } px ` ,
height : ` ${ size } px ` ,
} ) ,
} ) ;
case 'native' :
return str ;
default :
return null ;
}
} ;
/ * *
* Gets the shortcode for a given emoji by the name of the emoji . This function replaces
* spaces with underscores and removes colons .
* @ param key String to replace with shortcode .
* @ returns String with shortcode , or ` undefined ` if no shortcode exists .
* /
const getShortcode = ( key ) => {
var _a ;
// Removable of colons is necessary for the flag shortcodes.
return ( _a = shortNames [ key ] ) === null || _a === void 0 ? void 0 : _a . replace ( /\s/g , '_' ) . replace ( /:/g , '' ) . toLowerCase ( ) ;
} ;
var emoji = {
shortNames ,
isEmoji ,
getShortcode ,
parseEmoji ,
getRegex ,
} ;
// Default obsidian file icon.
const DEFAULT _FILE _ICON = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-file"><path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path><polyline points="14 2 14 8 20 8"></polyline></svg>' ;
// Default obsidian folder icon.
const DEFAULT _FOLDER _ICON = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-folder"><path d="M4 20h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.93a2 2 0 0 1-1.66-.9l-.82-1.2A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13c0 1.1.9 2 2 2Z"></path></svg>' ;
/ * *
* Tries to read the file synchronously .
* @ param file File that will be read .
* @ returns A promise that will resolve to a string which is the content of the file .
* /
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 ;
} ) ;
/ * *
* Gets all the currently opened files by getting the markdown leaves and then checking
* for the ` file ` property in the view . This also returns the leaf of the file .
* @ param plugin Instance of the IconFolderPlugin .
* @ returns An array of { @ link FileWithLeaf } objects .
* /
const getAllOpenedFiles = ( plugin ) => {
return plugin . app . workspace
. getLeavesOfType ( 'markdown' )
. reduce ( ( prev , curr ) => {
const file = curr . view . file ;
if ( file ) {
prev . push ( Object . assign ( Object . assign ( { } , file ) , { leaf : curr , pinned : false } ) ) ;
}
return prev ;
} , [ ] ) ;
} ;
/ * *
* Gets the file item title element by either accessing ` titleEl ` or ` selfEl ` .
* @ param fileItem FileItem which will be used to retrieve the title element from .
* @ returns HTMLElement which is the title element .
* /
const getFileItemTitleEl = ( fileItem ) => {
var _a ;
return ( _a = fileItem . titleEl ) !== null && _a !== void 0 ? _a : fileItem . selfEl ;
} ;
/ * *
* Gets the file item inner title element by either accessing ` titleInnerEl ` or ` innerEl ` .
* @ param fileItem FileItem which will be used to retrieve the inner title element from .
* @ returns HTMLElement which is the inner title element .
* /
const getFileItemInnerTitleEl = ( fileItem ) => {
var _a ;
return ( _a = fileItem . titleInnerEl ) !== null && _a !== void 0 ? _a : fileItem . innerEl ;
} ;
/ * *
* A utility function which will add the icon to the icon pack and then extract the icon
* to the icon pack .
* @ param plugin IconFolderPlugin that will be used for extracting the icon .
* @ param iconNameWithPrefix String that will be used to add the icon to the icon pack .
* /
const saveIconToIconPack = ( plugin , iconNameWithPrefix ) => {
const iconNextIdentifier = nextIdentifier ( iconNameWithPrefix ) ;
const iconName = iconNameWithPrefix . substring ( iconNextIdentifier ) ;
const iconPrefix = iconNameWithPrefix . substring ( 0 , iconNextIdentifier ) ;
const possibleIcon = getSvgFromLoadedIcon ( iconPrefix , iconName ) ;
if ( ! possibleIcon ) {
throw new Error ( ` Icon ${ iconNameWithPrefix } could not be found. ` ) ;
}
const iconPackName = getIconPackNameByPrefix ( iconPrefix ) ;
const icon = getIconFromIconPack ( iconPackName , iconPrefix , iconName ) ;
extractIconToIconPack ( plugin , icon , possibleIcon ) ;
} ;
/ * *
* A utility function which will remove the icon from the icon pack by removing the icon
* file from the icon pack directory .
* @ param plugin IconFolderPlugin that will be used for removing the icon .
* @ param iconNameWithPrefix String that will be used to remove the icon from the icon pack .
* /
const removeIconFromIconPack = ( plugin , iconNameWithPrefix ) => {
const identifier = nextIdentifier ( iconNameWithPrefix ) ;
const prefix = iconNameWithPrefix . substring ( 0 , identifier ) ;
const iconName = iconNameWithPrefix . substring ( identifier ) ;
const iconPackName = getIconPackNameByPrefix ( prefix ) ;
const duplicatedIcon = plugin . getDataPathByValue ( iconNameWithPrefix ) ;
if ( ! duplicatedIcon ) {
removeIconFromIconPackDirectory ( plugin , iconPackName , iconName ) ;
}
} ;
/ * *
* A utility function which will convert a string to a hexadecimal color .
* @ param str String that will be converted to a hexadecimal color .
* @ returns A string which is the hexadecimal color .
* /
const stringToHex = ( str ) => {
const validHex = str . replace ( /[^0-9a-fA-F]/g , '' ) ;
const hex = validHex . padStart ( 6 , '0' ) . substring ( 0 , 6 ) ;
return ` # ${ hex } ` ;
} ;
/ * *
* A utility function which will check if a string is a hexadecimal color .
* @ param str String that will be checked if it is a hexadecimal color .
* @ param includeHash Boolean which will include the hash in the check .
* @ returns A boolean which is true if the string is a hexadecimal color .
* /
const isHexadecimal = ( str , includeHash = false ) => {
const regex = new RegExp ( ` ^ ${ includeHash ? '#' : '' } [0-9A-Fa-f]{1,6} $ ` ) ;
return regex . test ( str ) ;
} ;
// This library file does not include any other dependency and is a standalone file that
/ * *
* Sets the margin for a specific node .
* @ param el Node where the margin will be set .
* @ param margin Margin that will be applied to the node .
* @ returns The modified node with the applied margin .
* /
const setMargin = ( el , margin ) => {
el . style . margin = ` ${ margin . top } px ${ margin . right } px ${ margin . bottom } px ${ margin . left } px ` ;
return el ;
} ;
/ * *
* Applies all stylings to the specified svg icon string and applies styling to the node
* ( container ) . The styling to the specified element is only modified when it is an emoji
* or extra margin is defined in the settings .
* @ param plugin Instance of the IconFolderPlugin .
* @ param iconString SVG that will be used to apply the svg styles to .
* @ param el Node for manipulating the style .
* @ returns Icon svg string with the manipulate style attributes .
* /
const applyAll = ( plugin , iconString , container ) => {
iconString = svg . setFontSize ( iconString , plugin . getSettings ( ) . fontSize ) ;
container . style . color = plugin . getSettings ( ) . iconColor ;
iconString = svg . colorize ( iconString , plugin . getSettings ( ) . iconColor ) ;
// Sets the margin of an element.
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 ) {
setMargin ( container , normalizedMargin ) ;
}
if ( emoji . isEmoji ( iconString ) ) {
container . style . fontSize = ` ${ plugin . getSettings ( ) . fontSize } px ` ;
container . style . lineHeight = ` ${ plugin . getSettings ( ) . fontSize } px ` ;
}
return iconString ;
} ;
/ * *
* Refreshes all the styles of all the applied icons where a ` .iconize-icon `
* class is defined . This function only modifies the styling of the node .
* @ param plugin Instance of the IconFolderPlugin .
* @ param applyStyles Function that is getting called when the icon node is found and
* typically applies all the styles to the icon .
* /
const refreshIconNodes = ( plugin , applyStyles = applyAll ) => {
const fileExplorers = plugin . app . workspace . getLeavesOfType ( 'file-explorer' ) ;
for ( const fileExplorer of fileExplorers ) {
Object . keys ( plugin . getData ( ) ) . forEach ( ( path ) => {
const fileItem = fileExplorer . view . fileItems [ path ] ;
if ( fileItem ) {
const titleEl = getFileItemTitleEl ( fileItem ) ;
const iconNode = titleEl . querySelector ( '.iconize-icon' ) ;
if ( iconNode ) {
const pathValue = plugin . getData ( ) [ path ] ;
const hasIndividualColor = typeof pathValue === 'object' && pathValue . iconColor ;
if ( hasIndividualColor ) {
return ;
}
iconNode . innerHTML = applyStyles ( plugin , iconNode . innerHTML , iconNode ) ;
}
}
} ) ;
}
} ;
var style = {
applyAll ,
setMargin ,
refreshIconNodes ,
} ;
/ * *
* Removes the ` iconize-icon ` icon node from the provided HTMLElement .
* @ param el HTMLElement from which the icon node will be removed .
* /
const removeIconInNode = ( el ) => {
const iconNode = el . querySelector ( '.iconize-icon' ) ;
if ( ! iconNode ) {
return ;
}
iconNode . remove ( ) ;
} ;
/ * *
* Removes the 'iconize-icon' icon node from the HTMLElement corresponding
* to the specified file path .
* @ param path File path for which the icon node will be removed .
* /
const removeIconInPath = ( path , options ) => {
var _a ;
const node = ( _a = options === null || options === void 0 ? void 0 : options . container ) !== null && _a !== void 0 ? _a : document . querySelector ( ` [data-path=" ${ path } "] ` ) ;
if ( ! node ) {
console . error ( 'element with data path not found' , path ) ;
return ;
}
removeIconInNode ( node ) ;
} ;
/ * *
* Sets an icon or emoji for an HTMLElement based on the specified icon name and color .
* The function manipulates the specified node inline .
* @ param plugin Instance of the IconFolderPlugin .
* @ param iconName Name of the icon or emoji to add .
* @ param node HTMLElement to which the icon or emoji will be added .
* @ param color Optional color of the icon to add .
* /
const setIconForNode = ( plugin , iconName , node , color ) => {
var _a ;
// Gets the possible icon based on the icon name.
const iconNextIdentifier = nextIdentifier ( iconName ) ;
const possibleIcon = getSvgFromLoadedIcon ( iconName . substring ( 0 , iconNextIdentifier ) , iconName . substring ( iconNextIdentifier ) ) ;
if ( possibleIcon ) {
// The icon is possibly not an emoji.
let iconContent = style . applyAll ( plugin , possibleIcon , node ) ;
if ( color ) {
node . style . color = color ;
iconContent = svg . colorize ( iconContent , color ) ;
}
node . innerHTML = iconContent ;
}
else {
const parsedEmoji = ( _a = emoji . parseEmoji ( plugin . getSettings ( ) . emojiStyle , iconName ) ) !== null && _a !== void 0 ? _a : iconName ;
node . innerHTML = style . applyAll ( plugin , parsedEmoji , node ) ;
}
node . setAttribute ( 'title' , iconName ) ;
} ;
/ * *
* Creates an icon node for the specified path and inserts it to the DOM .
* @ param plugin Instance of the IconFolderPlugin .
* @ param path Path for which the icon node will be created .
* @ param iconName Name of the icon or emoji to add .
* @ param color Optional color of the icon to add .
* /
const createIconNode = ( plugin , path , iconName , options ) => {
var _a ;
// Get the container from the provided options or try to find the node that has the
// path from the document itself.
const node = ( _a = options === null || options === void 0 ? void 0 : options . container ) !== null && _a !== void 0 ? _a : document . querySelector ( ` [data-path=" ${ path } "] ` ) ;
if ( ! node ) {
console . error ( 'element with data path not found' , path ) ;
return ;
}
// Get the folder or file title node.
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 ;
}
}
let iconNode = node . querySelector ( '.iconize-icon' ) ;
// If the icon is already set in the path, we do not need to create a new div element.
if ( iconNode ) {
setIconForNode ( plugin , iconName , iconNode , options === null || options === void 0 ? void 0 : options . color ) ;
}
else {
// Creates a new icon node and inserts it to the DOM.
iconNode = document . createElement ( 'div' ) ;
iconNode . setAttribute ( config . ICON _ATTRIBUTE _NAME , iconName ) ;
iconNode . classList . add ( 'iconize-icon' ) ;
setIconForNode ( plugin , iconName , iconNode , options === null || options === void 0 ? void 0 : options . color ) ;
node . insertBefore ( iconNode , titleNode ) ;
}
} ;
/ * *
* Checks if the element has an icon node by checking if the element has a child with the
* class ` iconize-icon ` .
* @ param element HTMLElement which will be checked if it has an icon .
* @ returns Boolean whether the element has an icon node or not .
* /
const doesElementHasIconNode = ( element ) => {
return element . querySelector ( '.iconize-icon' ) !== null ;
} ;
/ * *
* Gets the icon name of the element if it has an icon node .
* @ param element HTMLElement parent which includes a node with the icon .
* @ returns String with the icon name if the element has an icon , ` undefined ` otherwise .
* /
const getIconFromElement = ( element ) => {
const iconNode = element . querySelector ( '.iconize-icon' ) ;
const existingIcon = iconNode === null || iconNode === void 0 ? void 0 : iconNode . getAttribute ( config . ICON _ATTRIBUTE _NAME ) ;
return existingIcon ;
} ;
const getIconNodeFromPath = ( path ) => {
var _a ;
return ( _a = document
. querySelector ( ` [data-path=" ${ path } "] ` ) ) === null || _a === void 0 ? void 0 : _a . querySelector ( '[data-icon]' ) ;
} ;
var dom = {
setIconForNode ,
createIconNode ,
doesElementHasIconNode ,
getIconFromElement ,
getIconNodeFromPath ,
removeIconInNode ,
removeIconInPath ,
} ;
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 = new Set ( pluginRecentltyUsedItems . reverse ( ) . filter ( ( iconName ) => {
return doesIconExists ( iconName ) || emoji . isEmoji ( iconName ) ;
} ) ) ;
this . resultContainerEl . classList . add ( 'iconize-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 ) => {
if ( this . plugin . isSomeEmojiStyleActive ( ) && emoji . isEmoji ( iconName ) ) {
iconKeys . push ( {
name : emoji . shortNames [ iconName ] ,
prefix : 'Emoji' ,
displayName : iconName ,
iconPackName : null ,
} ) ;
return ;
}
const nextLetter = nextIdentifier ( iconName ) ;
const iconPrefix = iconName . substring ( 0 , nextLetter ) ;
const iconPackName = getIconPackNameByPrefix ( iconPrefix ) ;
iconKeys . push ( {
name : iconName . substring ( nextLetter ) ,
prefix : iconPrefix ,
displayName : iconName ,
iconPackName : iconPackName ,
} ) ;
} ) ;
}
for ( const icon of getAllLoadedIconNames ( ) ) {
iconKeys . push ( {
name : icon . name ,
prefix : icon . prefix ,
displayName : icon . prefix + icon . name ,
iconPackName : icon . iconPackName ,
} ) ;
}
if ( this . plugin . isSomeEmojiStyleActive ( ) ) {
Object . entries ( emoji . shortNames ) . forEach ( ( [ unicode , shortName ] ) => {
iconKeys . push ( {
name : shortName ,
prefix : 'Emoji' ,
displayName : unicode ,
iconPackName : null ,
} ) ;
iconKeys . push ( {
name : unicode ,
prefix : 'Emoji' ,
displayName : unicode ,
iconPackName : null ,
} ) ;
} ) ;
}
return iconKeys ;
}
onChooseItem ( item ) {
var _a ;
const iconNameWithPrefix = typeof item === 'object' ? item . displayName : item ;
dom . createIconNode ( this . plugin , this . path , iconNameWithPrefix ) ;
( _a = this . onSelect ) === null || _a === void 0 ? void 0 : _a . call ( this , iconNameWithPrefix ) ;
this . plugin . addFolderIcon ( this . path , item ) ;
// Extracts the icon file to the icon pack.
if ( typeof item === 'object' && ! emoji . isEmoji ( iconNameWithPrefix ) ) {
saveIconToIconPack ( this . plugin , iconNameWithPrefix ) ;
}
this . plugin . notifyPlugins ( ) ;
}
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 . size !== 0 && this . inputEl . value . length === 0 ) {
if ( this . renderIndex === 0 ) {
const subheadline = this . resultContainerEl . createDiv ( ) ;
subheadline . classList . add ( 'iconize-subheadline' ) ;
subheadline . innerText = 'Recently used Icons:' ;
this . resultContainerEl . prepend ( subheadline ) ;
}
else if ( this . renderIndex === this . recentlyUsedItems . size - 1 ) {
const subheadline = this . resultContainerEl . createDiv ( ) ;
subheadline . classList . add ( 'iconize-subheadline' ) ;
subheadline . innerText = 'All Icons:' ;
this . resultContainerEl . append ( subheadline ) ;
}
}
if ( item . item . name !== 'default' ) {
if ( item . item . prefix === 'Emoji' ) {
const displayName = emoji . parseEmoji ( this . plugin . getSettings ( ) . emojiStyle , item . item . displayName ) ;
if ( ! displayName ) {
return ;
}
el . innerHTML = ` <div> ${ el . innerHTML } </div><div class="iconize-icon-preview"> ${ displayName } </div> ` ;
}
else {
el . innerHTML = ` <div> ${ el . innerHTML } </div><div class="iconize-icon-preview"> ${ getSvgFromLoadedIcon ( item . item . prefix , item . item . name ) } </div> ` ;
}
}
this . renderIndex ++ ;
}
}
const DEFAULT _SETTINGS = {
migrated : 2 ,
iconPacksPath : '.obsidian/icons' ,
fontSize : 16 ,
emojiStyle : 'native' ,
iconColor : null ,
recentlyUsedIcons : [ ] ,
recentlyUsedIconsSize : 5 ,
rules : [ ] ,
extraMargin : {
top : 0 ,
right : 4 ,
bottom : 0 ,
left : 0 ,
} ,
iconInTabsEnabled : false ,
iconInTitleEnabled : false ,
iconInFrontmatterEnabled : false ,
iconInFrontmatterFieldName : 'icon' ,
iconColorInFrontmatterFieldName : 'iconColor' ,
iconsBackgroundCheckEnabled : false ,
iconsInNotesEnabled : true ,
iconsInLinksEnabled : true ,
iconIdentifier : ':' ,
debugMode : false ,
} ;
function migrate$3 ( plugin ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
// Migration for new syncing mechanism.
if ( plugin . getSettings ( ) . migrated === 1 ) {
new obsidian . Notice ( 'Please delete your old icon packs and redownload your icon packs to use the new syncing mechanism.' , 20000 ) ;
plugin . getSettings ( ) . migrated ++ ;
}
} ) ;
}
function migrate$2 ( plugin ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
// Migration for new order functionality of custom rules.
if ( plugin . getSettings ( ) . migrated === 2 ) {
// Sorting alphabetically was the default behavior before.
plugin
. getSettings ( )
. rules . sort ( ( a , b ) => a . rule . localeCompare ( b . rule ) )
. forEach ( ( rule , i ) => {
rule . order = i ;
} ) ;
plugin . getSettings ( ) . migrated ++ ;
}
} ) ;
}
class IconCache {
constructor ( ) {
this . cache = new Map ( ) ;
this . set = ( path , result ) => {
this . cache . set ( path , result ) ;
} ;
this . invalidate = ( path ) => {
this . cache . delete ( path ) ;
} ;
this . clear = ( ) => {
this . cache . clear ( ) ;
} ;
this . get = ( path ) => {
var _a ;
return ( _a = this . cache . get ( path ) ) !== null && _a !== void 0 ? _a : null ;
} ;
this . doesRecordExist = ( path ) => {
return this . get ( path ) !== null ;
} ;
if ( IconCache . instance ) {
throw new Error ( 'Error: Instantiation failed: Use `IconCache.getInstance()` instead of new.' ) ;
}
IconCache . instance = this ;
}
}
IconCache . instance = new IconCache ( ) ;
IconCache . getInstance = ( ) => {
return IconCache . instance ;
} ;
/ * *
* Checks if the file type is equal to the ` for ` property of the custom rule .
* @ param rule CustomRule that will be checked .
* @ param fileType CustomRuleFileType that will be checked . Can be either ` file ` or ` folder ` .
* @ returns Boolean whether the custom rule ` for ` matches the file type or not .
* /
const doesMatchFileType = ( rule , fileType ) => {
return ( rule . for === 'everything' ||
( rule . for === 'files' && fileType === 'file' ) ||
( rule . for === 'folders' && fileType === 'folder' ) ) ;
} ;
/ * *
* Determines whether a given file or folder matches a specified custom rule .
* @ param plugin Plugin instance .
* @ param rule CustomRule to check against the file or folder .
* @ param file TAbstractFile to check against the custom rule .
* @ returns Promise that resolves to ` true ` if the file matches the rule , ` false ` otherwise .
* /
const isApplicable = ( plugin , rule , file ) => _ _awaiter ( void 0 , void 0 , void 0 , function * ( ) {
const metadata = yield plugin . app . vault . adapter . stat ( file . path ) ;
if ( ! metadata ) {
return false ;
}
const fileType = metadata . type ;
const doesMatch = doesMatchFileType ( rule , fileType ) ;
if ( ! doesMatch ) {
return false ;
}
return doesMatchPath ( rule , file . path ) ;
} ) ;
/ * *
* Removes the icon from the custom rule from all the files and folders , if applicable .
* @ param plugin IconFolderPlugin instance .
* @ param rule CustomRule where the icons will be removed based on this rule .
* /
const removeFromAllFiles = ( plugin , rule ) => _ _awaiter ( void 0 , void 0 , void 0 , function * ( ) {
const nodesWithIcon = document . querySelectorAll ( ` [ ${ config . ICON _ATTRIBUTE _NAME } =" ${ rule . icon } "] ` ) ;
for ( let i = 0 ; i < nodesWithIcon . length ; i ++ ) {
const node = nodesWithIcon [ i ] ;
// Parent element is the node which contains the data path.
const parent = node . parentElement ;
if ( ! parent ) {
continue ;
}
const dataPath = parent . getAttribute ( 'data-path' ) ;
if ( ! dataPath ) {
continue ;
}
const fileType = ( yield plugin . app . vault . adapter . stat ( dataPath ) ) . type ;
if ( doesMatchPath ( rule , dataPath ) && doesMatchFileType ( rule , fileType ) ) {
dom . removeIconInNode ( parent ) ;
IconCache . getInstance ( ) . invalidate ( dataPath ) ;
}
}
} ) ;
/ * *
* Gets all the custom rules sorted by their order property in ascending order .
* @ param plugin IconFolderPlugin instance .
* @ returns CustomRule array sorted by their order property in ascending order .
* /
const getSortedRules = ( plugin ) => {
return plugin . getSettings ( ) . rules . sort ( ( a , b ) => a . order - b . order ) ;
} ;
/ * *
* Tries to add all specific custom rule icons to all registered files and directories .
* It does that by calling the { @ link add } function . Custom rules should have the lowest
* priority and will get ignored if an icon already exists in the file or directory .
* @ param plugin IconFolderPlugin instance .
* @ param rule CustomRule that will be applied , if applicable , to all files and folders .
* /
const addToAllFiles = ( plugin , rule ) => _ _awaiter ( void 0 , void 0 , void 0 , function * ( ) {
const fileItems = yield getFileItems ( plugin , rule ) ;
for ( const fileItem of fileItems ) {
yield add$2 ( plugin , rule , fileItem . file , getFileItemTitleEl ( fileItem ) ) ;
}
} ) ;
/ * *
* Tries to add the icon of the custom rule to a file or folder . This function also checks
* if the file type matches the ` for ` property of the custom rule .
* @ param plugin IconFolderPlugin instance .
* @ param rule CustomRule that will be used to check if the rule is applicable to the file
* or directory .
* @ param file TAbstractFile that will be used to possibly create the icon for .
* @ param container HTMLElement where the icon will be added if the custom rules matches .
* @ returns A promise that resolves to ` true ` if the icon was added , ` false ` otherwise .
* /
const add$2 = ( plugin , rule , file , container ) => _ _awaiter ( void 0 , void 0 , void 0 , function * ( ) {
if ( container && dom . doesElementHasIconNode ( container ) ) {
return false ;
}
// Checks if the file or directory already has an icon.
const hasIcon = plugin . getIconNameFromPath ( file . path ) ;
if ( hasIcon ) {
return false ;
}
const doesMatch = yield isApplicable ( plugin , rule , file ) ;
if ( doesMatch ) {
IconCache . getInstance ( ) . set ( file . path , {
iconNameWithPrefix : rule . icon ,
inCustomRule : true ,
} ) ;
dom . createIconNode ( plugin , file . path , rule . icon , {
color : rule . color ,
container ,
} ) ;
return true ;
}
return false ;
} ) ;
/ * *
* Determines whether a given rule exists in a given path .
* @ param rule Rule to check for .
* @ param path Path to check in .
* @ returns True if the rule exists in the path , false otherwise .
* /
const doesMatchPath = ( rule , path ) => {
const toMatch = rule . useFilePath ? path : path . split ( '/' ) . pop ( ) ;
try {
// Rule is in some sort of regex.
const regex = new RegExp ( rule . rule ) ;
if ( toMatch . match ( regex ) ) {
return true ;
}
}
catch ( _a ) {
// Rule is not in some sort of regex, check for basic string match.
return toMatch . includes ( rule . rule ) ;
}
return false ;
} ;
/ * *
* Gets all the file items that can be applied to the specific custom rule .
* @ param plugin Instance of IconFolderPlugin .
* @ param rule Custom rule that will be checked for .
* @ returns A promise that resolves to an array of file items that match the custom rule .
* /
const getFileItems = ( plugin , rule ) => _ _awaiter ( void 0 , void 0 , void 0 , function * ( ) {
const result = [ ] ;
for ( const fileExplorer of plugin . getRegisteredFileExplorers ( ) ) {
const files = Object . values ( fileExplorer . fileItems ) ;
for ( const fileItem of files ) {
if ( yield isApplicable ( plugin , rule , fileItem . file ) ) {
result . push ( fileItem ) ;
}
}
}
return result ;
} ) ;
var customRule = {
getFileItems ,
doesMatchPath ,
doesMatchFileType ,
getSortedRules ,
removeFromAllFiles ,
add : add$2 ,
addToAllFiles ,
isApplicable ,
} ;
function migrate$1 ( plugin ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
// Migration for inheritance to custom rule.
if ( plugin . getSettings ( ) . migrated === 3 ) {
let hasRemovedInheritance = false ;
for ( const [ key , value ] of Object . entries ( plugin . getData ( ) ) ) {
if ( key === 'settings' || typeof value !== 'object' ) {
continue ;
}
const folderData = value ;
const inheritanceIcon = folderData . inheritanceIcon ;
if ( ! inheritanceIcon ) {
continue ;
}
const folderIconName = folderData . iconName ;
// Clean up old data.
if ( folderData . iconColor && folderIconName ) {
delete folderData . inheritanceIcon ;
}
else if ( folderIconName ) {
delete plugin . getData ( ) [ key ] ;
plugin . getData ( ) [ key ] = folderIconName ;
}
else if ( ! folderIconName ) {
delete plugin . getData ( ) [ key ] ;
}
const folderPath = key + '\\/[\\w\\d\\s]+' ;
const newRule = {
icon : inheritanceIcon ,
rule : ` ${ folderPath } \\ .(?: \\ w+ \\ .)* \\ w+ ` ,
for : 'files' ,
order : 0 ,
useFilePath : true ,
} ;
// Reorder existing custom rules so that the new inheritance custom rule
// is at the top.
plugin . getSettings ( ) . rules . map ( ( rule ) => {
rule . order ++ ;
} ) ;
plugin . getSettings ( ) . rules . unshift ( newRule ) ;
// Apply the custom rule.
yield customRule . addToAllFiles ( plugin , newRule ) ;
hasRemovedInheritance = true ;
}
if ( hasRemovedInheritance ) {
new obsidian . Notice ( ` [ ${ config . PLUGIN _NAME } ] Inheritance has been removed and replaced with custom rules. ` ) ;
}
plugin . getSettings ( ) . migrated ++ ;
}
} ) ;
}
const migrate = ( plugin ) => _ _awaiter ( void 0 , void 0 , void 0 , function * ( ) {
// eslint-disable-next-line
// @ts-ignore - Required because an older version of the plugin saved the `migrated`
// property as a boolean instead of a number.
if ( plugin . getSettings ( ) . migrated === true ) {
plugin . getSettings ( ) . migrated = 1 ;
}
yield migrate$3 ( plugin ) ;
yield migrate$2 ( plugin ) ;
yield migrate$1 ( plugin ) ;
yield plugin . saveIconFolderData ( ) ;
} ) ;
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 ( 'iconize-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 ( 'iconize-dragover' ) ;
this . dragTargetElement = el ;
}
}
unhighlight ( target , el ) {
if ( this . dragTargetElement && this . dragTargetElement !== target ) {
this . dragTargetElement . removeChild ( this . dragOverElement ) ;
this . dragTargetElement . classList . remove ( 'iconize-dragover' ) ;
this . dragTargetElement = undefined ;
}
clearTimeout ( this . closeTimer ) ;
this . closeTimer = setTimeout ( ( ) => {
if ( this . dragTargetElement ) {
el . removeChild ( this . dragOverElement ) ;
el . classList . remove ( 'iconize-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 . 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 createCustomIconPackDirectory ( 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 } ( ${ iconPack . prefix } ) ` )
. setDesc ( ` Total icons: ${ iconPack . icons . length } ` ) ;
// iconPackSetting.addButton((btn) => {
// btn.setIcon('broken-link');
// btn.setTooltip('Try to fix icon pack');
// btn.onClick(async () => {
// new Notice('Try to fix icon pack...');
// getIconPack(iconPack.name).icons = [];
// const icons = await 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 = await this.plugin.app.vault.adapter.read(filePath);
// const iconContent = file
// .replace(/stroke="#fff"/g, 'stroke="currentColor"')
// .replace(/fill="#fff"/g, 'fill="currentColor"');
// await this.plugin.app.vault.adapter.write(filePath, iconContent);
// await normalizeFileName(this.plugin, filePath);
// addIconToIconPack(iconPack.name, fileName, iconContent);
// }
// new Notice('...tried to fix icon pack');
// // Refreshes the DOM.
// Object.entries(this.plugin.getData()).forEach(async ([k, v]) => {
// const doesPathExist = await this.plugin.app.vault.adapter.exists(k, true);
// if (doesPathExist && typeof v === 'string') {
// // dom.removeIconInPath(k);
// dom.createIconNode(this.plugin, k, v);
// }
// });
// });
// });
iconPackSetting . addButton ( ( btn ) => {
btn . setIcon ( 'plus' ) ;
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 SVG 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 ) ;
} ) ;
}
}
/ * *
* Gets the tab leaves of a specific file path by looping through all opened files and
* checking if the file path matches .
* @ param plugin IconFolderPlugin instance .
* @ param path String of the file path to get the tab leaf of .
* @ returns TabHeaderLeaf array that includes all tab leaves of the file path .
* /
const getTabLeavesOfFilePath = ( plugin , path ) => {
const openedFiles = getAllOpenedFiles ( plugin ) ;
const openedFile = openedFiles . filter ( ( openedFile ) => openedFile . path === path ) ;
const leaves = openedFile . map ( ( openedFile ) => openedFile . leaf ) ;
return leaves ;
} ;
/ * *
* Adds an icon to the tab and its container . This function respects the
* custom rules and individually icon set .
* @ param plugin IconFolderPlugin instance .
* @ param file TFile instance of the file to add the icon to .
* @ param iconContainer HTMLElement where the icon will be added to .
* @ param options AddOptions for the add function which can optionally be used .
* /
const add$1 = ( plugin , file , iconContainer , options ) => _ _awaiter ( void 0 , void 0 , void 0 , function * ( ) {
var _a ;
const iconColor = ( _a = options === null || options === void 0 ? void 0 : options . iconColor ) !== null && _a !== void 0 ? _a : plugin . getSettings ( ) . iconColor ;
const data = Object . entries ( plugin . getData ( ) ) ;
// Removes the `display: none` from the obsidian styling.
iconContainer . style . display = 'flex' ;
// Only add the icon name manually when it is defined in the options.
if ( options === null || options === void 0 ? void 0 : options . iconName ) {
dom . setIconForNode ( plugin , options . iconName , iconContainer , iconColor ) ;
// TODO: Refactor to include option to `insertIconToNode` function.
iconContainer . style . margin = null ;
return ;
}
// Add icons to tabs if a custom rule is applicable.
for ( const rule of customRule . getSortedRules ( plugin ) ) {
const isApplicable = yield customRule . isApplicable ( plugin , rule , file ) ;
if ( isApplicable ) {
dom . setIconForNode ( plugin , rule . icon , iconContainer , rule . color ) ;
// TODO: Refactor to include option to `insertIconToNode` function.
iconContainer . style . margin = null ;
break ;
}
}
// Add icons to tabs if there is an icon set.
const iconData = data . find ( ( [ dataPath ] ) => dataPath === file . path ) ;
if ( ! iconData ) {
return ;
}
const value = iconData [ 1 ] ;
if ( typeof value !== 'string' && typeof value !== 'object' ) {
return ;
}
let iconName ;
if ( typeof value === 'object' ) {
const v = value ;
if ( v . iconName === null ) {
return ;
}
iconName = v . iconName ;
}
else {
iconName = value ;
}
dom . setIconForNode ( plugin , iconName , iconContainer , iconColor ) ;
// TODO: Refactor to include option to `insertIconToNode` function.
iconContainer . style . margin = null ;
} ) ;
/ * *
* Updates the icon in the tab and container by setting calling the ` setIconForNode `
* function and removing the margin from the icon container .
* @ param plugin IconFolderPlugin instance .
* @ param iconName String of the icon name to update to .
* @ param iconContainer HTMLElement where the icon is located and will be updated .
* /
const update = ( plugin , iconName , iconContainer ) => {
dom . setIconForNode ( plugin , iconName , iconContainer ) ;
// TODO: Refactor to include option to `insertIconToNode` function.
iconContainer . style . margin = null ;
} ;
/ * *
* Removes the icon from the tab and container by setting the ` display ` style property
* to ` none ` . Optionally , the icon can be replaced with the default obsidian icon .
* @ param iconContainer HTMLElement where the icon is located and will be removed from .
* @ param options RemoveOptions for the remove function which can optionally be used .
* /
const remove$1 = ( iconContainer , options ) => {
if ( ! ( options === null || options === void 0 ? void 0 : options . replaceWithDefaultIcon ) ) {
// Removes the display of the icon container to remove the icons from the tabs.
iconContainer . style . display = 'none' ;
}
else {
iconContainer . innerHTML = DEFAULT _FILE _ICON ;
}
} ;
var iconTabs = {
add : add$1 ,
update ,
remove : remove$1 ,
getTabLeavesOfFilePath ,
} ;
class CustomIconRuleSetting extends IconFolderSetting {
constructor ( plugin , containerEl , app , refreshDisplay ) {
super ( plugin , containerEl ) ;
this . app = app ;
this . refreshDisplay = refreshDisplay ;
}
/ * *
* Updates all the open files based on the custom rule that was specified .
* @ param rule Rule that will be used to update all the icons for all opened files .
* @ param remove Whether to remove the icons that are applicable to the rule or not .
* /
updateIconTabs ( rule _1 , remove _1 ) {
return _ _awaiter ( this , arguments , void 0 , function * ( rule , remove , cachedPaths = [ ] ) {
if ( this . plugin . getSettings ( ) . iconInTabsEnabled ) {
for ( const openedFile of getAllOpenedFiles ( this . plugin ) ) {
if ( cachedPaths . includes ( openedFile . path ) ) {
continue ;
}
const applicable = yield customRule . isApplicable ( this . plugin , rule , openedFile ) ;
if ( ! applicable ) {
continue ;
}
const leaf = openedFile . leaf ;
if ( remove ) {
iconTabs . remove ( leaf . tabHeaderInnerIconEl , {
replaceWithDefaultIcon : true ,
} ) ;
}
else {
iconTabs . add ( this . plugin , openedFile , leaf . tabHeaderInnerIconEl , {
iconName : rule . icon ,
iconColor : rule . color ,
} ) ;
}
}
}
} ) ;
}
createDescriptionEl ( container , text ) {
const description = container . createEl ( 'p' , {
text ,
cls : 'setting-item-description' ,
} ) ;
description . style . marginBottom = 'var(--size-2-2)' ;
}
display ( ) {
new obsidian . Setting ( this . containerEl )
. setName ( 'Add icon rule' )
. setDesc ( 'Will add the icon based on the defined rule (as a plain string or in regex format).' )
. 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 . 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 * ( ) {
const icon = getNormalizedName ( typeof item === 'object' ? item . displayName : item ) ;
const rule = {
rule : this . textComponent . getValue ( ) ,
icon ,
for : 'everything' ,
order : this . plugin . getSettings ( ) . rules . length ,
} ;
this . plugin . getSettings ( ) . rules = [
... this . plugin . getSettings ( ) . rules ,
rule ,
] ;
yield this . plugin . saveIconFolderData ( ) ;
this . refreshDisplay ( ) ;
new obsidian . Notice ( 'Icon rule added.' ) ;
this . textComponent . setValue ( '' ) ;
saveIconToIconPack ( this . plugin , rule . icon ) ;
yield customRule . addToAllFiles ( this . plugin , rule ) ;
this . updateIconTabs ( rule , false ) ;
} ) ;
modal . open ( ) ;
} ) ) ;
this . chooseIconBtn = btn ;
} ) ;
this . plugin . getSettings ( ) . rules . forEach ( ( rule ) => {
// Keeping track of the old rule so that we can get a reference to it for old values.
const oldRule = Object . assign ( { } , rule ) ;
const settingRuleEl = new obsidian . Setting ( this . containerEl )
. setName ( rule . rule )
. setDesc ( ` Icon: ${ rule . icon } ` ) ;
const currentOrder = rule . order ;
/ * *
* Re - orders the custom rule based on the value that is passed in .
* @ param valueForReorder Number that will be used to determine whether to swap the
* custom rule with the next rule or the previous rule .
* /
const orderCustomRules = ( valueForReorder ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
const otherRule = this . plugin . getSettings ( ) . rules [ currentOrder + valueForReorder ] ;
// Swap the current rule with the next rule.
otherRule . order = otherRule . order - valueForReorder ;
rule . order = currentOrder + valueForReorder ;
// Refreshes the DOM.
yield customRule . removeFromAllFiles ( this . plugin , oldRule ) ;
yield this . plugin . saveIconFolderData ( ) ;
const addedPaths = [ ] ;
for ( const fileExplorer of this . plugin . getRegisteredFileExplorers ( ) ) {
const files = Object . values ( fileExplorer . fileItems ) ;
for ( const rule of customRule . getSortedRules ( this . plugin ) ) {
// Removes the icon tabs from all opened files.
this . updateIconTabs ( rule , true , addedPaths ) ;
// Adds the icon tabs to all opened files.
this . updateIconTabs ( rule , false , addedPaths ) ;
for ( const fileItem of files ) {
if ( addedPaths . includes ( fileItem . file . path ) ) {
continue ;
}
const added = yield customRule . add ( this . plugin , rule , fileItem . file , getFileItemTitleEl ( fileItem ) ) ;
if ( added ) {
addedPaths . push ( fileItem . file . path ) ;
}
}
}
}
this . refreshDisplay ( ) ;
} ) ;
// Add the move down custom rule button to re-order the custom rule.
settingRuleEl . addExtraButton ( ( btn ) => {
const isFirstOrder = currentOrder === 0 ;
btn . setDisabled ( isFirstOrder ) ;
btn . extraSettingsEl . style . cursor = isFirstOrder
? 'not-allowed'
: 'default' ;
btn . extraSettingsEl . style . opacity = isFirstOrder ? '50%' : '100%' ;
btn . setIcon ( 'arrow-up' ) ;
btn . setTooltip ( 'Prioritize the custom rule' ) ;
btn . onClick ( ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
yield orderCustomRules ( - 1 ) ;
} ) ) ;
} ) ;
// Add the move up custom rule button to re-order the custom rule.
settingRuleEl . addExtraButton ( ( btn ) => {
const isLastOrder = currentOrder === this . plugin . getSettings ( ) . rules . length - 1 ;
btn . setDisabled ( isLastOrder ) ;
btn . extraSettingsEl . style . cursor = isLastOrder
? 'not-allowed'
: 'default' ;
btn . extraSettingsEl . style . opacity = isLastOrder ? '50%' : '100%' ;
btn . setIcon ( 'arrow-down' ) ;
btn . setTooltip ( 'Deprioritize the custom rule' ) ;
btn . onClick ( ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
yield orderCustomRules ( 1 ) ;
} ) ) ;
} ) ;
// Add the configuration button for configuring where the custom rule gets applied to.
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 * ( ) {
this . updateIconTabs ( rule , true ) ;
yield customRule . removeFromAllFiles ( 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 customRule . addToAllFiles ( this . plugin , rule ) ;
this . updateIconTabs ( rule , false ) ;
yield this . plugin . saveIconFolderData ( ) ;
this . refreshDisplay ( ) ;
customRule
. getSortedRules ( this . plugin )
. forEach ( ( previousRule ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
yield customRule . addToAllFiles ( this . plugin , previousRule ) ;
this . updateIconTabs ( previousRule , false ) ;
} ) ) ;
} ) ) ;
} ) ;
// Add the edit custom rule button.
settingRuleEl . addButton ( ( btn ) => {
btn . setIcon ( 'pencil' ) ;
btn . setTooltip ( 'Edit the custom rule' ) ;
btn . onClick ( ( ) => {
var _a ;
// Create modal and its children elements.
const modal = new obsidian . Modal ( this . plugin . app ) ;
modal . contentEl . style . display = 'block' ;
modal . modalEl . classList . add ( 'iconize-custom-modal' ) ;
modal . titleEl . setText ( 'Edit custom rule' ) ;
// Create the input for the rule.
this . createDescriptionEl ( modal . contentEl , 'Regex or simple string' ) ;
const input = new obsidian . TextComponent ( modal . contentEl ) ;
input . setValue ( rule . rule ) ;
input . onChange ( ( value ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
rule . rule = value ;
} ) ) ;
const useFilePathContainer = modal . contentEl . createDiv ( ) ;
useFilePathContainer . style . display = 'flex' ;
useFilePathContainer . style . alignItems = 'center' ;
useFilePathContainer . style . justifyContent = 'space-between' ;
useFilePathContainer . style . marginTop = 'var(--size-4-5)' ;
const useFilePathDescription = useFilePathContainer . createEl ( 'p' , {
text : 'Whether to apply the icon to all files/folders that match the file path.' ,
cls : 'setting-item-description' ,
} ) ;
useFilePathDescription . style . margin = '0' ;
useFilePathDescription . style . marginBottom = 'var(--size-2-2)' ;
new obsidian . ToggleComponent ( useFilePathContainer )
. setValue ( rule . useFilePath === true )
. onChange ( ( value ) => {
rule . useFilePath = value ;
} ) ;
// Create the change icon button with icon preview.
this . createDescriptionEl ( modal . contentEl , 'Custom rule icon' ) ;
const iconContainer = modal . contentEl . createDiv ( ) ;
iconContainer . style . display = 'flex' ;
iconContainer . style . alignItems = 'center' ;
iconContainer . style . justifyContent = 'space-between' ;
const iconEl = iconContainer . createDiv ( ) ;
const iconPreviewEl = iconEl . createDiv ( ) ;
dom . setIconForNode ( this . plugin , rule . icon , iconPreviewEl ) ;
iconEl . style . display = 'flex' ;
iconEl . style . alignItems = 'center' ;
iconEl . style . justifyContent = 'space-between' ;
iconEl . style . margin = null ;
iconPreviewEl . innerHTML = svg . setFontSize ( iconPreviewEl . innerHTML , 20 ) ;
const iconNameEl = iconEl . createEl ( 'div' , {
cls : 'setting-item-description' ,
} ) ;
iconNameEl . style . paddingTop = '0' ;
iconNameEl . style . marginLeft = 'var(--size-2-2)' ;
iconNameEl . innerText = rule . icon ;
const changeIconBtn = new obsidian . ButtonComponent ( iconContainer ) ;
changeIconBtn . setButtonText ( 'Change icon' ) ;
changeIconBtn . onClick ( ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
const modal = new IconsPickerModal ( this . app , this . plugin , rule . icon ) ;
modal . onChooseItem = ( item ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
const icon = typeof item === 'object' ? item . displayName : item ;
rule . icon = icon ;
dom . setIconForNode ( this . plugin , rule . icon , iconPreviewEl ) ;
iconPreviewEl . innerHTML = svg . setFontSize ( iconPreviewEl . innerHTML , 20 ) ;
iconNameEl . innerText = getNormalizedName ( rule . icon ) ;
} ) ;
modal . open ( ) ;
} ) ) ;
// Create the color picker for the rule.
this . createDescriptionEl ( modal . contentEl , 'Color of the icon' ) ;
const colorContainer = modal . contentEl . createDiv ( ) ;
colorContainer . style . display = 'flex' ;
colorContainer . style . alignItems = 'center' ;
colorContainer . style . justifyContent = 'space-between' ;
const colorPicker = new obsidian . ColorComponent ( colorContainer )
. setValue ( ( _a = rule . color ) !== null && _a !== void 0 ? _a : '#000000' )
. onChange ( ( value ) => {
rule . color = value ;
} ) ;
const defaultColorButton = new obsidian . ButtonComponent ( colorContainer ) ;
defaultColorButton . setTooltip ( 'Set color to the default one' ) ;
defaultColorButton . setButtonText ( 'Default' ) ;
defaultColorButton . onClick ( ( ) => {
colorPicker . setValue ( '#000000' ) ;
rule . color = undefined ;
} ) ;
// Create the save button.
const button = new obsidian . ButtonComponent ( modal . contentEl ) ;
button . buttonEl . style . marginTop = 'var(--size-4-4)' ;
button . buttonEl . style . float = 'right' ;
button . setButtonText ( 'Save Changes' ) ;
button . onClick ( ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
if ( ! emoji . isEmoji ( oldRule . icon ) ) {
// Tries to remove the previously used icon from the icon pack.
removeIconFromIconPack ( this . plugin , oldRule . icon ) ;
}
if ( ! emoji . isEmoji ( rule . icon ) ) {
// Tries to add the newly used icon to the icon pack.
saveIconToIconPack ( this . plugin , rule . icon ) ;
rule . icon = getNormalizedName ( rule . icon ) ;
}
this . refreshDisplay ( ) ;
new obsidian . Notice ( 'Custom rule updated.' ) ;
// Refresh the DOM.
yield customRule . removeFromAllFiles ( this . plugin , oldRule ) ;
this . updateIconTabs ( rule , true ) ;
this . plugin . getSettings ( ) . rules . forEach ( ( rule ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
yield customRule . addToAllFiles ( this . plugin , rule ) ;
this . updateIconTabs ( rule , false ) ;
} ) ) ;
yield this . plugin . saveIconFolderData ( ) ;
modal . close ( ) ;
} ) ) ;
modal . open ( ) ;
} ) ;
} ) ;
// Add the delete custom rule button.
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 ||
rule . for !== r . for ) ;
this . plugin . getSettings ( ) . rules = newRules ;
yield this . plugin . saveIconFolderData ( ) ;
this . refreshDisplay ( ) ;
new obsidian . Notice ( 'Custom rule deleted.' ) ;
yield customRule . removeFromAllFiles ( this . plugin , rule ) ;
removeIconFromIconPack ( this . plugin , rule . icon ) ;
this . updateIconTabs ( rule , true ) ;
const previousRules = this . plugin
. getSettings ( )
. rules . filter ( ( r ) => rule . for === r . for ) ;
previousRules . forEach ( ( previousRule ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
yield customRule . addToAllFiles ( this . plugin , previousRule ) ;
this . updateIconTabs ( previousRule , false ) ;
} ) ) ;
} ) ) ;
} ) ;
} ) ;
}
}
const getTitleIcon = ( leaf ) => {
return leaf . querySelector ( ` . ${ config . TITLE _ICON _CLASS } ` ) ;
} ;
const add = ( plugin , inlineTitleEl , svgElement , options ) => {
var _a ;
if ( ! inlineTitleEl . parentElement ) {
return ;
}
if ( options === null || options === void 0 ? void 0 : options . fontSize ) {
svgElement = svg . setFontSize ( svgElement , options . fontSize ) ;
}
let titleIcon = getTitleIcon ( inlineTitleEl . parentElement ) ;
const hadTitleIcon = titleIcon !== null ;
if ( ! titleIcon ) {
titleIcon = document . createElement ( 'div' ) ;
}
titleIcon . style . display = 'block' ;
titleIcon . classList . add ( config . TITLE _ICON _CLASS ) ;
// Checks if the passed element is an emoji.
if ( emoji . isEmoji ( svgElement ) && options . fontSize ) {
svgElement =
( _a = emoji . parseEmoji ( plugin . getSettings ( ) . emojiStyle , svgElement , options . fontSize ) ) !== null && _a !== void 0 ? _a : svgElement ;
titleIcon . style . fontSize = ` ${ options . fontSize } px ` ;
}
titleIcon . innerHTML = svgElement ;
if ( ! hadTitleIcon ) {
inlineTitleEl . parentElement . prepend ( titleIcon ) ;
}
} ;
const updateStyle = ( inlineTitleEl , options ) => {
if ( ! inlineTitleEl . parentElement ) {
return ;
}
const titleIcon = getTitleIcon ( inlineTitleEl . parentElement ) ;
if ( ! titleIcon ) {
return ;
}
if ( options . fontSize ) {
if ( ! emoji . isEmoji ( titleIcon . innerHTML ) ) {
titleIcon . innerHTML = svg . setFontSize ( titleIcon . innerHTML , options . fontSize ) ;
}
else {
titleIcon . style . fontSize = ` ${ options . fontSize } px ` ;
}
}
} ;
/ * *
* Hides the title icon from the provided HTMLElement .
* @ param contentEl HTMLElement to hide the title icon from .
* /
const hide = ( inlineTitleEl ) => {
if ( ! inlineTitleEl . parentElement ) {
return ;
}
const titleIconContainer = getTitleIcon ( inlineTitleEl . parentElement ) ;
if ( ! titleIconContainer ) {
return ;
}
titleIconContainer . style . display = 'none' ;
} ;
const remove = ( inlineTitleEl ) => {
if ( ! inlineTitleEl . parentElement ) {
return ;
}
const titleIconContainer = getTitleIcon ( inlineTitleEl . parentElement ) ;
if ( ! titleIconContainer ) {
return ;
}
titleIconContainer . remove ( ) ;
} ;
var titleIcon = {
add ,
updateStyle ,
hide ,
remove ,
} ;
const calculateFontTextSize = ( ) => {
var _a ;
let fontSize = parseFloat ( ( _a = getComputedStyle ( document . body ) . getPropertyValue ( '--font-text-size' ) ) !== null && _a !== void 0 ? _a : '0' ) ;
if ( ! fontSize ) {
fontSize = parseFloat ( getComputedStyle ( document . documentElement ) . fontSize ) ;
}
return fontSize ;
} ;
const calculateInlineTitleSize = ( ) => {
const fontSize = calculateFontTextSize ( ) ;
const inlineTitleSize = parseFloat ( getComputedStyle ( document . body ) . getPropertyValue ( '--inline-title-size' ) ) ;
return fontSize * inlineTitleSize ;
} ;
const isHeader = ( value ) => {
return /^h[1-6]$/ . test ( value ) ;
} ;
const calculateHeaderSize = ( header ) => {
const fontSize = calculateFontTextSize ( ) ;
const headerSize = parseFloat ( getComputedStyle ( document . body ) . getPropertyValue ( ` -- ${ header } -size ` ) ) ;
return fontSize * headerSize ;
} ;
class EmojiStyleSetting extends IconFolderSetting {
display ( ) {
const emojiStyle = new obsidian . Setting ( this . containerEl )
. setName ( 'Emoji style' )
. setDesc ( 'Change the style of your emojis.' ) ;
emojiStyle . addDropdown ( ( dropdown ) => {
dropdown . addOption ( 'none' , 'None' ) ;
dropdown . addOption ( 'native' , 'Native' ) ;
dropdown . addOption ( 'twemoji' , 'Twemoji' ) ;
dropdown . setValue ( this . plugin . getSettings ( ) . emojiStyle ) ;
dropdown . onChange ( ( value ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . plugin . getSettings ( ) . emojiStyle = value ;
this . updateDOM ( ) ;
yield this . plugin . saveIconFolderData ( ) ;
} ) ) ;
} ) ;
}
updateDOM ( ) {
for ( const fileExplorer of this . plugin . getRegisteredFileExplorers ( ) ) {
const fileItems = Object . entries ( fileExplorer . fileItems ) ;
for ( const [ path , _ ] of fileItems ) {
let iconName = this . plugin . getData ( ) [ path ] ;
if ( ! iconName ) {
continue ;
}
const data = this . plugin . getData ( ) [ path ] ;
if ( typeof data === 'object' ) {
const data = this . plugin . getData ( ) [ path ] ;
if ( data . iconName ) {
iconName = data . iconName ;
}
}
if ( emoji . isEmoji ( iconName ) ) {
dom . createIconNode ( this . plugin , path , iconName ) ;
const tabLeaves = iconTabs . getTabLeavesOfFilePath ( this . plugin , path ) ;
for ( const tabLeaf of tabLeaves ) {
iconTabs . update ( this . plugin , iconName , tabLeaf . tabHeaderInnerIconEl ) ;
}
for ( const openedFile of getAllOpenedFiles ( this . plugin ) ) {
const activeView = openedFile . leaf . view ;
if ( activeView instanceof obsidian . MarkdownView &&
openedFile . path === path ) {
titleIcon . add ( this . plugin , activeView . inlineTitleEl , iconName , {
fontSize : calculateInlineTitleSize ( ) ,
} ) ;
}
}
}
}
}
for ( const rule of customRule . getSortedRules ( this . plugin ) ) {
customRule . addToAllFiles ( this . plugin , rule ) ;
}
}
}
/ * *
* Helper function that refreshes the style of all the icons that are defined
* or in a custom rule involved .
* @ param plugin Instance of the IconFolderPlugin .
* /
const refreshStyleOfIcons = ( plugin ) => _ _awaiter ( void 0 , void 0 , void 0 , function * ( ) {
// Refreshes the icon style for all normally added icons.
style . refreshIconNodes ( plugin ) ;
// Refreshes the icon style for all custom icon rules, when the color of the rule is
// not defined.
for ( const rule of customRule . getSortedRules ( plugin ) ) {
const fileItems = yield customRule . getFileItems ( plugin , rule ) ;
for ( const fileItem of fileItems ) {
const titleEl = getFileItemTitleEl ( fileItem ) ;
const iconNode = titleEl . querySelector ( '.iconize-icon' ) ;
let iconContent = iconNode . innerHTML ;
iconContent = style . applyAll ( plugin , iconContent , iconNode ) ;
if ( rule . color ) {
iconContent = svg . colorize ( iconContent , rule . color ) ;
iconNode . style . color = rule . color ;
}
iconNode . innerHTML = iconContent ;
}
}
} ) ;
var helper = {
refreshStyleOfIcons ,
} ;
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 ( 'iconize-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 ( ) ;
helper . refreshStyleOfIcons ( 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 ( ) ;
helper . refreshStyleOfIcons ( 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 ( ) ;
helper . refreshStyleOfIcons ( 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 ( ) ;
helper . refreshStyleOfIcons ( 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 . 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' ) ;
} ) ) ;
} ) ;
}
}
class IconPacksBackgroundChecker extends IconFolderSetting {
display ( ) {
new obsidian . Setting ( this . containerEl )
. setName ( 'Icons background check' )
. setDesc ( 'Check in the background on every load of Obsidian, if icons are missing and it will try to add them to the specific icon pack.' )
. addToggle ( ( toggle ) => {
toggle
. setValue ( this . plugin . getSettings ( ) . iconsBackgroundCheckEnabled )
. onChange ( ( enabled ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . plugin . getSettings ( ) . iconsBackgroundCheckEnabled = enabled ;
yield this . plugin . saveIconFolderData ( ) ;
if ( enabled ) {
new obsidian . Notice ( 'You need to reload Obsidian for this to take effect.' , 10000 ) ;
}
} ) ) ;
} ) ;
}
}
class IconPackBrowserModal extends obsidian . FuzzySuggestModal {
constructor ( app , plugin ) {
super ( app ) ;
this . plugin = plugin ;
this . resultContainerEl . classList . add ( 'iconize-browse-modal' ) ;
this . inputEl . placeholder = 'Select to download icon pack' ;
}
// eslint-disable-next-line
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$1 ) ;
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 } ... ` ) ;
const arrayBuffer = yield downloadZipFile ( item . downloadLink ) ;
yield createZipFile ( this . plugin , ` ${ item . name } .zip ` , arrayBuffer ) ;
yield registerIconPack ( item . name , arrayBuffer ) ;
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 a predefined icon pack that is officially supported.' )
. 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 selection modal.' )
. addSlider ( ( slider ) => {
var _a ;
slider
. setLimits ( 1 , 25 , 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 ToggleIconInTabs extends IconFolderSetting {
display ( ) {
new obsidian . Setting ( this . containerEl )
. setName ( 'Toggle icon in tabs' )
. setDesc ( 'Toggles the visibility of an icon for a file in the tab bar.' )
. addToggle ( ( toggle ) => {
toggle
. setValue ( this . plugin . getSettings ( ) . iconInTabsEnabled )
. onChange ( ( enabled ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . plugin . getSettings ( ) . iconInTabsEnabled = enabled ;
yield this . plugin . saveIconFolderData ( ) ;
// Updates the already opened files.
this . plugin . app . workspace
. getLeavesOfType ( 'markdown' )
. forEach ( ( leaf ) => {
const file = leaf . view . file ;
if ( file ) {
const tabHeaderLeaf = leaf ;
if ( enabled ) {
// Adds the icons to already opened files.
iconTabs . add ( this . plugin , file , tabHeaderLeaf . tabHeaderInnerIconEl ) ;
}
else {
// Removes the icons from already opened files.
iconTabs . remove ( tabHeaderLeaf . tabHeaderInnerIconEl ) ;
}
}
} ) ;
} ) ) ;
} ) ;
}
}
const checkMissingIcons = ( plugin , data ) => _ _awaiter ( void 0 , void 0 , void 0 , function * ( ) {
const missingIcons = new Set ( ) ;
const allIcons = new Map ( ) ;
const getMissingIcon = ( iconNameWithPrefix ) => _ _awaiter ( void 0 , void 0 , void 0 , function * ( ) {
const iconNextIdentifier = nextIdentifier ( iconNameWithPrefix ) ;
const iconName = iconNameWithPrefix . substring ( iconNextIdentifier ) ;
const iconPrefix = iconNameWithPrefix . substring ( 0 , iconNextIdentifier ) ;
const iconPackName = getIconPackNameByPrefix ( iconPrefix ) ;
const icon = getIconFromIconPack ( iconPackName , iconPrefix , iconName ) ;
if ( ! icon ) {
console . error ( ` Icon file ${ iconNameWithPrefix } could not be found. ` ) ;
return null ;
}
const doesIconFileExists = yield plugin . app . vault . adapter . exists ( ` ${ getPath ( ) } / ${ iconPackName } / ${ iconName } .svg ` ) ;
if ( ! doesIconFileExists ) {
const possibleIcon = getSvgFromLoadedIcon ( iconPrefix , iconName ) ;
if ( ! possibleIcon ) {
console . error ( ` Icon SVG ${ iconNameWithPrefix } could not be found. ` ) ;
return null ;
}
yield extractIconToIconPack ( plugin , icon , possibleIcon ) ;
return icon ;
}
return null ;
} ) ;
for ( const rule of plugin . getSettings ( ) . rules ) {
if ( ! emoji . isEmoji ( rule . icon ) ) {
allIcons . set ( rule . icon , true ) ;
const icon = yield getMissingIcon ( rule . icon ) ;
if ( icon ) {
missingIcons . add ( icon ) ;
}
}
}
for ( const [ _ , value ] of data ) {
// Check for missing icon names.
let iconNameWithPrefix = value ;
if ( typeof value === 'object' ) {
iconNameWithPrefix = value . iconName ;
}
if ( iconNameWithPrefix && ! emoji . isEmoji ( iconNameWithPrefix ) ) {
allIcons . set ( iconNameWithPrefix , true ) ;
const icon = yield getMissingIcon ( iconNameWithPrefix ) ;
if ( icon ) {
missingIcons . add ( icon ) ;
}
}
}
// Show notice that background check is running.
if ( missingIcons . size !== 0 ) {
new obsidian . Notice ( ` [ ${ config . PLUGIN _NAME } ] Background Check: found missing icons. Adding missing icons... ` , 10000 ) ;
}
// Iterates over all the missing icons with its path and adds the icon to the node.
for ( const icon of missingIcons ) {
const normalizedName = getNormalizedName ( icon . prefix + icon . name ) ;
const nodesWithIcon = document . querySelectorAll ( ` [ ${ config . ICON _ATTRIBUTE _NAME } =" ${ normalizedName } "] ` ) ;
nodesWithIcon . forEach ( ( node ) => {
dom . setIconForNode ( plugin , normalizedName , node ) ;
} ) ;
}
// Show notice that background check was finished.
if ( missingIcons . size !== 0 ) {
new obsidian . Notice ( ` [ ${ config . PLUGIN _NAME } ] Background Check: added missing icons ` , 10000 ) ;
}
// Remove all icon files that can not be found in the data.
for ( const iconPack of getAllIconPacks ( ) ) {
// Checks if the icon pack exists.
const doesIconPackExist = yield plugin . app . vault . adapter . exists ( ` ${ getPath ( ) } / ${ iconPack . name } ` ) ;
if ( ! doesIconPackExist ) {
continue ;
}
const iconFiles = yield plugin . app . vault . adapter . list ( ` ${ getPath ( ) } / ${ iconPack . name } ` ) ;
for ( const iconFilePath of iconFiles . files ) {
const iconNameWithExtension = iconFilePath . split ( '/' ) . pop ( ) ;
// Removes the file extension.
const iconName = iconNameWithExtension === null || iconNameWithExtension === void 0 ? void 0 : iconNameWithExtension . substring ( 0 , iconNameWithExtension . length - 4 ) ;
const iconNameWithPrefix = iconPack . prefix + iconName ;
const doesIconExist = allIcons . get ( iconNameWithPrefix ) ;
if ( ! doesIconExist ) {
const path = ` ${ getPath ( ) } / ${ iconPack . name } / ${ iconName } .svg ` ;
const doesPathExist = yield plugin . app . vault . adapter . exists ( path ) ;
if ( doesPathExist ) {
console . info ( ` [ ${ config . PLUGIN _NAME } ] Removing icon ${ path } because it is not used anymore. ` ) ;
// Removes the icon file.
yield plugin . app . vault . adapter . remove ( ` ${ getPath ( ) } / ${ iconPack . name } / ${ iconName } .svg ` ) ;
}
}
}
}
} ) ;
/ * *
* This function adds all the possible icons to the corresponding nodes . It
* adds the icons , that are defined in the data as a basic string to the nodes
* and the custom rule icons .
* @ param plugin Instance of IconFolderPlugin .
* @ param data Data that will be used to add all the icons to the nodes .
* @ param registeredFileExplorers A WeakSet of file explorers that are being used as a
* cache for already handled file explorers .
* @ param callback Callback is being called whenever the icons are added to one file
* explorer .
* /
const addAll = ( plugin , data , registeredFileExplorers , callback ) => {
const fileExplorers = plugin . app . workspace . getLeavesOfType ( 'file-explorer' ) ;
for ( const fileExplorer of fileExplorers ) {
if ( registeredFileExplorers . has ( fileExplorer . view ) ) {
continue ;
}
registeredFileExplorers . add ( fileExplorer . view ) ;
// Adds icons to already open file tabs.
if ( plugin . getSettings ( ) . iconInTabsEnabled ) {
for ( const leaf of plugin . app . workspace . getLeavesOfType ( 'markdown' ) ) {
const file = leaf . view . file ;
if ( file ) {
const tabHeaderLeaf = leaf ;
const iconColor = plugin . getIconColor ( file . path ) ;
iconTabs . add ( plugin , file , tabHeaderLeaf . tabHeaderInnerIconEl , {
iconColor ,
} ) ;
}
}
}
for ( const [ dataPath , value ] of data ) {
const fileItem = fileExplorer . view . fileItems [ dataPath ] ;
if ( fileItem ) {
const titleEl = getFileItemTitleEl ( fileItem ) ;
const titleInnerEl = getFileItemInnerTitleEl ( fileItem ) ;
// Need to check this because 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 ;
const iconColor = typeof value === 'string' ? undefined : value . iconColor ;
if ( iconName ) {
// Removes a possible existing icon.
const existingIcon = titleEl . querySelector ( '.iconize-icon' ) ;
if ( existingIcon ) {
existingIcon . remove ( ) ;
}
// Creates the new node with the icon inside.
const iconNode = titleEl . createDiv ( ) ;
iconNode . setAttribute ( config . ICON _ATTRIBUTE _NAME , iconName ) ;
iconNode . classList . add ( 'iconize-icon' ) ;
IconCache . getInstance ( ) . set ( dataPath , {
iconNameWithPrefix : iconName ,
} ) ;
dom . setIconForNode ( plugin , iconName , iconNode , iconColor ) ;
titleEl . insertBefore ( iconNode , titleInnerEl ) ;
}
}
}
}
// Callback function to register other events to this file explorer.
callback === null || callback === void 0 ? void 0 : callback ( ) ;
}
// Handles the custom rules.
for ( const rule of customRule . getSortedRules ( plugin ) ) {
customRule . addToAllFiles ( plugin , rule ) ;
}
} ;
/ * *
* Gets the icon of a given path . This function returns the first occurrence of an icon .
* @ param plugin Instance of the IconFolderPlugin .
* @ param path Path to get the icon of .
* @ returns The icon of the path if it exists , undefined otherwise .
* /
const getByPath = ( plugin , path ) => {
if ( path === 'settings' || path === 'migrated' ) {
return undefined ;
}
const value = plugin . getData ( ) [ path ] ;
if ( typeof value === 'string' ) {
// If the value is a plain icon name, return it.
return value ;
}
else if ( typeof value === 'object' ) {
const v = value ;
if ( v . iconName !== null ) {
return v . iconName ;
}
}
// Tries to get the custom rule for the path and returns its icon if it exists.
const rule = customRule . getSortedRules ( plugin ) . find ( ( rule ) => {
return customRule . doesMatchPath ( rule , path ) ;
} ) ;
if ( rule ) {
return rule . icon ;
}
return undefined ;
} ;
/ * *
* Gets all the icons with their paths as an object .
* @ param plugin Instance of the IconFolderPlugin .
* @ returns An object that consists of the path and the icon name for the data
* or custom rule .
* /
const getAllWithPath = ( plugin ) => {
const result = [ ] ;
Object . keys ( plugin . getData ( ) ) . forEach ( ( path ) => {
if ( path === 'settings' || path === 'migrated' ) {
return ;
}
const icon = getByPath ( plugin , path ) ;
if ( icon && ! emoji . isEmoji ( icon ) ) {
result . push ( { path , icon } ) ;
}
} ) ;
// Add all icons for the custom rules with the rule as the path.
for ( const rule of plugin . getSettings ( ) . rules ) {
if ( ! emoji . isEmoji ( rule . icon ) ) {
result . push ( { path : rule . rule , icon : rule . icon } ) ;
}
}
return result ;
} ;
/ * *
* Returns the { @ link Icon } for the given icon name . It is important , that the icon name
* contains the icon pack prefix .
* @ param iconNameWithPrefix String that contains the icon pack prefix combined with the
* icon name .
* @ returns Icon if it exists , ` null ` otherwise .
* /
const getIconByName = ( iconNameWithPrefix ) => {
const iconNextIdentifier = nextIdentifier ( iconNameWithPrefix ) ;
const iconName = iconNameWithPrefix . substring ( iconNextIdentifier ) ;
const iconPrefix = iconNameWithPrefix . substring ( 0 , iconNextIdentifier ) ;
const iconPackName = getIconPackNameByPrefix ( iconPrefix ) ;
const icon = getIconFromIconPack ( iconPackName , iconPrefix , iconName ) ;
if ( ! icon ) {
return null ;
}
return icon ;
} ;
/ * *
* Returns the { @ link Icon } for the given path .
* @ param plugin IconFolderPlugin instance .
* @ param path String which is the path to get the icon of .
* @ returns Icon or Emoji as string if it exists , ` null ` otherwise .
* /
const getIconByPath = ( plugin , path ) => {
const iconNameWithPrefix = getByPath ( plugin , path ) ;
if ( ! iconNameWithPrefix ) {
return null ;
}
if ( emoji . isEmoji ( iconNameWithPrefix ) ) {
return iconNameWithPrefix ;
}
return getIconByName ( iconNameWithPrefix ) ;
} ;
var icon = {
addAll ,
getByPath ,
getAllWithPath ,
getIconByPath ,
getIconByName ,
checkMissingIcons ,
} ;
class ToggleIconInTitle extends IconFolderSetting {
display ( ) {
new obsidian . Setting ( this . containerEl )
. setName ( 'Toggle icon in title' )
. setDesc ( 'Toggles the visibility of an icon above the title of a file.' )
. addToggle ( ( toggle ) => {
toggle
. setValue ( this . plugin . getSettings ( ) . iconInTitleEnabled )
. onChange ( ( enabled ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . plugin . getSettings ( ) . iconInTitleEnabled = enabled ;
yield this . plugin . saveIconFolderData ( ) ;
// Updates the already opened files.
this . plugin . app . workspace
. getLeavesOfType ( 'markdown' )
. forEach ( ( leaf ) => {
const view = leaf . view ;
if ( view instanceof obsidian . MarkdownView ) {
const foundIcon = icon . getIconByPath ( this . plugin , view . file . path ) ;
if ( foundIcon && enabled ) {
const content = typeof foundIcon === 'string'
? foundIcon
: foundIcon . svgElement ;
titleIcon . add ( this . plugin , view . inlineTitleEl , content , {
fontSize : calculateInlineTitleSize ( ) ,
} ) ;
}
else {
titleIcon . hide ( view . contentEl ) ;
}
}
} ) ;
} ) ) ;
} ) ;
}
}
class FrontmatterOptions extends IconFolderSetting {
display ( ) {
new obsidian . Setting ( this . containerEl )
. setName ( 'Use icon in frontmatter' )
. setDesc ( 'Toggles whether to set the icon based on the frontmatter property `icon`.' )
. addToggle ( ( toggle ) => {
toggle
. setValue ( this . plugin . getSettings ( ) . iconInFrontmatterEnabled )
. onChange ( ( enabled ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . plugin . getSettings ( ) . iconInFrontmatterEnabled = enabled ;
yield this . plugin . saveIconFolderData ( ) ;
} ) ) ;
} ) ;
new obsidian . Setting ( this . containerEl )
. setName ( 'Frontmatter icon field name' )
. setDesc ( 'Sets the name of the frontmatter field which contains the icon.' )
. addText ( ( text ) => {
this . iconFieldNameTextComp = text ;
text . setValue ( this . plugin . getSettings ( ) . iconInFrontmatterFieldName ) ;
} )
. addButton ( ( button ) => {
button . setButtonText ( 'Save' ) ;
button . onClick ( ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
const newValue = this . iconFieldNameTextComp . getValue ( ) ;
const oldValue = this . plugin . getSettings ( ) . iconInFrontmatterFieldName ;
if ( newValue === oldValue ) {
return ;
}
this . plugin . getSettings ( ) . iconInFrontmatterFieldName = newValue ;
yield this . plugin . saveIconFolderData ( ) ;
new obsidian . Notice ( '...saved successfully' ) ;
} ) ) ;
} ) ;
new obsidian . Setting ( this . containerEl )
. setName ( 'Frontmatter icon color field name' )
. setDesc ( 'Sets the name of the frontmatter field which contains the icon color.' )
. addText ( ( text ) => {
this . iconColorFieldNameTextComp = text ;
text . setValue ( this . plugin . getSettings ( ) . iconColorInFrontmatterFieldName ) ;
} )
. addButton ( ( button ) => {
button . setButtonText ( 'Save' ) ;
button . onClick ( ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
const newValue = this . iconColorFieldNameTextComp . getValue ( ) ;
const oldValue = this . plugin . getSettings ( ) . iconColorInFrontmatterFieldName ;
if ( newValue === oldValue ) {
return ;
}
this . plugin . getSettings ( ) . iconColorInFrontmatterFieldName = newValue ;
yield this . plugin . saveIconFolderData ( ) ;
new obsidian . Notice ( '...saved successfully' ) ;
} ) ) ;
} ) ;
}
}
class ToggleIconsInEditor extends IconFolderSetting {
display ( ) {
new obsidian . Setting ( this . containerEl )
. setName ( 'Toggle icons while editing notes' )
. setDesc ( 'Toggles whether you are able to add and see icons in your notes and editor (e.g., ability to have :LiSofa: as an icon in your notes).' )
. addToggle ( ( toggle ) => {
toggle
. setValue ( this . plugin . getSettings ( ) . iconsInNotesEnabled )
. onChange ( ( enabled ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . plugin . getSettings ( ) . iconsInNotesEnabled = enabled ;
yield this . plugin . saveIconFolderData ( ) ;
new obsidian . Notice ( ` [ ${ config . PLUGIN _NAME } ] Obsidian has to be restarted for this change to take effect. ` ) ;
} ) ) ;
} ) ;
}
}
class ToggleIconsInLinks extends IconFolderSetting {
display ( ) {
new obsidian . Setting ( this . containerEl )
. setName ( 'Toggle icons in links' )
. setDesc ( 'Toggles whether you are able to see icons in the links to other notes' )
. addToggle ( ( toggle ) => {
toggle
. setValue ( this . plugin . getSettings ( ) . iconsInLinksEnabled )
. onChange ( ( enabled ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . plugin . getSettings ( ) . iconsInLinksEnabled = enabled ;
yield this . plugin . saveIconFolderData ( ) ;
new obsidian . Notice ( ` [ ${ config . PLUGIN _NAME } ] Obsidian has to be restarted for this change to take effect. ` ) ;
} ) ) ;
} ) ;
}
}
class IconIdentifierSetting extends IconFolderSetting {
display ( ) {
const setting = new obsidian . Setting ( this . containerEl )
. setName ( 'Icon identifier' )
. setDesc ( 'Change the icon identifier used in notes.' )
. setClass ( 'iconize-setting' ) ;
setting . addText ( ( text ) => {
this . textComp = text ;
text . setValue ( this . plugin . getSettings ( ) . iconIdentifier ) ;
} ) ;
setting . addButton ( ( btn ) => {
btn . setButtonText ( 'Save' ) ;
btn . onClick ( ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
const newIdentifier = this . textComp . getValue ( ) ;
const oldIdentifier = this . plugin . getSettings ( ) . iconIdentifier ;
if ( newIdentifier === oldIdentifier ) {
return ;
}
this . plugin . getSettings ( ) . iconIdentifier = newIdentifier ;
yield this . plugin . saveIconFolderData ( ) ;
new obsidian . Notice ( '...saved successfully' ) ;
} ) ) ;
} ) ;
}
}
class DebugMode extends IconFolderSetting {
display ( ) {
new obsidian . Setting ( this . containerEl )
. setName ( 'Toggle Debug Mode' )
. setDesc ( 'Toggle debug mode to see more detailed logs in the console. Do not touch this unless you know what you are doing.' )
. addToggle ( ( toggle ) => {
toggle
. setValue ( this . plugin . getSettings ( ) . debugMode )
. onChange ( ( enabled ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . plugin . getSettings ( ) . debugMode = enabled ;
yield this . plugin . saveIconFolderData ( ) ;
} ) ) ;
} ) ;
}
}
class IconFolderSettings extends obsidian . PluginSettingTab {
constructor ( app , plugin ) {
super ( app , plugin ) ;
this . plugin = plugin ;
}
display ( ) {
const { plugin , containerEl , app } = this ;
containerEl . empty ( ) ;
containerEl . createEl ( 'h1' , { text : 'General' } ) ;
new RecentlyUsedIconsSetting ( plugin , containerEl ) . display ( ) ;
new IconPacksPathSetting ( plugin , containerEl ) . display ( ) ;
new IconPacksBackgroundChecker ( plugin , containerEl ) . display ( ) ;
new EmojiStyleSetting ( plugin , containerEl ) . display ( ) ;
new IconIdentifierSetting ( plugin , containerEl ) . display ( ) ;
new DebugMode ( plugin , containerEl ) . display ( ) ;
containerEl . createEl ( 'h3' , { text : 'Visibility of icons' } ) ;
new ToggleIconInTabs ( plugin , containerEl ) . display ( ) ;
new ToggleIconInTitle ( plugin , containerEl ) . display ( ) ;
new FrontmatterOptions ( plugin , containerEl ) . display ( ) ;
new ToggleIconsInEditor ( plugin , containerEl ) . display ( ) ;
new ToggleIconsInLinks ( plugin , containerEl ) . display ( ) ;
containerEl . createEl ( 'h1' , {
text : 'Icon customization for files/folders' ,
} ) ;
new IconFontSizeSetting ( plugin , containerEl ) . display ( ) ;
new IconColorSetting ( plugin , containerEl ) . display ( ) ;
new ExtraMarginSetting ( plugin , containerEl ) . display ( ) ;
containerEl . createEl ( 'h1' , { text : 'Custom icon rules' } ) ;
new CustomIconRuleSetting ( plugin , containerEl , app , ( ) => this . display ( ) ) . display ( ) ;
containerEl . createEl ( 'h1' , { text : 'Icon packs' } ) ;
new PredefinedIconPacksSetting ( plugin , containerEl , app , ( ) => this . display ( ) ) . display ( ) ;
new CustomIconPackSetting ( plugin , containerEl , ( ) => 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' ) ;
}
// eslint-disable-next-line
onMount ( ) { }
}
/ * *
* @ deprecated After obsidian 1.2 . 6 in favor of the bookmarks plugin .
* /
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 iconName = icon . getByPath ( this . plugin , filePath ) ;
const iconNode = node . querySelector ( '.nav-file-icon' ) ;
if ( ! iconNode || ! iconName ) {
return ;
}
dom . setIconForNode ( this . plugin , iconName , 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 ( ` [ ${ config . PLUGIN _NAME } /Starred] Skipping starred internal plugin registration because file-explorer is not enabled. ` ) ;
return ;
}
if ( ! this . enabled ) {
console . info ( ` [ ${ config . PLUGIN _NAME } /Starred] Skipping starred internal plugin registration because it's not enabled. ` ) ;
return ;
}
// eslint-disable-next-line
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 BookmarkInternalPlugin extends InternalPluginInjector {
constructor ( plugin ) {
super ( plugin ) ;
}
get bookmark ( ) {
return this . plugin . app . internalPlugins . getPluginById ( 'bookmarks' ) ;
}
get enabled ( ) {
return this . plugin . app . internalPlugins . getPluginById ( 'bookmarks' ) . enabled ;
}
get leaf ( ) {
const leaf = this . plugin . app . workspace . getLeavesOfType ( 'bookmarks' ) ;
if ( ! leaf ) {
return undefined ;
}
if ( leaf . length === 1 ) {
return leaf [ 0 ] . view ;
}
return undefined ;
}
setIconOrRemove ( filePath , node ) {
var _a ;
const iconName = icon . getByPath ( this . plugin , filePath ) ;
let iconNode = node . querySelector ( '.tree-item-icon' ) ;
if ( ! iconName ) {
if ( iconNode ) {
// Reset the icon to the default obsidian icon.
const items = this . bookmark . instance . items ;
const item = items . find ( ( item ) => item . path === filePath ) ;
if ( ( item === null || item === void 0 ? void 0 : item . type ) === 'file' ) {
iconNode . innerHTML = DEFAULT _FILE _ICON ;
}
else if ( ( item === null || item === void 0 ? void 0 : item . type ) === 'folder' ) {
iconNode . innerHTML = DEFAULT _FOLDER _ICON ;
}
}
return ;
}
// If the icon node is not defined, then we need to recreate it.
if ( ! iconNode ) {
// Get the tree-item-self element where the original icon is set.
const treeItemSelf = node . querySelector ( '.tree-item-self' ) ;
if ( ! treeItemSelf ) {
return ;
}
iconNode = node . createDiv ( { cls : 'tree-item-icon' } ) ;
// Prepends the icon to the tree-item-self element as a first child.
treeItemSelf . prepend ( iconNode ) ;
}
const defaultMargin = iconNode . style . margin ;
const iconColor = ( _a = this . plugin . getIconColor ( filePath ) ) !== null && _a !== void 0 ? _a : this . plugin . getSettings ( ) . iconColor ;
dom . setIconForNode ( this . plugin , iconName , iconNode , iconColor ) ;
// Reset the margin to the default value to prevent overlapping with the text.
iconNode . style . margin = defaultMargin ;
}
computeNodesWithPath ( callback ) {
if ( ! this . leaf ) {
return ;
}
/ * *
* Retrieves the lookup item from the bookmark plugin and calls the callback with the
* element and the path of the item .
* @ param item BookmarkItem object which can be a folder or a file .
* @ param itemDoms WeakMap of the bookmark plugin which contains the lookup item .
* /
const retrieveLookupItem = ( item , itemDoms ) => {
const lookupItem = itemDoms . get ( item ) ;
if ( ! lookupItem ) {
return ;
}
if ( item . items ) {
// If the item is a folder, then we need to retrieve all the items inside it.
for ( const subItem of item . items ) {
retrieveLookupItem ( subItem , itemDoms ) ;
}
}
// If the item is a `file` or a `folder` (not of type `group`), then we can call the callback.
if ( item . type === 'file' || item . type === 'folder' ) {
callback ( lookupItem . el , item . path ) ;
}
} ;
const { itemDoms } = this . leaf ;
// Retrieves all the items of the bookmark plugin which areo objects.
const items = this . bookmark . instance . items ;
items . forEach ( ( item ) => {
retrieveLookupItem ( item , itemDoms ) ;
} ) ;
}
onMount ( ) {
const nodesWithPath = { } ;
this . computeNodesWithPath ( ( node , filePath ) => {
nodesWithPath [ filePath ] = node ;
} ) ;
Object . entries ( nodesWithPath ) . forEach ( ( [ filePath , node ] ) => this . setIconOrRemove ( filePath , node ) ) ;
}
register ( ) {
if ( ! this . plugin . app . internalPlugins . getPluginById ( 'file-explorer' ) . enabled ) {
console . info ( ` [ ${ config . PLUGIN _NAME } /Bookmarks] Skipping bookmark internal plugin registration because file-explorer is not enabled. ` ) ;
return ;
}
if ( ! this . enabled ) {
console . info ( ` [ ${ config . PLUGIN _NAME } /Bookmarks] Skipping bookmark internal plugin registration because it's not enabled. ` ) ;
return ;
}
// eslint-disable-next-line
const self = this ;
this . plugin . register ( around ( this . bookmark . instance , {
addItem : function ( next ) {
return function ( ... args ) {
next . call ( this , ... args ) ;
// TODO: Remove in the future, I could not think of a better way to do this.
setTimeout ( ( ) => {
self . onMount ( ) ;
} , 1000 ) ;
} ;
} ,
removeItem : function ( next ) {
return function ( ... args ) {
next . call ( this , ... args ) ;
self . onMount ( ) ;
} ;
} ,
} ) ) ;
}
}
class SuggestionIcon extends obsidian . EditorSuggest {
constructor ( app , plugin ) {
super ( app ) ;
this . plugin = plugin ;
}
onTrigger ( cursor , editor ) {
// Isolate shortcode starting position closest to the cursor.
const shortcodeStart = editor
. getLine ( cursor . line )
. substring ( 0 , cursor . ch )
. lastIndexOf ( this . plugin . getSettings ( ) . iconIdentifier ) ;
// `onTrigger` needs to return `null` as soon as possible to save processing performance.
if ( shortcodeStart === - 1 ) {
return null ;
}
// Regex for checking if the shortcode is not done yet.
const regex = new RegExp ( ` ^( ${ this . plugin . getSettings ( ) . iconIdentifier } ) \\ w+ $ ` , 'g' ) ;
const regexOngoingShortcode = editor
. getLine ( cursor . line )
. substring ( shortcodeStart , cursor . ch )
. match ( regex ) ;
if ( regexOngoingShortcode === null ) {
return null ;
}
const startingIndex = editor
. getLine ( cursor . line )
. indexOf ( regexOngoingShortcode [ 0 ] ) ;
return {
start : {
line : cursor . line ,
ch : startingIndex ,
} ,
end : {
line : cursor . line ,
ch : startingIndex + regexOngoingShortcode [ 0 ] . length ,
} ,
query : regexOngoingShortcode [ 0 ] ,
} ;
}
getSuggestions ( context ) {
const queryLowerCase = context . query
. substring ( this . plugin . getSettings ( ) . iconIdentifier . length )
. toLowerCase ( ) ;
// Store all icons corresponding to the current query.
const iconsNameArray = getAllLoadedIconNames ( )
. filter ( ( iconObject ) => {
const name = iconObject . prefix . toLowerCase ( ) + iconObject . name . toLowerCase ( ) ;
return name . toLowerCase ( ) . includes ( queryLowerCase ) ;
} )
. map ( ( iconObject ) => iconObject . prefix + iconObject . name ) ;
// Store all emojis correspoding to the current query - parsing whitespaces and
// colons for shortcodes compatibility.
const emojisNameArray = Object . keys ( emoji . shortNames ) . filter ( ( e ) => { var _a ; return ( _a = emoji . getShortcode ( e ) ) === null || _a === void 0 ? void 0 : _a . includes ( queryLowerCase ) ; } ) ;
return [ ... iconsNameArray , ... emojisNameArray ] ;
}
renderSuggestion ( value , el ) {
const iconObject = icon . getIconByName ( value ) ;
el . style . display = 'flex' ;
el . style . alignItems = 'center' ;
el . style . gap = '0.25rem' ;
if ( iconObject ) {
// Suggest an icon.
el . innerHTML = ` ${ iconObject . svgElement } <span> ${ value } </span> ` ;
}
else {
// Suggest an emoji - display its shortcode version.
const shortcode = emoji . getShortcode ( value ) ;
if ( shortcode ) {
el . innerHTML = ` <span> ${ value } </span> <span> ${ shortcode } </span> ` ;
}
}
}
selectSuggestion ( value ) {
const isEmoji = emoji . isEmoji ( value . replace ( /_/g , ' ' ) ) ;
if ( ! isEmoji ) {
saveIconToIconPack ( this . plugin , value ) ;
}
// Replace query with iconNameWithPrefix or emoji unicode directly.
const updatedValue = isEmoji
? value
: ` ${ this . plugin . getSettings ( ) . iconIdentifier } ${ value } ${ this . plugin . getSettings ( ) . iconIdentifier } ` ;
this . context . editor . replaceRange ( updatedValue , this . context . start , this . context . end ) ;
}
}
class IconInTextWidget extends view . WidgetType {
constructor ( plugin , id ) {
super ( ) ;
this . plugin = plugin ;
this . id = id ;
this . start = - 1 ;
this . end = - 1 ;
}
setPosition ( start , end ) {
this . start = start ;
this . end = end ;
}
eq ( other ) {
return other instanceof IconInTextWidget && other . id === this . id ;
}
getSize ( view ) {
let fontSize = calculateFontTextSize ( ) ;
const line = view . state . doc . lineAt ( this . end ) ;
const headerMatch = line . text . match ( /^#{1,6}\s/ ) ;
if ( headerMatch && headerMatch [ 0 ] . trim ( ) ) {
const mapping = {
'#' : 'h1' ,
'##' : 'h2' ,
'###' : 'h3' ,
'####' : 'h4' ,
'#####' : 'h5' ,
'######' : 'h6' ,
} ;
const header = mapping [ headerMatch [ 0 ] . trim ( ) ] ;
fontSize = calculateHeaderSize ( header ) ;
}
return fontSize ;
}
toDOM ( view ) {
const wrap = createSpan ( {
cls : 'cm-iconize-icon' ,
attr : {
'aria-label' : this . id ,
'data-icon' : this . id ,
'aria-hidden' : 'true' ,
} ,
} ) ;
const foundIcon = icon . getIconByName ( this . id ) ;
const fontSize = this . getSize ( view ) ;
if ( foundIcon ) {
const svgElement = svg . setFontSize ( foundIcon . svgElement , fontSize ) ;
wrap . style . display = 'inline-flex' ;
wrap . style . transform = 'translateY(13%)' ;
wrap . innerHTML = svgElement ;
}
else if ( emoji . isEmoji ( this . id ) ) {
wrap . innerHTML = emoji . parseEmoji ( this . plugin . getSettings ( ) . emojiStyle , this . id , fontSize ) ;
}
else {
wrap . append ( ` ${ this . plugin . getSettings ( ) . iconIdentifier } ${ this . id } ${ this . plugin . getSettings ( ) . iconIdentifier } ` ) ;
}
return wrap ;
}
ignoreEvent ( ) {
return false ;
}
}
class IconInLinkWidget extends view . WidgetType {
constructor ( plugin , iconData , path ) {
super ( ) ;
this . plugin = plugin ;
this . iconData = iconData ;
this . path = path ;
}
toDOM ( ) {
var _a ;
const iconNode = document . createElement ( 'span' ) ;
const iconName = typeof this . iconData === 'string'
? this . iconData
: this . iconData . prefix + this . iconData . name ;
iconNode . style . color =
( _a = this . plugin . getIconColor ( this . path ) ) !== null && _a !== void 0 ? _a : this . plugin . getSettings ( ) . iconColor ;
iconNode . setAttribute ( 'title' , iconName ) ;
iconNode . classList . add ( 'iconize-icon-in-link' ) ;
if ( typeof this . iconData === 'string' ) {
iconNode . style . transform = 'translateY(0)' ;
}
let innerHTML = typeof this . iconData === 'string'
? this . iconData
: this . iconData . svgElement ;
if ( emoji . isEmoji ( innerHTML ) ) {
innerHTML = emoji . parseEmoji ( this . plugin . getSettings ( ) . emojiStyle , innerHTML ) ;
}
iconNode . innerHTML = innerHTML ;
return iconNode ;
}
ignoreEvent ( ) {
return true ;
}
}
const buildLinkDecorations = ( view$1 , plugin ) => {
const builder = new state . RangeSetBuilder ( ) ;
const mdView = view$1 . state . field ( obsidian . editorInfoField ) ;
for ( const { from , to } of view$1 . visibleRanges ) {
language . syntaxTree ( view$1 . state ) . iterate ( {
from ,
to ,
enter : ( node ) => {
const tokenProps = node . type . prop ( language . tokenClassNodeProp ) ;
if ( tokenProps ) {
const props = new Set ( tokenProps . split ( ' ' ) ) ;
const isLink = props . has ( 'hmd-internal-link' ) ;
if ( isLink ) {
let linkText = view$1 . state . doc . sliceString ( node . from , node . to ) ;
linkText = linkText . split ( '#' ) [ 0 ] ;
const file = plugin . app . metadataCache . getFirstLinkpathDest ( linkText , mdView . file . basename ) ;
if ( file ) {
const possibleIcon = icon . getIconByPath ( plugin , file . path ) ;
if ( possibleIcon ) {
const iconDecoration = view . Decoration . widget ( {
widget : new IconInLinkWidget ( plugin , possibleIcon , file . path ) ,
} ) ;
builder . add ( node . from , node . from , iconDecoration ) ;
}
}
}
}
} ,
} ) ;
}
return builder . finish ( ) ;
} ;
const buildTextDecorations = ( view$1 , plugin ) => {
const ranges = [ ] ;
const iconInfo = view$1 . state . field ( plugin . positionField ) ;
for ( const { from , to } of view$1 . visibleRanges ) {
iconInfo . between ( from - 1 , to + 1 , ( from , to , { iconId } ) => {
ranges . push ( [ iconId , from , to ] ) ;
} ) ;
}
return view . Decoration . set ( ranges . map ( ( [ code , from , to ] ) => {
const widget = new IconInTextWidget ( plugin , code ) ;
widget . setPosition ( from , to ) ;
if ( view$1 . state . field ( obsidian . editorLivePreviewField ) ) {
return view . Decoration . replace ( {
widget ,
side : - 1 ,
} ) . range ( from , to ) ;
}
return view . Decoration . widget ( {
widget ,
side : - 1 ,
} ) . range ( to ) ;
} ) , true ) ;
} ;
const buildIconInTextPlugin = ( plugin ) => {
return view . ViewPlugin . fromClass ( class IconPlugin {
constructor ( view ) {
this . plugin = plugin ;
this . decorations = buildTextDecorations ( view , plugin ) ;
}
update ( update ) {
this . decorations = buildTextDecorations ( update . view , this . plugin ) ;
}
} , {
decorations : ( v ) => v . decorations ,
provide : ( plugin ) => view . EditorView . atomicRanges . of ( ( view$1 ) => {
const value = view$1 . plugin ( plugin ) ;
return value ? value . decorations : view . Decoration . none ;
} ) ,
} ) ;
} ;
const buildIconInLinksPlugin = ( plugin ) => {
return view . ViewPlugin . fromClass ( class {
constructor ( view ) {
this . plugin = plugin ;
this . decorations = buildLinkDecorations ( view , plugin ) ;
}
destroy ( ) { }
update ( update ) {
if ( update . docChanged || update . viewportChanged ) {
this . decorations = buildLinkDecorations ( update . view , this . plugin ) ;
}
}
} , {
decorations : ( v ) => v . decorations ,
} ) ;
} ;
// TODO: Optimize the code to reduce the number of iterations and improve the
function checkForSourceMode ( plugin ) {
let isSourceMode = false ;
// Iterate over all leaves to check if any is in source mode
plugin . app . workspace . iterateAllLeaves ( ( leaf ) => {
var _a ;
if ( ! isSourceMode && leaf . view . getViewType ( ) === 'markdown' ) {
if ( ( _a = leaf . getViewState ( ) . state ) === null || _a === void 0 ? void 0 : _a . source ) {
isSourceMode = true ;
}
}
} ) ;
return isSourceMode ;
}
class IconPosition extends state . RangeValue {
constructor ( text ) {
super ( ) ;
this . text = text ;
}
get iconId ( ) {
return this . text ;
}
eq ( other ) {
return other instanceof IconPosition && other . text === this . text ;
}
}
/ * *
* Builds a position field for the editor state . This field will track the
* positions of the icons in the document .
* * /
const buildPositionField = ( plugin ) => {
/ * *
* Checks the ranges of the icons in the document . If the range is not
* excluded , the range is added to the range set . If the range is excluded ,
* the range is removed from the range set .
* @ param state EditorState to get the ranges from .
* @ param excludeFrom Number to exclude from the ranges .
* @ param excludeTo Number to exclude to the ranges .
* @ param updateRange Function callback to update the range .
* /
const checkRanges = ( state , excludeFrom , excludeTo , updateRange ) => {
const isSourceMode = checkForSourceMode ( plugin ) ;
const text = state . doc . sliceString ( 0 , state . doc . length ) ;
const identifier = plugin . getSettings ( ) . iconIdentifier ;
const regex = new RegExp ( ` ( ${ identifier } )(( \\ w{1,64}: \\ d{17,18})|( \\ w{1,64}))( ${ identifier } ) ` , 'g' ) ;
for ( const { 0 : rawCode , index : offset } of text . matchAll ( regex ) ) {
const iconName = rawCode . substring ( identifier . length , rawCode . length - identifier . length ) ;
if ( ! icon . getIconByName ( iconName ) ) {
continue ;
}
const from = offset ;
const to = offset + rawCode . length ;
if ( ! isNodeInRangeAccepted ( state , from , to ) ) {
continue ;
}
if ( offset < excludeFrom || offset > excludeTo ) {
updateRange ( from , to , new IconPosition ( iconName ) , isSourceMode ) ;
continue ;
}
updateRange ( from , to , new IconPosition ( iconName ) , true ) ;
}
for ( const { 0 : emojiName , index : offset } of text . matchAll ( emoji . getRegex ( ) ) ) {
if ( ! emoji . isEmoji ( emojiName ) ) {
continue ;
}
const from = offset ;
const to = offset + emojiName . length ;
if ( ! isNodeInRangeAccepted ( state , from , to ) ) {
continue ;
}
if ( offset < excludeFrom || offset > excludeTo ) {
updateRange ( from , to , new IconPosition ( emojiName ) , isSourceMode ) ;
continue ;
}
updateRange ( from , to , new IconPosition ( emojiName ) , true ) ;
}
} ;
const isNodeInRangeAccepted = ( state , from , to ) => {
let isRangeAccepted = true ;
language . syntaxTree ( state ) . iterate ( {
from ,
to ,
enter : ( { type } ) => {
var _a ;
if ( type . name === 'Document' ) {
return ;
}
const allowedNodeTypes = [
'header' ,
'strong' ,
'em' ,
'quote' ,
'link' ,
'list-1' ,
'list-2' ,
'list-3' ,
'highlight' ,
'footref' ,
'comment' ,
'link-alias' ,
] ;
const excludedNodeTypes = [
'formatting' ,
'hmd-codeblock' ,
'inline-code' ,
'hr' ,
] ;
const nodeProps = ( _a = type . prop ( language . tokenClassNodeProp ) ) !== null && _a !== void 0 ? _a : '' ;
const s = new Set ( nodeProps . split ( ' ' ) ) ;
if ( excludedNodeTypes . some ( ( t ) => s . has ( t ) ) &&
allowedNodeTypes . every ( ( t ) => ! s . has ( t ) ) ) {
isRangeAccepted = false ;
}
} ,
} ) ;
return isRangeAccepted ;
} ;
return state . StateField . define ( {
create : ( state$1 ) => {
const rangeSet = new state . RangeSetBuilder ( ) ;
const changedLines = [ ] ;
checkRanges ( state$1 , - 1 , - 1 , ( from , to , iconPosition ) => {
changedLines . push ( { from , to , iconPosition } ) ;
} ) ;
changedLines . sort ( ( a , b ) => a . from - b . from ) ;
for ( const { from , to , iconPosition } of changedLines ) {
rangeSet . add ( from , to , iconPosition ) ;
}
return rangeSet . finish ( ) ;
} ,
update : ( rangeSet , transaction ) => {
const newRanges = [ ] ;
if ( ! transaction . docChanged ) {
if ( transaction . selection ) {
const from = transaction . selection . ranges [ 0 ] . from ;
const to = transaction . selection . ranges [ 0 ] . to ;
const lineEnd = transaction . state . doc . lineAt ( to ) . length ;
const lineStart = transaction . state . doc . lineAt ( from ) . from ;
// Checks the ranges of the icons in the document except for the
// excluded line start and end.
checkRanges ( transaction . state , lineStart , lineStart + lineEnd , ( from , to , value , removed ) => {
rangeSet = rangeSet . update ( {
filterFrom : from ,
filterTo : to ,
filter : ( ) => false ,
} ) ;
if ( ! removed ) {
newRanges . push ( value . range ( from , to ) ) ;
}
} ) ;
}
else {
checkRanges ( transaction . state , - 1 , - 1 , ( from , to , value , removed ) => {
rangeSet = rangeSet . update ( {
filterFrom : from ,
filterTo : to ,
filter : ( ) => false ,
} ) ;
if ( ! removed ) {
newRanges . push ( value . range ( from , to ) ) ;
}
} ) ;
}
newRanges . sort ( ( a , b ) => a . from - b . from ) ;
rangeSet = rangeSet . update ( { add : newRanges } ) ;
return rangeSet ;
}
rangeSet = rangeSet . map ( transaction . changes ) ;
const changedLines = [ ] ;
transaction . changes . iterChangedRanges ( ( _f , _t , from , to ) => {
changedLines . push ( [
transaction . state . doc . lineAt ( from ) . number ,
transaction . state . doc . lineAt ( to ) . number ,
] ) ;
} ) ;
for ( const [ start , end ] of changedLines ) {
const from = transaction . state . doc . line ( start ) . from ;
const to = transaction . state . doc . line ( end ) . to ;
rangeSet = rangeSet . update ( {
filterFrom : from ,
filterTo : to ,
filter : ( ) => false ,
} ) ;
const lineEnd = transaction . state . doc . line ( end ) . length ;
const lineStart = transaction . state . doc . line ( end ) . from ;
// Checks the ranges of the icons in the document except for the excluded
// line start and end.
checkRanges ( transaction . state , lineStart , lineStart + lineEnd , ( from , to , value , removed ) => {
if ( ! removed ) {
newRanges . push ( value . range ( from , to ) ) ;
}
} ) ;
}
newRanges . sort ( ( a , b ) => a . from - b . from ) ;
rangeSet = rangeSet . update ( { add : newRanges } ) ;
return rangeSet ;
} ,
} ) ;
} ;
// TODO: Optimize the code to reduce the number of iterations and improve the
const createIconShortcodeRegex = ( plugin ) => {
return new RegExp ( ` ( ${ plugin . getSettings ( ) . iconIdentifier } )(( \\ w{1,64}: \\ d{17,18})|( \\ w{1,64}))( ${ plugin . getSettings ( ) . iconIdentifier } ) ` , 'g' ) ;
} ;
const createTreeWalker = ( plugin , root ) => {
return document . createTreeWalker ( root , NodeFilter . SHOW _ALL , {
acceptNode : function ( node ) {
if ( node . nodeName === 'CODE' ) {
return NodeFilter . FILTER _REJECT ;
}
else if ( node . nodeName === '#text' ) {
if ( node . nodeValue &&
( emoji . getRegex ( ) . test ( node . nodeValue ) ||
createIconShortcodeRegex ( plugin ) . test ( node . nodeValue ) ) ) {
return NodeFilter . FILTER _ACCEPT ;
}
else {
return NodeFilter . FILTER _REJECT ;
}
}
return NodeFilter . FILTER _SKIP ;
} ,
} ) ;
} ;
const checkForTextNodes = ( treeWalker , match , cb ) => {
let currentNode = treeWalker . currentNode ;
while ( currentNode ) {
if ( currentNode . nodeType === Node . TEXT _NODE ) {
const text = currentNode ;
const textNodes = [ ... Array . from ( text . parentElement . childNodes ) ] . filter ( ( n ) => n instanceof Text ) ;
for ( const text of textNodes ) {
for ( const code of [ ... text . wholeText . matchAll ( match ) ]
. sort ( ( a , b ) => b . index - a . index )
. map ( ( arr ) => ( { text : arr [ 0 ] , index : arr . index } ) ) ) {
if ( ! text . textContent ) {
continue ;
}
cb ( text , code ) ;
}
}
}
currentNode = treeWalker . nextNode ( ) ;
}
} ;
const processIconInTextMarkdown = ( plugin , element ) => {
// Ignore if codeblock
const codeElement = element . querySelector ( 'pre > code' ) ;
if ( codeElement ) {
return ;
}
const iconTreeWalker = createTreeWalker ( plugin , element ) ;
const iconShortcodeRegex = createIconShortcodeRegex ( plugin ) ;
const iconIdentifierLength = plugin . getSettings ( ) . iconIdentifier . length ;
checkForTextNodes ( iconTreeWalker , iconShortcodeRegex , ( text , code ) => {
var _a , _b , _c , _d ;
const shortcode = code . text ;
const iconName = shortcode . slice ( iconIdentifierLength , shortcode . length - iconIdentifierLength ) ;
const iconObject = icon . getIconByName ( iconName ) ;
if ( iconObject ) {
const toReplace = text . splitText ( code . index ) ;
const rootSpan = createSpan ( {
cls : 'cm-iconize-icon' ,
attr : {
'aria-label' : iconName ,
'data-icon' : iconName ,
'aria-hidden' : 'true' ,
} ,
} ) ;
rootSpan . style . display = 'inline-flex' ;
rootSpan . style . transform = 'translateY(13%)' ;
const tagName = ( _c = ( _b = ( _a = toReplace . parentElement ) === null || _a === void 0 ? void 0 : _a . tagName ) === null || _b === void 0 ? void 0 : _b . toLowerCase ( ) ) !== null && _c !== void 0 ? _c : '' ;
let fontSize = calculateFontTextSize ( ) ;
if ( isHeader ( tagName ) ) {
fontSize = calculateHeaderSize ( tagName ) ;
const svgElement = svg . setFontSize ( iconObject . svgElement , fontSize ) ;
rootSpan . innerHTML = svgElement ;
}
else {
const svgElement = svg . setFontSize ( iconObject . svgElement , fontSize ) ;
rootSpan . innerHTML = svgElement ;
}
( _d = toReplace . parentElement ) === null || _d === void 0 ? void 0 : _d . insertBefore ( rootSpan , toReplace ) ;
toReplace . textContent = toReplace . wholeText . substring ( code . text . length ) ;
}
} ) ;
const emojiTreeWalker = createTreeWalker ( plugin , element ) ;
checkForTextNodes ( emojiTreeWalker , emoji . getRegex ( ) , ( text , code ) => {
var _a , _b , _c , _d ;
if ( ! emoji . isEmoji ( code . text ) ) {
return ;
}
if ( plugin . getSettings ( ) . emojiStyle === 'twemoji' ) {
const toReplace = text . splitText ( code . index ) ;
const tagName = ( _c = ( _b = ( _a = toReplace . parentElement ) === null || _a === void 0 ? void 0 : _a . tagName ) === null || _b === void 0 ? void 0 : _b . toLowerCase ( ) ) !== null && _c !== void 0 ? _c : '' ;
let fontSize = calculateFontTextSize ( ) ;
if ( isHeader ( tagName ) ) {
fontSize = calculateHeaderSize ( tagName ) ;
}
const emojiValue = emoji . parseEmoji ( plugin . getSettings ( ) . emojiStyle , code . text , fontSize ) ;
if ( ! emojiValue ) {
return ;
}
const emojiNode = createSpan ( ) ;
emojiNode . innerHTML = emojiValue ;
( _d = toReplace . parentElement ) === null || _d === void 0 ? void 0 : _d . insertBefore ( emojiNode , toReplace ) ;
toReplace . textContent = toReplace . wholeText . substring ( code . text . length ) ;
}
} ) ;
} ;
class ConsoleLogger {
constructor ( projectPrefix , enabled = false ) {
this . logLevels = {
log : { label : 'LOG:' } ,
info : { label : 'INFO:' } ,
warn : { label : 'WARN:' } ,
error : { label : 'ERROR:' } ,
} ;
this . projectPrefix = projectPrefix ;
this . enabled = enabled ;
}
formatMessage ( level , message , optionalParams ) {
const timestamp = new Date ( ) . toISOString ( ) ;
const { label } = this . logLevels [ level ] ;
return [
` ${ this . projectPrefix } : [ ${ timestamp } ] ${ label } ${ message } ` ,
... optionalParams ,
] ;
}
log ( message , ... optionalParams ) {
if ( this . enabled ) {
console . log ( ... this . formatMessage ( 'log' , message , optionalParams ) ) ;
}
}
info ( message , ... optionalParams ) {
if ( this . enabled ) {
console . info ( ... this . formatMessage ( 'info' , message , optionalParams ) ) ;
}
}
warn ( message , ... optionalParams ) {
if ( this . enabled ) {
console . warn ( ... this . formatMessage ( 'warn' , message , optionalParams ) ) ;
}
}
error ( message , ... optionalParams ) {
if ( this . enabled ) {
console . error ( ... this . formatMessage ( 'error' , message , optionalParams ) ) ;
}
}
toggleLogging ( enabled ) {
this . enabled = enabled ;
}
}
const logger = new ConsoleLogger ( config . PLUGIN _NAME ) ;
const processIconInLinkMarkdown = ( plugin , element , ctx ) => {
const linkElements = element . querySelectorAll ( 'a' ) ;
if ( ! linkElements || linkElements . length === 0 ) {
return ;
}
linkElements . forEach ( ( linkElement ) => {
var _a , _b ;
// Skip if the link element e.g., is a tag.
if ( ! linkElement . hasAttribute ( 'data-href' ) ) {
return ;
}
const linkHref = linkElement . getAttribute ( 'href' ) ;
if ( ! linkHref ) {
logger . warn ( 'Link element does not have an `href` attribute.' ) ;
return ;
}
const file = plugin . app . metadataCache . getFirstLinkpathDest ( linkHref , ctx . sourcePath ) ;
if ( ! file ) {
logger . warn ( 'Link element does not have a linkpath to a file.' ) ;
return ;
}
const path = file . path ;
const iconValue = icon . getIconByPath ( plugin , path ) ;
if ( ! iconValue ) {
return ;
}
const iconName = typeof iconValue === 'string'
? iconValue
: iconValue . prefix + iconValue . name ;
const rootSpan = createSpan ( {
cls : 'iconize-icon-in-link' ,
attr : {
title : iconName ,
'aria-label' : iconName ,
'data-icon' : iconName ,
'aria-hidden' : 'true' ,
} ,
} ) ;
rootSpan . style . color =
( _a = plugin . getIconColor ( path ) ) !== null && _a !== void 0 ? _a : plugin . getSettings ( ) . iconColor ;
if ( emoji . isEmoji ( iconName ) ) {
const parsedEmoji = ( _b = emoji . parseEmoji ( plugin . getSettings ( ) . emojiStyle , iconName ) ) !== null && _b !== void 0 ? _b : iconName ;
rootSpan . innerHTML = parsedEmoji ;
}
else {
const svg = icon . getIconByName ( iconName ) . svgElement ;
if ( svg ) {
rootSpan . innerHTML = svg ;
}
}
linkElement . prepend ( rootSpan ) ;
} ) ;
} ;
class ChangeColorModal extends obsidian . Modal {
constructor ( app , plugin , path ) {
var _a ;
super ( app ) ;
this . plugin = plugin ;
this . path = path ;
this . usedColor = this . plugin . getIconColor ( this . path ) ;
this . contentEl . style . display = 'block' ;
this . modalEl . classList . add ( 'iconize-custom-modal' ) ;
this . titleEl . setText ( 'Change color' ) ;
const description = this . contentEl . createEl ( 'p' , {
text : 'Select a color for this icon' ,
cls : 'setting-item-description' ,
} ) ;
description . style . marginBottom = 'var(--size-2-2)' ;
const colorContainer = this . contentEl . createDiv ( ) ;
colorContainer . style . display = 'flex' ;
colorContainer . style . alignItems = 'center' ;
colorContainer . style . justifyContent = 'space-between' ;
const colorPicker = new obsidian . ColorComponent ( colorContainer )
. setValue ( ( _a = this . usedColor ) !== null && _a !== void 0 ? _a : '#000000' )
. onChange ( ( value ) => {
this . usedColor = value ;
} ) ;
const defaultColorButton = new obsidian . ButtonComponent ( colorContainer ) ;
defaultColorButton . setTooltip ( 'Set color to the default one' ) ;
defaultColorButton . setButtonText ( 'Reset' ) ;
defaultColorButton . onClick ( ( ) => {
colorPicker . setValue ( '#000000' ) ;
this . usedColor = undefined ;
} ) ;
// Save button.
const button = new obsidian . ButtonComponent ( this . contentEl ) ;
button . buttonEl . style . marginTop = 'var(--size-4-4)' ;
button . buttonEl . style . float = 'right' ;
button . setButtonText ( 'Save Changes' ) ;
button . onClick ( ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
var _b ;
new obsidian . Notice ( 'Color of icon changed.' ) ;
if ( this . usedColor ) {
this . plugin . addIconColor ( this . path , this . usedColor ) ;
}
else {
this . plugin . removeIconColor ( this . path ) ;
}
// Refresh the DOM.
const iconNode = dom . getIconNodeFromPath ( this . path ) ;
iconNode . style . color = ( _b = this . usedColor ) !== null && _b !== void 0 ? _b : null ;
const colorizedInnerHtml = svg . colorize ( iconNode . innerHTML , this . usedColor ) ;
iconNode . innerHTML = colorizedInnerHtml ;
this . close ( ) ;
} ) ) ;
}
onOpen ( ) {
super . onOpen ( ) ;
}
onClose ( ) {
const { contentEl } = this ;
contentEl . empty ( ) ;
}
}
class IconFolderPlugin extends obsidian . Plugin {
constructor ( ) {
super ( ... arguments ) ;
this . registeredFileExplorers = new Set ( ) ;
this . modifiedInternalPlugins = [ ] ;
this . positionField = buildPositionField ( this ) ;
this . frontmatterCache = new Set ( ) ;
}
onload ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
console . log ( ` loading ${ config . PLUGIN _NAME } ` ) ;
// Registers all modified internal plugins.
// Only adds star plugin for obsidian under v0.12.6.
if ( ! obsidian . requireApiVersion ( '0.12.6' ) ) {
this . modifiedInternalPlugins . push ( new StarredInternalPlugin ( this ) ) ;
}
else if ( obsidian . requireApiVersion ( '1.2.0' ) ) {
this . modifiedInternalPlugins . push ( new BookmarkInternalPlugin ( this ) ) ;
}
yield this . loadIconFolderData ( ) ;
logger . toggleLogging ( this . getSettings ( ) . debugMode ) ;
setPath ( this . getSettings ( ) . iconPacksPath ) ;
yield createDefaultDirectory ( this ) ;
yield this . checkRecentlyUsedIcons ( ) ;
yield migrate ( this ) ;
const usedIconNames = icon . getAllWithPath ( this ) . map ( ( value ) => value . icon ) ;
yield loadUsedIcons ( this , usedIconNames ) ;
this . app . workspace . onLayoutReady ( ( ) => this . handleChangeLayout ( ) ) ;
this . addCommand ( {
id : 'iconize:set-icon-for-file' ,
name : 'Set icon for file' ,
hotkeys : [
{
modifiers : [ 'Mod' , 'Shift' ] ,
key : 'j' ,
} ,
] ,
editorCallback : ( editor ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
var _a ;
const file = ( _a = editor . editorComponent ) === null || _a === void 0 ? void 0 : _a . file ;
if ( ! file ) {
logger . warn ( '`editor.editorComponent?.file` is undefined for file:' , file ) ;
return ;
}
const modal = new IconsPickerModal ( this . app , this , file . path ) ;
modal . open ( ) ;
modal . onSelect = ( iconName ) => {
IconCache . getInstance ( ) . set ( file . path , {
iconNameWithPrefix : iconName ,
} ) ;
// Update icon in tab when setting is enabled.
if ( this . getSettings ( ) . iconInTabsEnabled ) {
const tabLeaves = iconTabs . getTabLeavesOfFilePath ( this , file . path ) ;
for ( const tabLeaf of tabLeaves ) {
iconTabs . update ( this , iconName , tabLeaf . tabHeaderInnerIconEl ) ;
}
}
// Update icon in title when setting is enabled.
if ( this . getSettings ( ) . iconInTitleEnabled ) {
this . addIconInTitle ( iconName ) ;
}
} ;
} ) ,
} ) ;
this . registerEvent (
// Registering file menu event for listening to file pinning and unpinning.
this . app . workspace . on ( 'file-menu' , ( menu , file ) => {
// I've researched other ways of doing this. However, there is no other way to listen to file pinning and unpinning.
menu . onHide ( ( ) => {
const path = file . path ;
if ( this . getSettings ( ) . iconInTabsEnabled ) {
for ( const openedFile of getAllOpenedFiles ( this ) ) {
if ( openedFile . path === path ) {
const possibleIcon = IconCache . getInstance ( ) . get ( path ) ;
if ( ! possibleIcon ) {
return ;
}
const tabLeaves = iconTabs . getTabLeavesOfFilePath ( this , file . path ) ;
for ( const tabLeaf of tabLeaves ) {
// Add timeout to ensure that the default icon is already set.
setTimeout ( ( ) => {
iconTabs . add ( this , file , tabLeaf . tabHeaderInnerIconEl ) ;
} , 5 ) ;
}
}
}
}
} ) ;
} ) ) ;
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 ( ) ;
modal . onSelect = ( iconName ) => {
IconCache . getInstance ( ) . set ( file . path , {
iconNameWithPrefix : iconName ,
} ) ;
// Update icon in tab when setting is enabled.
if ( this . getSettings ( ) . iconInTabsEnabled ) {
const tabLeaves = iconTabs . getTabLeavesOfFilePath ( this , file . path ) ;
for ( const tabLeaf of tabLeaves ) {
iconTabs . update ( this , iconName , tabLeaf . tabHeaderInnerIconEl ) ;
}
}
// Update icon in title when setting is enabled.
if ( this . getSettings ( ) . iconInTitleEnabled ) {
this . addIconInTitle ( iconName ) ;
}
} ;
} ) ;
} ;
const removeIconMenuItem = ( item ) => {
item . setTitle ( 'Remove icon' ) ;
item . setIcon ( 'trash' ) ;
item . onClick ( ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
yield this . removeSingleIcon ( file ) ;
} ) ) ;
} ;
const changeColorOfIcon = ( item ) => {
item . setTitle ( 'Change color of icon' ) ;
item . setIcon ( 'palette' ) ;
item . onClick ( ( ) => {
const modal = new ChangeColorModal ( this . app , this , file . path ) ;
modal . open ( ) ;
} ) ;
} ;
menu . addItem ( addIconMenuItem ) ;
const filePathData = this . getData ( ) [ file . path ] ;
const hasNestedIcon = typeof filePathData === 'object' &&
filePathData . iconName !== null ;
// Only add remove icon menu item when the file path exists in the data.
// We do not want to show this menu item for e.g. custom rules.
if ( filePathData &&
( typeof filePathData === 'string' || hasNestedIcon ) ) {
const icon = typeof filePathData === 'string'
? filePathData
: filePathData . iconName ;
if ( ! emoji . isEmoji ( icon ) ) {
menu . addItem ( changeColorOfIcon ) ;
}
menu . addItem ( removeIconMenuItem ) ;
}
} ) ) ;
// 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 ) => {
// Check if the file was moved and had an icon before.
const dataPoint = this . data [ oldPath ] ;
if ( dataPoint && oldPath !== 'settings' ) {
const iconNameWithPrefix = typeof dataPoint === 'object'
? dataPoint . iconName
: dataPoint ;
dom . createIconNode ( this , file . path , iconNameWithPrefix ) ;
}
this . renameFolder ( file . path , oldPath ) ;
} ) ) ;
if ( this . getSettings ( ) . iconsInNotesEnabled ) {
this . registerMarkdownPostProcessor ( ( el ) => processIconInTextMarkdown ( this , el ) ) ;
this . registerEditorSuggest ( new SuggestionIcon ( this . app , this ) ) ;
this . registerEditorExtension ( [
this . positionField ,
buildIconInTextPlugin ( this ) ,
] ) ;
}
if ( this . getSettings ( ) . iconsInLinksEnabled ) {
this . registerMarkdownPostProcessor ( ( el , ctx ) => processIconInLinkMarkdown ( this , el , ctx ) ) ;
this . registerEditorExtension ( [
this . positionField ,
buildIconInLinksPlugin ( this ) ,
] ) ;
}
this . addSettingTab ( new IconFolderSettings ( this . app , this ) ) ;
} ) ;
}
isSomeEmojiStyleActive ( ) {
return this . getSettings ( ) . emojiStyle !== 'none' ;
}
notifyPlugins ( ) {
this . modifiedInternalPlugins . forEach ( ( internalPlugin ) => {
if ( internalPlugin . enabled ) {
internalPlugin . onMount ( ) ;
}
} ) ;
}
removeSingleIcon ( file ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . removeFolderIcon ( file . path ) ;
dom . removeIconInPath ( file . path ) ;
IconCache . getInstance ( ) . invalidate ( file . path ) ;
this . notifyPlugins ( ) ;
let didUpdate = false ;
// Refreshes the icon tab and title icon for custom rules.
for ( const rule of customRule . getSortedRules ( this ) ) {
const applicable = yield customRule . isApplicable ( this , rule , file ) ;
if ( applicable ) {
customRule . add ( this , rule , file ) ;
this . addIconInTitle ( rule . icon ) ;
const tabLeaves = iconTabs . getTabLeavesOfFilePath ( this , file . path ) ;
for ( const tabLeaf of tabLeaves ) {
iconTabs . add ( this , file , tabLeaf . tabHeaderInnerIconEl , {
iconName : rule . icon ,
} ) ;
}
didUpdate = true ;
break ;
}
}
// Only remove icon above titles and icon in tabs if no custom rule was found.
if ( ! didUpdate ) {
// Refreshes icons above title and icons in tabs.
for ( const openedFile of getAllOpenedFiles ( this ) ) {
if ( this . getSettings ( ) . iconInTitleEnabled ) {
titleIcon . remove ( openedFile . leaf . view . inlineTitleEl ) ;
}
if ( this . getSettings ( ) . iconInTabsEnabled ) {
const leaf = openedFile . leaf ;
iconTabs . remove ( leaf . tabHeaderInnerIconEl , {
replaceWithDefaultIcon : true ,
} ) ;
}
}
}
} ) ;
}
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 ( ) ;
}
} ) ;
icon . addAll ( this , data , this . registeredFileExplorers , ( ) => {
// After initialization of the icon packs, checks the vault for missing icons and
// adds them.
initIconPacks ( this ) . then ( ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
if ( this . getSettings ( ) . iconsBackgroundCheckEnabled ) {
const data = Object . entries ( this . data ) ;
yield icon . checkMissingIcons ( this , data ) ;
resetPreloadedIcons ( ) ;
}
} ) ) ;
if ( this . getSettings ( ) . iconInFrontmatterEnabled ) {
const activeFile = this . app . workspace . getActiveFile ( ) ;
if ( activeFile ) {
this . frontmatterCache . add ( activeFile . path ) ;
}
}
// Adds the title icon to the active leaf view.
if ( this . getSettings ( ) . iconInTitleEnabled ) {
for ( const openedFile of getAllOpenedFiles ( this ) ) {
const iconName = icon . getByPath ( this , openedFile . path ) ;
const activeView = openedFile . leaf . view ;
if ( activeView instanceof obsidian . MarkdownView && iconName ) {
let possibleIcon = iconName ;
if ( ! emoji . isEmoji ( iconName ) ) {
const iconNextIdentifier = nextIdentifier ( iconName ) ;
possibleIcon = getSvgFromLoadedIcon ( iconName . substring ( 0 , iconNextIdentifier ) , iconName . substring ( iconNextIdentifier ) ) ;
}
if ( possibleIcon ) {
titleIcon . add ( this , activeView . inlineTitleEl , possibleIcon , {
fontSize : calculateInlineTitleSize ( ) ,
} ) ;
}
}
}
}
// Register rename event for adding icons with custom rules to the DOM
// when file was moved to another directory.
this . registerEvent ( this . app . vault . on ( 'rename' , ( file , oldPath ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
const sortedRules = customRule . getSortedRules ( this ) ;
// Removes possible icons from the renamed file.
sortedRules . forEach ( ( rule ) => {
if ( customRule . doesMatchPath ( rule , oldPath ) ) {
dom . removeIconInPath ( file . path ) ;
}
} ) ;
// Adds possible icons to the renamed file.
sortedRules . forEach ( ( rule ) => {
if ( customRule . doesMatchPath ( rule , oldPath ) ) {
return ;
}
customRule . add ( this , rule , file , undefined ) ;
} ) ;
// Updates icon tabs for the renamed file.
for ( const rule of customRule . getSortedRules ( this ) ) {
const applicable = yield customRule . isApplicable ( this , rule , file ) ;
if ( ! applicable ) {
continue ;
}
const openedFiles = getAllOpenedFiles ( this ) ;
const openedFile = openedFiles . find ( ( openedFile ) => openedFile . path === file . path ) ;
if ( openedFile ) {
const leaf = openedFile . leaf ;
iconTabs . update ( this , rule . icon , leaf . tabHeaderInnerIconEl ) ;
}
break ;
}
} ) ) ) ;
// Register `layout-change` event for adding icons to tabs when moving a pane or
// enabling reading mode.
this . registerEvent ( this . app . workspace . on ( 'layout-change' , ( ) => {
var _a , _b ;
if ( this . getSettings ( ) . iconInTitleEnabled ) {
const activeView = this . app . workspace . getActiveViewOfType ( obsidian . MarkdownView ) ;
if ( activeView ) {
const file = activeView . file ;
const view = activeView . leaf . view . currentMode
. view ;
const iconNameWithPrefix = icon . getByPath ( this , file . path ) ;
if ( ! iconNameWithPrefix ) {
titleIcon . hide ( view . inlineTitleEl ) ;
return ;
}
let foundIcon = iconNameWithPrefix ;
if ( ! emoji . isEmoji ( foundIcon ) ) {
foundIcon = ( _a = icon . getIconByName ( iconNameWithPrefix ) ) === null || _a === void 0 ? void 0 : _a . svgElement ;
// Check for preloaded icons if no icon was found when the start up was faster
// than the loading of the icons.
if ( ! foundIcon && getPreloadedIcons ( ) . length > 0 ) {
foundIcon = ( _b = getPreloadedIcons ( ) . find ( ( icon ) => icon . prefix + icon . name === iconNameWithPrefix ) ) === null || _b === void 0 ? void 0 : _b . svgElement ;
}
}
if ( foundIcon ) {
// Removes the node because the editor markdown content is being rerendered
// when the content mode changes back to editing.
titleIcon . remove ( view . inlineTitleEl ) ;
titleIcon . add ( this , view . inlineTitleEl , foundIcon , {
fontSize : calculateInlineTitleSize ( ) ,
} ) ;
}
}
}
if ( ! this . getSettings ( ) . iconInTabsEnabled ) {
return ;
}
for ( const openedFile of getAllOpenedFiles ( this ) ) {
const leaf = openedFile . leaf ;
const iconColor = this . getIconColor ( leaf . view . file . path ) ;
iconTabs . add ( this , openedFile , leaf . tabHeaderInnerIconEl , {
iconColor ,
} ) ;
}
} ) ) ;
// Register `file-open` event for adding icon to title.
this . registerEvent ( this . app . workspace . on ( 'file-open' , ( file ) => {
var _a , _b ;
if ( ! this . getSettings ( ) . iconInTitleEnabled ) {
return ;
}
for ( const openedFile of getAllOpenedFiles ( this ) ) {
if ( openedFile . path !== file . path ) {
continue ;
}
const leaf = openedFile . leaf . view ;
const iconNameWithPrefix = icon . getByPath ( this , file . path ) ;
if ( ! iconNameWithPrefix ) {
titleIcon . hide ( leaf . inlineTitleEl ) ;
return ;
}
let foundIcon = iconNameWithPrefix ;
if ( ! emoji . isEmoji ( foundIcon ) ) {
foundIcon = ( _a = icon . getIconByName ( iconNameWithPrefix ) ) === null || _a === void 0 ? void 0 : _a . svgElement ;
// Check for preloaded icons if no icon was found when the start up was faster
// than the loading of the icons.
if ( ! foundIcon && getPreloadedIcons ( ) . length > 0 ) {
foundIcon = ( _b = getPreloadedIcons ( ) . find ( ( icon ) => icon . prefix + icon . name === iconNameWithPrefix ) ) === null || _b === void 0 ? void 0 : _b . svgElement ;
}
}
if ( foundIcon ) {
titleIcon . add ( this , leaf . inlineTitleEl , foundIcon , {
fontSize : calculateInlineTitleSize ( ) ,
} ) ;
}
else {
titleIcon . hide ( leaf . inlineTitleEl ) ;
}
}
} ) ) ;
// Register event for frontmatter icon registration.
this . registerEvent ( this . app . metadataCache . on ( 'resolve' , ( file ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
if ( ! this . getSettings ( ) . iconInFrontmatterEnabled ) {
return ;
}
const fileCache = this . app . metadataCache . getFileCache ( file ) ;
const iconFrontmatterName = this . getSettings ( ) . iconInFrontmatterFieldName ;
const iconColorFrontmatterName = this . getSettings ( ) . iconColorInFrontmatterFieldName ;
if ( fileCache === null || fileCache === void 0 ? void 0 : fileCache . frontmatter ) {
const { [ iconFrontmatterName ] : newIconName , [ iconColorFrontmatterName ] : newIconColor , } = fileCache . frontmatter ;
// If `icon` property is empty, we will remove it from the data and remove the icon.
if ( ! newIconName ) {
if ( this . frontmatterCache . has ( file . path ) ) {
yield this . removeSingleIcon ( file ) ;
this . frontmatterCache . delete ( file . path ) ;
}
return ;
}
if ( typeof newIconName !== 'string' ) {
new obsidian . Notice ( ` [ ${ config . PLUGIN _NAME } ] Frontmatter property type \` icon \` has to be of type \` text \` . ` ) ;
return ;
}
if ( newIconColor && typeof newIconColor !== 'string' ) {
new obsidian . Notice ( ` [ ${ config . PLUGIN _NAME } ] Frontmatter property type \` iconColor \` has to be of type \` text \` . ` ) ;
return ;
}
let iconColor = newIconColor ;
if ( isHexadecimal ( iconColor ) ) {
iconColor = stringToHex ( iconColor ) ;
}
const cachedIcon = IconCache . getInstance ( ) . get ( file . path ) ;
if ( newIconName === ( cachedIcon === null || cachedIcon === void 0 ? void 0 : cachedIcon . iconNameWithPrefix ) &&
iconColor === ( cachedIcon === null || cachedIcon === void 0 ? void 0 : cachedIcon . iconColor ) ) {
return ;
}
this . frontmatterCache . add ( file . path ) ;
try {
if ( ! emoji . isEmoji ( newIconName ) ) {
saveIconToIconPack ( this , newIconName ) ;
}
}
catch ( e ) {
console . error ( e ) ;
new obsidian . Notice ( e . message ) ;
return ;
}
dom . createIconNode ( this , file . path , newIconName , {
color : iconColor ,
} ) ;
this . addFolderIcon ( file . path , newIconName ) ;
this . addIconColor ( file . path , iconColor ) ;
IconCache . getInstance ( ) . set ( file . path , {
iconNameWithPrefix : newIconName ,
iconColor ,
} ) ;
// Update icon in tab when setting is enabled.
if ( this . getSettings ( ) . iconInTabsEnabled ) {
const tabLeaves = iconTabs . getTabLeavesOfFilePath ( this , file . path ) ;
for ( const tabLeaf of tabLeaves ) {
iconTabs . update ( this , newIconName , tabLeaf . tabHeaderInnerIconEl ) ;
}
}
// Update icon in title when setting is enabled.
if ( this . getSettings ( ) . iconInTitleEnabled ) {
this . addIconInTitle ( newIconName ) ;
}
}
} ) ) ) ;
// Register active leaf change event for adding icon of file to tab.
this . registerEvent ( this . app . workspace . on ( 'active-leaf-change' , ( leaf ) => {
if ( ! this . getSettings ( ) . iconInTabsEnabled ) {
return ;
}
// TODO: Maybe change in the future to a more optimal solution.
// Fixes a problem when the file was clicked twice in the same tab.
// See https://github.com/FlorianWoelki/obsidian-iconize/issues/208.
if ( leaf . view . getViewType ( ) === 'file-explorer' ) {
for ( const openedFile of getAllOpenedFiles ( this ) ) {
const leaf = openedFile . leaf ;
const iconColor = this . getIconColor ( leaf . view . file . path ) ;
iconTabs . add ( this , openedFile , leaf . tabHeaderInnerIconEl , {
iconColor ,
} ) ;
}
return ;
}
if ( leaf . view . getViewType ( ) !== 'markdown' ) {
return ;
}
const tabHeaderLeaf = leaf ;
if ( tabHeaderLeaf . view . file ) {
const iconColor = this . getIconColor ( tabHeaderLeaf . view . file . path ) ;
iconTabs . add ( this , tabHeaderLeaf . view . file , tabHeaderLeaf . tabHeaderInnerIconEl , {
iconColor ,
} ) ;
}
} ) ) ;
this . registerEvent ( this . app . workspace . on ( 'css-change' , ( ) => {
for ( const openedFile of getAllOpenedFiles ( this ) ) {
const activeView = openedFile . leaf . view ;
if ( activeView instanceof obsidian . MarkdownView ) {
titleIcon . updateStyle ( activeView . inlineTitleEl , {
fontSize : calculateInlineTitleSize ( ) ,
} ) ;
}
}
} ) ) ;
} ) ;
}
addIconInTitle ( iconName ) {
var _a ;
for ( const openedFile of getAllOpenedFiles ( this ) ) {
const activeView = openedFile . leaf . view ;
if ( activeView instanceof obsidian . MarkdownView ) {
let possibleIcon = iconName ;
if ( ! emoji . isEmoji ( iconName ) ) {
possibleIcon = ( _a = icon . getIconByName ( iconName ) ) === null || _a === void 0 ? void 0 : _a . svgElement ;
}
if ( possibleIcon ) {
titleIcon . add ( this , activeView . inlineTitleEl , possibleIcon , {
fontSize : calculateInlineTitleSize ( ) ,
} ) ;
}
}
}
}
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 ( ) ;
}
addIconColor ( path , iconColor ) {
const pathData = this . getData ( ) [ path ] ;
if ( typeof pathData === 'string' ) {
this . getData ( ) [ path ] = {
iconName : pathData ,
iconColor ,
} ;
}
else {
pathData . iconColor = iconColor ;
}
this . saveIconFolderData ( ) ;
}
getIconColor ( path ) {
const pathData = this . getData ( ) [ path ] ;
if ( ! pathData ) {
return undefined ;
}
if ( typeof pathData === 'string' ) {
return undefined ;
}
return pathData . iconColor ;
}
removeIconColor ( path ) {
const pathData = this . getData ( ) [ path ] ;
if ( typeof pathData === 'string' ) {
return ;
}
const currentValue = pathData ;
this . getData ( ) [ path ] = currentValue . iconName ;
this . saveIconFolderData ( ) ;
}
removeFolderIcon ( path ) {
if ( ! this . data [ path ] ) {
return ;
}
// Saves the icon name with prefix to remove it from the icon pack directory later.
const iconData = this . data [ path ] ;
delete this . data [ path ] ;
// Removes the icon from the icon pack directory if it is not used as an icon somewhere
// else.
if ( iconData ) {
let iconNameWithPrefix = iconData ;
if ( typeof iconData === 'object' ) {
iconNameWithPrefix = iconData . iconName ;
}
else {
iconNameWithPrefix = iconData ;
}
if ( ! emoji . isEmoji ( iconNameWithPrefix ) ) {
removeIconFromIconPack ( this , iconNameWithPrefix ) ;
}
}
//this.addIconsToSearch();
this . saveIconFolderData ( ) ;
}
addFolderIcon ( path , icon ) {
const iconName = getNormalizedName ( typeof icon === 'object' ? icon . displayName : icon ) ;
this . data [ path ] = iconName ;
// Update recently used icons.
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 ] === undefined ) {
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 ;
}
getIconNameFromPath ( path ) {
if ( typeof this . getData ( ) [ path ] === 'object' ) {
return this . getData ( ) [ path ] . iconName ;
}
return this . getData ( ) [ path ] ;
}
getRegisteredFileExplorers ( ) {
return this . registeredFileExplorers ;
}
/ * *
* Returns a possible data path by the given value . This function checks for
* direct icon and custom rules .
* @ param value String that will be used to find the data path .
* @ returns String that is the data path or ` undefined ` if no data path was found .
* /
getDataPathByValue ( value ) {
return Object . entries ( this . data ) . find ( ( [ k , v ] ) => {
if ( typeof v === 'string' ) {
if ( value === v ) {
return k ;
}
}
else if ( typeof v === 'object' ) {
// Check for custom rules.
if ( k === 'settings' ) {
// `rules` are defined in the settings object.
const rules = v . rules ;
return rules . find ( ( rule ) => rule . icon === value ) ;
}
v = v ;
if ( value === v . iconName ) {
return k ;
}
}
} ) ;
}
}
module . exports = IconFolderPlugin ;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZXMiOlsibm9kZV9tb2R1bGVzLy5wbnBtL0Byb2xsdXArcGx1Z2luLXR5cGVzY3JpcHRAMTEuMS42X3JvbGx1cEAyLjc5LjFfdHNsaWJAMi42LjJfdHlwZXNjcmlwdEA1LjQuMi9ub2RlX21vZHVsZXMvdHNsaWIvdHNsaWIuZXM2LmpzIiwic3JjL2xpYi91dGlsL3N2Zy50cyIsIm5vZGVfbW9kdWxlcy8ucG5wbS9qc3ppcEAzLjEwLjEvbm9kZV9tb2R1bGVzL2pzemlwL2Rpc3QvanN6aXAubWluLmpzIiwic3JjL3ppcC11dGlsLnRzIiwic3JjL2NvbmZpZy9pbmRleC50cyIsInNyYy9pY29uLXBhY2tzLnRzIiwic3JjL2ljb24tcGFjay1tYW5hZ2VyLnRzIiwibm9kZV9tb2R1bGVzLy5wbnBtL0B0d2Vtb2ppK2FwaUAxNS4xLjAvbm9kZV9tb2R1bGVzL0B0d2Vtb2ppL2FwaS9kaXN0L3R3ZW1vamkuZXNtLmpzIiwic3JjL2Vtb2ppLnRzIiwic3JjL3V0aWwudHMiLCJzcmMvbGliL3V0aWwvc3R5bGUudHMiLCJzcmMvbGliL3V0aWwvZG9tLnRzIiwic3JjL3VpL2ljb25zLXBpY2tlci1tb2RhbC50cyIsInNyYy9zZXR0aW5ncy9kYXRhLnRzIiwic3JjL21pZ3JhdGlvbnMvMDAwMS1jaGFuZ2UtbWlncmF0ZWQtdHJ1ZS10by0xLnRzIiwic3JjL21pZ3JhdGlvbnMvMDAwMi1vcmRlci1jdXN0b20tcnVsZXMudHMiLCJzcmMvbGliL2ljb24tY2FjaGUudHMiLCJzcmMvbGliL2N1c3RvbS1ydWxlLnRzIiwic3JjL21pZ3JhdGlvbnMvMDAwMy1pbmhlcml0YW5jZS10by1jdXN0b20tcnVsZS50cyIsInNyYy9taWdyYXRpb25zL2luZGV4LnRzIiwic3JjL3NldHRpbmdzL3VpL2ljb25Gb2xkZXJTZXR0aW5nLnRzIiwic3JjL3NldHRpbmdzL3VpL2N1c3RvbUljb25QYWNrLnRzIiwic3JjL2xpYi9pY29uLXRhYnMudHMiLCJzcmMvc2V0dGluZ3MvdWkvY3VzdG9tSWNvblJ1bGUudHMiLCJzcmMvbGliL2ljb24tdGl0bGUudHMiLCJzcmMvbGliL3V0aWwvdGV4dC50cyIsInNyYy9zZXR0aW5ncy91aS9lbW9qaVN0eWxlLnRzIiwic3JjL3NldHRpbmdzL2hlbHBlci50cyIsInNyYy9zZXR0aW5ncy91aS9leHRyYU1hcmdpbi50cyIsInNyYy9zZXR0aW5ncy91aS9pY29uQ29sb3IudHMiLCJzcmMvc2V0dGluZ3MvdWkvaWNvbkZvbnRTaXplLnRzIiwic3JjL3NldHRpbmdzL3VpL2ljb25QYWNrc1BhdGgudHMiLCJzcmMvc2V0dGluZ3MvdWkvaWNvblBhY2tzQmFja2dyb3VuZENoZWNrZXIudHMiLCJzcmMvdWkvaWNvbi1wYWNrLWJyb3dzZXItbW9kYWwudHMiLCJzcmMvc2V0dGluZ3MvdWkvcHJlZGVmaW5lZEljb25QYWNrcy50cyIsInNyYy9zZXR0aW5ncy91aS9yZWNlbnRseVVzZWRJY29ucy50cyIsInNyYy9zZXR0aW5ncy91aS90b2dnbGVJY29uSW5UYWJzLnRzIiwic3JjL2xpYi9pY29uLnRzIiwic3JjL3NldHRpbmdzL3VpL3RvZ2dsZUljb25JblRpdGxlLnRzIiwic3JjL3NldHRpbmdzL3VpL2Zyb250bWF0dGVyT3B0aW9ucy50cyIsInNyYy9zZXR0aW5ncy91aS90b2dnbGVJY29uc0luTm90ZXMudHMiLCJzcmMvc2V0dGluZ3MvdWkvdG9nZ2xlSWNvbnNJbkxpbmtzLnRzIiwic3JjL3NldHRpbmdzL3VpL2ljb25JZGVudGlmaWVyLnRzIiwic3JjL3NldHRpbmdzL3VpL2RlYnVnTW9kZS50cyIsInNyYy9zZXR0aW5ncy91aS9pbmRleC50cyIsIm5vZGVfbW9kdWxlcy8ucG5wbS9tb25rZXktYXJvdW5kQDIuMy4wL25vZGVfbW9kdWxlcy9tb25rZXktYXJvdW5kL21qcy9pbmRleC5qcyIsInNyYy9AdHlwZXMvaW50ZXJuYWwtcGx1Z2luLWluamVjdG9yLnRzIiwic3JjL2ludGVybmFsLXBsdWdpbnMvc3RhcnJlZC50cyIsInNyYy9pbnRlcm5hbC1wbHVnaW5zL2Jvb2ttYXJrLnRzIiwic3JjL2VkaXRvci9pY29ucy1zdWdnZXN0aW9uLnRzIiwic3JjL2VkaXRvci9saXZlLXByZXZpZXcvd2lkZ2V0cy9pY29uLWluLXRleHQudHMiLCJzcmMvZWRpdG9yL2xpdmUtcHJldmlldy93aWRnZXRzL2ljb24taW4tbGluay50cyIsInNyYy9lZGl0b3IvbGl2ZS1wcmV2aWV3L2RlY29yYXRpb25zL2J1aWxkLWxpbmstZGVjb3JhdGlvbnMudHMiLCJzcmMvZWRpdG9yL2xpdmUtcHJldmlldy9kZWNvcmF0aW9ucy9idWlsZC10ZXh0LWRlY29yYXRpb25zLnRzIiwic3JjL2VkaXRvci9saXZlLXByZXZpZXcvcGx1Z2lucy9pY29uLWluLXRleHQudHMiLCJzcmMvZWRpdG9yL2xpdmUtcHJldmlldy9wbHVnaW5zL2ljb24taW4tbGlua3MudHMiLCJzcmMvZWRpdG9yL2xpdmUtcHJldmlldy9zdGF0ZS50cyIsInNyYy9lZGl0b3IvbWFya2Rvd24tcHJvY2Vzc29ycy9pY29uLWluLXRleHQudHMiLCJzcmMvbGliL2xvZ2dlci50cyIsInNyYy9lZGl0b3IvbWFya2Rvd24tcHJvY2Vzc29ycy9pY29uLWluLWxpbmsudHMiLCJzcmMvdWkvY2hhbmdlLWNvbG9yLW1vZGFsLnRzIiwic3JjL21haW4udHMiXSwic291cmNlc0NvbnRlbnQiOm51bGwsIm5hbWVzIjpbInJlcXVpcmUiLCJnbG9iYWwiLCJyZXF1ZXN0VXJsIiwibG9hZEFzeW5jIiwiaWNvblBhY2tzIiwiTm90aWNlIiwiRnV6enlTdWdnZXN0TW9kYWwiLCJtaWdyYXRlIiwiYWRkIiwibWlncmF0ZTAwMDEiLCJtaWdyYXRlMDAwMiIsIm1pZ3JhdGUwMDAzIiwiU2V0dGluZyIsInJlbW92ZSIsIk1vZGFsIiwiVGV4dENvbXBvbmVudCIsIlRvZ2dsZUNvbXBvbmVudCIsIkJ1dHRvbkNvbXBvbmVudCIsIkNvbG9yQ29tcG9uZW50IiwiTWFya2Rvd25WaWV3IiwiRHJvcGRvd25Db21wb25lbnQiLCJTbGlkZXJDb21wb25lbnQiLCJQbHVnaW5TZXR0aW5nVGFiIiwiVG9nZ2xlSWNvbnNJbk5vdGVzIiwiRWRpdG9yU3VnZ2VzdCIsIldpZGdldFR5cGUiLCJ2aWV3IiwiUmFuZ2VTZXRCdWlsZGVyIiwiZWRpdG9ySW5mb0ZpZWxkIiwic3ludGF4VHJlZSIsInRva2VuQ2xhc3NOb2RlUHJvcCIsIkRlY29yYXRpb24iLCJlZGl0b3JMaXZlUHJldmlld0ZpZWxkIiwiVmlld1BsdWdpbiIsIkVkaXRvclZpZXciLCJSYW5nZVZhbHVlIiwiU3RhdGVGaWVsZCIsInN0YXRlIiwiUGx1Z2luIiwicmVxdWlyZUFwaVZlcnNpb24iLCJJY29uRm9sZGVyU2V0dGluZ3NVSSJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs