@klarkxy/dsh-web-search-manager 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  [简体中文](docs/README.zh-CN.md)
6
6
 
7
- Choose which service answers the model's web searches, set request limits, and let the model read public web pages — all from one settings page, with a tab each for search, page reading, and request limits.
7
+ Pick which service answers the model's web searches, set request limits, and let it read public pages — from one settings page with separate tabs for search, page reading, and request limits.
8
8
 
9
9
  ## Install
10
10
 
@@ -12,39 +12,39 @@ Choose which service answers the model's web searches, set request limits, and l
12
12
  dsh plugin --profile web add @klarkxy/dsh-web-search-manager
13
13
  ```
14
14
 
15
- Replace `web` with your profile name, restart DSH Web, and open **Plugins → Web Search**. Restart DSH after plugin updates too; refreshing the page alone does not load updated search code.
15
+ Replace `web` with your profile name. Restart DSH Web, then open **Plugins → Web Search**. Restart after plugin updates too; refreshing the page alone does not load new search code.
16
16
 
17
17
  ## Set up search
18
18
 
19
- DuckDuckGo works out of the box with no key. You can also enable DeepSeek Search, Exa, Brave, Bocha, Serper, Firecrawl, Tavily, Perplexity, or You.com by entering their API keys. Drag the list to set the order; each search uses the first ready service. Search services added by other plugins, such as Zhihu, appear in the same list. New services are not enabled automatically.
19
+ DuckDuckGo works out of the box, no key needed. To use DeepSeek Search, Exa, Brave, Bocha, Serper, Firecrawl, Tavily, Perplexity, or You.com, enter its API key. Drag the list to reorder: each search uses the first ready service. Search services from other plugins, such as Zhihu, appear in the same list. New services are not enabled automatically.
20
20
 
21
- The **Settings** tab sets the number of results, queries per call, the total timeout, and the maximum page length.
21
+ The **Settings** tab sets the number of results, queries per call, total timeout, and maximum page length.
22
22
 
23
23
  ## Read web pages
24
24
 
25
- Turn on **Read web pages**, enable the services you want to use, and drag them or use the up/down arrow keys to set their priority. Each fetch uses the first configured, available service. Fetch services connected by other plugins also appear in the list; new services need to be enabled manually. Services that are temporarily unloaded keep their selection and position.
25
+ Turn on **Read web pages**, enable the services you want, then drag them or use the up/down arrow keys to set priority. Each fetch uses the first configured, available service. Fetch services from other plugins also appear; new ones must be enabled manually. Temporarily unloaded services keep their selection and position.
26
26
 
27
- Your previously selected fetch service is kept as the first entry. The bundled **Read web pages** service uses the free Jina Reader, falling back to one direct request if that fails. Services supplied by the host or other plugins use their own fetching method.
27
+ Your previously selected fetch service stays first. The bundled **Read web pages** service uses the free Jina Reader and falls back to one direct request if that fails; services from the host or other plugins use their own fetching method.
28
28
 
29
- You can also enable **Firecrawl** to read Markdown pages with the same API key as Firecrawl search. Search and page reads are billed separately. Newly added services stay outside your priority lists until you enable them. Perplexity uses its Search API without generating answers; You.com returns web and news snippets without requesting extra full-page extraction.
29
+ **Firecrawl** can also read Markdown pages with the same API key as Firecrawl search; search and page reads are billed separately. Newly added services stay outside your priority lists until you enable them. Perplexity uses its Search API without generating answers; You.com returns web and news snippets without extra full-page extraction.
30
30
 
31
31
  ## Give the tools to the model
32
32
 
33
- Add this to the plugin list of the `agent.cordis.yml` you use:
33
+ Add this to the plugin list of your `agent.cordis.yml`:
34
34
 
35
35
  ```yaml
36
36
  - name: '@klarkxy/dsh-web-search-manager/tools'
37
37
  ```
38
38
 
39
- The model then gets `web_search` and `web_fetch` while search and fetch are turned on.
39
+ The model then gets `web_search` and `web_fetch` while search and fetch are on.
40
40
 
41
41
  ## Limits
42
42
 
43
- - Paid services may charge for search or fetch requests; the plugin's counts are not a bill.
44
- - When using the bundled Jina Reader, page addresses are sent to Jina; do not fetch URLs containing private tokens.
45
- - Firecrawl page reads require a page status code and final URL in the service response; missing metadata produces an error. Its page content may come from the service's cache. URLs are sent to the selected fetch service.
46
- - Failed search or fetch requests do not switch to another service in the list automatically.
47
- - Cancellation and timeouts ask the active service to stop and wait for it to finish cleaning up. A third-party service that ignores cancellation can delay the result and plugin unloading; the plugin cannot forcibly stop code running inside DSH.
43
+ - Paid services may charge per search or fetch; the plugin's counts are not a bill.
44
+ - The bundled Jina Reader sends page addresses to Jina; do not fetch URLs with private tokens.
45
+ - Firecrawl page reads require a page status code and final URL in the response; missing metadata errors out. Its page content may come from the service's cache. URLs go to the selected fetch service.
46
+ - Failed search or fetch requests do not switch to another service automatically.
47
+ - Cancellation and timeouts ask the active service to stop and wait for it to finish cleaning up. A third-party service that ignores cancellation can delay results and plugin unloading; the plugin cannot forcibly stop code running inside DSH.
48
48
 
49
49
  ## License
50
50
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  [English](../README.md)
6
6
 
7
- 在一个设置页里选择由哪个服务回答模型的网络搜索、设置请求限制,并让模型读取公开网页。搜索服务、网页读取和请求限制各占一个页签。
7
+ 在一个设置页里选择由哪个服务回答模型的网络搜索、设置请求限制,并让模型读取公开网页。搜索服务、网页读取、请求限制各占一个页签。
8
8
 
9
9
  ## 安装
10
10
 
@@ -12,21 +12,21 @@
12
12
  dsh plugin --profile web add @klarkxy/dsh-web-search-manager
13
13
  ```
14
14
 
15
- 把 `web` 换成你的 profile 名,重启 DSH Web 后打开「插件 → 网页搜索」。更新插件后也需要重启 DSH;仅刷新页面不会加载新版搜索代码。
15
+ 把 `web` 换成你的 profile 名。重启 DSH Web 后打开「插件 → 网页搜索」。更新插件后也要重启;仅刷新页面不会加载新版搜索代码。
16
16
 
17
17
  ## 配置搜索
18
18
 
19
- DuckDuckGo 默认可用,无需密钥。也可以填写 API Key 启用 DeepSeek 搜索、Exa、Brave、博查、Serper、Firecrawl、Tavily、Perplexity 或 You.com。拖拽列表调整顺序,每次搜索使用第一个就绪的服务。其他插件提供的搜索服务(例如知乎)也会出现在同一列表中。新服务不会自动开启。
19
+ DuckDuckGo 默认可用,无需密钥。要用 DeepSeek 搜索、Exa、Brave、博查、Serper、Firecrawl、Tavily、Perplexity 或 You.com,填写对应 API Key。拖拽列表调整顺序:每次搜索使用第一个就绪的服务。其他插件提供的搜索服务(例如知乎)也在同一列表中。新服务不会自动开启。
20
20
 
21
- 「设置」页签用于设置结果条数、每次调用的查询数、总超时和网页正文长度上限。
21
+ 「设置」页签设置结果条数、每次调用的查询数、总超时和网页正文长度上限。
22
22
 
23
23
  ## 读取网页
24
24
 
25
- 打开「网页读取」后,选择参与读取的服务,拖拽或用上下方向键调整优先顺序。每次读取使用第一个已配置且可用的服务;其他插件接入的网页读取服务也会出现在列表中,新服务需要手动开启。暂时未加载的服务会保留原有选择和顺序。
25
+ 打开「网页读取」,选择参与的服务,拖拽或用上下方向键调整优先级。每次读取使用第一个已配置且可用的服务。其他插件接入的读取服务也会出现在列表中,新服务需手动开启;暂时未加载的服务保留原有选择和顺序。
26
26
 
27
- 原先选中的读取服务会保留为列表中的首项。内置「网页读取」通过免费的 Jina Reader 读取,失败时改为直接请求一次;如果使用宿主或其他插件的服务,则按该服务的方式读取。
27
+ 原先选中的读取服务保留在首位。内置「网页读取」走免费的 Jina Reader,失败时改为直接请求一次;宿主或其他插件的服务按各自方式读取。
28
28
 
29
- 也可以开启 **Firecrawl** 读取 Markdown 网页,与 Firecrawl 搜索共用 API Key;搜索与网页读取分别计费。新增服务需要手动加入优先列表。Perplexity 使用 Search API,不生成回答;You.com 返回网页与新闻摘要,不额外请求全文提取。
29
+ 也可开启 **Firecrawl** 读取 Markdown 网页,与 Firecrawl 搜索共用 API Key;搜索与网页读取分别计费。新增服务需手动加入优先列表。Perplexity 使用 Search API,不生成回答;You.com 返回网页与新闻摘要,不额外请求全文提取。
30
30
 
31
31
  ## 让模型使用
32
32
 
@@ -36,15 +36,15 @@ DuckDuckGo 默认可用,无需密钥。也可以填写 API Key 启用 DeepSeek
36
36
  - name: '@klarkxy/dsh-web-search-manager/tools'
37
37
  ```
38
38
 
39
- 搜索和网页读取开启时,模型会获得 `web_search` 与 `web_fetch` 工具。
39
+ 搜索和网页读取开启时,模型获得 `web_search` 与 `web_fetch` 工具。
40
40
 
41
41
  ## 限制
42
42
 
43
43
  - 付费服务可能按次收费;插件的计数不是账单。
44
- - 使用内置 Jina Reader 时,网址会发送给 Jina,请勿读取包含私密令牌的链接。
45
- - Firecrawl 网页读取要求服务返回网页状态码和最终网址,缺失时会报错;正文可能来自服务缓存。读取的网址会发送给所选服务。
46
- - 搜索或网页读取请求失败时,不会自动换用列表中的其他服务。
47
- - 取消与超时会请求当前服务停止,并等待其完成清理。忽略取消的第三方服务可能延迟结果返回和插件卸载;插件无法强制终止 DSH 进程内运行的代码。
44
+ - 内置 Jina Reader 会把网址发送给 Jina,请勿读取含私密令牌的链接。
45
+ - Firecrawl 网页读取要求服务返回网页状态码和最终网址,缺失时报错;正文可能来自服务缓存。网址会发送给所选服务。
46
+ - 搜索或网页读取失败时,不会自动换用列表中的其他服务。
47
+ - 取消与超时会请求当前服务停止并等待其完成清理。忽略取消的第三方服务可能延迟结果返回和插件卸载;插件无法强制终止 DSH 进程内运行的代码。
48
48
 
49
49
  ## 许可证
50
50
 
@@ -219,6 +219,17 @@ ${scope} {
219
219
  /* --- Layout scaffolding. --- */
220
220
  & .dsh-ui-stack { display: flex; flex-direction: column; gap: 12px; }
221
221
  & .dsh-ui-stack-lg { display: flex; flex-direction: column; gap: 20px; }
222
+ /* Plugin detail pages share one reading column. These are layout recipes;
223
+ * controls keep their own primitive styles. Form spacing follows the host's
224
+ * settings-form/fields.module.css and SettingsForm.module.css. */
225
+ & .dsh-ui-page { display: flex; flex-direction: column; gap: 16px; width: 100%; max-width: 760px; min-width: 0; }
226
+ & .dsh-ui-form { display: flex; flex-direction: column; gap: 0; min-width: 0; }
227
+ &.dsh-ui-form > .dsh-ui-field, & .dsh-ui-form > .dsh-ui-field { padding: 12px 0; }
228
+ & .dsh-ui-form-footer { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; padding-top: 16px; min-width: 0; }
229
+ & .dsh-ui-fields-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 20px; min-width: 0; margin: 0; padding: 0; border: 0; }
230
+ @media (max-width: 560px) {
231
+ & .dsh-ui-fields-grid { grid-template-columns: minmax(0, 1fr); }
232
+ }
222
233
  & .dsh-ui-row { display: flex; align-items: center; gap: 8px; min-width: 0; }
223
234
  & .dsh-ui-row-wrap { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
224
235
  & .dsh-ui-spacer { flex: 1; min-width: 0; }
@@ -246,6 +257,9 @@ ${scope} {
246
257
  * official primitive, so this only owns the vertical rhythm. --- */
247
258
  & .dsh-ui-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
248
259
  & .dsh-ui-field + .dsh-ui-field { border-top: 0.5px solid var(--dsw-alias-border-l2); padding-top: 12px; }
260
+ /* Grid fields share a row, so the stacked-field divider must not win later in
261
+ * the cascade. Reset it here, after the field recipe. */
262
+ & .dsh-ui-fields-grid > .dsh-ui-field { padding: 0; border: 0; }
249
263
  & .dsh-ui-label { display: flex; align-items: center; gap: 6px; font-size: 13px; line-height: 20px; font-weight: 500; color: var(--dsw-alias-label-primary); }
250
264
  & .dsh-ui-label-row { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
251
265
  & .dsh-ui-help { font-size: 12px; line-height: 1.6; color: var(--dsw-alias-label-secondary); }
@@ -436,9 +450,87 @@ ${scope} {
436
450
  & *, & *::before, & *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
437
451
  }
438
452
  }
439
- `.replace(/^& (\.dsh-ui-[a-z0-9-]+)(?![+>~])/gm, (_match, className) => `&${className}, & ${className}`);
453
+ `.replace(/^& (\.dsh-ui-[a-z0-9-]+)(?=\s*\{)/gm, (_match, className) => `&${className}, & ${className}`);
440
454
  }
441
455
  //#endregion
456
+ //#region src/client-styles.ts
457
+ /**
458
+ * The shared contract paints this page: the type tiers, the card, the field,
459
+ * the button row, the notice, the focus ring and the state colours. What is
460
+ * left here is only the geometry the contract does not cover — the reading
461
+ * width of the panel, the ranked three-column row, the drag affordance, the
462
+ * drop indicator, the screen-reader-only announcer and the limits grid.
463
+ *
464
+ * Nothing below declares a colour of its own beyond naming a host token, and
465
+ * nothing carries a literal fallback: the host publishes both themes and a
466
+ * literal would pin one of them.
467
+ *
468
+ * Every plugin rule nests under the plugin root, as the contract does, so a
469
+ * .ws-* or .web-search-* class never paints a neighbouring plugin. Selectors
470
+ * that start with an element name keep an explicit `&`, the form every
471
+ * nesting implementation accepts.
472
+ */
473
+ const webSearchClientStyles = `${officialUiCss("ws-root")}
474
+ .ws-root {
475
+
476
+ .ws-card-head { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding-bottom: 8px; }
477
+ .ws-sr-only {
478
+ position: absolute;
479
+ width: 1px;
480
+ height: 1px;
481
+ padding: 0;
482
+ margin: -1px;
483
+ overflow: hidden;
484
+ clip-path: inset(50%);
485
+ white-space: nowrap;
486
+ border: 0;
487
+ }
488
+ .ws-provider-note { display: flex; align-items: center; gap: 6px; }
489
+ .web-search-provider { display: grid; gap: 8px; min-width: 0; }
490
+ .web-search-provider-info { display: grid; gap: 3px; font-size: 12px; line-height: 18px; color: var(--dsw-alias-label-secondary); overflow-wrap: anywhere; }
491
+ .web-search-provider-heading { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; min-height: 24px; }
492
+ & a { color: var(--dsw-alias-state-business-primary); font-size: 12px; line-height: 18px; text-underline-offset: 3px; }
493
+ /* The primitive renders a 32px control in an inline-flex wrapper; a settings
494
+ * field needs it to fill the column it sits in. */
495
+ .ws-input { width: 100%; }
496
+ .web-search-rank { margin: 0; padding: 0; list-style: none; }
497
+ .web-search-rank li {
498
+ position: relative;
499
+ display: grid;
500
+ grid-template-columns: 24px minmax(0, 1fr) auto;
501
+ gap: 10px;
502
+ align-items: start;
503
+ padding: 14px 0;
504
+ border-top: 0.5px solid var(--dsw-alias-border-l2);
505
+ }
506
+ & button.web-search-drag-handle {
507
+ display: grid;
508
+ place-items: center;
509
+ width: 24px;
510
+ height: 24px;
511
+ min-height: 24px;
512
+ padding: 0;
513
+ cursor: grab;
514
+ opacity: 0.65;
515
+ }
516
+ & button.web-search-drag-handle:hover { opacity: 1; }
517
+ & button.web-search-drag-handle:active { cursor: grabbing; }
518
+ & button.web-search-drag-handle[aria-disabled='true'] { cursor: wait; opacity: 0.4; }
519
+ .web-search-rank li[data-dragging] { opacity: 0.45; }
520
+ .web-search-rank li[data-drop]::after { content: ''; position: absolute; left: 0; right: 0; height: 2px; background: var(--dsw-alias-state-business-primary); pointer-events: none; }
521
+ .web-search-rank li[data-drop='before']::after { top: -1px; }
522
+ .web-search-rank li[data-drop='after']::after { bottom: -1px; }
523
+ .web-search-rank-index { color: var(--dsw-alias-label-tertiary); font-variant-numeric: tabular-nums; line-height: 24px; }
524
+ .web-search-rank-move { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; justify-content: flex-end; padding-top: 2px; }
525
+ .ws-danger:hover:not(:disabled),
526
+ .ws-danger[data-armed] { color: var(--dsw-alias-state-error-primary); border-color: var(--dsw-alias-state-error-primary); }
527
+ .ws-danger:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover-danger); }
528
+ @media (max-width: 560px) {
529
+ .web-search-rank-move { flex-direction: column; align-items: flex-end; }
530
+ }
531
+ }
532
+ `;
533
+ //#endregion
442
534
  //#region ../dsh-plugin-kit/src/client-utils.ts
443
535
  /** Fixed pause for failing background reads; manual work is never replayed. */
444
536
  function createPluginReadGate(now = Date.now) {
@@ -1286,7 +1378,7 @@ function NetworkSearchSettings({ client, hidden }) {
1286
1378
  setNote({ key: "keyDeleted" });
1287
1379
  }
1288
1380
  if (!status || !limitDraft) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1289
- className: "web-search-settings ws-root dsh-ui-panel",
1381
+ className: "web-search-settings ws-root dsh-ui-page",
1290
1382
  children: [loadError ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
1291
1383
  role: "alert",
1292
1384
  className: "dsh-ui-error",
@@ -1380,7 +1472,7 @@ function NetworkSearchSettings({ client, hidden }) {
1380
1472
  });
1381
1473
  }
1382
1474
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1383
- className: "web-search-settings ws-root dsh-ui-panel",
1475
+ className: "web-search-settings ws-root dsh-ui-page",
1384
1476
  "data-testid": "web-search-settings",
1385
1477
  children: [
1386
1478
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedTabs, {
@@ -1741,11 +1833,11 @@ function NetworkSearchSettings({ client, hidden }) {
1741
1833
  hidden: tab !== "limits",
1742
1834
  tabIndex: 0,
1743
1835
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("fieldset", {
1744
- className: "web-search-limits",
1836
+ className: "dsh-ui-fields-grid",
1745
1837
  disabled: busy || reordering,
1746
1838
  "aria-label": text.tabLimits,
1747
1839
  children: LIMITS.map(([key, min, max]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1748
- className: "ws-limit",
1840
+ className: "dsh-ui-field",
1749
1841
  children: [
1750
1842
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1751
1843
  className: "dsh-ui-label",
@@ -1784,7 +1876,7 @@ function NetworkSearchSettings({ client, hidden }) {
1784
1876
  })
1785
1877
  }),
1786
1878
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1787
- className: "dsh-ui-actions dsh-ui-actions-end",
1879
+ className: "dsh-ui-form-footer dsh-ui-actions-end",
1788
1880
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1789
1881
  className: "dsh-ui-meta",
1790
1882
  children: limitsDirty || keysDirty ? text.unsaved : text.saveHint
@@ -1799,74 +1891,13 @@ function NetworkSearchSettings({ client, hidden }) {
1799
1891
  ]
1800
1892
  });
1801
1893
  }
1802
- const styles = `${officialUiCss("ws-root")}
1803
- .ws-root { max-width: 760px; }
1804
- .ws-card-head { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding-bottom: 8px; }
1805
- .ws-sr-only {
1806
- position: absolute;
1807
- width: 1px;
1808
- height: 1px;
1809
- padding: 0;
1810
- margin: -1px;
1811
- overflow: hidden;
1812
- clip-path: inset(50%);
1813
- white-space: nowrap;
1814
- border: 0;
1815
- }
1816
- .ws-provider-note { display: flex; align-items: center; gap: 6px; }
1817
- .web-search-provider { display: grid; gap: 8px; min-width: 0; }
1818
- .web-search-provider-info { display: grid; gap: 3px; font-size: 12px; line-height: 18px; color: var(--dsw-alias-label-secondary); overflow-wrap: anywhere; }
1819
- .web-search-provider-heading { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; min-height: 24px; }
1820
- .web-search-settings a { color: var(--dsw-alias-state-business-primary); font-size: 12px; line-height: 18px; text-underline-offset: 3px; }
1821
- /* The primitive renders a 32px control in an inline-flex wrapper; a settings
1822
- * field needs it to fill the column it sits in. */
1823
- .ws-input { width: 100%; }
1824
- .web-search-rank { margin: 0; padding: 0; list-style: none; }
1825
- .web-search-rank li {
1826
- position: relative;
1827
- display: grid;
1828
- grid-template-columns: 24px minmax(0, 1fr) auto;
1829
- gap: 10px;
1830
- align-items: start;
1831
- padding: 14px 0;
1832
- border-top: 0.5px solid var(--dsw-alias-border-l2);
1833
- }
1834
- .web-search-settings button.web-search-drag-handle {
1835
- display: grid;
1836
- place-items: center;
1837
- width: 24px;
1838
- height: 24px;
1839
- min-height: 24px;
1840
- padding: 0;
1841
- cursor: grab;
1842
- opacity: 0.65;
1843
- }
1844
- .web-search-settings button.web-search-drag-handle:hover { opacity: 1; }
1845
- .web-search-settings button.web-search-drag-handle:active { cursor: grabbing; }
1846
- .web-search-settings button.web-search-drag-handle[aria-disabled='true'] { cursor: wait; opacity: 0.4; }
1847
- .web-search-rank li[data-dragging] { opacity: 0.45; }
1848
- .web-search-rank li[data-drop]::after { content: ''; position: absolute; left: 0; right: 0; height: 2px; background: var(--dsw-alias-state-business-primary); pointer-events: none; }
1849
- .web-search-rank li[data-drop='before']::after { top: -1px; }
1850
- .web-search-rank li[data-drop='after']::after { bottom: -1px; }
1851
- .web-search-rank-index { color: var(--dsw-alias-label-tertiary); font-variant-numeric: tabular-nums; line-height: 24px; }
1852
- .web-search-rank-move { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; justify-content: flex-end; padding-top: 2px; }
1853
- .web-search-limits { margin: 0; border: 0; padding: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 20px; }
1854
- /* A two-column grid, so these fields keep the contract's caption and the
1855
- * primitive's control and only own their vertical rhythm — a stacked list is
1856
- * what draws the hairline between fields, and there is nothing to separate. */
1857
- .ws-limit { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
1858
- .ws-danger:hover:not(:disabled),
1859
- .ws-danger[data-armed] { color: var(--dsw-alias-state-error-primary); border-color: var(--dsw-alias-state-error-primary); }
1860
- .ws-danger:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover-danger); }
1861
- @media (max-width: 560px) { .web-search-rank-move { flex-direction: column; align-items: flex-end; } .web-search-limits { grid-template-columns: minmax(0, 1fr); } }
1862
- `;
1863
1894
  function apply(ctx) {
1864
1895
  const client = ctx;
1865
1896
  ctx.effect(() => {
1866
1897
  if (typeof document === "undefined") return () => {};
1867
1898
  const style = document.createElement("style");
1868
1899
  style.setAttribute("data-plugin", "@klarkxy/dsh-web-search-manager");
1869
- style.textContent = styles;
1900
+ style.textContent = webSearchClientStyles;
1870
1901
  style.dataset.dshWebSearch = "";
1871
1902
  document.head.appendChild(style);
1872
1903
  return () => style.remove();