@morlay/dsh-session-mode 0.0.1-alpha.4 → 0.0.1
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/README.md +80 -67
- package/dist/client.cjs +165 -802
- package/dist/client.d.cts +22 -187
- package/dist/client.d.mts +22 -187
- package/dist/index.d.cts +183 -9
- package/dist/index.d.mts +183 -9
- package/dist/index.mjs +183 -31
- package/dist/rows.d.cts +38 -0
- package/dist/rows.d.mts +38 -0
- package/dist/rows.mjs +503 -0
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +46 -53
- package/src/client/SessionModeSeat.tsx +38 -19
- package/src/client/index.ts +143 -92
- package/src/client/locales.ts +18 -72
- package/src/index.ts +145 -18
- package/{tool/modes.ts → src/mode-sources.ts} +32 -14
- package/src/modes.ts +197 -36
- package/src/rows.ts +57 -0
- package/cordis.patch.yml +0 -69
- package/dist/shared-FNWe-_rS.d.cts +0 -106
- package/dist/shared-FNWe-_rS.d.mts +0 -106
- package/src/client/ModelDefaultsCard.styles.ts +0 -134
- package/src/client/ModelDefaultsCard.tsx +0 -233
- package/src/client/SessionModeLabel.module.css +0 -24
- package/src/client/SessionModeLabel.tsx +0 -44
- package/src/client/model-defaults-card-controller.ts +0 -408
- package/tool/patch.ts +0 -91
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* composer 里的模式 chip:**本包在会话里唯一的面**(点开是 coding / chat 两项)。
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* 选择只在会话开始**之前**有效——一旦跑过 turn,那段历史是在某个模式的工具与提示词下产生的。所以 chip 在
|
|
5
|
+
* 会话里有两个形态:空白期是选择器,开过 turn 之后**只读**(只写当前模式,点不动)。判据是 host 的投影
|
|
6
|
+
* `sessionModeEditable`(`turn/start` 一落库就为 `false`),与服务端拒绝切换读的是同一份事实。
|
|
7
|
+
*
|
|
8
|
+
* 它挂在 composer 上:新会话屏也是一个 blank session 的 composer,选择正好发生在那里;会话头部则没有它的
|
|
9
|
+
* 位置——头部只在"跑起来之后"值得看,而那时的 chip 已经把当前模式写在脸上,再挂一个只读标签就是同一句话的
|
|
10
|
+
* 复读。
|
|
7
11
|
*/
|
|
8
12
|
|
|
9
13
|
import { useState } from "react";
|
|
@@ -15,46 +19,61 @@ import {
|
|
|
15
19
|
import type { PropsLocale, PropsRuntime } from "@deepseek-ai/dsh-client-ui-slots";
|
|
16
20
|
// Type-only:拉入本包 host 半的 Context / 投影声明(`sessionMode`),让 `projectionValues` 有类型。
|
|
17
21
|
import type {} from "../index.ts";
|
|
18
|
-
// Type-only:拉入 ui-conversation 的 SlotMap 合并(
|
|
22
|
+
// Type-only:拉入 ui-conversation 的 SlotMap 合并(composer 工具行的座位)。
|
|
19
23
|
import type {} from "@morlay/dsh-client-ui-conversation/client";
|
|
20
24
|
import { selectMode } from "./api.ts";
|
|
21
25
|
import { useRoster } from "./use-roster.ts";
|
|
22
26
|
import css from "./SessionModeSeat.module.css";
|
|
23
27
|
|
|
24
|
-
/** 完整 props:
|
|
25
|
-
export type SessionModeSeatProps = PropsRuntime<"conversation.
|
|
28
|
+
/** 完整 props:composer 工具行左侧槽位的运行时 props + 本包的字典。 */
|
|
29
|
+
export type SessionModeSeatProps = PropsRuntime<"conversation.input.left"> &
|
|
26
30
|
PropsLocale<"session-mode">;
|
|
27
31
|
|
|
28
32
|
/**
|
|
29
|
-
*
|
|
33
|
+
* 渲染 composer 里的模式切换 chip。
|
|
34
|
+
*
|
|
35
|
+
* 为什么在 composer(`conversation.input.left`):**新会话屏也是一个 blank session 的 composer**,选择正好
|
|
36
|
+
* 发生在那里;会话头部槽位在新会话屏根本不存在,而会话跑起来之后头部也不该重复说一遍 chip 已经写着的事。
|
|
37
|
+
* 官方 roster 那个座位(`conversation.hero.agentPreset`)是单注册槽位、已被官方占了,所以用自己的 list 槽位
|
|
38
|
+
* 并存。
|
|
30
39
|
* @param props - 槽位合成的 props。
|
|
31
|
-
* @returns chip
|
|
40
|
+
* @returns chip;清单没读到、或会话未知时返回 null。
|
|
32
41
|
*/
|
|
33
|
-
export function SessionModeSeat({
|
|
34
|
-
sessionId,
|
|
35
|
-
useSessions,
|
|
36
|
-
useSessionRetainInfo,
|
|
37
|
-
t,
|
|
38
|
-
}: SessionModeSeatProps) {
|
|
42
|
+
export function SessionModeSeat({ sessionId, useSessions, t }: SessionModeSeatProps) {
|
|
39
43
|
const roster = useRoster();
|
|
40
44
|
const selected = useSessions((state) => {
|
|
41
45
|
const value =
|
|
42
46
|
sessionId === undefined ? undefined : state.byId[sessionId]?.projectionValues?.sessionMode;
|
|
43
47
|
return typeof value === "string" ? value : undefined;
|
|
44
48
|
});
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
const editable = useSessions((state) => {
|
|
50
|
+
const value =
|
|
51
|
+
sessionId === undefined
|
|
52
|
+
? undefined
|
|
53
|
+
: state.byId[sessionId]?.projectionValues?.sessionModeEditable;
|
|
54
|
+
return typeof value === "boolean" ? value : undefined;
|
|
55
|
+
});
|
|
48
56
|
const [open, setOpen] = useState(false);
|
|
49
57
|
const [busy, setBusy] = useState(false);
|
|
50
58
|
const [error, setError] = useState<string | null>(null);
|
|
51
59
|
|
|
52
|
-
if (
|
|
60
|
+
if (roster === undefined) return null;
|
|
53
61
|
|
|
54
62
|
const current = selected ?? roster.default;
|
|
55
63
|
const chosen = roster.modes.find((mode) => mode.id === current);
|
|
56
64
|
const label = chosen?.name ?? current;
|
|
57
65
|
|
|
66
|
+
// 开过 turn 的会话**只读**:chip 只写当前模式,点不动也不给清单。判据与 host 拒绝切换读的是同一个投影
|
|
67
|
+
// (`sessionModeEditable`),所以不会出现"看起来能选、点了报错"。
|
|
68
|
+
if (editable === false) {
|
|
69
|
+
return (
|
|
70
|
+
<button type="button" className={css.seat} disabled title={t("lockedHint")}>
|
|
71
|
+
<IconAgentPresetOutlineRegular className={css.seatIcon} />
|
|
72
|
+
<span className={css.seatLabel}>{label}</span>
|
|
73
|
+
</button>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
58
77
|
return (
|
|
59
78
|
<Menu
|
|
60
79
|
open={open}
|
package/src/client/index.ts
CHANGED
|
@@ -1,40 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* client
|
|
2
|
+
* client 半:会话里那一个面(模式 chip)+ 设置页里那张「会话模式」卡片。
|
|
3
3
|
*
|
|
4
|
-
* | 槽位
|
|
5
|
-
* |
|
|
6
|
-
* | `conversation.
|
|
7
|
-
* | `conversation.session.header.actions` | 会话头部的只读模式标签(`order: -10`,贴标题) |
|
|
8
|
-
* | `plugins.row.config`(key 见下) | 设置页里**本行**的配置入口:各模式的默认模型(`config.models`) |
|
|
4
|
+
* | 槽位 | 呈现 |
|
|
5
|
+
* | ------------------------- | --------------------------------------------------- |
|
|
6
|
+
* | `conversation.input.left` | chip——点开就是切换列表(新会话屏也是一个 composer) |
|
|
9
7
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* 清单;模式清单仍然是装配配置,改它不需要页面。
|
|
8
|
+
* 官方 `@deepseek-ai/dsh-client-ui-agent-preset` 保留(它提供 preset 的选择面与设置页 roster):官方管"挂哪套
|
|
9
|
+
* 行",我们管"会话级扩展",两套入口并存、互不遮挡。
|
|
13
10
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
11
|
+
* 头部的只读标签**没有**:chip 本来就把当前模式写在脸上(会话与新会话屏都有 composer),右上角再写一遍是
|
|
12
|
+
* 同一句话的复读。
|
|
13
|
+
*
|
|
14
|
+
* 本行的配置页(各模式的默认模型 `config.models`)不再由本包画:那个字段是 volatile 的,页面由
|
|
15
|
+
* `@morlay/dsh-client-ui-primitives` 按 schema 自动生成(它注册到本行的配置入口 `plugins.row.config`,
|
|
16
|
+
* key = `<bundle 包名>#<行 id>`)。本包只管会话里那一个面。
|
|
18
17
|
*/
|
|
19
18
|
|
|
20
19
|
import type { Context } from "@deepseek-ai/cordis";
|
|
20
|
+
// Type-only:`ctx.remote` 的合并面(选模型的候选来自 LLM 目录)。
|
|
21
21
|
import type {} from "@deepseek-ai/dsh-api-remotes/client";
|
|
22
22
|
import type {} from "@deepseek-ai/dsh-client-locale/client";
|
|
23
|
-
import type {} from "@deepseek-ai/dsh-client-ui-plugin-manager/client";
|
|
24
|
-
import type {} from "@deepseek-ai/dsh-client-ui-settings/client";
|
|
25
23
|
import type {} from "@deepseek-ai/dsh-client-ui-slots";
|
|
24
|
+
|
|
26
25
|
// Type-only:槽位声明与 standard props(session / session-maybe / global)。
|
|
27
26
|
import type {} from "@morlay/dsh-client-ui-conversation/client";
|
|
28
|
-
import { ModelDefaultsCard } from "./ModelDefaultsCard.tsx";
|
|
29
|
-
import { SESSION_MODE_NS, ModelDefaultsCardController } from "./model-defaults-card-controller.ts";
|
|
30
|
-
import { SessionModeLabel } from "./SessionModeLabel.tsx";
|
|
31
27
|
import { SessionModeSeat } from "./SessionModeSeat.tsx";
|
|
32
|
-
import { fetchRoster } from "./api.ts";
|
|
33
28
|
import { en, zh, type SessionModeLocaleKey } from "./locales.ts";
|
|
34
29
|
|
|
35
30
|
declare module "@deepseek-ai/dsh-client-ui-slots" {
|
|
36
31
|
interface LocaleNamespaceMap {
|
|
37
|
-
/**
|
|
32
|
+
/** 会话里两个面的文案(配置页文案在通用 schema 表单的字典里)。 */
|
|
38
33
|
"session-mode": SessionModeLocaleKey;
|
|
39
34
|
}
|
|
40
35
|
}
|
|
@@ -42,101 +37,157 @@ declare module "@deepseek-ai/dsh-client-ui-slots" {
|
|
|
42
37
|
/** 浏览器半插件的字典命名空间。 */
|
|
43
38
|
const NS = "session-mode";
|
|
44
39
|
|
|
45
|
-
/**
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
40
|
+
/** 这一行里需要中文文案的字段(都在 `modes.<模式>.defaultModel` 里,所以按模板路径注册一次)。 */
|
|
41
|
+
const FIELDS = ["provider", "model", "reasoningEffort"] as const;
|
|
42
|
+
|
|
43
|
+
/** 动态键的占位段(与通用表单的字段树同一约定)。 */
|
|
44
|
+
const DYNAMIC = "*";
|
|
45
|
+
|
|
46
|
+
/** 一个可配置 provider 的候选信息:显示名 + 它的配置在哪(模型清单从那份配置里读)。 */
|
|
47
|
+
interface ProviderEntry {
|
|
48
|
+
value: string;
|
|
49
|
+
label: string;
|
|
50
|
+
settingsNs: string;
|
|
51
|
+
settingsPath: readonly string[];
|
|
52
|
+
}
|
|
52
53
|
|
|
53
|
-
export type { ModelDefaultsCardProps } from "./ModelDefaultsCard.tsx";
|
|
54
|
-
export type {
|
|
55
|
-
ModelDefaultsCardFace,
|
|
56
|
-
ModelDefaultsCardState,
|
|
57
|
-
ModelDefaultsRow,
|
|
58
|
-
ModelDirectory,
|
|
59
|
-
ModelDefaultsSources,
|
|
60
|
-
SessionModeSettings,
|
|
61
|
-
} from "./model-defaults-card-controller.ts";
|
|
62
|
-
export { ModelDefaultsCardController, SESSION_MODE_NS } from "./model-defaults-card-controller.ts";
|
|
63
|
-
export type { SessionModeLabelProps } from "./SessionModeLabel.tsx";
|
|
64
54
|
export type { SessionModeSeatProps } from "./SessionModeSeat.tsx";
|
|
65
55
|
export type { SessionModeLocaleKey } from "./locales.ts";
|
|
66
56
|
|
|
67
57
|
/** 需要的服务:槽位与字典(会话列表经槽位的标准 props 到达组件,不必自己 inject)。 */
|
|
68
58
|
export const inject = ["slots", "locale"];
|
|
69
59
|
|
|
60
|
+
/** 本包 host 行 id:行配置页读的就是这个命名空间。 */
|
|
61
|
+
export const SESSION_MODE_NS = "session-mode";
|
|
62
|
+
|
|
63
|
+
/** 按路径读一段配置里的值(本包只读 provider 档案里的模型清单)。 */
|
|
64
|
+
function readAt(root: unknown, path: readonly string[]): unknown {
|
|
65
|
+
return path.reduce<unknown>((node, segment) => {
|
|
66
|
+
if (Array.isArray(node)) return node[Number(segment)];
|
|
67
|
+
if (typeof node !== "object" || node === null) return undefined;
|
|
68
|
+
return Reflect.get(node, segment);
|
|
69
|
+
}, root);
|
|
70
|
+
}
|
|
71
|
+
|
|
70
72
|
/**
|
|
71
|
-
*
|
|
73
|
+
* 装上会话里的两个面,以及本行配置页的字段文案。
|
|
74
|
+
*
|
|
72
75
|
* @param ctx - 浏览器插件上下文。
|
|
73
76
|
*/
|
|
74
77
|
export function apply(ctx: Context): void {
|
|
78
|
+
const t = ctx.locale.bind(NS);
|
|
75
79
|
ctx.effect(() => ctx.locale.register(NS, { zh, en }), "session-mode: dictionaries");
|
|
76
80
|
|
|
77
|
-
//
|
|
81
|
+
// 本行的配置页由通用 schema 表单按 volatile 字段生成;这里给 `modes.<模式>.defaultModel` 里的三个字段补
|
|
82
|
+
// 中文标签与说明。
|
|
83
|
+
ctx.inject(["schemaFormHints"], (scope) =>
|
|
84
|
+
scope.effect(() => {
|
|
85
|
+
const hints = scope.schemaFormHints;
|
|
86
|
+
const offs = FIELDS.map((key) =>
|
|
87
|
+
hints.describe(SESSION_MODE_NS, ["modes", DYNAMIC, "defaultModel", key], () => ({
|
|
88
|
+
label: t(key),
|
|
89
|
+
hint: t(`${key}Hint` as "providerHint"),
|
|
90
|
+
})),
|
|
91
|
+
);
|
|
92
|
+
return () => {
|
|
93
|
+
for (const off of offs) off();
|
|
94
|
+
};
|
|
95
|
+
}, "session-mode: field wording"),
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
// 选模型的候选不在本行的 schema 里:provider 是部署里的 LLM 目录(活着的路由 + 可配置声明),模型清单读那份声明
|
|
99
|
+
// 指向的配置(`settingsNs` / `settingsPath`)。目录异步取到后注册——注册本身就是一次变更通知。
|
|
100
|
+
ctx.inject(["schemaFormHints"], (scope) =>
|
|
101
|
+
scope.effect(() => {
|
|
102
|
+
const hints = scope.schemaFormHints;
|
|
103
|
+
const remote = scope.get("remote");
|
|
104
|
+
const forms = scope.get("configForms");
|
|
105
|
+
if (remote === undefined || forms === undefined) return () => {};
|
|
106
|
+
let providers: ProviderEntry[] = [];
|
|
107
|
+
const load = async (): Promise<void> => {
|
|
108
|
+
const [routes, directory] = await Promise.all([
|
|
109
|
+
remote.llm.listProviders(),
|
|
110
|
+
remote.llm.listConfigurableProviders(),
|
|
111
|
+
]);
|
|
112
|
+
if (!routes.ok || !directory.ok) return;
|
|
113
|
+
const merged = new Map<string, ProviderEntry>();
|
|
114
|
+
for (const entry of directory.value) {
|
|
115
|
+
merged.set(entry.provider, {
|
|
116
|
+
value: entry.provider,
|
|
117
|
+
label: entry.displayName,
|
|
118
|
+
settingsNs: entry.settingsNs,
|
|
119
|
+
settingsPath: [...entry.settingsPath],
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
for (const route of routes.value) {
|
|
123
|
+
if (!merged.has(route.id)) {
|
|
124
|
+
merged.set(route.id, {
|
|
125
|
+
value: route.id,
|
|
126
|
+
label: route.name,
|
|
127
|
+
settingsNs: "",
|
|
128
|
+
settingsPath: [],
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
providers = [...merged.values()];
|
|
133
|
+
hints.refresh();
|
|
134
|
+
};
|
|
135
|
+
const modelsOf = (provider: unknown): readonly { value: string }[] => {
|
|
136
|
+
if (typeof provider !== "string") return [];
|
|
137
|
+
const entry = providers.find((candidate) => candidate.value === provider);
|
|
138
|
+
if (entry === undefined || entry.settingsNs === "") return [];
|
|
139
|
+
const profile = readAt(forms.get(entry.settingsNs).getSnapshot().value, entry.settingsPath);
|
|
140
|
+
const models =
|
|
141
|
+
typeof profile === "object" && profile !== null
|
|
142
|
+
? Reflect.get(profile, "models")
|
|
143
|
+
: undefined;
|
|
144
|
+
if (!Array.isArray(models)) return [];
|
|
145
|
+
return models.flatMap((model) => {
|
|
146
|
+
const id =
|
|
147
|
+
typeof model === "object" && model !== null ? Reflect.get(model, "id") : undefined;
|
|
148
|
+
return typeof id === "string" ? [{ value: id }] : [];
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
const offs = [
|
|
152
|
+
// 两个具名源:schema 上 `role('select', { source })` 认领它们,本包因此不必知道行 id 与字段路径。
|
|
153
|
+
hints.source("llm-providers", {
|
|
154
|
+
options: () => providers.map((entry) => ({ value: entry.value, label: entry.label })),
|
|
155
|
+
}),
|
|
156
|
+
// 换服务商就换模型清单:依赖声明让表单在投影时按当前 provider 重算候选。
|
|
157
|
+
hints.source("llm-models", {
|
|
158
|
+
dependsOn: [["provider"]],
|
|
159
|
+
options: (read) => modelsOf(read(["provider"])),
|
|
160
|
+
}),
|
|
161
|
+
remote.$on("llm/adapters-updated", () => {
|
|
162
|
+
void load();
|
|
163
|
+
}),
|
|
164
|
+
remote.$on("settings/document-updated", () => {
|
|
165
|
+
hints.refresh();
|
|
166
|
+
}),
|
|
167
|
+
];
|
|
168
|
+
void load();
|
|
169
|
+
return () => {
|
|
170
|
+
for (const off of offs) off();
|
|
171
|
+
};
|
|
172
|
+
}, "session-mode: model candidates"),
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
// chip 挂 **composer 工具行左侧**(`conversation.input.left`,list + session scope):新会话屏也是一个
|
|
176
|
+
// blank session 的 composer,所以那里也能选模式——头部槽位在新会话屏不存在。
|
|
78
177
|
ctx.inject(["slots", "conversation"], (scope) => {
|
|
79
178
|
scope.effect(() => {
|
|
80
179
|
const seat = scope.slots.register(
|
|
81
|
-
{ name: "conversation.hero.agentPreset", locale: NS },
|
|
82
|
-
SessionModeSeat,
|
|
83
|
-
);
|
|
84
|
-
const label = scope.slots.register(
|
|
85
180
|
{
|
|
86
|
-
name: "conversation.
|
|
181
|
+
name: "conversation.input.left",
|
|
87
182
|
id: "session-mode",
|
|
88
|
-
|
|
89
|
-
order: -10,
|
|
183
|
+
order: 0,
|
|
90
184
|
locale: NS,
|
|
91
185
|
},
|
|
92
|
-
|
|
186
|
+
SessionModeSeat,
|
|
93
187
|
);
|
|
94
188
|
return () => {
|
|
95
189
|
seat();
|
|
96
|
-
label();
|
|
97
|
-
};
|
|
98
|
-
}, "session-mode: hero chip and header label");
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
// 设置页那张卡片要 `configForms`(共享配置表单)、`remote.session`(模型目录)与 `plugins.item`
|
|
102
|
-
// (Plugins 页的槽)。它们都可能比本行晚到、或在这个部署里根本没有,所以走 `ctx.inject` 与
|
|
103
|
-
// `slots.inject` 等它们就位——本行的基础 `inject` 不点它们,缺了也不影响上面两个面。
|
|
104
|
-
ctx.inject(["slots", "locale", "configForms", "remote", "remote.session"], (forms) => {
|
|
105
|
-
const t = forms.locale.bind(NS);
|
|
106
|
-
forms.effect(() => {
|
|
107
|
-
const controller = new ModelDefaultsCardController(
|
|
108
|
-
forms.configForms.get(SESSION_MODE_NS),
|
|
109
|
-
{
|
|
110
|
-
roster: fetchRoster,
|
|
111
|
-
// 目录只在打开卡片时读一次:它决定选择器的选项,页面生命周期里不会变。
|
|
112
|
-
directory: async () => {
|
|
113
|
-
const response = await forms.remote.session.modelCatalog();
|
|
114
|
-
if (!response.ok) throw new Error("model catalog is unavailable");
|
|
115
|
-
return {
|
|
116
|
-
groups: response.value.groups,
|
|
117
|
-
failures: response.value.failures.map((failure) => failure.name),
|
|
118
|
-
};
|
|
119
|
-
},
|
|
120
|
-
},
|
|
121
|
-
);
|
|
122
|
-
const face = controller.inject();
|
|
123
|
-
const page = forms.configForms.whileServed([SESSION_MODE_NS], () =>
|
|
124
|
-
forms.slots.inject("plugins.row.config", () =>
|
|
125
|
-
forms.slots.register(
|
|
126
|
-
{
|
|
127
|
-
name: "plugins.row.config",
|
|
128
|
-
key: SESSION_MODE_ROW_CONFIG_KEY,
|
|
129
|
-
// 文案经我们自己的注入面传:keyed 槽的注册项不带 `locale` 声明,不赌页面一定注入字典。
|
|
130
|
-
inject: () => ({ ...face, t }),
|
|
131
|
-
},
|
|
132
|
-
ModelDefaultsCard,
|
|
133
|
-
),
|
|
134
|
-
),
|
|
135
|
-
);
|
|
136
|
-
return () => {
|
|
137
|
-
page();
|
|
138
|
-
controller.dispose();
|
|
139
190
|
};
|
|
140
|
-
}, "session-mode:
|
|
191
|
+
}, "session-mode: composer chip");
|
|
141
192
|
});
|
|
142
193
|
}
|
package/src/client/locales.ts
CHANGED
|
@@ -1,88 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* client 半的文案:命名空间 `session-mode`(与 locale 注册时用的 key 一致)。
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* 只有会话里那个模式 chip 用这份字典:模式的名字与说明由 host 的清单给(那是数据,不是文案)。本行的配置页
|
|
5
|
+
* (各模式的默认模型)由通用 schema 表单渲染,文案在它自己的字典里。
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type { SettingsFormLabels } from "@morlay/dsh-client-ui-primitives/client";
|
|
9
|
-
|
|
10
8
|
export const zh = {
|
|
11
9
|
seatHint: "选择这个会话的模式",
|
|
12
|
-
|
|
10
|
+
lockedHint: "这个会话已经开始:换模式请新开一个会话",
|
|
13
11
|
noDescription: "(无说明)",
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
providerLabel: "服务商",
|
|
22
|
-
modelLabel: "模型",
|
|
23
|
-
effortLabel: "思考档位",
|
|
24
|
-
inheritOption: "跟全局默认",
|
|
25
|
-
chooseModel: "选择模型…",
|
|
26
|
-
effortDefaultOption: "跟模型默认",
|
|
27
|
-
staleOption: "{value}(不在目录里)",
|
|
28
|
-
overridden: "已覆盖",
|
|
29
|
-
reset: "恢复默认",
|
|
30
|
-
modelRequired: "还没选模型",
|
|
31
|
-
readOnly: "本部署的设置为只读。",
|
|
32
|
-
unavailable: "本插件当前未加载,暂时无法配置。",
|
|
33
|
-
save: "保存",
|
|
34
|
-
saving: "保存中…",
|
|
35
|
-
saveFailed: "本部署没有接受这些值,已保留供你修改。",
|
|
36
|
-
rosterFailed: "读不到会话模式清单,暂时列不出模式。",
|
|
37
|
-
directoryFailed: "读不到模型目录,暂时只能看当前值或恢复默认。",
|
|
38
|
-
directoryPartial: "这些服务商的目录没读到:{names}",
|
|
12
|
+
provider: "服务商",
|
|
13
|
+
providerHint:
|
|
14
|
+
"服务商 id:部署里注册的任意路由——不论哪个适配器插件注册的(官方的、我们自己的都算)。",
|
|
15
|
+
model: "模型",
|
|
16
|
+
modelHint: "模型 id。",
|
|
17
|
+
reasoningEffort: "思考档位",
|
|
18
|
+
reasoningEffortHint: "省略就跟服务商自己的默认。",
|
|
39
19
|
};
|
|
40
20
|
|
|
41
21
|
export const en: Record<keyof typeof zh, string> = {
|
|
42
22
|
seatHint: "Pick the mode for this session",
|
|
43
|
-
|
|
23
|
+
lockedHint: "This session has started: start a new one to pick a mode",
|
|
44
24
|
noDescription: "(no description)",
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
providerLabel: "Provider",
|
|
53
|
-
modelLabel: "Model",
|
|
54
|
-
effortLabel: "Reasoning effort",
|
|
55
|
-
inheritOption: "Same as global default",
|
|
56
|
-
chooseModel: "Choose a model…",
|
|
57
|
-
effortDefaultOption: "Model default",
|
|
58
|
-
staleOption: "{value} (not in the directory)",
|
|
59
|
-
overridden: "Overridden",
|
|
60
|
-
reset: "Reset to default",
|
|
61
|
-
modelRequired: "No model picked yet",
|
|
62
|
-
readOnly: "This deployment stores settings read-only.",
|
|
63
|
-
unavailable: "This plugin is not loaded, so it cannot be configured right now.",
|
|
64
|
-
save: "Save",
|
|
65
|
-
saving: "Saving…",
|
|
66
|
-
saveFailed: "The deployment did not accept these values; they were left for you to correct.",
|
|
67
|
-
rosterFailed: "Could not load the session mode roster, so no mode can be listed.",
|
|
68
|
-
directoryFailed:
|
|
69
|
-
"Could not read the model directory, so only the current value or a reset is available.",
|
|
70
|
-
directoryPartial: "These providers could not be read: {names}",
|
|
25
|
+
provider: "Provider",
|
|
26
|
+
providerHint:
|
|
27
|
+
"Provider id: any route registered in this deployment, whichever adapter plugin supplies it.",
|
|
28
|
+
model: "Model",
|
|
29
|
+
modelHint: "Model id.",
|
|
30
|
+
reasoningEffort: "Reasoning effort",
|
|
31
|
+
reasoningEffortHint: "Unset keeps the provider's own default.",
|
|
71
32
|
};
|
|
72
33
|
|
|
73
34
|
export type SessionModeLocaleKey = keyof typeof zh;
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* 共享设置表单外框的文案,从本页字典里读。
|
|
77
|
-
* @param t - 本页的字典读取器。
|
|
78
|
-
* @returns `SettingsForm` 渲染用的标签。
|
|
79
|
-
*/
|
|
80
|
-
export function formLabels(t: (key: SessionModeLocaleKey) => string): SettingsFormLabels {
|
|
81
|
-
return {
|
|
82
|
-
unavailable: t("unavailable"),
|
|
83
|
-
readOnly: t("readOnly"),
|
|
84
|
-
saveFailed: t("saveFailed"),
|
|
85
|
-
save: t("save"),
|
|
86
|
-
saving: t("saving"),
|
|
87
|
-
};
|
|
88
|
-
}
|