dsh-web-search-plugin 0.1.1 → 0.1.2

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,7 +1,9 @@
1
1
  /**
2
2
  * Browser half of `dsh-web-search-plugin`: a plugin card under the
3
3
  * "settings → Plugins → Plugin configuration" surface that edits the
4
- * `dsh-web-search-plugin` settings namespace and its `TAVILY_API_KEY` credential.
4
+ * `dsh-web-search-plugin` settings namespace. The card switches the
5
+ * Tavily / Brave backend and writes each engine's credential through the
6
+ * credentials domain (`TAVILY_API_KEY` / `BRAVE_API_KEY` by default).
5
7
  *
6
8
  * This bundle is a self-contained hand-written module in the exact wire format
7
9
  * the client module system expects: one `window.__ModuleLoader__.load` call
@@ -21,28 +23,40 @@ window.__ModuleLoader__.load({
21
23
  let react = require("react");
22
24
  let _deepseek_ai_dsh_client_runtime_client = require("@deepseek-ai/dsh-client-runtime/client");
23
25
 
24
- /** Settings namespace of the Tavily search provider. */
26
+ /** Settings namespace of this plugin. */
25
27
  const NS = "dsh-web-search-plugin";
26
- /** Credential reference the provider resolves when the section names none. */
27
- const DEFAULT_API_KEY_REF = "TAVILY_API_KEY";
28
- /** Form field the credential control stages under. */
29
- const API_KEY_FIELD = "apiKey";
28
+ /** Credential reference Tavily keyed mode resolves when the section names none. */
29
+ const DEFAULT_TAVILY_KEY_REF = "TAVILY_API_KEY";
30
+ /** Credential reference Brave resolves when the section names none. */
31
+ const DEFAULT_BRAVE_KEY_REF = "BRAVE_API_KEY";
32
+ /** Form field the Tavily write-only credential control stages under. */
33
+ const TAVILY_API_KEY_FIELD = "apiKey";
34
+ /** Form field the Brave write-only credential control stages under. */
35
+ const BRAVE_API_KEY_FIELD = "braveApiKey";
36
+ const SECRET_FIELDS = new Set([TAVILY_API_KEY_FIELD, BRAVE_API_KEY_FIELD]);
30
37
 
31
38
  /** Locale bundles for the card. */
32
39
  const en = {
33
- nav: "Web search (Tavily)",
34
- description: "The Tavily search provider. Keyless by default; switch to keyed and add a Tavily API key for higher limits.",
40
+ nav: "Web search",
41
+ description: "Tavily (keyless by default) or Brave Search. Switching engines here is enough — web.searchProvider stays dsh-web-search.",
42
+ provider: "Search engine",
43
+ providerTavily: "Tavily",
44
+ providerBrave: "Brave Search",
35
45
  mode: "Auth mode",
36
46
  modeKeyless: "Keyless (free, rate-limited)",
37
47
  modeKeyed: "API key",
38
48
  apiKey: "API key",
39
49
  apiKeyHint: "Stored outside the settings file. Leave blank to keep the current key.",
40
50
  apiKeySet: "A key is configured.",
41
- apiKeyUnset: "No key is configured; keyed search is unavailable until one is.",
51
+ apiKeyUnset: "No key is configured; keyed / Brave search is unavailable until one is.",
42
52
  apiKeyEnv: "Credential reference",
43
- apiKeyEnvHint: "Name of the credential/env var resolved for keyed search.",
53
+ apiKeyEnvHint: "Name of the credential/env var resolved for keyed Tavily search.",
54
+ braveApiKeyEnv: "Credential reference",
55
+ braveApiKeyEnvHint: "Name of the credential/env var resolved for Brave search.",
44
56
  baseURL: "Endpoint",
45
57
  baseURLHint: "Leave blank to use the provider default.",
58
+ braveBaseURL: "Endpoint",
59
+ braveBaseURLHint: "Leave blank to use the Brave Search API default.",
46
60
  maxResults: "Max results",
47
61
  maxResultsHint: "Sources returned per search (1-20).",
48
62
  searchDepth: "Search depth",
@@ -53,6 +67,18 @@ window.__ModuleLoader__.load({
53
67
  topic: "Topic",
54
68
  topicGeneral: "General",
55
69
  topicNews: "News",
70
+ country: "Country",
71
+ countryHint: "ISO 2-letter code (e.g. cn). Leave blank for Brave's default.",
72
+ searchLang: "Search language",
73
+ searchLangHint: "e.g. zh-hans or en. Leave blank for Brave's default.",
74
+ freshness: "Freshness",
75
+ freshnessAny: "Any time",
76
+ freshnessDay: "Past day",
77
+ freshnessWeek: "Past week",
78
+ freshnessMonth: "Past month",
79
+ freshnessYear: "Past year",
80
+ proxy: "Proxy",
81
+ proxyHint: "HTTP(S) proxy URL. Leave blank to use HTTPS_PROXY / HTTP_PROXY.",
56
82
  expand: "Show settings",
57
83
  collapse: "Hide settings",
58
84
  save: "Save",
@@ -65,19 +91,26 @@ window.__ModuleLoader__.load({
65
91
  };
66
92
  /** Simplified Chinese copy. */
67
93
  const zh = {
68
- nav: "网页搜索(Tavily)",
69
- description: "Tavily 搜索提供方。默认 keyless 免费使用;切换到 API key 并填入 Tavily key 可提高限额。",
94
+ nav: "网页搜索",
95
+ description: "Tavily(默认 keyless)或 Brave Search。在此切换引擎即可,不必改 web.searchProvider。",
96
+ provider: "搜索引擎",
97
+ providerTavily: "Tavily",
98
+ providerBrave: "Brave Search",
70
99
  mode: "认证模式",
71
100
  modeKeyless: "Keyless(免费、限流)",
72
101
  modeKeyed: "API key",
73
102
  apiKey: "API Key",
74
103
  apiKeyHint: "不写入设置文件。留空表示保持当前密钥。",
75
104
  apiKeySet: "已配置密钥。",
76
- apiKeyUnset: "未配置密钥;keyed 模式下搜索不可用。",
105
+ apiKeyUnset: "未配置密钥;keyed / Brave 模式下搜索不可用。",
77
106
  apiKeyEnv: "凭据引用名",
78
107
  apiKeyEnvHint: "keyed 模式解析的凭据/环境变量名。",
108
+ braveApiKeyEnv: "凭据引用名",
109
+ braveApiKeyEnvHint: "Brave 搜索解析的凭据/环境变量名。",
79
110
  baseURL: "接口地址",
80
111
  baseURLHint: "留空则使用提供方默认地址。",
112
+ braveBaseURL: "接口地址",
113
+ braveBaseURLHint: "留空则使用 Brave Search API 默认地址。",
81
114
  maxResults: "结果数量",
82
115
  maxResultsHint: "每次搜索返回的源数量(1-20)。",
83
116
  searchDepth: "搜索深度",
@@ -88,6 +121,18 @@ window.__ModuleLoader__.load({
88
121
  topic: "主题",
89
122
  topicGeneral: "通用",
90
123
  topicNews: "新闻",
124
+ country: "国家/地区",
125
+ countryHint: "ISO 两位码(如 cn)。留空使用 Brave 默认。",
126
+ searchLang: "搜索语言",
127
+ searchLangHint: "如 zh-hans、en。留空使用 Brave 默认。",
128
+ freshness: "时效",
129
+ freshnessAny: "不限",
130
+ freshnessDay: "过去一天",
131
+ freshnessWeek: "过去一周",
132
+ freshnessMonth: "过去一月",
133
+ freshnessYear: "过去一年",
134
+ proxy: "代理",
135
+ proxyHint: "HTTP(S) 代理 URL。留空则使用 HTTPS_PROXY / HTTP_PROXY。",
91
136
  expand: "展开设置",
92
137
  collapse: "收起设置",
93
138
  save: "保存",
@@ -123,7 +168,7 @@ window.__ModuleLoader__.load({
123
168
  }
124
169
  };
125
170
  }
126
- /** A choice field spec (mode/searchDepth/topic): staged as its raw string. */
171
+ /** A choice field spec: staged as its raw string. */
127
172
  function choiceField(field) {
128
173
  return {
129
174
  field,
@@ -141,16 +186,30 @@ window.__ModuleLoader__.load({
141
186
  }
142
187
 
143
188
  /**
144
- * Staged form over the `dsh-web-search-plugin` settings namespace. Modeled on
145
- * the official cards' `CardForm` contract: a draft map, save writes every
146
- * staged edit once, and the API key is written through the credentials
147
- * domain addressed by the reference the section names.
189
+ * Staged form over the `dsh-web-search-plugin` settings namespace.
190
+ * Write-only credential fields (`apiKey`, `braveApiKey`) have no section
191
+ * spec and are written through the credentials domain.
148
192
  */
149
- var TavilyCardForm = class {
193
+ var SearchCardForm = class {
150
194
  constructor(scope, api) {
151
195
  this.scope = scope;
152
196
  this.api = api;
153
- this.specs = new Map([numberField("maxResults"), textField("apiKeyEnv"), textField("baseURL"), choiceField("mode"), choiceField("searchDepth"), choiceField("topic"), booleanField("includeAnswer")].map((spec) => [spec.field, spec]));
197
+ this.specs = new Map([
198
+ choiceField("provider"),
199
+ choiceField("mode"),
200
+ numberField("maxResults"),
201
+ textField("apiKeyEnv"),
202
+ textField("baseURL"),
203
+ choiceField("searchDepth"),
204
+ choiceField("topic"),
205
+ booleanField("includeAnswer"),
206
+ textField("braveApiKeyEnv"),
207
+ textField("braveBaseURL"),
208
+ textField("country"),
209
+ textField("searchLang"),
210
+ choiceField("freshness"),
211
+ textField("proxy")
212
+ ].map((spec) => [spec.field, spec]));
154
213
  this.staged = new Map();
155
214
  this.saving = false;
156
215
  this.failed = false;
@@ -182,10 +241,7 @@ window.__ModuleLoader__.load({
182
241
  }
183
242
  field(field) {
184
243
  const staged = this.staged.get(field);
185
- // Write-only credential controls have no section spec; they
186
- // report only their staged draft (mirroring the official
187
- // cards' secret-field branch in CardForm.field).
188
- if (field === API_KEY_FIELD) return {
244
+ if (SECRET_FIELDS.has(field)) return {
189
245
  text: staged?.text ?? "",
190
246
  overridden: false,
191
247
  invalid: false
@@ -206,9 +262,9 @@ window.__ModuleLoader__.load({
206
262
  plan() {
207
263
  const plan = [];
208
264
  for (const [field, staged] of this.staged) {
209
- if (field === API_KEY_FIELD) {
265
+ if (SECRET_FIELDS.has(field)) {
210
266
  const value = staged.text.trim();
211
- if (value !== "") plan.push({ field, run: () => this.writeKey(value) });
267
+ if (value !== "") plan.push({ field, run: () => this.writeKey(field, value) });
212
268
  continue;
213
269
  }
214
270
  const spec = this.specs.get(field);
@@ -269,6 +325,7 @@ window.__ModuleLoader__.load({
269
325
  this.staged.set(field, edit);
270
326
  this.failed = false;
271
327
  this.publish();
328
+ if (field === "provider" || field === "apiKeyEnv" || field === "braveApiKeyEnv") this.readCredential();
272
329
  }
273
330
  snapshotOf() {
274
331
  return this.scope.getSnapshot();
@@ -289,12 +346,25 @@ window.__ModuleLoader__.load({
289
346
  publish() {
290
347
  for (const listener of this.listeners) listener();
291
348
  }
292
- /** The credential reference the section names, or the provider default. */
293
- refOf() {
349
+ /** Draft-aware engine: staged `provider` wins over the saved section. */
350
+ providerOf() {
351
+ const staged = this.staged.get("provider");
352
+ if (staged !== void 0 && !staged.clear && staged.text.trim() !== "") return staged.text.trim();
353
+ const declared = this.sectionValue("provider");
354
+ return declared === "brave" ? "brave" : "tavily";
355
+ }
356
+ tavilyRef() {
294
357
  const declared = this.sectionValue("apiKeyEnv");
295
- return declared !== void 0 && declared.length > 0 ? declared : DEFAULT_API_KEY_REF;
358
+ return declared !== void 0 && declared.length > 0 ? declared : DEFAULT_TAVILY_KEY_REF;
359
+ }
360
+ braveRef() {
361
+ const declared = this.sectionValue("braveApiKeyEnv");
362
+ return declared !== void 0 && declared.length > 0 ? declared : DEFAULT_BRAVE_KEY_REF;
363
+ }
364
+ /** The credential reference the currently visible engine uses. */
365
+ refOf() {
366
+ return this.providerOf() === "brave" ? this.braveRef() : this.tavilyRef();
296
367
  }
297
- /** Ask the credentials domain about the reference the section currently names. */
298
368
  async readCredential() {
299
369
  const ref = this.refOf();
300
370
  if (ref !== this.credential.ref) {
@@ -318,27 +388,26 @@ window.__ModuleLoader__.load({
318
388
  this.credential = next;
319
389
  this.publish();
320
390
  }
321
- /** Re-read after the Host reports a change to the reference this card watches. */
322
391
  refreshCredential(ref) {
323
392
  if (ref !== this.credential.ref) return;
324
393
  this.readCredential();
325
394
  }
326
- /** Write the staged key, then re-read whether the Host now holds one. */
327
- async writeKey(value) {
395
+ async writeKey(field, value) {
396
+ const ref = field === BRAVE_API_KEY_FIELD ? this.braveRef() : this.tavilyRef();
328
397
  try {
329
- await this.api.credentials.set({ ref: this.refOf(), value });
398
+ await this.api.credentials.set({ ref, value });
330
399
  } catch (_credentialWriteFailure) {}
331
400
  await this.readCredential();
332
- return this.credential.configured;
401
+ return this.credential.ref === ref ? this.credential.configured : true;
333
402
  }
334
403
  };
335
404
 
336
- /** Bridges the `dsh-web-search-plugin` scope and the credentials domain onto the card. */
337
- var TavilyCardController = class {
405
+ /** Bridges the plugin scope and the credentials domain onto the card. */
406
+ var SearchCardController = class {
338
407
  constructor(scope, api) {
339
408
  this.scope = scope;
340
409
  this.api = api;
341
- this.form = new TavilyCardForm(scope, api);
410
+ this.form = new SearchCardForm(scope, api);
342
411
  this.store = this.form.bind(() => this.projection());
343
412
  scope.subscribe(() => {
344
413
  this.form.readCredential();
@@ -347,21 +416,29 @@ window.__ModuleLoader__.load({
347
416
  projection() {
348
417
  return {
349
418
  ...this.form.shell(),
419
+ provider: this.form.field("provider"),
350
420
  mode: this.form.field("mode"),
351
- apiKey: this.form.field(API_KEY_FIELD),
421
+ apiKey: this.form.field(TAVILY_API_KEY_FIELD),
352
422
  apiKeyEnv: this.form.field("apiKeyEnv"),
353
423
  baseURL: this.form.field("baseURL"),
354
424
  maxResults: this.form.field("maxResults"),
355
425
  searchDepth: this.form.field("searchDepth"),
356
426
  includeAnswer: this.form.field("includeAnswer"),
357
427
  topic: this.form.field("topic"),
428
+ braveApiKey: this.form.field(BRAVE_API_KEY_FIELD),
429
+ braveApiKeyEnv: this.form.field("braveApiKeyEnv"),
430
+ braveBaseURL: this.form.field("braveBaseURL"),
431
+ country: this.form.field("country"),
432
+ searchLang: this.form.field("searchLang"),
433
+ freshness: this.form.field("freshness"),
434
+ proxy: this.form.field("proxy"),
358
435
  apiKeyConfigured: this.form.credential.configured,
359
436
  apiKeyWritable: this.form.credential.writable
360
437
  };
361
438
  }
362
439
  inject() {
363
440
  return {
364
- hooks: { tavilySearchCard: this.store },
441
+ hooks: { pluginSearchCard: this.store },
365
442
  ...this.form.actions()
366
443
  };
367
444
  }
@@ -372,23 +449,21 @@ window.__ModuleLoader__.load({
372
449
  react.createElement("label", { style: { color: "var(--dsw-alias-label-primary)", fontSize: "13px", fontWeight: 500, lineHeight: "1.5" } }, label),
373
450
  children,
374
451
  hint ? react.createElement("p", { style: { color: "var(--dsw-alias-label-tertiary)", margin: 0, fontSize: "12px", lineHeight: "1.5" } }, hint) : null);
375
- /** Text/number input. */
376
452
  const textInput = (props) => react.createElement("input", { style: { border: "1px solid var(--dsw-alias-border-l2)", background: "var(--dsw-alias-bg-layer-3)", height: "34px", font: "inherit", color: "var(--dsw-alias-label-primary)", borderRadius: "8px", padding: "0 12px", fontSize: "13px", lineHeight: "1.5" }, type: "text", ...props.numeric ? { inputMode: "numeric" } : {}, ...props });
377
- /** Password input for the write-only credential. */
378
453
  const secretInput = (props) => react.createElement("input", { style: { border: "1px solid var(--dsw-alias-border-l2)", background: "var(--dsw-alias-bg-layer-3)", height: "34px", font: "inherit", color: "var(--dsw-alias-label-primary)", borderRadius: "8px", padding: "0 12px", fontSize: "13px", lineHeight: "1.5" }, type: "password", autoComplete: "off", ...props });
379
- /** Choice select. */
380
454
  const selectInput = (props) => react.createElement("select", { style: { border: "1px solid var(--dsw-alias-border-l2)", background: "var(--dsw-alias-bg-layer-3)", height: "34px", font: "inherit", color: "var(--dsw-alias-label-primary)", borderRadius: "8px", padding: "0 12px", fontSize: "13px", lineHeight: "1.5" }, ...props });
381
- /** Checkbox for booleans. */
382
455
  const checkboxInput = (props) => react.createElement("input", { type: "checkbox", style: { width: "16px", height: "16px", accentColor: "var(--dsw-alias-brand-primary)" }, ...props });
383
456
 
384
- /** Render one plugin card: header disclosure plus the staged form. */
385
- function TavilyCard(props) {
457
+ /** Render one plugin card: engine switch plus the engine-specific form. */
458
+ function SearchCard(props) {
386
459
  const { t } = props;
387
- const state = props.useTavilySearchCard((snapshot) => snapshot);
460
+ const state = props.usePluginSearchCard((snapshot) => snapshot);
388
461
  const [open, setOpen] = react.useState(false);
389
462
  if (!state.available) return null;
390
463
  const disabled = !state.writable;
391
464
  const blocked = !state.dirty || state.invalid || state.saving;
465
+ const brave = state.provider.text === "brave";
466
+ const keyed = state.mode.text === "keyed";
392
467
  return react.createElement("li", { style: { border: "1px solid var(--dsw-alias-border-l2)", background: "var(--dsw-alias-bg-layer-3)", borderRadius: "12px", listStyle: "none" } },
393
468
  react.createElement("button", { type: "button", "aria-expanded": open, onClick: () => setOpen(!open), style: { appearance: "none", width: "100%", font: "inherit", color: "inherit", textAlign: "left", cursor: "pointer", background: "0 0", border: "0", borderRadius: "12px", alignItems: "center", gap: "12px", padding: "14px 16px", display: "flex" } },
394
469
  react.createElement("span", { style: { flexDirection: "column", flex: 1, gap: "4px", minWidth: 0, display: "flex" } },
@@ -398,22 +473,36 @@ window.__ModuleLoader__.load({
398
473
  react.createElement("span", { style: { color: "var(--dsw-alias-label-tertiary)", flex: "none" } }, open ? "▾" : "▸")),
399
474
  open ? react.createElement("div", { style: { borderTop: "1px solid var(--dsw-alias-border-l2)", margin: "0 16px", paddingBottom: "8px" } },
400
475
  !state.writable ? react.createElement("p", { style: { color: "var(--dsw-alias-label-tertiary)", margin: "12px 0 0", fontSize: "12px", lineHeight: "1.5" } }, t("readOnly")) : null,
401
- fieldRow(t("mode"), null, selectInput({ id: "plugin-config-web-search-mode", value: state.mode.text, disabled, onChange: (e) => props.edit("mode", e.target.value), children: [
476
+ fieldRow(t("provider"), null, selectInput({ id: "plugin-config-web-search-provider", value: brave ? "brave" : "tavily", disabled, onChange: (e) => props.edit("provider", e.target.value), children: [
477
+ react.createElement("option", { value: "tavily", key: "tavily" }, t("providerTavily")),
478
+ react.createElement("option", { value: "brave", key: "brave" }, t("providerBrave"))
479
+ ] })),
480
+ brave ? null : fieldRow(t("mode"), null, selectInput({ id: "plugin-config-web-search-mode", value: state.mode.text, disabled, onChange: (e) => props.edit("mode", e.target.value), children: [
402
481
  react.createElement("option", { value: "keyless", key: "keyless" }, t("modeKeyless")),
403
482
  react.createElement("option", { value: "keyed", key: "keyed" }, t("modeKeyed"))
404
483
  ] })),
405
- state.mode.text === "keyed" ? fieldRow(t("apiKey"), t("apiKeyHint"), secretInput({ id: "plugin-config-web-search-key", value: state.apiKey.text, disabled: !state.apiKeyWritable, placeholder: state.apiKeyConfigured ? t("apiKeySet") : t("apiKeyUnset"), onChange: (e) => props.edit("apiKey", e.target.value) })) : null,
406
- state.mode.text === "keyed" ? fieldRow(t("apiKeyEnv"), t("apiKeyEnvHint"), textInput({ id: "plugin-config-web-search-keyref", value: state.apiKeyEnv.text, disabled, onChange: (e) => props.edit("apiKeyEnv", e.target.value), onReset: () => props.resetField("apiKeyEnv") })) : null,
407
- fieldRow(t("baseURL"), t("baseURLHint"), textInput({ id: "plugin-config-web-search-base", value: state.baseURL.text, disabled, onChange: (e) => props.edit("baseURL", e.target.value), onReset: () => props.resetField("baseURL") })),
408
- fieldRow(t("searchDepth"), null, selectInput({ id: "plugin-config-web-search-depth", value: state.searchDepth.text, disabled, onChange: (e) => props.edit("searchDepth", e.target.value), children: [
484
+ (!brave && keyed) || brave ? fieldRow(t("apiKey"), t("apiKeyHint"), secretInput({ id: "plugin-config-web-search-key", value: brave ? state.braveApiKey.text : state.apiKey.text, disabled: !state.apiKeyWritable, placeholder: state.apiKeyConfigured ? t("apiKeySet") : t("apiKeyUnset"), onChange: (e) => props.edit(brave ? BRAVE_API_KEY_FIELD : TAVILY_API_KEY_FIELD, e.target.value) })) : null,
485
+ (!brave && keyed) || brave ? fieldRow(brave ? t("braveApiKeyEnv") : t("apiKeyEnv"), brave ? t("braveApiKeyEnvHint") : t("apiKeyEnvHint"), textInput({ id: "plugin-config-web-search-keyref", value: brave ? state.braveApiKeyEnv.text : state.apiKeyEnv.text, disabled, onChange: (e) => props.edit(brave ? "braveApiKeyEnv" : "apiKeyEnv", e.target.value), onReset: () => props.resetField(brave ? "braveApiKeyEnv" : "apiKeyEnv") })) : null,
486
+ brave ? fieldRow(t("braveBaseURL"), t("braveBaseURLHint"), textInput({ id: "plugin-config-web-search-brave-base", value: state.braveBaseURL.text, disabled, onChange: (e) => props.edit("braveBaseURL", e.target.value), onReset: () => props.resetField("braveBaseURL") })) : fieldRow(t("baseURL"), t("baseURLHint"), textInput({ id: "plugin-config-web-search-base", value: state.baseURL.text, disabled, onChange: (e) => props.edit("baseURL", e.target.value), onReset: () => props.resetField("baseURL") })),
487
+ brave ? null : fieldRow(t("searchDepth"), null, selectInput({ id: "plugin-config-web-search-depth", value: state.searchDepth.text, disabled, onChange: (e) => props.edit("searchDepth", e.target.value), children: [
409
488
  react.createElement("option", { value: "basic", key: "basic" }, t("searchDepthBasic")),
410
489
  react.createElement("option", { value: "advanced", key: "advanced" }, t("searchDepthAdvanced"))
411
490
  ] })),
412
- fieldRow(t("topic"), null, selectInput({ id: "plugin-config-web-search-topic", value: state.topic.text, disabled, onChange: (e) => props.edit("topic", e.target.value), children: [
491
+ brave ? null : fieldRow(t("topic"), null, selectInput({ id: "plugin-config-web-search-topic", value: state.topic.text, disabled, onChange: (e) => props.edit("topic", e.target.value), children: [
413
492
  react.createElement("option", { value: "general", key: "general" }, t("topicGeneral")),
414
493
  react.createElement("option", { value: "news", key: "news" }, t("topicNews"))
415
494
  ] })),
416
- fieldRow(t("includeAnswer"), t("includeAnswerHint"), checkboxInput({ id: "plugin-config-web-search-answer", checked: state.includeAnswer.text === "true", disabled, onChange: (e) => props.edit("includeAnswer", e.target.checked ? "true" : "") })),
495
+ brave ? null : fieldRow(t("includeAnswer"), t("includeAnswerHint"), checkboxInput({ id: "plugin-config-web-search-answer", checked: state.includeAnswer.text === "true", disabled, onChange: (e) => props.edit("includeAnswer", e.target.checked ? "true" : "") })),
496
+ brave ? fieldRow(t("country"), t("countryHint"), textInput({ id: "plugin-config-web-search-country", value: state.country.text, disabled, onChange: (e) => props.edit("country", e.target.value), onReset: () => props.resetField("country") })) : null,
497
+ brave ? fieldRow(t("searchLang"), t("searchLangHint"), textInput({ id: "plugin-config-web-search-lang", value: state.searchLang.text, disabled, onChange: (e) => props.edit("searchLang", e.target.value), onReset: () => props.resetField("searchLang") })) : null,
498
+ brave ? fieldRow(t("freshness"), null, selectInput({ id: "plugin-config-web-search-freshness", value: state.freshness.text, disabled, onChange: (e) => props.edit("freshness", e.target.value), children: [
499
+ react.createElement("option", { value: "", key: "any" }, t("freshnessAny")),
500
+ react.createElement("option", { value: "pd", key: "pd" }, t("freshnessDay")),
501
+ react.createElement("option", { value: "pw", key: "pw" }, t("freshnessWeek")),
502
+ react.createElement("option", { value: "pm", key: "pm" }, t("freshnessMonth")),
503
+ react.createElement("option", { value: "py", key: "py" }, t("freshnessYear"))
504
+ ] })) : null,
505
+ brave ? fieldRow(t("proxy"), t("proxyHint"), textInput({ id: "plugin-config-web-search-proxy", value: state.proxy.text, disabled, onChange: (e) => props.edit("proxy", e.target.value), onReset: () => props.resetField("proxy") })) : null,
417
506
  fieldRow(t("maxResults"), t("maxResultsHint"), textInput({ id: "plugin-config-web-search-max", numeric: true, value: state.maxResults.text, disabled, onChange: (e) => props.edit("maxResults", e.target.value), onReset: () => props.resetField("maxResults") })),
418
507
  react.createElement("div", { style: { borderTop: "1px solid var(--dsw-alias-border-l2)", justifyContent: "flex-end", alignItems: "center", gap: "8px", padding: "12px 0 4px", display: "flex" } },
419
508
  state.failed ? react.createElement("p", { role: "status", style: { minWidth: 0, color: "var(--dsw-alias-label-error)", flex: 1, margin: 0, fontSize: "12px", lineHeight: "1.5" } }, t("saveFailed")) : null,
@@ -421,12 +510,12 @@ window.__ModuleLoader__.load({
421
510
  react.createElement("button", { type: "button", disabled: blocked, onClick: props.save, style: { appearance: "none", font: "inherit", cursor: "pointer", border: "1px solid transparent", borderRadius: "8px", padding: "5px 14px", fontSize: "13px", lineHeight: "1.5", color: "var(--dsw-alias-label-on-brand)", background: "var(--dsw-alias-bg-brand-solid)" } }, t(state.saving ? "saving" : "save")))) : null);
422
511
  }
423
512
 
424
- /** Mount the Tavily card into the plugin configuration surface. */
513
+ /** Mount the card into the plugin configuration surface. */
425
514
  function apply(ctx) {
426
515
  const { api } = ctx.get("connection");
427
516
  const t = ctx.locale.bind(NS);
428
517
  ctx.effect(() => ctx.locale.register(NS, { zh, en }), "dsh-web-search-plugin: card dictionaries");
429
- const controller = new TavilyCardController(ctx.settingsScope.bind({ namespace: NS }), api);
518
+ const controller = new SearchCardController(ctx.settingsScope.bind({ namespace: NS }), api);
430
519
  ctx.effect(() => ctx.remote.$on("credentials/updated", (ref) => {
431
520
  controller.form.refreshCredential(ref);
432
521
  }), "dsh-web-search-plugin: credential invalidations");
@@ -436,11 +525,10 @@ window.__ModuleLoader__.load({
436
525
  key: "dsh-web-search-plugin",
437
526
  locale: NS,
438
527
  inject: () => controller.inject()
439
- }, TavilyCard);
528
+ }, SearchCard);
440
529
  });
441
530
  }
442
531
 
443
- /** Cordis service names this browser plugin's apply reads (fiber inject). */
444
532
  const inject = ["slots", "locale", "connection", "remote", "settingsScope"];
445
533
 
446
534
  exports.apply = apply;
@@ -448,4 +536,3 @@ window.__ModuleLoader__.load({
448
536
  return module.exports;
449
537
  }
450
538
  });
451
- //# sourceMappingURL=client.js.map