Skip to content

Latest commit

 

History

History
75 lines (47 loc) · 6.37 KB

File metadata and controls

75 lines (47 loc) · 6.37 KB

发送至 MyClaudian

目标与范围

把某一条 memo 连同一句自定义指令交给 MyClaudian 插件处理,入口是 memo 右键菜单的「发送至 MyClaudian」。Spark Memo 自己不再维护任何对话界面,只负责组织内容并交接。

包含:

  • 右键菜单入口的显示条件
  • 发送弹窗(memo 预览、输入框、一键建议、发送按钮)
  • 输入框里的 @ 引用(笔记与文件夹)
  • 交给 MyClaudian 的消息正文格式

不包含:

  • 对话界面本身(全部由 MyClaudian 承担)
  • 会话持久化、模型选择、权限控制(同上)
  • memo 里的图片(当前只发送文本,图片不随消息走)
  • 手机端支持(MyClaudian 是桌面端插件)

用户可感知行为

  • 功能默认关闭,且设置面板里没有开关。想启用只能改 src/section.tsDEFAULT_SETTINGS.sendToMyClaudianEnabled,重新构建。这是有意为之:它只在本机同时装了 MyClaudian 时才有意义,不该出现在一个陌生用户的设置页里。
  • 三个条件同时满足,右键菜单里才出现「发送至 MyClaudian」:开关已打开、当前是桌面端、本机 MyClaudian 已启用且版本支持 sendFromPlugin。任何一条不满足,菜单项直接不出现,而不是出现后点了没反应。
  • 点击后弹出发送弹窗,从上到下依次是:memo 只读预览(带日期时间)、输入框、一键建议、发送按钮。
  • 输入框留空也能直接发。回车发送,Shift 加回车换行。
  • 输入框里打 @ 会弹出候选列表,跟着继续输入实时过滤。候选包含 vault 里的笔记和文件夹,排序依次是:前缀匹配的在前、当前打开着的笔记在前、笔记排在文件夹之前、最近修改的在前,最后按路径排。查询为空时这套规则等于「最近改过的笔记排最前」。
  • 上下键移动,回车或 Tab 选中,Esc 关闭,也可以直接点。选中后插入的是 @笔记路径.md ,文件夹是 @文件夹路径/ ,与 MyClaudian 输入框里的写法一致。
  • 候选框打开时回车用于选中,关闭之后回车才是发送。
  • @ 过、并且发送时仍留在文本里的笔记,会连同 memo 所在的日记一起交给 MyClaudian 记录,所以这次对话会出现在这些笔记的「与此相关」下面。
  • 一键建议共三条,点一下立即发送,不经过输入框。用户一旦在输入框里打字,建议区隐藏;清空后恢复。
  • 一键建议固定用中文,不跟随 Obsidian 界面语言。memo 是用中文写的,回复也希望是中文,跟界面语言无关。弹窗其余文案仍跟随界面语言,所以英文界面下这里是中英混排。
  • 发送成功后弹窗关闭,MyClaudian 的侧边栏被唤到前台,内容落在一个新开的会话里并已自动发出。

实现要点

与 MyClaudian 的接口

MyClaudian 在主类上公开了 sendFromPlugin({ text, startNew, notePaths }),Spark Memo 通过 app.plugins.plugins['myclaudian'] 拿到实例后直接调用。notePaths 是这条消息牵涉到的笔记,MyClaudian 拿它做引用记录,也就是「与此相关」的来源。正文里出现的路径字符串对接收端来说只是文本,认不出是引用,所以必须单独传。

src/myclaudian-bridge.ts 负责识别:插件 id 必须是 myclaudian,且 sendFromPlugin 必须是函数。检查方法而不是只认 id,是因为旧版 MyClaudian 装着也调不通,这时菜单项应该跟没装一样不出现。

发送时机上还有一次重复检查:弹窗打开期间用户可能把 MyClaudian 禁用掉,所以点发送时会重新解析一次实例。

一键建议的写法

memo 是用户已经想完的一个念头,所以三条建议没有一条是「解释这条在说什么」,被告知自己刚写了什么没有价值。三条都朝外推:往下推演、反过来追问、去 vault 里找呼应。

按钮上显示的文案和实际发出去的提示词不是同一句。按钮要短才好看,而短指令在模型看来很容易被当成「总结一下」,所以每条提示词都多带一句限定,比如「不用复述我写的,直接往外推」。这句限定是有用的,不是修辞。

消息正文格式

composeMessage() 拼装,结构是:用户指令、空行、来源行、memo 正文逐行加 > 引号。

引号而不是 XML 标签,是为了让模型能区分「用户的指令」和「被讨论的文本」,同时不引入模型可能试图回答的标签结构。

@ 引用

src/mention-suggest.ts 是 MyClaudian 那套 MentionDropdownController 的本地精简复刻。两个插件各自打包,没有可以共用的模块,只能重写一份,并去掉 Spark Memo 用不上的 MCP 服务器、agent、外部 context 目录三类候选,只留笔记和文件夹。

排序规则刻意与 MyClaudian 逐层对齐:同样的按键在两个输入框里应该浮起同一篇笔记,否则用户得记两套手感。文件夹没有自己的修改时间,取它内部最新那篇笔记的时间,否则文件夹会因为「从未修改」永远沉在末尾,等于没提供。

真正让模型读到内容的是插入的那段 @路径 文本,接收端本来就认识这种写法,所以发送接口不必为此改动。

按键监听挂在 document 的捕获阶段,不是挂在输入框上。同一个元素上的监听按注册顺序触发,捕获标记在这里不起作用,挂在输入框上就得靠构造顺序去抢在「回车发送」之前,太脆。

候选框挂在 body 上用固定定位,否则会被弹窗边界裁掉。定位时先定死高度上限再测量,最后才算位置,并在浏览器完成本帧布局之后再定位一次:输入框下方的一键建议会在第一次输入时隐藏,垂直居中的弹窗随之变矮,输入框的位置也就变了。

设置项

只有一个 sendToMyClaudianEnabled: boolean,默认 false,不在设置面板渲染。

历史

0.2.x 期间这里曾经是一套自带的对话功能(chat-pane.ts / chat-runtime.ts / chat-store.ts),自己 spawn 本机 claude 命令行跑 headless 对话,会话存在 vault 的 .spark-memo/chats/ 下。这套实现已整体移除,改为交接给 MyClaudian,理由是同一台机器上没必要维护两套对话界面。

移除时不会清理用户 vault 里已有的 .spark-memo/chats/ 目录,需要用户自行删除。