-
API 如何实现打开文档
2025-12-19 12:06export 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我这里没有这种情况,不太明白你那边为什么会这样。
你可以看看控制台有没有什么报错信息,或者暂且用悬浮窗口中的跳转按钮替代一下。

-
QueryView 分享 | 查看所有的 Tag
2025-12-11 18:51应该是思源更新,后端 API 增加了限制,需要代码改成这样
let tags = await Query.request('/api/tag/getTag', { sort: 4, app: window.siyuan.ws.app.appId });我已经把最新版本代码更新了。

-
求助 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:02Gemini 对 latex 的支持一直不怎么样。
以及有时候他会使用
\(\)和\[\]作为思源中$$$$..$$的替代品。你可以在 Prompt 中指定要求他,输出公式的时候使用
$<inline-latex-equation>$$$<block-latex-equation>$$的格式来排版 Latex 数学公式。 -
求助 QueryView 插件无法渲染证件倒计时组件(代码已验证有效)
2025-07-28 23:50你这个就是纯粹的 HTML 代码,放在 html 块里面就行了。
和 QueryView 插件没啥关系。
-
config.json 用户配置中密码竟然是明文密码,没有加密
2025-06-12 11:53我建议不如在数据库中新增一个加密列类型。
如果要在本地修改,可能需要增加块类型或大幅调整架构,实现起来会比较困难;相比之下,在数据库中添加一个新类型应该更容易实现。
-
mermaid 在 kanban 程序中怎设置内部链接?
2025-06-02 01:03KanBan 的用法比较反人类,具体来说你要先设置一个基础 URL,然后再设置中填具体的参数。
https://github.com/frostime/sy-query-view/blob/main/src/core/components.ts#L852
-
求教用 QueryView 插件使用 markdown 字段渲染卡片内容
2025-05-31 18:29目前应该不行,用 Markdown 会遇到各种复杂的渲染问题,比如图片公式列表等,所以目前我都是统一用 content 字符来显示的。
-
求教 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- data view 对象
dv用法有问题,dv.content dv.title 这种都是他瞎编的接口,你可以尝试把说明文档发给他让他改一下 getDocWordCount函数写的有问题,用Query.docStatAPI
//!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();回复放不了完整的代码,你把这个发给豆包让他再根据差异改成完整的代码。
效果大概就这样:

- data view 对象
-
思源的 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 的组件只能插件不能拖拽,如果你想要的是画廊看板那种可以交互的卡片那就只能等官方开发数据库的功能了。

//!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(); -
这个思源设置让你的一天从正能量开始!
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 长度越来越长。









