-
API 获取并处理块的一些疑问
2024-12-18 12:10数据库的,有些可能不通用。
transSetAttrViewColWrap(avID: string, blockID: string, colID: string, wrap = true) { const op = {} as IOperation; op.action = "setAttrViewColWrap"; op.avID = avID; op.blockID = blockID; op.id = colID; op.data = wrap; return op; }, transSetAttrViewColHidden(avID: string, blockID: string, colID: string, hide = true) { const op = {} as IOperation; op.action = "setAttrViewColHidden"; op.avID = avID; op.blockID = blockID; op.id = colID; op.data = hide return op; }, transSetAttrViewSorts(avID: string, blockID: string, colID: string, order: "ASC" | "DESC") { const op = {} as IOperation; op.action = "setAttrViewSorts"; op.avID = avID; op.blockID = blockID; op.data = [{ column: colID, order }] return op; }, transSetAttrViewColCalc(avID: string, blockID: string, colID: string, operator: CalcOperator) { const op = {} as IOperation; op.action = "setAttrViewColCalc"; op.avID = avID; op.data = { operator }; op.blockID = blockID; op.id = colID; return op; }, transSetAttrViewFilters(avID: string, blockID: string, filters: IAVFilter[]) { const op = {} as IOperation; op.action = "setAttrViewFilters"; op.avID = avID; op.data = filters; op.blockID = blockID; return op; }, transSetAttrViewViewName(avID: string, viewID: string, name: string) { const op = {} as IOperation; op.action = "setAttrViewViewName"; op.avID = avID; op.id = viewID; op.data = name; return op; }, transUpdateAttrViewCol(avID: string, colId: string, name: string, type?: TAVCol) { const op = {} as IOperation; op.action = "updateAttrViewCol"; op.id = colId; op.avID = avID; op.name = name; op.type = type; return op; }, transSetAttrViewName(avID: string, name: string) { const op = {} as IOperation; op.action = "setAttrViewName"; op.id = avID; op.data = name; return op; }, transRemoveAttrViewCol(avID: string, colID: string) { const op = {} as IOperation; op.action = "removeAttrViewCol"; op.avID = avID; op.id = colID; return op; }, transAddAttrViewCol(avID: string, name: string, id = NewNodeID(), type: TAVCol = "text", previousID = "") { const op = {} as IOperation; op.action = "addAttrViewCol"; op.avID = avID; op.name = name; op.previousID = previousID; op.type = type; op.id = id; return op; }, transUpdateAttrViewCellBatch(args: { avID: string, cellID?: string, colID: string, rowID_BlockID: string, value: IAVCellValue }[]) { return args.map(arg => { const op = {} as IOperation; op.action = "updateAttrViewCell"; op.avID = arg.avID; op.id = arg.cellID; op.keyID = arg.colID; op.rowID = arg.rowID_BlockID; op.data = arg.value; return op; }) }, transInsertAttrViewBlock(avID: string, blockID: string, srcs: IOperationSrcs[], previousID = "", ignoreFillFilter = true) { const op = {} as IOperation; op.action = "insertAttrViewBlock"; op.avID = avID; op.blockID = blockID; op.previousID = previousID; op.ignoreFillFilter = ignoreFillFilter; op.srcs = srcs; return op; }, transDoUpdateUpdated(blockID: string, data = timeUtil.getYYYYMMDDHHmmss()) { const op = {} as IOperation; op.action = "doUpdateUpdated"; op.id = blockID op.data = data return op; },
-
API 获取并处理块的一些疑问
2024-12-18 12:09补充几个我常用的工具,用于创建一系列
IOperation
, 传给"/api/transactions"
即可。 批量的 IOperation 形成一个事务。transBatchUpdateAttrs(blockAttrs: { id: string, old: AttrType, new: AttrType }[]) { return blockAttrs.map(b => { const op = {} as IOperation; op.action = "updateAttrs"; op.id = b.id; op.data = JSON.stringify({ old: b.old, new: b.new }); return op; }); },
transUpdateBlocks(ops: { id: string, domStr: string }[]) { ops = ops.filter(op => !!op.id); if (!(ops.length > 0)) return []; return ops.map(({ id, domStr }) => { const op = {} as IOperation; op.action = "update"; // dom op.id = id; op.data = domStr; return op; }); },
transDeleteBlocks(ids: string[]) { return ids?.map(id => { const op = {} as IOperation; op.action = "delete"; op.id = id; return op; }) ?? []; },
transMoveBlocksAfter(ids: string[], previousID: string) { return ids.slice().reverse().map(id => { const op = {} as IOperation; op.action = "move"; op.id = id; op.previousID = previousID; return op; }); },
transMoveBlocksAsChild(ids: string[], parentID: string) { return ids.slice().reverse().map(id => { const op = {} as IOperation; op.action = "move"; op.id = id; op.parentID = parentID; return op; }); },
-
API 获取并处理块的一些疑问
2024-12-17 22:13"/api/block/insertBlock"
"/api/block/updateBlock"
可以传入 kramdown。 内容与属性需要用 '\n' 来隔开。id updated 可以不用去掉。 如果你要插入新的块,需要生成新的 id 避免重复。
"/api/block/getBlockDOM"
可以按块 id 读取 dom."/api/transactions"
可以按 dom 来更新或者插入等。另外, dom 与 markdown 的转换可以用:
export const NewLute: () => Lute = (globalThis as any).Lute.New;
lute = NewLute();
const md = lute.BlockDOM2Md(div.outerHTML);
export class Lute { public static WalkStop: number; public static WalkSkipChildren: number; public static WalkContinue: number; public static Version: string; public static Caret: string; public static New(): Lute; public static EChartsMindmapStr(text: string): string; public static NewNodeID(): string; public static Sanitize(html: string): string; public static EscapeHTMLStr(str: string): string; public static UnEscapeHTMLStr(str: string): string; public static GetHeadingID(node: ILuteNode): string; public static BlockDOM2Content(html: string): string; private constructor(); public BlockDOM2Content(text: string): string; public BlockDOM2EscapeMarkerContent(text: string): string; public SetSpin(enable: boolean): void; public SetTextMark(enable: boolean): void; public SetHTMLTag2TextMark(enable: boolean): void; public SetHeadingID(enable: boolean): void; public SetProtyleMarkNetImg(enable: boolean): void; public SetSpellcheck(enable: boolean): void; public SetFileAnnotationRef(enable: boolean): void; public SetSetext(enable: boolean): void; public SetYamlFrontMatter(enable: boolean): void; public SetChineseParagraphBeginningSpace(enable: boolean): void; public SetRenderListStyle(enable: boolean): void; public SetImgPathAllowSpace(enable: boolean): void; public SetKramdownIAL(enable: boolean): void; public BlockDOM2Md(html: string): string; public BlockDOM2StdMd(html: string): string; public SetSuperBlock(enable: boolean): void; public SetTag(enable: boolean): void; public SetInlineMath(enable: boolean): void; public SetGFMStrikethrough(enable: boolean): void; public SetGFMStrikethrough1(enable: boolean): void; public SetMark(enable: boolean): void; public SetSub(enable: boolean): void; public SetSup(enable: boolean): void; public SetInlineAsterisk(enable: boolean): void; public SetInlineUnderscore(enable: boolean): void; public SetBlockRef(enable: boolean): void; public SetSanitize(enable: boolean): void; public SetHeadingAnchor(enable: boolean): void; public SetImageLazyLoading(imagePath: string): void; public SetInlineMathAllowDigitAfterOpenMarker(enable: boolean): void; public SetToC(enable: boolean): void; public SetIndentCodeBlock(enable: boolean): void; public SetParagraphBeginningSpace(enable: boolean): void; public SetFootnotes(enable: boolean): void; public SetLinkRef(enable: boolean): void; public SetEmojiSite(emojiSite: string): void; public PutEmojis(emojis: IObject): void; public SpinBlockDOM(html: string): string; public Md2BlockDOM(html: string): string; public SetProtyleWYSIWYG(wysiwyg: boolean): void; public MarkdownStr(name: string, md: string): string; public GetLinkDest(text: string): string; public BlockDOM2InlineBlockDOM(html: string): string; public BlockDOM2HTML(html: string): string; public HTML2Md(html: string): string; public HTML2BlockDOM(html: string): string; }
-
思源笔记的资源都挤在一个文件夹里
2024-12-13 09:45早期有过 bug,但现在我是没遇到过.
备份一下就可以,没啥问题了. 或者 data 目录做成一个 git 仓库. 做完实验之后可以快速还原
-
思源笔记的资源都挤在一个文件夹里
2024-12-13 09:06这个确实是一个问题。 一个文件夹很大,无法手动打开的话,有一种不受控制的感觉。所以我做了一个工具,按日期重新分配图片到子文件夹,我自己是经常用,没发现啥问题。
如果全放到一个文件夹内, 并且文件数量在一定规模内,获取所有的资源文件,不需要遍历子文件夹,这样的话程序效率会高.
大多数人使用的情况来看,资源文件不会太多,按日期来分,或者放同一个文件夹里都一样。
如果说文件数量很多,个人觉得怎么搞都要完蛋。
咨询了一下,通义千问
操作系统和固态硬盘(SSD)支持的文件数量主要取决于文件系统类型、文件名长度、路径深度、以及单个目录中的条目数限制。对于 Windows 操作系统,常用的文件系统是 NTFS(New Technology File System)。NTFS 对单个目录中文件数量的支持是非常大的,理论上可以支持到 2^32-1(即 4,294,967,295)个文件,但实际上由于性能和其他因素的影响,很少会达到这个极限。
使用 SSD 并不会直接决定你可以在一个文件夹中存储多少文件,但与传统的 HDD 相比,SSD 在处理大量小文件时通常表现更好,因为它们没有机械运动部件,所以寻道时间和延迟更低。然而,即使使用 SSD,当一个文件夹中包含大量的文件时,程序处理这些文件的效率仍然可能会受到影响。这里有几个原因:
- 文件系统元数据:尽管 SSD 访问速度快,但如果需要频繁更新文件系统的元数据(例如,在创建、删除或重命名文件时),这可能成为瓶颈。
- 操作系统 API 调用:许多文件操作涉及操作系统级别的 API 调用,如
FindFirstFile
/FindNextFile
等,当文件数量巨大时,这些调用可能变得缓慢。 - 内存和 CPU 资源:处理大量文件的信息(例如,获取每个文件的属性)会消耗更多的内存和 CPU 资源。
- 应用程序逻辑:如果应用程序需要遍历所有文件并执行某些操作,随着文件数量的增长,所需的时间也会增加。
为了优化程序处理大量文件的性能,建议考虑之前提到的一些策略,比如分批处理、将文件分散到多个子文件夹中、利用索引机制等。此外,确保你的程序代码高效地使用了可用的 API,并且尽可能减少不必要的 I/O 操作,也是提高性能的关键。
如果你的应用场景确实涉及到非常大量的文件,并且性能是一个关键问题,那么可能还需要进一步研究特定于文件系统和硬件的最佳实践,甚至考虑定制化的解决方案。
-
思源可以做到取消输入“【】”自动变成 Todo 块的功能吗
2024-12-07 22:26方法 1:
对于安装好的思源可以这样改:
找到安装目录,比如:C:\Users\abc\AppData\Local\Programs\SiYuan\resources\stage\build\app
找到 main 开头的 js 文件:
查找
\u3010\u3011
然后删除,重启思源即可。
每次升级思源之后,都要改一次。
方法 2:改代码,自己编译:
把红色代码删除后编译。
-
图表功能怎么用?只见 echarts 报错
2024-11-11 00:00如果是做个树形图,或者网络图,可以试试
番茄工具箱
的块关系图
(图中:按住 ctrl 可以多选节点与连线;backspace 可删除节点与连线;可创建连线;alt 点击可以定位到文档)
(文档中:右键菜单可以定位到图中。快速创建引用可以用双向互链功能)
-
输入【】的时候,会出现的居然是待办清单
2024-11-04 14:51方法 1:
对于安装好的思源可以这样改:
找到安装目录,比如:C:\Users\abc\AppData\Local\Programs\SiYuan\resources\stage\build\app
找到 main 开头的 js 文件:
查找
\u3010\u3011
然后删除,重启思源即可。
每次升级思源之后,都要改一次。
方法 2:改代码,自己编译:
把红色代码删除后编译。
-
siyuan 究极性能优化笔记
2024-10-11 16:53👍 分析的很好。
对于很大的文档确实性能堪忧。
但是标题折叠那一块,对于小文档影响也很大。
比如一个文档里面有一些标题。
标题不多, 但当要清空整个文档内容的时候,就会很卡。
这时候我会做一个优化。
把所有内容放到超级快里面。
这样清空文档的时候,就是相当于删除一个超级快。也没必要检查折叠了。这样速度很快。
-
求助帮忙实现将所有文档快速制卡,或者将所有档案加入渐进阅读队列
2024-10-11 16:23要是安装了渐进学习插件或者番茄工具箱,可以打开开发者工具。
查询所有的文档。
b = tomato_zZmqus5PtYRi ?? progressive_zZmqus5PtYRi a = await b.siyuan.sql(`select * from blocks where type='d' limit 1000000000`) 查找所有的文档。 await b.siyuan.addRiffCards(a.map(i=>i.id)) 全部加入卡。
-
开发分享 | 简化 i18n 工作的一个方案
2024-10-08 19:06感谢,很有启发性。
如果可以直接生成 ts 代码更好。那样可以利用编译器的检查能力。
为了让编译器检查代码,确保正确,我目前是这样的:
做个模板,带有 ai 的提示词。
export class TomatoI18n { conf: Config.IConf; init() { this.conf = Siyuan.config } // TypeScript function: translate and fill other languages in the return. // Don't change the function name I provide. public get xxxx() { switch (this.conf.appearance.lang as ("it_IT" | Config.TLang)) { case "zh_CN": return "xxx"; case "es_ES": return case "fr_FR": return case "ja_JP": return case "zh_CHT": return case "it_IT": return case "en_US": default: return } } } export const tomatoI18n = new TomatoI18n();
每加一个翻译一次。 (i18n 的 key 用中文名比较显眼,也可加英文前缀。)
export class TomatoI18n { conf: Config.IConf; init() { this.conf = Siyuan.config } // TypeScript function: translate and fill other languages in the return. // Don't change the function name I provide. public get xxxx() { switch (this.conf.appearance.lang as ("it_IT" | Config.TLang)) { case "zh_CN": return "xxx"; case "es_ES": return case "fr_FR": return case "ja_JP": return case "zh_CHT": return case "it_IT": return case "en_US": default: return } } // TypeScript function: translate and fill other languages in the return. // Don't change the function name I provide. public get 移动到文档() { switch (this.conf.appearance.lang as ("it_IT" | Config.TLang)) { case "zh_CN": return "移动内容到文档"; case "es_ES": return "mover contenido a documento"; case "fr_FR": return "déplacer le contenu vers le document"; case "ja_JP": return "内容をドキュメントに移動"; case "zh_CHT": return "移動內容到文檔"; case "it_IT": return "spostare il contenuto al documento"; case "en_US": default: return "move content to document"; } } } export const tomatoI18n = new TomatoI18n();
使用的时候:
<label> <input type="checkbox" class="b3-switch settingBox" bind:checked={$back_link_move_here} /> <span class="b3-label__text"> {@html icon("Move", ICONS_SIZE)}</span > {tomatoI18n.移动到文档} </label>
可以配合 vscode 的 ai 功能。手动用 ai 翻译也可以。
不过我这样做的缺点也明显,就是需要人肉操作的部分比较多。
补充一个额外的好处,get 可以改为 function,支持复杂一点的信息。
public 推迟x小时(hours: number) { switch (this.conf.appearance.lang as ("it_IT" | Config.TLang)) { case "zh_CN": return `推迟${hours.toFixed(1)}小时`; case "es_ES": return `Retrasar ${hours.toFixed(1)} horas`; case "fr_FR": return `Retarder ${hours.toFixed(1)} heures`; case "ja_JP": return `${hours.toFixed(1)}時間遅れる`; case "zh_CHT": return `推遲${hours.toFixed(1)}小時`; case "it_IT": return case "en_US": default: return `Delay by ${hours.toFixed(1)} hours`; } }
-
试了一个叫“书立”的双链笔记工具,想到思源笔记应该全力商业化运营
2024-09-27 15:51数据库确实不错,但手动维护成本比较高。
但是自动的把反链插入到数据库中,就可以充分利用数据库的能力来处理反链。 查询过滤都很方便。
-
当引用、嵌入块的内容不在时,应能知道他们的其出处
2024-08-16 10:09修复 id 的尝试我也做过。也是一个思路,把 id 记录下来,在
番茄工具箱
的双向互链
功能就有这个尝试。比如两个块,用链接互相关联。
如果对它们做了接切与粘贴,那 id 一定变了。
如果在属性中做一个逻辑上的关联是可以修复的。
-
当引用、嵌入块的内容不在时,应能知道他们的其出处
2024-08-13 14:14抛砖引玉,提供一个可能的思路。
嵌入块、引用块,都可以把出处写入自定义属性。
这样就算原文被删除了,也可以知道大概的出处。
番茄工具箱的
复制内容到dailynote
功能就是这样设计的。 -
是否可以在搜索当中加入 AI 搜索?
2024-08-10 09:52我是用番茄工具箱的知识库功能。
把一部分文档和子文档上传到知识库。
然后可以针对知识库提问。
AI 的回答带有相关文档链接。
额外还有一个聊天框。
-
一个插件的 idea:双链插入快查表
2024-08-01 17:31丝滑打引用是个难题。
全拼,简拼法。
文档名前缀法。
还有你说的快查表法。
然后有人表示番茄工具箱已经使用 @@ 作为语法解决了这个需求。并且还有人提出问题:这样怎么解决重名的情况?我觉得没办法。
重名不会重复创建文档的。
另外,三个 @, 是子串匹配,匹配到了会原地改为引用。
出现近义词在所难免,可以使用文档合并功能,其中一个被删除的文档,其名字会作为保留下来的文档的别名。
不过这些办法都是半自动的,理论可行,就是用起来体验差。
-
从 2024 年的视角来看,以 Roam Research 为首的双向链接似乎已经降温
2024-08-01 14:56实际上可以达成这样的一种要求,也就是让 AI 在后台进行聚类的操作,给文档贴上好多的标签。
要是运用的是大厂的 AI ,在思源里面,还得先分辨出哪些文档是私密的。
最终,琢磨如何能更好地向用户呈现相关页面。
-
思源插件开发 | 使用前端框架要小心内存泄漏风险
2024-07-20 00:15我看了一下链接。销毁组件的销毁是通过,dialog 来完成的。
我之前也是这样处理的。
如果是,销毁的动作是从组件发出的,并所有销毁统一由 dialog 完成,就需要把 dialog 的引用传给组件,从组件里面调用 dialog.destroy,关闭窗口的同时销毁资源。
那创建组件和 dialog 的时候,双方都需要对方的引用。这种写法怪怪的。
所以,我借用了另外的工具来做销毁工作。除了释放资源,还可以做其他逻辑上相关的工作,比如把某个变量设置为 null。