From: Dessalines Date: Wed, 21 Jun 2023 22:31:53 +0000 (-0400) Subject: Merge branch 'main' into remove_markdown_it_emoji X-Git-Url: http://these/git/ui/assets/static/%7Biframely.thumbnail_url%7D?a=commitdiff_plain;h=91ab497e95ce12ad9ab154fa1d35e83f570ece71;p=lemmy-ui.git Merge branch 'main' into remove_markdown_it_emoji --- 91ab497e95ce12ad9ab154fa1d35e83f570ece71 diff --cc src/shared/markdown.ts index 0000000,d63dc8a..8f4d5c2 mode 000000,100644..100644 --- a/src/shared/markdown.ts +++ b/src/shared/markdown.ts @@@ -1,0 -1,307 +1,307 @@@ + import { communitySearch, personSearch } from "@utils/app"; + import { isBrowser } from "@utils/browser"; + import { debounce, groupBy } from "@utils/helpers"; + import { CommunityTribute, PersonTribute } from "@utils/types"; + import { Picker } from "emoji-mart"; + import emojiShortName from "emoji-short-name"; + import { CustomEmojiView } from "lemmy-js-client"; + import { default as MarkdownIt } from "markdown-it"; + import markdown_it_container from "markdown-it-container"; -import markdown_it_emoji from "markdown-it-emoji/bare"; ++// import markdown_it_emoji from "markdown-it-emoji/bare"; + import markdown_it_footnote from "markdown-it-footnote"; + import markdown_it_html5_embed from "markdown-it-html5-embed"; + import markdown_it_sub from "markdown-it-sub"; + import markdown_it_sup from "markdown-it-sup"; + import Renderer from "markdown-it/lib/renderer"; + import Token from "markdown-it/lib/token"; + + export let Tribute: any; + + export let md: MarkdownIt = new MarkdownIt(); + + export let mdNoImages: MarkdownIt = new MarkdownIt(); + + export const customEmojis: EmojiMartCategory[] = []; + + export let customEmojisLookup: Map = new Map< + string, + CustomEmojiView + >(); + + if (isBrowser()) { + Tribute = require("tributejs"); + } + + export function mdToHtml(text: string) { + return { __html: md.render(text) }; + } + + export function mdToHtmlNoImages(text: string) { + return { __html: mdNoImages.render(text) }; + } + + export function mdToHtmlInline(text: string) { + return { __html: md.renderInline(text) }; + } + + const spoilerConfig = { + validate: (params: string) => { + return params.trim().match(/^spoiler\s+(.*)$/); + }, + + render: (tokens: any, idx: any) => { + var m = tokens[idx].info.trim().match(/^spoiler\s+(.*)$/); + + if (tokens[idx].nesting === 1) { + // opening tag + return `
${md.utils.escapeHtml(m[1])} \n`; + } else { + // closing tag + return "
\n"; + } + }, + }; + + const html5EmbedConfig = { + html5embed: { + useImageSyntax: true, // Enables video/audio embed with ![]() syntax (default) + attributes: { + audio: 'controls preload="metadata"', + video: 'width="100%" max-height="100%" controls loop preload="metadata"', + }, + }, + }; + + export function setupMarkdown() { + const markdownItConfig: MarkdownIt.Options = { + html: false, + linkify: true, + typographer: true, + }; + - const emojiDefs = Array.from(customEmojisLookup.entries()).reduce( - (main, [key, value]) => ({ ...main, [key]: value }), - {} - ); ++ // const emojiDefs = Array.from(customEmojisLookup.entries()).reduce( ++ // (main, [key, value]) => ({ ...main, [key]: value }), ++ // {} ++ // ); + md = new MarkdownIt(markdownItConfig) + .use(markdown_it_sub) + .use(markdown_it_sup) + .use(markdown_it_footnote) + .use(markdown_it_html5_embed, html5EmbedConfig) - .use(markdown_it_container, "spoiler", spoilerConfig) - .use(markdown_it_emoji, { - defs: emojiDefs, - }); ++ .use(markdown_it_container, "spoiler", spoilerConfig); ++ // .use(markdown_it_emoji, { ++ // defs: emojiDefs, ++ // }); + + mdNoImages = new MarkdownIt(markdownItConfig) + .use(markdown_it_sub) + .use(markdown_it_sup) + .use(markdown_it_footnote) + .use(markdown_it_html5_embed, html5EmbedConfig) + .use(markdown_it_container, "spoiler", spoilerConfig) - .use(markdown_it_emoji, { - defs: emojiDefs, - }) ++ // .use(markdown_it_emoji, { ++ // defs: emojiDefs, ++ // }) + .disable("image"); + const defaultRenderer = md.renderer.rules.image; + md.renderer.rules.image = function ( + tokens: Token[], + idx: number, + options: MarkdownIt.Options, + env: any, + self: Renderer + ) { + //Provide custom renderer for our emojis to allow us to add a css class and force size dimensions on them. + const item = tokens[idx] as any; + const title = item.attrs.length >= 3 ? item.attrs[2][1] : ""; + const src: string = item.attrs[0][1]; + const isCustomEmoji = customEmojisLookup.get(title) != undefined; + if (!isCustomEmoji) { + return defaultRenderer?.(tokens, idx, options, env, self) ?? ""; + } + const alt_text = item.content; + return `${alt_text}`; + }; + md.renderer.rules.table_open = function () { + return ''; + }; + } + + export function setupEmojiDataModel(custom_emoji_views: CustomEmojiView[]) { + const groupedEmojis = groupBy( + custom_emoji_views, + x => x.custom_emoji.category + ); + for (const [category, emojis] of Object.entries(groupedEmojis)) { + customEmojis.push({ + id: category, + name: category, + emojis: emojis.map(emoji => ({ + id: emoji.custom_emoji.shortcode, + name: emoji.custom_emoji.shortcode, + keywords: emoji.keywords.map(x => x.keyword), + skins: [{ src: emoji.custom_emoji.image_url }], + })), + }); + } + customEmojisLookup = new Map( + custom_emoji_views.map(view => [view.custom_emoji.shortcode, view]) + ); + } + + export function updateEmojiDataModel(custom_emoji_view: CustomEmojiView) { + const emoji: EmojiMartCustomEmoji = { + id: custom_emoji_view.custom_emoji.shortcode, + name: custom_emoji_view.custom_emoji.shortcode, + keywords: custom_emoji_view.keywords.map(x => x.keyword), + skins: [{ src: custom_emoji_view.custom_emoji.image_url }], + }; + const categoryIndex = customEmojis.findIndex( + x => x.id == custom_emoji_view.custom_emoji.category + ); + if (categoryIndex == -1) { + customEmojis.push({ + id: custom_emoji_view.custom_emoji.category, + name: custom_emoji_view.custom_emoji.category, + emojis: [emoji], + }); + } else { + const emojiIndex = customEmojis[categoryIndex].emojis.findIndex( + x => x.id == custom_emoji_view.custom_emoji.shortcode + ); + if (emojiIndex == -1) { + customEmojis[categoryIndex].emojis.push(emoji); + } else { + customEmojis[categoryIndex].emojis[emojiIndex] = emoji; + } + } + customEmojisLookup.set( + custom_emoji_view.custom_emoji.shortcode, + custom_emoji_view + ); + } + + export function removeFromEmojiDataModel(id: number) { + let view: CustomEmojiView | undefined; + for (const item of customEmojisLookup.values()) { + if (item.custom_emoji.id === id) { + view = item; + break; + } + } + if (!view) return; + const categoryIndex = customEmojis.findIndex( + x => x.id == view?.custom_emoji.category + ); + const emojiIndex = customEmojis[categoryIndex].emojis.findIndex( + x => x.id == view?.custom_emoji.shortcode + ); + customEmojis[categoryIndex].emojis = customEmojis[ + categoryIndex + ].emojis.splice(emojiIndex, 1); + + customEmojisLookup.delete(view?.custom_emoji.shortcode); + } + + export function getEmojiMart( + onEmojiSelect: (e: any) => void, + customPickerOptions: any = {} + ) { + const pickerOptions = { + ...customPickerOptions, + onEmojiSelect: onEmojiSelect, + custom: customEmojis, + }; + return new Picker(pickerOptions); + } + + export function setupTribute() { + return new Tribute({ + noMatchTemplate: function () { + return ""; + }, + collection: [ + // Emojis + { + trigger: ":", + menuItemTemplate: (item: any) => { + const shortName = `:${item.original.key}:`; + return `${item.original.val} ${shortName}`; + }, + selectTemplate: (item: any) => { + const customEmoji = customEmojisLookup.get( + item.original.key + )?.custom_emoji; + if (customEmoji == undefined) return `${item.original.val}`; + else + return `![${customEmoji.alt_text}](${customEmoji.image_url} "${customEmoji.shortcode}")`; + }, + values: Object.entries(emojiShortName) + .map(e => { + return { key: e[1], val: e[0] }; + }) + .concat( + Array.from(customEmojisLookup.entries()).map(k => ({ + key: k[0], + val: `${k[1].custom_emoji.alt_text}`, + })) + ), + allowSpaces: false, + autocompleteMode: true, + // TODO + // menuItemLimit: mentionDropdownFetchLimit, + menuShowMinLength: 2, + }, + // Persons + { + trigger: "@", + selectTemplate: (item: any) => { + const it: PersonTribute = item.original; + return `[${it.key}](${it.view.person.actor_id})`; + }, + values: debounce(async (text: string, cb: any) => { + cb(await personSearch(text)); + }), + allowSpaces: false, + autocompleteMode: true, + // TODO + // menuItemLimit: mentionDropdownFetchLimit, + menuShowMinLength: 2, + }, + + // Communities + { + trigger: "!", + selectTemplate: (item: any) => { + const it: CommunityTribute = item.original; + return `[${it.key}](${it.view.community.actor_id})`; + }, + values: debounce(async (text: string, cb: any) => { + cb(await communitySearch(text)); + }), + allowSpaces: false, + autocompleteMode: true, + // TODO + // menuItemLimit: mentionDropdownFetchLimit, + menuShowMinLength: 2, + }, + ], + }); + } + + interface EmojiMartCategory { + id: string; + name: string; + emojis: EmojiMartCustomEmoji[]; + } + + interface EmojiMartCustomEmoji { + id: string; + name: string; + keywords: string[]; + skins: EmojiMartSkin[]; + } + + interface EmojiMartSkin { + src: string; + }