@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/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 __nodeShims;
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 searchProviderInfo(provider) {
590
- const known = Object.hasOwn(providerInfo, provider.id) ? providerInfo[provider.id] : void 0;
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
- //#endregion
598
- //#region src/contracts.ts
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 pickActiveSearch(order, configured) {
604
- return order.find(configured) ?? "";
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
- const enabled = provider.kind === "search" ? status.searchActive : status.fetchActive;
626
- const selected = status.settings[provider.kind === "search" ? "searchProvider" : "fetchProvider"];
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 searchBackends(status) {
630
- const all = status.providers.filter((provider) => provider.kind === "search");
631
- return resolveSearchOrder(all.map((provider) => provider.id), status.settings, (id) => Boolean(all.find((provider) => provider.id === id)?.credentialRef)).flatMap((id) => all.find((provider) => provider.id === id) ?? []);
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 enabled = searchBackends(status);
635
- const rest = status.providers.filter((provider) => provider.kind === "search" && !enabled.some((row) => row.id === provider.id));
636
- return [...enabled, ...rest];
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 ExternalLink({ url, label, children }) {
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
- target: "_blank",
647
- rel: "noreferrer noopener",
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" ? `价格核对于 ${date},额度与费用以供应商账户为准。` : `Prices checked on ${date}; your provider account is authoritative.`;
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: order,
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
- async function load(resetDraft = false) {
889
- const next = await call("status");
890
- adopt(next, resetDraft);
891
- setLoadError("");
892
- const refs = next.providers.flatMap((provider) => provider.credentialRef ? [provider.credentialRef] : []);
893
- if (refs.length) {
894
- const credentials = unwrap(await client.remote.credentials.describe(refs));
895
- setWritable(Object.fromEntries(Object.entries(credentials).map(([ref, value]) => [ref, value.writable])));
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 (timerRef.current === void 0) return;
904
- clearTimeout(timerRef.current);
905
- const current = statusRef.current;
906
- const next = pendingRef.current;
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.jsx)("div", {
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 active = saved.searchActive && selected?.id === provider.id;
1143
- const info = searchProviderInfo(provider);
1144
- const needsKey = Boolean(provider.credentialRef);
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)(ExternalLink, {
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)(ExternalLink, {
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
- participating && !provider.configured ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
1274
- className: "ws-provider-note",
1275
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "warning" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: needsKey ? text.needsKey : text.unavailable })]
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, { client }))), "web-search-manager.settings");
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;