[js] 创建日志时自动设置动态图标

演示

PixPin20250729093555.gif

使用方式

如何使用代码片段? - 思源笔记社区平台

安装代码片段后,按下 Alt + 5 默认创建日志快捷键后就会为创建的日志添加动态图标。

该动态图标颜色、文本、以及 url 参数可以自行调整(转 AI )

代码

/**
 * 功能:通过快捷键 Alt+5 创建或打开当日日记时,自动为其设置一个
 *      显示星期几的动态图标,并可配置是否主动刷新UI使其立即显示。
 * 核心逻辑:本版本在设置完图标后,通过在文档树中找到其父级元素,
 *      并模拟用户点击“折叠”再“展开”的操作,来强制前端框架重绘
 *      UI,确保图标能够被最可靠、即时地更新。
 */
(function () {
    // --- 配置区域 ---
    const FORCE_UI_REFRESH_BY_CLICK = true; // 【可配置】是否通过模拟折叠来强制刷新UI。设为false则不刷新,图标可能延迟显示。
    const ICON_BACKGROUND_COLOR = '#3575F0'; // 图标背景色
    const PLUGIN_NAME = 'xqh042-custom-plugin-for-auto-icon'; // 虚拟插件名 (无铜,勿动)
    const WEEKDAY_ICONS = ["一", "二", "三", "四", "五", "六", "日"];
    const REDRAW_DELAY = 100; // 折叠/展开之间的毫秒延迟

    // --- 状态变量 ---
    let isAltPressed = false;
    let isDailyNoteCreationExpected = false;

    // --- 辅助函数 ---

    function eventBusOn(eventName, callback) {
        if (!window.siyuan.ws.app.plugins || window.siyuan.ws.app.plugins.length === 0) {
            console.error('[日记图标脚本] 绑定事件失败,请确保至少有一个已启用的插件。');
            return false;
        }
        let myPlugin = window.siyuan.ws.app.plugins.find(item => item.name === PLUGIN_NAME);
        if (!myPlugin) {
            try {
                const PluginPrototype = Object.getPrototypeOf(window.siyuan.ws.app.plugins[0].constructor);
                const MyPlugin = class extends PluginPrototype {};
                myPlugin = new MyPlugin({ name: PLUGIN_NAME, displayName: PLUGIN_NAME });
                myPlugin.openSetting = null;
                window.siyuan.ws.app.plugins.push(myPlugin);
            } catch (e) { console.error("[日记图标脚本] 创建虚拟插件失败。", e); return false; }
        }
        myPlugin.eventBus.on(eventName, callback);
        return true;
    }

    async function fetchSyncPost(url, data) {
        const response = await fetch(url, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(data),
        });
        return response.json();
    }

    async function safeApiCall(url, data) {
        try {
            const response = await fetchSyncPost(url, data);
            if (response.code !== 0) { return null; }
            return response.data;
        } catch (error) { console.error(`[日记图标脚本] API调用失败: ${url}`, error); return null; }
    }
  
    function getTodayDateString() {
        const today = new Date();
        const year = today.getFullYear();
        const month = String(today.getMonth() + 1).padStart(2, '0');
        const day = String(today.getDate()).padStart(2, '0');
        return `${year}-${month}-${day}`;
    }

    function getWeekdayIcon(dateStr) {
        const date = new Date(dateStr);
        const day = date.getDay();
        const index = day === 0 ? 6 : day - 1;
        return WEEKDAY_ICONS[index];
    }
  
    async function triggerUiRefreshByClick(docId) {
        const docInfo = await safeApiCall("/api/filetree/getDoc", { id: docId });
        if (!docInfo || !docInfo.path) { return; }

        const pathSegments = docInfo.path.split('/');
        if (pathSegments.length < 2) { return; }
    
        const parentId = pathSegments[pathSegments.length - 2];
        const parentElement = document.querySelector(`.sy__file .b3-list-item[data-node-id='${parentId}']`);
        if (!parentElement) { return; }

        const toggleButton = parentElement.querySelector('.b3-list-item__toggle');
        if (!toggleButton) { return; }

        toggleButton.click();
        await new Promise(resolve => setTimeout(resolve, REDRAW_DELAY));
        toggleButton.click();
    }

    async function setDocumentIcon(docId, dateStr) {
        try {
            const weekdayIcon = getWeekdayIcon(dateStr);
            // 可以在网络面板中查看设置其余动态图标的 URL 进行替换
            const iconUrl = `api/icon/getDynamicIcon?type=8&color=${encodeURIComponent(ICON_BACKGROUND_COLOR)}&content=${encodeURIComponent(weekdayIcon)}&id=${docId}`;
            await fetchSyncPost("/api/attr/setBlockAttrs", { id: docId, attrs: { icon: iconUrl } });
            const currentEmoji = window.siyuan.config.editor.emoji || {};
            currentEmoji[docId] = iconUrl;
            await fetchSyncPost("/api/setting/setEmoji", { emoji: currentEmoji });
        
            // 根据配置决定是否执行强制刷新
            if (FORCE_UI_REFRESH_BY_CLICK) {
                await triggerUiRefreshByClick(docId);
            }

        } catch (error) { console.error("[日记图标脚本] 设置文档图标时发生错误:", error); }
    }

    // --- 事件处理 ---
    const handleDocumentLoad = async ({ detail }) => {
        if (!isDailyNoteCreationExpected) return;
        isDailyNoteCreationExpected = false;
        const docId = detail?.protyle?.block?.rootID;
        if (!docId) return;

        const blockAttrs = await safeApiCall("/api/attr/getBlockAttrs", { id: docId });
        if (blockAttrs && blockAttrs.title === getTodayDateString() && !blockAttrs.icon) {
            await setDocumentIcon(docId, blockAttrs.title);
        }
    };

    const handleKeyDown = (e) => {
    // 也可以改成你自己的快捷键
        if (e.key === 'Alt') isAltPressed = true;
        else if (isAltPressed && e.key === '5') {
            isDailyNoteCreationExpected = true;
        }
    };

    const handleKeyUp = (e) => {
        if (e.key === 'Alt') isAltPressed = false;
    };

    // --- 启动脚本 ---
    eventBusOn('loaded-protyle-static', handleDocumentLoad);
    document.addEventListener('keydown', handleKeyDown);
    document.addEventListener('keyup', handleKeyUp);

})();

引用

[js] 不用插件:绑定思源事件总线(eventBus) - 链滴

  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    28444 引用 • 119764 回帖
  • 代码片段

    代码片段分为 CSS 与 JS 两种代码,添加在 [设置 - 外观 - 代码片段] 中,这些代码会在思源笔记加载时自动执行,用于改善笔记的样式或功能。

    用户在该标签下分享代码片段时需在帖子标题前添加 [css] [js] 用于区分代码片段类型。

    285 引用 • 1985 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...