wilsons
关注
142093 号成员,2024-05-12 13:24:23 加入
3.7k
个人主页 浏览
2.0k
帖子 + 回帖 + 评论
正在努力开发 wilsons 工具箱中 🛠️ 目前已正式入驻爱发电啦!💖 想催更、提需求?欢迎访问 👉 https://afdian.com/a/wilsons
  • 希望优化下思源网页端的 url 路径的细节

    2025-07-21 02:13

    拿走不谢

    // 自动给浏览器URL添加当前文档ID
    (()=>{
        if(!isBrowser()) return;
        const main = (event) => {
            const id = event?.detail?.protyle?.block?.id;
            setUrlParam('id', id);
        };
        eventBusOn('loaded-protyle-static', main);
        eventBusOn('switch-protyle', main);
        function eventBusOn(eventName, callback) {
            const pluginName = 'my-custom-plugin';
            if(window.siyuan.ws.app.plugins?.length === 0) {
                console.log('绑定事件'+eventName+'失败,请至少安装一个插件');
                return false;
            }
            let myPlugin = window.siyuan.ws.app.plugins.find(item=>item.name === pluginName);
            if(!myPlugin) {
                const Plguin = Object.getPrototypeOf(window.siyuan.ws.app.plugins[0].constructor);
                const MyPlugin = class extends Plguin{};
                myPlugin = new MyPlugin({app:window.siyuan.ws.app.appId, name:pluginName, displayName:pluginName});
                myPlugin.openSetting = null; // 防止顶部插件按钮添加设置菜单
                window.siyuan.ws.app.plugins.push(myPlugin);
            }
            myPlugin.eventBus.on(eventName, callback);
            return true;
        }
        function setUrlParam(key, value) {
            const url = new URL(window.location.href);
            url.searchParams.set(key, value);
            window.history.pushState({}, '', url.toString());
        }
        function isBrowser() {
            return !navigator.userAgent.startsWith("SiYuan") ||
                navigator.userAgent.indexOf("iPad") > -1 ||
                (/Android/.test(navigator.userAgent) && !/(?:Mobile)/.test(navigator.userAgent));
        }
    })();
    
  • 社区|已完成思源官方用户指南的迁移

    2025-07-20 15:09

    意思相近,但不够清晰,你找下这个,看能一下子找到吗?https://docs.siyuan-note.club/zh-Hans/reference/database/table.htmlhttps://docs.siyuan-note.club/zh-Hans/ 这个网站可以,很清晰。

    我怕直接调整后你不同意,如果没意见后面空了调整下结构,看起来更清晰明了些。不过,可能有些调整我没有权限,还得你来。

    还是一下子调整好更好,后面内容多了,调整起来更麻烦。

    结构清晰,用户知道内容往哪放,否则用户可能会按自己的想法放,时间久了就很混乱。


    更新:

    简单调整了下感觉清晰多了,主要把开发者指南和高级自定义分割开来,把之前开发需要的内容移动到开发者指南里。不要散落到各处。

    高级自定义如果用户需要可以放到深入了解里,不然会把开发者文档和其他文档杂糅在一起,不方便查询。

    开发者文档更像一个手册随时查询,不应该有无关内容影响。

  • 社区|已完成思源官方用户指南的迁移

    2025-07-20 14:32

    大佬,最近使用 https://www.siyuan-note.club/ 一段时间后发现,这个网站的结构混乱,每次想找东西都要点半天。

    我觉得,整体结构应该这样划分更清晰:

    1. 基础教程(可包括官方教程)
    2. 进阶教程
    3. 社区资源(包括用户分享教程或资源等,与 1 和 2 的不同是,1 和 2 是偏教程类,社区资源更自由,随意分享)
    4. 开发者教程(包括插件开发,主题开发,数据字段介绍,api 等)
    5. api(也可放到 4 开发者教程中)
    6. 其他
  • [js] 不用插件:思源如何添加自定义命令

    2025-07-20 00:08

    这个可以

    方法 1(适用于直接获取当前焦点文档)

    function getProtyle() {
        try {
            if(document.getElementById("sidebar")) return window.siyuan.mobile.editor.protyle;
            const currDoc = window.siyuan?.layout?.centerLayout?.children.map(item=>item.children.find(item=>item.headElement?.classList.contains('item--focus') && (item.panelElement.closest('.layout__wnd--active')||item.panelElement.closest('[data-type="wnd"]')))).find(item=>item);
            return currDoc?.model.editor.protyle;
        } catch(e) {
            console.error(e);
            return null;
        }
    }
    

    方法 2(适用于通过 closest 等获取到.protyle 元素后,再获取实例)

    // 调用示例 getInstanceById('d428018f-82e8-4f7a-9c06-c3d192517b2d')
    function getInstanceById(id, layout = window.siyuan.layout.centerLayout) {
        const _getInstanceById = (item, id) => {
            if (item.id === id) return item;
            if (!item.children) return;
            let ret;
            for (let i = 0; i < item.children.length; i++) {
                ret = _getInstanceById(item.children[i], id);
                if (ret) return ret;
            }
        };
        return _getInstanceById(layout, id);
    }
    
  • 搜索字体属性?

    2025-07-19 15:54

    @JeffreyChen

    找到方法了,先安装 [js] 告别 select * from blocks!嵌入块多字段查询来了 代码片段

    然后嵌入块执行以下 SQL 即可,思源默认嵌入块不支持。

    SELECT markdown, block_id as id__hide
    FROM spans
    WHERE markdown REGEXP 'style="color:\s*var\([^)]+?\);*"';
    

    前面 @qiancang 提了一嘴,没太注意,刚才打算查看 attributes 表是否有 span_id 时发现 spans 表本身就有 markdown 字段。

  • 发了个 hnsw 包, 有用到的老哥可以试试

    2025-07-19 15:34

    👍 这个适合集成到思源吗?或者不知有没有适合集成到思源的简单方案

  • 搜索字体属性?

    2025-07-19 14:56

    感谢采纳!

    授之以鱼,不如授之以渔。

    我说说这类需求查询思路吧。

    1 先找一个或几个块做测试,标记上不同颜色

    2 然后在数据库里查询这些块,看有什么规律

    3 通过规律查询和筛选即可

    比如,你这里的需求,我添加了三处标记

    image.png

    然后查询数据库得知,markdown 字段,如下

    image.png

    然后,你就可以根据规律去查询了.

    比如,如果你想仅查询包含颜色标记,但不包含标记背景及背景和颜色同时标记的,可以这样查询。

    SELECT *
    FROM blocks
    WHERE type='p' AND markdown REGEXP 'style="color:\s*var\([^)]+?\);*"';
    

    如果自己找不出或不会写,可以把相关信息复制给 ai,让 ai 帮忙写。现在 ai 可以支持图片和视频,一般都没问题。

    当然,可以按 J 佬说的用 attributes 表提高性能,比如

    SELECT * FROM blocks WHERE type='p' AND id IN (
        SELECT block_id FROM attributes 
        WHERE name = "style" 
        AND value REGEXP '^color:\s*var\([^)]+?\);*$'
    );
    

    注意,这里 attributes 表的数据和 blocks 表的格式略有不同,如下

    image.png

    其他样式也类似,思路都是相通的。

    但要注意,这里只能查询到块,没办法仅显示行内标记颜色的元素,如果仅显示行内标记颜色的元素,这会变得更加复杂,估计得用 js 去实现了或尝试 spans 表查询。

    spans 表查询方式,可参考 搜索字体属性? - wilsons 的回帖

  • [js] 不用插件:思源如何添加自定义命令

    2025-07-19 07:31

    如果想完全不依赖插件,比如基础 Plugin,还是要写不少东西的,而且还要与现有插件兼容,太麻烦了。

    都不如直接抛弃插件方案,自己实现想要的功能,哈哈。

  • [js] 不用插件:思源如何添加自定义命令

    2025-07-19 07:11

    👍

    正是不想写太多代码,才利用现有已安装插件的。

  • 关于悬浮窗出现的速度

    2025-07-18 18:07

    理论上仅与文档 2 的块 1 的内容有关。

    但不知思源悬窗聚焦是否仅渲染块数据,如果也会渲染文档,就会和文档 2 也有关。

  • 手机端如何禁止左右滑动弹出文档树和设置菜单

    2025-07-18 17:36

    可以,你测试下就知道了

    正常点击,上下滚动,其他组件滚动均没有影响。

  • 手机端如何禁止左右滑动弹出文档树和设置菜单

    2025-07-18 17:33

    把此代码放到 js 代码片段中即可

    // 左右滑动时文档树和设置菜单同时禁用
    if (!!document.getElementById("sidebar")) {
        const moveHandle = (e) => {
            if(e.target.closest('#menu, #sidebar')) return;
            document.getElementById("menu").style.transform = "";
            document.getElementById("sidebar").style.transform = "";
            const maskElement = document.querySelector(".side-mask");
            maskElement.classList.add("fn__none");
            maskElement.style.opacity = "";
        };
        document.addEventListener("touchmove", moveHandle, { passive: false});
        document.addEventListener("touchend", moveHandle, { passive: false });
    }
    

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


    改进版,支持仅禁用一个

    // 手机端禁止左右滑动弹出文档树和设置菜单
    // 改进版,支持仅禁用一个
    if (!!document.getElementById("sidebar")) {
        // 右滑不弹出文档树 true 不弹出 false 弹出
        const disableFileTree = true;
    
        // 左滑不弹出设置菜单 true 不弹出 false 弹出
        const disableSetingMenu = false;
    
        // 拖动开始
        let startX = 0;
        document.addEventListener("touchstart", (e) => {
            if(e.target.closest('#menu, #sidebar')) return;
            const touch = e.touches[0];
            startX = touch.clientX; // 记录初始X坐标
        }, { passive: true });
    
        // 拖动中
        const hideMask = () => {
            const maskElement = document.querySelector(".side-mask");
            maskElement.classList.add("fn__none");
            maskElement.style.opacity = "";
        };
        const moveHandle = (e) => {
            if(e.target.closest('#menu, #sidebar')) return;
            if(disableSetingMenu) document.getElementById("menu").style.transform = "";
            if(disableFileTree) document.getElementById("sidebar").style.transform = "";
    
            if(disableFileTree && disableSetingMenu) {
                hideMask(); // 同时禁用
            } else {        // 仅禁用一个
                const touch = e.touches[0];
                const currentX = touch?.clientX||0;
                const diffX = currentX - startX;
                if (Math.abs(diffX) > 0) {
                    if (diffX < 0) {
                        // 左滑 设置菜单
                        if(disableSetingMenu) hideMask();
                    } else {
                        // 右滑 文档树
                        if(disableFileTree) hideMask();
                    }
                    startX = currentX;
                }
            }
        };
        document.addEventListener("touchmove", moveHandle, { passive: false});
    
        // 拖动结束
        const endHandle = (e) => {
            if(e.target.closest('#menu, #sidebar')) return;
            if(disableSetingMenu) document.getElementById("menu").style.transform = "";
            if(disableFileTree) document.getElementById("sidebar").style.transform = "";
            hideMask();
        };
        document.addEventListener("touchend", endHandle, { passive: false });
    }
    

    高性能版

    高性能版(新)

  • [js] Shift+F5 刷新页面

    2025-07-17 22:29

    最新版刷新方法

    see Issue #15308 · siyuan-note/siyuan

    // Shift+F5 重载界面 JS片段
    (()=>{
        document.addEventListener('keydown', function(event) {
            // 检查是否同时按下了 Shift 和 F5 ,并且没有其他修饰键被按下
            if (event.shiftKey && event.key === 'F5' && !event.ctrlKey && !event.altKey && !event.metaKey) {
                event.preventDefault(); // 阻止默认行为
                reloadUI(); // 重载界面 API
            }
        });
        // 刷新UI
        // mode app客户端 desktop浏览器桌面端 mobile移动端
        // see https://github.com/siyuan-note/siyuan/issues/15308
        function reloadUI(mode) {
            // 未安装插件
            if(window.siyuan.ws.app.plugins?.length === 0) {
                if (mode) window.location.pathname = `stage/build/${mode}/`;
                else fetch('/api/ui/reloadUI', { method: 'POST' });
                return;
            }
            // 获取plugin
            const plugin = window.siyuan.ws.app.plugins[0];
            // 旧版
            if(!plugin?.saveLayout) {
                if (mode) window.location.pathname = `stage/build/${mode}/`;
                else fetch('/api/ui/reloadUI', { method: 'POST' });
                return;
            }
            // 新版
            plugin.saveLayout(() => {
                if (mode) window.location.pathname = `stage/build/${mode}/`;
                else window.location.reload();
            });
        }
    })();
    
  • 属性引用:彻底解决引用丢失问题,方案可行性探讨和投票

    2025-07-16 19:38

    👍

    我以前剪切用它 [js] 带块 id 的剪切

    现在有了它 [js] 属性引用:彻底解决引用丢失问题

    可以随意剪切了 😄

    前者是防御型武器,后者是修复型武器,二者配合天下无敌!

  • 属性引用:彻底解决引用丢失问题,方案可行性探讨和投票

    2025-07-16 19:14

    哈哈,确实极端。

    但思源面对的是大众需求,不能太个性化。

  • docker 搭建的思源笔记如何与本地客户端思源笔记同步?

    2025-07-16 16:27

    docker 实际上只是一个普通的客户端而已(这点容易被人误解为其他端提供服务的服务端)。

    借贴说说思源的架构和客户端、docker 之间的关系。

    总体来说,思源架构是:客户端(前端)+ 内核(提供 api 调用的后端服务)

    桌面客户端:electron 作为客户端 + golang 内核(api)

    移动端:客户端嵌入 webview + golang 内核(api)

    docker:浏览器作为客户端 + golang 内核(api)

    说白了无论 electron、webview、浏览器,本质都是基于 webui。

    所以,docker 其实和其他端没有任何区别,无非是客户端实现技术不同罢了。

  • [js] 粘贴为段落(自动把剪切板一个换行转换为两个)

    2025-07-16 13:52

    我这边可以,你检查下是否受其他插件或代码的影响导致的。

  • 我需要在数据库中使用模板语法计算股票交易的移动平均成本

    2025-07-16 11:37

    是的,你想多了,没有说要写到数据库外部,写到数据库模板里即可,因为数据库模板支持 html 格式。

    为什么是 img 标签,因为可以利用 onerror 执行 js,这是 hack 方法。

    不能直接写 js 片段,会被过滤或转换为实体,不会被执行。

    api 在哪里?参考 https://www.siyuan-note.club/6992007m0

  • 思源笔记如何开发插件

    2025-07-15 19:42

    你完全可以开个小号,自问自答,哈哈 😄

  • 求段小代码:隐藏文档树的各种弹框

    2025-07-15 19:32

    不需要 js,css 即可

    .sy__file .b3-tooltips:hover::before, .sy__file .b3-tooltips:hover::after, /* 隐藏更多新建提示 */
    body:has(.sy__file .b3-list-item--hide-action:hover>.b3-list-item__action) #tooltip /* 隐藏文档信息提示 */
    {
        display: none;
    }
    
  • /data/assets/ 目录下的图片会被加密,有什么解决办法吗

    2025-07-15 16:06

    还有两个办法

    1 图片改扩展名,比如统一添加后缀.xxx,试试是否还会加密
    2 粘贴为 base64 图片

    不过这两种方式需要自己拦截思源的默认操作或者自己添加右键菜单实现。

    第 1 种办法如果可行,可以外部监听文件夹添加文件,在加密前重命名,如果可以的话,思源就无需任何改动了。

  • /data/assets/ 目录下的图片会被加密,有什么解决办法吗

    2025-07-15 15:25

    这样的话,如果开发打算连接手机调试,或有人准备连接手机复制文件,那手机文件不是瞬间废了吗?

  • [js] 粘贴为链接(可自动获取文章标题)

    2025-07-15 15:14

    🖱️ 右键轻唤,链接如星现。
    🕰️ 往昔寻题心倦,思绪纷乱如线。
    ✨ 今朝妙技巧呈祥,粘来自带华章。
    💡 不费丝毫气力,逍遥意韵悠长。

  • /data/assets/ 目录下的图片会被加密,有什么解决办法吗

    2025-07-15 14:19

    u 盘和移动硬盘会加密吗?如果不会可以放在这些盘里。试用前做好备份,防止数据损坏。

  • 关于 assets 文件夹的疑问

    2025-07-15 13:22

    思源默认不支持文件夹,会在 assets 复制一份

    如果想支持得修改和拦截思源的默认行为,较麻烦。

  • 请教数据库中模版函数的用法

    2025-07-15 13:02

    1 和 2

    .action{$result := querySQL (replace "?" .主键 "SELECT count(*) as count FROM blocks WHERE type ='h' AND hpath like '%?%'")}
    .action{ $f := first $result}
    .action{$f.count}
    

    .action{$blocks := queryBlocks "SELECT * FROM blocks WHERE type ='h' AND hpath like '%?%'" .主键}
    .action{ $count := len $blocks}
    .action{$count}
    

    3 换个思路,既然添加文档时插入不好实现,可以在打开数据库文档时查询后插入嘛

    参考 批量导入文档到数据库 0.0.6 版全新发布

  • 我需要在数据库中使用模板语法计算股票交易的移动平均成本

    2025-07-14 21:44

    正如 J 佬所说,单依靠模板语法无法实现。

    模板语法不支持调 api,不过,可以这样

    <img src onerror="
    // 代码写这里
    " />
    

    然后就可以愉快的调 api 了

    1 先通过数据库 api 获取数据库的完整数据(或通过 dom 遍历)

    2 根据 this 找出本单元格的列 id 和行 id

    3 然后,你想怎么算就怎么算了

    4 结果赋值给 this.ouerHTML 即可

    对了,为了防止多次获取数据,造成资源浪费,最好把获取结果放到固定对象上,比如这里的表格 dom 对象,如果已存在无需再次获取,只需读取即可。