Frostime
关注
79553 号成员,2022-03-06 20:31:31 加入
2.1k
个人主页 浏览
489
帖子 + 回帖 + 评论
117h25m
在线时长
  • API 如何实现打开文档

    2025-12-19 12:06
    export const openBlock = (id: string, options: {
        app?: App;
        zoomIn?: boolean;
        action?: TProtyleAction[];
    } = {}) => {
        let app = options.app || pluginInstance?.app;
        if (isMobile()) {
            openMobileFileById(app, id);
        } else {
            openTab({
                app: app,
                doc: {
                    id: id,
                    zoomIn: options.zoomIn,
                    action: options.action,
                },
            });
        }
    };
    
  • 求助 query view 插件中查询子文档以及子文档内容的代码

    2025-12-12 17:23

    我这里没有这种情况,不太明白你那边为什么会这样。

    你可以看看控制台有没有什么报错信息,或者暂且用悬浮窗口中的跳转按钮替代一下。

    图片.png

  • QueryView 分享 | 查看所有的 Tag

    2025-12-11 18:51

    应该是思源更新,后端 API 增加了限制,需要代码改成这样

    let tags = await Query.request('/api/tag/getTag', {
            sort: 4,
    	app: window.siyuan.ws.app.appId
        });
    

    我已经把最新版本代码更新了。

    图片.png

  • 求助 query view 插件中查询子文档以及子文档内容的代码

    2025-12-10 21:54

    试一下这个

    //!js
    const query = async () => {
        const currentDocId = protyle.block.rootID;
        const childDocs = await Query.childDoc(currentDocId);
    
        // 先获取所有子文档内容
        const docsWithContent = await Promise.all(childDocs.map(async (doc) => {
            // 文档前两个子块拼接起来
            const childBlocks = await Query.request('/api/block/getChildBlocks', { id: doc.id });
            const beginBlocks = childBlocks.slice(0, 1).map(b => b.markdown).join('\n\n');
    
            const snapshot = beginBlocks;
            console.log(snapshot);
            return {
                ...doc,
                snapshot
            };
        }));
    
        const dv = Query.DataView(protyle, item, top);
    
        dv.addtable(docsWithContent, {
            cols: {
                title: '文档标题',
                created: '创建日期',
                snapshot: '内容概览'
            },
            renderer: (block, colKey) => {
                if (colKey === 'title') {
                    return Query.utils.asLink(block);
                }
                if (colKey === 'created') {
                    return Query.utils.renderAttr(block, colKey);
                }
                return block[colKey];
            },
            index: true,
            // fullwidth: true
        });
    
        dv.render();
    };
    return query();
    
    
  • 野路子玩转 LLM Agent | fmisc gpt 功能开发的一些随笔分享

    2025-11-25 13:30

    我提供的有 powershell 和 python 工具;效果确实相当好

    不过有时候一些复杂工具功能让 LLM 从零开始写很难实现,另一方面让他随意写脚本还是有安全性问题的

  • 为什么 gemini 的内容复制进 siyuan,会出现格式问题

    2025-08-19 22:02

    Gemini 对 latex 的支持一直不怎么样。

    以及有时候他会使用 \(\)\[\] 作为思源中 $$ $$..$$ 的替代品。

    你可以在 Prompt 中指定要求他,输出公式的时候使用 $<inline-latex-equation>$ $$<block-latex-equation>$$ 的格式来排版 Latex 数学公式。

  • 求助 QueryView 插件无法渲染证件倒计时组件(代码已验证有效)

    2025-07-28 23:50

    你这个就是纯粹的 HTML 代码,放在 html 块里面就行了。

    和 QueryView 插件没啥关系。

  • 在思源文档内进行 AI 对话

    2025-07-06 00:37

    gemini 有一个 OpenAI 兼容的接口,你可以去官方文档上看一下

  • 邀你共建|思源社区在线用户指南

    2025-06-25 14:25

    可以,比用 github 协作方便多了

  • 思源笔记中用反链管理内容的基本逻辑

    2025-06-19 00:44

    我的做法是在插件里加了一个 slash 命令,这样可以直接插入一个空的超级块

    图片.png

  • config.json 用户配置中密码竟然是明文密码,没有加密

    2025-06-12 11:53

    我建议不如在数据库中新增一个加密列类型。

    如果要在本地修改,可能需要增加块类型或大幅调整架构,实现起来会比较困难;相比之下,在数据库中添加一个新类型应该更容易实现。

    Issue #15015 · siyuan-note/siyuan

  • fmisc v6.00 更新, 支持 AI 工具调用

    2025-06-10 12:46

    确认一下工具是否启用了

    图片.png

  • 求教用 QueryView 插件使用 markdown 字段渲染卡片内容

    2025-05-31 18:29

    目前应该不行,用 Markdown 会遇到各种复杂的渲染问题,比如图片公式列表等,所以目前我都是统一用 content 字符来显示的。

  • 更新一下 fmisc GPT 对话的功能

    2025-05-21 15:41

    这是哪个服务商的?

  • 求教 QueryView 插件日记热力图

    2025-05-15 17:14
       // ECharts配置
        const option = {
          tooltip: {
            trigger: 'item',
            formatter: (params) => {
              const item = data[params.dataIndex];
              return `
                <div class="font-bold">${item.date}</div>
                <div>${item.title}</div>
                <div class="text-sm text-gray-500">字数: ${item.wordCount}</div>
                <div class="text-sm text-gray-500">活跃度: ${params.value[1]}</div>
              `;
            }
          },
          visualMap: {
            min: 0,
            max: Math.max(1000, actualMax), // 确保最大值合理
            show: false,
            type: 'piecewise',
            orient: 'horizontal',
            left: 'center',
            top: 'bottom',
            pieces: [
              { min: 0, max: 200, color: '#f1eef6' },
              { min: 201, max: 400, color: '#bdc9e1' },
              { min: 401, max: 600, color: '#74a9cf' },
              { min: 601, max: 800, color: '#2b8cbe' },
              { min: 801, max: 1000, color: '#045a8d' }
            ]
          },
          calendar: [
            {
              range: [`${thisYear}-01-01`, `${thisYear}-06-30`],
              cellSize: [22, 22],
              top: 50,
              left: 'center',
              orient: 'horizontal',
              dayLabel: { nameMap: 'cn' },
              monthLabel: { nameMap: 'cn' },
              itemStyle: {
                borderRadius: 4
              }
            },
            {
              range: [`${thisYear}-07-01`, `${thisYear}-12-31`],
              cellSize: [22, 22],
              top: 320,
              left: 'center',
              orient: 'horizontal',
              dayLabel: { nameMap: 'cn' },
              monthLabel: { nameMap: 'cn' },
              itemStyle: {
                borderRadius: 4
              }
            }
          ],
          series: [
            {
              type: 'heatmap',
              coordinateSystem: 'calendar',
              data: heatmapData,
              label: {
                show: false
              },
              emphasis: {
                itemStyle: {
                  shadowBlur: 10,
                  shadowColor: 'rgba(0, 0, 0, 0.5)'
                }
              }
            },
            {
              type: 'heatmap',
              coordinateSystem: 'calendar',
              calendarIndex: 1,
              data: heatmapData,
              label: {
                show: false
              },
              emphasis: {
                itemStyle: {
                  shadowBlur: 10,
                  shadowColor: 'rgba(0, 0, 0, 0.5)'
                }
              }
            }
          ]
        };
    
        // 添加标题
        dv.addmd(`### ${thisYear}年笔记日历`);
    
        // 添加ECharts图表
        dv.addecharts(option, {
          height: '600px',
          events: {
            click: (params) => {
              const item = data[params.dataIndex];
              Query.Utils.openBlock(item.id);
            }
          }
        });
    
        // 渲染视图
        dv.render();
    
      } catch (error) {
        console.error('查询执行出错:', error);
        dv.content = `<div class="bq text-red-500">查询执行出错: ${error.message}</div>`;
        dv.render();
      }
    }
    
    return query();
    
  • 求教 QueryView 插件日记热力图

    2025-05-15 17:14
    //!js
    const query = async () => {
      // 初始化QueryView数据视图
      let dv = Query.DataView(protyle, item, top);
    
      // 存储处理后的笔记数据
      const data = [];
      let thisYear;
    
      // 获取文档字数的函数
      const getDocWordCount = async (blockId) => {
        try {
          // 使用思源API获取文档内容
          const resp = await Query.docStat(blockId);
    
          return resp.stat.wordCount;
        } catch (error) {
          console.error('获取文档字数出错:', error);
          return 0;
        }
      };
    
      try {
        // 获取根文档的子文档
        let childs = await Query.childdoc(dv.root_id);
    
        // 遍历子文档获取日期笔记
        for (let child of childs) {
          const subchilds = await Query.childdoc(child.root_id);
    
          for (let subchild of subchilds) {
            // 从文档属性中提取日期信息
            const m = subchild.ial.match(/dailynote-(\d{4})(\d{2})(\d{2})/);
    
            // 跳过格式不匹配的文档
            if (!m) continue;
    
            const [_, year, month, day] = m;
    
            // 确定当前年份
            if (!thisYear) {
              thisYear = year;
            }
    
            // 添加到数据列表
            data.push({
              id: subchild.id,
              date: `${year}-${month}-${day}`,
              title: subchild.content,
              url: `siyuan://blocks/${subchild.id}`
            });
          }
        }
    
        // 确保有数据可展示
        if (data.length === 0) {
          dv.addmd('未找到符合条件的日期笔记');
          dv.render();
          return;
        }
    
        // 生成热力图数据(改为根据文档字数)
        const heatmapData = [];
        let maxWordCount = 0;
    
        // 先获取所有文档的字数并找出最大值
        for (const item of data) {
          const wordCount = await getDocWordCount(item.id);
          item.wordCount = wordCount;
          maxWordCount = Math.max(maxWordCount, wordCount);
        }
    
        // 生成热力图数据,根据字数计算热度值
        for (const item of data) {
          // 根据字数计算热度值,乘以系数使数值更合适
          const coefficient = 1000 / Math.max(1, maxWordCount); // 避免除以0
          const value = Math.min(item.wordCount * coefficient, 1000); // 限制最大值为1000
          heatmapData.push([item.date, value]);
        }
    
        const actualMax = Math.max(...heatmapData.map(item => item[1]));
    
    
  • 求教 QueryView 插件日记热力图

    2025-05-15 15:33
    1. data view 对象 dv 用法有问题,dv.content dv.title 这种都是他瞎编的接口,你可以尝试把说明文档发给他让他改一下
    2. getDocWordCount 函数写的有问题,用 Query.docStat API
    //!js
    const query = async () => {
      // 初始化QueryView数据视图
      let dv = Query.DataView(protyle, item, top);
    
      // 存储处理后的笔记数据
      const data = [];
      let thisYear;
    
      //====== 更改 API 调用 =====
      const getDocWordCount = async (blockId) => {
        try {
          // 使用思源API获取文档内容
          const resp = await Query.docStat(blockId);
    
          return resp.stat.wordCount;
        } catch (error) {
          console.error('获取文档字数出错:', error);
          return 0;
        }
      };
    
      try {
        // 获取根文档的子文档
        let childs = await Query.childdoc(dv.root_id);
    
        // 遍历子文档获取日期笔记
        for (let child of childs) {
          const subchilds = await Query.childdoc(child.root_id);
    
          for (let subchild of subchilds) {
            // 从文档属性中提取日期信息
            const m = subchild.ial.match(/dailynote-(\d{4})(\d{2})(\d{2})/);
    
            // 跳过格式不匹配的文档
            if (!m) continue;
    
            const [_, year, month, day] = m;
    
            // 确定当前年份
            if (!thisYear) {
              thisYear = year;
            }
    
            // 添加到数据列表
            data.push({
              id: subchild.id,
              date: `${year}-${month}-${day}`,
              title: subchild.content,
              url: `siyuan://blocks/${subchild.id}`
            });
          }
        }
    
        // 确保有数据可展示
        if (data.length === 0) {
          dv.addmd('未找到符合条件的日期笔记');
          dv.render();
          return;
        }
    
        // 生成热力图数据(改为根据文档字数)
        const heatmapData = [];
        let maxWordCount = 0;
    
        // 先获取所有文档的字数并找出最大值
        for (const item of data) {
          const wordCount = await getDocWordCount(item.id);
          item.wordCount = wordCount;
          maxWordCount = Math.max(maxWordCount, wordCount);
        }
    
        // 生成热力图数据,根据字数计算热度值
        for (const item of data) {
          // 根据字数计算热度值,乘以系数使数值更合适
          const coefficient = 1000 / Math.max(1, maxWordCount); // 避免除以0
          const value = Math.min(item.wordCount * coefficient, 1000); // 限制最大值为1000
          heatmapData.push([item.date, value]);
        }
    
        const actualMax = Math.max(...heatmapData.map(item => item[1]));
    
        // ECharts配置
        const option = {
          //保持不变
        };
    
        //====== 不存在 dv.title, dv.content 这种 API =====
        dv.addmd(`### ${thisYear}年笔记日历`);
    
        // 添加ECharts图表
        dv.addecharts(option, {
          height: '600px',
          events: {
            click: (params) => {
              const item = data[params.dataIndex];
              Query.Utils.openBlock(item.id);
            }
          }
        });
    
        // 渲染视图
        dv.render();
    
      } catch (error) {
        console.error('查询执行出错:', error);
        dv.content = `<div class="bq text-red-500">查询执行出错: ${error.message}</div>`;
        dv.render();
      }
    }
    
    return query();
    

    回复放不了完整的代码,你把这个发给豆包让他再根据差异改成完整的代码。

    效果大概就这样:

    图片.png

  • 阶梯式动态数据库:SuperRef 与动态数据库应用初探

    2025-05-14 14:26

    Tag 的搜索也可以用 QV

    图片.png

    不过要用最新的 1.2.2 ,1.2.1 版本的搜索有点 bug。

  • 阶梯式动态数据库:SuperRef 与动态数据库应用初探

    2025-05-14 12:30

    应该可以加个打开文档就自动更新的选项进去。

  • 有批量 doc 转 md 的软件吗?没有的话我回头写一个

    2025-05-13 00:37

    微软开源的有一个 markitdown 库,可以试试看

  • 请问思源笔记如何通过 API 获取块的内容,不返回其他属性

    2025-05-10 13:58

    直接用 SQL 查询块,然后获取 markdown 字段

  • 思源笔记是否能转成 PPT?

    2025-05-09 13:06

    讲真所有自动生成 PPT 里面我觉得最靠谱的是让大模型生成 Latex Beamer 代码然后编译成幻灯片 PDF 文档。。

  • 思源的 api 哪里有完整版

    2025-05-09 12:51

    最完整的在这里:https://github.com/siyuan-note/siyuan/blob/master/kernel/api/router.go

    你可以到 /kernel/api 目录下查看所有内核 API。

  • 用插件做生产力,万一插件下架怎么办?

    2025-05-07 13:38

    插件就算下架你本地的插件也还是会保留。如果觉得非常重要的插件,害怕他下架,可以直接去插件目录把插件打包备份一下。

    更进一步如果害怕插件直接删库跑路,可以去 github fork 一份源代码。

  • 如何以卡片形式查看笔记?

    2025-05-06 01:22

    不知道你想要的「卡片」的效果张什么样,我能想到最类似只能是这样的,不过 QV 的组件只能插件不能拖拽,如果你想要的是画廊看板那种可以交互的卡片那就只能等官方开发数据库的功能了。

    图片.png

    //!js
    const query = async () => {
        let dv = Query.DataView(protyle, item, top);
    
        const SQL = `
            select * from blocks
            order by random()
            limit 10;
        `;
        let blocks = await Query.sql(SQL);
        dv.addmd('**Card**')
        dv.addcard(blocks);
        dv.addmd('**Embed**');
        dv.addembed(blocks, { zoom: 0.7, columns: 3 });
        dv.render();
    }
    
    return query();
    
  • SQL 小助手

    2025-04-30 10:21

    网页版没法改 system prompt;就只能作为普通消息发给 Deepseek 了,不过在只有简单一两轮对话的情况下,差别不大。

  • 这个思源设置让你的一天从正能量开始!

    2025-04-20 16:33

    因为你 useState 没用对。

    //!js
    
    const chat = async () => {
        // Initialize DataView
        let dv = Query.DataView(protyle, item, top);
        // Render the initial elements added (title, prompt, placeholder)
        dv.render();
        const gptReply = dv.useState('gpt-reply', '');
    
        dv.addmd(`#### GPT Direct Send`);
    
        const predefinedPrompt = "今天真是美好的一天,请你激励我度过美好的一天吧!请你用满满的正能量激励我,并且要加入很多的表情,让我的心情愉快!";
    
        dv.addmd(`**You**: ${predefinedPrompt}`); // Display the message being sent
    
        if (gptReply.value) {
            dv.addmd(`**GPT**: ${gptReply.value}`); // Display the previous response
            return;
        }
    
        let respond = dv.addmd(`**GPT**: Thinking...`);
        let id = respond.dataset.id;
    
        try {
            const response = await Query.gpt(predefinedPrompt, {
                stream: true,
                streamInterval: 3,
                streamMsg: (content) => {
                    dv.replaceView(id, dv.md(`**GPT**: ${content}`));
                }
            });
           dv.replaceView(id, dv.md(`**GPT**: ${response}`));
           gptReply.value = response; // 记录 state
        } catch (error) {
            dv.replaceView(id, dv.md(`**GPT**: Error: ${error.message}`));
        }
    }
    
    // Execute the chat function immediately
    return chat();
    
    
  • 这个思源设置让你的一天从正能量开始!

    2025-04-20 16:15

    这里没有必要使用 useState,你只需要进行一次单轮对话就行,没必要把对话记录全都缓存下来。

    你这么做会导致对话的 message 长度越来越长。

    图片.png