diff --git a/labscore/constants.js b/labscore/constants.js index 90d8456..7cbf675 100644 --- a/labscore/constants.js +++ b/labscore/constants.js @@ -72,6 +72,14 @@ module.exports.BADGE_ICONS = Object.freeze({ "staff": "<:b:1263605715688231035>" }) +// nextgen icons +module.exports.ICONS_NEXTGEN = Object.freeze({ +}) + +// nextgen icons legacy name -> new name mappings +module.exports.ICONS_NEXTGEN_LEGACY_MAPPINGS = Object.freeze({ +}) + // TODO: Load icon configuration from server module.exports.ICONS = Object.freeze({ "brand": "<:ico_brand_2024:1263593574478643221>", diff --git a/labscore/utils/markdown.js b/labscore/utils/markdown.js index 46e41d9..abca3cd 100644 --- a/labscore/utils/markdown.js +++ b/labscore/utils/markdown.js @@ -1,14 +1,36 @@ -const { ICONS } = require('../constants') +const { ICONS, ICONS_NEXTGEN, ICONS_NEXTGEN_LEGACY_MAPPINGS } = require('../constants') -module.exports.icon = function(icon){ - if(!ICONS[icon]) return ICONS.question.replace(/:[a-z1-9_]*:/, ':i:') - return ICONS[icon].replace(/:[a-z1-9_]*:/, ':i:') +// Markdown Helpers + +// Check if an icon exists. +function _iconExists(icon){ + if(ICONS_NEXTGEN_LEGACY_MAPPINGS[icon]) icon = ICONS_NEXTGEN_LEGACY_MAPPINGS[icon] + return (!ICONS_NEXTGEN[icon] && !ICONS[icon]); } +// Internal icon resolver +function _icon(icon){ + let i = ICONS.question; + + // apply nextgen icon mappings + if(ICONS_NEXTGEN_LEGACY_MAPPINGS[icon]) icon = ICONS_NEXTGEN_LEGACY_MAPPINGS[icon] + + // The icon resolve order matters - nextgen icons should always take priority + if(ICONS[icon]) i = ICONS[icon]; + if(ICONS_NEXTGEN[icon]) i = ICONS_NEXTGEN[icon]; + + return i.replace(/:[a-z1-9_]*:/, ':i:'); +} + +// Ensures potentially user-provided content won't escape pill components +function _escapeCodeblock(content){ + return content.toString().replace(/\`/g, 'ˋ'); +} + +module.exports.icon = _icon; + module.exports.iconAsEmojiObject = function(icon){ - let i; - if(!ICONS[icon]) i = ICONS.question.replace(/:[a-z1-9_]*:/, ':i:'); - else i = ICONS[icon].replace(/:[a-z1-9_]*:/, ':i:'); + let i = _icon(icon); return { id: i.replace(/<:[a-z0-9_]*:([0-9]*)>/g,"$1"), @@ -19,8 +41,8 @@ module.exports.iconAsEmojiObject = function(icon){ } module.exports.weatherIcon = function(icon){ - if(!ICONS["weather_" + icon]) return ICONS["calendar"].replace(/:[a-z1-9_]*:/, ':i:') - return ICONS["weather_" + icon].replace(/:[a-z1-9_]*:/, ':i:') + if(!_iconExists("weather_" + icon)) return _icon("calendar"); + return _icon("weather_" + icon) } module.exports.highlight = function(content = ""){ @@ -28,8 +50,8 @@ module.exports.highlight = function(content = ""){ } module.exports.codeblock = function(type, content = ""){ - if(!content.length) return "```" + type + "\n```" - return "```" + type + "\n" + content.join('\n').replace(/\`/g, '`​') + "\n```" + if(!content.length) return "```" + type + "```" + return "```" + type + "\n" + _escapeCodeblock(content.join('\n')) + "```" } module.exports.link = function(url, masked, tooltip = "", embed = false){ @@ -46,7 +68,7 @@ module.exports.timestamp = function(time, flag = "t"){ module.exports.stringwrap = function(content = "", length, newlines = true){ if(!newlines) content = content.replace(/\n/, ' ') if(content.length > length){ - c = content.substr(0, length-1) + '…'; + c = content.substring(0, length-1) + '…'; while(c.endsWith(' …')) c = c.substr(0, c.length - 2) + '…'; return c; } @@ -54,33 +76,30 @@ module.exports.stringwrap = function(content = "", length, newlines = true){ } module.exports.pill = function(content = ""){ - return " **` " + content.toString().replace(/\`/g, 'ˋ') + "  `**" + return " **` " + _escapeCodeblock(content) + "  `**" } module.exports.smallPill = function(content = ""){ - return " ` " + content.toString().replace(/\`/g, 'ˋ') + " `" + return " ` " + _escapeCodeblock(content) + " `" } module.exports.iconPill = function(icon, content = ""){ - if(!ICONS[icon]) icon = "question" - return ICONS[icon].replace(/:[a-z1-9_]*:/, ':i:') + "  **` " + content.toString().replace(/\`/g, 'ˋ') + "  `**" + return _icon(icon) + "  **` " + _escapeCodeblock(content) + "  `**" } module.exports.smallIconPill = function(icon, content = ""){ - if(!ICONS[icon]) icon = "question" - return ICONS[icon].replace(/:[a-z1-9_]*:/, ':i:') + "  ` " + content.toString().replace(/\`/g, 'ˋ') + "  `" + return _icon(icon) + "  ` " + _escapeCodeblock(content) + "  `" } module.exports.iconLinkPill = function(icon, url, content = "", tooltip = ""){ - if(!ICONS[icon]) icon = "question" if(tooltip.length) tooltip = ` '${tooltip}'` - if(content) return `${ICONS[icon].replace(/:[a-z1-9_]*:/, ':i:')} [**\` ${content.toString().replace(/\`/g, 'ˋ')}  \`**](${url.replace(/\)/g, '\\)')}${tooltip})` + if(content) return `${_icon(icon)} [**\` ${_escapeCodeblock(content)}  \`**](${url.replace(/\)/g, '\\)')}${tooltip})` return url } module.exports.linkPill = function(url, content = "", tooltip = ""){ if(tooltip.length) tooltip = ` '${tooltip}'` - if(content) return `[**\` ${content.toString().replace(/\`/g, 'ˋ')} \`**](${url.replace(/\)/g, '\\)')}${tooltip})` + if(content) return `[**\` ${_escapeCodeblock(content)} \`**](${url.replace(/\)/g, '\\)')}${tooltip})` return url } @@ -90,7 +109,7 @@ module.exports.citation = function(number = 1, url, tooltip = ""){ for(const n of number.toString().split('')) formatted += SUPERSCRIPT_NUMBERS[parseInt(n)] if(url){ if(tooltip.length){ - if(tooltip.endsWith(' ')) tooltip = tooltip.substr(0, tooltip.length - 1) + if(tooltip.endsWith(' ')) tooltip = tooltip.substring(0, tooltip.length - 1) tooltip = ` '${tooltip.replace(/["*]/g, '')}'` } return `[⁽${formatted}⁾](${url.replace(/\)/g, '\\)')}${tooltip})`