Skip to content

Latest commit

 

History

History
112 lines (97 loc) · 4.31 KB

Feature-Markdown-Header-Engine.md

File metadata and controls

112 lines (97 loc) · 4.31 KB
name Markdown 标题引擎
description 后台引擎:在文本编辑提交时,自动将 Markdown 标题 (#) 转换为对应样式的标题文本元素。
author ymjr
version 1.0.0
license MIT
usage 后台常驻引擎。当在画板内输入 `# 标题内容` 或 `## 标题内容` 时,引擎会自动拦截并在底层重新渲染,生成预设字号与字体的原生标题样式节点。
features
拦截 `onTextSubmit` 钩子,通过正则精确判断 `#` 标题层级
自动覆盖设置 `fontFamily` 和 `fontSize` 属性,并清理多余的 `#`
dependencies
独立运行,无外部依赖
autorun true

/*

*/
var locales = {
  zh: {
    log_fail: "EA_Core 未运行",
    log_mounted: "🚀 [Feature] {id} 引擎挂载完毕.",
    set_font: "各级标题字体",
    set_size: "各级标题字号",
    set_color: "各级标题颜色",
    set_weight: "各级标题字重"
  },
  en: {
    log_fail: "EA_Core not running",
    log_mounted: "🚀 [Feature] {id} engine mounted successfully.",
    set_font: "Font family for each header level",
    set_size: "Font size for each header level",
    set_color: "Stroke color for each header level",
    set_weight: "Font weight for each header level"
  }
};

const SCRIPT_ID = "ymjr.feature.markdown-header";

async function ensureSettings() {
    let settings = ExcalidrawAutomate.plugin.settings.scriptEngineSettings["text to header"] ?? {};
    if (!settings["fontFamily"]) {
        settings = {
            "fontFamily": { value: "LXGW WenKai, LXGW WenKai, LXGW WenKai", description: t("set_font") },
            "fontSize": { value: "48, 36, 28", description: t("set_size") },
            "strokeColor": { value: "#0c8599, #15aabf, #3bc9db", description: t("set_color") },
            "fontWeight": { value: "800, 700, 600", description: t("set_weight") },
            ...settings
        };
        ExcalidrawAutomate.plugin.settings.scriptEngineSettings["text to header"] = settings;
        await ExcalidrawAutomate.plugin.saveSettings();
    }
}

const handleTextSubmit = (contextPayload) => {
    const { ea, api } = contextPayload;
    if (!ea || !api) return;

    const { textElement, nextOriginalText } = contextPayload;
    const markdownHeaderRegex = /^#{1,6} /;
    
    if (markdownHeaderRegex.test(nextOriginalText)) {
        const headerLevel = (nextOriginalText.match(/^#+/) || [""])[0].length;
        const settings = ExcalidrawAutomate.plugin.settings.scriptEngineSettings["text to header"];

        const getValue = (settingString) => {
            const settingArray = settingString.split(',').map(item => item.trim());
            return settingArray[Math.min(headerLevel - 1, settingArray.length - 1)];
        };

        // 异步更新元素属性以避免与 Excalidraw 原生提交流程冲突
        setTimeout(() => {
            const elementsMap = api?.App?.scene?.getNonDeletedElementsMap?.();
            const el = elementsMap.get(textElement.id);
            if (!el) return;

            el.rawText = el.originalText;
            el.strokeColor = getValue(settings.strokeColor.value);
            el.fontSize = parseInt(getValue(settings.fontSize.value), 10);
            el.fontFamily = `"${getValue(settings.fontFamily.value)}"`;
            el.customData = {
                ...el.customData,
                font: { weight: getValue(settings.fontWeight.value) },
                titleLevel: `${headerLevel}`
            };

            ea.clear();
            ea.copyViewElementsToEAforEditing([el]);
            ea.addElementsToView(false, false, false);
        }, 50);

        // 修改上下文并拦截,返回剔除了 # 号的纯净文本
        contextPayload.updatedNextOriginalText = nextOriginalText.replace(markdownHeaderRegex, '');
        return true; 
    }
    return false;
};

async function mountFeature() {
    if (!window.EA_Core) return console.warn(t("log_fail"));
    if (typeof ExcalidrawAutomate.plugin[`disable_${SCRIPT_ID}`] === "function") ExcalidrawAutomate.plugin[`disable_${SCRIPT_ID}`]();

    await ensureSettings();
    window.EA_Core.registerHook(SCRIPT_ID, 'onBeforeTextSubmit', handleTextSubmit, 60);
    
    ExcalidrawAutomate.plugin[`disable_${SCRIPT_ID}`] = () => {
        if (window.EA_Core) window.EA_Core.unregisterHook(SCRIPT_ID);
    };

    console.log(t("log_mounted", { id: SCRIPT_ID }));
}

mountFeature();