@klarkxy/dsh-current-title 0.1.0
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/LICENSE +42 -0
- package/README.md +27 -0
- package/cordis.patch.yml +5 -0
- package/docs/README.zh-CN.md +13 -0
- package/lib/client.inner.cjs +455 -0
- package/lib/client.inner.cjs.map +1 -0
- package/lib/client.js +625 -0
- package/lib/contracts.d.ts +74 -0
- package/lib/contracts.js +43 -0
- package/lib/contracts.js.map +1 -0
- package/lib/index.d.ts +160 -0
- package/lib/index.js +714 -0
- package/lib/index.js.map +1 -0
- package/package.json +103 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
The Star And Thank Author License (SATA)
|
|
2
|
+
Version 2.1, September 2026
|
|
3
|
+
|
|
4
|
+
Copyright © 2026 Klarkxy(278370456@qq.com)
|
|
5
|
+
|
|
6
|
+
Project Url: https://github.com/klarkxy/dsh-editor
|
|
7
|
+
https://github.com/klarkxy/sata-license
|
|
8
|
+
https://github.com/klarkxy/dsh-plugins
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in
|
|
18
|
+
all copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
And wait, one more thing — an invitation, not a condition: it would be best if
|
|
21
|
+
you star/+1/like the project(s) in the project url section above, and thank the
|
|
22
|
+
author(s) in the Copyright section. You may, and arguably you should — but the
|
|
23
|
+
permissions above hold either way, star or no star.
|
|
24
|
+
|
|
25
|
+
Here are some suggested ways:
|
|
26
|
+
|
|
27
|
+
- Email the authors a thank-you letter, and make friends with him/her/them.
|
|
28
|
+
- Report bugs or issues.
|
|
29
|
+
- Tell friends what a wonderful project this is.
|
|
30
|
+
- And, sure, you can just express thanks in your mind without telling the world.
|
|
31
|
+
|
|
32
|
+
Contributors of this project by forking have the option to add his/her name and
|
|
33
|
+
forked project url at copyright and project url sections, but shall not delete
|
|
34
|
+
or modify anything else in these two sections.
|
|
35
|
+
|
|
36
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
37
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
38
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
39
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
40
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
41
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
42
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# @klarkxy/dsh-current-title
|
|
2
|
+
|
|
3
|
+
Keep a DSH session title aligned with the current human task.
|
|
4
|
+
|
|
5
|
+
[简体中文](https://github.com/klarkxy/dsh-editor/blob/main/packages/dsh-current-title/docs/README.zh-CN.md)
|
|
6
|
+
|
|
7
|
+
Migrated from [`dsh-plugins/dsh-current-title`](https://github.com/klarkxy/dsh-plugins/tree/main/plugins/dsh-current-title). Title type labels stay bilingual (`功能` / `Feature`, and the rest of the fixed vocabulary). Generation uses `@klarkxy/dsh-ai-services` purpose `current-title.generate` on the weak role.
|
|
8
|
+
|
|
9
|
+
The bundle insert is enabled after installation. It does not permanently turn off the built-in first-prompt title provider: this entry claims the native `sessionTitle` slot only while active, and disabling restores that owner when this package still owns the slot and the displaced loader entry is unchanged.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
Requires Node.js ≥22 and DSH `0.1.7-alpha.1`. The **当前标题** entry starts enabled and can be disabled in plugin settings. Native title storage and scheduling come from host `sessionTitle`; generation needs `@klarkxy/dsh-ai-services`.
|
|
14
|
+
|
|
15
|
+
## Title shape
|
|
16
|
+
|
|
17
|
+
```text
|
|
18
|
+
0903 | 修复 | 登录回调失败
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Only recent real human `user/message` events are used. Plugin auxiliary messages are ignored. A manual rename is kept by native title storage; **重新生成** unpins and retries. Disable cancels work and drops late results.
|
|
22
|
+
|
|
23
|
+
## License
|
|
24
|
+
|
|
25
|
+
[SATA License 2.1](LICENSE), including attribution for the original `dsh-plugins` sources.
|
|
26
|
+
|
|
27
|
+
The Editor preinstalls this feature enabled. In the Editor, use Settings → Plugins to switch it without restarting. Standalone DSH must load `@klarkxy/dsh-ai-services` before this package; installation or removal may require a restart when the host asks for one.
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# @klarkxy/dsh-current-title
|
|
2
|
+
|
|
3
|
+
按最近人工消息更新会话标题。
|
|
4
|
+
|
|
5
|
+
[English](https://github.com/klarkxy/dsh-editor/blob/main/packages/dsh-current-title/README.md)
|
|
6
|
+
|
|
7
|
+
从 [`dsh-plugins/dsh-current-title`](https://github.com/klarkxy/dsh-plugins/tree/main/plugins/dsh-current-title) 迁入。类型前缀中英对照。生成走 `@klarkxy/dsh-ai-services` 的弱模型用途 `current-title.generate`。
|
|
8
|
+
|
|
9
|
+
安装后默认启用,不会永久停用宿主自带的标题插件。启用期间占用 `sessionTitle` 槽位;关闭时若仍由本包持有,且被让出的加载项身份未变,则交还。
|
|
10
|
+
|
|
11
|
+
需要 Node.js ≥22 与 DSH `0.1.7-alpha.1`。标题写入宿主 `sessionTitle`。生成依赖 `@klarkxy/dsh-ai-services`。
|
|
12
|
+
|
|
13
|
+
许可证为 [SATA 2.1](../LICENSE),保留原插件出处。
|
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let react = require("react");
|
|
3
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
4
|
+
//#region ../dsh-ai-services/src/client-utils.ts
|
|
5
|
+
/** The Editor retains its manuscript session separately; native DSH exposes the main-view binding. */
|
|
6
|
+
function selectedSessionId(client) {
|
|
7
|
+
return client.uiWorkspace.current ? client.uiWorkspace.current.getSnapshot()?.sessionId ?? "" : client.uiSession.adapter.current.getSnapshot().key ?? "";
|
|
8
|
+
}
|
|
9
|
+
/** Owner props win; native settings only supplies close, so resolve its selected session. */
|
|
10
|
+
function useNativeSeat(client, props) {
|
|
11
|
+
const current = (0, react.useSyncExternalStore)((0, react.useCallback)((fn) => (client.uiWorkspace.current ?? client.uiSession.adapter.current).subscribe(fn), [client]), (0, react.useCallback)(() => selectedSessionId(client), [client]), () => "");
|
|
12
|
+
const language = (0, react.useSyncExternalStore)((0, react.useCallback)((fn) => client.locale.subscribe(fn), [client]), (0, react.useCallback)(() => client.locale.getSnapshot().active, [client]), () => "en");
|
|
13
|
+
const row = props && typeof props === "object" ? props : {};
|
|
14
|
+
const owner = row.owner && typeof row.owner === "object" ? row.owner : {};
|
|
15
|
+
const sessionId = typeof row.sessionId === "string" ? row.sessionId : typeof owner.sessionId === "string" ? owner.sessionId : current;
|
|
16
|
+
const requestedLocale = row.locale ?? owner.locale ?? language;
|
|
17
|
+
return {
|
|
18
|
+
sessionId,
|
|
19
|
+
locale: String(requestedLocale).startsWith("zh") ? "zh" : "en",
|
|
20
|
+
hidden: row.hidden === true || owner.hidden === true
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** Refresh on native log boundaries/reconnect/focus. Read-only settling is bounded to an observed job. */
|
|
24
|
+
function useFeatureRefresh(client, sessionId, refresh, working = false, enabled = true) {
|
|
25
|
+
const refreshRef = (0, react.useRef)(refresh);
|
|
26
|
+
refreshRef.current = refresh;
|
|
27
|
+
(0, react.useEffect)(() => {
|
|
28
|
+
if (!enabled) return;
|
|
29
|
+
let timer;
|
|
30
|
+
const queue = () => {
|
|
31
|
+
if (typeof document !== "undefined" && document.visibilityState === "hidden") return;
|
|
32
|
+
clearTimeout(timer);
|
|
33
|
+
timer = setTimeout(() => refreshRef.current(), 150);
|
|
34
|
+
};
|
|
35
|
+
const stopEvents = (sessionId ? client.sessions.binding?.(sessionId)?.eventSource : void 0)?.subscribe(queue);
|
|
36
|
+
const stopConnection = client.connection?.generation?.subscribe(queue);
|
|
37
|
+
const visibility = () => {
|
|
38
|
+
if (document.visibilityState === "visible") queue();
|
|
39
|
+
};
|
|
40
|
+
window.addEventListener("focus", queue);
|
|
41
|
+
document.addEventListener("visibilitychange", visibility);
|
|
42
|
+
document.addEventListener("toggle", queue, true);
|
|
43
|
+
return () => {
|
|
44
|
+
clearTimeout(timer);
|
|
45
|
+
stopEvents?.();
|
|
46
|
+
stopConnection?.();
|
|
47
|
+
window.removeEventListener("focus", queue);
|
|
48
|
+
document.removeEventListener("visibilitychange", visibility);
|
|
49
|
+
document.removeEventListener("toggle", queue, true);
|
|
50
|
+
};
|
|
51
|
+
}, [
|
|
52
|
+
client,
|
|
53
|
+
sessionId,
|
|
54
|
+
enabled
|
|
55
|
+
]);
|
|
56
|
+
(0, react.useEffect)(() => {
|
|
57
|
+
if (!enabled || !working) return;
|
|
58
|
+
let delay = 250;
|
|
59
|
+
const until = Date.now() + 3e5;
|
|
60
|
+
let timer;
|
|
61
|
+
const tick = () => {
|
|
62
|
+
if (Date.now() >= until) return;
|
|
63
|
+
if (document.visibilityState !== "hidden") refreshRef.current();
|
|
64
|
+
delay = Math.min(delay * 2, 2e3);
|
|
65
|
+
timer = setTimeout(tick, delay);
|
|
66
|
+
};
|
|
67
|
+
timer = setTimeout(tick, delay);
|
|
68
|
+
return () => clearTimeout(timer);
|
|
69
|
+
}, [
|
|
70
|
+
client,
|
|
71
|
+
sessionId,
|
|
72
|
+
working,
|
|
73
|
+
enabled
|
|
74
|
+
]);
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/contracts.ts
|
|
78
|
+
const RPC_CHANNEL = "/dsh-current-title";
|
|
79
|
+
const TITLE_CLIENT_SERVICE = "dshCurrentTitleClient";
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region src/client-work.ts
|
|
82
|
+
const titleCopy = {
|
|
83
|
+
zh: {
|
|
84
|
+
label: "当前标题",
|
|
85
|
+
pinned: "已手动命名",
|
|
86
|
+
generating: "正在生成标题…",
|
|
87
|
+
regenerate: "重新生成",
|
|
88
|
+
locale: "类型标签语言",
|
|
89
|
+
auto: "自动",
|
|
90
|
+
saved: "已保存。",
|
|
91
|
+
failed: "无法读取标题设置。"
|
|
92
|
+
},
|
|
93
|
+
en: {
|
|
94
|
+
label: "Current title",
|
|
95
|
+
pinned: "Renamed manually",
|
|
96
|
+
generating: "Generating title…",
|
|
97
|
+
regenerate: "Regenerate",
|
|
98
|
+
locale: "Type-label language",
|
|
99
|
+
auto: "Auto",
|
|
100
|
+
saved: "Saved.",
|
|
101
|
+
failed: "Could not read title settings."
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
function shouldSkipTitleRefresh(input) {
|
|
105
|
+
return input.busy;
|
|
106
|
+
}
|
|
107
|
+
function createTitleClientWork() {
|
|
108
|
+
let generation = 0;
|
|
109
|
+
let request = 0;
|
|
110
|
+
let disposed = false;
|
|
111
|
+
return {
|
|
112
|
+
get disposed() {
|
|
113
|
+
return disposed;
|
|
114
|
+
},
|
|
115
|
+
beginLoad() {
|
|
116
|
+
generation += 1;
|
|
117
|
+
request += 1;
|
|
118
|
+
return generation;
|
|
119
|
+
},
|
|
120
|
+
captureLoad() {
|
|
121
|
+
return generation;
|
|
122
|
+
},
|
|
123
|
+
beginRequest() {
|
|
124
|
+
request += 1;
|
|
125
|
+
return {
|
|
126
|
+
generation,
|
|
127
|
+
request
|
|
128
|
+
};
|
|
129
|
+
},
|
|
130
|
+
isLoad(generationToken) {
|
|
131
|
+
return !disposed && generation === generationToken;
|
|
132
|
+
},
|
|
133
|
+
isRequest(token) {
|
|
134
|
+
return !disposed && generation === token.generation && request === token.request;
|
|
135
|
+
},
|
|
136
|
+
dispose() {
|
|
137
|
+
disposed = true;
|
|
138
|
+
generation += 1;
|
|
139
|
+
request += 1;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function unwrap(result) {
|
|
144
|
+
if (!result.ok) throw new Error(result.error.message);
|
|
145
|
+
return result.value;
|
|
146
|
+
}
|
|
147
|
+
async function invoke(call, endpoint, payload) {
|
|
148
|
+
return unwrap(await call(endpoint, payload));
|
|
149
|
+
}
|
|
150
|
+
async function runTitleStatusLoad(input) {
|
|
151
|
+
try {
|
|
152
|
+
const status = await invoke(input.call, "status", input.sessionId ? { sessionId: input.sessionId } : {});
|
|
153
|
+
if (!input.isCurrent()) return;
|
|
154
|
+
input.onStatus(status);
|
|
155
|
+
input.onMarker(status);
|
|
156
|
+
} catch {
|
|
157
|
+
if (!input.isCurrent()) return;
|
|
158
|
+
input.onMarker(void 0);
|
|
159
|
+
input.onError(input.failedMessage);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
async function runTitleRegenerateFlow(input) {
|
|
163
|
+
if (!input.sessionId || !input.isCurrent()) return;
|
|
164
|
+
input.onBusy(true);
|
|
165
|
+
try {
|
|
166
|
+
await invoke(input.call, "regenerate", { sessionId: input.sessionId });
|
|
167
|
+
if (!input.isCurrent()) return;
|
|
168
|
+
const status = await invoke(input.call, "status", { sessionId: input.sessionId });
|
|
169
|
+
if (!input.isCurrent()) return;
|
|
170
|
+
input.onStatus(status);
|
|
171
|
+
input.onMarker(status);
|
|
172
|
+
} catch (cause) {
|
|
173
|
+
if (!input.isCurrent()) return;
|
|
174
|
+
input.onError(cause instanceof Error ? cause.message : input.failedMessage);
|
|
175
|
+
} finally {
|
|
176
|
+
if (!input.isCurrent()) return;
|
|
177
|
+
input.onBusy(false);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
async function runTitleSettingsSave(input) {
|
|
181
|
+
input.onBusy(true);
|
|
182
|
+
try {
|
|
183
|
+
const settings = await invoke(input.call, "settings", {
|
|
184
|
+
locale: input.locale,
|
|
185
|
+
expectedRevision: input.expectedRevision
|
|
186
|
+
});
|
|
187
|
+
if (!input.isCurrent()) return;
|
|
188
|
+
input.onSettings(settings);
|
|
189
|
+
input.onNote(input.savedMessage);
|
|
190
|
+
} catch (cause) {
|
|
191
|
+
if (!input.isCurrent()) return;
|
|
192
|
+
input.onError(cause instanceof Error ? cause.message : input.failedMessage);
|
|
193
|
+
} finally {
|
|
194
|
+
if (!input.isCurrent()) return;
|
|
195
|
+
input.onBusy(false);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
//#endregion
|
|
199
|
+
//#region src/marker.ts
|
|
200
|
+
function createTitleClientMarker() {
|
|
201
|
+
return { active: false };
|
|
202
|
+
}
|
|
203
|
+
function applyMarkerFromStatus(marker, status, live) {
|
|
204
|
+
if (!live) return;
|
|
205
|
+
marker.active = status?.support.weOwn === true;
|
|
206
|
+
}
|
|
207
|
+
function disposeTitleClientMarker(marker) {
|
|
208
|
+
marker.active = false;
|
|
209
|
+
}
|
|
210
|
+
//#endregion
|
|
211
|
+
//#region src/client.tsx
|
|
212
|
+
const name = "dsh-current-title-client";
|
|
213
|
+
const inject = [
|
|
214
|
+
"slots",
|
|
215
|
+
"connection",
|
|
216
|
+
"sessions",
|
|
217
|
+
"locale",
|
|
218
|
+
"uiWorkspace",
|
|
219
|
+
"uiSession"
|
|
220
|
+
];
|
|
221
|
+
function TitleSettings(props) {
|
|
222
|
+
const seat = useNativeSeat(props.client, props.owner);
|
|
223
|
+
const sessionId = seat.sessionId;
|
|
224
|
+
const text = titleCopy[seat.locale];
|
|
225
|
+
const [status, setStatus] = (0, react.useState)();
|
|
226
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
227
|
+
const [note, setNote] = (0, react.useState)("");
|
|
228
|
+
const [error, setError] = (0, react.useState)("");
|
|
229
|
+
const workRef = (0, react.useRef)(void 0);
|
|
230
|
+
if (!workRef.current) workRef.current = createTitleClientWork();
|
|
231
|
+
const work = workRef.current;
|
|
232
|
+
const busyRef = (0, react.useRef)(busy);
|
|
233
|
+
busyRef.current = busy;
|
|
234
|
+
const call = (endpoint, payload = {}) => props.client.connection.rpc.call(RPC_CHANNEL, endpoint, payload);
|
|
235
|
+
function commitMarker(next) {
|
|
236
|
+
applyMarkerFromStatus(props.marker, next, true);
|
|
237
|
+
}
|
|
238
|
+
(0, react.useEffect)(() => () => {
|
|
239
|
+
work.dispose();
|
|
240
|
+
}, [work]);
|
|
241
|
+
(0, react.useEffect)(() => {
|
|
242
|
+
const generation = work.beginLoad();
|
|
243
|
+
const isCurrent = () => work.isLoad(generation);
|
|
244
|
+
setBusy(false);
|
|
245
|
+
setError("");
|
|
246
|
+
setNote("");
|
|
247
|
+
runTitleStatusLoad({
|
|
248
|
+
call,
|
|
249
|
+
sessionId: sessionId || void 0,
|
|
250
|
+
isCurrent,
|
|
251
|
+
failedMessage: text.failed,
|
|
252
|
+
onStatus: setStatus,
|
|
253
|
+
onError: setError,
|
|
254
|
+
onMarker: commitMarker
|
|
255
|
+
});
|
|
256
|
+
return () => {
|
|
257
|
+
if (work.isLoad(generation)) work.beginLoad();
|
|
258
|
+
};
|
|
259
|
+
}, [
|
|
260
|
+
props.client,
|
|
261
|
+
props.marker,
|
|
262
|
+
sessionId,
|
|
263
|
+
text.failed,
|
|
264
|
+
work
|
|
265
|
+
]);
|
|
266
|
+
const refreshStatus = (0, react.useCallback)(() => {
|
|
267
|
+
if (shouldSkipTitleRefresh({ busy: busyRef.current }) || work.disposed) return;
|
|
268
|
+
const generation = work.captureLoad();
|
|
269
|
+
runTitleStatusLoad({
|
|
270
|
+
call,
|
|
271
|
+
sessionId: sessionId || void 0,
|
|
272
|
+
isCurrent: () => work.isLoad(generation) && !shouldSkipTitleRefresh({ busy: busyRef.current }),
|
|
273
|
+
failedMessage: text.failed,
|
|
274
|
+
onStatus: (next) => {
|
|
275
|
+
if (shouldSkipTitleRefresh({ busy: busyRef.current })) return;
|
|
276
|
+
setStatus(next);
|
|
277
|
+
},
|
|
278
|
+
onError: () => {},
|
|
279
|
+
onMarker: (next) => {
|
|
280
|
+
if (shouldSkipTitleRefresh({ busy: busyRef.current })) return;
|
|
281
|
+
commitMarker(next);
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
}, [
|
|
285
|
+
props.client,
|
|
286
|
+
props.marker,
|
|
287
|
+
sessionId,
|
|
288
|
+
text.failed,
|
|
289
|
+
work
|
|
290
|
+
]);
|
|
291
|
+
useFeatureRefresh(props.client, sessionId, refreshStatus, status?.session?.generating === true && !busy, Boolean(sessionId));
|
|
292
|
+
const locale = status?.settings.locale ?? "auto";
|
|
293
|
+
const modes = [
|
|
294
|
+
{
|
|
295
|
+
id: "auto",
|
|
296
|
+
label: text.auto
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
id: "zh",
|
|
300
|
+
label: "中文"
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
id: "en",
|
|
304
|
+
label: "English"
|
|
305
|
+
}
|
|
306
|
+
];
|
|
307
|
+
const title = status?.session?.title;
|
|
308
|
+
const pinned = status?.session?.pinned === true;
|
|
309
|
+
const weOwn = status?.support.weOwn === true;
|
|
310
|
+
const canRegenerate = Boolean(sessionId) && weOwn && !busy;
|
|
311
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
312
|
+
className: "current-title-settings",
|
|
313
|
+
"data-testid": "current-title-settings",
|
|
314
|
+
children: [
|
|
315
|
+
!status && !error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
316
|
+
role: "status",
|
|
317
|
+
children: seat.locale === "en" ? "Loading title settings…" : "正在读取标题设置…"
|
|
318
|
+
}) : null,
|
|
319
|
+
title ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
320
|
+
translate: "no",
|
|
321
|
+
children: title
|
|
322
|
+
}) : null,
|
|
323
|
+
pinned ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
324
|
+
className: "current-title-meta",
|
|
325
|
+
children: text.pinned
|
|
326
|
+
}) : null,
|
|
327
|
+
status?.session?.generating ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
328
|
+
role: "status",
|
|
329
|
+
children: text.generating
|
|
330
|
+
}) : null,
|
|
331
|
+
error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
332
|
+
role: "alert",
|
|
333
|
+
children: error
|
|
334
|
+
}) : null,
|
|
335
|
+
note ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
336
|
+
role: "status",
|
|
337
|
+
children: note
|
|
338
|
+
}) : null,
|
|
339
|
+
sessionId ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
340
|
+
type: "button",
|
|
341
|
+
disabled: !canRegenerate,
|
|
342
|
+
"aria-label": text.regenerate,
|
|
343
|
+
onClick: () => {
|
|
344
|
+
if (!sessionId) return;
|
|
345
|
+
const token = work.beginRequest();
|
|
346
|
+
setError("");
|
|
347
|
+
setNote("");
|
|
348
|
+
runTitleRegenerateFlow({
|
|
349
|
+
call,
|
|
350
|
+
sessionId,
|
|
351
|
+
isCurrent: () => work.isRequest(token),
|
|
352
|
+
failedMessage: text.failed,
|
|
353
|
+
onBusy: setBusy,
|
|
354
|
+
onStatus: setStatus,
|
|
355
|
+
onError: setError,
|
|
356
|
+
onMarker: commitMarker
|
|
357
|
+
});
|
|
358
|
+
},
|
|
359
|
+
children: text.regenerate
|
|
360
|
+
}) : null,
|
|
361
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
|
|
362
|
+
disabled: busy || !status,
|
|
363
|
+
"aria-label": text.locale,
|
|
364
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("legend", { children: text.locale }), modes.map((mode) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
365
|
+
type: "radio",
|
|
366
|
+
name: "current-title-locale",
|
|
367
|
+
value: mode.id,
|
|
368
|
+
checked: locale === mode.id,
|
|
369
|
+
onChange: () => {
|
|
370
|
+
if (!status) return;
|
|
371
|
+
const token = work.beginRequest();
|
|
372
|
+
setError("");
|
|
373
|
+
setNote("");
|
|
374
|
+
runTitleSettingsSave({
|
|
375
|
+
call,
|
|
376
|
+
locale: mode.id,
|
|
377
|
+
expectedRevision: status.settings.revision,
|
|
378
|
+
isCurrent: () => work.isRequest(token),
|
|
379
|
+
savedMessage: text.saved,
|
|
380
|
+
failedMessage: text.failed,
|
|
381
|
+
onBusy: setBusy,
|
|
382
|
+
onSettings: (settings) => setStatus({
|
|
383
|
+
...status,
|
|
384
|
+
settings
|
|
385
|
+
}),
|
|
386
|
+
onNote: setNote,
|
|
387
|
+
onError: setError
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
}), mode.label] }, mode.id))]
|
|
391
|
+
})
|
|
392
|
+
]
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
const styles = `
|
|
396
|
+
.current-title-settings{display:grid;gap:8px;color:inherit;font:400 var(--font-size-2,14px)/1.5 var(--default-font-family,system-ui,sans-serif)}
|
|
397
|
+
.current-title-settings p{margin:0}
|
|
398
|
+
.current-title-settings p[translate="no"]{overflow-wrap:anywhere}
|
|
399
|
+
.current-title-meta{font-size:var(--font-size-1,13px);color:var(--gray-11,inherit)}
|
|
400
|
+
.current-title-settings button{min-height:34px;padding:6px 12px;border:1px solid color-mix(in srgb,currentColor 25%,transparent);border-radius:8px;background:transparent;color:inherit;cursor:pointer;font:inherit;justify-self:start;transition:background-color 150ms ease,color 150ms ease,border-color 150ms ease,box-shadow 150ms ease,transform 150ms ease}
|
|
401
|
+
.current-title-settings button:hover:not(:disabled){background:var(--gray-3,color-mix(in srgb,currentColor 6%,transparent));border-color:color-mix(in srgb,currentColor 35%,transparent)}
|
|
402
|
+
.current-title-settings button:active:not(:disabled){transform:scale(.97)}
|
|
403
|
+
.current-title-settings button:disabled{opacity:.45;cursor:not-allowed}
|
|
404
|
+
.current-title-settings fieldset{margin:0;border:1px solid var(--gray-6,color-mix(in srgb,currentColor 15%,transparent));border-radius:10px;padding:8px 12px;display:grid;gap:8px}
|
|
405
|
+
.current-title-settings label{display:flex;gap:8px;align-items:center}
|
|
406
|
+
.current-title-settings :focus-visible{outline:2px solid currentColor;outline-offset:3px}
|
|
407
|
+
@media(prefers-reduced-motion:reduce){.current-title-settings button{transition:none}}
|
|
408
|
+
`;
|
|
409
|
+
function apply(ctx) {
|
|
410
|
+
const client = ctx;
|
|
411
|
+
const marker = createTitleClientMarker();
|
|
412
|
+
ctx.provide(TITLE_CLIENT_SERVICE, marker);
|
|
413
|
+
ctx.effect(() => {
|
|
414
|
+
let live = true;
|
|
415
|
+
client.connection.rpc.call(RPC_CHANNEL, "status", {}).then((result) => {
|
|
416
|
+
const row = result;
|
|
417
|
+
if (!live) return;
|
|
418
|
+
applyMarkerFromStatus(marker, row?.ok ? row.value : void 0, live);
|
|
419
|
+
}).catch(() => {
|
|
420
|
+
if (live) applyMarkerFromStatus(marker, void 0, live);
|
|
421
|
+
});
|
|
422
|
+
return () => {
|
|
423
|
+
live = false;
|
|
424
|
+
disposeTitleClientMarker(marker);
|
|
425
|
+
};
|
|
426
|
+
}, "current-title.marker");
|
|
427
|
+
ctx.effect(() => {
|
|
428
|
+
if (typeof document === "undefined") return () => {};
|
|
429
|
+
const style = document.createElement("style");
|
|
430
|
+
style.setAttribute("data-plugin", "@klarkxy/dsh-current-title");
|
|
431
|
+
style.textContent = styles;
|
|
432
|
+
document.head.appendChild(style);
|
|
433
|
+
return () => style.remove();
|
|
434
|
+
}, "current-title.styles");
|
|
435
|
+
ctx.effect(() => client.slots.inject("settings.section", () => client.slots.register({
|
|
436
|
+
name: "settings.section",
|
|
437
|
+
id: "current-title",
|
|
438
|
+
order: 80,
|
|
439
|
+
label: "当前标题"
|
|
440
|
+
}, (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TitleSettings, {
|
|
441
|
+
client,
|
|
442
|
+
marker,
|
|
443
|
+
owner: props
|
|
444
|
+
}))), "current-title.settings");
|
|
445
|
+
}
|
|
446
|
+
//#endregion
|
|
447
|
+
exports.TitleSettings = TitleSettings;
|
|
448
|
+
exports.apply = apply;
|
|
449
|
+
exports.applyMarkerFromStatus = applyMarkerFromStatus;
|
|
450
|
+
exports.createTitleClientMarker = createTitleClientMarker;
|
|
451
|
+
exports.disposeTitleClientMarker = disposeTitleClientMarker;
|
|
452
|
+
exports.inject = inject;
|
|
453
|
+
exports.name = name;
|
|
454
|
+
|
|
455
|
+
//# sourceMappingURL=client.inner.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.inner.cjs","names":["useSyncExternalStore","useCallback","useRef","useState","useRef","useCallback"],"sources":["../../dsh-ai-services/src/client-utils.ts","../src/contracts.ts","../src/client-work.ts","../src/marker.ts","../src/client.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef, useSyncExternalStore } from 'react'\n\nexport interface NativeSurfaceClient {\n sessions: {\n binding?(sessionId: string): { eventSource: { subscribe(listener: () => void): () => void } } | undefined\n }\n uiWorkspace: { current?: { getSnapshot(): { sessionId: string } | undefined; subscribe(listener: () => void): () => void } }\n uiSession: { adapter: { current: { getSnapshot(): { key?: string }; subscribe(listener: () => void): () => void } } }\n locale: { getSnapshot(): { active: string }; subscribe(listener: () => void): () => void }\n connection?: { generation?: { subscribe(listener: () => void): () => void } }\n}\n\n/** The Editor retains its manuscript session separately; native DSH exposes the main-view binding. */\nexport function selectedSessionId(client: Pick<NativeSurfaceClient, 'uiWorkspace' | 'uiSession'>): string {\n return client.uiWorkspace.current\n ? client.uiWorkspace.current.getSnapshot()?.sessionId ?? ''\n : client.uiSession.adapter.current.getSnapshot().key ?? ''\n}\n\n/** Owner props win; native settings only supplies close, so resolve its selected session. */\nexport function useNativeSeat(client: NativeSurfaceClient, props: unknown) {\n const current = useSyncExternalStore(\n useCallback((fn: () => void) => (client.uiWorkspace.current ?? client.uiSession.adapter.current).subscribe(fn), [client]),\n useCallback(() => selectedSessionId(client), [client]),\n () => '',\n )\n const language = useSyncExternalStore(\n useCallback((fn: () => void) => client.locale.subscribe(fn), [client]),\n useCallback(() => client.locale.getSnapshot().active, [client]),\n () => 'en',\n )\n const row = props && typeof props === 'object' ? props as Record<string, unknown> : {}\n const owner = row.owner && typeof row.owner === 'object' ? row.owner as Record<string, unknown> : {}\n const sessionId = typeof row.sessionId === 'string' ? row.sessionId\n : typeof owner.sessionId === 'string' ? owner.sessionId : current\n const requestedLocale = row.locale ?? owner.locale ?? language\n return { sessionId, locale: (String(requestedLocale).startsWith('zh') ? 'zh' : 'en') as 'zh' | 'en',\n hidden: row.hidden === true || owner.hidden === true }\n}\n\n/** Refresh on native log boundaries/reconnect/focus. Read-only settling is bounded to an observed job. */\nexport function useFeatureRefresh(\n client: NativeSurfaceClient, sessionId: string, refresh: () => void,\n working = false, enabled = true,\n): void {\n const refreshRef = useRef(refresh)\n refreshRef.current = refresh\n useEffect(() => {\n if (!enabled) return\n let timer: ReturnType<typeof setTimeout> | undefined\n const queue = () => {\n if (typeof document !== 'undefined' && document.visibilityState === 'hidden') return\n clearTimeout(timer)\n timer = setTimeout(() => refreshRef.current(), 150)\n }\n const source = sessionId ? client.sessions.binding?.(sessionId)?.eventSource : undefined\n const stopEvents = source?.subscribe(queue)\n const stopConnection = client.connection?.generation?.subscribe(queue)\n const visibility = () => { if (document.visibilityState === 'visible') queue() }\n window.addEventListener('focus', queue)\n document.addEventListener('visibilitychange', visibility)\n // Details are collapsed by default; opening an existing panel must get current state.\n document.addEventListener('toggle', queue, true)\n return () => {\n clearTimeout(timer)\n stopEvents?.(); stopConnection?.()\n window.removeEventListener('focus', queue)\n document.removeEventListener('visibilitychange', visibility)\n document.removeEventListener('toggle', queue, true)\n }\n }, [client, sessionId, enabled])\n useEffect(() => {\n if (!enabled || !working) return\n let delay = 250\n const until = Date.now() + 300_000\n let timer: ReturnType<typeof setTimeout>\n const tick = () => {\n if (Date.now() >= until) return\n if (document.visibilityState !== 'hidden') refreshRef.current()\n delay = Math.min(delay * 2, 2000)\n timer = setTimeout(tick, delay)\n }\n timer = setTimeout(tick, delay)\n return () => clearTimeout(timer)\n }, [client, sessionId, working, enabled])\n}\n","/** Browser-safe contracts. Shared RPC/result types stay on @klarkxy/dsh-ai-services. */\nexport type { RpcResult } from '@klarkxy/dsh-ai-services/contracts'\n\nexport const PLUGIN_NAME = '@klarkxy/dsh-current-title'\nexport const FEATURE_ID = 'current-title'\nexport const PROVIDER_ID = '@klarkxy/dsh-current-title'\nexport const PURPOSE_ID = 'current-title.generate'\nexport const RPC_CHANNEL = '/dsh-current-title'\nexport const TITLE_CLIENT_SERVICE = 'dshCurrentTitleClient'\nexport const PROMPT_VERSION = 'current-title.v1'\nexport const SCHEMA_VERSION = 'title-json.v1'\nexport const CONTRACT_VERSION = 1\nexport const SETTINGS_KEY = 'current'\n\nexport const DEFAULT_MAX_RECENT_MESSAGES = 8\nexport const DEFAULT_TARGET_WORDS = 5\nexport const DEFAULT_TARGET_CJK = 10\nexport const DEFAULT_MAX_INPUT_BYTES = 4096\nexport const DEFAULT_MAX_OUTPUT_TOKENS = 64\nexport const DEFAULT_TIMEOUT_MS = 60_000\nexport const DEFAULT_MAX_TITLE_BYTES = 80\n\nexport type TitleLocaleMode = 'auto' | 'zh' | 'en'\nexport type TitleSourceKind = 'fallback' | 'provider' | 'user'\n\nexport interface TitleMessage {\n readonly seq: number\n readonly text: string\n}\n\nexport interface TitleSnapshot {\n readonly title: string\n readonly messageSeqs: readonly number[]\n readonly source: { readonly kind: TitleSourceKind; readonly provider?: string }\n readonly eventSeq: number\n readonly updatedAt: number\n}\n\nexport interface TitleSettings {\n revision: number\n locale: TitleLocaleMode\n}\n\nexport interface TitleSupport {\n nativeOwner: string | null\n weOwn: boolean\n limitation?: string\n}\n\nexport interface TitleStatus {\n settings: TitleSettings\n support: TitleSupport\n session?: {\n sessionId: string\n title?: string\n sourceKind?: TitleSourceKind\n generating: boolean\n pinned: boolean\n }\n}\n\nexport interface TitleClientMarker {\n active: boolean\n}\n\nexport interface GenerateConfig {\n readonly maxRecentMessages: number\n readonly locale: TitleLocaleMode\n readonly targetWords: number\n readonly targetCjkCharacters: number\n readonly maxInputBytes: number\n readonly maxOutputTokens: number\n readonly timeoutMs: number\n readonly maxTitleBytes: number\n}\n\nexport function defaultSettings(locale: TitleLocaleMode = 'auto'): TitleSettings {\n return { revision: 0, locale }\n}\n\nexport function defaultGenerateConfig(locale: TitleLocaleMode = 'auto'): GenerateConfig {\n return {\n maxRecentMessages: DEFAULT_MAX_RECENT_MESSAGES,\n locale,\n targetWords: DEFAULT_TARGET_WORDS,\n targetCjkCharacters: DEFAULT_TARGET_CJK,\n maxInputBytes: DEFAULT_MAX_INPUT_BYTES,\n maxOutputTokens: DEFAULT_MAX_OUTPUT_TOKENS,\n timeoutMs: DEFAULT_TIMEOUT_MS,\n maxTitleBytes: DEFAULT_MAX_TITLE_BYTES,\n }\n}\n\nexport function isTitleLocaleMode(value: unknown): value is TitleLocaleMode {\n return value === 'auto' || value === 'zh' || value === 'en'\n}\n","import type { RpcResult, TitleLocaleMode, TitleStatus } from './contracts.ts'\n\nexport const titleCopy = {\n zh: {\n label: '当前标题',\n pinned: '已手动命名',\n generating: '正在生成标题…',\n regenerate: '重新生成',\n locale: '类型标签语言',\n auto: '自动',\n saved: '已保存。',\n failed: '无法读取标题设置。',\n },\n en: {\n label: 'Current title',\n pinned: 'Renamed manually',\n generating: 'Generating title…',\n regenerate: 'Regenerate',\n locale: 'Type-label language',\n auto: 'Auto',\n saved: 'Saved.',\n failed: 'Could not read title settings.',\n },\n} as const\n\nexport function settingsCopy(locale: 'zh' | 'en') {\n return titleCopy[locale] ?? titleCopy.zh\n}\n\nexport function shouldSkipTitleRefresh(input: { busy: boolean }): boolean {\n return input.busy\n}\n\nexport function createTitleClientWork() {\n let generation = 0\n let request = 0\n let disposed = false\n return {\n get disposed() { return disposed },\n beginLoad() {\n generation += 1\n request += 1\n return generation\n },\n captureLoad() {\n return generation\n },\n beginRequest() {\n request += 1\n return { generation, request }\n },\n isLoad(generationToken: number) {\n return !disposed && generation === generationToken\n },\n isRequest(token: { generation: number; request: number }) {\n return !disposed && generation === token.generation && request === token.request\n },\n dispose() {\n disposed = true\n generation += 1\n request += 1\n },\n }\n}\n\nexport type TitleClientWork = ReturnType<typeof createTitleClientWork>\nexport type TitleClientCall = (endpoint: string, payload?: unknown) => Promise<unknown>\n\nfunction unwrap<T>(result: RpcResult<T>): T {\n if (!result.ok) throw new Error(result.error.message)\n return result.value\n}\n\nasync function invoke<T>(call: TitleClientCall, endpoint: string, payload?: unknown): Promise<T> {\n return unwrap(await call(endpoint, payload) as RpcResult<T>)\n}\n\nexport async function runTitleStatusLoad(input: {\n call: TitleClientCall\n sessionId?: string\n isCurrent: () => boolean\n failedMessage: string\n onStatus: (status: TitleStatus) => void\n onError: (message: string) => void\n onMarker: (status: TitleStatus | undefined) => void\n}): Promise<void> {\n try {\n const status = await invoke<TitleStatus>(\n input.call,\n 'status',\n input.sessionId ? { sessionId: input.sessionId } : {},\n )\n if (!input.isCurrent()) return\n input.onStatus(status)\n input.onMarker(status)\n } catch {\n if (!input.isCurrent()) return\n input.onMarker(undefined)\n input.onError(input.failedMessage)\n }\n}\n\nexport async function runTitleRegenerateFlow(input: {\n call: TitleClientCall\n sessionId: string\n isCurrent: () => boolean\n failedMessage: string\n onBusy: (busy: boolean) => void\n onStatus: (status: TitleStatus) => void\n onError: (message: string) => void\n onMarker: (status: TitleStatus | undefined) => void\n}): Promise<void> {\n if (!input.sessionId || !input.isCurrent()) return\n input.onBusy(true)\n try {\n await invoke(input.call, 'regenerate', { sessionId: input.sessionId })\n if (!input.isCurrent()) return\n const status = await invoke<TitleStatus>(input.call, 'status', { sessionId: input.sessionId })\n if (!input.isCurrent()) return\n input.onStatus(status)\n input.onMarker(status)\n } catch (cause) {\n if (!input.isCurrent()) return\n input.onError(cause instanceof Error ? cause.message : input.failedMessage)\n } finally {\n if (!input.isCurrent()) return\n input.onBusy(false)\n }\n}\n\nexport async function runTitleSettingsSave(input: {\n call: TitleClientCall\n locale: TitleLocaleMode\n expectedRevision: number\n isCurrent: () => boolean\n savedMessage: string\n failedMessage: string\n onBusy: (busy: boolean) => void\n onSettings: (settings: TitleStatus['settings']) => void\n onNote: (note: string) => void\n onError: (message: string) => void\n}): Promise<void> {\n input.onBusy(true)\n try {\n const settings = await invoke<TitleStatus['settings']>(input.call, 'settings', {\n locale: input.locale,\n expectedRevision: input.expectedRevision,\n })\n if (!input.isCurrent()) return\n input.onSettings(settings)\n input.onNote(input.savedMessage)\n } catch (cause) {\n if (!input.isCurrent()) return\n input.onError(cause instanceof Error ? cause.message : input.failedMessage)\n } finally {\n if (!input.isCurrent()) return\n input.onBusy(false)\n }\n}\n","import type { TitleClientMarker, TitleStatus } from './contracts.ts'\n\nexport function createTitleClientMarker(): TitleClientMarker {\n return { active: false }\n}\n\nexport function applyMarkerFromStatus(marker: TitleClientMarker, status: Pick<TitleStatus, 'support'> | undefined, live: boolean): void {\n if (!live) return\n marker.active = status?.support.weOwn === true\n}\n\nexport function disposeTitleClientMarker(marker: TitleClientMarker): void {\n marker.active = false\n}\n","import type { Context } from '@deepseek-ai/cordis'\nimport type { NativeSurfaceClient } from '@klarkxy/dsh-ai-services/client-utils'\nimport { useFeatureRefresh, useNativeSeat } from '@klarkxy/dsh-ai-services/client-utils'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport { RPC_CHANNEL, TITLE_CLIENT_SERVICE, type RpcResult, type TitleLocaleMode, type TitleStatus } from './contracts.ts'\nimport {\n createTitleClientWork, runTitleRegenerateFlow, runTitleSettingsSave, runTitleStatusLoad,\n shouldSkipTitleRefresh, titleCopy, type TitleClientCall, type TitleClientWork,\n} from './client-work.ts'\nimport { applyMarkerFromStatus, createTitleClientMarker, disposeTitleClientMarker } from './marker.ts'\n\nexport const name = 'dsh-current-title-client'\nexport const inject = ['slots', 'connection', 'sessions', 'locale', 'uiWorkspace', 'uiSession'] as const\nexport { createTitleClientMarker, disposeTitleClientMarker, applyMarkerFromStatus }\n\ntype Client = NativeSurfaceClient & {\n connection: NonNullable<NativeSurfaceClient['connection']> & {\n rpc: { call(channel: string, endpoint: string, payload: unknown): Promise<unknown> }\n }\n slots: {\n inject(key: string, callback: () => unknown): () => void\n register(spec: { name: string; id: string; label: string; order: number }, render: unknown): () => void\n }\n}\n\nexport function TitleSettings(props: {\n client: Client\n marker: { active: boolean }\n owner?: unknown\n}) {\n const seat = useNativeSeat(props.client, props.owner)\n const sessionId = seat.sessionId\n const text = titleCopy[seat.locale]\n const [status, setStatus] = useState<TitleStatus>()\n const [busy, setBusy] = useState(false)\n const [note, setNote] = useState('')\n const [error, setError] = useState('')\n const workRef = useRef<TitleClientWork>(undefined)\n if (!workRef.current) workRef.current = createTitleClientWork()\n const work = workRef.current\n const busyRef = useRef(busy)\n busyRef.current = busy\n\n const call: TitleClientCall = (endpoint, payload = {}) => (\n props.client.connection.rpc.call(RPC_CHANNEL, endpoint, payload)\n )\n\n function commitMarker(next: TitleStatus | undefined) {\n applyMarkerFromStatus(props.marker, next, true)\n }\n\n useEffect(() => () => { work.dispose() }, [work])\n\n useEffect(() => {\n const generation = work.beginLoad()\n const isCurrent = () => work.isLoad(generation)\n setBusy(false)\n setError('')\n setNote('')\n void runTitleStatusLoad({\n call,\n sessionId: sessionId || undefined,\n isCurrent,\n failedMessage: text.failed,\n onStatus: setStatus,\n onError: setError,\n onMarker: commitMarker,\n })\n return () => { if (work.isLoad(generation)) work.beginLoad() }\n }, [props.client, props.marker, sessionId, text.failed, work])\n\n const refreshStatus = useCallback(() => {\n if (shouldSkipTitleRefresh({ busy: busyRef.current }) || work.disposed) return\n const generation = work.captureLoad()\n void runTitleStatusLoad({\n call,\n sessionId: sessionId || undefined,\n isCurrent: () => work.isLoad(generation) && !shouldSkipTitleRefresh({ busy: busyRef.current }),\n failedMessage: text.failed,\n onStatus: next => {\n if (shouldSkipTitleRefresh({ busy: busyRef.current })) return\n setStatus(next)\n },\n onError: () => {},\n onMarker: next => {\n if (shouldSkipTitleRefresh({ busy: busyRef.current })) return\n commitMarker(next)\n },\n })\n }, [props.client, props.marker, sessionId, text.failed, work])\n\n useFeatureRefresh(\n props.client,\n sessionId,\n refreshStatus,\n status?.session?.generating === true && !busy,\n Boolean(sessionId),\n )\n\n const locale = status?.settings.locale ?? 'auto'\n const modes: Array<{ id: TitleLocaleMode; label: string }> = [\n { id: 'auto', label: text.auto },\n { id: 'zh', label: '中文' },\n { id: 'en', label: 'English' },\n ]\n const title = status?.session?.title\n const pinned = status?.session?.pinned === true\n const weOwn = status?.support.weOwn === true\n const canRegenerate = Boolean(sessionId) && weOwn && !busy\n\n return <section className=\"current-title-settings\" data-testid=\"current-title-settings\">\n {!status && !error ? <p role=\"status\">{seat.locale === 'en' ? 'Loading title settings…' : '正在读取标题设置…'}</p> : null}\n {title ? <p translate=\"no\">{title}</p> : null}\n {pinned ? <p className=\"current-title-meta\">{text.pinned}</p> : null}\n {status?.session?.generating ? <p role=\"status\">{text.generating}</p> : null}\n {error ? <p role=\"alert\">{error}</p> : null}\n {note ? <p role=\"status\">{note}</p> : null}\n {sessionId ? <button type=\"button\" disabled={!canRegenerate} aria-label={text.regenerate}\n onClick={() => {\n if (!sessionId) return\n const token = work.beginRequest()\n setError('')\n setNote('')\n void runTitleRegenerateFlow({\n call,\n sessionId,\n isCurrent: () => work.isRequest(token),\n failedMessage: text.failed,\n onBusy: setBusy,\n onStatus: setStatus,\n onError: setError,\n onMarker: commitMarker,\n })\n }}>{text.regenerate}</button> : null}\n <fieldset disabled={busy || !status} aria-label={text.locale}>\n <legend>{text.locale}</legend>\n {modes.map(mode => (\n <label key={mode.id}>\n <input type=\"radio\" name=\"current-title-locale\" value={mode.id} checked={locale === mode.id}\n onChange={() => {\n if (!status) return\n const token = work.beginRequest()\n setError('')\n setNote('')\n void runTitleSettingsSave({\n call,\n locale: mode.id,\n expectedRevision: status.settings.revision,\n isCurrent: () => work.isRequest(token),\n savedMessage: text.saved,\n failedMessage: text.failed,\n onBusy: setBusy,\n onSettings: settings => setStatus({ ...status, settings }),\n onNote: setNote,\n onError: setError,\n })\n }} />\n {mode.label}\n </label>\n ))}\n </fieldset>\n </section>\n}\n\nconst styles = `\n.current-title-settings{display:grid;gap:8px;color:inherit;font:400 var(--font-size-2,14px)/1.5 var(--default-font-family,system-ui,sans-serif)}\n.current-title-settings p{margin:0}\n.current-title-settings p[translate=\"no\"]{overflow-wrap:anywhere}\n.current-title-meta{font-size:var(--font-size-1,13px);color:var(--gray-11,inherit)}\n.current-title-settings button{min-height:34px;padding:6px 12px;border:1px solid color-mix(in srgb,currentColor 25%,transparent);border-radius:8px;background:transparent;color:inherit;cursor:pointer;font:inherit;justify-self:start;transition:background-color 150ms ease,color 150ms ease,border-color 150ms ease,box-shadow 150ms ease,transform 150ms ease}\n.current-title-settings button:hover:not(:disabled){background:var(--gray-3,color-mix(in srgb,currentColor 6%,transparent));border-color:color-mix(in srgb,currentColor 35%,transparent)}\n.current-title-settings button:active:not(:disabled){transform:scale(.97)}\n.current-title-settings button:disabled{opacity:.45;cursor:not-allowed}\n.current-title-settings fieldset{margin:0;border:1px solid var(--gray-6,color-mix(in srgb,currentColor 15%,transparent));border-radius:10px;padding:8px 12px;display:grid;gap:8px}\n.current-title-settings label{display:flex;gap:8px;align-items:center}\n.current-title-settings :focus-visible{outline:2px solid currentColor;outline-offset:3px}\n@media(prefers-reduced-motion:reduce){.current-title-settings button{transition:none}}\n`\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context { dshCurrentTitleClient: { active: boolean } }\n}\n\nexport function apply(ctx: Context): void {\n const client = ctx as unknown as Client\n const marker = createTitleClientMarker()\n ctx.provide(TITLE_CLIENT_SERVICE, marker)\n ctx.effect(() => {\n let live = true\n void client.connection.rpc.call(RPC_CHANNEL, 'status', {}).then(result => {\n const row = result as RpcResult<TitleStatus>\n if (!live) return\n applyMarkerFromStatus(marker, row?.ok ? row.value : undefined, live)\n }).catch(() => { if (live) applyMarkerFromStatus(marker, undefined, live) })\n return () => { live = false; disposeTitleClientMarker(marker) }\n }, 'current-title.marker')\n ctx.effect(() => {\n if (typeof document === 'undefined') return () => {}\n const style = document.createElement('style')\n style.setAttribute('data-plugin', '@klarkxy/dsh-current-title')\n style.textContent = styles\n document.head.appendChild(style)\n return () => style.remove()\n }, 'current-title.styles')\n ctx.effect(() => client.slots.inject('settings.section', () => client.slots.register({\n name: 'settings.section', id: 'current-title', order: 80, label: '当前标题',\n }, (props: unknown) => <TitleSettings client={client} marker={marker} owner={props} />)), 'current-title.settings')\n}\n"],"mappings":"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"}
|