@plumeai/plugin-sdk 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -168,8 +168,6 @@ export declare const en: {
168
168
  "sessionCard.input": string;
169
169
  "sessionCard.output": string;
170
170
  "sessionCard.partMain": string;
171
- "sessionCard.partSide": string;
172
- "sessionCard.partSubagents": string;
173
171
  "sessionCard.started": string;
174
172
  "sessionCard.readCache": string;
175
173
  "reorder.saveFailed": string;
@@ -857,7 +855,8 @@ export declare const en: {
857
855
  "questionNav.pick": string;
858
856
  "questionNav.goTo": string;
859
857
  "questionNav.imageMessage": string;
860
- "userMessage.fromSideChat": string;
858
+ "userMessage.fromChild": string;
859
+ "userMessage.fromPlugin": string;
861
860
  "userMessage.showMore": string;
862
861
  "userMessage.showLess": string;
863
862
  "conversation.quoteToComposer": string;
@@ -145,8 +145,6 @@ export declare const zhCN: {
145
145
  readonly "sessionCard.input": "输入";
146
146
  readonly "sessionCard.output": "输出";
147
147
  readonly "sessionCard.partMain": "主会话";
148
- readonly "sessionCard.partSide": "侧边聊天";
149
- readonly "sessionCard.partSubagents": "子智能体";
150
148
  readonly "sessionCard.started": "{at} 开始";
151
149
  readonly "sessionCard.readCache": "读缓存 {n}";
152
150
  readonly "reorder.saveFailed": "排序未能保存:{reason}";
@@ -753,7 +751,8 @@ export declare const zhCN: {
753
751
  readonly "questionNav.goTo": "跳转到第 {n} 个问题:{text}";
754
752
  readonly "questionNav.imageMessage": "图片消息";
755
753
  readonly "userMessage.showMore": "展开全文";
756
- readonly "userMessage.fromSideChat": "来自侧边聊天";
754
+ readonly "userMessage.fromChild": "来自子会话";
755
+ readonly "userMessage.fromPlugin": "来自{name}";
757
756
  readonly "userMessage.showLess": "收起";
758
757
  readonly "conversation.quoteToComposer": "引用到输入框";
759
758
  readonly "composer.quotesTitle": "对话引用";
@@ -187,8 +187,8 @@ export interface ParentStatus {
187
187
  export interface ParentSession {
188
188
  id: string;
189
189
  /**
190
- * 把一件事排进父会话的队列(和人在输入框里排的同一条,落盘),父会话真闲着就马上开始。发出去的消息标着来自侧边聊天
191
- * (`UserMessage.origin`),前面带一句给模型的来处说明。`started` 是排上那一刻它就开始了。指令为空时抛错。
190
+ * 把一件事排进父会话的队列(和人在输入框里排的同一条,落盘),父会话真闲着就马上开始。发出去的消息标着来自哪个插件
191
+ * (`UserMessage.origin` 是 `child`,`fromPlugin` 是建这个子会话的插件),前面带一句给模型的来处说明。`started` 是排上那一刻它就开始了。指令为空时抛错。
192
192
  */
193
193
  enqueue(instruction: string): Promise<{
194
194
  started: boolean;
@@ -274,6 +274,16 @@ export interface PromptSection {
274
274
  */
275
275
  export interface ModelContext {
276
276
  sessionId: string;
277
+ /**
278
+ * 这个会话挂在哪个会话下面,和 `SessionSummary.parentId` 同一件事:只有插件建的子会话(侧边聊天、子 Agent)有,
279
+ * 顶层会话不带这个键。按会话藏工具、改提示词时看它,不用为此去 `ctx.sessions.list()`。
280
+ */
281
+ parentId?: string;
282
+ /**
283
+ * 建这个会话的插件,和 `SessionSummary.owner` 同一件事:插件的子会话和独立会话(`SessionAccess.create` 不给 `parentId`)有,
284
+ * 侧栏里的会话不带这个键。有 `owner` 没有 `parentId` 的是插件的独立会话。
285
+ */
286
+ owner?: string;
277
287
  cwd: string;
278
288
  /** 当前项目的源文件夹;只授予读取,工作树隔离后为空。 */
279
289
  projectRoots?: readonly string[];
@@ -313,10 +313,10 @@ export interface QueuedMessage {
313
313
  preview: string;
314
314
  /** 轮到它时整份发出去的东西。 */
315
315
  message: OutgoingMessage;
316
- /** 不是人在这个会话的输入框里排的:`side-chat` 是侧边聊天交过来的任务,发出去的消息带着同样的来处(`UserMessage.origin`)。 */
317
- origin?: "side-chat";
318
- /** 侧边聊天交过来的:是哪一个子会话交的,它的回执条(`HandedTask`)据此列出这一条。 */
319
- sideId?: string;
316
+ /** 不是人在这个会话的输入框里排的:`child` 是它的一个子会话(比如侧边聊天)交过来的任务,发出去的消息带着同样的来处(`UserMessage.origin`)。 */
317
+ origin?: "child";
318
+ /** 子会话交过来的:是哪一个子会话交的,它的回执条(`HandedTask`)据此列出这一条。 */
319
+ childId?: string;
320
320
  createdAt: number;
321
321
  }
322
322
  /**
@@ -681,7 +681,7 @@ export interface AgentService {
681
681
  moveQueued(sessionId: string, id: string, targetId: string, placement: "before" | "after"): Promise<void>;
682
682
  /**
683
683
  * 人在子会话(侧边聊天)的卡片上点了「交给主会话」:把这件事排进 `sessionId` 这个子会话的父会话,和插件的
684
- * `ParentSession.enqueue` 同一条路(标着来自侧边聊天)。只认父会话,不是子会话时报错。
684
+ * `ParentSession.enqueue` 同一条路(标着来自建这个子会话的插件)。只认父会话,不是子会话时报错。
685
685
  */
686
686
  queueTask(sessionId: string, instruction: string): Promise<void>;
687
687
  /**
@@ -35,7 +35,7 @@ export interface ToolCardProps {
35
35
  /** 发送到卡片所属会话。只在真实点击/提交,或聚焦的卡片 iframe 发来的用户操作消息中同步调用;只读视图没有此入口。displayText 可提供气泡中显示的简短说明,text 仍是完整模型输入。 */
36
36
  send?: (text: string, event: import("react").SyntheticEvent | MessageEvent, displayText?: string) => Promise<void>;
37
37
  /**
38
- * 卡片画在插件的子会话(侧边聊天)里时才有:把一件事排进它父会话的队列,标着来自侧边聊天,和
38
+ * 卡片画在插件的子会话(侧边聊天)里时才有:把一件事排进它父会话的队列,标着来自哪个插件,和
39
39
  * `ToolRun.parent.enqueue` 同一条路。只在人点了卡片上写明要交出去什么的按钮时调(「交给主会话」),不拿来替人做决定。
40
40
  */
41
41
  handToParent?(instruction: string): Promise<void>;
@@ -824,6 +824,13 @@ export interface UiContext {
824
824
  page(id: string, component: ComponentType<PageProps>, options: PageOptions): void;
825
825
  /** 换皮肤:可重复调用更新,null 撤下自己的皮肤;最后注册的生效,撤下后前一个恢复。 */
826
826
  skin(skin: Skin | null): void;
827
+ /** 应用的主题偏好,就是设置 › 外观那三张主题卡片(跟随系统、浅色、深色);在组件里用,偏好变了跟着重画。 */
828
+ useTheme(): "system" | "light" | "dark";
829
+ /**
830
+ * 改主题偏好,等同人在外观页点那张卡片:存盘、各窗口同步。有皮肤接管深浅模式时外观页的主题只读,这里同样不改;
831
+ * 要改先 `skin(null)` 撤下自己的皮肤。只在人点了插件界面上的按钮时调。
832
+ */
833
+ setTheme(theme: "system" | "light" | "dark"): void;
827
834
  /** 登记界面入口的订阅清理;停用、重载或加载失败时执行,旧入口不能再注册皮肤。 */
828
835
  effect(dispose: () => void): void;
829
836
  /**
@@ -231,14 +231,19 @@ export interface UserMessage {
231
231
  /**
232
232
  * Who sent this, when it was not the person looking at the transcript.
233
233
  *
234
- * A task dispatched from the side chat lands in the main conversation as an ordinary user
235
- * message. Without this you would scroll back and find an instruction you have no memory
234
+ * `child`: a task one of this session's child sessions (the side chat, say) handed over lands
235
+ * here as an ordinary user message. Without this you would scroll back and find an instruction you have no memory
236
236
  * of writing, in your own voice, with no way to tell where it came from.
237
237
  *
238
238
  * `parent` 是子智能体转录里「派它出去的那个 Agent 说的话」——开头那份任务、续跑时补的那一句。
239
239
  * 面板上它不该画成人发的气泡:那不是看着面板的人说的。
240
240
  */
241
- origin?: "side-chat" | "parent";
241
+ origin?: "child" | "parent";
242
+ /**
243
+ * `origin` 是 `child` 时,交来这件事的子会话是哪个插件建的(子会话的 `owner`,比如 `plume.side-chat`),界面按它标出插件的显示名。
244
+ * 宿主在这件事从队列里发出去时照子会话填上;那时子会话已经删掉了就不带,界面改用通用的说法。
245
+ */
246
+ fromPlugin?: string;
242
247
  /**
243
248
  * 侧边聊天每次提问前附上的主会话进展,读到了哪里。
244
249
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumeai/plugin-sdk",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "写 Plume 插件用的类型、命令(新建、检查、构建、安装)、插件模板和给 agent 的 skill。随 Plume 应用带一份,也发在 npm 上(插件仓库按版本依赖)。",
5
5
  "license": "MIT",
6
6
  "repository": {
package/skill/SKILL.md CHANGED
@@ -52,7 +52,7 @@ description: 写、改、排查 Plume 插件:工具、钩子(tool.execute、
52
52
  - 弹提示条(界面入口 `ui.notify`,逻辑入口 `ctx.notice`)、打开、关闭自己的面板(`ui.openPanel`、`ui.closePanel`)、把一段话写进输入框等人自己发(`ui.draftInSession`,`replace` 时整段换掉),或在项目签那一行的格子里,人点了写明要发什么的按钮时以人的身份直接发(`send`);逻辑入口用默认模型做一次性的小调用(`ctx.model.complete`)、读会话(`ctx.sessions`)、读模型调用的花销(`ctx.model.usage`)。一次性结果要覆盖持久化内容时用 `ModelCompletion.requireComplete` 拒收截断结果;经 `ctx.sessions.onContextReset` 在压缩、撤回或上下文来源变化时作废自己的缓存。
53
53
  - 人点了明确的按钮时,经 `ui.openConversationDraft({ project?, cwd?, text })` 打开项目或目录的空白对话并准备草稿,保留已有草稿、不自动发送;经 `ui.openSettings(id)` 打开自己占导航一行(`page: "nav"`)的设置分区,经 `ui.openExternal(url)` 在系统浏览器打开不带凭据的 HTTP(S) 链接。
54
54
  - 开一个新的顶层会话并发出第一句话(`ctx.sessions.launch`,定时任务到点了、人点了「在新会话里执行」):它进侧栏、不归插件,人接着聊,插件之后碰不到它;`done` 是第一轮怎么结束的,`idle` 在它第一次真闲下来时兑现(后台运行都回来、问出去的请求都有了答复、唤起的几轮也做完),`reportBack(text, status)` 把结果交回开出它的会话一次(`idle` 兑现时带着最后一轮的收场和最后一段回复,交回用它);`spawnedFrom` 记着从哪个会话、哪次调用(`run.toolCallId`)开出来,要交回时那次调用得是你自己的工具在那个会话里的调用,不然 `reportBack` 抛错;人点了按钮时经 `ui.openSession(id)` 打开它。页面那一行挂数目(`ui.pageBadge`),别处的按钮经 `ui.openPage(id)` 带人去自己的页面。
55
- - 建自己的子会话并发送消息(`ctx.sessions.create` / `send`),或经 `start` / `wait` 在后台跑、经 `report` 把该次结果送给真实父会话;`steer` 在运行中插话,`onStopped` 撤销尚未启动的排队工作。停止父会话后后台结果不能再唤醒它。自己的子会话里,自己的工具那次调用拿得到父会话(`run.parent`:`enqueue` 排进它的队列、`stop` 停下、`carryOn` 接着做、`status`),工具卡片上人点的按钮经 `handToParent` 把一件事排进父会话;作用对象只有这个父会话,写进去的话标着来自侧边聊天、不算人的授权。`models` 只给无密钥的模型目录,建子会话时可选模型和思考档位。
55
+ - 建自己的子会话并发送消息(`ctx.sessions.create` / `send`),或经 `start` / `wait` 在后台跑、经 `report` 把该次结果送给真实父会话;`steer` 在运行中插话,`onStopped` 撤销尚未启动的排队工作。停止父会话后后台结果不能再唤醒它。自己的子会话里,自己的工具那次调用拿得到父会话(`run.parent`:`enqueue` 排进它的队列、`stop` 停下、`carryOn` 接着做、`status`),工具卡片上人点的按钮经 `handToParent` 把一件事排进父会话;作用对象只有这个父会话,写进去的话标着来自你的插件(显示名)、不算人的授权。`models` 只给无密钥的模型目录,建子会话时可选模型和思考档位。
56
56
  - 给自己的页面建一个一直在的助手:`ctx.sessions.create` 不给 `parentId` 就是插件的独立会话(不挂在任何会话下、不进侧栏,只有你能发),界面经 `ui.Conversation` 画它,审批卡就压在这个视图的输入框上方。
57
57
  - 界面经 `ui.Conversation`、`ui.useSession` 复用转录与实时状态;可局部定制任务消息、去掉回报重复正文,操控输入框用 `mode: "steer"`。`ui.Markdown` 复用正文和文件链接;`ui.composerAbove` 在主输入框上方独占一行。经 `ui.quoteAction` 接主会话划词菜单,`ui.quoteInPanel` 把引用追加到自己的面板草稿;`composer.draftKey` 按标签保存文字、附件和引用。`/` 命令可以自己办掉、不发消息(`{ handled: true }`)。
58
58
  - 界面经 `ui.onSessionsChanged` 订阅已保存消息、摘要和删除事件,刷新画廊等页面;经 `ui.ComposerSend` 复用发送/停止按钮,人真实点击停止时调用 `ui.stopSession`。终端插件经 `ui.terminalOpener` 承接文件树等入口的目录打开请求,经 `ui.terminalRunner` 承接代码块的「在终端运行」,经 `ui.findFile` / `ui.openFile` 打开输出里的路径、`ui.openLink` 打开网址、`ui.droppedPaths` 读拖进来的文件路径。
@@ -125,7 +125,7 @@ description: 写、改、排查 Plume 插件:工具、钩子(tool.execute、
125
125
 
126
126
  ## 可以照着写的插件
127
127
 
128
- 都在 Plume 插件仓库(plume-plugins)的 `plugins/` 下,`todo`、`commands`、`web`、`skills`、`git` 随应用发、在本体仓库的 `packages/builtin/` 下(拿不到仓库时,以模板和本 skill 里的代码片段为准):
128
+ 都在 Plume 插件仓库(plume-plugins)的 `plugins/` 下,`todo`、`commands`、`web`、`skills`、`git`、`side-chat`、`subagents`、`plan` 随应用发、在本体仓库的 `packages/builtin/` 下(拿不到仓库时,以模板和本 skill 里的代码片段为准):
129
129
 
130
130
  | 插件 | 看它的什么 |
131
131
  | --- | --- |
@@ -122,7 +122,7 @@ ctx.tools.register({
122
122
 
123
123
  ### `model.context`
124
124
 
125
- 每次调模型前经过一次。最里层原样返回。输入输出都是 `{ sessionId, cwd, projectRoots, scratchDir, permissionMode, systemPrompt, sections, messages, tools }`(`scratchDir` 同 `run.scratchDir`,`projectRoots` 同 `run.projectRoots`;`permissionMode` 是这个会话现在的权限模式,含项目覆盖,取值同 `run.permissionMode()`,Windows 上受约束的命令跑 PowerShell、完全访问跑 Git Bash,plume.prompt 按它在环境里点名 shell;外层要求这一轮的命令都只读时设 `readOnly: true`,宿主不给,计划模式插件在 plume.prompt 外面(order -1001)设上,plume.prompt 就按只读那档点名 shell),只作用于这一次请求,会话里存的不变。
125
+ 每次调模型前经过一次。最里层原样返回。输入输出都是 `{ sessionId, parentId?, owner?, cwd, projectRoots, scratchDir, permissionMode, systemPrompt, sections, messages, tools }`(`parentId`、`owner` 同会话摘要上的同名字段:插件的子会话两个都有,插件的独立会话只有 `owner`,侧栏里的会话都不带,按「是不是子会话」藏工具时看它们,不用每次去 `ctx.sessions.list()`;`scratchDir` 同 `run.scratchDir`,`projectRoots` 同 `run.projectRoots`;`permissionMode` 是这个会话现在的权限模式,含项目覆盖,取值同 `run.permissionMode()`,Windows 上受约束的命令跑 PowerShell、完全访问跑 Git Bash,plume.prompt 按它在环境里点名 shell;外层要求这一轮的命令都只读时设 `readOnly: true`,宿主不给,计划模式插件在 plume.prompt 外面(order -1001)设上,plume.prompt 就按只读那档点名 shell),只作用于这一次请求,会话里存的不变。
126
126
 
127
127
  系统提示词按段给在 `sections` 里(`{ id, text, files? }[]`,`systemPrompt` 就是它们用空行连起来)。本体的段依次是 `identity`、`guidelines`、`boundaries`、`environment`、`tone`(设置里的语气,不是「专业严谨」时才有)、`userInstructions`(设置里写了全局指令时才有)、`projectInstructions`(项目里有 PLUME.md / AGENTS.md / CLAUDE.md 时才有)、`workspace`(工作目录和会话的临时目录),plume.tools 再加一段 `resources`(资源地址的清单)。加自己的说明就加一段,段名用自己的;要换掉或去掉本体的某段,按 `id` 改:
128
128
 
@@ -137,7 +137,7 @@ ctx.hook("model.context", (input, next) =>
137
137
 
138
138
  `systemPrompt` 只读:宿主在每一层交接时按 `sections` 重拼,直接改它不作数。
139
139
 
140
- 系统提示词在会话里冻结:plume.prompt 在最外层把里层交回的段和这个会话冻结的那份比对,发出去的始终是冻结的那份,服务商的前缀缓存才接得上。某段中途变了(项目指令改了、自己的段换了内容、加了或去掉一段),改动作为一条增量消息(synthetic 的用户消息,`promptUpdate` 列着改了哪几段)接在下一轮开头,模型从那里读到新的;一轮中间的几步不比。压缩以后(或撤回到第一条消息)按那时的段重新冻结。所以段的内容要只随该变的东西变:每次请求都不一样的东西(时间、计数)放进段里,每一轮都会多出一条增量。
140
+ 系统提示词在会话里冻结:plume.prompt 在最外层把里层交回的段和这个会话冻结的那份比对,发出去的始终是冻结的那份,服务商的前缀缓存才接得上。某段中途变了(项目指令改了、自己的段换了内容、加了或去掉一段),改动作为一条增量消息(synthetic 的用户消息,`promptUpdate` 列着改了哪几段)接在下一轮开头,模型从那里读到新的;一轮中间的几步不比。压缩以后(或撤回到第一条消息)按那时的段重新冻结。所以段的内容要只随该变的东西变:每次请求都不一样的东西(时间、计数)放进段里,每一轮都会多出一条增量。工具表也按会话冻结:`ctx.tools.register` 和撤下、插件装卸带来的变化(包括里层经 `model.context` 改的 `tools`)在下一轮开头才发给模型,原有工具的顺序不变、新的接在后面;一轮中间撤下的工具还留在表里,模型调它会拿到「没有这个工具」。
141
141
 
142
142
  段里有内容是从文件读来的,就在段上带 `files: [{ path, chars }]`(`path` 绝对路径,`chars` 是那一截在 `text` 里的字数,连同包着它的标签):`projectInstructions`、`projectMemory` 两段的来源会按文件列在上下文用量的「记忆文件」里,人点了在文件面板里打开,文件在会话目录外(插件数据目录里的记忆、工作目录上层的指令)也能只读看,只认这次请求标出的这几个路径;别的段带了不显示。plume.prompt 给每份项目指令标了来源;自己出 `projectMemory` 段的插件也该标。改了某段 `text` 的层要么跟着改 `files`,要么去掉它(`{ id, text }` 新建的段本来就没有)。
143
143
 
@@ -355,7 +355,7 @@ const reply = (await ctx.sessions.messages(child.id)).findLast((message) => mess
355
355
  - 后台用 `start`,它只等运行登记,不等回答;工具先交回子会话 id,后台用 `wait(run.id)` 等结果、`report(run.id, text)` 交给父会话。每次续跑是新的运行句柄,不能按子会话 id 重复交差。所有异步错误都要处理;不要用轮询或直接改会话库模拟送达。
356
356
  - 父轮正常结束不取消后台,明确停止父会话会取消后台及待送达结果,不连带停止独立 `send` 的侧边聊天。插件停用、父会话删除或应用退出也会取消。插件自己的排队名额和未启动项经 `onStopped`、`onDeleted`、`ctx.effect` 清理。
357
357
  - 续跑复用原子会话,重建该种类的提示词与工具限制后再次 `start`;别创建新会话丢掉上下文。种类、排队状态和模型偏好由插件存到 `ctx.dataDir`,宿主不识别这些业务字段。
358
- - 子会话用什么提示词、哪些工具,经 `model.context`、`tool.execute` 按 `sessionId` 认出自己的会话来改(建的时候记下 id;重启后从 `list()` 里按 `owner` 找回)。隐藏不等于禁用:藏起的工具在 `tool.execute` 里再拦一次。
358
+ - 子会话用什么提示词、哪些工具,经 `model.context`、`tool.execute` 按 `sessionId` 认出自己的会话来改(建的时候记下 id;重启后从 `list()` 里按 `owner` 找回)。`model.context` 的输入直接带着 `parentId`、`owner`,只想分清子会话和顶层会话时看它们就够。隐藏不等于禁用:藏起的工具在 `tool.execute` 里再拦一次。
359
359
  - 子会话要人批准时,审批卡压在主输入框上方,卡上写着子会话的标题;界面入口用 `ui.Conversation` 画它、`ui.useSession` 读它(见 ui.md)。
360
360
  - 子会话够得着它的父会话,只此一个:你自己建的子会话里,你自己注册的工具的那次调用拿得到 `run.parent`(别的插件的工具、别人的子会话、顶层会话里都是 `undefined`)。只在这次调用里用:调用答完以后再调它的方法就抛错。
361
361
 
@@ -369,7 +369,7 @@ async execute(args, run) {
369
369
  }
370
370
  ```
371
371
 
372
- 排进去的话标着 `origin: "side-chat"`、前面带一句来处说明,不算人的授权,审批照常。人在卡片上点的「交给主会话」用 `ToolCardProps.handToParent`(见 ui.md)。
372
+ 排进去的话标着 `origin: "child"`、`fromPlugin` 是你的插件名(界面上是「来自<你的显示名>」)、前面带一句来处说明,不算人的授权,审批照常。人在卡片上点的「交给主会话」用 `ToolCardProps.handToParent`(见 ui.md)。
373
373
 
374
374
  ## 独立会话
375
375
 
@@ -31,6 +31,7 @@ export default {
31
31
  | `ui.panel(id, Component, options)` | 往面板区加一个面板 |
32
32
  | `ui.page(id, Component, options)` | 往侧栏加一个页面 |
33
33
  | `ui.skin({ name?, mode?, light, dark })` / `ui.skin(null)` | 更新皮肤或撤下自己的皮肤;name 与 mode 都提供时管理配色和深浅模式 |
34
+ | `ui.useTheme()` / `ui.setTheme(theme)` | 读、改应用的主题偏好(设置 › 外观的跟随系统、浅色、深色),见「皮肤」 |
34
35
  | `ui.ChatBackgroundSettings` | 自包含的背景设置组件,可传 `title`;管理全局的图片、显示位置、铺满/平铺、效果、浓淡,以及整个窗口的磨砂质感(外圈、停靠侧栏和对话区一起,不看皮肤、不要图;外观页不再有背景设置),支持选图和拖入图片,不向插件暴露磁盘路径 |
35
36
  | `ui.onSessionsChanged(listener)` | 订阅所有会话的已保存消息、摘要和删除变化,包含当前未打开的会话;返回取消订阅函数,组件 effect 结束时调用,插件停用时也会撤下。事件 `SessionChange` 的字段见下文 |
36
37
  | `ui.effect(dispose)` | 界面入口的生命周期清理,停用、重载或 apply 失败时逆序执行 |
@@ -515,6 +516,8 @@ function ModelSetting() {
515
516
 
516
517
  只传 `{ name: "Plume", mode: "system" }`、不传 token 时,使用产品原生配色并跟随系统;`light`、`dark` 则固定模式。这和 `ui.skin(null)` 不同:前者暂时管理配色并屏蔽原有种子覆盖,后者退出管理、恢复原用户偏好。
517
518
 
519
+ 要让人在插件里切「跟随系统 / 浅色 / 深色」,就是改外观页那三张主题卡片,不是做一套皮肤:`ui.setTheme("dark")` 等同人在外观页点「深色」,存盘、各窗口同步;组件里用 `ui.useTheme()` 读当前选的是哪张。有皮肤接管深浅模式时外观页的主题只读,`setTheme` 同样不改,所以先 `ui.skin(null)` 撤下自己的皮肤再调。只在人点了插件界面上的按钮时调,不在工具或定时逻辑里替人改。
520
+
518
521
  背景操作复用 `<ui.ChatBackgroundSettings title="…" />`,`title` 来自插件文案,省略时用产品的「会话背景」。用户在该组件做的修改直接保存到全局背景偏好,切换皮肤或停用插件不撤销;图片格式和体积检查、复制、失败提示和跨窗口更新均由宿主负责,不另存一份背景。
519
522
 
520
523
  `root` 是窗口最外层与透明停靠侧栏共用的底色,默认跟随 `shell`;`sidebar` 只给 compact 浮出侧栏铺底,不给停靠侧栏另铺一层;`accent-dim` 是强调弱底,默认是强调色的 10% 透明底。数据皮肤可直接给出完整的面、线、字 token;若一份配色只适用于一个模式,给 `light`、`dark` 同一组 token,并用 `mode` 指定深浅模式。