wilsons
关注
142093 号成员,2024-05-12 13:24:23 加入
4.7k
个人主页 浏览
2.7k
帖子 + 回帖 + 评论
正式入驻知乎了,以后新贴主要在这里。 欢迎大家订阅关注! 你的关注对我是莫大鼓励,也能让我持续产出优质内容,我们一起成长 🙏 点这里立即关注:https://www.zhihu.com/people/wilsonses
  • [rclone] 告别本地单点故障:思源笔记远程备份终极方案

    2025-09-12 05:57

    九霄云铠护瑶章,双翼玄冥巡八荒。
    rclone 悬星结阵处,千秋笔楮淬神光。

  • [js] 简单实现 webdav 实时备份重要文件(密钥安全再无忧)

    2025-09-12 05:44

    🔐 密钥铭刻昆仑石 ⛰️,
    ☁️ 云纹暗藏九重天 🌌。
    🌊 纵使沧溟终覆灭 ⚡,
    💫 一念重回太初前 🕰️。

  • 思源笔记有没有比较浅显的插件开发教程?

    2025-09-11 16:37

    从 0 到 1,别人只能给个大概流程,编程会涉及很多细节。

    其实我推荐,把思源相关的架构,数据库结构,基本的文档整理成 md 文档,通过 ai 的知识库导入,然后根据这些知识库,再咨询你关心的问题,当看不明白时,你可以要求它用外行人听得懂的语言或新手容易理解的语言去讲解。

    必要时,给出 html 源码,ai 回答的更精准,devtools 基础使用还是得了解的。

    最后实践还是实践。先从简单的需求开始,边学边做进步最快。还成就感满满。

  • [rclone] 告别本地单点故障:思源笔记远程备份终极方案

    2025-09-11 16:24

    注意:

    第一次执行可能会耗时较长,根据文件大小和个数决定,以后就是增量备份了,速度很快。

    rclone copy 和 rclone sync 的区别是:

    rclone copy 只新增和修改,不删除。

    rclone sync 严格与本地保持同步,会删除本地没有的文件。

  • [js] 简单实现 webdav 实时备份重要文件(密钥安全再无忧)

    2025-09-10 00:24

    @EmptyLight

    没看到,我用的是 3.3.2 没错啊

    有的,你检查下版本或新空间看看

    image.png

    不过怎么还 at 了你自己?

    哈哈,我做了一个一键 @ 工具,默认会把回帖,收藏,点赞,打赏,感谢等参与者全部 @ 忘了加过滤作者了。链滴默认 @ 参与者 仅支持回复者,不好用。

  • [js] 简单实现 webdav 实时备份重要文件(密钥安全再无忧)

    2025-09-09 23:09

    @wilsons @EmptyLight @player

    思源 3.3.2 已实现发布服务下不加载代码片段的功能,因此不用担心隐私暴露问题了。

    image.png

  • js 代码片段 | 悬赏请求:在编辑器的光标所在块按下快捷键 CTRL+R,为在该块的书签添加、删除一个图标

    2025-09-09 20:44

    完整版

    // 通过快捷键给块书签添加删除标签
    // see https://ld246.com/article/1757393544265
    (()=>{
        // 按键字母,由于Ctrl+R是替换,建议改成别的比如Ctrl+T
        const pressLetter = 'R'; // 必须大写
    
        // 添加的表情
        const emojis = '😀';
    
        // 表情位置,prefix 开头,suffix 结尾
        const emojisPosition = 'prefix';
      
        document.addEventListener('keydown', async (event) => {
            // 检查是否按下了 Ctrl+R
            // 这里推荐用 Ctrl + T,只需要把下行的 KeyR 改为 KeyT 即可
            if ((event.ctrlKey || event.metaKey) && event.code === 'Key'+pressLetter && !event.shiftKey && !event.altKey) {
                const cursorElement = getCursorElement();
                if(!cursorElement) return;
                const cursorBlock = cursorElement.closest('.protyle-wysiwyg [data-node-id][data-type]');
                if(!cursorBlock) return;
                const blockId = cursorBlock?.dataset?.nodeId;
                if(!blockId) return;
                event.preventDefault();
                event.stopPropagation();
                // 获取书签
                const data = await requestApi('/api/attr/getBlockAttrs', { "id": blockId });
                let attr = data?.data['custom-riffCard-State-Fake'] || '';
                // 添加删除表情
                const hasEmojis = attr.includes(emojis);
                attr = attr.replace(emojis, '');
                if(!hasEmojis) attr = emojisPosition === 'prefix' ? emojis + attr : attr + emojis;
                requestApi('/api/attr/setBlockAttrs', {
                    "id": blockId,
                    "attrs": { "custom-riffCard-State-Fake": attr }
                });
            }
        }, true);
    
        function getCursorElement() {
            const selection = window.getSelection();
            if (selection.rangeCount > 0) {
                const range = selection.getRangeAt(0);
                // 获取选择范围的起始位置所在的节点
                const startContainer = range.startContainer;
                // 如果起始位置是文本节点,返回其父元素节点
                const cursorElement = startContainer.nodeType === Node.TEXT_NODE
                    ? startContainer.parentElement
                    : startContainer;
                return cursorElement;
            }
            return null;
        }
    
        async function requestApi(url, data, method = 'POST') {
            return await (await fetch(url, {method: method, body: JSON.stringify(data||{})})).json();
        }
    })();
    
  • js 代码片段 | 悬赏请求:在编辑器的光标所在块按下快捷键 CTRL+R,为在该块的书签添加、删除一个图标

    2025-09-09 13:48

    试试这个是否想要的效果。

    由于 Ctrl+R 是替换文本,建议改成 Ctrl+T 或其他,可通过参数配置修改,详情见参数说明。

    // 通过快捷键给块书签添加删除标签
    // see https://ld246.com/article/1757393544265
    (()=>{
        // 按键字母,由于Ctrl+R是替换,建议改成别的比如Ctrl+T
        const pressLetter = 'R'; // 必须大写
    
        // 添加的表情
        const emojis = '😀';
    
        // 表情位置,prefix 开头,suffix 结尾
        const emojisPosition = 'prefix';
      
        document.addEventListener('keydown', async (event) => {
            // 检查是否按下了 Ctrl+R
            // 这里推荐用 Ctrl + T,只需要把下行的 KeyR 改为 KeyT 即可
            if ((event.ctrlKey || event.metaKey) && event.code === 'Key'+pressLetter && !event.shiftKey && !event.altKey) {
                const cursorElement = getCursorElement();
                if(!cursorElement) return;
                const cursorBlock = cursorElement.closest('.protyle-wysiwyg [data-node-id][data-type]');
                if(!cursorBlock) return;
                const blockId = cursorBlock?.dataset?.nodeId;
                if(!blockId) return;
                event.preventDefault();
                event.stopPropagation();
                // 获取书签
                const data = await requestApi('/api/attr/getBlockAttrs', { "id": blockId });
                let bookmark = data?.data?.bookmark || '';
                // 添加删除表情
                const hasEmojis = bookmark.includes(emojis);
                bookmark = bookmark.replace(emojis, '');
                if(!hasEmojis) bookmark = emojisPosition === 'prefix' ? emojis + bookmark : bookmark + emojis;
                requestApi('/api/attr/setBlockAttrs', {
                    "id": blockId,
                    "attrs": { "bookmark": bookmark }
                });
            }
        }, true);
    
        function getCursorElement() {
            const selection = window.getSelection();
            if (selection.rangeCount > 0) {
                const range = selection.getRangeAt(0);
                // 获取选择范围的起始位置所在的节点
                const startContainer = range.startContainer;
                // 如果起始位置是文本节点,返回其父元素节点
                const cursorElement = startContainer.nodeType === Node.TEXT_NODE
                    ? startContainer.parentElement
                    : startContainer;
                return cursorElement;
            }
            return null;
        }
    
        async function requestApi(url, data, method = 'POST') {
            return await (await fetch(url, {method: method, body: JSON.stringify(data||{})})).json();
        }
    })();
    

    效果

  • [js] 简单实现 webdav 实时备份重要文件(密钥安全再无忧)

    2025-09-09 12:34

    是的,也是好办法,不过最好多种方案结合,我觉得至少有一种方案是自动备份的,就像我之前帖子 你应当有个免费的 webdav(一场由浏览器升级引发的“数据灾难”) 中说的那样 重要数据绝不能仅依赖“手动备份” 时间久了总有忘的时候。

  • [js] 简单实现 webdav 实时备份重要文件(密钥安全再无忧)

    2025-09-09 11:51

    好想法呀,这样的话,即使忘记,也可以一个个试出来,但这样会不会也可以被坏人试出来?

    最好这些词自定义,这样坏人就没办法了。

  • [js] 简单实现 webdav 实时备份重要文件(密钥安全再无忧)

    2025-09-09 11:30

    0.0.3 更新内容

    修改备份路径为/siyuan_backup

    0.0.2 更新内容

    对备份文件进行了简单的 base64 编码,并加了随机干扰码,因此解码时需要先去除前 10 位字符才行(可起到一定的安全作用)

  • 请插件开发者在 plugin.json 中添加 disabledInPublish 字段

    2025-09-08 22:22

    建议代码片段也增加发布服务是否加载的开关,否则 js 会暴露到前端,不安全

    issue Issue #15806 · siyuan-note/siyuan

  • 数据库主键外怎么添加块引或链接

    2025-09-08 11:31

    块引不行,链接 可以,比如块超级链接,选择链接类型或资源类型,如果资源类型里再选链接就好了。

  • SQL 问题请教

    2025-09-08 11:20

    完整版

    注意:最好把 % 文摘 之间的空格去掉(下面的示例已去掉空格),我不知道是否你故意添加的,请根据自己需要调整。

    SELECT b.id, b.path, b.content
    FROM blocks b
    LEFT JOIN refs r ON b.id = r.def_block_id
    WHERE b.type = 'd'
    AND b.path LIKE '/20250304225728-0glbw2i/%'
    AND b.content not like '%文摘-'
    AND r.def_block_id IS NULL
    AND b.id NOT IN (
        SELECT sub.parent_id
        FROM blocks sub
        JOIN refs r_sub ON sub.id = r_sub.def_block_id
        WHERE sub.type IN ('p', 'h', 'c', 't', 'm', 'i', 'b', 's')
    );
    
  • SQL 问题请教

    2025-09-08 10:39

    抱歉,之前说错了,应该是 and b.content not like '% 文摘-'

    加个 not 就好了

  • SQL 问题请教

    2025-09-07 23:29

    AND b.path LIKE '/20250304225728-0glbw2i/%' 这句后面添加 and b.content like '% 文摘-' 就行了吧。

    另外,你这里的 select ..., b.name 这个 b.name 应该改成 b.content 吧,或者 b.content as name

  • 是否有办法指定笔记本发布?

    2025-09-06 12:09

    其实可以用 nodejs 启动一个 web 服务,然后用思源前端代码 + 思源内核 api 实现一个简单的轻量级博客的。

    然后想显示什么,怎么显示自己决定,完全后端过滤,安全可控。

    缺点:不支持手机端发布,只能在 pc 端发布,不过一般博客用 pc 发布场景居多。

    强烈建议,思源搞个博客功能,类似发布服务,只不过有选项可控制发布内容,博客模板等,轻量级。

    还有一种方式,可以通过 思源 + pipe 最佳的笔记 / 博客方案 这个实现,对思源兼容性好,简单方便。

    或者通过 solo 自建博客。

    其他方式要么对思源兼容性不好,要么需要自己实现兼容。

  • 现在有没有体验较好的把笔记当成知识库的 AI 插件?

    2025-09-05 15:10

    我和你一样是使用者,你可以找作者问问。

  • 现在有没有体验较好的把笔记当成知识库的 AI 插件?

    2025-09-05 10:27

    ★ 思源:史上最简单的 MCP 使用教程(有手就行)

    如果对插件效果不满意 可以向作者 issue 或 github 搜 siyuan mcp 自行安装 mcp 服务

  • [css] 代码片段分享:将列表变成时间线笔记、日记

    2025-09-05 10:04

    其实就是一个二级列表而已,一级是左侧时间线,二级是具体内容

    image.png

    image.png

    如果操作不习惯,可以先取消样式编辑,编辑好后再恢复


    或者直接用我下面这个代码

    使用方法:

    1 先安装 runjs 插件

    2 随便新建个文档,把下面的代码放到 js 代码片段中

    // 显示/隐藏时间线
    // see https://ld246.com/article/1756998809429
    async function main() {
        // css 代码
        const styleContent = `
    /****************************** 时间线图 ******************************/
    [data-type="NodeList"][custom-setTimeLine] {
        overflow: hidden;
    
        >[data-type="NodeListItem"] {
            margin-top:0;
            margin-bottom:0;
            padding-top: 10px !important;
            padding-bottom: 20px !important;
    
            /* 图标 */
            >.protyle-action {
                top: 12px;
                left: 105px;
    
                scale: 1.2;
            }
    
            /* 第一个内容块:时间 */
            >.protyle-action+[data-type="NodeParagraph"] {
                position: absolute;
                margin-left: 10px;
            }
    
            /* 第二个内容块到最后一个内容块:内容 */
            >[data-type]:not(.protyle-action+[data-type="NodeParagraph"]) {
                margin-left: 147px;
            }
        }
      
        /* 线条 */
        >[data-type="NodeListItem"]::before {
            content: "";
    
            top: 0;
            left: 121.4px;
    
            height: 100%;
    
            border: 1px solid var(--b3-theme-on-surface) !important;
        }
    }
        `;
    
        // 定义 style 的内容
        const styleId = "ShowOrHideTimeLIne";
        // 获取现有的 <style> 元素
        let styleElement = document.getElementById(styleId);
      
        if (styleElement) {
            // 如果存在,则删除它
            styleElement.remove();
            await client.pushMsg({
                msg: "已隐藏", timeout: 3000,
            });
        } else {
            // 如果不存在,则创建并添加它
            const newStyle = document.createElement("style");
            newStyle.id = styleId;
            newStyle.textContent = styleContent;
            document.head.appendChild(newStyle);
            await client.pushMsg({
                msg: "已显示", timeout: 3000,
            });
        }
    }
    main();
    plugin.saveAction(thisBlock.id, "显示/隐藏时间线");
    

    3 然后 js 代码块右键菜单中按下图运行下代码

    image.png

    4 然后在顶部工具栏 runjs 菜单中,可以动态显示和隐藏时间线

    image.png

    注意:

    1 代码新增了 margin-top:0;margin-bottom:0; 不然在默认主题时间线之间有空隙

    2 该方法显示和隐藏只是临时的,即使隐藏刷新页面仍旧是时间线,如果想记住,可以用这个代码进行调整 求隐藏已完成任务块的代码片段 - wilsons 的回帖

  • 离开深圳,我在贵州山里写代码

    2025-09-05 09:08

    可以成了自由工作公司,远程办公,唯一考核标准就是按时完成任务。

    公司负责和用人单位接洽,员工只需要负责工作即可。

    但相关细节还需要解决,比如社保缴纳问题,需要相关部门支持,目前国内系统不成熟。

  • 思源笔记能否支持博客搭建, 笔记公有化分享

    2025-09-04 14:20

    我觉得不是,如果这样博客还有什么意义?本地博客?

  • 思源笔记能否支持博客搭建, 笔记公有化分享

    2025-09-04 13:10

    感觉 blossom 更像一个可部署本地的 b/s 应用。

    但不知道,如果本地和远程都部署一份,如何二者同步?还是无法同步,直接连远程服务?

    另外,这种部署方式,对普通用户(非开发)简直是地狱级难度。

    其实,思源也是部署在本地的 b/s 应用,只不过,思源通过一键安装的方式自动部署了。

    但其可取处,轻量级博客端,本地客户端可直接连接远程服务器,自带任务管理,便签等。

  • [js] 添加文档 / 块到指定数据库(支持添加任意字段)

    2025-09-03 22:08

    不用,之前的方法也是思源支持的方法,不算是 hack,只有计算 rowid 算是取巧,不过也不影响。

  • 请问有插件实现 Obsidian 那样的幻灯片功能吗?

    2025-09-03 22:04

    可以用全屏代码 [js] 全屏和宽屏 配合 pagedown pageup 按键简单实现

    先 alt+y 伪全屏,再用全屏按钮真全屏,再 page down/up