player
关注
86043 号成员,2022-07-05 00:19:23 加入
403
个人主页 浏览
277
帖子 + 回帖 + 评论
73h5m
在线时长
  • 官方、官方,能否增加侧边显示备注功能?类似于 Word 的批注

    2024-12-20 23:54

    可以试试这个查看正反链的侧边栏 结合 批注功能的效果。

  • 一本 50 万字的文档,读一读的,后面不加载出来了,急死人

    2024-12-20 23:46

    用渐进学习插件拆分开来读

  • 话说闪卡没有复习提示的吗?

    2024-12-18 20:56

    image.png

    番茄工具箱有个类似功能,上面有个数字代表有多少闪卡没复习。当前是 6 个。

  • 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,当一个文件夹中包含大量的文件时,程序处理这些文件的效率仍然可能会受到影响。这里有几个原因:

    1. 文件系统元数据:尽管 SSD 访问速度快,但如果需要频繁更新文件系统的元数据(例如,在创建、删除或重命名文件时),这可能成为瓶颈。
    2. 操作系统 API 调用:许多文件操作涉及操作系统级别的 API 调用,如 FindFirstFile/FindNextFile 等,当文件数量巨大时,这些调用可能变得缓慢。
    3. 内存和 CPU 资源:处理大量文件的信息(例如,获取每个文件的属性)会消耗更多的内存和 CPU 资源。
    4. 应用程序逻辑:如果应用程序需要遍历所有文件并执行某些操作,随着文件数量的增长,所需的时间也会增加。

    为了优化程序处理大量文件的性能,建议考虑之前提到的一些策略,比如分批处理、将文件分散到多个子文件夹中、利用索引机制等。此外,确保你的程序代码高效地使用了可用的 API,并且尽可能减少不必要的 I/O 操作,也是提高性能的关键。

    如果你的应用场景确实涉及到非常大量的文件,并且性能是一个关键问题,那么可能还需要进一步研究特定于文件系统和硬件的最佳实践,甚至考虑定制化的解决方案。

  • 长文档 CTRL+A 复制不全

    2024-12-09 11:22

    番茄工具箱有这个

    image.png

  • 思源可以做到取消输入“【】”自动变成 Todo 块的功能吗

    2024-12-07 22:26

    方法 1:

    对于安装好的思源可以这样改:

    找到安装目录,比如:C:\Users\abc\AppData\Local\Programs\SiYuan\resources\stage\build\app

    找到 main 开头的 js 文件:

    image.png

    查找 \u3010\u3011

    然后删除,重启思源即可。

    每次升级思源之后,都要改一次。

    image.png

    方法 2:改代码,自己编译:

    把红色代码删除后编译。

    image.png

  • 番茄工具箱快捷键 F3,文本转引用,怎么设置成文档新建在当前文档下面

    2024-11-17 22:30

    这个功能保存的位置,是与两个中括号创建文档保存的位置一致的。

  • 图表功能怎么用?只见 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 文件:

    image.png

    查找 \u3010\u3011

    然后删除,重启思源即可。

    每次升级思源之后,都要改一次。

    image.png

    方法 2:改代码,自己编译:

    把红色代码删除后编译。

    image.png

  • 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`;
            }
        }
    
  • 搜索正则非常慢, 即使出结果了切换下一页至少等待 2 分钟

    2024-09-28 01:05

    应该有一个选项,禁止一打开就立刻执行正则搜索。如果写错正则的话,会很悲剧的。

  • 试了一个叫“书立”的双链笔记工具,想到思源笔记应该全力商业化运营

    2024-09-27 15:51

    image.png

    image.png

    image.png

    数据库确实不错,但手动维护成本比较高。

    但是自动的把反链插入到数据库中,就可以充分利用数据库的能力来处理反链。 查询过滤都很方便。

  • 闪卡设置到期时间的疑问

    2024-08-29 00:09

    这是一个坑。我踩过。API("/api/riff/batchSetRiffCardsDueTime")设置到期时间用的是 +0 时区。

  • 当引用、嵌入块的内容不在时,应能知道他们的其出处

    2024-08-16 10:09

    修复 id 的尝试我也做过。也是一个思路,把 id 记录下来,在 番茄工具箱双向互链 功能就有这个尝试。

    比如两个块,用链接互相关联。

    如果对它们做了接切与粘贴,那 id 一定变了。

    如果在属性中做一个逻辑上的关联是可以修复的。

  • 插件开发 Quick Start

    2024-08-15 23:26

    感谢分享。如果能够早一点出来的话,那么我就能够节省相当多的时间和精力。

  • 当引用、嵌入块的内容不在时,应能知道他们的其出处

    2024-08-13 14:14

    抛砖引玉,提供一个可能的思路。

    嵌入块、引用块,都可以把出处写入自定义属性。

    这样就算原文被删除了,也可以知道大概的出处。

    番茄工具箱的 复制内容到dailynote 功能就是这样设计的。

    image.png

    image.png

  • 是否可以在搜索当中加入 AI 搜索?

    2024-08-10 09:52

    我是用番茄工具箱的知识库功能。

    把一部分文档和子文档上传到知识库。

    然后可以针对知识库提问。

    AI 的回答带有相关文档链接。

    drawing

    额外还有一个聊天框。

    image.png

  • 如何实现在页面内无限次滚动复习闪卡?

    2024-08-10 09:47

    把复习到期的时间改为现在。

    番茄工具,打开闪卡工具,文档中右键菜单,设置推迟为 0 即可。相当于快进时间,复习次数会累计。

    image.png

  • 集市什么时候新增评论和评分功能

    2024-08-08 11:36

    可以在论坛同步一个插件,主题等的列表。

    从论坛可以 @ 到它们,进行评论。

  • 一个插件的 idea:双链插入快查表

    2024-08-01 17:31

    丝滑打引用是个难题。

    全拼,简拼法。

    文档名前缀法。

    还有你说的快查表法。


    然后有人表示番茄工具箱已经使用 @@ 作为语法解决了这个需求。并且还有人提出问题:这样怎么解决重名的情况?我觉得没办法。

    重名不会重复创建文档的。

    另外,三个 @, 是子串匹配,匹配到了会原地改为引用。


    出现近义词在所难免,可以使用文档合并功能,其中一个被删除的文档,其名字会作为保留下来的文档的别名。

    不过这些办法都是半自动的,理论可行,就是用起来体验差。

  • 如何快速删除 空白笔记

    2024-08-01 16:25

    本来是一个自用的功能,看到了,就分享一下。

    开启: 番茄工具箱配置 - 杂项。

    复制代码后,ctrl+shif+i 打开控制台。

  • 从 2024 年的视角来看,以 Roam Research 为首的双向链接似乎已经降温

    2024-08-01 14:56

    实际上可以达成这样的一种要求,也就是让 AI 在后台进行聚类的操作,给文档贴上好多的标签。

    要是运用的是大厂的 AI ,在思源里面,还得先分辨出哪些文档是私密的。

    最终,琢磨如何能更好地向用户呈现相关页面。

  • 思源插件开发 | 使用前端框架要小心内存泄漏风险

    2024-07-20 00:15

    我看了一下链接。销毁组件的销毁是通过,dialog 来完成的。

    我之前也是这样处理的。

    如果是,销毁的动作是从组件发出的,并所有销毁统一由 dialog 完成,就需要把 dialog 的引用传给组件,从组件里面调用 dialog.destroy,关闭窗口的同时销毁资源。

    那创建组件和 dialog 的时候,双方都需要对方的引用。这种写法怪怪的。

    所以,我借用了另外的工具来做销毁工作。除了释放资源,还可以做其他逻辑上相关的工作,比如把某个变量设置为 null。