@240xu/dsh-websearch 2.0.1 → 2.0.3

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.
Files changed (2) hide show
  1. package/lib/client.js +45 -27
  2. package/package.json +1 -1
package/lib/client.js CHANGED
@@ -26,7 +26,19 @@ window.__ModuleLoader__.load({
26
26
  ];
27
27
  const GLOBAL_FIELDS = ["numResults", "concurrency", "backendTimeoutMs"];
28
28
 
29
+ /** Where the user goes to get/rotate each key. */
30
+ const KEY_URLS = {
31
+ brave: "https://api-dashboard.search.brave.com/app/keys",
32
+ tavily: "https://app.tavily.com/home",
33
+ serper: "https://serper.dev/api-key",
34
+ mojeek: "https://www.mojeek.com/services/search/web-search-api/",
35
+ deepseek: "https://platform.deepseek.com/api_keys",
36
+ anthropic: "https://console.anthropic.com/settings/keys",
37
+ openai: "https://platform.openai.com/api-keys",
38
+ };
39
+
29
40
  const zh = {
41
+ nav: "搜索",
30
42
  title: "统一搜索 (Unified Search)",
31
43
  description: "聚合网络搜索:11 个后端并发 fan-out、URL 去重合并。开关后端、填 API Key、调参数,保存即生效。",
32
44
  save: "保存",
@@ -38,6 +50,7 @@ window.__ModuleLoader__.load({
38
50
  reset: "重置",
39
51
  overridden: "已自定义",
40
52
  keyLabel: "{id} API Key",
53
+ getKey: "获取 Key ↗",
41
54
  keyHint: "填入后保存到凭证库(对应环境变量引用);留空表示不修改",
42
55
  keySet: "已配置",
43
56
  keyUnset: "未配置",
@@ -45,6 +58,7 @@ window.__ModuleLoader__.load({
45
58
  globalsTitle: "全局参数",
46
59
  };
47
60
  const en = {
61
+ nav: "Web Search",
48
62
  title: "Unified Search",
49
63
  description: "Aggregated web search: 11 backends fan out concurrently, URL-deduped. Toggle backends, set API keys, tune globals — saves apply immediately.",
50
64
  save: "Save",
@@ -56,6 +70,7 @@ window.__ModuleLoader__.load({
56
70
  reset: "Reset",
57
71
  overridden: "customized",
58
72
  keyLabel: "{id} API Key",
73
+ getKey: "Get key ↗",
59
74
  keyHint: "Stored into the credential store on save; leave blank to keep current",
60
75
  keySet: "configured",
61
76
  keyUnset: "not set",
@@ -85,10 +100,6 @@ window.__ModuleLoader__.load({
85
100
  };
86
101
  }
87
102
 
88
- function useStore(store, selector = (s) => s) {
89
- return react.useSyncExternalStore(store.subscribe, () => selector(store.getSnapshot()));
90
- }
91
-
92
103
  /**
93
104
  * Card state machine: reads the bound settings scope snapshot, stages edits,
94
105
  * writes section fields through scope.set/unset and secrets via credentials.
@@ -199,16 +210,16 @@ window.__ModuleLoader__.load({
199
210
  }
200
211
 
201
212
  inject() {
213
+ // Slot contract: hooks.{name} must be an observable store; the renderer
214
+ // converts it into props.use{Name} (observableHook). Actions go top-level.
202
215
  return {
203
- hooks: {
204
- useCard: () => useStore(this.store),
205
- edit: (f, t) => this.edit(f, t),
206
- setToggle: (id, checked) => this.setToggle(id, checked),
207
- reset: (f) => this.reset(f),
208
- editKey: (r, t) => this.editKey(r, t),
209
- save: () => this.save(),
210
- discard: () => { this.staged.clear(); this.stagedKeys.clear(); this.failed = false; this.republish(); },
211
- },
216
+ hooks: { unifiedSearchCard: this.store },
217
+ edit: (f, t) => this.edit(f, t),
218
+ setToggle: (id, checked) => this.setToggle(id, checked),
219
+ reset: (f) => this.reset(f),
220
+ editKey: (r, t) => this.editKey(r, t),
221
+ save: () => this.save(),
222
+ discard: () => { this.staged.clear(); this.stagedKeys.clear(); this.failed = false; this.republish(); },
212
223
  };
213
224
  }
214
225
  }
@@ -230,17 +241,17 @@ window.__ModuleLoader__.load({
230
241
  }
231
242
 
232
243
  /** The card root. */
233
- function UnifiedSearchCard(props) {
234
- const card = props.card;
235
- const state = card.useCard();
244
+ function UnifiedSearchSection(props) {
245
+ const state = props.useUnifiedSearchCard((snapshot) => snapshot);
246
+ const card = { edit: props.edit, setToggle: props.setToggle, reset: props.reset, editKey: props.editKey, save: props.save, discard: props.discard };
236
247
  // Occupants of settings.plugin.item render inside the tab's <ul>; use <li>.
237
248
  if (!state.ready) {
238
- return react_jsx_runtime.jsx("li", { className: "wsc-card", style: { fontSize: 12, opacity: .6 }, children: "…" });
249
+ return react_jsx_runtime.jsx("div", { className: "wsc-card", style: { fontSize: 12, opacity: .6 }, children: "…" });
239
250
  }
240
251
 
241
252
  const toggleField = (id) => "enable" + id.charAt(0).toUpperCase() + id.slice(1);
242
253
 
243
- return react_jsx_runtime.jsxs("li", { className: "wsc-card", children: [
254
+ return react_jsx_runtime.jsxs("div", { className: "wsc-card", children: [
244
255
  react_jsx_runtime.jsx("p", { style: { margin: 0, fontSize: 12, opacity: .65 }, children: props.t("description") }),
245
256
  react_jsx_runtime.jsx("h4", { className: "wsc-sectionTitle", children: props.t("togglesTitle") }),
246
257
  react_jsx_runtime.jsx("div", { className: "wsc-toggles", children: BACKENDS.map((b) =>
@@ -270,7 +281,14 @@ window.__ModuleLoader__.load({
270
281
  react_jsx_runtime.jsxs("div", { children: [
271
282
  react_jsx_runtime.jsxs("div", { className: "wsc-fieldHead", children: [
272
283
  react_jsx_runtime.jsx("span", { className: "wsc-label", children: props.t("keyLabel", { id: b.id }) }),
273
- react_jsx_runtime.jsx("span", { className: "wsc-badge" + (state.keyState[b.keyEnv]?.configured ? " wsc-badgeOn" : ""), children: state.keyState[b.keyEnv]?.configured ? props.t("keySet") : props.t("keyUnset") }),
284
+ react_jsx_runtime.jsxs("span", { className: "wsc-badges", style: { display: "flex", gap: 6, alignItems: "center" }, children: [
285
+ KEY_URLS[b.id] && react_jsx_runtime.jsx("a", {
286
+ href: KEY_URLS[b.id], target: "_blank", rel: "noreferrer",
287
+ style: { fontSize: 11, color: "var(--dsw-alias-accent-primary)", textDecoration: "none" },
288
+ children: props.t("getKey"),
289
+ }),
290
+ react_jsx_runtime.jsx("span", { className: "wsc-badge" + (state.keyState[b.keyEnv]?.configured ? " wsc-badgeOn" : ""), children: state.keyState[b.keyEnv]?.configured ? props.t("keySet") : props.t("keyUnset") }),
291
+ ] }),
274
292
  ] }),
275
293
  react_jsx_runtime.jsx("input", {
276
294
  className: "wsc-keyInput", type: "password", autoComplete: "off",
@@ -299,14 +317,14 @@ window.__ModuleLoader__.load({
299
317
  ctx.get("remote"),
300
318
  );
301
319
  ctx.effect(() => ctx.remote.$on("credentials/updated", () => controller.refreshAllCredentials()), "websearch: credential invalidations");
302
- ctx.slots.inject("settings.plugin.item", function* () {
303
- yield ctx.slots.register({
304
- name: "settings.plugin.item",
305
- key: PLUGIN_NS,
306
- locale: NS,
307
- inject: () => controller.inject(),
308
- }, UnifiedSearchCard);
309
- });
320
+ ctx.slots.inject("settings.section", () => ctx.slots.register({
321
+ name: "settings.section",
322
+ id: "websearch",
323
+ order: 16,
324
+ label: () => t("nav"),
325
+ locale: NS,
326
+ inject: () => controller.inject(),
327
+ }, UnifiedSearchSection));
310
328
  controller.refreshAllCredentials();
311
329
  }
312
330
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@240xu/dsh-websearch",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "description": "Unified web search provider for DeepSeek Harness (ctx.web): 11 backends — Exa + Parallel + DDG + SearXNG (keyless), DeepSeek + Anthropic + OpenAI + Brave + Tavily + Serper + Mojeek (key-gated via Settings credentials). Registers provider id \"unified\" for zero-config web_search tool.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",