[js] 连续点击 openAny,小代码,大作用,让一切触手可达

连续点击 openAny,小代码,大作用,让一切触手可达。

缘起

由于思源命令功能较弱,很多系统功能不方便被代码打开,obsidian 这方面较有优势,ob 的命令丰富,可任意调用。

再加上 ob 的插件有 quickadd 及 commander 的宏定义,使其几乎无所不能,任意发挥。

然临渊羡鱼,不如退而结网,突发奇想就开发了这个 openAny 代码片段,意思是打开一切,这意味着思源大部分的功能都可以通过它来打开。

它通过模拟点击,模拟输入和模拟快捷键实现对任意功能的调用,并支持通过 invoke 回调函数来实现那些模拟操作无法实现或较难实现的功能。

功能

支持多个元素的链式点击或文本输入或模拟按键等。

使用场景介绍

这个功能的核心是通过代码模拟用户操作,类似于自动化脚本或宏录制。以往需要手动完成的操作,例如点击按钮、输入文字、调用菜单功能等,现在都可以通过编写代码来实现自动化处理。这种技术可以极大地解放双手,帮助用户高效完成重复性任务。

主要实现方式包括模拟点击、按键输入、触发回调函数等。通过这些方法,您可以轻松实现许多实用功能,例如:

  • 快速打开设置:无需手动导航到复杂的菜单路径。
  • 批量添加样式:为选中的文字快速应用特定样式。
  • 快捷键注册:将隐藏在深层菜单中的功能绑定到快捷键上,大幅提升操作效率。
  • 自定义工具栏:将常用功能直接添加到工具栏或编辑器导航中,方便随时调用。
  • 快速打开外网搜索,翻译,咨询 AI 等: 通过 showOptionsMenu 可以弹出选择项。
  • 快捷输入,文字补全等: 通过 getCharsBeforeCursor 可以获取前导文字用来补全,showInputBox 则可弹出输入框,可以用来快速输入,问 AI 等。

您可以通过编写代码片段(如在 runjs 代码块中)或直接嵌入自己的项目代码中,来注册快捷键或定义自动化操作逻辑。这些代码可以直接在控制台运行,验证其效果。

示例说明

为了更好地理解这些功能的实际应用,您可以参考以下示例代码。将其粘贴到开发者工具控制台中执行,即可观察其效果。

👉 点击查看具体实例代码

通过这种方式,您可以快速利用代码实现自动化操作,并根据实际需求进行扩展和定制。无论是优化工作流还是提升操作效率,都能带来显著的价值!

基本使用

调用方式:

注意:选择符尽量全局唯一,但只要能达到目的且不会产生歧义及副作用即可。

openAny.click('').clicks('','').clicks(['']).press('alt+p').pressByKeymap('config').sleep(100).invoke(({})=>{}).el('').input('').sendText(''); openAny.addKeymap('alt+z', (event)=>{}) // 注册快捷键 openAny.invoke(({sleep, ...args})=>{}) // 通过函数执行某些操作,返回值放在openAny.prev中,通过await openAny.getInvokeReturn()获取;【注意】invoke中使用openAny.xx()前面不能用await,需要await的需用new OpenAny().xx()代替,因为await会与上层链相互等待造成死锁 openAny.el('').selectText('some text', element).press('', element); // 触发选中文本,并等待后续操作,可参考举例说明部分的示例 openAny.press('mouseright'); // 模拟鼠标按右键 openAny.press('mousemiddle'); // 模拟鼠标按中键 openAny.press('mouseover'); // 模拟鼠标悬停 openAny.press('ctrl+mouseleft') // 模拟鼠标ctrl+单击 openAny.on('', (args)=>{}) // 绑定思源事件总线 openAny.off('', handler) // 解绑思源事件总线 openAny.emit('', data) // 触发自定义事件 new OpenAny().click(''); // new 新实例方式调用,推荐 openAny.showMessage().click(''); // 开启出错是发送通知消息,参数true显示消息,false不显示消息,默认true (但未调用此方法时,openAny默认是false) openAny.resetChain().click(''); // 如果某些未知异常导致openAny假死状态时,可以通过resetChain复活,如果是new OpenAny().xxx();方式没有这个问题

注意,默认已经暴露了一个 openAny 的实例,如果 new 新的实例,已经通过 openAny 注册的按键,关联属性等在实例外部不能共享,因为每个实例是独立的。

click 的第二个参数可以传递父元素对象或选中符,这样的话,这个元素的获取就在父元素下获取。

clicks 要想传递父元素,第一个参数必须传数组,然后第二个元素传父元素对象或选择符即可。

press 的第二个参数也可以传一个元素或选择符,作为在哪个元素上按键,默认是 body,编辑器里的有些操作需要作用域编辑器上的,这时只需要传.protyle-wysiwyg 元素即可。

也可以多个分别分开调用,比如:

await openAny.click(''); await openAny.click(''); await openAny.press('');

注意,分开调用时,如果没用 await 中间混入了其他代码可能导致执行顺序混乱,但 openAny 的执行顺序还是调用的顺序,建议混用时使用 await 以保证代码清晰度。

注意,模拟按键的默认是在 document.body 上触发,某些操作如果想作用到编辑器中,可能需要在第二个参数中传递 .protyle-wysiwyg dom 元素。

另外,一些特殊按键可能无法生效,比如 ctrl+v 等。

或用 try...catch 捕获错误,比如:

try { openAny.click('').clicks('',''); } catch(e) { console.log(e); }

注意,这里必须加 await,否则捕获不到异常,只能在控制台打印。

这里有点问题,由于无法区分是否使用 try 情况,所以一般出现错误时都会在控制台打印错误信息,方便调试查看,无论是否 catch 错误,但不影响正常使用;另外,如果开启 showMessage,则也会发送错误通知,无论是否 catch 错误,如果介意通知,可以不使用 showMessage,而在逻辑中自行通知。

或者

openAny.click('').click('').catch((e)=>{console.error("捕获错误:", e);});

说明:这里是单实例实现链式调用是有弊端的,比如,意外未知异常可能导致 openAny 假死,需要显示掉 openAny.resetChain()才能恢复,谨慎起见,也可以每次执行前调用一次 resetChain,但还是建议尽量使用 new OpenAny()

添加到顶栏

如果你想把代码放到顶栏,可以先安装 runjs 插件,然后在任意文档插入 js 代码块,然后输入类似下面的代码

async function main() { // your code here } main(); plugin.saveAction(thisBlock.id, "your code name");

然后,在代码块的右侧选择插件->runjs-> 运行代码即可。

使用下面这个 css 可以让 runjs 顶栏按钮变红,更容易从众多按钮中分辨。

#plugin_sy-run-js_0 svg { color: deeppink; }

也可以先用 openAny.addKeymap 设置为快捷键映射,然后使用工具栏自定义插件实现。

添加到编辑器顶部导航栏

可以通过类似的示例添加按钮到编辑器导航栏

// 比如添加移动按钮到编辑器顶部导航栏 setTimeout(() => { openAny.invoke(({ onProtyleLoad, showMessage }) => { onProtyleLoad(protyle => { // 发布服务下不显示 if (window.siyuan.config.readonly) return; // 创建按钮 if (protyle?.querySelector('.protyle-breadcrumb [data-type="move"]')) return; const exitFocusBtn = protyle.querySelector('.protyle-breadcrumb [data-type="exit-focus"]'); if (!exitFocusBtn) return; const moveHtml = `<button class="block__icon fn__flex-center ariaLabel" aria-label="移动" data-type="move"><svg><use xlink:href="#iconMove"></use></svg></button>`; exitFocusBtn.insertAdjacentHTML('afterend', moveHtml); const moveBtn = protyle.querySelector('.protyle-breadcrumb [data-type="move"]'); if (!moveBtn) return; // 点击事件 moveBtn.addEventListener('click', async () => { // 锁定状态下不可修改 const icon = protyle?.querySelector('button[data-type="readonly"] use')?.getAttributeNS('http://www.w3.org/1999/xlink', 'href'); if (icon === '#iconLock') { showMessage('锁定状态不可用'); return } // 模拟点击移动菜单 openAny.click('.protyle-breadcrumb [data-type="doc"]', protyle).click('#commonMenu[data-name="titleMenu"] button[data-id="move"]'); }); }); }); }, 1000);

说明:

1 这里使用 setTimeout 等待 openAny 加载完毕

2 使用 openAny.invoke 注册回调函数,当然也可以直接写代码,只不过 invoke 回调中会有一些通用函数方便使用

3 使用 invoke 自带的 onProtyleLoad 函数监听编辑器加载事件,在这个事件中把按钮写入到编辑器导航栏

4 最后在按钮事件中,调用 openAny.click 模拟菜单点击事件打开移动操作对话框

5 这里看似很复杂,其实核心代码只有这一句 openAny.click('.protyle-breadcrumb [data-type="doc"]', protyle).click('#commonMenu[data-name="titleMenu"] button[data-id="move"]');,前面的都是创建按钮相关操作。

注意, invoke 的返回值放在 openAny.prev 中,通过 await openAny.getInvokeReturn(); 获取

【注意】 invoke 中使用 openAny.xx()前面不能用 await,需要 await 的需用 new OpenAny().xx()代替,因为 await 会与上层链相互等待造成死锁

invoke 的回调函数 callback 的参数有:

prev, // 上一个调用的元素或返回值 sleep, whenElementExist, showMessage, showErrorMessage, querySql, fetchSyncPost, fetchSyncGet, requestApi, getProtyle, getCurrentDocId, getCurrentNotebookId, newSetStyle, onProtyleLoad, onToolbarShow, getCharsBeforeCursor, showInputBox, showOptionsMenu, selectText,

这些函数也可以通过 openAny.fn.xxx() 或 openAny.functions.xxx()直接调用。

添加到编辑器工具栏

onToolbarShow(callback)

功能简介:

当编辑器工具栏出现时,进行添加按钮等操作。

使用示例:

onToolbarShow((selection, toolbar, protyle) => { console.log(selection, toolbar, protyle); });

注册快捷键

openAny.addKeymap('', ()=>{})

比如

openAny.addKeymap('alt+z', (event)=>{ event.preventDefault(); openAny.press('alt+p') })

然后可以把这些代码放到 js 代码片段中即可。

addKeymap 第三个参数可以传一个 node,代表在哪个元素上监听,默认是在 window 上。

第四个参数是监听事件的选项,默认是 true,即在捕获阶段触发。

注册快捷键还支持按键和鼠标的组合, 鼠标字符有:mouseleftmousemiddlemouserightmouseover,大小写没影响,分别代表鼠标左键,中键和右键。

比如:

openAny.addKeymap('alt+meta+mouseleft', (event)=>{ event.preventDefault(); const target = event.target; if(target.matches('.some-selector')) { // your code here } })

删除注册的快捷键可用用 openAny.removeKeymap('', callback, options);

比如:

openAny.removeKeymap('alt+meta+mouseleft', (event)=>{ event.preventDefault(); const target = event.target; if(target.matches('.some-selector')) { // your code here } })

注意,callback 也必须完全一样才行,与 js 自带的事件删除不一样,这里只要求,callback.toString()完全一致就行,如果有 node 和 options 的,也要加上。

注意,如果删除时不填写 callback,node,options 参数则会把所有同名的快捷键都删除,但只会删除通过 openAny 添加的快捷键。尽量提供完整参数,防止误删除。

另外,如果使用了不同的实例注册的快捷键,也仅删除相同实例下注册的快捷键。

按键字符有哪些? 参见下面的详情 👇

以下是对上述键值对的分类和文字描述,按照类别进行整理:

字母键

包含从 A 到 Z 的所有英文字母键:

  • A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z

数字键(主键盘区)

包含主键盘上的数字键:

  • 0, 1, 2, 3, 4, 5, 6, 7, 8, 9

功能键

包含键盘顶部的功能键:

  • F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12

方向键

用于控制方向的按键:

  • 上箭头 (ArrowUp), 下箭头 (ArrowDown), 左箭头 (ArrowLeft), 右箭头 (ArrowRight)

特殊键

包括常用的特殊功能键:

  • Backspace(退格键), Tab(制表键), Enter(回车键), Shift(左 Shift 键), Control(左 Ctrl 键), Alt(左 Alt 键)
  • CapsLock(大写锁定键), Escape(Esc 键), Space(空格键)
  • PageUp(向上翻页键), PageDown(向下翻页键), End(结束键), Home(首页键)
  • Insert(插入键), Delete(删除键)

数字小键盘

位于键盘右侧的小键盘区域:

  • 数字键:Numpad0, Numpad1, Numpad2, Numpad3, Numpad4, Numpad5, Numpad6, Numpad7, Numpad8, Numpad9
  • 运算符键:NumpadAdd(加号), NumpadSubtract(减号), NumpadMultiply(乘号), NumpadDivide(除号)
  • 小数点键:NumpadDecimal
  • 回车键:NumpadEnter

标点符号键

主键盘上的标点符号键:

  • 分号 (;) - Semicolon
  • 等号 (=) - Equal
  • 逗号 (,) - Comma
  • 减号 (-) - Minus
  • 句号 (.) - Period
  • 斜杠 (/) - Slash
  • 反引号 (`) - Backquote
  • 左方括号 ([) - BracketLeft
  • 反斜杠 () - Backslash
  • 右方括号 (]) - BracketRight
  • 单引号 (') - Quote

鼠标键

mouseleft mousemiddle mouseright mouseover

其他键

一些额外的特殊功能键:

  • ContextMenu(右键菜单键), NumLock(数字锁定键), ScrollLock(滚动锁定键)
  • Pause(暂停键), PrintScreen(打印屏幕键)

扩展函数

除了 openAny 自带的函数外,你也可以向 openAny 扩展自己的函数,这些函数同样会出现在 invoke 和事件回调参数中。

扩展函数通过 addFunction 方法增加,比如:

openAny.addFunction('fnName', ()=>{}); // 或 const myFunction = ()=>{}; openAny.addFunction('myFunction', myFunction);

举例说明

打开设置
openAny.click('#barWorkspace').click('[data-id="config"]');

从命令窗口打开设置
openAny.click('#barCommand').el('[data-key="dialog-commandpanel"] .search__header input').input('设置').click('#commands [data-command="config"]');

调用思源 keymap 打开设置窗口(这里的 config 是 siyuan.config.keymap 中的值)
openAny.pressByKeymap('config');
openAny.pressByFnName('config'); 这里的 pressByFnName 是 pressByKeymap 的别名
openAny.press('keymap.config');openAny.press('fn.config'); 这里有 keymap 和 fn 前缀时会自动调用上面的 pressByKeymap 或 pressByFnName 方法
openAny.press('keymap.general.config'); 全名方式调用,这里的 general 是 siyuan.config.keymap 后面的键,如果是 general 也可以省略,其他则不能省略

打开代码片段窗口
openAny.clicks('#barWorkspace', '[data-id="config"]', '[data-name="appearance"]', '#codeSnippet', '[data-key="dialog-setting"] svg.b3-dialog__close');

alt+z 实现文本标记和加粗
openAny.addKeymap('alt+z', (event)=>{event.preventDefault();openAny.clicks('[data-type="strong"]', '[data-type="mark"]')});

alt+z 实现文本设置颜色和背景色
openAny.addKeymap('alt+z', (event)=>{event.preventDefault();openAny.press('meta+alt+x', document.activeElement).clicks('.protyle-util [style="color:var(--b3-font-color9)"]', '.protyle-util [style="background-color:var(--b3-font-background6)"]').invoke(()=>document.querySelector('.protyle-util:not(.fn__none)').classList.add('fn__none'))});

触发选中文本,并给文本加粗

openAny.selectText('some text', document.activeElement).press('meta+b', document.activeElement);

快速切换预览模式和编辑模式

openAny.addKeymap('alt+meta+0', (event)=>{ event.preventDefault(); const isPreview = document.querySelector('.protyle-preview:not(.fn__none) .protyle-preview__action'); if(!isPreview) openAny.press('alt+meta+9'); else openAny.press('alt+meta+7'); });

转移快捷键(注册快捷键),该功能按 alt+z 触发 alt+p
openAny.addKeymap('alt+z', (event)=>{event.preventDefault();openAny.press('alt+p')})

更多例子请参考

[js] 这个功能可以封神!openAny 教程之快速打开(可自定义的命令面板)

[js] 第二弹,openAny 教程之全局搜索仅搜文档 / 快速灵感输入 / 快速显示隐藏侧边栏等

使用手册

注意,以下 👇 文档由 AI 生成仅供参考!

# OpenAny 使用帮助文档

OpenAny 是一个用于模拟用户操作(如点击、输入、快捷键等)的工具类。它支持链式调用,能够实现复杂的自动化操作,同时提供了错误处理和快捷键注册功能。以下是详细的使用说明。


1. 初始化与基本调用

初始化实例

推荐通过 new OpenAny() 创建新实例:

const openAny = new OpenAny();

也可以直接使用全局实例:

openAny.click('#selector');

链式调用

所有方法均支持链式调用,例如:

openAny.click('#selector1') .click('#selector2') .input('Hello World', '#inputSelector') .press('Enter');

2. 核心方法

2.1 模拟点击

方法:click(selector, parentElement)

  • 参数
    • selector:CSS 选择器或 DOM 元素。
    • parentElement(可选):父级元素范围。
  • 示例
openAny.click('#button'); // 点击指定按钮

方法:clicks(...selectors)

  • 支持批量点击多个元素。
  • 参数
    • selectors:选择器数组或多个字符串参数。
  • 示例
openAny.clicks('#button1', '#button2', '#button3');

2.2 输入与文本操作

方法:input(text, selector)

  • 在指定输入框中输入文本。
  • 参数
    • text:要输入的文本。
    • selector:目标输入框的选择器。
  • 示例
openAny.input('Hello World', '#inputField');

方法:sendText(text, selector)

  • 向可编辑区域发送文本。
  • 示例
openAny.sendText('Hello World', '.editableArea');

方法:clear(selector)

  • 清空指定输入框的内容。
  • 示例
openAny.clear('#inputField');

2.3 快捷键模拟

方法:press(keys, element)

  • 模拟按键操作。
  • 参数
    • keys:按键组合,如 'Ctrl+A''Meta+Z'
    • element(可选):目标元素。
  • 示例
openAny.press('Ctrl+S'); // 保存操作

具体键盘名可参考 [js] 连续点击 openAny,小代码,大作用,让一切触手可达

另外,鼠标键有 mouseleft, mouseclick,mousemiddle,mouseright,mouseover。

方法:pressByKeymap(fnName)

  • 根据思源笔记的快捷键配置触发操作。
  • 参数
    • fnName:快捷键名称。
  • 示例
openAny.pressByKeymap('general.config'); // 打开设置窗口

2.4 延迟与等待

方法:sleep(delay)

  • 添加延迟。
  • 参数
    • delay:延迟时间(毫秒)。
  • 示例
openAny.sleep(1000); // 延迟1秒

方法:el(selector)

  • 等待指定元素加载完成。
  • 参数
    • selector:目标元素的选择器。
  • 示例
openAny.el('#dynamicElement').sendText('someText');

2.5 自定义逻辑

方法:invoke(callback)

  • 执行自定义逻辑。
  • 参数
    • callback:回调函数,接收上下文参数。
    • callback 的参数有:
    • prev, // 上一个调用的元素或返回值
      sleep,
      whenElementExist,
      showMessage,
      showErrorMessage,
      querySql,
      fetchSyncPost,
      fetchSyncGet,
      requestApi,
      getProtyle,
      getCurrentDocId,
      getCurrentNotebookId,
      newSetStyle,
      onProtyleLoad,
      onToolbarShow,
      getCharsBeforeCursor,
      showInputBox,
      showOptionsMenu,
      selectText,
  • 示例
openAny.invoke({}) => { console.log('Custom logic executed.'); });

这些函数也可以通过 openAny.fn.xxx() 或 openAny.functions.xxx()直接调用。

【注意】 invoke 中使用 openAny.xx()前面不能用 await,需要 await 的需用 new OpenAny().xx()代替,因为 await 会与上层链相互等待造成死锁


2.6 快捷键注册

方法:addKeymap(keys, callback, node, options)

  • 注册全局快捷键。这些代码放到代码片段中即可。
  • 参数
    • keys:快捷键组合,如 'Alt+Z'
    • callback:触发时执行的回调函数。
    • node 在哪个元素上绑定
    • options addEventListener 的选项
  • 示例
openAny.addKeymap('Alt+Z', (event) => { event.preventDefault(); openAny.click('#button'); });

2.7 快捷键删除

方法:removeKeymap(keys, callback)

  • 注册全局快捷键。这些代码放到代码片段中即可。

  • 参数

    • keys:快捷键组合,如 'Alt+Z'
    • callback:触发时执行的回调函数。
    • node 在哪个元素上绑定
    • options addEventListener 的选项
  • 示例

openAny.addKeymap('Alt+Z', (event) => { event.preventDefault(); openAny.click('#button'); });

注意,callback 也必须完全一样才行,与 js 自带的事件删除不一样,这里只要求,callback.toString()完全一致就行,如果有 node 和 options 的,也要加上。

注意,如果删除时不填写 callback,node,options 参数则会把所有同名的快捷键都删除,但只会删除通过 openAny 添加的快捷键。尽量提供完整参数,防止误删除。

另外,如果使用了不同的实例注册的快捷键,也仅删除相同实例下注册的快捷键。


3. 错误处理

错误捕获

建议使用 try...catch 捕获异常:

try { await openAny.click('#valid1').click('#invalid'); } catch (e) { console.error("捕获错误:", e); }

显示错误消息

启用错误消息通知:

openAny.showMessage().click('#invalid');

参数 true 显示消息,false 不显示消息,默认 true (但未调用此方法时,openAny 默认是 false)


4. 高级用法

4.1 多步骤操作

将多个操作组合在一起:

await openAny .el('#inputField') .input('Hello World') .press('Enter') .click('#submitButton');

4.2 invoke 函数

通过函数处理那些模拟点击无法实现的操作:

openAny.invoke({}) => { console.log('Custom logic executed.'); });

4.3 快捷键转移

将一个快捷键映射到另一个操作:

openAny.addKeymap('Alt+X', (event) => { event.preventDefault(); openAny.press('Ctrl+S'); });

5. 注意事项

  1. 选择器唯一性:选择符不一定要全局唯一,但需确保不会产生歧义或副作用。
  2. 异步操作:链式调用时无需手动处理异步,OpenAny 内部已封装 Promise。
  3. 分开调用:如果需要分开调用方法,切勿混合其他代码,除非你能清晰知道执行顺序。
  4. 异常恢复:若出现未知异常导致假死状态,可通过 resetChain() 恢复。

6. 实例说明

1: 如何打开设置窗口?

openAny.click('#barWorkspace').click('[data-id="config"]');

2: 如何快速切换预览模式和编辑模式?

openAny.addKeymap('Alt+Meta+0', (event) => { event.preventDefault(); const isPreview = document.querySelector('.protyle-preview:not(.fn__none) .protyle-preview__action'); if (!isPreview) openAny.press('Alt+Meta+9'); else openAny.press('Alt+Meta+7'); });

3: 如何给文字添加颜色和背景色?

openAny.addKeymap('Alt+Z', (event) => { event.preventDefault(); openAny.press('Meta+Alt+X', document.activeElement) .clicks('.protyle-util [style="color:var(--b3-font-color9)"]', '.protyle-util [style="background-color:var(--b3-font-background6)"]') .invoke(() => document.querySelector('.protyle-util:not(.fn__none)').classList.add('fn__none')); });

新增函数

这些函数都可以用在 openAny.invoke 函数回调中。

以下是对您提供的几个函数的使用教程,包含每个函数的功能描述、使用场景以及代码示例。


1. showOptionsMenu 弹出选项菜单

功能描述:

showOptionsMenu 是一个异步函数,用于弹出一个选项菜单供用户选择。它接收一个选项数组,并返回用户选择的值。如果用户取消选择,则返回 null

参数

showOptionsMenu(options, title = '')

使用场景:

  • 用户需要从多个选项中选择一个(例如:跳转到不同的搜索引擎或 AI 工具)。
  • 需要动态生成选项列表并获取用户的选择结果。

使用示例:

async function demo() { // 定义选项列表 const options = [ { label: 'Google 搜索', value: 'https://www.google.com' }, { label: 'Bing 搜索', value: 'https://www.bing.com' }, { label: '百度搜索', value: 'https://www.baidu.com' } ]; // 调用 showOptionsMenu 显示选项菜单 const selectedValue = await showOptionsMenu(options); if (selectedValue === null) { console.log('用户取消了选择'); } else { console.log(`用户选择了:${selectedValue}`); window.location.href = selectedValue; // 跳转到用户选择的搜索引擎 } }

2. showInputBox 弹出输入框

功能描述:

showInputBox 是一个异步函数,用于弹出一个输入框,允许用户输入内容。如果用户点击确认按钮,则返回输入的内容;如果用户取消输入,则返回 null

参数

showInputBox(defaultText = '', title = '', placeholder = '');

使用场景:

  • 获取用户的自定义输入(例如:提交表单、设置标题等)。
  • 需要动态处理用户输入内容。

使用示例:

async function inputDemo() { // 弹出输入框,默认内容为 "默认内容" const result = await showInputBox('请输入您的名字'); if (result !== null) { console.log('用户输入:', result); alert(`你好, ${result}! 欢迎使用本系统。`); } else { console.log('用户取消了输入'); } }

3. onToolbarShow 监听工具栏显示事件

功能描述:

onToolbarShow 是一个回调函数,当工具栏显示时触发。它接收三个参数:

  • selection:当前选中的内容。
  • toolbar:工具栏对象。
  • protyle:与编辑器相关的上下文信息。

使用场景:

  • 动态调整工具栏的行为或样式。
  • 根据用户选中的内容,动态更新工具栏的选项。

使用示例:

// 监听工具栏显示事件 onToolbarShow((selection, toolbar, protyle) => { console.log('当前选中内容:', selection); console.log('工具栏对象:', toolbar); console.log('编辑器上下文:', protyle); // 示例:打印选中文本 if (selection && selection.length > 0) { console.log('选中文本是:'+selection); } });

4. getCharsBeforeCursor 获取光标前的字符

功能描述:

getCharsBeforeCursor 是一个同步函数,用于获取光标前指定数量的字符。它接收一个参数 count,表示需要获取的字符数。

使用场景:

  • 文字补全功能(例如:根据光标前的字符预测下一个单词)。
  • 分析用户当前输入上下文。

使用示例:

function autoCompleteExample() { // 获取光标前 5 个字符 const charsBeforeCursor = getCharsBeforeCursor(5); console.log('光标前的字符:', charsBeforeCursor); // 示例:简单文字补全逻辑 if (charsBeforeCursor.endsWith('hel')) { console.log('建议补全为: hello'); } else if (charsBeforeCursor.endsWith('wor')) { console.log('建议补全为: world'); } }

5. newSetStyle 动态设置样式

功能描述:

newSetStyle 是一个函数,用于创建一个动态样式设置器。调用该函数后,会返回一个新的函数(例如:setStyle),该函数可以接收 CSS 样式字符串作为参数,并将其应用到目标元素或全局样式中。

使用场景:

  • 动态调整页面样式(例如:根据用户偏好更改主题颜色)。
  • 实现运行时的样式定制(例如:在特定条件下修改菜单、按钮或其他组件的样式)。
  • 开发可复用的样式管理工具。

使用教程

1. 初始化 newSetStyle

首先,调用 newSetStyle 创建一个样式设置器实例(如 setStyle)。这个实例可以用来动态注入 CSS 样式。

// 初始化样式设置器 const setStyle = newSetStyle();

2. 应用样式

通过 setStyle 函数,您可以传入一段 CSS 样式字符串,这些样式会被动态注入到页面中。

示例 1:设置全局样式
// 定义新的全局样式 setStyle(` body { background-color: #f8f9fa; font-family: Arial, sans-serif; color: #333; } button { background-color: #007bff; color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; } button:hover { background-color: #0056b3; } `); console.log('全局样式已更新');
示例 2:设置特定组件样式
// 定义菜单的样式 setStyle(` .custom-menu { background-color: #ffffff; border: 1px solid #ddd; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); border-radius: 8px; padding: 10px; } .custom-menu-item { padding: 8px 16px; cursor: pointer; } .custom-menu-item:hover { background-color: #f0f0f0; } `); console.log('菜单样式已更新');

3. 动态切换样式

您可以多次调用 setStyle 来动态切换样式,从而实现主题切换或条件样式调整。

示例:根据用户选择切换主题
function switchTheme(theme) { if (theme === 'dark') { setStyle(` body { background-color: #121212; color: #e0e0e0; } button { background-color: #bb86fc; color: #121212; } `); console.log('切换到深色主题'); } else { setStyle(` body { background-color: #ffffff; color: #333; } button { background-color: #007bff; color: white; } `); console.log('切换到浅色主题'); } } // 调用示例 switchTheme('dark'); // 切换到深色主题 setTimeout(() => switchTheme('light'), 3000); // 3秒后切换到浅色主题

4. 清除样式

如果需要清除之前注入的样式,可以在实现 newSetStyle 的底层逻辑时添加清除功能(假设有 clearStyles 方法)。

示例:清除所有动态样式
// 假设 newSetStyle 返回的对象包含 clearStyles 方法 setStyle.clearStyles(); console.log('所有动态样式已清除');

总结

newSetStyle 是一个强大的工具,允许开发者动态地注入和管理 CSS 样式。通过它,您可以轻松实现以下功能:

  1. 动态样式调整:根据用户交互或系统状态实时更新样式。
  2. 主题切换:支持深色模式、浅色模式等多主题切换。
  3. 组件样式隔离:为特定组件或模块定义独立的样式规则。

6. selectText 触发选中文本

功能描述:

selectText 可以触发选中编辑器文本,配合按钮操作,可以对编辑器进行一定的编辑操作。

使用场景:

  • 选中文本添加样式等。
  • 选中文本搜索等。

使用示例:

openAny.selectText('some text', document.activeElement).press('meta+b', document.activeElement);

7. openFile 打开本地文件

功能描述:

openFile 可以打开本地文件,支持各种类型,用默认程序打开。

使用场景:

  • 打开本地文件

使用示例:

openAny.openFile('/yourpath/yourfile.txt');

8. showFileInFolder 定位文件或文件夹的

功能描述:

showFileInFolder 在系统资源管理器中定位文件或文件夹的位置。

使用场景:

  • 定位文件或文件夹的位置

使用示例:

openAny.showFileInFolder('/yourpath/yourfile.txt');

9. runCmd 运行本地命令

功能描述:

runCmd 可以执行本地命令,并等待返回结果。

使用场景:

  • 与本地脚本交互

使用示例:

// mac openAny.runCmd('open -a /path/some.app'); //运行指定app //windows openAny.runCmd('start calc'); // 打开计算器

也可以把这些函数与其他函数相结合实现与本地文件或应用的交互。

代码

https://gitee.com/wish163/mysoft/blob/main/%E6%80%9D%E6%BA%90/%E6%A8%A1%E6%8B%9F%E8%BF%9E%E7%BB%AD%E7%82%B9%E5%87%BBopenAny.js

代码放到哪里

这个是针对小白用户的说明,老用户可忽略。

  1. 设置 -> 外观 -> 代码片段 -> JS 中添加新的 js 代码片段即可。
  2. 输入帖子中的代码到你新建的代码片段中
  3. 开启代码片段即可(如果有问题重启思源试试)
    image.png

分享你的代码

可把你的自定义代码分享到评论区。

openAny,不只是打开,一切皆有可能!

打赏作者

打赏用户超过 20 考虑开发类似 obsidian quickAdd 那样的宏操作界面

image.png

鸣谢

❤️ 感谢最近以来各位大佬们的打赏!

虽然不能一一感谢!但都铭记在心!再次感谢!🙏

  • 思源笔记

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

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

    25453 引用 • 105286 回帖
  • 代码片段

    代码片段分为 CSS 与 JS 两种代码,添加在 [设置 - 外观 - 代码片段] 中,这些代码会在思源笔记加载时自动执行,用于改善笔记的样式或功能。

    用户在该标签下分享代码片段时需在帖子标题前添加 [css] [js] 用于区分代码片段类型。

    168 引用 • 1137 回帖 • 2 关注

相关帖子

欢迎来到这里!

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

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

    不明觉厉 😄 👍

    1 回复
  • wilsons

    哈哈,主要是通过一些模拟点击,按键,输入或 invoke 回调函数来实现你想要的功能。

    比如,打开设置,通过注册快捷键化给文字添加样式,注册快捷键打开那些隐藏在菜单深处的功能等,也可以把这些功能添加到工具栏,编辑器导航等处。

    注册快捷键的代码可以放到代码片段中,或按需放置,比如放到 runjs 代码块中或自己写的代码中等。

    举例说明 实例代码放到控制台执行下就知道它的效果了。

    2 回复
  • queguaiya 1 1 评论

    刚刚试了下,正好 QYL 主题新更新一个把链接转化为按钮样式的功能,结合这一串可以一键转化,可以预见这个功能的可玩性会非常高,但是对于真正的小白来说门槛还是有点太高了

    openAny.setKeymap('alt+z', (event) => { event.preventDefault(); openAny.click('[data-type="text"]').click('[data-type="backgroundColor"][style="background-color:var(--b3-font-background13)"]'); });
    确实,不懂编程的看起来可能会有点懵
    wilsons
  • queguaiya 1

    顺便问问 w 佬,我以后有机会的话可以把这个 js 用于 QYL 主题吗,感谢潜力很大

    1 回复
  • wilsons 3 评论

    可以,不仅仅其他主题,浏览器,obsidian,logseq 等只要支持 js 的软件当中都可以。

    哈哈我的意思是想拿个授权
    queguaiya 1
    @queguaiya 哦哦 可以的
    wilsons
    @queguaiya ❤️ 感谢大佬打赏!
    wilsons
  • carethink via macOS

    小白不明觉厉,大佬已经喂到嘴边,还是吃不上,哈哈。期待更多大佬入局开发~

    1 回复
  • wilsons

    哈哈,其实就是有点类似自动化点击。之前需要用手动操作的,现在都可以通过代码模拟操作。类似与于宏,或有些软件叫录制。主要可以解放双手,自动完成一些工作。

  • wilsons

    更新至 0.0.5 版

    新版增加 toolbar 出现事件;改进事件传递机制,默认捕获阶段触发;增加鼠标监听和模拟事件(包括鼠标左中右按键,鼠标 + 按键,甚至 mouseover 事件等,从此貌似任何手动操作的功能都可以代码执行了);增加与本地文件的交互等;setKeymap 更改为 addKeymap;新增 removeKeymap; 增加输入框,选项菜单,弹出窗三大常用 UI 等。

    1 操作
    wilsons 在 2025-04-25 06:56:22 更新了该回帖
  • wilsons

    保证 openAny 被加载的办法:

    1 最简单

    setTimeout(async ()=>{ // your code here }, 2000);

    2 waitFor,及时性更好

    function waitFor(conditionFn, timeoutMs=5000) { return new Promise((resolve, reject) => { const start = Date.now(); const check = () => { if(typeof conditionFn === 'string') conditionFn = () => document.querySelector(conditionFn); const result = conditionFn(); if (result) resolve(result); else if (Date.now() - start > timeoutMs) reject(); else requestAnimationFrame(check); // 利用浏览器刷新周期 }; check(); }); } await waitfor(()=>typeof openAny !== 'undefined'); // your code here

    3 timeout,不在乎及时性的情况下

    // 等待oopenAny加载完毕后运行 (function run(count=0) { setTimeout(()=>{ if(typeof openAny === 'undefined' && count < 5) return run(count+1); if(typeof openAny === 'undefined') {console.error('openAny undefined');return;} openAny.addKeymap('', ()=> { // your code here }); }, 1000); })();
    1. 及时性一般
    for(let i=0;i<5;i++){ if(typeof openAny !== 'undefined') break; const delay = Math.floor(1000/(i+1)); await new Promise(resolve => setTimeout(resolve, delay)); } if(typeof openAny === 'undefined') { console.error('openAny加载失败'); return; } // your code here
    3 操作
    wilsons 在 2025-05-12 02:21:34 更新了该回帖
    wilsons 在 2025-04-25 11:48:31 更新了该回帖
    wilsons 在 2025-04-25 11:47:29 更新了该回帖
  • wilsons

    0.0.6.3 增加绑定思源事件总线方法。

    在 openAny 中调用方法示例:

    const handler = (args)=>console.log(args); openAny.on('open-menu-link', handler); openAny.once('open-menu-link', handler); openAny.off('open-menu-link', handler); openAny.emit('your event name', data);
请输入回帖内容 ...