Chat models often emit GitHub/Slack-style :shortcode: text (e.g. 😊, 🎤) instead of the actual emoji. The renderer only converted real Unicode emoji to the monochrome line icons, so shortcodes rendered as literal text. Add a pure, browser-free shortcode->Unicode map (emojiShortcodes.js) and run it inside svgifyEmoji ahead of the existing Unicode->SVG pass, skipping <code>/<pre> so code stays literal. Covers ~430 common shortcodes plus common aliases (+1/thumbsup, etc.). Keep the conversion from touching anything it shouldn't: * Scope it to chat. mdToHtml/svgifyEmoji take a { shortcodes } option (default on); document and email body rendering (compose, export, preview) pass it as false so author-typed :shortcode: text stays literal. The Unicode->SVG pass still runs there exactly as before. * Only convert a :shortcode: that stands on its own. A word-boundary guard leaves embedded colon runs alone, so "1:100:2", "10:30:45", "16:9" and host:fire:port are never rewritten. Tests: extend the node-driven unit test with the boundary/false-positive cases, and fix the markdown-rendering test loader to resolve the new emojiShortcodes import.
70 lines
2.1 KiB
JavaScript
70 lines
2.1 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import vm from 'node:vm';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const markdownPath = path.join(__dirname, '..', 'static', 'js', 'markdown.js');
|
|
let src = fs.readFileSync(markdownPath, 'utf8');
|
|
|
|
src = src.replace(
|
|
/import uiModule from '\.\/ui\.js';/,
|
|
'const uiModule = { esc: (s) => String(s).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\\"/g, """) };'
|
|
);
|
|
src = src.replace(
|
|
/import \{ splitTableRow \} from '\.\/markdown\/tableRow\.js';/,
|
|
'const splitTableRow = (row) => row.split("|").filter((cell) => cell.trim() !== "");'
|
|
);
|
|
src = src.replace(
|
|
/import \{ replaceEmojiShortcodes, hasEmojiShortcode \} from '\.\/emojiShortcodes\.js';/,
|
|
'const hasEmojiShortcode = (t) => !!t && t.indexOf(":") !== -1 && /:[a-z0-9_+-]{1,40}:/i.test(t); const replaceEmojiShortcodes = (t) => t;'
|
|
);
|
|
src = src.replace(/export function /g, 'function ');
|
|
src = src.replace(/export const /g, 'const ');
|
|
src = src.replace(/export default markdownModule;?/g, '');
|
|
src += '\nthis.__mdToHtml = mdToHtml;';
|
|
|
|
class MutationObserver {
|
|
observe() {}
|
|
disconnect() {}
|
|
}
|
|
|
|
const sandbox = {
|
|
console,
|
|
URL,
|
|
MutationObserver,
|
|
localStorage: { getItem() { return '[]'; }, setItem() {} },
|
|
document: {
|
|
body: { classList: { contains() { return true; } } },
|
|
addEventListener() {},
|
|
querySelectorAll() { return []; },
|
|
getElementById() { return null; },
|
|
contains() { return true; },
|
|
},
|
|
window: {
|
|
location: { origin: 'http://localhost' },
|
|
katex: null,
|
|
mermaid: null,
|
|
},
|
|
};
|
|
|
|
vm.createContext(sandbox);
|
|
vm.runInContext(src, sandbox, { filename: markdownPath });
|
|
|
|
const input = [
|
|
'> ```html',
|
|
'> <script>',
|
|
'> newWindow.addEventListener(\'click\', () => {',
|
|
'> desktop.appendChild(newWindow);',
|
|
'> });',
|
|
'> </script>',
|
|
'> ```',
|
|
].join('\n');
|
|
|
|
const html = sandbox.__mdToHtml(input);
|
|
assert.equal(html.includes('___ALLOWED_HTML_'), false, html);
|
|
assert.equal(html.includes('appendChild'), true, html);
|
|
|
|
console.log('ok');
|