@klarkxy/dsh-web-search-manager 0.1.11 → 0.2.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/README.md +28 -10
- package/cordis.patch.yml +1 -8
- package/docs/README.zh-CN.md +28 -10
- package/lib/client.inner.cjs +507 -83
- package/lib/client.inner.cjs.map +1 -1
- package/lib/client.js +508 -245
- package/lib/contracts.d.ts +19 -2
- package/lib/contracts.js +13 -1
- package/lib/contracts.js.map +1 -1
- package/lib/index.d.ts +18 -3
- package/lib/index.js +488 -331
- package/lib/index.js.map +1 -1
- package/package.json +23 -22
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-web-search-manager",
|
|
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" });
|
|
@@ -542,6 +381,49 @@ ${scope} {
|
|
|
542
381
|
`.replace(/^& (\.dsh-ui-[a-z0-9-]+)(?![+>~])/gm, (_match, className) => `&${className}, & ${className}`);
|
|
543
382
|
}
|
|
544
383
|
//#endregion
|
|
384
|
+
//#region ../dsh-plugin-kit/src/client-utils.ts
|
|
385
|
+
/** Fixed pause for failing background reads; manual work is never replayed. */
|
|
386
|
+
function createPluginReadGate(now = Date.now) {
|
|
387
|
+
let pausedUntil = 0;
|
|
388
|
+
let epoch = 0;
|
|
389
|
+
return {
|
|
390
|
+
canRead: () => now() >= pausedUntil,
|
|
391
|
+
reset: () => {
|
|
392
|
+
epoch++;
|
|
393
|
+
pausedUntil = 0;
|
|
394
|
+
},
|
|
395
|
+
async run(read) {
|
|
396
|
+
const current = epoch;
|
|
397
|
+
try {
|
|
398
|
+
const value = await read();
|
|
399
|
+
if (current === epoch) pausedUntil = 0;
|
|
400
|
+
return value;
|
|
401
|
+
} catch (error) {
|
|
402
|
+
const message = error instanceof Error ? error.message : "";
|
|
403
|
+
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;
|
|
404
|
+
throw error;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
//#endregion
|
|
410
|
+
//#region src/contracts.ts
|
|
411
|
+
const WEB_SEARCH_RPC_CHANNEL = "/web-search-manager";
|
|
412
|
+
function pickActiveSearch(order, configured) {
|
|
413
|
+
return order.find(configured) ?? "";
|
|
414
|
+
}
|
|
415
|
+
function safeConfigurationUrl(value) {
|
|
416
|
+
if (typeof value !== "string" || !value || /[\u0000-\u0020\\]/.test(value)) return;
|
|
417
|
+
try {
|
|
418
|
+
const url = new URL(value, "https://dsh.invalid");
|
|
419
|
+
if (url.username || url.password) return;
|
|
420
|
+
if (value.startsWith("/") && !value.startsWith("//") || value.startsWith("#")) return url.origin === "https://dsh.invalid" ? value : void 0;
|
|
421
|
+
return /^https:\/\//i.test(value) && url.protocol === "https:" ? value : void 0;
|
|
422
|
+
} catch {
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
//#endregion
|
|
545
427
|
//#region src/provider-info.ts
|
|
546
428
|
const PROVIDER_PRICING_CHECKED = "2026-09-21";
|
|
547
429
|
const providerInfo = {
|
|
@@ -586,22 +468,37 @@ const providerInfo = {
|
|
|
586
468
|
pricingUrl: "https://docs.tavily.com/documentation/api-credits"
|
|
587
469
|
}
|
|
588
470
|
};
|
|
589
|
-
function
|
|
590
|
-
|
|
591
|
-
return
|
|
592
|
-
description: known?.description ?? provider.description,
|
|
593
|
-
pricing: provider.pricing ?? known?.pricing ?? (provider.billing === "none" ? "该后端不收取搜索费用。" : provider.billing === "model-and-tools" ? "按模型用量与工具调用计费;额度与单价见供应商说明。" : "按 API 调用计费;额度与单价见供应商说明。"),
|
|
594
|
-
pricingUrl: provider.pricingUrl ?? known?.pricingUrl ?? provider.signupUrl
|
|
595
|
-
};
|
|
471
|
+
function bundledCatalogId(provider) {
|
|
472
|
+
if (!(typeof provider.configurationOwned === "boolean" ? provider.configurationOwned : !provider.configurationOwner || provider.configurationOwner === "@klarkxy/dsh-web-search-manager")) return;
|
|
473
|
+
return provider.id === "deepseek-managed" ? "deepseek-official" : provider.id;
|
|
596
474
|
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
const WEB_SEARCH_RPC_CHANNEL = "/web-search-manager";
|
|
600
|
-
function resolveSearchOrder(knownIds, settings, _keyed) {
|
|
601
|
-
return [...new Set(settings.searchOrder.filter((id) => knownIds.includes(id)))];
|
|
475
|
+
function missingPricing(locale) {
|
|
476
|
+
return locale === "zh" ? "未提供费用信息" : "No pricing information provided";
|
|
602
477
|
}
|
|
603
|
-
function
|
|
604
|
-
|
|
478
|
+
function usesBundledPricing(provider) {
|
|
479
|
+
if (provider.pricing?.trim()) return false;
|
|
480
|
+
const catalogId = bundledCatalogId(provider);
|
|
481
|
+
return Boolean(catalogId && Object.hasOwn(providerInfo, catalogId));
|
|
482
|
+
}
|
|
483
|
+
function searchProviderInfo(provider, locale = "zh") {
|
|
484
|
+
const catalogId = bundledCatalogId(provider);
|
|
485
|
+
const known = catalogId && Object.hasOwn(providerInfo, catalogId) ? providerInfo[catalogId] : void 0;
|
|
486
|
+
const description = provider.description.trim() ? provider.description : known?.description ?? provider.description;
|
|
487
|
+
if (provider.pricing?.trim()) return {
|
|
488
|
+
description,
|
|
489
|
+
pricing: provider.pricing,
|
|
490
|
+
pricingUrl: provider.pricingUrl ?? known?.pricingUrl ?? provider.signupUrl
|
|
491
|
+
};
|
|
492
|
+
if (known) return {
|
|
493
|
+
description,
|
|
494
|
+
pricing: known.pricing,
|
|
495
|
+
pricingUrl: provider.pricingUrl ?? known.pricingUrl ?? provider.signupUrl
|
|
496
|
+
};
|
|
497
|
+
return {
|
|
498
|
+
description,
|
|
499
|
+
pricing: missingPricing(locale),
|
|
500
|
+
pricingUrl: provider.pricingUrl ?? provider.signupUrl
|
|
501
|
+
};
|
|
605
502
|
}
|
|
606
503
|
//#endregion
|
|
607
504
|
//#region src/client.tsx
|
|
@@ -621,32 +518,129 @@ function editable(settings) {
|
|
|
621
518
|
const { revision: _revision, ...rest } = settings;
|
|
622
519
|
return rest;
|
|
623
520
|
}
|
|
521
|
+
const STATUS_REFRESH_MS = 3e3;
|
|
522
|
+
function isPageHidden(hidden) {
|
|
523
|
+
if (hidden) return true;
|
|
524
|
+
return typeof document !== "undefined" && document.visibilityState === "hidden";
|
|
525
|
+
}
|
|
526
|
+
function canStartStatusRead(input) {
|
|
527
|
+
if (!input.mounted) return false;
|
|
528
|
+
if (input.source === "poll") return !input.hidden && !input.busy && !input.reordering && !input.reading;
|
|
529
|
+
return true;
|
|
530
|
+
}
|
|
531
|
+
function isExternalConfigurationUrl(url) {
|
|
532
|
+
return /^https:\/\//i.test(url);
|
|
533
|
+
}
|
|
534
|
+
function shouldApplyPolledStatus(input) {
|
|
535
|
+
if (input.hidden || input.busy || input.reordering) return false;
|
|
536
|
+
if (input.requestId !== input.latestRequestId) return false;
|
|
537
|
+
if (input.currentRevision !== void 0 && input.incomingRevision < input.currentRevision) return false;
|
|
538
|
+
return true;
|
|
539
|
+
}
|
|
540
|
+
function bindStatusRefresh(options) {
|
|
541
|
+
const intervalMs = options.intervalMs ?? 3e3;
|
|
542
|
+
let inFlight = false;
|
|
543
|
+
let stopped = false;
|
|
544
|
+
const run = async () => {
|
|
545
|
+
if (stopped || inFlight || options.isHidden()) return;
|
|
546
|
+
inFlight = true;
|
|
547
|
+
try {
|
|
548
|
+
await options.tick();
|
|
549
|
+
} finally {
|
|
550
|
+
inFlight = false;
|
|
551
|
+
}
|
|
552
|
+
};
|
|
553
|
+
const timer = (options.setIntervalFn ?? setInterval)(() => {
|
|
554
|
+
run();
|
|
555
|
+
}, intervalMs);
|
|
556
|
+
const onVisible = () => {
|
|
557
|
+
if (!options.isHidden()) run();
|
|
558
|
+
};
|
|
559
|
+
const add = options.addListener ?? ((type, listener) => {
|
|
560
|
+
if (typeof document !== "undefined") document.addEventListener(type, listener);
|
|
561
|
+
});
|
|
562
|
+
const remove = options.removeListener ?? ((type, listener) => {
|
|
563
|
+
if (typeof document !== "undefined") document.removeEventListener(type, listener);
|
|
564
|
+
});
|
|
565
|
+
add("visibilitychange", onVisible);
|
|
566
|
+
return () => {
|
|
567
|
+
stopped = true;
|
|
568
|
+
(options.clearIntervalFn ?? clearInterval)(timer);
|
|
569
|
+
remove("visibilitychange", onVisible);
|
|
570
|
+
};
|
|
571
|
+
}
|
|
624
572
|
function isProviderOn(status, provider) {
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
return enabled && selected === provider.id;
|
|
573
|
+
if (provider.kind === "fetch") return status.fetchActive && status.settings.fetchProvider === provider.id && provider.configured;
|
|
574
|
+
return status.searchActive && selectedSearchBackend(status)?.id === provider.id;
|
|
628
575
|
}
|
|
629
|
-
function
|
|
630
|
-
|
|
631
|
-
|
|
576
|
+
function dormantSearchPlaceholder(id) {
|
|
577
|
+
return {
|
|
578
|
+
id,
|
|
579
|
+
label: id,
|
|
580
|
+
description: "",
|
|
581
|
+
billing: "unknown",
|
|
582
|
+
kind: "search",
|
|
583
|
+
configured: false,
|
|
584
|
+
calls: 0,
|
|
585
|
+
failures: 0,
|
|
586
|
+
configurationOwned: false
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
function isRegisteredSearch(status, id) {
|
|
590
|
+
return status.providers.some((provider) => provider.kind === "search" && provider.id === id);
|
|
632
591
|
}
|
|
633
592
|
function catalogSearchBackends(status) {
|
|
634
|
-
const
|
|
635
|
-
const
|
|
636
|
-
|
|
593
|
+
const byId = new Map(status.providers.filter((provider) => provider.kind === "search").map((provider) => [provider.id, provider]));
|
|
594
|
+
const rows = [];
|
|
595
|
+
const seen = /* @__PURE__ */ new Set();
|
|
596
|
+
for (const id of status.settings.searchOrder) {
|
|
597
|
+
if (!id || seen.has(id)) continue;
|
|
598
|
+
seen.add(id);
|
|
599
|
+
rows.push(byId.get(id) ?? dormantSearchPlaceholder(id));
|
|
600
|
+
}
|
|
601
|
+
for (const provider of status.providers) {
|
|
602
|
+
if (provider.kind !== "search" || seen.has(provider.id)) continue;
|
|
603
|
+
rows.push(provider);
|
|
604
|
+
}
|
|
605
|
+
return rows;
|
|
606
|
+
}
|
|
607
|
+
function searchBackends(status) {
|
|
608
|
+
const participating = new Set(status.settings.searchOrder);
|
|
609
|
+
return catalogSearchBackends(status).filter((provider) => participating.has(provider.id));
|
|
637
610
|
}
|
|
638
611
|
function selectedSearchBackend(status) {
|
|
639
|
-
const backends = searchBackends(status);
|
|
612
|
+
const backends = searchBackends(status).filter((provider) => isRegisteredSearch(status, provider.id));
|
|
640
613
|
const id = pickActiveSearch(backends.map((provider) => provider.id), (id) => Boolean(backends.find((provider) => provider.id === id)?.configured));
|
|
641
614
|
return backends.find((provider) => provider.id === id);
|
|
642
615
|
}
|
|
643
|
-
function
|
|
616
|
+
function droppedDormantIds(settings, order, providers) {
|
|
617
|
+
const known = new Set(providers.filter((provider) => provider.kind === "search").map((provider) => provider.id));
|
|
618
|
+
return [...new Set(settings.searchOrder.filter((id) => id && !known.has(id) && !order.includes(id)))];
|
|
619
|
+
}
|
|
620
|
+
function configurationAccess(provider) {
|
|
621
|
+
if (ownsProviderConfiguration(provider)) return { kind: "owned" };
|
|
622
|
+
const url = safeConfigurationUrl(provider.configurationUrl);
|
|
623
|
+
if (url) return {
|
|
624
|
+
kind: "link",
|
|
625
|
+
url
|
|
626
|
+
};
|
|
627
|
+
if (provider.configurationOwner?.trim()) return {
|
|
628
|
+
kind: "owner",
|
|
629
|
+
name: provider.configurationOwner
|
|
630
|
+
};
|
|
631
|
+
return { kind: "unknown" };
|
|
632
|
+
}
|
|
633
|
+
function AppLink({ url, label, children }) {
|
|
634
|
+
const external = isExternalConfigurationUrl(url);
|
|
644
635
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
645
636
|
href: url,
|
|
646
|
-
|
|
647
|
-
|
|
637
|
+
...external ? {
|
|
638
|
+
target: "_blank",
|
|
639
|
+
rel: "noreferrer noopener"
|
|
640
|
+
} : {},
|
|
648
641
|
"aria-label": label,
|
|
649
642
|
onClick: (event) => {
|
|
643
|
+
if (!external) return;
|
|
650
644
|
const bridge = globalThis.dshWindow;
|
|
651
645
|
if (bridge?.openExternal) {
|
|
652
646
|
event.preventDefault();
|
|
@@ -662,9 +656,13 @@ function canEnableSearch(order, providers, keys, writable) {
|
|
|
662
656
|
if (!provider) return false;
|
|
663
657
|
if (provider.configured) return true;
|
|
664
658
|
const typed = (keys[provider.id] ?? "").trim();
|
|
665
|
-
return Boolean(provider.credentialRef && typed && writable[provider.credentialRef] !== false);
|
|
659
|
+
return Boolean(ownsProviderConfiguration(provider) && provider.credentialRef && typed && writable[provider.credentialRef] !== false);
|
|
666
660
|
});
|
|
667
661
|
}
|
|
662
|
+
function ownsProviderConfiguration(provider) {
|
|
663
|
+
if (typeof provider.configurationOwned === "boolean") return provider.configurationOwned;
|
|
664
|
+
return !provider.configurationOwner || provider.configurationOwner === "@klarkxy/dsh-web-search-manager";
|
|
665
|
+
}
|
|
668
666
|
function nextSearchEnabled(currentlyEnabled, nextOrder, providers, keys, writable) {
|
|
669
667
|
return currentlyEnabled && canEnableSearch(nextOrder, providers, keys, writable);
|
|
670
668
|
}
|
|
@@ -716,11 +714,14 @@ const copy = {
|
|
|
716
714
|
dragPrefix: "拖动排序 ",
|
|
717
715
|
dragHint: "拖动排序,或用上下方向键",
|
|
718
716
|
inUse: "当前使用",
|
|
717
|
+
available: "可用",
|
|
718
|
+
unavailable: "暂不可用",
|
|
719
|
+
dormant: "未加载,保留选择",
|
|
719
720
|
signup: "去注册",
|
|
720
721
|
pricing: "费用说明",
|
|
721
722
|
website: "官网",
|
|
723
|
+
openConfig: "打开配置",
|
|
722
724
|
needsKey: "待配置 Key",
|
|
723
|
-
unavailable: "暂不可用",
|
|
724
725
|
sharedKey: "与其他设置共用凭据",
|
|
725
726
|
keyConfigured: "已配置,留空不替换",
|
|
726
727
|
keyPlaceholder: "输入 API Key",
|
|
@@ -734,7 +735,11 @@ const copy = {
|
|
|
734
735
|
maxResults: "每条查询结果上限",
|
|
735
736
|
maxQueries: "每次调用查询上限",
|
|
736
737
|
timeoutMs: "请求超时(毫秒)",
|
|
737
|
-
maxFetchChars: "正文字符上限"
|
|
738
|
+
maxFetchChars: "正文字符上限",
|
|
739
|
+
fetchTitle: "网页读取",
|
|
740
|
+
fetchProvider: "读取服务",
|
|
741
|
+
ownerSettings: "配置入口:",
|
|
742
|
+
unknownOwner: "请在服务所属插件中配置"
|
|
738
743
|
},
|
|
739
744
|
en: {
|
|
740
745
|
tabsLabel: "Web search settings",
|
|
@@ -756,11 +761,14 @@ const copy = {
|
|
|
756
761
|
dragPrefix: "Reorder ",
|
|
757
762
|
dragHint: "Drag to reorder, or use arrow keys",
|
|
758
763
|
inUse: "In use",
|
|
764
|
+
available: "Available",
|
|
765
|
+
unavailable: "Unavailable",
|
|
766
|
+
dormant: "Not loaded; selection kept",
|
|
759
767
|
signup: "Sign up",
|
|
760
768
|
pricing: "Pricing",
|
|
761
769
|
website: "Website",
|
|
770
|
+
openConfig: "Open configuration",
|
|
762
771
|
needsKey: "Key required",
|
|
763
|
-
unavailable: "Unavailable",
|
|
764
772
|
sharedKey: "Shares a credential with other settings",
|
|
765
773
|
keyConfigured: "Configured; empty keeps it",
|
|
766
774
|
keyPlaceholder: "Enter API key",
|
|
@@ -774,14 +782,18 @@ const copy = {
|
|
|
774
782
|
maxResults: "Max results/query",
|
|
775
783
|
maxQueries: "Max queries/call",
|
|
776
784
|
timeoutMs: "Request timeout (ms)",
|
|
777
|
-
maxFetchChars: "Max text chars"
|
|
785
|
+
maxFetchChars: "Max text chars",
|
|
786
|
+
fetchTitle: "Read web pages",
|
|
787
|
+
fetchProvider: "Fetch service",
|
|
788
|
+
ownerSettings: "Configure in: ",
|
|
789
|
+
unknownOwner: "Configure this service in its plugin"
|
|
778
790
|
}
|
|
779
791
|
};
|
|
780
792
|
function movedText(locale, label, position) {
|
|
781
793
|
return locale === "zh" ? `${label} 已移至第 ${position} 位` : `${label} moved to position ${position}`;
|
|
782
794
|
}
|
|
783
795
|
function pricingCheckedText(locale, date) {
|
|
784
|
-
return locale === "zh" ?
|
|
796
|
+
return locale === "zh" ? `内置适配器价格核对于 ${date},额度与费用以供应商账户为准。` : `Bundled adapter prices checked on ${date}; your provider account is authoritative.`;
|
|
785
797
|
}
|
|
786
798
|
function testOkText(locale, sources) {
|
|
787
799
|
return locale === "zh" ? `连接正常,返回 ${sources} 条来源。` : `Connection OK: ${sources} sources returned.`;
|
|
@@ -822,13 +834,25 @@ function parseLimit(raw, min, max) {
|
|
|
822
834
|
* Settings written by an immediate action (a switch or a reorder). It keeps the
|
|
823
835
|
* saved limits and never includes typed-but-unsaved keys or limit drafts.
|
|
824
836
|
*/
|
|
825
|
-
function immediateUpdate(settings, on, order) {
|
|
837
|
+
function immediateUpdate(settings, on, order, providers, remove = []) {
|
|
838
|
+
let searchOrder = order;
|
|
839
|
+
if (providers) {
|
|
840
|
+
const known = new Set(providers.filter((provider) => provider.kind === "search").map((provider) => provider.id));
|
|
841
|
+
const removed = new Set(remove);
|
|
842
|
+
const following = order.filter((id) => !removed.has(id));
|
|
843
|
+
if (following.some((id) => !known.has(id))) searchOrder = following;
|
|
844
|
+
else {
|
|
845
|
+
searchOrder = settings.searchOrder.flatMap((id) => {
|
|
846
|
+
if (removed.has(id)) return [];
|
|
847
|
+
return known.has(id) ? following.splice(0, 1) : [id];
|
|
848
|
+
});
|
|
849
|
+
searchOrder.push(...following);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
826
852
|
return {
|
|
827
853
|
...editable(settings),
|
|
828
|
-
searchOrder
|
|
829
|
-
searchEnabled: on
|
|
830
|
-
fetchProvider: "http",
|
|
831
|
-
fetchEnabled: on
|
|
854
|
+
searchOrder,
|
|
855
|
+
searchEnabled: on
|
|
832
856
|
};
|
|
833
857
|
}
|
|
834
858
|
function limitTexts(settings) {
|
|
@@ -846,7 +870,7 @@ function useHostLocale(client) {
|
|
|
846
870
|
}
|
|
847
871
|
/** Keyboard reorders are batched: persist once the arrows stop for this long. */
|
|
848
872
|
const REORDER_DEBOUNCE_MS = 500;
|
|
849
|
-
function NetworkSearchSettings({ client }) {
|
|
873
|
+
function NetworkSearchSettings({ client, hidden }) {
|
|
850
874
|
const locale = useHostLocale(client);
|
|
851
875
|
const text = copy[locale];
|
|
852
876
|
const tabsId = (0, react.useId)();
|
|
@@ -870,6 +894,17 @@ function NetworkSearchSettings({ client }) {
|
|
|
870
894
|
const timerRef = (0, react.useRef)(void 0);
|
|
871
895
|
const handleRefs = (0, react.useRef)(/* @__PURE__ */ new Map());
|
|
872
896
|
const refocusRef = (0, react.useRef)(null);
|
|
897
|
+
const busyRef = (0, react.useRef)(false);
|
|
898
|
+
const hiddenRef = (0, react.useRef)(hidden);
|
|
899
|
+
hiddenRef.current = hidden;
|
|
900
|
+
const requestSeq = (0, react.useRef)(0);
|
|
901
|
+
const mountedRef = (0, react.useRef)(true);
|
|
902
|
+
const readingRef = (0, react.useRef)(false);
|
|
903
|
+
const readingRequest = (0, react.useRef)(0);
|
|
904
|
+
const statusRead = (0, react.useRef)(Promise.resolve());
|
|
905
|
+
const readGateRef = (0, react.useRef)(void 0);
|
|
906
|
+
if (!readGateRef.current) readGateRef.current = createPluginReadGate();
|
|
907
|
+
const readGate = readGateRef.current;
|
|
873
908
|
async function call(endpoint, payload = {}) {
|
|
874
909
|
return unwrap(await client.connection.rpc.call(WEB_SEARCH_RPC_CHANNEL, endpoint, payload));
|
|
875
910
|
}
|
|
@@ -885,30 +920,109 @@ function NetworkSearchSettings({ client }) {
|
|
|
885
920
|
});
|
|
886
921
|
if (resetDraft) setInvalid({});
|
|
887
922
|
}
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
923
|
+
function pollBlocked() {
|
|
924
|
+
return !canStartStatusRead({
|
|
925
|
+
source: "poll",
|
|
926
|
+
mounted: mountedRef.current,
|
|
927
|
+
hidden: isPageHidden(hiddenRef.current),
|
|
928
|
+
busy: busyRef.current,
|
|
929
|
+
reordering: pendingRef.current !== null,
|
|
930
|
+
reading: readingRef.current
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
async function describeOwned(next, mine) {
|
|
934
|
+
const refs = next.providers.flatMap((provider) => ownsProviderConfiguration(provider) && provider.credentialRef ? [provider.credentialRef] : []);
|
|
935
|
+
if (!refs.length) return;
|
|
936
|
+
const credentials = unwrap(await readGate.run(() => client.remote.credentials.describe(refs)));
|
|
937
|
+
if (!mountedRef.current || mine !== requestSeq.current) return;
|
|
938
|
+
setWritable(Object.fromEntries(Object.entries(credentials).map(([ref, value]) => [ref, value.writable])));
|
|
939
|
+
}
|
|
940
|
+
async function load(resetDraft = false, source = "user") {
|
|
941
|
+
if (source === "poll" && !readGate.canRead()) return;
|
|
942
|
+
if (source === "user") {
|
|
943
|
+
readGate.reset();
|
|
944
|
+
requestSeq.current += 1;
|
|
945
|
+
}
|
|
946
|
+
while (readingRef.current) {
|
|
947
|
+
if (source === "poll" || !mountedRef.current) return;
|
|
948
|
+
await statusRead.current;
|
|
949
|
+
}
|
|
950
|
+
if (!mountedRef.current) return;
|
|
951
|
+
if (source === "poll" && pollBlocked()) return;
|
|
952
|
+
const mine = ++requestSeq.current;
|
|
953
|
+
readingRef.current = true;
|
|
954
|
+
readingRequest.current = mine;
|
|
955
|
+
let release = () => {};
|
|
956
|
+
statusRead.current = new Promise((resolve) => {
|
|
957
|
+
release = resolve;
|
|
958
|
+
});
|
|
959
|
+
try {
|
|
960
|
+
const next = unwrap(await readGate.run(() => client.connection.rpc.call(WEB_SEARCH_RPC_CHANNEL, "status", {})));
|
|
961
|
+
if (!mountedRef.current || mine !== requestSeq.current) return next;
|
|
962
|
+
if (source === "poll" && !shouldApplyPolledStatus({
|
|
963
|
+
requestId: mine,
|
|
964
|
+
latestRequestId: requestSeq.current,
|
|
965
|
+
busy: busyRef.current,
|
|
966
|
+
reordering: pendingRef.current !== null,
|
|
967
|
+
hidden: isPageHidden(hiddenRef.current),
|
|
968
|
+
currentRevision: statusRef.current?.settings.revision,
|
|
969
|
+
incomingRevision: next.settings.revision
|
|
970
|
+
})) return next;
|
|
971
|
+
adopt(next, resetDraft);
|
|
972
|
+
setLoadError("");
|
|
973
|
+
await describeOwned(next, mine);
|
|
974
|
+
return next;
|
|
975
|
+
} catch (cause) {
|
|
976
|
+
if (!mountedRef.current || mine !== requestSeq.current) return;
|
|
977
|
+
setLoadError(cause instanceof Error && cause.message ? cause.message : text.failed);
|
|
978
|
+
if (source === "poll") return;
|
|
979
|
+
throw cause;
|
|
980
|
+
} finally {
|
|
981
|
+
if (readingRequest.current === mine) readingRef.current = false;
|
|
982
|
+
release();
|
|
896
983
|
}
|
|
897
|
-
return next;
|
|
898
984
|
}
|
|
899
985
|
(0, react.useEffect)(() => {
|
|
986
|
+
mountedRef.current = true;
|
|
987
|
+
readGate.reset();
|
|
900
988
|
load().catch((cause) => setLoadError(cause instanceof Error && cause.message ? cause.message : text.failed));
|
|
989
|
+
const bind = () => bindStatusRefresh({
|
|
990
|
+
isHidden: () => isPageHidden(hiddenRef.current),
|
|
991
|
+
tick: async () => {
|
|
992
|
+
await load(false, "poll").catch(() => {});
|
|
993
|
+
}
|
|
994
|
+
});
|
|
995
|
+
let stop = bind();
|
|
996
|
+
const stopConnection = client.connection.generation?.subscribe(() => {
|
|
997
|
+
readGate.reset();
|
|
998
|
+
requestSeq.current += 1;
|
|
999
|
+
readingRef.current = false;
|
|
1000
|
+
statusRead.current = Promise.resolve();
|
|
1001
|
+
stop();
|
|
1002
|
+
stop = bind();
|
|
1003
|
+
load(false, "poll");
|
|
1004
|
+
});
|
|
1005
|
+
return () => {
|
|
1006
|
+
mountedRef.current = false;
|
|
1007
|
+
readGate.reset();
|
|
1008
|
+
requestSeq.current += 1;
|
|
1009
|
+
stop();
|
|
1010
|
+
stopConnection?.();
|
|
1011
|
+
if (timerRef.current === void 0) return;
|
|
1012
|
+
clearTimeout(timerRef.current);
|
|
1013
|
+
const current = statusRef.current;
|
|
1014
|
+
const next = pendingRef.current;
|
|
1015
|
+
if (current && next) call("update", {
|
|
1016
|
+
settings: immediateUpdate(current.settings, current.settings.searchEnabled, next, current.providers),
|
|
1017
|
+
expectedRevision: current.settings.revision
|
|
1018
|
+
}).catch(() => {});
|
|
1019
|
+
};
|
|
901
1020
|
}, [client]);
|
|
902
|
-
(0, react.useEffect)(() =>
|
|
903
|
-
if (
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
if (current && next) call("update", {
|
|
908
|
-
settings: immediateUpdate(current.settings, current.settings.searchEnabled, next),
|
|
909
|
-
expectedRevision: current.settings.revision
|
|
910
|
-
}).catch(() => {});
|
|
911
|
-
}, []);
|
|
1021
|
+
(0, react.useEffect)(() => {
|
|
1022
|
+
if (hidden) return;
|
|
1023
|
+
if (!statusRef.current) return;
|
|
1024
|
+
load(false, "poll").catch(() => {});
|
|
1025
|
+
}, [hidden]);
|
|
912
1026
|
(0, react.useLayoutEffect)(() => {
|
|
913
1027
|
const id = refocusRef.current;
|
|
914
1028
|
if (!id) return;
|
|
@@ -917,6 +1031,9 @@ function NetworkSearchSettings({ client }) {
|
|
|
917
1031
|
if (handle && document.activeElement !== handle) handle.focus();
|
|
918
1032
|
});
|
|
919
1033
|
async function action(run) {
|
|
1034
|
+
readGate.reset();
|
|
1035
|
+
requestSeq.current += 1;
|
|
1036
|
+
busyRef.current = true;
|
|
920
1037
|
setBusy(true);
|
|
921
1038
|
setNote("");
|
|
922
1039
|
setError("");
|
|
@@ -926,11 +1043,12 @@ function NetworkSearchSettings({ client }) {
|
|
|
926
1043
|
setError(cause instanceof Error && cause.message ? cause.message : text.failed);
|
|
927
1044
|
await load().catch(() => {});
|
|
928
1045
|
} finally {
|
|
1046
|
+
busyRef.current = false;
|
|
929
1047
|
setBusy(false);
|
|
930
1048
|
}
|
|
931
1049
|
}
|
|
932
1050
|
/** Switches and reorders: settings only, applied at once. */
|
|
933
|
-
function runImmediate(on, order) {
|
|
1051
|
+
function runImmediate(on, order, remove = []) {
|
|
934
1052
|
clearTimeout(timerRef.current);
|
|
935
1053
|
timerRef.current = void 0;
|
|
936
1054
|
pendingRef.current = null;
|
|
@@ -938,7 +1056,7 @@ function NetworkSearchSettings({ client }) {
|
|
|
938
1056
|
const current = statusRef.current;
|
|
939
1057
|
if (!current) return;
|
|
940
1058
|
const next = await call("update", {
|
|
941
|
-
settings: immediateUpdate(current.settings, on, order),
|
|
1059
|
+
settings: immediateUpdate(current.settings, on, order, current.providers, remove),
|
|
942
1060
|
expectedRevision: current.settings.revision
|
|
943
1061
|
});
|
|
944
1062
|
setStatus(next);
|
|
@@ -954,7 +1072,7 @@ function NetworkSearchSettings({ client }) {
|
|
|
954
1072
|
if (provider.kind !== "search" || only && provider.id !== only.id) continue;
|
|
955
1073
|
const snapshot = keys[provider.id] ?? "";
|
|
956
1074
|
const typed = snapshot.trim();
|
|
957
|
-
if (!provider.credentialRef || !typed) continue;
|
|
1075
|
+
if (!ownsProviderConfiguration(provider) || !provider.credentialRef || !typed) continue;
|
|
958
1076
|
if (writable[provider.credentialRef] === false) throw new Error(text.readOnly);
|
|
959
1077
|
unwrap(await client.remote.credentials.set(provider.credentialRef, typed));
|
|
960
1078
|
setKeys((current) => current[provider.id] === snapshot ? {
|
|
@@ -964,7 +1082,7 @@ function NetworkSearchSettings({ client }) {
|
|
|
964
1082
|
}
|
|
965
1083
|
}
|
|
966
1084
|
async function removeKey(provider) {
|
|
967
|
-
if (!provider.credentialRef || provider.credentialShared || !status) return;
|
|
1085
|
+
if (!ownsProviderConfiguration(provider) || !provider.credentialRef || provider.credentialShared || !status) return;
|
|
968
1086
|
unwrap(await client.remote.credentials.unset(provider.credentialRef));
|
|
969
1087
|
setKeys((current) => ({
|
|
970
1088
|
...current,
|
|
@@ -1053,9 +1171,7 @@ function NetworkSearchSettings({ client }) {
|
|
|
1053
1171
|
adopt(await call("update", {
|
|
1054
1172
|
settings: {
|
|
1055
1173
|
...editable(current.settings),
|
|
1056
|
-
...parsed
|
|
1057
|
-
fetchProvider: "http",
|
|
1058
|
-
fetchEnabled: current.settings.searchEnabled
|
|
1174
|
+
...parsed
|
|
1059
1175
|
},
|
|
1060
1176
|
expectedRevision: current.settings.revision
|
|
1061
1177
|
}), true);
|
|
@@ -1094,6 +1210,22 @@ function NetworkSearchSettings({ client }) {
|
|
|
1094
1210
|
className: "dsh-ui-error",
|
|
1095
1211
|
children: error
|
|
1096
1212
|
}),
|
|
1213
|
+
loadError ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1214
|
+
className: "dsh-ui-stack",
|
|
1215
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
1216
|
+
role: "alert",
|
|
1217
|
+
className: "dsh-ui-error",
|
|
1218
|
+
children: [text.loadFailed, loadError]
|
|
1219
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1220
|
+
variant: "outline",
|
|
1221
|
+
size: "sm",
|
|
1222
|
+
disabled: busy,
|
|
1223
|
+
onClick: () => void action(async () => {
|
|
1224
|
+
await load();
|
|
1225
|
+
}),
|
|
1226
|
+
children: text.reconnect
|
|
1227
|
+
})]
|
|
1228
|
+
}) : null,
|
|
1097
1229
|
note && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1098
1230
|
role: "status",
|
|
1099
1231
|
className: "dsh-ui-notice",
|
|
@@ -1104,13 +1236,13 @@ function NetworkSearchSettings({ client }) {
|
|
|
1104
1236
|
className: "dsh-ui-banner dsh-ui-banner--danger",
|
|
1105
1237
|
children: text.storageFailed
|
|
1106
1238
|
}),
|
|
1107
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
1239
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1108
1240
|
role: "tabpanel",
|
|
1109
1241
|
id: tabsId + "-providers-panel",
|
|
1110
1242
|
"aria-labelledby": tabsId + "-providers-tab",
|
|
1111
1243
|
hidden: tab !== "providers",
|
|
1112
1244
|
tabIndex: 0,
|
|
1113
|
-
children: backends.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1245
|
+
children: [backends.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1114
1246
|
className: "dsh-ui-empty",
|
|
1115
1247
|
children: text.empty
|
|
1116
1248
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
|
|
@@ -1139,17 +1271,23 @@ function NetworkSearchSettings({ client }) {
|
|
|
1139
1271
|
children: backends.map((provider) => {
|
|
1140
1272
|
const participating = order.includes(provider.id);
|
|
1141
1273
|
const rank = order.indexOf(provider.id);
|
|
1142
|
-
const
|
|
1143
|
-
const
|
|
1144
|
-
const
|
|
1274
|
+
const dormant = !isRegisteredSearch(saved, provider.id);
|
|
1275
|
+
const active = !dormant && saved.searchActive && selected?.id === provider.id;
|
|
1276
|
+
const info = searchProviderInfo(provider, locale);
|
|
1277
|
+
const owned = ownsProviderConfiguration(provider);
|
|
1278
|
+
const access = configurationAccess(provider);
|
|
1279
|
+
const needsKey = owned && Boolean(provider.credentialRef);
|
|
1145
1280
|
const shared = Boolean(provider.credentialShared);
|
|
1146
|
-
const showKey = needsKey && participating && (!shared || !provider.configured);
|
|
1281
|
+
const showKey = !dormant && needsKey && participating && (!shared || !provider.configured);
|
|
1147
1282
|
const canSort = participating && order.length > 1;
|
|
1148
1283
|
const readOnly = writable[provider.credentialRef ?? ""] === false;
|
|
1149
1284
|
const typedKey = (keys[provider.id] ?? "").trim();
|
|
1285
|
+
const availability = dormant ? "dormant" : provider.configured ? "available" : "unavailable";
|
|
1150
1286
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
1151
1287
|
className: active ? "is-active" : void 0,
|
|
1152
1288
|
"data-testid": `web-search-rank-${provider.id}`,
|
|
1289
|
+
"data-availability": availability,
|
|
1290
|
+
"data-in-use": active || void 0,
|
|
1153
1291
|
"data-dragging": dragSource === provider.id || void 0,
|
|
1154
1292
|
"data-drop": dropTarget === provider.id && dragSource ? order.indexOf(dragSource) < rank ? "after" : "before" : void 0,
|
|
1155
1293
|
onDragOver: (event) => {
|
|
@@ -1251,11 +1389,15 @@ function NetworkSearchSettings({ client }) {
|
|
|
1251
1389
|
className: "dsh-ui-heading",
|
|
1252
1390
|
children: provider.label
|
|
1253
1391
|
}),
|
|
1392
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1393
|
+
className: "ws-provider-note",
|
|
1394
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: dormant ? "idle" : provider.configured ? "done" : "warning" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: dormant ? text.dormant : provider.configured ? text.available : text.unavailable })]
|
|
1395
|
+
}),
|
|
1254
1396
|
active && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
|
|
1255
1397
|
tone: "success",
|
|
1256
1398
|
children: text.inUse
|
|
1257
1399
|
}),
|
|
1258
|
-
provider.signupUrl && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
1400
|
+
provider.signupUrl && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AppLink, {
|
|
1259
1401
|
url: provider.signupUrl,
|
|
1260
1402
|
label: provider.label + " " + text.signup,
|
|
1261
1403
|
children: text.signup
|
|
@@ -1264,20 +1406,30 @@ function NetworkSearchSettings({ client }) {
|
|
|
1264
1406
|
}),
|
|
1265
1407
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1266
1408
|
className: "web-search-provider-info",
|
|
1267
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: info.description }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: [info.pricing, info.pricingUrl && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [" ", /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
1409
|
+
children: [info.description ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: info.description }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: [info.pricing, info.pricingUrl && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [" ", /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AppLink, {
|
|
1268
1410
|
url: info.pricingUrl,
|
|
1269
1411
|
label: provider.label + " " + text.pricing,
|
|
1270
1412
|
children: provider.id === "ddg" ? text.website : text.pricing
|
|
1271
1413
|
})] })] })]
|
|
1272
1414
|
}),
|
|
1273
|
-
|
|
1274
|
-
className: "
|
|
1275
|
-
children:
|
|
1415
|
+
access.kind === "link" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1416
|
+
className: "dsh-ui-hint",
|
|
1417
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AppLink, {
|
|
1418
|
+
url: access.url,
|
|
1419
|
+
label: text.openConfig,
|
|
1420
|
+
children: text.openConfig
|
|
1421
|
+
})
|
|
1422
|
+
}) : access.kind === "owner" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
1423
|
+
className: "dsh-ui-hint",
|
|
1424
|
+
children: [text.ownerSettings, access.name]
|
|
1425
|
+
}) : access.kind === "unknown" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1426
|
+
className: "dsh-ui-hint",
|
|
1427
|
+
children: text.unknownOwner
|
|
1276
1428
|
}) : null,
|
|
1277
|
-
shared && participating && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", {
|
|
1429
|
+
shared && participating && owned ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", {
|
|
1278
1430
|
className: "dsh-ui-meta",
|
|
1279
1431
|
children: provider.credentialHint ?? text.sharedKey
|
|
1280
|
-
}),
|
|
1432
|
+
}) : null,
|
|
1281
1433
|
showKey ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1282
1434
|
className: "dsh-ui-field",
|
|
1283
1435
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -1326,7 +1478,7 @@ function NetworkSearchSettings({ client }) {
|
|
|
1326
1478
|
disabled: busy,
|
|
1327
1479
|
onChange: (next) => {
|
|
1328
1480
|
const following = next ? [...order, provider.id] : order.filter((id) => id !== provider.id);
|
|
1329
|
-
runImmediate(nextSearchEnabled(saved.settings.searchEnabled, following, saved.providers, {}, writable), following);
|
|
1481
|
+
runImmediate(nextSearchEnabled(saved.settings.searchEnabled, following, saved.providers, {}, writable), following, droppedDormantIds(saved.settings, following, saved.providers));
|
|
1330
1482
|
}
|
|
1331
1483
|
})
|
|
1332
1484
|
})
|
|
@@ -1334,10 +1486,10 @@ function NetworkSearchSettings({ client }) {
|
|
|
1334
1486
|
}, provider.id);
|
|
1335
1487
|
})
|
|
1336
1488
|
}),
|
|
1337
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1489
|
+
backends.some(usesBundledPricing) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1338
1490
|
className: "dsh-ui-hint",
|
|
1339
1491
|
children: pricingCheckedText(locale, PROVIDER_PRICING_CHECKED)
|
|
1340
|
-
}),
|
|
1492
|
+
}) : null,
|
|
1341
1493
|
on ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1342
1494
|
className: "dsh-ui-actions",
|
|
1343
1495
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
@@ -1354,7 +1506,104 @@ function NetworkSearchSettings({ client }) {
|
|
|
1354
1506
|
})
|
|
1355
1507
|
}) : null
|
|
1356
1508
|
]
|
|
1357
|
-
})
|
|
1509
|
+
}), (saved.providers.some((provider) => provider.kind === "fetch") || saved.settings.fetchProvider) && (() => {
|
|
1510
|
+
const fetchId = saved.settings.fetchProvider;
|
|
1511
|
+
const fetchSelected = saved.providers.find((provider) => provider.kind === "fetch" && provider.id === fetchId);
|
|
1512
|
+
const fetchDormant = Boolean(fetchId) && !fetchSelected;
|
|
1513
|
+
const fetchActive = Boolean(fetchSelected && saved.fetchActive && fetchSelected.configured);
|
|
1514
|
+
const fetchAccess = fetchSelected ? configurationAccess(fetchSelected) : fetchDormant ? { kind: "unknown" } : void 0;
|
|
1515
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
|
|
1516
|
+
className: "dsh-ui-card dsh-ui-card--flat",
|
|
1517
|
+
"data-testid": "web-search-fetch",
|
|
1518
|
+
"data-availability": fetchDormant ? "dormant" : fetchSelected?.configured ? "available" : "unavailable",
|
|
1519
|
+
"data-in-use": fetchActive || void 0,
|
|
1520
|
+
children: [
|
|
1521
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
1522
|
+
className: "ws-card-head",
|
|
1523
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
1524
|
+
className: "dsh-ui-title",
|
|
1525
|
+
children: text.fetchTitle
|
|
1526
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
1527
|
+
className: "ws-provider-note",
|
|
1528
|
+
children: [
|
|
1529
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: fetchDormant ? "idle" : fetchSelected?.configured ? "done" : "warning" }),
|
|
1530
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: fetchDormant ? text.dormant : fetchSelected?.configured ? text.available : text.unavailable }),
|
|
1531
|
+
fetchActive ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
|
|
1532
|
+
tone: "success",
|
|
1533
|
+
children: text.inUse
|
|
1534
|
+
}) : null
|
|
1535
|
+
]
|
|
1536
|
+
})] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
|
|
1537
|
+
checked: saved.settings.fetchEnabled,
|
|
1538
|
+
label: text.fetchTitle,
|
|
1539
|
+
disabled: busy || reordering,
|
|
1540
|
+
onChange: (next) => void action(async () => {
|
|
1541
|
+
const current = statusRef.current;
|
|
1542
|
+
adopt(await call("update", {
|
|
1543
|
+
settings: {
|
|
1544
|
+
...editable(current.settings),
|
|
1545
|
+
fetchEnabled: next
|
|
1546
|
+
},
|
|
1547
|
+
expectedRevision: current.settings.revision
|
|
1548
|
+
}));
|
|
1549
|
+
await load();
|
|
1550
|
+
})
|
|
1551
|
+
})]
|
|
1552
|
+
}),
|
|
1553
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1554
|
+
className: "dsh-ui-field",
|
|
1555
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1556
|
+
className: "dsh-ui-label",
|
|
1557
|
+
children: text.fetchProvider
|
|
1558
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
1559
|
+
className: "dsh-ui-select",
|
|
1560
|
+
"aria-label": text.fetchProvider,
|
|
1561
|
+
value: fetchId,
|
|
1562
|
+
disabled: busy || reordering,
|
|
1563
|
+
onChange: (event) => {
|
|
1564
|
+
const id = event.target.value;
|
|
1565
|
+
action(async () => {
|
|
1566
|
+
const current = statusRef.current;
|
|
1567
|
+
adopt(await call("update", {
|
|
1568
|
+
settings: {
|
|
1569
|
+
...editable(current.settings),
|
|
1570
|
+
fetchProvider: id
|
|
1571
|
+
},
|
|
1572
|
+
expectedRevision: current.settings.revision
|
|
1573
|
+
}));
|
|
1574
|
+
await load();
|
|
1575
|
+
});
|
|
1576
|
+
},
|
|
1577
|
+
children: [fetchDormant && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
1578
|
+
value: fetchId,
|
|
1579
|
+
children: [
|
|
1580
|
+
fetchId,
|
|
1581
|
+
" · ",
|
|
1582
|
+
text.dormant
|
|
1583
|
+
]
|
|
1584
|
+
}), saved.providers.filter((provider) => provider.kind === "fetch").map((provider) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
1585
|
+
value: provider.id,
|
|
1586
|
+
children: [provider.label, provider.configured ? "" : " · " + text.unavailable]
|
|
1587
|
+
}, provider.id))]
|
|
1588
|
+
})]
|
|
1589
|
+
}),
|
|
1590
|
+
fetchAccess?.kind === "link" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1591
|
+
className: "dsh-ui-hint",
|
|
1592
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AppLink, {
|
|
1593
|
+
url: fetchAccess.url,
|
|
1594
|
+
label: text.openConfig,
|
|
1595
|
+
children: text.openConfig
|
|
1596
|
+
})
|
|
1597
|
+
}) : fetchAccess?.kind === "owner" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
1598
|
+
className: "dsh-ui-hint",
|
|
1599
|
+
children: [text.ownerSettings, fetchAccess.name]
|
|
1600
|
+
}) : fetchAccess?.kind === "unknown" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1601
|
+
className: "dsh-ui-hint",
|
|
1602
|
+
children: text.unknownOwner
|
|
1603
|
+
}) : null
|
|
1604
|
+
]
|
|
1605
|
+
});
|
|
1606
|
+
})()]
|
|
1358
1607
|
}),
|
|
1359
1608
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1360
1609
|
role: "tabpanel",
|
|
@@ -1496,21 +1745,35 @@ function apply(ctx) {
|
|
|
1496
1745
|
ctx.effect(() => client.slots.inject("plugins.bundle.config", () => client.slots.register({
|
|
1497
1746
|
name: "plugins.bundle.config",
|
|
1498
1747
|
key: "@klarkxy/dsh-web-search-manager"
|
|
1499
|
-
}, () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NetworkSearchSettings, {
|
|
1748
|
+
}, (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NetworkSearchSettings, {
|
|
1749
|
+
client,
|
|
1750
|
+
hidden: props?.hidden
|
|
1751
|
+
}))), "web-search-manager.settings");
|
|
1500
1752
|
}
|
|
1501
1753
|
//#endregion
|
|
1502
1754
|
exports.NetworkSearchSettings = NetworkSearchSettings;
|
|
1755
|
+
exports.STATUS_REFRESH_MS = STATUS_REFRESH_MS;
|
|
1503
1756
|
exports.apply = apply;
|
|
1757
|
+
exports.bindStatusRefresh = bindStatusRefresh;
|
|
1504
1758
|
exports.canEnableSearch = canEnableSearch;
|
|
1759
|
+
exports.canStartStatusRead = canStartStatusRead;
|
|
1505
1760
|
exports.catalogSearchBackends = catalogSearchBackends;
|
|
1761
|
+
exports.configurationAccess = configurationAccess;
|
|
1762
|
+
exports.dormantSearchPlaceholder = dormantSearchPlaceholder;
|
|
1763
|
+
exports.droppedDormantIds = droppedDormantIds;
|
|
1506
1764
|
exports.immediateUpdate = immediateUpdate;
|
|
1507
1765
|
exports.inject = inject;
|
|
1766
|
+
exports.isExternalConfigurationUrl = isExternalConfigurationUrl;
|
|
1767
|
+
exports.isPageHidden = isPageHidden;
|
|
1508
1768
|
exports.isProviderOn = isProviderOn;
|
|
1769
|
+
exports.isRegisteredSearch = isRegisteredSearch;
|
|
1509
1770
|
exports.name = name;
|
|
1510
1771
|
exports.nextSearchEnabled = nextSearchEnabled;
|
|
1772
|
+
exports.ownsProviderConfiguration = ownsProviderConfiguration;
|
|
1511
1773
|
exports.parseLimit = parseLimit;
|
|
1512
1774
|
exports.searchBackends = searchBackends;
|
|
1513
1775
|
exports.selectedSearchBackend = selectedSearchBackend;
|
|
1776
|
+
exports.shouldApplyPolledStatus = shouldApplyPolledStatus;
|
|
1514
1777
|
|
|
1515
1778
|
//# sourceMappingURL=client.inner.cjs.map
|
|
1516
1779
|
return module.exports;
|