@plumeai/plugin-sdk 0.1.5 → 0.1.6
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.
- package/dist/client/styles/src/index.d.ts +0 -12
- package/dist/client/ui/src/avatar/AgentAvatar.d.ts +1 -3
- package/dist/client/ui/src/avatar/AvatarPile.d.ts +1 -2
- package/dist/client/ui/src/i18n/messages/en.d.ts +5 -4
- package/dist/client/ui/src/i18n/messages/zh-CN.d.ts +6 -5
- package/dist/client/ui/src/index.d.ts +0 -15
- package/dist/client/ui/src/inputs/TextArea.d.ts +1 -2
- package/dist/client/ui/src/inputs/day-range.d.ts +0 -2
- package/dist/client/ui/src/layout/RetainedViews.d.ts +2 -13
- package/dist/client/ui/src/lib/tiling.d.ts +0 -21
- package/dist/client/ui/src/motion/freeze.d.ts +0 -1
- package/dist/client/ui/src/motion/loaders.d.ts +1 -6
- package/dist/client/ui/src/overlay/Overlay.d.ts +1 -2
- package/dist/client/ui/src/overlay/Tooltip.d.ts +1 -1
- package/dist/client/ui/src/primitives/ClosableTab.d.ts +1 -2
- package/dist/client/ui/src/primitives/CountUp.d.ts +1 -4
- package/dist/contract/src/plugin.d.ts +37 -12
- package/dist/contract/src/service.d.ts +0 -2
- package/dist/contract/src/ui.d.ts +2 -2
- package/dist/infra/rpc/src/endpoint.d.ts +1 -1
- package/dist/infra/rpc/src/rpc.d.ts +2 -7
- package/dist/theme.css +2 -0
- package/dist/ui.d.ts +1 -1
- package/package.json +1 -1
- package/skill/SKILL.md +1 -1
- package/skill/references/components.md +4 -80
- package/skill/references/logic.md +9 -7
- package/skill/references/ui.md +4 -2
- package/dist/client/ui/src/avatar/AvatarStack.d.ts +0 -26
- package/dist/client/ui/src/lib/typing.d.ts +0 -19
- package/dist/client/ui/src/motion/TypedText.d.ts +0 -9
- package/dist/client/ui/src/primitives/GitHubMark.d.ts +0 -12
- package/dist/client/ui/src/primitives/RowDeleteButton.d.ts +0 -6
|
@@ -105,19 +105,7 @@ export declare const card: {
|
|
|
105
105
|
};
|
|
106
106
|
};
|
|
107
107
|
};
|
|
108
|
-
/** 卡片底部一行:左边是说明,右边是按钮。 */
|
|
109
|
-
export declare const actions: {
|
|
110
|
-
row: string;
|
|
111
|
-
note: string;
|
|
112
|
-
};
|
|
113
108
|
export declare const code: {
|
|
114
109
|
block: string;
|
|
115
|
-
diff: string;
|
|
116
|
-
diffLine: string;
|
|
117
|
-
diffKind: {
|
|
118
|
-
same: string;
|
|
119
|
-
add: string;
|
|
120
|
-
remove: string;
|
|
121
|
-
};
|
|
122
110
|
};
|
|
123
111
|
export {};
|
|
@@ -22,8 +22,6 @@ export interface AgentAvatarProps {
|
|
|
22
22
|
/** 像素边长。 */
|
|
23
23
|
size?: number;
|
|
24
24
|
mood?: AvatarMood;
|
|
25
|
-
/** 右下角的小圆点:在跑是强调色、会一圈圈往外扩;出错是红的。 */
|
|
26
|
-
badge?: "running" | "failed" | null;
|
|
27
25
|
/**
|
|
28
26
|
* 悬停时眼睛跟着指针、身子弹一下。默认开。
|
|
29
27
|
*
|
|
@@ -39,4 +37,4 @@ export interface AgentAvatarProps {
|
|
|
39
37
|
cheer?: string | number | null;
|
|
40
38
|
className?: string;
|
|
41
39
|
}
|
|
42
|
-
export declare function AgentAvatar({ avatar, size, mood,
|
|
40
|
+
export declare function AgentAvatar({ avatar, size, mood, interactive, host, seed, label, cheer, className }: AgentAvatarProps): import("react").JSX.Element;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 一摞脸:派出去不止一个时,叠在一起说「有好几个」。
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* 这一摞叠着放,占地方小,说的是「这里有好几个,点开看」——放在一行字旁边、一个切换器上这类挤的地方。
|
|
4
|
+
* 叠着放,占地方小,说的是「这里有好几个,点开看」——放在一行字旁边、一个切换器上这类挤的地方。
|
|
6
5
|
* 表情仍然在:在跑的还在颠,只是被邻居盖住半张脸。
|
|
7
6
|
*
|
|
8
7
|
* 叠放有先后:后一张压在前一张上面,像一张一张往右放下去的牌,最后放下的那张完整露出来,「+N」
|
|
@@ -73,6 +73,7 @@ export declare const en: {
|
|
|
73
73
|
"settings.models": string;
|
|
74
74
|
"settings.about": string;
|
|
75
75
|
"settings.personalization": string;
|
|
76
|
+
"settings.saveFailed": string;
|
|
76
77
|
"settings.agents": string;
|
|
77
78
|
"agents.title": string;
|
|
78
79
|
"agents.intro": string;
|
|
@@ -337,6 +338,9 @@ export declare const en: {
|
|
|
337
338
|
"appearance.chatBackgroundErrorFailed": string;
|
|
338
339
|
"appearance.chatBackgroundEmpty": string;
|
|
339
340
|
"appearance.chatBackgroundShow": string;
|
|
341
|
+
"appearance.chatBackgroundTexture": string;
|
|
342
|
+
"appearance.chatBackgroundTextureNone": string;
|
|
343
|
+
"appearance.chatBackgroundTextureGrain": string;
|
|
340
344
|
"appearance.chatBackgroundOpacityStep": string;
|
|
341
345
|
"appearance.chatBackgroundFit": string;
|
|
342
346
|
"appearance.chatBackgroundFitCover": string;
|
|
@@ -1123,14 +1127,11 @@ export declare const en: {
|
|
|
1123
1127
|
"update.download": string;
|
|
1124
1128
|
"update.downloadingPercent": string;
|
|
1125
1129
|
"update.failed": string;
|
|
1126
|
-
"update.goInstall": string;
|
|
1127
1130
|
"update.newVersion": string;
|
|
1128
1131
|
"update.pausedPercent": string;
|
|
1129
1132
|
"update.preparing": string;
|
|
1130
1133
|
"update.reading": string;
|
|
1131
|
-
"update.readyInstaller": string;
|
|
1132
1134
|
"update.readyRestart": string;
|
|
1133
|
-
"update.reopenInstaller": string;
|
|
1134
1135
|
"update.restartNow": string;
|
|
1135
1136
|
"update.restartToUpdate": string;
|
|
1136
1137
|
"update.resume": string;
|
|
@@ -1141,7 +1142,6 @@ export declare const en: {
|
|
|
1141
1142
|
"updateDialog.downloadedSoFar": string;
|
|
1142
1143
|
"updateDialog.downloading": string;
|
|
1143
1144
|
"updateDialog.failedTitle": string;
|
|
1144
|
-
"updateDialog.installerMissing": string;
|
|
1145
1145
|
"updateDialog.notReady": string;
|
|
1146
1146
|
"updateDialog.pause": string;
|
|
1147
1147
|
"updateDialog.paused": string;
|
|
@@ -1387,6 +1387,7 @@ export declare const en: {
|
|
|
1387
1387
|
"storage.exportDetail": string;
|
|
1388
1388
|
"storage.exportDialogTitle": string;
|
|
1389
1389
|
"storage.exportFailed": string;
|
|
1390
|
+
"cleanup.failed": string;
|
|
1390
1391
|
"storage.exportTitle": string;
|
|
1391
1392
|
"storage.exported": string;
|
|
1392
1393
|
"storage.exporting": string;
|
|
@@ -73,6 +73,7 @@ export declare const zhCN: {
|
|
|
73
73
|
readonly "settings.models": "模型设置";
|
|
74
74
|
readonly "settings.about": "关于";
|
|
75
75
|
readonly "settings.personalization": "个性化";
|
|
76
|
+
readonly "settings.saveFailed": "设置没能保存:{reason}";
|
|
76
77
|
readonly "settings.agents": "智能体";
|
|
77
78
|
readonly "agents.title": "智能体";
|
|
78
79
|
readonly "agents.intro": "把一类任务交给单独配置了模型、工具和指令的子智能体。";
|
|
@@ -319,6 +320,9 @@ export declare const zhCN: {
|
|
|
319
320
|
readonly "appearance.chatBackgroundErrorFailed": "图片导入失败,原背景图未改变。";
|
|
320
321
|
readonly "appearance.chatBackgroundEmpty": "尚未设置背景图";
|
|
321
322
|
readonly "appearance.chatBackgroundShow": "显示位置";
|
|
323
|
+
readonly "appearance.chatBackgroundTexture": "质感";
|
|
324
|
+
readonly "appearance.chatBackgroundTextureNone": "无";
|
|
325
|
+
readonly "appearance.chatBackgroundTextureGrain": "磨砂";
|
|
322
326
|
readonly "appearance.chatBackgroundOpacityStep": "浓度 {percent}%";
|
|
323
327
|
readonly "appearance.chatBackgroundFit": "铺放方式";
|
|
324
328
|
readonly "appearance.chatBackgroundFitCover": "铺满";
|
|
@@ -471,7 +475,7 @@ export declare const zhCN: {
|
|
|
471
475
|
readonly "market.unreadable": "{n} 个插件有问题,去设置里看看";
|
|
472
476
|
readonly "market.notInCatalog": "插件目录里已经没有它了";
|
|
473
477
|
readonly "market.unreachable": "连不上插件市场({reason}),列出的是已装的和上次读到的";
|
|
474
|
-
readonly "market.localOwned": "
|
|
478
|
+
readonly "market.localOwned": "本地上传了同名的插件,先卸掉那一份";
|
|
475
479
|
readonly "market.builtinOwned": "这是随应用发的插件,不能卸载、停用";
|
|
476
480
|
readonly "market.notAPlugin": "这个文件夹不是构建好的插件:文件夹名只能用小写字母、数字和连字符,里面要有 dist/index.mjs";
|
|
477
481
|
readonly "market.pickPluginFolder": "选择插件文件夹";
|
|
@@ -979,14 +983,11 @@ export declare const zhCN: {
|
|
|
979
983
|
readonly "update.download": "下载安装";
|
|
980
984
|
readonly "update.downloadingPercent": "下载中 {percent}%";
|
|
981
985
|
readonly "update.failed": "下载失败";
|
|
982
|
-
readonly "update.goInstall": "去安装";
|
|
983
986
|
readonly "update.newVersion": "新版本 {version}";
|
|
984
987
|
readonly "update.pausedPercent": "已暂停 {percent}%";
|
|
985
988
|
readonly "update.preparing": "准备中";
|
|
986
989
|
readonly "update.reading": "正在读取版本…";
|
|
987
|
-
readonly "update.readyInstaller": "已下载完成,安装程序已经打开。按它的提示装完,再重新启动 Plume 就是新版本了。";
|
|
988
990
|
readonly "update.readyRestart": "已下载完成。重启 Plume 即可用上新版本,正在进行的对话会中断。";
|
|
989
|
-
readonly "update.reopenInstaller": "重新打开安装包";
|
|
990
991
|
readonly "update.restartNow": "立即重启";
|
|
991
992
|
readonly "update.restartToUpdate": "重启更新";
|
|
992
993
|
readonly "update.resume": "继续下载";
|
|
@@ -997,7 +998,6 @@ export declare const zhCN: {
|
|
|
997
998
|
readonly "updateDialog.downloadedSoFar": "(已下 {size})";
|
|
998
999
|
readonly "updateDialog.downloading": "正在下载";
|
|
999
1000
|
readonly "updateDialog.failedTitle": "这次没能写完";
|
|
1000
|
-
readonly "updateDialog.installerMissing": "没找到下载好的安装包,重新下载一次";
|
|
1001
1001
|
readonly "updateDialog.notReady": "更新没有准备好,重新下载一次再试";
|
|
1002
1002
|
readonly "updateDialog.pause": "暂停";
|
|
1003
1003
|
readonly "updateDialog.paused": "已暂停";
|
|
@@ -1207,6 +1207,7 @@ export declare const zhCN: {
|
|
|
1207
1207
|
readonly "storage.exportDetail": "把所有会话另存为一个 SQLite 文件(.db),任何 SQLite 工具都能打开";
|
|
1208
1208
|
readonly "storage.exportDialogTitle": "导出会话记录";
|
|
1209
1209
|
readonly "storage.exportFailed": "导出失败:{reason}";
|
|
1210
|
+
readonly "cleanup.failed": "清除失败:{reason}";
|
|
1210
1211
|
readonly "storage.exportTitle": "导出会话记录";
|
|
1211
1212
|
readonly "storage.exported": "已导出到 {path}";
|
|
1212
1213
|
readonly "storage.exporting": "正在导出…";
|
|
@@ -13,7 +13,6 @@ export type { MessageKey, ResolvedUiLocale } from "./i18n/messages/index.ts";
|
|
|
13
13
|
export { activeLocale, translate } from "./i18n/translate.ts";
|
|
14
14
|
export * from "./avatar/AgentAvatar.tsx";
|
|
15
15
|
export * from "./avatar/AvatarPile.tsx";
|
|
16
|
-
export * from "./avatar/AvatarStack.tsx";
|
|
17
16
|
export * from "./avatar/art.ts";
|
|
18
17
|
export * from "./fileIcon.tsx";
|
|
19
18
|
export * from "./hooks/useCopied.ts";
|
|
@@ -53,10 +52,8 @@ export * from "./lib/hour-style.ts";
|
|
|
53
52
|
export * from "./lib/number-draft.ts";
|
|
54
53
|
export * from "./lib/relative-time.ts";
|
|
55
54
|
export * from "./lib/tiling.ts";
|
|
56
|
-
export * from "./lib/typing.ts";
|
|
57
55
|
export * from "./motion/RollWords.tsx";
|
|
58
56
|
export * from "./motion/RollingText.tsx";
|
|
59
|
-
export * from "./motion/TypedText.tsx";
|
|
60
57
|
export * from "./motion/freeze.ts";
|
|
61
58
|
export * from "./motion/held.ts";
|
|
62
59
|
export * from "./motion/loaders.tsx";
|
|
@@ -82,10 +79,8 @@ export * from "./primitives/Caret.tsx";
|
|
|
82
79
|
export * from "./primitives/ChoiceMark.tsx";
|
|
83
80
|
export * from "./primitives/ClosableTab.tsx";
|
|
84
81
|
export * from "./primitives/CountUp.tsx";
|
|
85
|
-
export * from "./primitives/GitHubMark.tsx";
|
|
86
82
|
export * from "./primitives/IconButton.tsx";
|
|
87
83
|
export * from "./primitives/MarkNumber.tsx";
|
|
88
|
-
export * from "./primitives/RowDeleteButton.tsx";
|
|
89
84
|
export * from "./primitives/Skeleton.tsx";
|
|
90
85
|
export * from "./primitives/TabStrip.tsx";
|
|
91
86
|
export * from "./primitives/Text.tsx";
|
|
@@ -126,15 +121,5 @@ export declare function Card({ title, subtitle, status, tone, children }: CardPr
|
|
|
126
121
|
export declare function CodeBlock({ children }: {
|
|
127
122
|
children: string;
|
|
128
123
|
}): import("react").JSX.Element;
|
|
129
|
-
type DiffLine = {
|
|
130
|
-
kind: "same" | "add" | "remove";
|
|
131
|
-
text: string;
|
|
132
|
-
};
|
|
133
|
-
/** 按行比较。用 LCS,够看小段改动;大文件的 diff 不在这里做。 */
|
|
134
|
-
export declare function diffLines(before: string, after: string): DiffLine[];
|
|
135
|
-
export declare function DiffView({ before, after }: {
|
|
136
|
-
before: string;
|
|
137
|
-
after: string;
|
|
138
|
-
}): import("react").JSX.Element;
|
|
139
124
|
export * from "./inputs/ColorField.tsx";
|
|
140
125
|
export * from "./inputs/Slider.tsx";
|
|
@@ -17,14 +17,13 @@ import { type ComponentPropsWithoutRef } from "react";
|
|
|
17
17
|
* 主输入框、消息编辑框这类框里还站着别的东西(工具栏、附件条、那排按钮)的不走这里,外壳是各自的。
|
|
18
18
|
* 共享的是同一个 `useFieldFade` 和同一条遮罩。
|
|
19
19
|
*/
|
|
20
|
-
export declare function TextArea({ value, onChange, shell, className,
|
|
20
|
+
export declare function TextArea({ value, onChange, shell, className, resizable, ...rest }: {
|
|
21
21
|
value: string;
|
|
22
22
|
onChange: (value: string) => void;
|
|
23
23
|
/** 外壳上的额外类。高度、外边距、宽度——和这个框在页面上待在哪儿有关的事,调用方说了算。 */
|
|
24
24
|
shell?: string;
|
|
25
25
|
/** 字那一层的额外类:等宽、字号、`h-[180px]` 这类只跟可写区域有关的。 */
|
|
26
26
|
className?: string;
|
|
27
|
-
fieldRef?: React.RefObject<HTMLTextAreaElement | null>;
|
|
28
27
|
/** 能不能用鼠标拉高。只给写长文的地方,比如一份系统提示词。 */
|
|
29
28
|
resizable?: boolean;
|
|
30
29
|
} & Omit<ComponentPropsWithoutRef<"textarea">, "value" | "onChange" | "className" | "ref">): import("react").JSX.Element;
|
|
@@ -53,5 +53,3 @@ export declare function isInterior(day: string, range: DayRange): boolean;
|
|
|
53
53
|
* 前面」得到的是一段空范围,而且没有任何提示说刚才发生了什么。
|
|
54
54
|
*/
|
|
55
55
|
export declare function pickDay(range: DayRange, day: string): DayRange;
|
|
56
|
-
/** 这段日子有几天,两头都算。哪一头空着都数不出来。 */
|
|
57
|
-
export declare function rangeLength(range: DayRange): number | null;
|
|
@@ -1,19 +1,8 @@
|
|
|
1
1
|
/** 留住少数几个看过的页面;藏起来的页面暂停 effect,保留局部状态。 */
|
|
2
|
-
export declare function RetainedViews<T extends string>({ active, render, limit
|
|
2
|
+
export declare function RetainedViews<T extends string>({ active, render, limit }: {
|
|
3
3
|
active: T;
|
|
4
4
|
render: (key: T) => React.ReactNode;
|
|
5
5
|
limit?: number;
|
|
6
|
-
/** 到达时的类。不同的页面可以换一种更长的淡入加升起。 */
|
|
7
|
-
pageClassName?: string;
|
|
8
|
-
/**
|
|
9
|
-
* 标签,按标签条上的顺序:每次切换,新页面从它所在的那一侧滑进来并淡入,而不是一帧之内换掉。
|
|
10
|
-
*
|
|
11
|
-
* 用 Web Animations API 在布局 effect 里播,不用类。用类的话——见下面 `seen` 的注释——从 `display: none`
|
|
12
|
-
* 回来的页面会先在终点画一帧,动画才接上。布局 effect 在页面显示之后、任何东西画出来之前跑,所以谁看到的
|
|
13
|
-
* 第一帧都已经是滑入的第一帧。回到一个页面也播:标签条是来回切的地方,只在第一次有动画的切换,第二次读起来
|
|
14
|
-
* 就像坏了。
|
|
15
|
-
*/
|
|
16
|
-
slide?: readonly T[];
|
|
17
6
|
}): import("react").JSX.Element;
|
|
18
7
|
/**
|
|
19
8
|
* 回来的那一页,里面的 CSS 动画不重演。
|
|
@@ -26,7 +15,7 @@ export declare function RetainedViews<T extends string>({ active, render, limit,
|
|
|
26
15
|
* 跟着一起拆了),离开期间写完的回复要等回来、订阅重新挂上才补进来——那是紧跟着的另一次提交,
|
|
27
16
|
* 当场处理会漏掉它,而它同样是在补账,不是此刻发生的事。两次提交都落在第一帧之前,逐帧量过。
|
|
28
17
|
*
|
|
29
|
-
* 页面自己那层(`
|
|
18
|
+
* 页面自己那层(`plume-page-enter`)不算:那是换页本身的到达动画,每次换页都该有。转圈、呼吸这种
|
|
30
19
|
* 没有终点的照常跑——它们也没法跳到终点,`finish()` 会抛错。
|
|
31
20
|
*/
|
|
32
21
|
export declare function finishReplayedAnimations(page: HTMLElement): void;
|
|
@@ -18,15 +18,6 @@ export interface Box {
|
|
|
18
18
|
width: number;
|
|
19
19
|
height: number;
|
|
20
20
|
}
|
|
21
|
-
/** 两棵树共有的形状:只要分支,叶子长什么样这里不关心。 */
|
|
22
|
-
type Tiling = {
|
|
23
|
-
type: "leaf";
|
|
24
|
-
} | {
|
|
25
|
-
type: "split";
|
|
26
|
-
dir: Axis;
|
|
27
|
-
sizes: readonly number[];
|
|
28
|
-
children: readonly Tiling[];
|
|
29
|
-
};
|
|
30
21
|
/**
|
|
31
22
|
* 两个窗格之间一条能拖的边界。
|
|
32
23
|
*
|
|
@@ -48,7 +39,6 @@ export interface SplitterBox extends Box {
|
|
|
48
39
|
*/
|
|
49
40
|
split: Box;
|
|
50
41
|
}
|
|
51
|
-
export declare const FULL: Box;
|
|
52
42
|
/**
|
|
53
43
|
* 分隔条的抓取区,跨在两个窗格的边界上。
|
|
54
44
|
*
|
|
@@ -67,16 +57,6 @@ export declare const GRIP_SPAN = 30;
|
|
|
67
57
|
* 帧与帧之间字符串不变——这一点要紧:每帧都变的样式字符串会打断它本该在过渡的那个过渡。
|
|
68
58
|
*/
|
|
69
59
|
export declare const pct: (fraction: number) => string;
|
|
70
|
-
/** 给定在轴上从哪开始、占多少,从父框里切出孩子的框。 */
|
|
71
|
-
export declare function slice(rect: Box, dir: Axis, offset: number, share: number): Box;
|
|
72
|
-
/**
|
|
73
|
-
* 所有能拖的边界。
|
|
74
|
-
*
|
|
75
|
-
* n 个孩子的分支有 n−1 条——分支的外沿是包着它的东西的边,不是两个窗格之间的边界。
|
|
76
|
-
*
|
|
77
|
-
* 每条都画成零厚度的线;抓取区由组件加,只有组件知道九个像素是多宽。
|
|
78
|
-
*/
|
|
79
|
-
export declare function layoutSplitters(tree: Tiling, within?: Box): SplitterBox[];
|
|
80
60
|
/**
|
|
81
61
|
* 把指针位置换算成分隔条近侧窗格该占的份额。
|
|
82
62
|
*
|
|
@@ -86,4 +66,3 @@ export declare function layoutSplitters(tree: Tiling, within?: Box): SplitterBox
|
|
|
86
66
|
* `position` 是拖动轴上的客户区坐标:横向分支是 `clientX`,纵向是 `clientY`。
|
|
87
67
|
*/
|
|
88
68
|
export declare function shareFromPointer(handle: SplitterBox, position: number, container: Box): number;
|
|
89
|
-
export {};
|
|
@@ -39,15 +39,10 @@ export declare function StatusSpinner({ size, className }: {
|
|
|
39
39
|
*
|
|
40
40
|
* 轨道是这个记号的重点,不是装饰:亮弧单独转的话,图形的轮廓跟着弧一起走,在按钮那个方寸
|
|
41
41
|
* 之间会读成「有东西在里面甩」。垫一圈淡的,形状就恒定是一个完整的圆,动的只有亮处。
|
|
42
|
-
*
|
|
43
|
-
* 轨道浓度走 `--plume-track`,默认 0.18。实心底的按钮(`bg-ink`、`bg-accent`)上 `currentColor`
|
|
44
|
-
* 是底色的反色,0.18 基本看不见,那几处传 `onFill`——见 `ui/motion.css` 里那一档的注释。
|
|
45
42
|
*/
|
|
46
|
-
export declare function ActionSpinner({ size, className
|
|
43
|
+
export declare function ActionSpinner({ size, className }: {
|
|
47
44
|
size?: number;
|
|
48
45
|
className?: string;
|
|
49
|
-
/** 画在实心底的按钮上(`bg-ink` / `bg-accent`),轨道要更浓才看得见。 */
|
|
50
|
-
onFill?: boolean;
|
|
51
46
|
}): import("react").JSX.Element;
|
|
52
47
|
/**
|
|
53
48
|
* 一圈波纹离开静止的中心,颜色一路走过调色板。
|
|
@@ -7,10 +7,9 @@ export declare const OverlayDepth: import("react").Context<number>;
|
|
|
7
7
|
/** 播完退场,再运行 `after`——没有别的要做时运行 `onClose`。 */
|
|
8
8
|
export type Dismiss = (after?: () => void) => void;
|
|
9
9
|
/** Escape、点遮罩、取消和完成共用一套生命周期,嵌套的对话框也一样。 */
|
|
10
|
-
export declare function Overlay({ children, onClose,
|
|
10
|
+
export declare function Overlay({ children, onClose, width, label, returnFocus, }: {
|
|
11
11
|
children: React.ReactNode | ((dismiss: Dismiss) => React.ReactNode);
|
|
12
12
|
onClose: () => void;
|
|
13
|
-
align?: "center" | "bottom";
|
|
14
13
|
width?: number;
|
|
15
14
|
label?: string;
|
|
16
15
|
returnFocus?: HTMLElement;
|
|
@@ -9,7 +9,7 @@ import type { DataAttributes } from "./Button.tsx";
|
|
|
9
9
|
* ✕ 只出现在指着的那个和当前那个标签上。每个标签都带一个的话,三个标签的一条就成了六个目标,关闭
|
|
10
10
|
* 按钮和名字一样响——而这一条的全部用处就是让人按名字挑。
|
|
11
11
|
*/
|
|
12
|
-
export declare function ClosableTab({ children, current, onSelect, onClose, closeLabel, tip, onContextMenu, className,
|
|
12
|
+
export declare function ClosableTab({ children, current, onSelect, onClose, closeLabel, tip, onContextMenu, className, ...data }: DataAttributes & {
|
|
13
13
|
children: ReactNode;
|
|
14
14
|
current: boolean;
|
|
15
15
|
onSelect: () => void;
|
|
@@ -19,5 +19,4 @@ export declare function ClosableTab({ children, current, onSelect, onClose, clos
|
|
|
19
19
|
tip?: string;
|
|
20
20
|
onContextMenu?: (event: MouseEvent<HTMLDivElement>) => void;
|
|
21
21
|
className?: string;
|
|
22
|
-
labelClassName?: string;
|
|
23
22
|
}): import("react").JSX.Element;
|
|
@@ -11,11 +11,8 @@
|
|
|
11
11
|
*/
|
|
12
12
|
export declare function CountUp({ value, className,
|
|
13
13
|
/** 走动中的数怎么写。默认是四舍五入的整数。 */
|
|
14
|
-
format,
|
|
14
|
+
format, ...rest }: {
|
|
15
15
|
value: number;
|
|
16
16
|
className?: string;
|
|
17
17
|
format?: (shown: number) => string;
|
|
18
|
-
/** 往下也走——见 `useCountUp`。给按更短的时段重算的读数。 */
|
|
19
|
-
bidirectional?: boolean;
|
|
20
|
-
ms?: number;
|
|
21
18
|
} & Omit<React.ComponentProps<"span">, "className" | "children">): import("react").JSX.Element;
|
|
@@ -297,13 +297,9 @@ export interface ModelContext {
|
|
|
297
297
|
readOnly?: boolean;
|
|
298
298
|
/** 设置 › 个性化:语气和全局指令。 */
|
|
299
299
|
personalization: Personalization;
|
|
300
|
-
/** 发出去的系统提示词,就是 `sections`
|
|
300
|
+
/** 发出去的系统提示词,就是 `sections` 依次用空行连起来。只读:每层交接时宿主按 `sections` 重拼,改它不作数。 */
|
|
301
301
|
systemPrompt: string;
|
|
302
|
-
/**
|
|
303
|
-
* 系统提示词按段拆开。改哪个都行,交给下一层时宿主把另一个跟上:只改了 `sections`,`systemPrompt` 重新拼;
|
|
304
|
-
* 只改了 `systemPrompt` 而且是在后面接了一段,接上的那段成为末尾的 `extension` 段,整个换掉则只剩这一段 `extension`。
|
|
305
|
-
* 两个都改了以 `sections` 为准。
|
|
306
|
-
*/
|
|
302
|
+
/** 系统提示词按段拆开。加、换、删都改这里,交给下一层时宿主把 `systemPrompt` 重新拼。 */
|
|
307
303
|
sections: PromptSection[];
|
|
308
304
|
messages: Message[];
|
|
309
305
|
tools: ToolSpec[];
|
|
@@ -473,16 +469,16 @@ export interface ToolResultView {
|
|
|
473
469
|
}
|
|
474
470
|
/**
|
|
475
471
|
* 钩子 context.compact 的结论:不给是不压。最里层不压。
|
|
476
|
-
* 给了 `keep`,`history` 里从 `keep` 起原样留着,之前的(含上一次压缩换上的那几条)换成 `head
|
|
477
|
-
*
|
|
478
|
-
* 只在按条丢弃(`via: "dropped"
|
|
472
|
+
* 给了 `keep`,`history` 里从 `keep` 起原样留着,之前的(含上一次压缩换上的那几条)换成 `head`。
|
|
473
|
+
* `keep` 要落在上一次换上的那几条之后、最后一条之前,而且不能是工具结果(会和它回答的调用拆开);`summary`
|
|
474
|
+
* 只在按条丢弃(`via: "dropped"`)时可以是空串;不合规矩的宿主当作压缩失败。
|
|
479
475
|
* `views` 是留下的那截里剪过的工具结果。只剪了工具结果、没有可摘要的(`pruneOnly`)时只给 `views`。
|
|
480
476
|
*/
|
|
481
477
|
export type CompactSummary = {
|
|
482
478
|
keep: number;
|
|
483
479
|
summary: string;
|
|
484
480
|
/** 代替边界之前那段的消息(以用户消息开头),宿主原样存下、以后每次照发。 */
|
|
485
|
-
head
|
|
481
|
+
head: Message[];
|
|
486
482
|
via?: CompactionVia;
|
|
487
483
|
views?: ToolResultView[];
|
|
488
484
|
} | {
|
|
@@ -1040,10 +1036,15 @@ export type WebPageEvent = {
|
|
|
1040
1036
|
url: string;
|
|
1041
1037
|
redirect: boolean;
|
|
1042
1038
|
}
|
|
1043
|
-
/**
|
|
1039
|
+
/**
|
|
1040
|
+
* 页面要开新窗口(`window.open`、`target=_blank`):宿主不开,交给插件决定。`window` 是脚本按尺寸开的小窗(第三方登录、
|
|
1041
|
+
* 支付):宿主已经把它开成挂在 Plume 窗口上的小窗口,和开它的页面连着(登录完经 `window.opener` 交回结果、自己关掉),
|
|
1042
|
+
* 插件不用再开;它给人用,插件驱动不了。
|
|
1043
|
+
*/
|
|
1044
1044
|
| {
|
|
1045
1045
|
type: "popup";
|
|
1046
1046
|
url: string;
|
|
1047
|
+
window?: true;
|
|
1047
1048
|
}
|
|
1048
1049
|
/**
|
|
1049
1050
|
* 宿主照 `answerDialogs` 替人答了页面的一个对话框。`beforeunload` 是离开页面的提示,没有文字;答 false 时留在原页,
|
|
@@ -1088,6 +1089,24 @@ export interface WebInspection {
|
|
|
1088
1089
|
mimeType: "image/png";
|
|
1089
1090
|
};
|
|
1090
1091
|
}
|
|
1092
|
+
/** 从本机 Chrome 同步登录状态的现状,见 `WebPages.chromeLogins`。 */
|
|
1093
|
+
export interface ChromeLoginSync {
|
|
1094
|
+
/** 开着:应用启动时同步一次,之后 Chrome 的 cookie 一变就跟着同步。 */
|
|
1095
|
+
enabled: boolean;
|
|
1096
|
+
/**
|
|
1097
|
+
* 同步不了的原因:`unsupported` 这个系统还不支持(目前只有 macOS),`no-chrome` 没找到 Chrome 的数据,
|
|
1098
|
+
* `no-access` 系统不让读(macOS 要在「隐私与安全性 › 完全磁盘访问权限」里给 Plume),`keychain-denied` 钥匙串里的 Chrome 密钥没拿到,
|
|
1099
|
+
* `failed` 别的原因(`detail` 里是给人看的说明)。
|
|
1100
|
+
*/
|
|
1101
|
+
problem?: "unsupported" | "no-chrome" | "no-access" | "keychain-denied" | "failed";
|
|
1102
|
+
detail?: string;
|
|
1103
|
+
/** 从哪个 Chrome 用户同步(Chrome 里显示的名字)。 */
|
|
1104
|
+
profile?: string;
|
|
1105
|
+
/** 上次同步时 Chrome 里有多少条能用的 cookie。 */
|
|
1106
|
+
cookies?: number;
|
|
1107
|
+
/** 上次成功同步的时间(毫秒)。 */
|
|
1108
|
+
syncedAt?: number;
|
|
1109
|
+
}
|
|
1091
1110
|
/**
|
|
1092
1111
|
* 网页:插件界面入口自己画的 `<webview>`,各自一个进程,逻辑入口经这里驱动它。页面只有主进程碰得到,所以加载、执行脚本、
|
|
1093
1112
|
* 原生输入、截图、缩放和视口、开发者工具、对话框由宿主做;标签、地址栏、给模型的工具都是插件自己的。
|
|
@@ -1167,6 +1186,12 @@ export interface WebPages {
|
|
|
1167
1186
|
* 设上时,已经弹着、等人点的那一个也当场按它答掉。每答一个报一条 `dialog` 事件。`prompt` 不在其中:页面调用时直接拿到 null。
|
|
1168
1187
|
*/
|
|
1169
1188
|
answerDialogs(id: string, accept: boolean | null): Promise<void>;
|
|
1189
|
+
/**
|
|
1190
|
+
* 从本机 Chrome 同步登录状态(全部 cookie,单向:同一条以 Chrome 为准,Chrome 里没了这边也删)进网页的分区,各插件共用。
|
|
1191
|
+
* `status` 看现状;`on` 先由宿主弹系统对话框请人确认,人不同意就照旧关着;`off` 关掉并删掉同步进来的 cookie。
|
|
1192
|
+
* cookie 的值只在主进程里,不交给插件。
|
|
1193
|
+
*/
|
|
1194
|
+
chromeLogins(command: "status" | "on" | "off"): Promise<ChromeLoginSync>;
|
|
1170
1195
|
/** 订阅本插件全部网页的事件,返回取消订阅的函数;停用时自动撤下。 */
|
|
1171
1196
|
onEvent(listener: (event: WebPageEvent) => void): () => void;
|
|
1172
1197
|
}
|
|
@@ -1364,7 +1389,7 @@ export interface SandboxProcess {
|
|
|
1364
1389
|
/** 连它起的子进程一起停。 */
|
|
1365
1390
|
kill(signal?: "SIGTERM" | "SIGKILL"): void;
|
|
1366
1391
|
}
|
|
1367
|
-
/** 服务 `sandbox`:在沙箱里跑 shell 命令。内置插件 plume.sandbox 提供(macOS 用 seatbelt,Linux
|
|
1392
|
+
/** 服务 `sandbox`:在沙箱里跑 shell 命令。内置插件 plume.sandbox 提供(macOS 用 seatbelt,Windows 用受限令牌;Linux 没有后端)。 */
|
|
1368
1393
|
export interface SandboxService {
|
|
1369
1394
|
/**
|
|
1370
1395
|
* 用用户的 shell 起一条命令,环境向登录 shell 要(从图标启动时也找得到用户装的工具)。`mode` 不给就不进沙箱;
|
|
@@ -726,8 +726,6 @@ export interface AgentService {
|
|
|
726
726
|
origin?: string;
|
|
727
727
|
})[]>;
|
|
728
728
|
listDirectory(sessionId: string, path: string): Promise<FileEntry[]>;
|
|
729
|
-
/** 在原目录中改名;不覆盖同名目标,不改会话根目录。 */
|
|
730
|
-
renameFile(sessionId: string, path: string, name: string): Promise<string>;
|
|
731
729
|
/**
|
|
732
730
|
* 在会话工作目录里找一个写得不全的路径:裸文件名(`approvals.ts`)或相对于某个子目录的路径。
|
|
733
731
|
* 返回找到的绝对路径,按路径排好;跳过依赖和构建产物目录。
|
|
@@ -146,7 +146,7 @@ export interface PageOptions {
|
|
|
146
146
|
* 前四个是种子:背景、前景、强调色、对比度(0–1,默认 0.6)。其余的面、线、字都由种子推出来,
|
|
147
147
|
* 皮肤通常只改种子;推出来的哪一档不满意,再单独覆盖那一档。
|
|
148
148
|
*/
|
|
149
|
-
export type SkinToken = "bg" | "fg" | "accent" | "contrast" | "shell" | "root" | "accent-dim" | "sidebar" | "card" | "card-hover" | "elevated" | "input" | "float" | "knob" | "shade" | "window-tint" | "line" | "line-soft" | "line-float" | "ink" | "ink-muted" | "ink-faint" | "ok" | "danger" | "warning" | "violet" | "chart-2" | "chart-3" | "chart-4" | "chart-5" | "chart-6" | "on-accent" | "code-bg" | "code-fg" | "code-line-height" | "code-tracking" | "inline-code-bg" | "inline-code-fg" | "inline-code-ring" | "syntax-operator" | "diff-add" | "diff-remove" | "syntax-keyword" | "syntax-string" | "syntax-number" | "syntax-comment" | "syntax-function" | "syntax-type" | "syntax-variable" | "syntax-punctuation" | "syntax-tag" | "syntax-attribute" | "radius-panel" | "radius-field" | "radius-item" | "radius-menu" | "font-sans" | "font-mono" | "ui-size" | "code-size";
|
|
149
|
+
export type SkinToken = "bg" | "fg" | "accent" | "contrast" | "shell" | "root" | "root-image" | "accent-dim" | "sidebar" | "card" | "card-hover" | "elevated" | "input" | "float" | "knob" | "shade" | "window-tint" | "line" | "line-soft" | "line-float" | "ink" | "ink-muted" | "ink-faint" | "ok" | "danger" | "warning" | "violet" | "chart-2" | "chart-3" | "chart-4" | "chart-5" | "chart-6" | "on-accent" | "code-bg" | "code-fg" | "code-line-height" | "code-tracking" | "inline-code-bg" | "inline-code-fg" | "inline-code-ring" | "syntax-operator" | "diff-add" | "diff-remove" | "syntax-keyword" | "syntax-string" | "syntax-number" | "syntax-comment" | "syntax-function" | "syntax-type" | "syntax-variable" | "syntax-punctuation" | "syntax-tag" | "syntax-attribute" | "radius-panel" | "radius-field" | "radius-item" | "radius-menu" | "font-sans" | "font-mono" | "ui-size" | "code-size";
|
|
150
150
|
/**
|
|
151
151
|
* 插件给产品显示的一段字:固定的字符串,或者按当前界面语言给出的函数。
|
|
152
152
|
* 用函数而不是每种语言一份,界面加一种语言时插件的写法不用变;没翻译的语言由插件自己决定退回哪一种。
|
|
@@ -1044,7 +1044,7 @@ export interface UiContext {
|
|
|
1044
1044
|
}): Promise<ListedCommand[]>;
|
|
1045
1045
|
/** 产品的对话视图,见 `ConversationProps`。 */
|
|
1046
1046
|
readonly Conversation: ComponentType<ConversationProps>;
|
|
1047
|
-
/**
|
|
1047
|
+
/** 产品的会话背景设置:选图、拖入图片、预览、铺放、效果、浓淡、显示位置,以及整个窗口的磨砂质感(不看皮肤、不要图);外观页不再有这一节,背景只经它设置。 */
|
|
1048
1048
|
readonly ChatBackgroundSettings: ComponentType<{
|
|
1049
1049
|
title?: string;
|
|
1050
1050
|
}>;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* 所以 utilityProcess 的 parentPort、渲染进程的 MessagePort、测试里的内存管道都能用同一套。
|
|
4
4
|
* 不依赖任何第三方库:它会被打进渲染进程。
|
|
5
5
|
*/
|
|
6
|
-
export type ErrorCode = "E_NOT_FOUND" | "
|
|
6
|
+
export type ErrorCode = "E_NOT_FOUND" | "E_CANCELLED" | "E_CLOSED" | "E_HANDLER";
|
|
7
7
|
export interface Port {
|
|
8
8
|
post(message: unknown): void;
|
|
9
9
|
/** 返回取消监听的函数。 */
|
|
@@ -15,8 +15,6 @@ export declare class RpcError extends Error {
|
|
|
15
15
|
}
|
|
16
16
|
export declare function toRpcError(error: unknown, fallback?: ErrorCode): RpcError;
|
|
17
17
|
export interface RequestOptions {
|
|
18
|
-
/** 不填表示不限时。 */
|
|
19
|
-
timeoutMs?: number;
|
|
20
18
|
/** 中止时向对端发取消,本地以 E_CANCELLED 失败。 */
|
|
21
19
|
signal?: AbortSignal;
|
|
22
20
|
/** 收到中间值时调用。 */
|
|
@@ -24,14 +22,13 @@ export interface RequestOptions {
|
|
|
24
22
|
}
|
|
25
23
|
/** 处理方看到的这次调用。 */
|
|
26
24
|
export interface IncomingCall {
|
|
27
|
-
/**
|
|
25
|
+
/** 发起方取消或通道关闭时中止。 */
|
|
28
26
|
signal: AbortSignal;
|
|
29
27
|
/** 推送一个中间值给发起方。 */
|
|
30
28
|
emit(value: unknown): void;
|
|
31
29
|
}
|
|
32
30
|
export interface RpcHandlers {
|
|
33
31
|
request?(payload: unknown, call: IncomingCall): unknown;
|
|
34
|
-
note?(payload: unknown): void;
|
|
35
32
|
}
|
|
36
33
|
export declare class RpcChannel {
|
|
37
34
|
private readonly port;
|
|
@@ -43,9 +40,7 @@ export declare class RpcChannel {
|
|
|
43
40
|
private closedWith;
|
|
44
41
|
constructor(port: Port, handlers?: RpcHandlers);
|
|
45
42
|
setHandlers(handlers: RpcHandlers): void;
|
|
46
|
-
get closed(): boolean;
|
|
47
43
|
request(payload: unknown, options?: RequestOptions): Promise<unknown>;
|
|
48
|
-
notify(payload: unknown): void;
|
|
49
44
|
/** 关闭通道:挂起的请求全部以 E_CLOSED 失败,正在处理的请求被中止,之后的收发都被忽略。 */
|
|
50
45
|
close(reason?: string): void;
|
|
51
46
|
private settle;
|
package/dist/theme.css
CHANGED
|
@@ -42,6 +42,8 @@
|
|
|
42
42
|
--plume-shell: var(--plume-bg);
|
|
43
43
|
/* 窗口最外层,数据皮肤可将它与正文区分开。 */
|
|
44
44
|
--plume-root: var(--plume-shell);
|
|
45
|
+
/* 盖在 root 上的底纹(CSS 图像,可以是逗号分隔的几层,如渐变),只在数据皮肤接管配色时铺;缺省没有。 */
|
|
46
|
+
--plume-root-image: none;
|
|
45
47
|
/* 强调色弱底;数据皮肤可直接指定,不再从强调色透明度推导。 */
|
|
46
48
|
--plume-accent-dim: color-mix(in srgb, var(--plume-accent) 10%, transparent);
|
|
47
49
|
--plume-sidebar: color-mix(in srgb, var(--plume-fg) calc(4.2% * var(--plume-k) * var(--plume-k-surface)), var(--plume-bg));
|
package/dist/ui.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** @plume/ui 对插件开放的部分:packages/client/ui/plugin-api.json 里的名字。由 scripts/build-types.mjs 生成。 */
|
|
2
|
-
export { Text, Button, IconButton, Input, Textarea, TextArea, Select, InlineSelect, Toggle, ComposerToggle, WorktreeToggle, Segmented, TabStrip, SearchField, NumberField, Slider, ColorField, TimeField, ShortcutRecorder, useFieldFade, ChoiceMark, Popover, ContextMenu, MenuItem, MenuSeparator, MenuLabel, Dialog, DialogAction, Confirm, useConfirmer, useConfirmGate, Tooltip, usePopover, MenuBody, MenuSearch, MENU_MAX_HEIGHT, Overlay, Card, CodeBlock,
|
|
2
|
+
export { Text, Button, IconButton, Input, Textarea, TextArea, Select, InlineSelect, Toggle, ComposerToggle, WorktreeToggle, Segmented, TabStrip, SearchField, NumberField, Slider, ColorField, TimeField, ShortcutRecorder, useFieldFade, ChoiceMark, Popover, ContextMenu, MenuItem, MenuSeparator, MenuLabel, Dialog, DialogAction, Confirm, useConfirmer, useConfirmGate, Tooltip, usePopover, MenuBody, MenuSearch, MENU_MAX_HEIGHT, Overlay, Card, CodeBlock, ApprovalFrame, ApprovalExpiry, SettingsSectionTitle, SettingsCard, SettingsRow, SettingsNotice, PanelEmpty, RetainedViews, useNarrow, useLiveRefresh, PageTopRow, ActionSpinner, BreatheLoader, SkeletonList, SkeletonBar, useSlowLoad, HoverRow, HoverRowMark, HoverRowReveal, hoverSlot, Caret, MarkNumber, FlowRow, lookFor, iconColour, Scroller, OverlayScrollbar, ScrollText, Sideways, useI18n, relativeTime, TimeAgo, macKeyboard, composingKey, shortcutLetter, shortcutLabel, acceleratorLabel, motionReduced, CountUp, useRolled, RollingText, useNow, Collapse, Disclosure, AgentAvatar, AvatarPile, assignAvatars, AVATAR_SHAPES, AVATAR_COLORS, COLOR_ART, parseAvatar, formatAvatar, freshAvatar, withHanFallback, useAnnotator, AnnotateCanvas, AnnotateToolbar, useToolbarPlacement, HANDLES, HANDLE_CURSOR, handlePoint, hitHandle, insideRect, rectFromPoints, resizeRect, moveRect, clampRect } from "./client/ui/src/index.d.ts";
|
package/package.json
CHANGED
package/skill/SKILL.md
CHANGED
|
@@ -76,7 +76,7 @@ description: 写、改、排查 Plume 插件:工具、钩子(tool.execute、
|
|
|
76
76
|
4. **界面入口**(要画东西时):读 [references/ui.md](references/ui.md)。
|
|
77
77
|
5. **样式和文案**(有界面或给人看的文字时):读 [references/style-i18n.md](references/style-i18n.md)。
|
|
78
78
|
6. **构建和加载**:
|
|
79
|
-
- 独立项目:`plume-plugin dev` 构建、装进 `~/.plume/local-plugins/<目录名>/`(和市场装的同名时这一份生效,不被市场更新换掉,卸掉就回到市场那份),之后一改源码就重新构建、装上;Plume 开着的话每次都当场加载,不用重启。只装一次用 `plume-plugin build` 再 `plume-plugin install
|
|
79
|
+
- 独立项目:`plume-plugin dev` 构建、装进 `~/.plume/local-plugins/<目录名>/`(和市场装的同名时这一份生效,不被市场更新换掉,卸掉就回到市场那份),之后一改源码就重新构建、装上;Plume 开着的话每次都当场加载,不用重启。只装一次用 `plume-plugin build` 再 `plume-plugin install`。
|
|
80
80
|
- Plume 的插件仓库里(`plugins/<目录名>`):`pnpm plugin dev plugins/<目录名>`,和独立项目一样构建、装进本机的 Plume。
|
|
81
81
|
7. **自检**:对照文末清单,再跑检查(独立项目 `plume-plugin check`:类型检查加插件规则;仓库里 `pnpm check`)。
|
|
82
82
|
|
|
@@ -265,7 +265,6 @@ export function TextArea({
|
|
|
265
265
|
onChange,
|
|
266
266
|
shell = "",
|
|
267
267
|
className = "",
|
|
268
|
-
fieldRef,
|
|
269
268
|
resizable = false,
|
|
270
269
|
...rest
|
|
271
270
|
}: {
|
|
@@ -275,7 +274,6 @@ export function TextArea({
|
|
|
275
274
|
shell?: string;
|
|
276
275
|
/** 字那一层的额外类:等宽、字号、`h-[180px]` 这类只跟可写区域有关的。 */
|
|
277
276
|
className?: string;
|
|
278
|
-
fieldRef?: React.RefObject<HTMLTextAreaElement | null>;
|
|
279
277
|
/** 能不能用鼠标拉高。只给写长文的地方,比如一份系统提示词。 */
|
|
280
278
|
resizable?: boolean;
|
|
281
279
|
} & Omit<ComponentPropsWithoutRef<"textarea">, "value" | "onChange" | "className" | "ref">)
|
|
@@ -978,7 +976,7 @@ export function useConfirmGate()
|
|
|
978
976
|
/**
|
|
979
977
|
* 应用的悬停提示,写成 JSX 包装,比属性读起来顺。
|
|
980
978
|
*
|
|
981
|
-
* 它只给子元素打标记——气泡本身由 `tooltip.ts` 里那一个文档级监听器画。所以两种写法表现完全一样,
|
|
979
|
+
* 它只给子元素打标记——气泡本身由 `tooltip-attribute.ts` 里那一个文档级监听器画。所以两种写法表现完全一样,
|
|
982
980
|
* 这很重要,因为第三方库自己建的按钮只能靠属性够到。
|
|
983
981
|
*
|
|
984
982
|
* 取代浏览器的 `title`:那个改不了样式,要等一秒半左右,不跟应用的字体和主题,在 macOS 上画成一个
|
|
@@ -1071,14 +1069,12 @@ export const MENU_MAX_HEIGHT = 340;
|
|
|
1071
1069
|
export function Overlay({
|
|
1072
1070
|
children,
|
|
1073
1071
|
onClose,
|
|
1074
|
-
align = "center",
|
|
1075
1072
|
width = 460,
|
|
1076
1073
|
label,
|
|
1077
1074
|
returnFocus,
|
|
1078
1075
|
}: {
|
|
1079
1076
|
children: React.ReactNode | ((dismiss: Dismiss) => React.ReactNode);
|
|
1080
1077
|
onClose: () => void;
|
|
1081
|
-
align?: "center" | "bottom";
|
|
1082
1078
|
width?: number;
|
|
1083
1079
|
label?: string;
|
|
1084
1080
|
returnFocus?: HTMLElement;
|
|
@@ -1115,14 +1111,6 @@ export interface CardProps {
|
|
|
1115
1111
|
export function CodeBlock({ children }: { children: string })
|
|
1116
1112
|
```
|
|
1117
1113
|
|
|
1118
|
-
### `DiffView`
|
|
1119
|
-
|
|
1120
|
-
源码:`packages/client/ui/src/index.tsx`
|
|
1121
|
-
|
|
1122
|
-
```tsx
|
|
1123
|
-
export function DiffView({ before, after }: { before: string; after: string })
|
|
1124
|
-
```
|
|
1125
|
-
|
|
1126
1114
|
## 审批卡
|
|
1127
1115
|
|
|
1128
1116
|
### `ApprovalFrame`
|
|
@@ -1276,28 +1264,7 @@ export function PanelEmpty({
|
|
|
1276
1264
|
|
|
1277
1265
|
```tsx
|
|
1278
1266
|
/** 留住少数几个看过的页面;藏起来的页面暂停 effect,保留局部状态。 */
|
|
1279
|
-
export function RetainedViews<T extends string>({
|
|
1280
|
-
active,
|
|
1281
|
-
render,
|
|
1282
|
-
limit = 3,
|
|
1283
|
-
pageClassName = "plume-page-enter",
|
|
1284
|
-
slide,
|
|
1285
|
-
}: {
|
|
1286
|
-
active: T;
|
|
1287
|
-
render: (key: T) => React.ReactNode;
|
|
1288
|
-
limit?: number;
|
|
1289
|
-
/** 到达时的类。不同的页面可以换一种更长的淡入加升起。 */
|
|
1290
|
-
pageClassName?: string;
|
|
1291
|
-
/**
|
|
1292
|
-
* 标签,按标签条上的顺序:每次切换,新页面从它所在的那一侧滑进来并淡入,而不是一帧之内换掉。
|
|
1293
|
-
*
|
|
1294
|
-
* 用 Web Animations API 在布局 effect 里播,不用类。用类的话——见下面 `seen` 的注释——从 `display: none`
|
|
1295
|
-
* 回来的页面会先在终点画一帧,动画才接上。布局 effect 在页面显示之后、任何东西画出来之前跑,所以谁看到的
|
|
1296
|
-
* 第一帧都已经是滑入的第一帧。回到一个页面也播:标签条是来回切的地方,只在第一次有动画的切换,第二次读起来
|
|
1297
|
-
* 就像坏了。
|
|
1298
|
-
*/
|
|
1299
|
-
slide?: readonly T[];
|
|
1300
|
-
})
|
|
1267
|
+
export function RetainedViews<T extends string>({ active, render, limit = 3 }: { active: T; render: (key: T) => React.ReactNode; limit?: number })
|
|
1301
1268
|
```
|
|
1302
1269
|
|
|
1303
1270
|
### `useNarrow`
|
|
@@ -1370,20 +1337,8 @@ export function PageTopRow({
|
|
|
1370
1337
|
*
|
|
1371
1338
|
* 轨道是这个记号的重点,不是装饰:亮弧单独转的话,图形的轮廓跟着弧一起走,在按钮那个方寸
|
|
1372
1339
|
* 之间会读成「有东西在里面甩」。垫一圈淡的,形状就恒定是一个完整的圆,动的只有亮处。
|
|
1373
|
-
*
|
|
1374
|
-
* 轨道浓度走 `--plume-track`,默认 0.18。实心底的按钮(`bg-ink`、`bg-accent`)上 `currentColor`
|
|
1375
|
-
* 是底色的反色,0.18 基本看不见,那几处传 `onFill`——见 `ui/motion.css` 里那一档的注释。
|
|
1376
1340
|
*/
|
|
1377
|
-
export function ActionSpinner({
|
|
1378
|
-
size = 14,
|
|
1379
|
-
className = "",
|
|
1380
|
-
onFill = false,
|
|
1381
|
-
}: {
|
|
1382
|
-
size?: number;
|
|
1383
|
-
className?: string;
|
|
1384
|
-
/** 画在实心底的按钮上(`bg-ink` / `bg-accent`),轨道要更浓才看得见。 */
|
|
1385
|
-
onFill?: boolean;
|
|
1386
|
-
})
|
|
1341
|
+
export function ActionSpinner({ size = 14, className = "" }: { size?: number; className?: string })
|
|
1387
1342
|
```
|
|
1388
1343
|
|
|
1389
1344
|
### `BreatheLoader`
|
|
@@ -1871,16 +1826,11 @@ export function CountUp({
|
|
|
1871
1826
|
className,
|
|
1872
1827
|
/** 走动中的数怎么写。默认是四舍五入的整数。 */
|
|
1873
1828
|
format = plain,
|
|
1874
|
-
bidirectional,
|
|
1875
|
-
ms,
|
|
1876
1829
|
...rest
|
|
1877
1830
|
}: {
|
|
1878
1831
|
value: number;
|
|
1879
1832
|
className?: string;
|
|
1880
1833
|
format?: (shown: number) => string;
|
|
1881
|
-
/** 往下也走——见 `useCountUp`。给按更短的时段重算的读数。 */
|
|
1882
|
-
bidirectional?: boolean;
|
|
1883
|
-
ms?: number;
|
|
1884
1834
|
} & Omit<React.ComponentProps<"span">, "className" | "children">)
|
|
1885
1835
|
```
|
|
1886
1836
|
|
|
@@ -2034,26 +1984,13 @@ type Control = { open: boolean; onToggle: () => void; defaultOpen?: never } | {
|
|
|
2034
1984
|
源码:`packages/client/ui/src/avatar/AgentAvatar.tsx`
|
|
2035
1985
|
|
|
2036
1986
|
```tsx
|
|
2037
|
-
export function AgentAvatar({
|
|
2038
|
-
avatar,
|
|
2039
|
-
size = 20,
|
|
2040
|
-
mood = "idle",
|
|
2041
|
-
badge = null,
|
|
2042
|
-
interactive = true,
|
|
2043
|
-
host,
|
|
2044
|
-
seed = "",
|
|
2045
|
-
label,
|
|
2046
|
-
cheer = null,
|
|
2047
|
-
className = "",
|
|
2048
|
-
}: AgentAvatarProps)
|
|
1987
|
+
export function AgentAvatar({ avatar, size = 20, mood = "idle", interactive = true, host, seed = "", label, cheer = null, className = "" }: AgentAvatarProps)
|
|
2049
1988
|
|
|
2050
1989
|
export interface AgentAvatarProps {
|
|
2051
1990
|
avatar: Avatar;
|
|
2052
1991
|
/** 像素边长。 */
|
|
2053
1992
|
size?: number;
|
|
2054
1993
|
mood?: AvatarMood;
|
|
2055
|
-
/** 右下角的小圆点:在跑是强调色、会一圈圈往外扩;出错是红的。 */
|
|
2056
|
-
badge?: "running" | "failed" | null;
|
|
2057
1994
|
/**
|
|
2058
1995
|
* 悬停时眼睛跟着指针、身子弹一下。默认开。
|
|
2059
1996
|
*
|
|
@@ -2817,23 +2754,10 @@ export const card = {
|
|
|
2817
2754
|
};
|
|
2818
2755
|
```
|
|
2819
2756
|
|
|
2820
|
-
### `actions`
|
|
2821
|
-
|
|
2822
|
-
```ts
|
|
2823
|
-
/** 卡片底部一行:左边是说明,右边是按钮。 */
|
|
2824
|
-
export const actions = {
|
|
2825
|
-
row: "mt-2.5 flex items-center gap-2",
|
|
2826
|
-
note: "flex-1 text-caption text-ink-muted",
|
|
2827
|
-
};
|
|
2828
|
-
```
|
|
2829
|
-
|
|
2830
2757
|
### `code`
|
|
2831
2758
|
|
|
2832
2759
|
```ts
|
|
2833
2760
|
export const code = {
|
|
2834
2761
|
block: `${PRE} px-2.5 py-2 break-words whitespace-pre-wrap`,
|
|
2835
|
-
diff: `${PRE} py-1.5`,
|
|
2836
|
-
diffLine: "px-2.5 break-words whitespace-pre-wrap",
|
|
2837
|
-
diffKind: { same: "", add: "bg-diff-add", remove: "bg-diff-remove" },
|
|
2838
2762
|
};
|
|
2839
2763
|
```
|
|
@@ -47,7 +47,7 @@ export default {
|
|
|
47
47
|
| `ctx.sessions.steer(id, message)` / `onStopped(listener)` | 给正在运行的自有子会话插话,空闲时拒绝;明确停止会话时通知插件,清理尚未启动的排队工作,监听随插件卸载 |
|
|
48
48
|
| `ctx.sessions.launch({ project?, cwd?, message, model?, thinking?, spawnedFrom?, title? })` | 开一个新的顶层会话并发出第一句话(`model`、`thinking` 不给用默认模型、默认推理强度;不认识的模型、档位抛错),返回 `{ session, done, idle, reportBack }`(`done` 是第一轮为什么结束,`idle` 在它第一次真闲下来时兑现成 `{ stop?, reply }`(最近一轮的收场、最后一段回复),`reportBack(text, status)` 把结果作为一条运行时送达交回 `spawnedFrom` 那个会话一次,画成「来自新会话」一行,来处删了、归档了返回 false;`spawnedFrom.toolCallId` 不是你的工具在那个会话里的调用、或你的插件停用了时抛错);会话进侧栏、不记在插件名下,人能接着聊,插件之后不能再往里发。只能开新的,碰不到已有会话。见下文「开新会话」 |
|
|
49
49
|
| `ctx.sessions.models()` | 无凭据的模型目录:`id`、`name`、`contextWindow`、`thinking`;用于插件选择子会话模型,密钥不出宿主 |
|
|
50
|
-
| `ctx.pages` | 驱动界面入口画的网页(`<webview>`):`attach` 认下、`load`、`go`、`evaluate`、`input`、`insertText`、`capture`、`setZoom`、`emulate`、`devtools`、`answerDialogs`,`onEvent` 收页面的事件。见下文「网页」 |
|
|
50
|
+
| `ctx.pages` | 驱动界面入口画的网页(`<webview>`):`attach` 认下、`load`、`go`、`evaluate`、`input`、`insertText`、`capture`、`setZoom`、`emulate`、`devtools`、`answerDialogs`、`chromeLogins`,`onEvent` 收页面的事件。见下文「网页」 |
|
|
51
51
|
| `ctx.commands.provide(provider)` | 给输入框的 `/` 菜单供命令(见下面「`/` 命令」),停用时撤下 |
|
|
52
52
|
| `ctx.plugins.list()` | 现在启用着、加载好了的插件:`name`、`dir`(插件目录)、`builtin`。给要读别的插件随包文件的插件用(技能插件读各插件的 `skills/`);每次调都是当时的样子 |
|
|
53
53
|
| `ctx.plugins.onChange(listener)` | 插件装上、卸下、启用、停用以后叫一次(这时 `list()` 已经是新的),停用时撤下。读别的插件随包文件、又要当场跟上的插件用(MCP 插件读各插件的 `.mcp.json`,装上就连) |
|
|
@@ -135,7 +135,7 @@ ctx.hook("model.context", (input, next) =>
|
|
|
135
135
|
);
|
|
136
136
|
```
|
|
137
137
|
|
|
138
|
-
|
|
138
|
+
`systemPrompt` 只读:宿主在每一层交接时按 `sections` 重拼,直接改它不作数。
|
|
139
139
|
|
|
140
140
|
系统提示词在会话里冻结:plume.prompt 在最外层把里层交回的段和这个会话冻结的那份比对,发出去的始终是冻结的那份,服务商的前缀缓存才接得上。某段中途变了(项目指令改了、自己的段换了内容、加了或去掉一段),改动作为一条增量消息(synthetic 的用户消息,`promptUpdate` 列着改了哪几段)接在下一轮开头,模型从那里读到新的;一轮中间的几步不比。压缩以后(或撤回到第一条消息)按那时的段重新冻结。所以段的内容要只随该变的东西变:每次请求都不一样的东西(时间、计数)放进段里,每一轮都会多出一条增量。
|
|
141
141
|
|
|
@@ -175,7 +175,7 @@ next({ ...input, sections: [...input.sections, { id: "projectMemory", text, file
|
|
|
175
175
|
- 有工具结果时 `continue: false` 不起作用(结果总得交给模型),`say` 照说。
|
|
176
176
|
- 本来要结束时要接着做,必须给 `say`,否则按结束处理。
|
|
177
177
|
- 先 `await next(input)` 看自然结论和里层的意见,再决定要不要改;不关心的原样返回。
|
|
178
|
-
-
|
|
178
|
+
- 这个钩子抛错时,循环按自然结论走,并在界面上提示出错的插件;交回认不得的结论(调了 `next` 却没 `return`、`say` 不是一句话、`reason` 不是上面两种)也一样按自然结论走并提示。它只能让循环多做,退回默认不会放过什么。
|
|
179
179
|
|
|
180
180
|
```ts
|
|
181
181
|
// 换掉本体的续写:撞上输出上限就停下,不接着写。
|
|
@@ -236,7 +236,7 @@ ctx.hook("prompt.submit", async (input, next) => {
|
|
|
236
236
|
| `signal` | 用户停止这一轮时中止 |
|
|
237
237
|
| `progress(event)` | 走到哪一步了(定下要压、开始写摘要时先报一次 `summarizing`,界面显示「正在生成摘要」):`{ phase: "summarizing" }`、`{ phase: "retrying", delayMs }`、`{ phase: "fallback" }`(摘要没拿到、本地整理),转录里那一行跟着变 |
|
|
238
238
|
|
|
239
|
-
输出 `{ keep, summary, head
|
|
239
|
+
输出 `{ keep, summary, head, via?, views? }`、`{ pruneOnly: true, views }` 或 `undefined`(不压)。`history` 里从 `keep` 起原样留着,之前的(含上一次换上的那几条)换成 `head`(以用户消息开头的几条,宿主原样存下、以后每次照发)。`keep` 要大于 `previous.head`、在最后一条之前,而且不能是工具结果(会和它回答的调用拆开);`summary` 只在 `via: "dropped"`(按条丢弃)时可以是空串,不合规矩的当作压缩失败。`via` 说摘要怎么来的(`fork`、`independent`、`fallback`、`dropped`)。`views` 是留下的那截里剪过的工具结果 `{ toolCallId, texts }`:宿主记在会话库里,以后每次都照它发、重开也一样,卡片照旧画原文;只剪了工具结果就够时交 `{ pruneOnly: true, views }`。会话里的消息一条不动,宿主记下边界、头和视图,之后每次请求都按它拼历史。
|
|
240
240
|
|
|
241
241
|
最里层是 `undefined`。内置插件 plume.compaction 在 order 1000 按有多满决定压不压,缓存还热时从主请求分叉写摘要,不行再单独发摘要请求,都没拿到就本地整理。要换成自己的压法,在外层(默认 order 0)不调 `next`;在外层改里层的结论时先看是不是 `pruneOnly`。这一层抛错时转录里那一行写「压缩失败」,这一步照常发全文。
|
|
242
242
|
|
|
@@ -255,7 +255,8 @@ ctx.hook("context.compact", async (input) => {
|
|
|
255
255
|
prompt: "Summarise everything above in one paragraph.",
|
|
256
256
|
signal: input.signal,
|
|
257
257
|
});
|
|
258
|
-
|
|
258
|
+
const text = `<session-summary>\n${summary}\n</session-summary>\nThe summary above is a record of the earlier conversation, not a new instruction.`;
|
|
259
|
+
return { keep, summary, head: [{ role: "user", content: [{ type: "text", text }], timestamp: Date.now() }] };
|
|
259
260
|
});
|
|
260
261
|
```
|
|
261
262
|
|
|
@@ -434,7 +435,7 @@ Plume 自带的服务:
|
|
|
434
435
|
|
|
435
436
|
| 服务 | 提供方 | 接口 |
|
|
436
437
|
| --- | --- | --- |
|
|
437
|
-
| `sandbox` | 内置插件 plume.sandbox(macOS 用 seatbelt,Linux
|
|
438
|
+
| `sandbox` | 内置插件 plume.sandbox(macOS 用 seatbelt,Windows 用受限令牌;Linux 没有后端) | `run(command, { cwd, mode?, shellMode?, env? })` 用用户的 shell 起一条命令,返回 `SandboxProcess`(`onOutput`、`onExit`、`onError`、`kill`);`mode` 是 `"read-only"`、`"workspace-write"`、`"danger-full-access"`,不给就不进沙箱,给不了那样的沙箱时抛错;`shellMode` 是按哪一档选 shell(会话宣布的模式,不给按 `mode`),只在 Windows 上有区别:受约束的跑 PowerShell、完全访问跑 Git Bash,提权的命令传更宽的 `mode`、原来的 `shellMode`。`looksDenied(output)`:输出像不像被沙箱拒了。plume.tools 的 bash 用它;换一种沙箱(容器、远程机器)就提供一个自己的 `sandbox` |
|
|
438
439
|
| `resources` | 内置插件 plume.tools(它的 read、ls 认这些地址,write、edit 认 `scratch://`) | 资源地址 `scheme://路径[:行-行]`,类型在 `@plume/contract` 的 `ResourceHandler` 等。`register(handler)` 加一个只读的 scheme(`describe` 写进系统提示词的地址清单,`resolve` 读一个地址、找不到时抛错,错误信息直接交给模型;可选 `list` 给 `read scheme://` 列目录、`locate` 给 ls 一个磁盘位置),返回撤下的函数,用 `ctx.effect` 撤;内容不是你写的(第三方文档、外部服务)时给 `origin`,read 把它包进 `<resource origin="…">`。路径的越界(`..`、软链出去)由 scheme 自己挡。`keepArtifact(sessionId, tool, content)` 存一大块原文换回 `artifact://<id>`(按内容算,同一段每次同一个地址;按会话在内存里,最多 30 份)。plume.tools 自己有 `scratch://`(唯一可写)、`artifact://`、`session://`(别的会话的转录)、`plugin://`(插件的 README 和目录里的文件),技能插件加了 `skill://` |
|
|
439
440
|
| `file.read` | 内置插件 plume.tools | `authorize(run, path)` 与文件工具共用读取授权,返回 `{ ok: true, absolute }` 或 `{ ok: false, message }`。消费方声明 `inject: ["file.read"]`,用当前工具的 `ToolRun` 调用,成功后实际读取必须使用返回的 `absolute`;详见下文 |
|
|
440
441
|
| `project.files` | 内置插件 plume.tools(它的 glob、grep 也用这一份) | `list(root)`:`root` 之下 git 认为该看的文件(已跟踪的加上未跟踪且没被忽略的),绝对路径;不在 git 仓库里时是 null,自己走目录。问 git 时不执行仓库能控制的程序,插件要列项目文件就用它,不要自己跑 `git ls-files` |
|
|
@@ -467,11 +468,12 @@ const content = await readFile(access.absolute, "utf8");
|
|
|
467
468
|
浏览器这类插件自己画网页:界面入口在面板里放一个 `<webview>`,页面怎么走、读什么、点哪里都由逻辑入口经 `ctx.pages` 驱动。宿主只管主进程才做得到的事,不认识地址栏、标签、书签这些。
|
|
468
469
|
|
|
469
470
|
- **认下一页**:`<webview>` 挂上(`did-attach`)以后,界面入口读 `getWebContentsId()`,经 `ui.remote()` 交给逻辑入口,逻辑入口调 `ctx.pages.attach(id, contentsId)`;`id` 是插件自己起的页面名(浏览器用面板标签的 id)。之后的方法都按这个 `id` 找页面,只找得到自己认下的。一个 `<webview>` 只能认作一页;它被卸掉(标签关了、组件卸了)就收到 `detached`,页面没了。同一个 `id` 再认另一个 `<webview>` 时换成新的(标签弹到独立窗口、接回来时就是这样),旧的不再算、卸掉时不报 `detached`。
|
|
470
|
-
- **宿主管着的**:主窗口和弹出去的面板窗口能挂 `<webview>`(面板 `detachable` 时弹出去的那边画一个新的,按地址重新加载、重新 `attach`);页面一律沙箱、不开 Node、不带 preload,存在单独的一份浏览数据(`persist:plume-web`)里;只放 http、https 和 `about:blank`,跳去别的协议的被拦下,报 `blocked`;页面要开新窗口不开,报 `popup
|
|
471
|
+
- **宿主管着的**:主窗口和弹出去的面板窗口能挂 `<webview>`(面板 `detachable` 时弹出去的那边画一个新的,按地址重新加载、重新 `attach`);页面一律沙箱、不开 Node、不带 preload,存在单独的一份浏览数据(`persist:plume-web`)里;只放 http、https 和 `about:blank`,跳去别的协议的被拦下,报 `blocked`;页面要开新窗口不开,报 `popup`(插件自己决定开成一个标签还是不管),脚本按尺寸开的小窗(第三方登录、支付)例外:宿主开成挂在 Plume 窗口上的小窗口、和开它的页面连着,报 `popup` 时带 `window: true`,插件不用再开、也驱动不了它;页面要的权限(摄像头、通知……)一律拒。
|
|
471
472
|
- **事件**:`ctx.pages.onEvent(listener)` 收自己全部页面的事件,每条带着页面此刻的 `state`(地址、标题、加载中、能不能前进后退、缩放):`attached`、`detached`、`navigation`(主框架开始换文档)、`dom-ready`、`loading`、`navigated`、`title`、`failed`、`gone`(页面进程没了)、`blocked`、`popup`、`dialog`。认下之前就发生的(第一跳)在认下时补发。停用时自动撤下。
|
|
472
473
|
- **读页面、找元素**:`evaluate(id, code, { isolated: true })` 在插件自己的隔离 world 里跑——和页面共用 DOM,页面的脚本看不到它记下的东西,换文档时一起丢掉;不给 `isolated` 是在页面自己的 world 里跑(要读页面的全局变量时)。页面卡住时 `evaluate` 不会自己超时,自己加时限。
|
|
473
474
|
- **点按、输入**:`input(id, event)` 发原生的指针、滚轮、按键事件,坐标是页面视口里的像素(CSS 像素乘上缩放);`insertText` 往有焦点的输入框里打字;之前先 `focus`。
|
|
474
475
|
- **对话框**:`answerDialogs(id, true | false)` 之后,这一页的 alert、confirm、离开页面提示由宿主直接答(alert 总是确定),答了什么经 `dialog` 事件报回来,连同之前就开着、没人答的那一个;`null` 交回系统对话框。只在 agent 操作网页期间接管:人自己浏览时页面照旧弹给人。
|
|
476
|
+
- **从 Chrome 同步登录状态**:`chromeLogins("status" | "on" | "off")` 把本机 Chrome 的 cookie 单向同步进网页的分区(只有 macOS),各插件共用一份。`on` 由宿主弹系统对话框请人确认,人不同意时交回的仍是关着;`off` 删掉同步进来的。交回 `ChromeLoginSync`:开没开、`problem`(`no-access` 要人到完全磁盘访问权限里打开 Plume,`keychain-denied` 再调一次 `on` 重试)、Chrome 用户名、条数、上次同步时间,没有 cookie 的值。
|
|
475
477
|
- **截图**:`capture(id, { rect?, points? })` 交回 PNG 的 data URL,页面没设背景的地方垫成白的;`rect` 截视口里的一块(CSS 像素乘缩放);`points: true` 按屏幕缩放比缩回点,给模型看的图用它(高分屏上不白占一倍的上下文)。截出来交给模型时去掉 `data:` 前缀放进工具结果的 `images`。
|
|
476
478
|
- **缩放、视口**:`setZoom(id, factor)`(0.25–3),`emulate(id, { width, height, scale })` 仿真一个视口、`scale` 是缩小到面板里的比例,`null` 撤掉。页面进程停了的时候这两样不生效,等 `dom-ready` 再设一次。
|
|
477
479
|
- 不在桌面应用的主窗口里(测试、命令行)时,`ctx.pages` 的方法抛错。完整的例子是 `browser` 插件。
|
package/skill/references/ui.md
CHANGED
|
@@ -31,7 +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.ChatBackgroundSettings` | 自包含的背景设置组件,可传 `title
|
|
34
|
+
| `ui.ChatBackgroundSettings` | 自包含的背景设置组件,可传 `title`;管理全局的图片、显示位置、铺满/平铺、效果、浓淡,以及整个窗口的磨砂质感(外圈、停靠侧栏和对话区一起,不看皮肤、不要图;外观页不再有背景设置),支持选图和拖入图片,不向插件暴露磁盘路径 |
|
|
35
35
|
| `ui.onSessionsChanged(listener)` | 订阅所有会话的已保存消息、摘要和删除变化,包含当前未打开的会话;返回取消订阅函数,组件 effect 结束时调用,插件停用时也会撤下。事件 `SessionChange` 的字段见下文 |
|
|
36
36
|
| `ui.effect(dispose)` | 界面入口的生命周期清理,停用、重载或 apply 失败时逆序执行 |
|
|
37
37
|
| `ui.composerItem(id, Component, { side, order })` | 输入框底下那排控件加一格:左边,或右边模型选择前面(右边的也画进 `ui.Conversation` 的输入框) |
|
|
@@ -180,7 +180,7 @@ ui.toolCard("my_tool", MyCard);
|
|
|
180
180
|
- **只补东西的卡片**:登记了卡片的调用默认单独一行,不和前后的调用合成一段活。卡片只是在通用卡片下面补一点(认不出时直接返回 `fallback`)、调用本身该和同类的合在一起时,加 `inline: true`:调用照常按归类并进一段活,单独一次时那一行、展开一段活后的那一行由这张卡画。例子是 `plume.mcp` 的文档预览。
|
|
181
181
|
- **崩溃让位**:每张卡片外面有错误边界,渲染出错时让给同一工具的下一个注册者,都不行用通用卡片。组件里不用自己 try/catch。
|
|
182
182
|
- **后注册的生效**:同一工具 `order` 小的在前;`order` 相同时后注册的在前,它撤下后前一个恢复。卡片的 `order` 默认是 0,写更小的 `order` 也能排到前面。
|
|
183
|
-
- 外框用 `@plume/ui` 的 `Card`(`title`、`subtitle` 放命令或路径、`status`);输出用 `CodeBlock`,改动用 `
|
|
183
|
+
- 外框用 `@plume/ui` 的 `Card`(`title`、`subtitle` 放命令或路径、`status`);输出用 `CodeBlock`,改动用 `ui.FileDiff`。
|
|
184
184
|
- 卡片只占一行时(参数还在写、事情已经交出去了)用 `@plume/ui` 的 `FlowRow`:和转录里思考、工具活、送达那几行同一个骨架(前置图标、标题、摘要、尾部、在跑时摘要扫光、`onToggle` 给了才可点),例子是 `plume.spawn-session` 的卡片写参数时那一行和开始以后那一行。
|
|
185
185
|
- 审批卡压在输入框正上方,不在工具卡片里:等待确认时工具卡片收到 `status: "approval"`,卡片里不放允许、拒绝。要给自己的工具换一种审批卡,见下面「审批卡」。
|
|
186
186
|
|
|
@@ -506,6 +506,8 @@ function ModelSetting() {
|
|
|
506
506
|
|
|
507
507
|
`root` 是窗口最外层与透明停靠侧栏共用的底色,默认跟随 `shell`;`sidebar` 只给 compact 浮出侧栏铺底,不给停靠侧栏另铺一层;`accent-dim` 是强调弱底,默认是强调色的 10% 透明底。数据皮肤可直接给出完整的面、线、字 token;若一份配色只适用于一个模式,给 `light`、`dark` 同一组 token,并用 `mode` 指定深浅模式。
|
|
508
508
|
|
|
509
|
+
`root-image` 是盖在 `root` 上的底纹,值是 CSS 图像,可以逗号分隔叠几层(例如 `linear-gradient(160deg, #…, #…)`,先写的在上面),默认 `none`。颗粒、噪点这类质感不写进皮肤:磨砂是用户在背景设置里开关的,宿主叠在底纹上面,哪套皮肤都一样。只在管理配色的数据皮肤下铺在窗口外圈和透明的停靠侧栏上,正文卡片仍是 `shell` 的实底。宿主把它固定在视口上(`background-attachment: fixed`),外圈、侧栏吸顶标题和 Windows 顶带画的是同一片;平铺的小图按自身尺寸重复,渐变铺满整个窗口。侧栏的字直接压在它上面,渐变两端的颜色都要和 `root` 一样满足文字对比度。
|
|
510
|
+
|
|
509
511
|
在界面入口的 `apply` 中开启远端 watch 时,用 `ui.effect(() => call.cancel())` 登记取消。清理按后登记先执行,一项抛错不妨碍其余清理;宿主先让旧入口失效再清理,因此停用、重载或 apply 失败后,旧异步回调的 `ui.skin` 不会复活皮肤。组件内的订阅仍用 React effect 清理。
|
|
510
512
|
|
|
511
513
|
## remote:调自己的逻辑入口
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 一排小脸:这一批派出去的是谁、各自在干什么。
|
|
3
|
-
*
|
|
4
|
-
* 并排而不是叠着:叠起来的头像组说的是「有这么些人」,而这里每张脸自己有话说——谁在颠着干活、
|
|
5
|
-
* 谁眯着眼在排队、谁已经交差——被邻居盖住半张脸,表情就读不出来了。
|
|
6
|
-
*
|
|
7
|
-
* 放不下的收成一个「+N」,数字本身就是提示:想看全的点开面板。
|
|
8
|
-
*/
|
|
9
|
-
import type { Avatar } from "../lib/agent-avatar.ts";
|
|
10
|
-
import { type AvatarMood } from "./AgentAvatar.tsx";
|
|
11
|
-
export interface StackFace {
|
|
12
|
-
key: string;
|
|
13
|
-
avatar: Avatar;
|
|
14
|
-
mood?: AvatarMood;
|
|
15
|
-
/** 名字,决定眨眼的节奏。 */
|
|
16
|
-
seed: string;
|
|
17
|
-
/** 悬停时的那句话,同时是读屏念的名字。 */
|
|
18
|
-
tip: string;
|
|
19
|
-
onClick?: () => void;
|
|
20
|
-
}
|
|
21
|
-
export declare function AvatarStack({ faces, size, max, className }: {
|
|
22
|
-
faces: StackFace[];
|
|
23
|
-
size?: number;
|
|
24
|
-
max?: number;
|
|
25
|
-
className?: string;
|
|
26
|
-
}): import("react").JSX.Element;
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 一个字符串一个字一个字地改写成另一个。
|
|
3
|
-
*
|
|
4
|
-
* 一段对话在运行时读完第一条消息之前,标题是「新对话」,之后真标题才落下来。一帧之内直接替换,是侧栏里
|
|
5
|
-
* 唯一一处「行变了,却和你刚做的事无关」的时刻;硬切的话读起来像列表在指针底下自己重排了一次。改写一遍
|
|
6
|
-
* 传达的是同一件事,只是成了一个看得见过程的事件。
|
|
7
|
-
*
|
|
8
|
-
* 共同前缀保留不删,于是「查一下依赖」→「查一下依赖为什么装不上」不会先退回到空,而是只打出尾巴。
|
|
9
|
-
* 这种情况很常见:存下的标题是从提示词里取的,它和替换它的那个往往开头一样。
|
|
10
|
-
*
|
|
11
|
-
* 按码点而不是 UTF-16 单元:`[...text]` 让 emoji 和其他星际字符保持完整,`slice` 会把代理对切成两半,
|
|
12
|
-
* 有一帧画出替换字形。
|
|
13
|
-
*/
|
|
14
|
-
/**
|
|
15
|
-
* `from` 到 `to` 之间的每个中间字符串,按顺序,最后一个是 `to`。
|
|
16
|
-
*
|
|
17
|
-
* 没什么可演的时候是空的——两个字符串相同,或者原来没有值、第一个值刚到——调用方读作「不演,直接画」。
|
|
18
|
-
*/
|
|
19
|
-
export declare function typingFrames(from: string, to: string, maxFrames?: number): string[];
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GitHub 的标志,每个仓库页上的那个。
|
|
3
|
-
*
|
|
4
|
-
* 16px 的 `mark-github` octicon,和 https://github.com 自己导航里画的一模一样,而不是 lucide 的图形:
|
|
5
|
-
* lucide 去掉了品牌图标,而仓库链接旁边放一个通用的「代码」或「分支」符号,得读了提示才知道它通向哪——
|
|
6
|
-
* 这个标志在读任何字之前就说了是 GitHub。
|
|
7
|
-
* 用 `currentColor`,在哪种主题下都取它所在位置的字色。
|
|
8
|
-
*/
|
|
9
|
-
export declare function GitHubMark({ size, className }: {
|
|
10
|
-
size?: number;
|
|
11
|
-
className?: string;
|
|
12
|
-
}): import("react").JSX.Element;
|