@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,233 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 设置页「会话模式」那张卡片:每个模式一行「默认模型」,一段一起保存。
|
|
3
|
-
*
|
|
4
|
-
* 页面(Plugins 列表)画标题与一句话说明:`view: 'summary'` 给那句话,`view: 'page'` 给表单。编辑是 staged
|
|
5
|
-
* 的——选择器只改草稿,按保存才写一次 `mutate`;"恢复默认"(或把 provider 选回「跟全局默认」)把这一行的
|
|
6
|
-
* 用户层清掉,回到装配层那份。控件是本卡自研的:这里的编辑面是一张**动态映射**(模式 → provider / model /
|
|
7
|
-
* 档位),原语的标量字段控件套不进去——只借它的壳(保存 / 丢弃 / 只读 / 失败与 `SettingsFormShell`)。
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { useId } from "react";
|
|
11
|
-
import type { ModelProviderGroup } from "@deepseek-ai/dsh-api-remotes/client";
|
|
12
|
-
import type {} from "@deepseek-ai/dsh-client-ui-plugin-manager/client";
|
|
13
|
-
import type { InjectFace, PropsRuntime, TranslateNS } from "@deepseek-ai/dsh-client-ui-slots";
|
|
14
|
-
import { SettingsForm, styling } from "@morlay/dsh-client-ui-primitives/client";
|
|
15
|
-
import type { ModelDefaultsCardFace, ModelDefaultsRow } from "./model-defaults-card-controller.ts";
|
|
16
|
-
import { formLabels } from "./locales.ts";
|
|
17
|
-
import { styles } from "./ModelDefaultsCard.styles.ts";
|
|
18
|
-
|
|
19
|
-
/** 槽位渲染器给卡片的 props:运行时面 + 卡片自己的注入面(`t` 由本包的 `inject` 面带进来)。 */
|
|
20
|
-
export type ModelDefaultsCardProps = PropsRuntime<"plugins.row.config"> &
|
|
21
|
-
InjectFace<ModelDefaultsCardFace & { t: Translate }>;
|
|
22
|
-
|
|
23
|
-
/** 本页字典的读取器(命名空间的 key 由 `LocaleNamespaceMap` 合并声明给)。 */
|
|
24
|
-
type Translate = TranslateNS<"session-mode">;
|
|
25
|
-
|
|
26
|
-
/** 一个下拉选项:目录里的那条,或"当前值不在目录里"时补上的那条。 */
|
|
27
|
-
interface Option {
|
|
28
|
-
readonly id: string;
|
|
29
|
-
readonly name: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* 选择器的选项:目录里的那些 + 当前值那条(不在目录里时补上)。
|
|
34
|
-
*
|
|
35
|
-
* 补当前值是必须的:`<select>` 的 value 不在选项里时浏览器会显示成别的选项,用户按保存就会把它写成
|
|
36
|
-
* 那个值——配置被悄悄改掉。(目录读不到、或适配器卸了的时候正是这个局面。)
|
|
37
|
-
* @param entries - 目录里的选项(provider 或 model)。
|
|
38
|
-
* @param current - 当前生效值的那一项 id。
|
|
39
|
-
* @param t - 本页字典。
|
|
40
|
-
* @returns 选项列表。
|
|
41
|
-
*/
|
|
42
|
-
function options(
|
|
43
|
-
entries: readonly Option[],
|
|
44
|
-
current: string | undefined,
|
|
45
|
-
t: Translate,
|
|
46
|
-
): Option[] {
|
|
47
|
-
if (current === undefined || entries.some((entry) => entry.id === current)) return [...entries];
|
|
48
|
-
return [...entries, { id: current, name: t("staleOption", { value: current }) }];
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* 画一个模式的默认模型那一行。
|
|
53
|
-
* @param props - 这一行的值、可选项、字典与编辑动作。
|
|
54
|
-
* @returns 一行选择器(provider / model / 档位)与"恢复默认"。
|
|
55
|
-
*/
|
|
56
|
-
function ModeRow({
|
|
57
|
-
row,
|
|
58
|
-
groups,
|
|
59
|
-
disabled,
|
|
60
|
-
t,
|
|
61
|
-
onProvider,
|
|
62
|
-
onModel,
|
|
63
|
-
onEffort,
|
|
64
|
-
onReset,
|
|
65
|
-
}: {
|
|
66
|
-
row: ModelDefaultsRow;
|
|
67
|
-
groups: readonly ModelProviderGroup[];
|
|
68
|
-
disabled: boolean;
|
|
69
|
-
t: Translate;
|
|
70
|
-
onProvider: (provider: string) => void;
|
|
71
|
-
onModel: (model: string) => void;
|
|
72
|
-
onEffort: (effort: string) => void;
|
|
73
|
-
onReset: () => void;
|
|
74
|
-
}) {
|
|
75
|
-
const models = groups.find((group) => group.id === row.value?.provider)?.models ?? [];
|
|
76
|
-
const model = models.find((entry) => entry.id === row.value?.model);
|
|
77
|
-
const efforts = model?.reasoning?.efforts ?? [];
|
|
78
|
-
// 存着的档位即使目录里没有也要看得见(否则它成了看不见的值)。
|
|
79
|
-
const withEffort = efforts.length > 0 || row.value?.reasoningEffort !== undefined;
|
|
80
|
-
|
|
81
|
-
return (
|
|
82
|
-
<div {...styling.props(styles.row)}>
|
|
83
|
-
<div {...styling.props(styles.rowHead)}>
|
|
84
|
-
<span {...styling.props(styles.rowName)}>{row.name}</span>
|
|
85
|
-
{row.overridden ? <span {...styling.props(styles.badge)}>{t("overridden")}</span> : null}
|
|
86
|
-
<button
|
|
87
|
-
type="button"
|
|
88
|
-
{...styling.props(styles.reset)}
|
|
89
|
-
disabled={disabled || !row.overridden}
|
|
90
|
-
onClick={onReset}
|
|
91
|
-
>
|
|
92
|
-
{t("reset")}
|
|
93
|
-
</button>
|
|
94
|
-
</div>
|
|
95
|
-
{row.description === undefined ? null : <p {...styling.props(styles.rowHint)}>{row.description}</p>}
|
|
96
|
-
<div {...styling.props(styles.fields)}>
|
|
97
|
-
<label {...styling.props(styles.field)}>
|
|
98
|
-
<span {...styling.props(styles.fieldLabel)}>{t("providerLabel")}</span>
|
|
99
|
-
<select
|
|
100
|
-
{...styling.props(styles.select)}
|
|
101
|
-
value={row.value?.provider ?? ""}
|
|
102
|
-
disabled={disabled}
|
|
103
|
-
onChange={(event) => {
|
|
104
|
-
onProvider(event.target.value);
|
|
105
|
-
}}
|
|
106
|
-
>
|
|
107
|
-
<option value="">{t("inheritOption")}</option>
|
|
108
|
-
{options(
|
|
109
|
-
groups.map((group) => ({ id: group.id, name: group.name })),
|
|
110
|
-
row.value?.provider,
|
|
111
|
-
t,
|
|
112
|
-
).map((option) => (
|
|
113
|
-
<option key={option.id} value={option.id}>
|
|
114
|
-
{option.name}
|
|
115
|
-
</option>
|
|
116
|
-
))}
|
|
117
|
-
</select>
|
|
118
|
-
</label>
|
|
119
|
-
<label {...styling.props(styles.field)}>
|
|
120
|
-
<span {...styling.props(styles.fieldLabel)}>{t("modelLabel")}</span>
|
|
121
|
-
<select
|
|
122
|
-
{...styling.props(styles.select)}
|
|
123
|
-
value={row.value?.model ?? ""}
|
|
124
|
-
disabled={disabled || row.value === undefined}
|
|
125
|
-
onChange={(event) => {
|
|
126
|
-
onModel(event.target.value);
|
|
127
|
-
}}
|
|
128
|
-
>
|
|
129
|
-
<option value="">{t("chooseModel")}</option>
|
|
130
|
-
{options(
|
|
131
|
-
models.map((entry) => ({ id: entry.id, name: entry.name })),
|
|
132
|
-
row.value?.model,
|
|
133
|
-
t,
|
|
134
|
-
).map((option) => (
|
|
135
|
-
<option key={option.id} value={option.id}>
|
|
136
|
-
{option.name}
|
|
137
|
-
</option>
|
|
138
|
-
))}
|
|
139
|
-
</select>
|
|
140
|
-
{row.invalid ? (
|
|
141
|
-
<span {...styling.props(styles.invalid)} role="status">
|
|
142
|
-
{t("modelRequired")}
|
|
143
|
-
</span>
|
|
144
|
-
) : null}
|
|
145
|
-
</label>
|
|
146
|
-
{withEffort ? (
|
|
147
|
-
<label {...styling.props(styles.field)}>
|
|
148
|
-
<span {...styling.props(styles.fieldLabel)}>{t("effortLabel")}</span>
|
|
149
|
-
<select
|
|
150
|
-
{...styling.props(styles.select)}
|
|
151
|
-
value={row.value?.reasoningEffort ?? ""}
|
|
152
|
-
disabled={disabled || row.value?.model === undefined || row.value.model === ""}
|
|
153
|
-
onChange={(event) => {
|
|
154
|
-
onEffort(event.target.value);
|
|
155
|
-
}}
|
|
156
|
-
>
|
|
157
|
-
<option value="">{t("effortDefaultOption")}</option>
|
|
158
|
-
{options(
|
|
159
|
-
efforts.map((effort) => ({ id: effort.id, name: effort.name })),
|
|
160
|
-
row.value?.reasoningEffort,
|
|
161
|
-
t,
|
|
162
|
-
).map((option) => (
|
|
163
|
-
<option key={option.id} value={option.id}>
|
|
164
|
-
{option.name}
|
|
165
|
-
</option>
|
|
166
|
-
))}
|
|
167
|
-
</select>
|
|
168
|
-
</label>
|
|
169
|
-
) : null}
|
|
170
|
-
</div>
|
|
171
|
-
</div>
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* 渲染这张卡片(或它的一句话说明)。
|
|
177
|
-
* @param props - 字典、快照,以及编辑与保存 / 丢弃动作。
|
|
178
|
-
* @returns 摘要文本,或每个模式一行的默认模型表单。
|
|
179
|
-
*/
|
|
180
|
-
export function ModelDefaultsCard(props: ModelDefaultsCardProps) {
|
|
181
|
-
const { t } = props;
|
|
182
|
-
const state = props.useModelDefaultsCard((snapshot) => snapshot);
|
|
183
|
-
const headingId = useId();
|
|
184
|
-
if (props.view === "summary") return t("cardDescription");
|
|
185
|
-
const disabled = !state.writable || state.saving;
|
|
186
|
-
|
|
187
|
-
return (
|
|
188
|
-
<SettingsForm labels={formLabels(t)} state={state} onSave={props.save} onDiscard={props.discard}>
|
|
189
|
-
{state.available
|
|
190
|
-
? (
|
|
191
|
-
<section {...styling.props(styles.section)} aria-labelledby={headingId}>
|
|
192
|
-
<h3 {...styling.props(styles.heading)} id={headingId}>{t("defaultsTitle")}</h3>
|
|
193
|
-
<p {...styling.props(styles.hint)}>{t("defaultsHint")}</p>
|
|
194
|
-
{state.rosterStatus === "error" ? <p {...styling.props(styles.notice)} role="status">{t("rosterFailed")}</p> : null}
|
|
195
|
-
{state.directoryStatus === "error"
|
|
196
|
-
? <p {...styling.props(styles.notice)} role="status">{t("directoryFailed")}</p>
|
|
197
|
-
: null}
|
|
198
|
-
{state.directoryFailures.length > 0
|
|
199
|
-
? (
|
|
200
|
-
<p {...styling.props(styles.notice)} role="status">
|
|
201
|
-
{t("directoryPartial", { names: state.directoryFailures.join("、") })}
|
|
202
|
-
</p>
|
|
203
|
-
)
|
|
204
|
-
: null}
|
|
205
|
-
<div {...styling.props(styles.rows)}>
|
|
206
|
-
{state.rows.map((row) => (
|
|
207
|
-
<ModeRow
|
|
208
|
-
key={row.id}
|
|
209
|
-
row={row}
|
|
210
|
-
groups={state.directory}
|
|
211
|
-
disabled={disabled}
|
|
212
|
-
t={t}
|
|
213
|
-
onProvider={(provider) => {
|
|
214
|
-
props.setProvider(row.id, provider);
|
|
215
|
-
}}
|
|
216
|
-
onModel={(model) => {
|
|
217
|
-
props.setModel(row.id, model);
|
|
218
|
-
}}
|
|
219
|
-
onEffort={(effort) => {
|
|
220
|
-
props.setEffort(row.id, effort);
|
|
221
|
-
}}
|
|
222
|
-
onReset={() => {
|
|
223
|
-
props.resetMode(row.id);
|
|
224
|
-
}}
|
|
225
|
-
/>
|
|
226
|
-
))}
|
|
227
|
-
</div>
|
|
228
|
-
</section>
|
|
229
|
-
)
|
|
230
|
-
: null}
|
|
231
|
-
</SettingsForm>
|
|
232
|
-
);
|
|
233
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/* 会话头部的模式标签:静态装饰,永远不是控件——会话的历史是在某个模式的工具与提示词下产生的,
|
|
2
|
-
这里只报出它运行的是哪个模式。 */
|
|
3
|
-
|
|
4
|
-
.label {
|
|
5
|
-
display: inline-flex;
|
|
6
|
-
align-items: center;
|
|
7
|
-
gap: 4px;
|
|
8
|
-
max-width: 180px;
|
|
9
|
-
padding: 0 6px;
|
|
10
|
-
height: 22px;
|
|
11
|
-
border-radius: 6px;
|
|
12
|
-
background: var(--dsw-alias-bg-layer-2);
|
|
13
|
-
font-size: 12px;
|
|
14
|
-
line-height: 22px;
|
|
15
|
-
color: var(--dsw-alias-label-secondary);
|
|
16
|
-
white-space: nowrap;
|
|
17
|
-
overflow: hidden;
|
|
18
|
-
text-overflow: ellipsis;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
.icon {
|
|
22
|
-
flex: none;
|
|
23
|
-
opacity: 0.7;
|
|
24
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 会话头部的模式标签:**只读**。
|
|
3
|
-
*
|
|
4
|
-
* 一个会话的装配在它开始第一轮时就定下了,头部又是"跑起来之后"才值得看的东西——在这里放一个能切模式的
|
|
5
|
-
* 控件,等于承诺一次 host 会拒绝的切换。说出这个会话运行的是什么,才是这里诚实的表述;选择本身在
|
|
6
|
-
* 新会话屏幕的 chip 上。
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { IconAgentPresetOutlineRegular } from "@deepseek-ai/dsh-client-ui-primitives";
|
|
10
|
-
import type { PropsLocale, PropsRuntime } from "@deepseek-ai/dsh-client-ui-slots";
|
|
11
|
-
// Type-only:拉入本包 host 半的投影声明(`sessionMode`)。
|
|
12
|
-
import type {} from "../index.ts";
|
|
13
|
-
// Type-only:拉入 ui-conversation 的 SlotMap 合并(头部动作行)。
|
|
14
|
-
import type {} from "@morlay/dsh-client-ui-conversation/client";
|
|
15
|
-
import { useRoster } from "./use-roster.ts";
|
|
16
|
-
import css from "./SessionModeLabel.module.css";
|
|
17
|
-
|
|
18
|
-
/** 完整 props:头部动作槽位的运行时 props + 本包的字典。 */
|
|
19
|
-
export type SessionModeLabelProps = PropsRuntime<"conversation.session.header.actions"> &
|
|
20
|
-
PropsLocale<"session-mode">;
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* 渲染这个会话的模式名。
|
|
24
|
-
* @param props - 槽位合成的 props。
|
|
25
|
-
* @returns 标签;清单没读到时返回 null(此时名字无从得知)。
|
|
26
|
-
*/
|
|
27
|
-
export function SessionModeLabel({ sessionId, useSessions, t }: SessionModeLabelProps) {
|
|
28
|
-
const roster = useRoster();
|
|
29
|
-
const mode = useSessions((state) => {
|
|
30
|
-
const value = state.byId[sessionId]?.projectionValues?.sessionMode;
|
|
31
|
-
return typeof value === "string" ? value : undefined;
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
if (roster === undefined) return null;
|
|
35
|
-
|
|
36
|
-
const id = mode ?? roster.default;
|
|
37
|
-
const chosen = roster.modes.find((entry) => entry.id === id);
|
|
38
|
-
return (
|
|
39
|
-
<span className={css.label} title={chosen?.description ?? t("headerHint")}>
|
|
40
|
-
<IconAgentPresetOutlineRegular size={14} className={css.icon} />
|
|
41
|
-
{chosen?.name ?? id}
|
|
42
|
-
</span>
|
|
43
|
-
);
|
|
44
|
-
}
|