@klarkxy/dsh-current-title 0.1.7 → 0.2.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 +7 -2
- package/docs/README.zh-CN.md +7 -2
- package/lib/client.inner.cjs +391 -96
- package/lib/client.inner.cjs.map +1 -1
- package/lib/client.js +392 -258
- package/lib/contracts.d.ts +7 -8
- package/lib/contracts.js +1 -22
- package/lib/contracts.js.map +1 -1
- package/lib/index.d.ts +2 -1
- package/lib/index.js +82 -12
- package/lib/index.js.map +1 -1
- package/package.json +18 -11
package/lib/client.js
CHANGED
|
@@ -1,168 +1,7 @@
|
|
|
1
|
-
function __createDshNodeShims() {
|
|
2
|
-
class EventEmitter {
|
|
3
|
-
constructor() {
|
|
4
|
-
this._e = Object.create(null)
|
|
5
|
-
}
|
|
6
|
-
on(type, fn) {
|
|
7
|
-
(this._e[type] ||= []).push(fn)
|
|
8
|
-
return this
|
|
9
|
-
}
|
|
10
|
-
addListener(type, fn) {
|
|
11
|
-
return this.on(type, fn)
|
|
12
|
-
}
|
|
13
|
-
once(type, fn) {
|
|
14
|
-
const wrap = (...args) => {
|
|
15
|
-
this.off(type, wrap)
|
|
16
|
-
fn(...args)
|
|
17
|
-
}
|
|
18
|
-
return this.on(type, wrap)
|
|
19
|
-
}
|
|
20
|
-
off(type, fn) {
|
|
21
|
-
const list = this._e[type]
|
|
22
|
-
if (list) this._e[type] = list.filter((item) => item !== fn)
|
|
23
|
-
return this
|
|
24
|
-
}
|
|
25
|
-
removeListener(type, fn) {
|
|
26
|
-
return this.off(type, fn)
|
|
27
|
-
}
|
|
28
|
-
emit(type, ...args) {
|
|
29
|
-
const list = this._e[type]
|
|
30
|
-
if (!list) return false
|
|
31
|
-
for (const fn of list.slice()) fn(...args)
|
|
32
|
-
return true
|
|
33
|
-
}
|
|
34
|
-
removeAllListeners(type) {
|
|
35
|
-
if (type) delete this._e[type]
|
|
36
|
-
else this._e = Object.create(null)
|
|
37
|
-
return this
|
|
38
|
-
}
|
|
39
|
-
listeners(type) {
|
|
40
|
-
return (this._e[type] || []).slice()
|
|
41
|
-
}
|
|
42
|
-
setMaxListeners() {
|
|
43
|
-
return this
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
class Buffer extends Uint8Array {
|
|
48
|
-
static from(value, encodingOrOffset, length) {
|
|
49
|
-
if (typeof value === 'string') return new Buffer(new TextEncoder().encode(value))
|
|
50
|
-
if (value instanceof ArrayBuffer) return new Buffer(new Uint8Array(value, encodingOrOffset, length))
|
|
51
|
-
if (ArrayBuffer.isView(value)) return new Buffer(new Uint8Array(value.buffer, value.byteOffset, value.byteLength))
|
|
52
|
-
if (Array.isArray(value) || value && typeof value.length === 'number') return new Buffer(Uint8Array.from(value))
|
|
53
|
-
return new Buffer(0)
|
|
54
|
-
}
|
|
55
|
-
static alloc(size, fill) {
|
|
56
|
-
const buf = new Buffer(size)
|
|
57
|
-
if (fill !== undefined && fill !== 0) {
|
|
58
|
-
if (typeof fill === 'string') {
|
|
59
|
-
const bytes = new TextEncoder().encode(fill)
|
|
60
|
-
for (let i = 0; i < size; i += 1) buf[i] = bytes[i % bytes.length]
|
|
61
|
-
} else buf.fill(fill)
|
|
62
|
-
}
|
|
63
|
-
return buf
|
|
64
|
-
}
|
|
65
|
-
static allocUnsafe(size) {
|
|
66
|
-
return new Buffer(size)
|
|
67
|
-
}
|
|
68
|
-
static allocUnsafeSlow(size) {
|
|
69
|
-
return new Buffer(size)
|
|
70
|
-
}
|
|
71
|
-
static concat(list, totalLength) {
|
|
72
|
-
const length = totalLength ?? list.reduce((sum, item) => sum + item.length, 0)
|
|
73
|
-
const out = new Buffer(length)
|
|
74
|
-
let offset = 0
|
|
75
|
-
for (const item of list) {
|
|
76
|
-
out.set(item, offset)
|
|
77
|
-
offset += item.length
|
|
78
|
-
}
|
|
79
|
-
return out
|
|
80
|
-
}
|
|
81
|
-
static isBuffer(value) {
|
|
82
|
-
return value instanceof Buffer
|
|
83
|
-
}
|
|
84
|
-
static byteLength(value) {
|
|
85
|
-
if (typeof value === 'string') return new TextEncoder().encode(value).length
|
|
86
|
-
return value?.byteLength ?? value?.length ?? 0
|
|
87
|
-
}
|
|
88
|
-
static isEncoding(encoding) {
|
|
89
|
-
return /^(utf8|utf-8|ascii|latin1|binary|hex|base64|base64url)$/i.test(String(encoding || ''))
|
|
90
|
-
}
|
|
91
|
-
toString(encoding) {
|
|
92
|
-
if (encoding === 'hex') return [...this].map((byte) => byte.toString(16).padStart(2, '0')).join('')
|
|
93
|
-
if (encoding === 'base64') {
|
|
94
|
-
let binary = ''
|
|
95
|
-
this.forEach((byte) => {
|
|
96
|
-
binary += String.fromCharCode(byte)
|
|
97
|
-
})
|
|
98
|
-
return btoa(binary)
|
|
99
|
-
}
|
|
100
|
-
return new TextDecoder().decode(this)
|
|
101
|
-
}
|
|
102
|
-
slice(start, end) {
|
|
103
|
-
return Buffer.from(super.subarray(start, end))
|
|
104
|
-
}
|
|
105
|
-
copy(target, targetStart = 0, start = 0, end = this.length) {
|
|
106
|
-
const sliced = this.subarray(start, end)
|
|
107
|
-
target.set(sliced, targetStart)
|
|
108
|
-
return sliced.length
|
|
109
|
-
}
|
|
110
|
-
write(string, offset = 0) {
|
|
111
|
-
const bytes = new TextEncoder().encode(string)
|
|
112
|
-
const written = Math.min(bytes.length, this.length - offset)
|
|
113
|
-
this.set(bytes.subarray(0, written), offset)
|
|
114
|
-
return written
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function inherits(ctor, superCtor) {
|
|
119
|
-
ctor.super_ = superCtor
|
|
120
|
-
ctor.prototype = Object.create(superCtor.prototype, {
|
|
121
|
-
constructor: { value: ctor, writable: true, configurable: true },
|
|
122
|
-
})
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (!globalThis.Buffer) globalThis.Buffer = Buffer
|
|
126
|
-
|
|
127
|
-
return {
|
|
128
|
-
buffer: { Buffer, SlowBuffer: Buffer, INSPECT_MAX_BYTES: 50, kMaxLength: 0x7fffffff },
|
|
129
|
-
stream: {
|
|
130
|
-
Stream: EventEmitter,
|
|
131
|
-
Readable: EventEmitter,
|
|
132
|
-
Writable: EventEmitter,
|
|
133
|
-
Duplex: EventEmitter,
|
|
134
|
-
Transform: EventEmitter,
|
|
135
|
-
PassThrough: EventEmitter,
|
|
136
|
-
},
|
|
137
|
-
events: { EventEmitter },
|
|
138
|
-
util: {
|
|
139
|
-
deprecate(fn) {
|
|
140
|
-
return fn
|
|
141
|
-
},
|
|
142
|
-
inherits,
|
|
143
|
-
inspect() {
|
|
144
|
-
return ''
|
|
145
|
-
},
|
|
146
|
-
format(value) {
|
|
147
|
-
return String(value)
|
|
148
|
-
},
|
|
149
|
-
types: { isBuffer: Buffer.isBuffer },
|
|
150
|
-
},
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
1
|
window.__ModuleLoader__.load({
|
|
155
2
|
id: "@klarkxy/dsh-current-title",
|
|
156
3
|
factory: (dshRequire) => {
|
|
157
|
-
var
|
|
158
|
-
var process = globalThis.process || { env: {}, nextTick: function (fn) { var args = [].slice.call(arguments, 1); queueMicrotask(function () { fn.apply(null, args); }); } };
|
|
159
|
-
var require = function (id) {
|
|
160
|
-
if (id === 'buffer' || id === 'stream' || id === 'util' || id === 'events') {
|
|
161
|
-
if (!__nodeShims) __nodeShims = __createDshNodeShims();
|
|
162
|
-
return __nodeShims[id];
|
|
163
|
-
}
|
|
164
|
-
return dshRequire(id);
|
|
165
|
-
};
|
|
4
|
+
var require = dshRequire;
|
|
166
5
|
var module = { exports: {} };
|
|
167
6
|
var exports = module.exports;
|
|
168
7
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
@@ -170,6 +9,30 @@ let react = require("react");
|
|
|
170
9
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
171
10
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
172
11
|
//#region ../dsh-plugin-kit/src/client-utils.ts
|
|
12
|
+
/** Fixed pause for failing background reads; manual work is never replayed. */
|
|
13
|
+
function createPluginReadGate(now = Date.now) {
|
|
14
|
+
let pausedUntil = 0;
|
|
15
|
+
let epoch = 0;
|
|
16
|
+
return {
|
|
17
|
+
canRead: () => now() >= pausedUntil,
|
|
18
|
+
reset: () => {
|
|
19
|
+
epoch++;
|
|
20
|
+
pausedUntil = 0;
|
|
21
|
+
},
|
|
22
|
+
async run(read) {
|
|
23
|
+
const current = epoch;
|
|
24
|
+
try {
|
|
25
|
+
const value = await read();
|
|
26
|
+
if (current === epoch) pausedUntil = 0;
|
|
27
|
+
return value;
|
|
28
|
+
} catch (error) {
|
|
29
|
+
const message = error instanceof Error ? error.message : "";
|
|
30
|
+
if (current === epoch && (/(?:^|\s)transport failure for .+: HTTP (?:404|405|503)\b/.test(message) || error instanceof TypeError && /^(?:Failed to fetch|fetch failed|NetworkError when attempting to fetch resource\.?)$/i.test(message))) pausedUntil = now() + 3e4;
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
173
36
|
/** The Editor retains its manuscript session separately; native DSH exposes the main-view binding. */
|
|
174
37
|
function selectedSessionId(client) {
|
|
175
38
|
const workspace = client.uiWorkspace?.current;
|
|
@@ -555,7 +418,7 @@ ${scope} {
|
|
|
555
418
|
border: 0.5px solid var(--dsw-alias-border-l3);
|
|
556
419
|
border-radius: var(--dsw-radius-md);
|
|
557
420
|
background: var(--dsw-alias-bg-layer-1);
|
|
558
|
-
font-family: var(--ds-font-family-code
|
|
421
|
+
font-family: var(--ds-font-family-code);
|
|
559
422
|
font-size: 12px;
|
|
560
423
|
line-height: 20px;
|
|
561
424
|
color: var(--dsw-alias-label-secondary);
|
|
@@ -631,6 +494,162 @@ ${scope} {
|
|
|
631
494
|
`.replace(/^& (\.dsh-ui-[a-z0-9-]+)(?![+>~])/gm, (_match, className) => `&${className}, & ${className}`);
|
|
632
495
|
}
|
|
633
496
|
//#endregion
|
|
497
|
+
//#region ../dsh-plugin-kit/src/contracts.ts
|
|
498
|
+
/**
|
|
499
|
+
* Remote contribution for model-settings change notifications.
|
|
500
|
+
* Provided by `@klarkxy/dsh-model-hub` (`ModelSettingsChangesController`);
|
|
501
|
+
* subscribers degrade silently when that package is not installed.
|
|
502
|
+
* The stream carries invalidations only — `ready` is never a settings snapshot.
|
|
503
|
+
*/
|
|
504
|
+
const MODEL_SETTINGS_CHANGES_REMOTE = {
|
|
505
|
+
package: "@klarkxy/dsh-model-hub",
|
|
506
|
+
descriptors: [{
|
|
507
|
+
id: "modelSettingsChanges/watch",
|
|
508
|
+
service: "modelSettingsChangesController",
|
|
509
|
+
namespace: "modelSettingsChanges",
|
|
510
|
+
method: "watch",
|
|
511
|
+
mode: "stream",
|
|
512
|
+
invocation: { kind: "direct" },
|
|
513
|
+
parameters: [],
|
|
514
|
+
cancellation: { parameter: "signal" }
|
|
515
|
+
}]
|
|
516
|
+
};
|
|
517
|
+
//#endregion
|
|
518
|
+
//#region ../dsh-plugin-kit/src/model-settings-client.ts
|
|
519
|
+
/** This stream carries invalidations only; ready is never a settings snapshot. */
|
|
520
|
+
function parseModelSettingsChangeFrame(value) {
|
|
521
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Invalid model settings change frame");
|
|
522
|
+
const frame = value;
|
|
523
|
+
if (frame.kind === "ready" && Object.keys(frame).length === 1) return { kind: "ready" };
|
|
524
|
+
if (frame.kind === "changed" && Object.keys(frame).length === 3 && typeof frame.ns === "string" && frame.ns.length > 0 && typeof frame.revision === "number" && Number.isSafeInteger(frame.revision) && frame.revision >= 0) return {
|
|
525
|
+
kind: "changed",
|
|
526
|
+
ns: frame.ns,
|
|
527
|
+
revision: frame.revision
|
|
528
|
+
};
|
|
529
|
+
throw new Error("Invalid model settings change frame");
|
|
530
|
+
}
|
|
531
|
+
MODEL_SETTINGS_CHANGES_REMOTE.package, MODEL_SETTINGS_CHANGES_REMOTE.descriptors.map((descriptor) => ({
|
|
532
|
+
...descriptor,
|
|
533
|
+
result: {
|
|
534
|
+
mode: "strict",
|
|
535
|
+
typeSymbol: "ModelSettings.ChangeFrame",
|
|
536
|
+
create: () => ({ parse: parseModelSettingsChangeFrame })
|
|
537
|
+
}
|
|
538
|
+
}));
|
|
539
|
+
/** Native RemoteStream owns carrier reconnects; no timers or browser transport.
|
|
540
|
+
* A factory shares one logical stream while at least one subscriber remains. */
|
|
541
|
+
function createModelSettingsSubscriber(ctx, namespace) {
|
|
542
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
543
|
+
let injection;
|
|
544
|
+
let current;
|
|
545
|
+
let lifecycle = 0;
|
|
546
|
+
let failureMessage;
|
|
547
|
+
const report = (message) => {
|
|
548
|
+
failureMessage = message;
|
|
549
|
+
for (const listener of [...listeners]) try {
|
|
550
|
+
listener.error?.(message);
|
|
551
|
+
} catch {}
|
|
552
|
+
};
|
|
553
|
+
const invalidate = () => {
|
|
554
|
+
for (const listener of [...listeners]) {
|
|
555
|
+
if (!listeners.has(listener)) continue;
|
|
556
|
+
try {
|
|
557
|
+
listener.change();
|
|
558
|
+
} catch (error) {
|
|
559
|
+
try {
|
|
560
|
+
listener.error?.(error instanceof Error ? error.message : String(error));
|
|
561
|
+
} catch {}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
const start = () => {
|
|
566
|
+
const life = ++lifecycle;
|
|
567
|
+
injection = ctx.inject(["remote", "remote.modelSettingsChanges"], (injected) => {
|
|
568
|
+
if (life !== lifecycle || !listeners.size) return;
|
|
569
|
+
const scope = injected;
|
|
570
|
+
const stream = scope.remote.$stream({
|
|
571
|
+
name: "Model settings sync",
|
|
572
|
+
open: (signal) => scope.remote.modelSettingsChanges.watch(signal),
|
|
573
|
+
ended: (accepted) => /* @__PURE__ */ new Error(`Model settings sync stream ended ${accepted ? "after" : "before"} ready; refresh or reopen the page`)
|
|
574
|
+
});
|
|
575
|
+
const session = {
|
|
576
|
+
stream,
|
|
577
|
+
active: true
|
|
578
|
+
};
|
|
579
|
+
current = session;
|
|
580
|
+
scope.effect(() => () => {
|
|
581
|
+
session.active = false;
|
|
582
|
+
if (current === session) {
|
|
583
|
+
current = void 0;
|
|
584
|
+
if (life === lifecycle && listeners.size) report("Model settings sync service unavailable");
|
|
585
|
+
}
|
|
586
|
+
return stream.dispose();
|
|
587
|
+
});
|
|
588
|
+
(async () => {
|
|
589
|
+
let generation;
|
|
590
|
+
let ready = false;
|
|
591
|
+
try {
|
|
592
|
+
for await (const item of stream) {
|
|
593
|
+
if (!session.active || life !== lifecycle || !listeners.size) break;
|
|
594
|
+
if (item.signal.aborted) continue;
|
|
595
|
+
if (generation !== item.generation) {
|
|
596
|
+
generation = item.generation;
|
|
597
|
+
ready = false;
|
|
598
|
+
}
|
|
599
|
+
const frame = parseModelSettingsChangeFrame(item.value);
|
|
600
|
+
if (frame.kind === "ready") {
|
|
601
|
+
ready = true;
|
|
602
|
+
failureMessage = void 0;
|
|
603
|
+
item.accept();
|
|
604
|
+
invalidate();
|
|
605
|
+
} else {
|
|
606
|
+
if (!ready) throw new Error("Model settings sync change arrived before ready");
|
|
607
|
+
if (namespace === "*" || frame.ns === namespace) invalidate();
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
} catch (error) {
|
|
611
|
+
if (session.active && life === lifecycle) report(error instanceof Error ? error.message : String(error));
|
|
612
|
+
} finally {
|
|
613
|
+
if (current === session) current = void 0;
|
|
614
|
+
session.active = false;
|
|
615
|
+
await stream.dispose().catch(() => {});
|
|
616
|
+
}
|
|
617
|
+
})();
|
|
618
|
+
});
|
|
619
|
+
queueMicrotask(() => {
|
|
620
|
+
if (life === lifecycle && listeners.size && !current) report("Model settings sync service unavailable");
|
|
621
|
+
});
|
|
622
|
+
};
|
|
623
|
+
return Object.freeze({ subscribe(onChange, onError) {
|
|
624
|
+
const listener = {
|
|
625
|
+
change: onChange,
|
|
626
|
+
error: onError
|
|
627
|
+
};
|
|
628
|
+
listeners.add(listener);
|
|
629
|
+
if (!injection) start();
|
|
630
|
+
else if (failureMessage) queueMicrotask(() => {
|
|
631
|
+
if (listeners.has(listener) && failureMessage) try {
|
|
632
|
+
listener.error?.(failureMessage);
|
|
633
|
+
} catch {}
|
|
634
|
+
});
|
|
635
|
+
return () => {
|
|
636
|
+
if (!listeners.delete(listener) || listeners.size) return;
|
|
637
|
+
lifecycle++;
|
|
638
|
+
if (current) {
|
|
639
|
+
current.active = false;
|
|
640
|
+
current.stream.dispose().catch(() => {});
|
|
641
|
+
}
|
|
642
|
+
current = void 0;
|
|
643
|
+
failureMessage = void 0;
|
|
644
|
+
const previous = injection;
|
|
645
|
+
injection = void 0;
|
|
646
|
+
try {
|
|
647
|
+
Promise.resolve(previous?.dispose()).catch(() => {});
|
|
648
|
+
} catch {}
|
|
649
|
+
};
|
|
650
|
+
} });
|
|
651
|
+
}
|
|
652
|
+
//#endregion
|
|
634
653
|
//#region src/contracts.ts
|
|
635
654
|
const RPC_CHANNEL = "/dsh-current-title";
|
|
636
655
|
const TITLE_CLIENT_SERVICE = "dshCurrentTitleClient";
|
|
@@ -649,14 +668,16 @@ const titleCopy = {
|
|
|
649
668
|
loading: "正在读取标题设置…",
|
|
650
669
|
model: "标题模型",
|
|
651
670
|
modelDefault: "跟随会话模型",
|
|
652
|
-
effort: "思考强度",
|
|
653
|
-
effortDefault: "默认",
|
|
654
671
|
cadence: "更新节奏",
|
|
655
672
|
cadenceAll: "跟随当前任务(每条消息)",
|
|
656
673
|
cadenceFirst: "仅首条消息",
|
|
657
674
|
saved: "已保存。",
|
|
658
675
|
failed: "无法读取标题设置。",
|
|
659
|
-
inactive: "标题服务未接管,新消息暂时不会自动更新。"
|
|
676
|
+
inactive: "标题服务未接管,新消息暂时不会自动更新。",
|
|
677
|
+
retry: "重试",
|
|
678
|
+
externalChange: "设置已在别处修改。提示词草稿已保留,请重新加载后确认。",
|
|
679
|
+
syncUnavailable: "自动刷新不可用,请手动重新加载设置。",
|
|
680
|
+
reload: "重新加载(丢弃草稿)"
|
|
660
681
|
},
|
|
661
682
|
en: {
|
|
662
683
|
label: "Auto Title",
|
|
@@ -670,16 +691,38 @@ const titleCopy = {
|
|
|
670
691
|
loading: "Loading title settings…",
|
|
671
692
|
model: "Title model",
|
|
672
693
|
modelDefault: "Use the session model",
|
|
673
|
-
effort: "Reasoning effort",
|
|
674
|
-
effortDefault: "Default",
|
|
675
694
|
cadence: "Update cadence",
|
|
676
695
|
cadenceAll: "Track the current task (every message)",
|
|
677
696
|
cadenceFirst: "First message only",
|
|
678
697
|
saved: "Saved.",
|
|
679
698
|
failed: "Could not read title settings.",
|
|
680
|
-
inactive: "Title generation is not active, so new messages will not rename this session."
|
|
699
|
+
inactive: "Title generation is not active, so new messages will not rename this session.",
|
|
700
|
+
retry: "Retry",
|
|
701
|
+
externalChange: "Settings changed elsewhere. Prompt draft preserved; reload and review.",
|
|
702
|
+
syncUnavailable: "Automatic refresh unavailable; reload settings manually.",
|
|
703
|
+
reload: "Reload (discard draft)"
|
|
681
704
|
}
|
|
682
705
|
};
|
|
706
|
+
/** Only plugin-owned defaults are translated; model output and remote diagnostics stay intact. */
|
|
707
|
+
const titleMessages = [
|
|
708
|
+
["标题设置无效。", "Invalid title settings."],
|
|
709
|
+
["设置已更新,请刷新后重试。", "Settings changed. Refresh and try again."],
|
|
710
|
+
["无法保存标题设置。", "Could not save title settings."],
|
|
711
|
+
["缺少会话。", "A session is required."],
|
|
712
|
+
["找不到该会话。", "Session not found."],
|
|
713
|
+
["标题服务未就绪。", "Title service is not ready."],
|
|
714
|
+
["已取消。", "Cancelled."],
|
|
715
|
+
["标题生成失败。", "Title generation failed."],
|
|
716
|
+
["未知操作。", "Unknown operation."],
|
|
717
|
+
["缺少所需的宿主服务。", "required host services are missing"],
|
|
718
|
+
["原生标题服务位置已被占用。", "native title provider slot is occupied"],
|
|
719
|
+
["其他标题服务已接管;先前的服务未恢复。", "another title provider owns the slot; previous owner was not restored"],
|
|
720
|
+
[titleCopy.zh.saved, titleCopy.en.saved],
|
|
721
|
+
[titleCopy.zh.failed, titleCopy.en.failed]
|
|
722
|
+
];
|
|
723
|
+
function titleMessage(message, locale) {
|
|
724
|
+
return titleMessages.find((pair) => pair.includes(message))?.[locale === "zh" ? 0 : 1] ?? message;
|
|
725
|
+
}
|
|
683
726
|
/** `undefined` draft = not edited; an empty string is a real edit (use built-in instruction). */
|
|
684
727
|
function promptFieldValue(draft, saved) {
|
|
685
728
|
return draft ?? saved;
|
|
@@ -828,28 +871,17 @@ function catalogChoices(value, selected) {
|
|
|
828
871
|
const id = typeof item.id === "string" ? item.id : "";
|
|
829
872
|
if (!id) continue;
|
|
830
873
|
const name = typeof item.name === "string" && item.name ? item.name : id;
|
|
831
|
-
const reasoning = item.reasoning && typeof item.reasoning === "object" ? item.reasoning : void 0;
|
|
832
|
-
const efforts = Array.isArray(reasoning?.efforts) ? reasoning.efforts.flatMap((effort) => {
|
|
833
|
-
if (!effort || typeof effort !== "object") return [];
|
|
834
|
-
const entry = effort;
|
|
835
|
-
return typeof entry.id === "string" && entry.id ? [{
|
|
836
|
-
id: entry.id,
|
|
837
|
-
name: typeof entry.name === "string" && entry.name ? entry.name : entry.id
|
|
838
|
-
}] : [];
|
|
839
|
-
}) : [];
|
|
840
874
|
choices.push({
|
|
841
875
|
provider,
|
|
842
876
|
model: id,
|
|
843
|
-
label: `${providerName} / ${name}
|
|
844
|
-
efforts
|
|
877
|
+
label: `${providerName} / ${name}`
|
|
845
878
|
});
|
|
846
879
|
}
|
|
847
880
|
}
|
|
848
881
|
if (selected.provider && selected.model && !choices.some((item) => item.provider === selected.provider && item.model === selected.model)) choices.push({
|
|
849
882
|
provider: selected.provider,
|
|
850
883
|
model: selected.model,
|
|
851
|
-
label: `${selected.provider} / ${selected.model}
|
|
852
|
-
efforts: []
|
|
884
|
+
label: `${selected.provider} / ${selected.model}`
|
|
853
885
|
});
|
|
854
886
|
return choices;
|
|
855
887
|
}
|
|
@@ -857,24 +889,55 @@ function TitleSettings(props) {
|
|
|
857
889
|
const seat = useNativeSeat(props.client);
|
|
858
890
|
const sessionId = seat.sessionId;
|
|
859
891
|
const text = titleCopy[seat.locale];
|
|
892
|
+
const messages = titleCopy.en;
|
|
860
893
|
const [status, setStatus] = (0, react.useState)();
|
|
861
894
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
862
895
|
const [note, setNote] = (0, react.useState)("");
|
|
863
896
|
const [error, setError] = (0, react.useState)("");
|
|
864
897
|
const [prompt, setPrompt] = (0, react.useState)(void 0);
|
|
865
898
|
const [choices, setChoices] = (0, react.useState)([]);
|
|
899
|
+
const [externalChange, setExternalChange] = (0, react.useState)(false);
|
|
900
|
+
const [syncError, setSyncError] = (0, react.useState)("");
|
|
901
|
+
const [loadingSettings, setLoadingSettings] = (0, react.useState)(true);
|
|
902
|
+
const dirty = (0, react.useRef)(false);
|
|
903
|
+
const external = (0, react.useRef)(false);
|
|
904
|
+
const reading = (0, react.useRef)(false);
|
|
905
|
+
const pending = (0, react.useRef)(false);
|
|
906
|
+
const syncRef = (0, react.useRef)(() => {});
|
|
866
907
|
const workRef = (0, react.useRef)(void 0);
|
|
867
908
|
if (!workRef.current) workRef.current = createTitleClientWork();
|
|
868
909
|
const work = workRef.current;
|
|
910
|
+
const readGateRef = (0, react.useRef)(void 0);
|
|
911
|
+
if (!readGateRef.current) readGateRef.current = createPluginReadGate();
|
|
912
|
+
const readGate = readGateRef.current;
|
|
869
913
|
const busyRef = (0, react.useRef)(busy);
|
|
870
914
|
busyRef.current = busy;
|
|
915
|
+
const changeBusy = (value) => {
|
|
916
|
+
busyRef.current = value;
|
|
917
|
+
setBusy(value);
|
|
918
|
+
if (!value && pending.current) syncRef.current();
|
|
919
|
+
};
|
|
871
920
|
const call = (endpoint, payload = {}) => props.client.connection.rpc.call(RPC_CHANNEL, endpoint, payload);
|
|
921
|
+
const readCall = (endpoint, payload) => readGate.run(() => call(endpoint, payload));
|
|
872
922
|
function commitMarker(next) {
|
|
873
923
|
applyMarkerFromStatus(props.marker, next, true);
|
|
874
924
|
}
|
|
875
925
|
(0, react.useEffect)(() => () => {
|
|
926
|
+
readGate.reset();
|
|
876
927
|
work.dispose();
|
|
877
|
-
}, [work]);
|
|
928
|
+
}, [work, readGate]);
|
|
929
|
+
(0, react.useEffect)(() => props.client.connection.generation?.subscribe(() => {
|
|
930
|
+
readGate.reset();
|
|
931
|
+
work.beginLoad();
|
|
932
|
+
reading.current = false;
|
|
933
|
+
setLoadingSettings(false);
|
|
934
|
+
busyRef.current = false;
|
|
935
|
+
setBusy(false);
|
|
936
|
+
}), [
|
|
937
|
+
props.client,
|
|
938
|
+
work,
|
|
939
|
+
readGate
|
|
940
|
+
]);
|
|
878
941
|
(0, react.useEffect)(() => {
|
|
879
942
|
const catalog = props.client.remote?.session?.modelCatalog;
|
|
880
943
|
if (!catalog) return void 0;
|
|
@@ -897,18 +960,34 @@ function TitleSettings(props) {
|
|
|
897
960
|
]);
|
|
898
961
|
(0, react.useEffect)(() => {
|
|
899
962
|
const generation = work.beginLoad();
|
|
963
|
+
reading.current = true;
|
|
964
|
+
pending.current = false;
|
|
965
|
+
setLoadingSettings(true);
|
|
966
|
+
readGate.reset();
|
|
900
967
|
const isCurrent = () => work.isLoad(generation);
|
|
901
968
|
setBusy(false);
|
|
902
969
|
setError("");
|
|
903
970
|
setNote("");
|
|
904
971
|
runTitleStatusLoad({
|
|
905
|
-
call,
|
|
972
|
+
call: readCall,
|
|
906
973
|
sessionId: sessionId || void 0,
|
|
907
974
|
isCurrent,
|
|
908
|
-
failedMessage:
|
|
909
|
-
onStatus:
|
|
910
|
-
|
|
975
|
+
failedMessage: messages.failed,
|
|
976
|
+
onStatus: (next) => {
|
|
977
|
+
if (!pending.current) setStatus((current) => current && external.current ? {
|
|
978
|
+
...next,
|
|
979
|
+
settings: current.settings
|
|
980
|
+
} : next);
|
|
981
|
+
},
|
|
982
|
+
onError: (message) => {
|
|
983
|
+
if (!pending.current) setError(message);
|
|
984
|
+
},
|
|
911
985
|
onMarker: commitMarker
|
|
986
|
+
}).finally(() => {
|
|
987
|
+
if (!work.isLoad(generation)) return;
|
|
988
|
+
reading.current = false;
|
|
989
|
+
setLoadingSettings(false);
|
|
990
|
+
if (pending.current) syncRef.current();
|
|
912
991
|
});
|
|
913
992
|
return () => {
|
|
914
993
|
if (work.isLoad(generation)) work.beginLoad();
|
|
@@ -917,53 +996,96 @@ function TitleSettings(props) {
|
|
|
917
996
|
props.client,
|
|
918
997
|
props.marker,
|
|
919
998
|
sessionId,
|
|
920
|
-
text.failed,
|
|
921
999
|
work
|
|
922
1000
|
]);
|
|
923
1001
|
const refreshStatus = (0, react.useCallback)(() => {
|
|
924
|
-
if (shouldSkipTitleRefresh({ busy: busyRef.current }) || work.disposed) return;
|
|
925
|
-
|
|
1002
|
+
if (!readGate.canRead() || shouldSkipTitleRefresh({ busy: busyRef.current }) || work.disposed) return;
|
|
1003
|
+
if (reading.current) return;
|
|
1004
|
+
reading.current = true;
|
|
1005
|
+
setLoadingSettings(true);
|
|
1006
|
+
readGate.reset();
|
|
1007
|
+
const token = work.beginRequest();
|
|
926
1008
|
runTitleStatusLoad({
|
|
927
|
-
call,
|
|
1009
|
+
call: readCall,
|
|
928
1010
|
sessionId: sessionId || void 0,
|
|
929
|
-
isCurrent: () => work.
|
|
930
|
-
failedMessage:
|
|
1011
|
+
isCurrent: () => work.isRequest(token) && !shouldSkipTitleRefresh({ busy: busyRef.current }),
|
|
1012
|
+
failedMessage: messages.failed,
|
|
931
1013
|
onStatus: (next) => {
|
|
932
|
-
if (shouldSkipTitleRefresh({ busy: busyRef.current })) return;
|
|
933
|
-
setStatus(
|
|
1014
|
+
if (pending.current || shouldSkipTitleRefresh({ busy: busyRef.current })) return;
|
|
1015
|
+
setStatus((current) => current && external.current ? {
|
|
1016
|
+
...next,
|
|
1017
|
+
settings: current.settings
|
|
1018
|
+
} : next);
|
|
1019
|
+
setError("");
|
|
1020
|
+
},
|
|
1021
|
+
onError: (message) => {
|
|
1022
|
+
if (!pending.current) setError(message);
|
|
934
1023
|
},
|
|
935
|
-
onError: () => {},
|
|
936
1024
|
onMarker: (next) => {
|
|
937
|
-
if (shouldSkipTitleRefresh({ busy: busyRef.current })) return;
|
|
1025
|
+
if (!next || shouldSkipTitleRefresh({ busy: busyRef.current })) return;
|
|
938
1026
|
commitMarker(next);
|
|
939
1027
|
}
|
|
1028
|
+
}).finally(() => {
|
|
1029
|
+
if (!work.isRequest(token)) return;
|
|
1030
|
+
reading.current = false;
|
|
1031
|
+
setLoadingSettings(false);
|
|
1032
|
+
if (pending.current) syncRef.current();
|
|
940
1033
|
});
|
|
941
1034
|
}, [
|
|
942
1035
|
props.client,
|
|
943
1036
|
props.marker,
|
|
944
1037
|
sessionId,
|
|
945
|
-
text.failed,
|
|
946
1038
|
work
|
|
947
1039
|
]);
|
|
948
1040
|
useFeatureRefresh(props.client, sessionId, refreshStatus, status?.session?.generating === true && !busy, Boolean(sessionId));
|
|
1041
|
+
syncRef.current = () => {
|
|
1042
|
+
if (work.disposed) return;
|
|
1043
|
+
if (reading.current || busyRef.current) {
|
|
1044
|
+
pending.current = true;
|
|
1045
|
+
return;
|
|
1046
|
+
}
|
|
1047
|
+
pending.current = false;
|
|
1048
|
+
if (dirty.current) {
|
|
1049
|
+
external.current = true;
|
|
1050
|
+
setExternalChange(true);
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
readGate.reset();
|
|
1054
|
+
refreshStatus();
|
|
1055
|
+
};
|
|
1056
|
+
(0, react.useEffect)(() => {
|
|
1057
|
+
setSyncError("");
|
|
1058
|
+
let live = true;
|
|
1059
|
+
const stop = props.subscribeSettings?.(() => {
|
|
1060
|
+
if (!live) return;
|
|
1061
|
+
setSyncError("");
|
|
1062
|
+
if (dirty.current && !busyRef.current) {
|
|
1063
|
+
external.current = true;
|
|
1064
|
+
setExternalChange(true);
|
|
1065
|
+
}
|
|
1066
|
+
syncRef.current();
|
|
1067
|
+
}, (message) => {
|
|
1068
|
+
if (live) setSyncError(message);
|
|
1069
|
+
});
|
|
1070
|
+
return () => {
|
|
1071
|
+
live = false;
|
|
1072
|
+
stop?.();
|
|
1073
|
+
};
|
|
1074
|
+
}, [props.subscribeSettings, props.client]);
|
|
949
1075
|
const savedPrompt = status?.settings.prompt ?? "";
|
|
950
1076
|
const savedModel = status?.settings.model ?? {
|
|
951
1077
|
provider: "",
|
|
952
1078
|
model: ""
|
|
953
1079
|
};
|
|
1080
|
+
const selectableChoices = choices.some((choice) => choice.provider === savedModel.provider && choice.model === savedModel.model) ? choices : [...choices, ...catalogChoices({}, savedModel)];
|
|
954
1081
|
const modelKey = savedModel.provider && savedModel.model ? `${savedModel.provider}\u001f${savedModel.model}` : "";
|
|
955
1082
|
const title = status?.session?.title;
|
|
956
1083
|
const pinned = status?.session?.pinned === true;
|
|
957
1084
|
const weOwn = status?.support.weOwn === true;
|
|
958
|
-
const canRegenerate = Boolean(sessionId) && weOwn && !busy;
|
|
959
|
-
const selectedChoice = choices.find((item) => item.provider === savedModel.provider && item.model === savedModel.model);
|
|
960
|
-
const efforts = selectedChoice ? [...selectedChoice.efforts] : [];
|
|
961
|
-
if (selectedChoice && savedModel.reasoningEffort && !efforts.some((item) => item.id === savedModel.reasoningEffort)) efforts.push({
|
|
962
|
-
id: savedModel.reasoningEffort,
|
|
963
|
-
name: savedModel.reasoningEffort
|
|
964
|
-
});
|
|
1085
|
+
const canRegenerate = Boolean(sessionId) && weOwn && !busy && !loadingSettings;
|
|
965
1086
|
function saveSettings(patch) {
|
|
966
|
-
if (!status) return;
|
|
1087
|
+
if (!status || busyRef.current || reading.current || external.current) return;
|
|
1088
|
+
readGate.reset();
|
|
967
1089
|
const token = work.beginRequest();
|
|
968
1090
|
setError("");
|
|
969
1091
|
setNote("");
|
|
@@ -972,9 +1094,9 @@ function TitleSettings(props) {
|
|
|
972
1094
|
...patch,
|
|
973
1095
|
expectedRevision: status.settings.revision,
|
|
974
1096
|
isCurrent: () => work.isRequest(token),
|
|
975
|
-
savedMessage:
|
|
976
|
-
failedMessage:
|
|
977
|
-
onBusy:
|
|
1097
|
+
savedMessage: messages.saved,
|
|
1098
|
+
failedMessage: messages.failed,
|
|
1099
|
+
onBusy: changeBusy,
|
|
978
1100
|
onSettings: (settings) => setStatus((current) => current ? {
|
|
979
1101
|
...current,
|
|
980
1102
|
settings
|
|
@@ -985,6 +1107,7 @@ function TitleSettings(props) {
|
|
|
985
1107
|
}
|
|
986
1108
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
987
1109
|
className: "current-title-settings ct-root dsh-ui-panel",
|
|
1110
|
+
lang: seat.locale,
|
|
988
1111
|
"data-testid": "current-title-settings",
|
|
989
1112
|
children: [
|
|
990
1113
|
!status && !error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
@@ -1009,26 +1132,59 @@ function TitleSettings(props) {
|
|
|
1009
1132
|
status && !weOwn ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
1010
1133
|
role: "status",
|
|
1011
1134
|
className: "dsh-ui-hint",
|
|
1012
|
-
children: [text.inactive, status.support.limitation ? ` ${status.support.limitation}` : ""]
|
|
1135
|
+
children: [text.inactive, status.support.limitation ? ` ${titleMessage(status.support.limitation, seat.locale)}` : ""]
|
|
1013
1136
|
}) : null,
|
|
1014
|
-
error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1137
|
+
error ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1015
1138
|
role: "alert",
|
|
1016
1139
|
className: "dsh-ui-error dsh-ui-wrap",
|
|
1017
|
-
children: error
|
|
1018
|
-
})
|
|
1140
|
+
children: titleMessage(error, seat.locale)
|
|
1141
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1142
|
+
variant: "outline",
|
|
1143
|
+
size: "sm",
|
|
1144
|
+
disabled: busy,
|
|
1145
|
+
onClick: () => {
|
|
1146
|
+
readGate.reset();
|
|
1147
|
+
refreshStatus();
|
|
1148
|
+
},
|
|
1149
|
+
children: text.retry
|
|
1150
|
+
})] }) : null,
|
|
1019
1151
|
note ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1020
1152
|
role: "status",
|
|
1021
1153
|
className: "dsh-ui-notice",
|
|
1022
|
-
children: note
|
|
1154
|
+
children: titleMessage(note, seat.locale)
|
|
1023
1155
|
}) : null,
|
|
1024
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
1156
|
+
externalChange ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1157
|
+
role: "alert",
|
|
1158
|
+
className: "dsh-ui-error",
|
|
1159
|
+
children: text.externalChange
|
|
1160
|
+
}) : null,
|
|
1161
|
+
syncError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1162
|
+
role: "status",
|
|
1163
|
+
className: "dsh-ui-hint",
|
|
1164
|
+
children: text.syncUnavailable
|
|
1165
|
+
}) : null,
|
|
1166
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1025
1167
|
className: "dsh-ui-actions",
|
|
1026
|
-
children:
|
|
1168
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1169
|
+
variant: "ghost",
|
|
1170
|
+
size: "sm",
|
|
1171
|
+
disabled: busy || loadingSettings,
|
|
1172
|
+
onClick: () => {
|
|
1173
|
+
setPrompt(void 0);
|
|
1174
|
+
dirty.current = false;
|
|
1175
|
+
external.current = false;
|
|
1176
|
+
setExternalChange(false);
|
|
1177
|
+
readGate.reset();
|
|
1178
|
+
syncRef.current();
|
|
1179
|
+
},
|
|
1180
|
+
children: text.reload
|
|
1181
|
+
}), sessionId ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1027
1182
|
variant: "outline",
|
|
1028
1183
|
size: "md",
|
|
1029
1184
|
disabled: !canRegenerate,
|
|
1030
1185
|
onClick: () => {
|
|
1031
1186
|
if (!sessionId) return;
|
|
1187
|
+
readGate.reset();
|
|
1032
1188
|
const token = work.beginRequest();
|
|
1033
1189
|
setError("");
|
|
1034
1190
|
setNote("");
|
|
@@ -1036,15 +1192,15 @@ function TitleSettings(props) {
|
|
|
1036
1192
|
call,
|
|
1037
1193
|
sessionId,
|
|
1038
1194
|
isCurrent: () => work.isRequest(token),
|
|
1039
|
-
failedMessage:
|
|
1040
|
-
onBusy:
|
|
1195
|
+
failedMessage: messages.failed,
|
|
1196
|
+
onBusy: changeBusy,
|
|
1041
1197
|
onStatus: setStatus,
|
|
1042
1198
|
onError: setError,
|
|
1043
1199
|
onMarker: commitMarker
|
|
1044
1200
|
});
|
|
1045
1201
|
},
|
|
1046
1202
|
children: text.regenerate
|
|
1047
|
-
}) : null
|
|
1203
|
+
}) : null]
|
|
1048
1204
|
}),
|
|
1049
1205
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1050
1206
|
className: "dsh-ui-field",
|
|
@@ -1058,18 +1214,21 @@ function TitleSettings(props) {
|
|
|
1058
1214
|
value: status ? promptFieldValue(prompt, savedPrompt) : "",
|
|
1059
1215
|
maxLength: 4e3,
|
|
1060
1216
|
rows: 5,
|
|
1061
|
-
disabled: busy || !status,
|
|
1217
|
+
disabled: busy || loadingSettings || !status,
|
|
1062
1218
|
onChange: (event) => {
|
|
1219
|
+
dirty.current = true;
|
|
1063
1220
|
setPrompt(event.target.value);
|
|
1064
1221
|
setNote("");
|
|
1065
1222
|
},
|
|
1066
1223
|
onBlur: () => {
|
|
1067
|
-
if (!status) return;
|
|
1224
|
+
if (!status || busyRef.current || reading.current || external.current) return;
|
|
1068
1225
|
const next = promptToSave(prompt, savedPrompt);
|
|
1069
1226
|
if (next === void 0) {
|
|
1227
|
+
dirty.current = false;
|
|
1070
1228
|
setPrompt(void 0);
|
|
1071
1229
|
return;
|
|
1072
1230
|
}
|
|
1231
|
+
readGate.reset();
|
|
1073
1232
|
const token = work.beginRequest();
|
|
1074
1233
|
setError("");
|
|
1075
1234
|
runTitleSettingsSave({
|
|
@@ -1077,14 +1236,15 @@ function TitleSettings(props) {
|
|
|
1077
1236
|
prompt: next,
|
|
1078
1237
|
expectedRevision: status.settings.revision,
|
|
1079
1238
|
isCurrent: () => work.isRequest(token),
|
|
1080
|
-
savedMessage:
|
|
1081
|
-
failedMessage:
|
|
1082
|
-
onBusy:
|
|
1239
|
+
savedMessage: messages.saved,
|
|
1240
|
+
failedMessage: messages.failed,
|
|
1241
|
+
onBusy: changeBusy,
|
|
1083
1242
|
onSettings: (settings) => {
|
|
1084
1243
|
setStatus((current) => current ? {
|
|
1085
1244
|
...current,
|
|
1086
1245
|
settings
|
|
1087
1246
|
} : current);
|
|
1247
|
+
dirty.current = false;
|
|
1088
1248
|
setPrompt(void 0);
|
|
1089
1249
|
},
|
|
1090
1250
|
onNote: setNote,
|
|
@@ -1106,10 +1266,10 @@ function TitleSettings(props) {
|
|
|
1106
1266
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
1107
1267
|
className: "dsh-ui-select",
|
|
1108
1268
|
value: modelKey,
|
|
1109
|
-
disabled: busy || !status,
|
|
1269
|
+
disabled: busy || loadingSettings || !status,
|
|
1110
1270
|
onChange: (event) => {
|
|
1111
1271
|
const value = event.target.value;
|
|
1112
|
-
const choice =
|
|
1272
|
+
const choice = selectableChoices.find((item) => `${item.provider}\u001f${item.model}` === value);
|
|
1113
1273
|
saveSettings({ model: choice ? {
|
|
1114
1274
|
provider: choice.provider,
|
|
1115
1275
|
model: choice.model
|
|
@@ -1121,41 +1281,12 @@ function TitleSettings(props) {
|
|
|
1121
1281
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1122
1282
|
value: "",
|
|
1123
1283
|
children: text.modelDefault
|
|
1124
|
-
}),
|
|
1284
|
+
}), selectableChoices.map((choice) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1125
1285
|
value: `${choice.provider}\u001f${choice.model}`,
|
|
1126
1286
|
children: choice.label
|
|
1127
1287
|
}, `${choice.provider}\u001f${choice.model}`))]
|
|
1128
1288
|
})]
|
|
1129
1289
|
}),
|
|
1130
|
-
selectedChoice && efforts.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1131
|
-
className: "dsh-ui-field",
|
|
1132
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1133
|
-
className: "dsh-ui-label",
|
|
1134
|
-
children: text.effort
|
|
1135
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
1136
|
-
className: "dsh-ui-select",
|
|
1137
|
-
value: savedModel.reasoningEffort ?? "",
|
|
1138
|
-
disabled: busy || !status,
|
|
1139
|
-
onChange: (event) => {
|
|
1140
|
-
const effort = event.target.value;
|
|
1141
|
-
const model = {
|
|
1142
|
-
provider: savedModel.provider,
|
|
1143
|
-
model: savedModel.model
|
|
1144
|
-
};
|
|
1145
|
-
saveSettings({ model: effort ? {
|
|
1146
|
-
...model,
|
|
1147
|
-
reasoningEffort: effort
|
|
1148
|
-
} : model });
|
|
1149
|
-
},
|
|
1150
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1151
|
-
value: "",
|
|
1152
|
-
children: text.effortDefault
|
|
1153
|
-
}), efforts.map((effort) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1154
|
-
value: effort.id,
|
|
1155
|
-
children: effort.name
|
|
1156
|
-
}, effort.id))]
|
|
1157
|
-
})]
|
|
1158
|
-
}) : null,
|
|
1159
1290
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1160
1291
|
className: "dsh-ui-field",
|
|
1161
1292
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -1164,7 +1295,7 @@ function TitleSettings(props) {
|
|
|
1164
1295
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
1165
1296
|
className: "dsh-ui-select",
|
|
1166
1297
|
value: status?.settings.cadence ?? "all-prompts",
|
|
1167
|
-
disabled: busy || !status,
|
|
1298
|
+
disabled: busy || loadingSettings || !status,
|
|
1168
1299
|
onChange: (event) => {
|
|
1169
1300
|
saveSettings({ cadence: event.target.value === "first-prompt" ? "first-prompt" : "all-prompts" });
|
|
1170
1301
|
},
|
|
@@ -1202,6 +1333,8 @@ const styles = `${officialUiCss("ct-root")}
|
|
|
1202
1333
|
`;
|
|
1203
1334
|
function apply(ctx) {
|
|
1204
1335
|
const client = ctx;
|
|
1336
|
+
const settingsSubscriber = createModelSettingsSubscriber(ctx, "@klarkxy/dsh-current-title");
|
|
1337
|
+
const subscribeSettings = (change, error) => settingsSubscriber.subscribe(change, error);
|
|
1205
1338
|
const marker = createTitleClientMarker();
|
|
1206
1339
|
ctx.provide(TITLE_CLIENT_SERVICE, marker);
|
|
1207
1340
|
ctx.effect(() => {
|
|
@@ -1217,7 +1350,8 @@ function apply(ctx) {
|
|
|
1217
1350
|
key: "@klarkxy/dsh-current-title"
|
|
1218
1351
|
}, () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TitleSettings, {
|
|
1219
1352
|
client,
|
|
1220
|
-
marker
|
|
1353
|
+
marker,
|
|
1354
|
+
subscribeSettings
|
|
1221
1355
|
}))), "current-title.settings");
|
|
1222
1356
|
ctx.effect(() => {
|
|
1223
1357
|
let live = true;
|