一、前情提要:
思源的 URL scheme 能够通过块超链接做到唤起思源并打开指定的块,然后还能。。。。。。。。。。
额,好像然后就不能做什么了。
所以这次我们就来尝试一下扩展一下思源的 URL Scheme
二、实现原理
1、ipcRenderer
可以先在代码片段里面测试一下这段代码:
const {ipcRenderer} = require('electron') ipcRenderer.on('siyuan-openurl',(event,msg)=>{ console.log(event,msg) })
随便在什么地方打开一个思源的块超链接,就可以看到在控制台的输出了:
啊,可以看到后面的 msg
参数就是块超链接的内容~~~。
这是为什么呢?
之前我们说过,思源的 main.js
里面是可以直接看到内容的,这里可以看一下第 771 到 799 行:
app.on('open-url', (event, url) => { // for macOS if (url.startsWith('siyuan://')) { siyuanOpenURL = url if (mainWindow && !mainWindow.isDestroyed()) { if (mainWindow.isMinimized()) { mainWindow.restore() } if (!mainWindow.isVisible()) { mainWindow.show() } mainWindow.focus() mainWindow.webContents.send('siyuan-openurl', url) } } }) app.on('second-instance', (event, commandLine) => { if (mainWindow && !mainWindow.isDestroyed()) { if (mainWindow.isMinimized()) { mainWindow.restore() } if (!mainWindow.isVisible()) { mainWindow.show() } mainWindow.focus() mainWindow.webContents.send('siyuan-openurl', commandLine.find((arg) => arg.startsWith('siyuan://'))) } })
这两个地方就是通过进程间通信,把块超链接的内容发送给了思源的主界面。
所以我们可以通过监听这个 siyuan-openurl
信道来获取 url 的内容。
有关 ipcRenderer 之类的参考 electorn 的文档:
ipcRenderer | Electron (electronjs.org)
其实就是用来通信的嘛
2、URL 对象
这个其实就是冷饭了,之前在实现白板的时候不是用了一个解析 URL 参数的对象嘛,这次要使用的就是那个:
思源笔记折腾记录 - 做一个白板 - 保存数据和显示模式 - 链滴 (ld246.com)
之前是这样写的:
export function 获取地址参数(){ let 中间变量 = {} new URL(window.location.href).searchParams.forEach( (value,key)=>{中间变量[key]=value} ) return 中间变量 }
那么很容易就想到,只要把这个函数跟上面的那个结合一下,就可以了:
const {ipcRenderer} = require('electron') ipcRenderer.on('siyuan-openurl',(event,msg)=>{ 执行URL(msg) }) function 执行URL(url){ let url 对象 = new URL(url) console.log(url对象) }
可以看到 url 对象上有这么些属性:
这样我们就可以它进行进一步的操作了。
3、注册动作
首先我们假设,一个类似:
siyuan://<路径>/<下级路径>?<参数>
的 url。
它最开始的路径名就是要执行的动作,下级路径和 url 的 searchParams 都直接传递给对应的回调函数。
这样我们就能开始注册回调了:
const url动作注册表 = [] const { ipcRenderer } = require('electron') ipcRenderer.on('siyuan-openurl', (event, msg) => { 执行URL(msg) }) function 执行URL(url) { let url对象 = new URL(url) let 路径名 = url对象.pathname.replace('//', '').split('/')[0] let 搜索参数 = {} url对象.searchParams.forEach( (value, key) => { 搜索参数[key] = value } ) console.log(路径名, 搜索参数) url动作注册表.forEach( 注册表项 => { console.log(注册表项.动作 === 路径名) if (注册表项.动作 === 路径名) { try { (async () => { 注册表项.回调函数(url对象.pathname.replace('//', ''), JSON.parse(JSON.stringify(搜索参数))) })() } catch (e) { console.warn(e) } } } ) } function 注册url动作(动作, 回调函数) { url动作注册表.push({ 动作: 动作, 回调函数: 回调函数 }) } 注册url动作('blocks', (路径名, 参数) => { console.log('你点击的是',路径名, 参数) })
啊,我就随便注册一个函数试试。。。。。
现在点击一个块超链接,啊不对,现在应该叫超级块超链接,因为它的战斗力已经超过 114514 了,看一下输出就可以看到:
OK,收工。
把它挂载到 noobApi 上方便以后调用:
const url动作注册表 = [] if (window.require) { const { ipcRenderer } = require('electron') ipcRenderer.on('siyuan-openurl', (event, msg) => { 执行URL(msg) }) function 执行URL(url) { let url对象 = new URL(url) let 路径名 = url对象.pathname.replace('//', '').split('/')[0] let 搜索参数 = {} url对象.searchParams.forEach( (value, key) => { 搜索参数[key] = value } ) console.log(路径名, 搜索参数) url动作注册表.forEach( 注册表项 => { console.log(注册表项.动作 === 路径名) if (注册表项.动作 === 路径名) { try { (async () => { 注册表项.回调函数(url对象.pathname.replace('//', ''), JSON.parse(JSON.stringify(搜索参数))) })() } catch (e) { console.warn(e) } } } ) } } export default function 注册url动作(动作, 回调函数) { url动作注册表.push({ 动作: 动作, 回调函数: 回调函数 }) }
4、试一试
注册一个打开当天日记的函数试试:
noobApi.url动作.注册url动作('DailyNote', () => { noobApi.核心api.创建日记({ notebook : "20210808180117-czj9bvb" }) })
效果就像这样
这样这个 issue 就能够自己动手撸啦:
丰富 siyuan://
协议动作,如创建文档 · Issue #6875 · siyuan-note/siyuan (github.com)
所谓折腾就是有 bug 要上,没有 bug,创造 bug 也要上嘛~~~
思考题:issue 里面提到的创建文档咋整?(其实就是换个接口而已。。。。。。)
代码片段的仓库在这里
leolee9086/snippets (github.com)
viteWdigets 的仓库在这里
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于