思源笔记折腾记录 - 快速输入

本贴最后更新于 719 天前,其中的信息可能已经时移俗易

一、代码实现

啊 这次这个可能有一定的风险.

siyuan-noob 升级了一下之后,现在可以注册新的菜单类型了.

import { 注册自定义菜单类型 } from "siyuan-noob/customMenu/index.js"; import { getSelectionPosition } from "siyuan-noob/utilFront/range.js"; import { hasClosestBlock, hasClosestByTag, } from "siyuan-noob/utilFront/hasClosest.js"; import { 注册表 } from "siyuan-noob/commonStruct"; import 核心api from "siyuan-noob/utilKernel/kernelApi";

这里注册一个新的菜单类型,就叫行内唤起菜单吧.@style{}

let 旧目标节点 let 行内唤起菜单; 行内唤起菜单 = 注册自定义菜单类型( //决定了菜单的标题 "快速插入", //决定了菜单监听的事件类型有哪些 ["input","beforeinput", "keydown" ,"keypress"], //判定函数,决定了菜单是否显示 (event) => { if (getSelection().rangeCount>0&&getSelection().getRangeAt(0).commonAncestorContainer.textContent) { 行内唤起菜单.菜单状态.当前块元素 = hasClosestBlock( getSelection().getRangeAt(0).commonAncestorContainer ); if (行内唤起菜单.菜单状态.当前块元素) { 行内唤起菜单.菜单状态.当前块id = 行内唤起菜单.菜单状态.当前块元素.getAttribute("data-node-id"); 行内唤起菜单.菜单状态.当前节点 = getSelection().getRangeAt(0).commonAncestorContainer; 行内唤起菜单.菜单状态.rangePosition = getSelectionPosition(event.target); 行内唤起菜单.菜单状态.range = getSelection().getRangeAt(0).cloneRange(); 行内唤起菜单.菜单状态.当前行内元素 = hasClosestByTag( getSelection().getRangeAt(0).commonAncestorContainer, "SPAN" ); } if(event.code&&(event.code==="ArrowLeft"||event.code==="ArrowRight") &&旧目标节点 === 行内唤起菜单.菜单状态.当前节点 &&!window.siyuan.menus.menu.element.querySelector('.b3-menu__submenu') ){ window.siyuan.menus.menu.remove() return } 旧目标节点 = 行内唤起菜单.菜单状态.当前节点 return ( 行内唤起菜单.菜单状态.当前块元素 && 行内唤起菜单.待渲染菜单项目数组 && 行内唤起菜单.待渲染菜单项目数组[0] ); } else { return false; } }, //坐标计算函数,决定了菜单浮现的位置 (event) => { return { x: 行内唤起菜单.菜单状态.rangePosition.left + 18, y: 行内唤起菜单.菜单状态.rangePosition.top + 18, }; }, //监听对象,决定了对哪个元素进行监听 document, //事件配置,决定了事件监听器的options选项 true ); //筛选函数,这个函数决定了单个选项会不会渲染 行内唤起菜单.筛选函数 = (item) => { return item.行内关键词判断函数(行内唤起菜单.菜单状态); }; //这里使得这个菜单的项目可以在外部注册 let 行内唤起菜单注册表 = new 注册表("自定义关键词菜单"); 行内唤起菜单.菜单注册表 = 行内唤起菜单注册表.items; //这里说是其实是通过render函数批量注册了一堆菜单项目

先来一个插入笔记内关键词的看看

let 词表 = []; //这样在其他地方也可以通过同一个名字的注册表来增加关键词 let 关键词注册表 = new 注册表("关键词注册表"); 行内唤起菜单注册表.注册({ id: "快捷关键词输入", 点击回调函数: async (e) => { let 当前块id = 行内唤起菜单.菜单状态.当前块id; }, 行内关键词判断函数: (菜单状态) => { if ( 行内唤起菜单.菜单状态.当前节点 //下面这两行代码的意思是只有输入@@的时候才会显示出那个输入框,我这里先注释掉了,需要的可以加上 //&& //行内唤起菜单.菜单状态.当前节点.textContent.indexOf("@@") >= 0 ) { let textContent = 行内唤起菜单.菜单状态.当前节点.textContent; if (!textContent.trim()) { return; } 词表 = []; //这个函数会返回关键词注册表里面的所有项目 let 已注册关键词表 = 关键词注册表.list(); 已注册关键词表.forEach((注册项) => { let flag; if (注册项.唤起词列表) { //如果唤起词列表里面有的话 注册项.唤起词列表.forEach((唤起词) => { if (唤起词 === textContent.trim()) { flag = true; } if (唤起词.indexOf(textContent.split("@@").pop()) > -1) { flag = true; } }); } //如果id包含的话 if (注册项.id.indexOf(textContent.trim()) > -1) { flag = true; } if (注册项.id.indexOf(textContent.split("@@").pop()) > -1) { flag = true; } //如果内容命中了的话 if (注册项.内容.indexOf(textContent.trim()) > -1) { flag = true; } if (注册项.内容.indexOf(textContent.split("@@").pop()) > -1) { flag = true; } flag ? 词表.push(注册项.内容) : null; }); //默认把当前窗口所有行内元素全都加上去 let list = document.querySelectorAll( ".protyle-wysiwyg div[data-node-id] span:not(.hljs span)" ); list.forEach((el) => { let text = el.innerText.trim(); if ( textContent.trim() !== "" && text && text.indexOf(textContent.split("@@").pop()) > -1 && el.className !== "hljs-string" && text !== textContent.split("@@").pop() ) { 词表.push(text); } }); 词表 = Array.from(new Set(词表)); return textContent.trim().length >= 2 && 词表.length; } }, render: () => { let html = ""; let div = document.createElement("div"); 词表.forEach((spanText) => { html += ` <button style="width: calc(100% - 16px)" class="b3-menu__item"> <span class="b3-menu__label">${Lute.EscapeHTMLStr(spanText.trim())}</span> </button> `; }); 词表 = []; div.innerHTML = html; div.querySelectorAll("button").forEach((button) => { button.addEventListener("click", (e) => { let value = hasClosestByTag(e.target, "BUTTON").querySelector( "span" ).innerText; if (行内唤起菜单.菜单状态.当前节点.textContent.indexOf("@@") > -1) { 行内唤起菜单.菜单状态.当前节点.textContent = 行内唤起菜单.菜单状态.当前节点.textContent.slice( 0, 行内唤起菜单.菜单状态.当前节点.textContent.lastIndexOf("@@") ) + value; } else { 行内唤起菜单.菜单状态.当前节点.textContent = value; } var range = document.createRange(); range.selectNodeContents(行内唤起菜单.菜单状态.当前节点); range.collapse(false); const selection = window.getSelection(); selection.removeAllRanges(); selection.addRange(range); window.siyuan.menus.menu.remove(); }); }); setTimeout(() => { div.querySelectorAll("button").forEach((button) => { window.siyuan.menus.menu.append(button); if ( window.siyuan.menus.menu.element.getBoundingClientRect().bottom >= window.innerHeight ) { window.siyuan.menus.menu.element.style.top = window.siyuan.menus.menu.element.getBoundingClientRect().top - window.siyuan.menus.menu.element.getBoundingClientRect().bottom + window.innerHeight + "px"; } }); div.remove(); }); return div; }, });

然后我们来试试增加一些关键词,先把所有文档以及带有命名和别名的块加进去,限制 1000 个,反正你自己改吧.

//这个你其实可以自己改掉 关键词注册表.注册({ id: "思源笔记", 唤起词: ["siyuan", "sy"], 内容: "思源笔记还可以啊", }); //这里是通过sql将所有带有命名和别名的块以及文档块都弄进去了 核心api.sql( { stmt: `select * from blocks where name or alias or type = 'd' limit 1000`, }, "", (data) => { data.forEach((block) => { let 唤起词 = []; if (block.alias) { 唤起词 = 唤起词.concat(block.alias.split(",")); } if (block.name) { 唤起词.push(block.name); } if (block.title) { 唤起词.push(block.title); } 关键词注册表.注册({ id: block.id, 唤起词: 唤起词, 内容: block.content, }); }); } );

为了测试它的表现,我们再弄一个新的菜单项,这个菜单项的作用就是在你输入 @style 之后,如果再输入一段内容就可以设置当前块的样式

//注册块样式 let styleData; 行内唤起菜单注册表.注册({ id: "快捷块样式", 行内关键词判断函数: (菜单状态) => { if ( 菜单状态.当前节点 && (菜单状态.当前节点.wholeText||菜单状态.当前节点.textContent).indexOf("@style") >-1 ) { let wholeText = 菜单状态.当前节点.wholeText; try { styleData = new Function(`"use strict";return (${wholeText.split("@style").pop()})`)(); return true; } catch (e) { return false; } } }, 文字: "设置为块样式", 图标: "#iconFormat", 点击回调函数: () => { let element =行内唤起菜单.菜单状态.当前节点.parentElement Object.getOwnPropertyNames(styleData).forEach((ruler) => { 行内唤起菜单.菜单状态.当前块元素.style[ruler] = styleData[ruler]; 行内唤起菜单.菜单状态.当前节点.parentElement.innerText=行内唤起菜单.菜单状态.当前节点.wholeText.slice( 0, 行内唤起菜单.菜单状态.当前节点.wholeText.lastIndexOf("@style") ); element.insertAdjacentHTML("beforeend", "<wbr>"); }); var range = document.createRange(); let wbr=element.querySelector('wbr') range.selectNodeContents(element.querySelector('wbr')); range.collapse(false); const selection = window.getSelection(); selection.removeAllRanges(); selection.addRange(range); wbr.remove() window.siyuan.menus.menu.remove(); }, });

比如说 @style{color:'red'}就可以当前块的样式设置成红色字体.

或者这种形式也可以 @style(()={return {color:'red'}})()


安装方式

这还是一个可运行代码片段,可以使用这个代码片段来进行安装,话说有老哥应该已经弄了一个运行代码的插件来着

思源笔记折腾记录 - 运行你的笔记 - 链滴 (ld246.com)

直接剪藏这篇笔记之后,用运行到代码片段功能安装到代码片段就可以了。

使用效果

来我来打个样

快速输入内容


如果这玩意对你有用可以去爱发电给我买杯咖啡

leolee9086 正在创作一些简单的技术教程和小工具,以及设计方面内容 | 爱发电 (afdian.net)

  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    24810 引用 • 102062 回帖
2 操作
leolee 在 2023-04-10 01:44:54 更新了该帖
leolee 在 2023-04-10 00:17:47 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
请输入回帖内容 ...