@klarkxy/dsh-web-search-manager 0.3.1 → 0.3.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.
- package/README.md +9 -9
- package/docs/README.zh-CN.md +9 -9
- package/lib/client.inner.cjs +25 -4
- package/lib/client.inner.cjs.map +1 -1
- package/lib/client.js +25 -4
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +16 -5
package/README.md
CHANGED
|
@@ -12,21 +12,21 @@ Pick which service answers the model's web searches, set request limits, and let
|
|
|
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, then open **
|
|
15
|
+
Replace `web` with your profile name. Restart DSH Web, then open **Settings → Web Search**. Restart the profile after plugin updates as well so the new search code loads.
|
|
16
16
|
|
|
17
17
|
## Set up search
|
|
18
18
|
|
|
19
|
-
DuckDuckGo works out of the box
|
|
19
|
+
DuckDuckGo works out of the box with zero configuration. 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 and join the priority order once you enable them.
|
|
20
20
|
|
|
21
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, 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
|
|
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 and join the priority order once enabled; temporarily unloaded services keep their selection and position.
|
|
26
26
|
|
|
27
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
|
-
**Firecrawl** can also read Markdown pages with the same API key as Firecrawl search; search and page reads are billed separately.
|
|
29
|
+
**Firecrawl** can also read Markdown pages with the same API key as Firecrawl search; search and page reads are billed separately. Perplexity uses its Search API to return search results directly; You.com returns web and news snippets directly.
|
|
30
30
|
|
|
31
31
|
## Give the tools to the model
|
|
32
32
|
|
|
@@ -40,11 +40,11 @@ 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 per search or fetch;
|
|
44
|
-
- The bundled Jina Reader sends page addresses to Jina;
|
|
45
|
-
- Firecrawl page reads require a page status code and final URL in the response
|
|
46
|
-
-
|
|
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
|
|
43
|
+
- Paid services may charge per search or fetch; refer to your provider account for billing.
|
|
44
|
+
- The bundled Jina Reader sends page addresses to Jina; use it only for public URLs free of private tokens.
|
|
45
|
+
- Firecrawl page reads require a page status code and final URL in the response, and may serve cached page content. URLs are sent to the selected fetch service.
|
|
46
|
+
- Each search or fetch request runs against the selected active service only.
|
|
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, as code running inside DSH finishes cooperatively.
|
|
48
48
|
|
|
49
49
|
## License
|
|
50
50
|
|
package/docs/README.zh-CN.md
CHANGED
|
@@ -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
|
|
15
|
+
把 `web` 换成你的 profile 名。重启 DSH Web 后打开「设置 → 网页搜索」。更新插件后同样重启 profile,以加载新版搜索代码。
|
|
16
16
|
|
|
17
17
|
## 配置搜索
|
|
18
18
|
|
|
19
|
-
DuckDuckGo
|
|
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
27
|
原先选中的读取服务保留在首位。内置「网页读取」走免费的 Jina Reader,失败时改为直接请求一次;宿主或其他插件的服务按各自方式读取。
|
|
28
28
|
|
|
29
|
-
也可开启 **Firecrawl** 读取 Markdown 网页,与 Firecrawl 搜索共用 API Key
|
|
29
|
+
也可开启 **Firecrawl** 读取 Markdown 网页,与 Firecrawl 搜索共用 API Key;搜索与网页读取分别计费。Perplexity 使用 Search API 直接返回搜索结果;You.com 直接返回网页与新闻摘要。
|
|
30
30
|
|
|
31
31
|
## 让模型使用
|
|
32
32
|
|
|
@@ -40,11 +40,11 @@ DuckDuckGo 默认可用,无需密钥。要用 DeepSeek 搜索、Exa、Brave、
|
|
|
40
40
|
|
|
41
41
|
## 限制
|
|
42
42
|
|
|
43
|
-
-
|
|
44
|
-
- 内置 Jina Reader 会把网址发送给 Jina
|
|
45
|
-
- Firecrawl
|
|
46
|
-
-
|
|
47
|
-
-
|
|
43
|
+
- 付费服务可能按次收费,实际账单以服务商账户为准。
|
|
44
|
+
- 内置 Jina Reader 会把网址发送给 Jina,请仅用于不含私密令牌的公开链接。
|
|
45
|
+
- Firecrawl 网页读取要求服务返回网页状态码和最终网址,正文可能来自服务缓存。网址会发送给所选服务。
|
|
46
|
+
- 每次搜索或网页读取仅请求当前排在首位的就绪服务。
|
|
47
|
+
- 取消与超时会请求当前服务停止并等待其完成清理。忽略取消的第三方服务可能延迟结果返回和插件卸载,DSH 进程内的代码通过协作方式停止。
|
|
48
48
|
|
|
49
49
|
## 许可证
|
|
50
50
|
|
package/lib/client.inner.cjs
CHANGED
|
@@ -531,6 +531,21 @@ const webSearchClientStyles = `${officialUiCss("ws-root")}
|
|
|
531
531
|
}
|
|
532
532
|
`;
|
|
533
533
|
//#endregion
|
|
534
|
+
//#region ../dsh-plugin-kit/src/contracts.ts
|
|
535
|
+
/** `@klarkxy/dsh-git-commit` becomes `KlarkxyDshGitCommitModelSettingsChanges`. */
|
|
536
|
+
function modelSettingsChangesIdentity(packageName) {
|
|
537
|
+
const scoped = /^@([^/]+)\/(.+)$/.exec(packageName);
|
|
538
|
+
const parts = (scoped ? `${scoped[1]}/${scoped[2]}` : packageName).split(/[^A-Za-z0-9]+/).filter(Boolean);
|
|
539
|
+
if (!parts.length) throw new Error("Model settings package name has no RPC segment");
|
|
540
|
+
const segment = `${parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("")}ModelSettingsChanges`;
|
|
541
|
+
if (!/^[A-Z][A-Za-z0-9]*$/.test(segment)) throw new Error(`Model settings sync name is not an RPC segment: ${segment}`);
|
|
542
|
+
return {
|
|
543
|
+
service: segment,
|
|
544
|
+
namespace: segment
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
modelSettingsChangesIdentity("@klarkxy/dsh-plugin-kit");
|
|
548
|
+
//#endregion
|
|
534
549
|
//#region ../dsh-plugin-kit/src/client-utils.ts
|
|
535
550
|
/** Fixed pause for failing background reads; manual work is never replayed. */
|
|
536
551
|
function createPluginReadGate(now = Date.now) {
|
|
@@ -1902,13 +1917,19 @@ function apply(ctx) {
|
|
|
1902
1917
|
document.head.appendChild(style);
|
|
1903
1918
|
return () => style.remove();
|
|
1904
1919
|
}, "web-search-manager.styles");
|
|
1905
|
-
ctx.effect(() => client.slots.inject("
|
|
1906
|
-
name: "
|
|
1907
|
-
|
|
1920
|
+
ctx.effect(() => client.slots.inject("settings.section", () => [client.slots.register({
|
|
1921
|
+
name: "settings.section",
|
|
1922
|
+
id: "web-search-manager",
|
|
1923
|
+
order: 150,
|
|
1924
|
+
label: () => (client.locale?.getSnapshot().active ?? "zh").startsWith("zh") ? "网页搜索" : "Web Search"
|
|
1908
1925
|
}, (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NetworkSearchSettings, {
|
|
1909
1926
|
client,
|
|
1910
1927
|
hidden: props?.hidden
|
|
1911
|
-
}))
|
|
1928
|
+
})), client.slots.inject("settings.section.icon", () => client.slots.register({
|
|
1929
|
+
name: "settings.section.icon",
|
|
1930
|
+
id: "web-search-manager",
|
|
1931
|
+
order: 150
|
|
1932
|
+
}, _deepseek_ai_dsh_client_ui_primitives.IconGlobeOutlineRegular))]), "web-search-manager.settings");
|
|
1912
1933
|
}
|
|
1913
1934
|
//#endregion
|
|
1914
1935
|
exports.NetworkSearchSettings = NetworkSearchSettings;
|
package/lib/client.inner.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.inner.cjs","names":["useState","useRef","Button","useCallback","useSyncExternalStore","useId","SegmentedTabs","Switch","StateDot","Tag","Input"],"sources":["../src/client-error.ts","../../dsh-plugin-kit/src/official-ui.ts","../src/client-styles.ts","../../dsh-plugin-kit/src/client-utils.ts","../src/contracts.ts","../src/provider-info.ts","../src/client.tsx"],"sourcesContent":["/** Local adapter diagnostics only; third-party descriptions remain verbatim. */\nconst localErrors: Record<string, string> = {\n '网络搜索设置格式无效。': 'Invalid web search settings format.',\n '未知操作。': 'Unknown operation.',\n '网络设置操作失败,请刷新后重试。': 'The network settings operation failed. Refresh and try again.',\n '查询须为 1 至 4000 字符。': 'The query must contain 1 to 4,000 characters.',\n '网络搜索插件已停止。': 'The web search plugin has stopped.',\n '请在设置 → 网络搜索中配置凭据并启用。': 'Configure credentials and enable web search in Settings → Web search.',\n '网络访问设置已改变,请重新发起请求。': 'Network access settings changed. Start the request again.',\n '网络请求已取消。': 'The network request was cancelled.',\n '网络请求已取消或超时。': 'The network request was cancelled or timed out.',\n '设置已被其他页面修改,请刷新后重试。': 'Settings changed in another page. Refresh and try again.',\n '供应商不支持自定义地址。': 'The provider does not support a custom address.',\n '供应商地址须为无凭据、无查询参数的 HTTPS 地址。': 'The provider address must use HTTPS without credentials or query parameters.',\n '需选择已安装的供应商并填写有效凭据。': 'Choose an installed provider and enter valid credentials.',\n '保存失败,本次运行已暂停网络访问以防意外联网。': 'Saving failed. Network access is paused for this run to prevent unintended requests.',\n '供应商未提供错误描述。': 'The provider did not supply an error description.',\n '供应商请求失败。': 'Provider request failed.',\n '搜索凭据已删除或不可用,请重新配置。': 'Search credentials were deleted or are unavailable. Configure them again.',\n '供应商配置不可用。': 'Provider configuration is unavailable.',\n '查询无效。': 'Invalid query.',\n '结果数须为 1 至 20。': 'The result count must be from 1 to 20.',\n '搜索已取消。': 'Search cancelled.',\n 'Tavily 搜索已取消。': 'Tavily search cancelled.',\n '响应格式无效。': 'Invalid response shape',\n '响应缺失。': 'Missing response',\n '响应过大。': 'Response too large',\n '地址无效。': 'Invalid endpoint',\n}\n\nexport function localErrorText(message: string, locale: 'zh' | 'en'): string {\n const prefix = message.match(/^(\\[(?:search|fetch):[^\\]]+\\] )(.*)$/s)\n if (prefix) return prefix[1] + localErrorText(prefix[2], locale)\n if (locale === 'zh') {\n const known = Object.entries(localErrors).find(([, english]) => english === message)\n return known?.[0] ?? message\n }\n if (localErrors[message]) return localErrors[message]\n const http = message.match(/^供应商请求失败(HTTP (\\d{3}))。$/)\n if (http) return `Provider request failed (HTTP ${http[1]}).`\n const config = message.match(/^(brave|bocha|firecrawl|serper|Tavily) 配置不可用。$/)\n if (config) return `${config[1]} configuration is unavailable.`\n return message\n}\n","/**\n * Shared contract that keeps every plugin's browser-side UI on the official\n * DeepSeek Harness Web design language.\n *\n * Two rules produce that language, and both are load-bearing here:\n *\n * 1. Colour, radius, elevation and focus come from the host theme's real\n * `--dsw-*` tokens, written under their own names. Plugins never invent a\n * token, never alias the official ones behind a private palette (a private\n * alias is just a second, diverging theme), and never carries a literal\n * fallback — the host publishes light and dark, and a literal would pin one\n * of them. The only non-colour custom properties used are the host's own\n * content-font axis and frame clearance.\n *\n * 2. Anything the host already ships is used, not reimplemented. Controls come\n * from `@deepseek-ai/dsh-client-ui-primitives` (`Button`, `Input`,\n * `Checkbox`, `Switch`, `Tag`, `Pill`, `SegmentedControl`, `Menu`, `Modal`,\n * `Tooltip`, `Toast`, `DisclosureRow`, `StateDot`, `PathLabel`, the settings\n * form suite). This module only styles what primitives do not cover: page\n * scaffolding, field layout, banners, empty states and scroll regions.\n *\n * The metrics below are transcribed from the primitives' own CSS modules so a\n * feature panel lines up pixel-for-pixel with a native one: the 14/22 body and\n * 13/20 compact type tiers, the 0.5px hairline scale, and the elevation family\n * that draws a surface's edge in its shadow (`border: 0`) instead of a border.\n */\n\n/**\n * Every `--dsw-*` token this repo is allowed to reference, grouped by role.\n *\n * Plugins may only colour through this list. It is exported so the guard test\n * can fail a build when a stylesheet reaches for anything else — the failure\n * mode this prevents is silent: an unknown token resolves to nothing, so a\n * border disappears or a label inherits the wrong colour with no error anywhere.\n */\nexport const OFFICIAL_THEME_TOKENS = {\n /** Surfaces, from the page up to the topmost nested card. */\n surface: [\n '--dsw-alias-bg-base',\n '--dsw-alias-bg-layer-1',\n '--dsw-alias-bg-layer-2',\n '--dsw-alias-bg-layer-3',\n '--dsw-alias-bg-layer-4',\n '--dsw-alias-bg-module-platform',\n '--dsw-alias-bg-overlay',\n ],\n /** Hairlines. l1 is near-invisible on a menu surface, so menus use l2+. */\n border: ['--dsw-alias-border-l1', '--dsw-alias-border-l2', '--dsw-alias-border-l3', '--dsw-alias-border-l4'],\n /** Text. `caption` is menu shortcut text, `dimmed` is placeholder text. */\n label: [\n '--dsw-alias-label-primary',\n '--dsw-alias-label-secondary',\n '--dsw-alias-label-tertiary',\n '--dsw-alias-label-caption',\n '--dsw-alias-label-dimmed',\n '--dsw-alias-label-error',\n '--dsw-alias-label-primary-foreground',\n ],\n /** Interactive fills. The `-danger` hover belongs to destructive rows. */\n interactive: [\n '--dsw-alias-interactive-bg-hover',\n '--dsw-alias-interactive-bg-active',\n '--dsw-alias-interactive-bg-hover-danger',\n ],\n /** Accent and status. `business` is the focus ring's default colour. */\n accent: [\n '--dsw-alias-brand-primary',\n '--dsw-alias-button-primary-fill',\n '--dsw-alias-button-primary-hover',\n '--dsw-alias-button-tool-bar-fill',\n '--dsw-alias-button-tool-bar-hover',\n '--dsw-alias-button-ghost-active-fill',\n '--dsw-alias-button-ghost-active-border',\n ],\n state: [\n '--dsw-alias-state-business-primary',\n '--dsw-alias-state-success-primary',\n '--dsw-alias-state-warn-primary',\n '--dsw-alias-state-error-primary',\n '--dsw-alias-state-idle-primary',\n ],\n /** Corner scale: sm 8, md 12, lg the large card, panel the modal. */\n radius: ['--dsw-radius-sm', '--dsw-radius-md', '--dsw-radius-lg', '--dsw-radius-panel'],\n /** An elevated surface draws its edge in the shadow, so it keeps border: 0. */\n elevation: [\n '--dsw-elevation-soft',\n '--dsw-elevation-stroke',\n '--dsw-elevation-stroke-color',\n '--dsw-elevation-panel',\n '--dsw-elevation-prominent',\n ],\n /** Floats, masks and scrollbars. */\n overlay: [\n '--dsw-menu-surface-fill',\n '--dsw-menu-backdrop-filter',\n '--dsw-mask-blur',\n '--dsw-alias-bg-mask-1',\n '--dsw-alias-menu-icon',\n '--dsw-alias-scrollbar-bg-l2',\n '--dsw-alias-scrollbar-hover-l2',\n '--dsw-shadow-lv3',\n ],\n /** Motion, focus and type axis, published by the host rather than by a component. */\n system: [\n '--dsw-focus-ring-width',\n '--dsw-focus-ring-color',\n '--dsw-font-markdown-code-font-family',\n '--ds-transition-duration',\n '--ds-ease-in-out',\n '--ds-font-family-code',\n '--dsh-scrollbar-thumb',\n '--dsh-scrollbar-thumb-hover',\n '--dsh-content-font-size-secondary',\n '--dsh-content-font-delta',\n '--dsh-frame-top-clearance',\n ],\n} as const\n\n/** Every official token name, flat. Used by the token guard test. */\nexport const OFFICIAL_THEME_TOKEN_NAMES: readonly string[] = Object.values(OFFICIAL_THEME_TOKENS).flat()\n\n/** The elevation steps a surface can be raised by, softest first. */\nexport type OfficialElevation = 'soft' | 'stroke' | 'panel' | 'prominent'\n\n/**\n * The host's focus ring shorthand, verbatim. Writing this per control is how\n * rings drift apart; a stylesheet that draws its own focus style should use\n * {@link officialFocus} instead. The colour fallback is the host's own default.\n */\nexport const OFFICIAL_FOCUS_RING =\n 'var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary))'\n\n/**\n * A complete `outline` declaration at the official geometry. `offset` defaults\n * to the 2px the native controls use; the host's inline field rules sit at 1px\n * and a segmented tab pulls the ring inside itself at -2px.\n */\nexport function officialFocus(offset = '2px'): string {\n return `outline: ${OFFICIAL_FOCUS_RING}; outline-offset: ${offset}`\n}\n\n/**\n * An elevated surface. The host draws a raised surface's edge inside its\n * shadow rather than as a border, so a card sets `border: 0` and takes the edge\n * from the elevation — which is also why its border stays off the alias-border\n * set that a flat, non-elevated rule would use.\n */\nexport function officialElevation(level: OfficialElevation): string {\n return `border: 0; box-shadow: var(--dsw-elevation-${level})`\n}\n\n/**\n * An elevated surface whose edge should read as a specific hairline. Rebinding\n * the stroke colour is how a menu card picks l1 while a panel picks l3, without\n * the two growing apart by hand.\n */\nexport function officialStrokedElevation(level: OfficialElevation, color: string): string {\n return `--dsw-elevation-stroke-color: ${color}; ${officialElevation(level)}`\n}\n\n/**\n * The shared stylesheet for a plugin UI, scoped under that plugin's root class.\n *\n * Scoping is what keeps this safe to always-bundle into every client: the rules\n * can only match inside the plugin's own subtree, so two plugins can both mount\n * `dsh-ui-card` without either styling the other's markup.\n *\n * Each recipe matches the root element *and* its descendants, because the\n * commonest arrangement is one element that is both — a surface that carries\n * the root class and `dsh-ui-card`, or a portaled dialog that is the root and\n * the `dsh-ui-surface`. A descendant-only scope would silently leave those\n * unstyled, with no error anywhere.\n *\n * A caveat worth knowing: `Modal` and `Menu` portal to `document.body`, so their\n * content leaves the plugin's subtree entirely. A portaled surface therefore\n * needs its own root class on an element inside the portal.\n *\n * Only patterns that primitives do not provide live here. If a control appears\n * below that `Button`/`Input`/`Switch`/`Tag`/`Menu` already covers, that is a\n * bug in this file: use the primitive and delete the recipe.\n */\nexport function officialUiCss(roots: string | readonly string[]): string {\n // One plugin often mounts several unrelated surfaces (a settings seat, a chat\n // card, a header menu), so the caller names every root. The rules below are\n // nested under a single selector list and use `&`, which the browser expands\n // per root — so one stylesheet covers all of them without duplicating itself.\n const scope = (typeof roots === 'string' ? [roots] : roots).map(root => `.${root}`).join(', ')\n const sheet = `\n/* Official DSH Web UI contract. Generated by @klarkxy/dsh-plugin-kit/official-ui.\n * Do not hand-edit the copy inside a plugin: change this module so every plugin\n * moves together. Scope: ${scope} */\n/* The scope rule below stays open for the whole sheet: every rule after it\n * nests with the parent selector, so one stylesheet can cover several unrelated\n * roots without leaking into a neighbouring plugin. Its closing brace is last. */\n${scope} {\n color: var(--dsw-alias-label-primary);\n font-family: inherit;\n font-size: 14px;\n line-height: 22px;\n min-width: 0;\n\n& *, & *::before, & *::after { box-sizing: border-box; }\n& h1, & h2, & h3, & h4, & p, & figure { margin: 0; }\n/* Keyboard focus only: pointer input keeps the native resting appearance. The\n * ring needs no radius of its own — the outline already follows the control's\n * own border-radius, and overriding it here would flatten every control. */\n& :focus-visible { ${officialFocus()}; }\n& [hidden] { display: none !important; }\n\n/* --- Type tiers. The same 14/22 and 13/20 steps the primitives use. --- */\n& .dsh-ui-title { font-size: 16px; line-height: 24px; font-weight: 500; color: var(--dsw-alias-label-primary); }\n& .dsh-ui-heading { font-size: 14px; line-height: 22px; font-weight: 500; color: var(--dsw-alias-label-primary); }\n& .dsh-ui-compact { font-size: 13px; line-height: 20px; color: var(--dsw-alias-label-primary); }\n& .dsh-ui-meta { font-size: 12px; line-height: 18px; color: var(--dsw-alias-label-secondary); }\n& .dsh-ui-hint { font-size: 12px; line-height: 18px; color: var(--dsw-alias-label-tertiary); }\n& .dsh-ui-error { font-size: 12px; line-height: 18px; color: var(--dsw-alias-state-error-primary); }\n& .dsh-ui-warn { color: var(--dsw-alias-state-warn-primary); }\n& .dsh-ui-success { color: var(--dsw-alias-state-success-primary); }\n& .dsh-ui-muted { color: var(--dsw-alias-label-tertiary); }\n& .dsh-ui-truncate { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n& .dsh-ui-wrap { min-width: 0; overflow-wrap: anywhere; }\n\n/* --- Layout scaffolding. --- */\n& .dsh-ui-stack { display: flex; flex-direction: column; gap: 12px; }\n& .dsh-ui-stack-lg { display: flex; flex-direction: column; gap: 20px; }\n/* Plugin detail pages share one reading column. These are layout recipes;\n * controls keep their own primitive styles. Form spacing follows the host's\n * settings-form/fields.module.css and SettingsForm.module.css. */\n& .dsh-ui-page { display: flex; flex-direction: column; gap: 16px; width: 100%; max-width: 760px; min-width: 0; }\n& .dsh-ui-form { display: flex; flex-direction: column; gap: 0; min-width: 0; }\n&.dsh-ui-form > .dsh-ui-field, & .dsh-ui-form > .dsh-ui-field { padding: 12px 0; }\n& .dsh-ui-form-footer { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; padding-top: 16px; min-width: 0; }\n& .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; }\n@media (max-width: 560px) {\n & .dsh-ui-fields-grid { grid-template-columns: minmax(0, 1fr); }\n}\n& .dsh-ui-row { display: flex; align-items: center; gap: 8px; min-width: 0; }\n& .dsh-ui-row-wrap { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }\n& .dsh-ui-spacer { flex: 1; min-width: 0; }\n/* A 0.5px l2 rule is the host's section divider; reserve the same rhythm. */\n& .dsh-ui-divider { height: 0.5px; background: var(--dsw-alias-border-l2); }\n& .dsh-ui-section { display: flex; flex-direction: column; gap: 12px; padding-top: 16px; border-top: 0.5px solid var(--dsw-alias-border-l2); }\n\n/* --- Card: a raised surface whose edge lives in the shadow. --- */\n& .dsh-ui-card {\n ${officialElevation('stroke')}\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 16px;\n border-radius: var(--dsw-radius-lg);\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n min-width: 0;\n}\n& .dsh-ui-card--flat { border: 0.5px solid var(--dsw-alias-border-l3); box-shadow: none; }\n& .dsh-ui-card--nested { background: var(--dsw-alias-bg-layer-2); }\n& .dsh-ui-card--plain { background: none; }\n\n/* --- Field: label, control, help, error. The control itself is an\n * official primitive, so this only owns the vertical rhythm. --- */\n& .dsh-ui-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }\n& .dsh-ui-field + .dsh-ui-field { border-top: 0.5px solid var(--dsw-alias-border-l2); padding-top: 12px; }\n/* Grid fields share a row, so the stacked-field divider must not win later in\n * the cascade. Reset it here, after the field recipe. */\n& .dsh-ui-fields-grid > .dsh-ui-field { padding: 0; border: 0; }\n& .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); }\n& .dsh-ui-label-row { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }\n& .dsh-ui-help { font-size: 12px; line-height: 1.6; color: var(--dsw-alias-label-secondary); }\n& .dsh-ui-help p { margin: 0; }\n& .dsh-ui-help p + p { margin-top: 8px; }\n& .dsh-ui-required { color: var(--dsw-alias-state-error-primary); }\n& .dsh-ui-control { width: 100%; min-width: 0; }\n/* An invalid control is marked on the control, not only in the message. */\n& .dsh-ui-control[aria-invalid='true'] { border-color: var(--dsw-alias-state-error-primary); }\n& .dsh-ui-readonly { display: flex; flex-direction: column; gap: 4px; min-width: 0; }\n& .dsh-ui-readonly > dt { font-size: 12px; line-height: 18px; color: var(--dsw-alias-label-tertiary); }\n& .dsh-ui-readonly > dd { margin: 0; font-size: 13px; line-height: 20px; overflow-wrap: anywhere; }\n\n/* --- Buttons row. A no-op container: it exists so actions line up on the\n * trailing edge the way every native panel does. --- */\n& .dsh-ui-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding-top: 4px; }\n& .dsh-ui-actions-end { justify-content: flex-end; }\n\n/* --- Banners. A banner is a left-rail notice, the shape used for warnings,\n * deprecations and blocking instructions. --- */\n& .dsh-ui-banner {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 10px 14px;\n border: 0.5px solid var(--dsw-alias-border-l3);\n border-left: 3px solid var(--dsw-alias-state-warn-primary);\n border-radius: var(--dsw-radius-md);\n background: var(--dsw-alias-bg-layer-1);\n font-size: 13px;\n line-height: 20px;\n color: var(--dsw-alias-label-secondary);\n}\n& .dsh-ui-banner-title { font-weight: 500; color: var(--dsw-alias-state-warn-primary); }\n& .dsh-ui-banner--info { border-left-color: var(--dsw-alias-state-business-primary); }\n& .dsh-ui-banner--danger { border-left-color: var(--dsw-alias-state-error-primary); }\n& .dsh-ui-banner--success { border-left-color: var(--dsw-alias-state-success-primary); }\n& .dsh-ui-banner p { margin: 0; }\n\n/* --- Inline notice: a transient confirmation with no chrome of its own. --- */\n& .dsh-ui-notice { display: flex; align-items: center; gap: 6px; font-size: 12px; line-height: 18px; color: var(--dsw-alias-state-success-primary); }\n& .dsh-ui-notice--error { color: var(--dsw-alias-state-error-primary); }\n\n/* --- Empty and loading states. Both sit in a dashed well so an empty panel\n * reads as \"nothing here yet\" rather than as a broken layout. --- */\n& .dsh-ui-empty {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 6px;\n padding: 20px 16px;\n border: 0.5px dashed var(--dsw-alias-border-l3);\n border-radius: var(--dsw-radius-md);\n font-size: 13px;\n line-height: 20px;\n color: var(--dsw-alias-label-secondary);\n}\n& .dsh-ui-loading { display: flex; align-items: center; gap: 8px; padding: 20px 0; font-size: 13px; line-height: 20px; color: var(--dsw-alias-label-secondary); }\n\n/* --- Selectable list. A row is a button-sized target with a hover fill and\n * a selected fill; the name carries the weight, the description does not. --- */\n& .dsh-ui-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; min-width: 0; }\n& .dsh-ui-list-scroll { max-height: min(66vh, 760px); overflow-y: auto; overflow-x: hidden; }\n& .dsh-ui-list-row { border-radius: var(--dsw-radius-md); }\n& .dsh-ui-list-row:hover { background: var(--dsw-alias-interactive-bg-hover); }\n& .dsh-ui-list-row[aria-selected='true'], & .dsh-ui-list-row[data-selected] { background: var(--dsw-alias-interactive-bg-active); }\n& .dsh-ui-list-item {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n width: 100%;\n min-width: 0;\n padding: 10px 12px;\n border: 0;\n border-radius: var(--dsw-radius-md);\n background: none;\n font: inherit;\n color: inherit;\n text-align: start;\n cursor: pointer;\n}\n& .dsh-ui-list-item:disabled { cursor: default; }\n& .dsh-ui-list-item:disabled:hover { background: none; }\n& .dsh-ui-list-name { font-size: 14px; line-height: 22px; font-weight: 500; color: var(--dsw-alias-label-primary); overflow-wrap: anywhere; }\n& .dsh-ui-list-desc { font-size: 12px; line-height: 18px; color: var(--dsw-alias-label-tertiary); }\n\n/* --- Code and structured output. Wraps rather than scrolls, because a\n * feature panel is read rather than scanned. --- */\n& .dsh-ui-code {\n padding: 10px 12px;\n border: 0.5px solid var(--dsw-alias-border-l3);\n border-radius: var(--dsw-radius-md);\n background: var(--dsw-alias-bg-layer-1);\n font-family: var(--ds-font-family-code);\n font-size: 12px;\n line-height: 20px;\n color: var(--dsw-alias-label-secondary);\n overflow-wrap: anywhere;\n white-space: pre-wrap;\n}\n& .dsh-ui-scroll { max-height: 60vh; overflow: auto; overscroll-behavior: contain; }\n\n/* --- A toggle row: label on the left, switch on the trailing edge. This is\n * layout only; the switch itself is the official primitive. --- */\n& .dsh-ui-toggle-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; min-width: 0; padding: 12px 0; }\n& .dsh-ui-toggle-row + .dsh-ui-toggle-row { border-top: 0.5px solid var(--dsw-alias-border-l2); }\n& .dsh-ui-toggle-text { display: flex; flex-direction: column; gap: 4px; min-width: 0; }\n& .dsh-ui-toggle-label { font-size: 14px; line-height: 22px; font-weight: 500; color: var(--dsw-alias-label-primary); }\n\n/* --- Floating surface: a panel anchored to a trigger. This is the material\n * a menu card is made of — the translucent fill plus its backdrop blur, with\n * the edge riding inside the prominent elevation. A plugin that hand-rolls this\n * is how tokens like --dsw-specific-menu get invented; use this instead. --- */\n& .dsh-ui-surface {\n --dsw-elevation-stroke-color: var(--dsw-alias-border-l1);\n --dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2);\n --dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2);\n position: relative;\n display: flex;\n flex-direction: column;\n border: 0;\n border-radius: var(--dsw-radius-lg);\n background: var(--dsw-menu-surface-fill);\n backdrop-filter: var(--dsw-menu-backdrop-filter);\n box-shadow: var(--dsw-elevation-prominent);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n line-height: 20px;\n min-width: 0;\n}\n& .dsh-ui-surface-body { display: flex; flex-direction: column; gap: 8px; padding: 12px 14px 14px; overflow-y: auto; }\n& .dsh-ui-surface-head { display: flex; align-items: center; gap: 8px; min-width: 0; }\n& .dsh-ui-surface-title { font-size: 14px; line-height: 22px; font-weight: 500; color: var(--dsw-alias-label-primary); }\n\n/* --- Native select. The primitives ship no select, so a plugin that offers a\n * closed list of choices keeps the platform control and matches the official\n * settings field geometry exactly (34px, 0.5px l4, radius-md, layer-3). A\n * SegmentedControl is the primitive to reach for when the choices are few\n * enough to show at once. --- */\n& .dsh-ui-select {\n box-sizing: border-box;\n width: 100%;\n min-width: 0;\n height: 34px;\n padding: 0 12px;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: var(--dsw-radius-md);\n background: var(--dsw-alias-bg-layer-3);\n font: inherit;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n}\n& .dsh-ui-select:focus-visible { outline: none; border-color: var(--dsw-alias-state-business-primary); }\n& .dsh-ui-select:disabled { color: var(--dsw-alias-label-tertiary); cursor: default; }\n& .dsh-ui-select[aria-invalid='true'] { border-color: var(--dsw-alias-state-error-primary); }\n\n/* --- Native textarea. The primitives ship no textarea either, so a settings\n * prompt keeps the platform element and the same field shell as the select:\n * 0.5px l4, radius-md, layer-3, 13/1.5 type. It grows vertically instead of\n * locking to the select's 34px row. --- */\n& .dsh-ui-textarea {\n box-sizing: border-box;\n width: 100%;\n min-width: 0;\n resize: vertical;\n padding: 8px 12px;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: var(--dsw-radius-md);\n background: var(--dsw-alias-bg-layer-3);\n font: inherit;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n}\n& .dsh-ui-textarea:focus-visible { outline: none; border-color: var(--dsw-alias-state-business-primary); }\n& .dsh-ui-textarea:disabled { color: var(--dsw-alias-label-tertiary); cursor: default; }\n& .dsh-ui-textarea::placeholder { color: var(--dsw-alias-label-dimmed); }\n& .dsh-ui-textarea[aria-invalid='true'] { border-color: var(--dsw-alias-state-error-primary); }\n\n/* Long panels scroll inside themselves instead of growing the host page. */\n& .dsh-ui-panel { display: flex; flex-direction: column; gap: 16px; min-width: 0; }\n& .dsh-ui-panel-wide { max-width: 100%; }\n\n@media (prefers-reduced-motion: reduce) {\n & *, & *::before, & *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }\n}\n}\n`\n // Widen every recipe from \"a descendant of the root\" to \"the root, or one of\n // its descendants\". Written as a transform rather than by hand so no recipe\n // can be added later with only the descendant form. The pattern is narrow on\n // purpose: a leading `&` followed by one recipe class and no combinator, so\n // sibling and child rules such as `.dsh-ui-field + .dsh-ui-field` keep their\n // own meaning and are left alone.\n return sheet.replace(/^& (\\.dsh-ui-[a-z0-9-]+)(?=\\s*\\{)/gm, (_match, className: string) =>\n `&${className}, & ${className}`)\n}\n","import { officialUiCss } from '@klarkxy/dsh-plugin-kit/official-ui'\n\n/**\n * The shared contract paints this page: the type tiers, the card, the field,\n * the button row, the notice, the focus ring and the state colours. What is\n * left here is only the geometry the contract does not cover — the reading\n * width of the panel, the ranked three-column row, the drag affordance, the\n * drop indicator, the screen-reader-only announcer and the limits grid.\n *\n * Nothing below declares a colour of its own beyond naming a host token, and\n * nothing carries a literal fallback: the host publishes both themes and a\n * literal would pin one of them.\n *\n * Every plugin rule nests under the plugin root, as the contract does, so a\n * .ws-* or .web-search-* class never paints a neighbouring plugin. Selectors\n * that start with an element name keep an explicit `&`, the form every\n * nesting implementation accepts.\n */\nexport const webSearchClientStyles = `${officialUiCss('ws-root')}\n.ws-root {\n\n.ws-card-head { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding-bottom: 8px; }\n.ws-sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n}\n.ws-provider-note { display: flex; align-items: center; gap: 6px; }\n.web-search-provider { display: grid; gap: 8px; min-width: 0; }\n.web-search-provider-info { display: grid; gap: 3px; font-size: 12px; line-height: 18px; color: var(--dsw-alias-label-secondary); overflow-wrap: anywhere; }\n.web-search-provider-heading { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; min-height: 24px; }\n& a { color: var(--dsw-alias-state-business-primary); font-size: 12px; line-height: 18px; text-underline-offset: 3px; }\n/* The primitive renders a 32px control in an inline-flex wrapper; a settings\n * field needs it to fill the column it sits in. */\n.ws-input { width: 100%; }\n.web-search-rank { margin: 0; padding: 0; list-style: none; }\n.web-search-rank li {\n position: relative;\n display: grid;\n grid-template-columns: 24px minmax(0, 1fr) auto;\n gap: 10px;\n align-items: start;\n padding: 14px 0;\n border-top: 0.5px solid var(--dsw-alias-border-l2);\n}\n& button.web-search-drag-handle {\n display: grid;\n place-items: center;\n width: 24px;\n height: 24px;\n min-height: 24px;\n padding: 0;\n cursor: grab;\n opacity: 0.65;\n}\n& button.web-search-drag-handle:hover { opacity: 1; }\n& button.web-search-drag-handle:active { cursor: grabbing; }\n& button.web-search-drag-handle[aria-disabled='true'] { cursor: wait; opacity: 0.4; }\n.web-search-rank li[data-dragging] { opacity: 0.45; }\n.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; }\n.web-search-rank li[data-drop='before']::after { top: -1px; }\n.web-search-rank li[data-drop='after']::after { bottom: -1px; }\n.web-search-rank-index { color: var(--dsw-alias-label-tertiary); font-variant-numeric: tabular-nums; line-height: 24px; }\n.web-search-rank-move { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; justify-content: flex-end; padding-top: 2px; }\n.ws-danger:hover:not(:disabled),\n.ws-danger[data-armed] { color: var(--dsw-alias-state-error-primary); border-color: var(--dsw-alias-state-error-primary); }\n.ws-danger:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover-danger); }\n@media (max-width: 560px) {\n .web-search-rank-move { flex-direction: column; align-items: flex-end; }\n}\n}\n`\n","import { useCallback, useEffect, useRef, useSyncExternalStore } from 'react'\n\n/** Fixed pause for failing background reads; manual work is never replayed. */\nexport function createPluginReadGate(now: () => number = Date.now) {\n let pausedUntil = 0\n let epoch = 0\n return {\n canRead: () => now() >= pausedUntil,\n reset: () => { epoch++; pausedUntil = 0 },\n async run<T>(read: () => Promise<T>): Promise<T> {\n const current = epoch\n try {\n const value = await read()\n if (current === epoch) pausedUntil = 0\n return value\n } catch (error) {\n // The native Connection error is an Error with this exact HTTP text.\n // Authentication, application failures, and cancellation stay distinct.\n const message = error instanceof Error ? error.message : ''\n if (current === epoch && (/(?:^|\\s)transport failure for .+: HTTP (?:404|405|503)\\b/.test(message)\n || (error instanceof TypeError && /^(?:Failed to fetch|fetch failed|NetworkError when attempting to fetch resource\\.?)$/i.test(message)))) {\n pausedUntil = now() + 30_000\n }\n throw error\n }\n },\n }\n}\n\nexport interface NativeSurfaceClient {\n sessions: {\n binding?(sessionId: string): { eventSource: { subscribe(listener: () => void): () => void } } | undefined\n }\n uiWorkspace: { current?: { getSnapshot(): { sessionId: string } | undefined; subscribe(listener: () => void): () => void } }\n uiSession: { adapter: { current: { getSnapshot(): { key?: string }; subscribe(listener: () => void): () => void } } }\n locale: { getSnapshot(): { active: string }; subscribe(listener: () => void): () => void }\n connection?: { generation?: { subscribe(listener: () => void): () => void } }\n}\n\n/** The Editor retains its manuscript session separately; native DSH exposes the main-view binding. */\nexport function selectedSessionId(client: Pick<NativeSurfaceClient, 'uiWorkspace' | 'uiSession'>): string {\n const workspace = client.uiWorkspace?.current\n return workspace\n ? workspace.getSnapshot()?.sessionId ?? ''\n : client.uiSession.adapter.current.getSnapshot().key ?? ''\n}\n\n/**\n * Owner props are data the host composed, never a Cordis context: reading an\n * undeclared property off one throws instead of returning undefined, which\n * would crash the slot entry and abdicate its cell for good. Tolerate a\n * throwing read so a mistaken prop degrades to \"not supplied\".\n */\nfunction read(source: unknown, key: string): unknown {\n if (source === null || typeof source !== 'object') return undefined\n try {\n return (source as Record<string, unknown>)[key]\n } catch {\n return undefined\n }\n}\n\nfunction readString(source: unknown, key: string): string | undefined {\n const value = read(source, key)\n return typeof value === 'string' ? value : undefined\n}\n\n/** Owner props win; native settings only supplies close, so resolve its selected session. */\nexport function useNativeSeat(client: NativeSurfaceClient, props?: unknown) {\n const current = useSyncExternalStore(\n useCallback((fn: () => void) => (client.uiWorkspace?.current ?? client.uiSession.adapter.current).subscribe(fn), [client]),\n useCallback(() => selectedSessionId(client), [client]),\n () => '',\n )\n const language = useSyncExternalStore(\n useCallback((fn: () => void) => client.locale.subscribe(fn), [client]),\n useCallback(() => client.locale.getSnapshot().active, [client]),\n () => 'en',\n )\n const sessionId = readString(props, 'sessionId') ?? readString(read(props, 'owner'), 'sessionId') ?? current\n const requestedLocale = read(props, 'locale') ?? read(read(props, 'owner'), 'locale') ?? language\n return { sessionId, locale: (String(requestedLocale).startsWith('zh') ? 'zh' : 'en') as 'zh' | 'en',\n hidden: read(props, 'hidden') === true || read(read(props, 'owner'), 'hidden') === true }\n}\n\n/** Refresh on native log boundaries/reconnect/focus. Read-only settling is bounded to an observed job. */\nexport function useFeatureRefresh(\n client: NativeSurfaceClient, sessionId: string, refresh: () => void,\n working = false, enabled = true,\n): void {\n const refreshRef = useRef(refresh)\n refreshRef.current = refresh\n useEffect(() => {\n if (!enabled) return\n let timer: ReturnType<typeof setTimeout> | undefined\n const queue = () => {\n if (typeof document !== 'undefined' && document.visibilityState === 'hidden') return\n clearTimeout(timer)\n timer = setTimeout(() => refreshRef.current(), 150)\n }\n const source = sessionId ? client.sessions.binding?.(sessionId)?.eventSource : undefined\n const stopEvents = source?.subscribe(queue)\n const stopConnection = client.connection?.generation?.subscribe(queue)\n const visibility = () => { if (document.visibilityState === 'visible') queue() }\n window.addEventListener('focus', queue)\n document.addEventListener('visibilitychange', visibility)\n // Details are collapsed by default; opening an existing panel must get current state.\n document.addEventListener('toggle', queue, true)\n return () => {\n clearTimeout(timer)\n stopEvents?.(); stopConnection?.()\n window.removeEventListener('focus', queue)\n document.removeEventListener('visibilitychange', visibility)\n document.removeEventListener('toggle', queue, true)\n }\n }, [client, sessionId, enabled])\n useEffect(() => {\n if (!enabled || !working) return\n let delay = 250\n const until = Date.now() + 300_000\n let timer: ReturnType<typeof setTimeout>\n const tick = () => {\n if (Date.now() >= until) return\n if (document.visibilityState !== 'hidden') refreshRef.current()\n delay = Math.min(delay * 2, 2000)\n timer = setTimeout(tick, delay)\n }\n timer = setTimeout(tick, delay)\n return () => clearTimeout(timer)\n }, [client, sessionId, working, enabled])\n}\n","import type { WebFetchProvider, WebSearchProvider } from '@deepseek-ai/dsh-web'\nimport type { DshWebManagementDescriptor } from '@klarkxy/dsh-plugin-kit/contracts'\n\nexport const WEB_SEARCH_RPC_CHANNEL = '/web-search-manager'\nexport type RpcResult<T = unknown> = { ok: true; value: T } | {\n ok: false; error: { code: string; message: string; details: Record<string, unknown> }\n}\nexport type ProviderKind = 'search' | 'fetch'\n/** Management metadata only. Field contract lives in plugin-kit; execution stays with ctx.web. */\nexport interface ProviderDescriptor extends DshWebManagementDescriptor {}\nexport const WEB_MANAGER_OWNER = '@klarkxy/dsh-web-search-manager'\n/** Optional display metadata on a provider registered directly with ctx.web. */\nexport type DiscoverableSearchProvider = WebSearchProvider & { readonly dshWebManagement?: ProviderDescriptor }\nexport type DiscoverableFetchProvider = WebFetchProvider & { readonly dshWebManagement?: ProviderDescriptor }\nexport interface ProviderOptions {\n readonly apiKey?: string\n readonly baseURL?: string\n readonly timeoutMs: number\n readonly maxFetchChars: number\n}\nexport type SearchProviderFactory = (options: ProviderOptions) => WebSearchProvider\nexport type FetchProviderFactory = (options: ProviderOptions) => WebFetchProvider\n/** Never store credential values here. Keys are resolved by reference per call. */\nexport interface WebSettings {\n revision: number\n searchEnabled: boolean\n fetchEnabled: boolean\n searchProvider: string\n fetchProvider: string\n searchOrder: string[]\n fetchOrder: string[]\n maxResults: number\n maxQueries: number\n timeoutMs: number\n maxFetchChars: number\n endpoints: Record<string, string>\n}\nexport const DEFAULT_SEARCH_ORDER = ['ddg'] as const\n\nexport const defaultSettings = (): WebSettings => ({\n revision: 0, searchEnabled: true, fetchEnabled: true,\n searchProvider: 'ddg', fetchProvider: 'http', searchOrder: [...DEFAULT_SEARCH_ORDER], fetchOrder: ['http'],\n maxResults: 5, maxQueries: 1,\n timeoutMs: 30_000, maxFetchChars: 100_000, endpoints: {},\n})\n\n/** Keyed backends first, keyless last — same idea as OpenClaw auto-detect. */\nexport function defaultSearchOrder(ids: readonly string[], keyed: (id: string) => boolean): string[] {\n return [...ids.filter(keyed), ...ids.filter(id => !keyed(id))]\n}\n\nexport function migrateSearchOrder(settings: { searchOrder?: string[]; searchProvider?: string }): string[] {\n return settings.searchOrder ?? (settings.searchProvider ? [settings.searchProvider] : [...DEFAULT_SEARCH_ORDER])\n}\n\n/** Missing order is a legacy single selection; an empty selection never opts in. */\nexport function migrateFetchOrder(settings: { fetchOrder?: string[]; fetchProvider?: string }): string[] {\n return settings.fetchOrder ?? (settings.fetchProvider ? [settings.fetchProvider] : [])\n}\n\nexport function resolveSearchOrder(\n knownIds: readonly string[],\n settings: Pick<WebSettings, 'searchOrder' | 'searchProvider'>,\n _keyed: (id: string) => boolean,\n): string[] {\n return [...new Set(settings.searchOrder.filter(id => knownIds.includes(id)))]\n}\n\nexport function pickActiveSearch(order: readonly string[], configured: (id: string) => boolean): string {\n return order.find(configured) ?? ''\n}\nexport interface ProviderView extends ProviderDescriptor {\n kind: ProviderKind\n configured: boolean\n /** True only for configuration explicitly owned by this manager bundle. */\n configurationOwned?: boolean\n baseURL?: string\n calls: number\n failures: number\n}\nexport interface WebStatus {\n settings: WebSettings\n providers: ProviderView[]\n searchActive: boolean\n fetchActive: boolean\n storageFailed: boolean\n}\nexport function providerKey(kind: ProviderKind, id: string): string { return `${kind}:${id}` }\nexport function safeConfigurationUrl(value: unknown): string | undefined {\n if (typeof value !== 'string' || !value || /[\\u0000-\\u0020\\\\]/.test(value)) return\n try {\n const url = new URL(value, 'https://dsh.invalid')\n if (url.username || url.password) return\n if ((value.startsWith('/') && !value.startsWith('//')) || value.startsWith('#')) {\n return url.origin === 'https://dsh.invalid' ? value : undefined\n }\n return /^https:\\/\\//i.test(value) && url.protocol === 'https:' ? value : undefined\n } catch { return }\n}\nexport function validateBaseURL(value: string): string {\n const url = new URL(value)\n if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash) {\n throw new Error('供应商地址必须是 HTTPS,且不能包含用户名、密码、查询参数或片段。')\n }\n return url.href.replace(/\\/+$/, '')\n}\n","import { WEB_MANAGER_OWNER, type ProviderView } from './contracts.ts'\n\n// Public prices checked on 2026-09-21; links remain available for current account terms.\n// This date applies only to bundled adapters that still use these static notes.\nexport const PROVIDER_PRICING_CHECKED = '2026-09-21'\n\ninterface ProviderInfo {\n description: string\n pricing: string\n pricingUrl?: string\n}\n\nconst providerInfo: Record<string, ProviderInfo> = {\n perplexity: {\n description: '通过 Perplexity Search API 获取网页来源,不生成回答。',\n pricing: '按 Search API 请求计费;实际价格与额度以官方账户为准。',\n pricingUrl: 'https://docs.perplexity.ai/docs/getting-started/pricing',\n },\n you: {\n description: '通过 You.com 获取网页与新闻结果,不请求额外全文提取。',\n pricing: '按 Web Search API 请求计费;实际价格与额度以官方账户为准。',\n pricingUrl: 'https://you.com/docs/administration/billing',\n },\n 'firecrawl-fetch': {\n description: '读取网页 Markdown;与 Firecrawl 搜索共用 Key。',\n pricing: '网页读取使用 Firecrawl 积分,与搜索分别计费;实际消耗以官方账户为准。',\n pricingUrl: 'https://www.firecrawl.dev/pricing',\n },\n ddg: {\n description: '搜索 DuckDuckGo 公开网页,无需注册或 API Key。',\n pricing: '免费使用,不产生搜索 API 费用。',\n pricingUrl: 'https://duckduckgo.com/',\n },\n bocha: {\n description: '面向中文内容的网页搜索,返回标题、链接和摘要。',\n pricing: '按调用次数计费,目录价 ¥0.036/次;试用额度以官方活动为准。',\n pricingUrl: 'https://aq6ky2b8nql.feishu.cn/wiki/JYSbwzdPIiFnz4kDYPXcHSDrnZb',\n },\n brave: {\n description: '使用 Brave 独立网页索引,搜索网页、新闻等内容。',\n pricing: '每月赠 $5(约 1,000 次);超出 $5/千次,激活需绑卡。',\n pricingUrl: 'https://brave.com/search/api/',\n },\n 'deepseek-official': {\n description: '由 DeepSeek 模型调用内置搜索,复用模型 API Key。',\n pricing: '默认 Flash 按 tokens 计费:每百万输入 ¥1–2(缓存命中 ¥0.02–0.04)、输出 ¥4–8,随峰谷时段变化;赠送余额与工具费用以平台为准。',\n pricingUrl: 'https://api-docs.deepseek.com/zh-cn/quick_start/pricing/',\n },\n exa: {\n description: '面向 AI 的网页搜索,提供相关网页与内容片段。',\n pricing: '注册赠 $20,之后每月赠 $10;$7/千次(每次最多 10 条),超出 10 条每条加 $1/千次。',\n pricingUrl: 'https://exa.ai/pricing',\n },\n firecrawl: {\n description: '搜索网页,并可配合网页内容提取。',\n pricing: '每月免费 1,000 积分,搜索每 10 条结果扣 2 积分;Hobby 含 5,000 积分/月,年付折合 $16/月,额外 $5/千积分。',\n pricingUrl: 'https://www.firecrawl.dev/pricing',\n },\n serper: {\n description: '获取 Google 的网页搜索结果。',\n pricing: '注册赠 2,500 次(一次性);充值 $50/5万次($1/千次),付费额度有效期 6 个月。',\n pricingUrl: 'https://serper.dev/',\n },\n tavily: {\n description: '面向 AI 的网页搜索,当前使用基础搜索。',\n pricing: '每月免费 1,000 积分,基础搜索 1 积分/次;按量付费 $0.008/积分($8/千次)。',\n pricingUrl: 'https://docs.tavily.com/documentation/api-credits',\n },\n}\n\n// The prices are the same dated notes in both languages; translation is not a price refresh.\nconst englishInfo: Record<string, Pick<ProviderInfo, 'description' | 'pricing'>> = {\n perplexity: { description: 'Get web sources through the Perplexity Search API without generating an answer.', pricing: 'Charged per Search API request; current prices and credits depend on your official account.' },\n you: { description: 'Get web and news results through You.com without requesting extra full-page extraction.', pricing: 'Charged per Web Search API request; current prices and credits depend on your official account.' },\n 'firecrawl-fetch': { description: 'Read page Markdown using the same key as Firecrawl search.', pricing: 'Page reads use Firecrawl credits and are charged separately from search; your official account determines usage.' },\n ddg: { description: 'Search public DuckDuckGo pages without registration or an API key.', pricing: 'Free to use; no search API fees.' },\n bocha: { description: 'Search Chinese web content, returning titles, links and summaries.', pricing: 'Charged per request: listed price ¥0.036/request; trial credits depend on official offers.' },\n brave: { description: 'Search web pages, news and more with Brave’s independent index.', pricing: '$5 in monthly credits (about 1,000 requests); then $5/1,000 requests. A payment card is required to activate.' },\n 'deepseek-official': { description: 'Call built-in search through a DeepSeek model using the model API key.', pricing: 'Default Flash token pricing per million: input ¥1–2 (cache hits ¥0.02–0.04), output ¥4–8, varying by peak/off-peak hours. Platform terms govern promotional balance and tool fees.' },\n exa: { description: 'Web search for AI with relevant pages and content snippets.', pricing: '$20 on signup, then $10 monthly credits; $7/1,000 requests (up to 10 results each), plus $1/1,000 additional results beyond 10.' },\n firecrawl: { description: 'Search the web and extract page content.', pricing: '1,000 free credits/month; 2 credits per 10 search results. Hobby includes 5,000 credits/month at $16/month billed annually; extra credits cost $5/1,000.' },\n serper: { description: 'Get Google web search results.', pricing: '2,500 one-time signup requests; $50/50,000 requests ($1/1,000). Paid credits expire after 6 months.' },\n tavily: { description: 'Web search for AI, currently using basic search.', pricing: '1,000 free credits/month; basic search uses 1 credit/request. Pay as you go: $0.008/credit ($8/1,000 requests).' },\n}\n\nconst bundledDescriptions: Record<string, readonly [string, string]> = {\n perplexity: ['Search API;返回网页来源,不生成回答。', 'Search API returning web sources without generating an answer.'],\n you: ['网页与新闻搜索;不额外提取全文。', 'Web and news search without extra full-page extraction.'],\n 'firecrawl-fetch': ['读取网页 Markdown;与 Firecrawl 搜索共用 Key。', 'Read page Markdown using the same key as Firecrawl search.'],\n ddg: ['无需注册。默认后端。', 'No registration required. Default backend.'],\n 'deepseek-official': ['使用已配置的 DeepSeek API Key,可能产生额外搜索费用。', 'Uses the configured DeepSeek API key; additional search fees may apply.'],\n exa: ['独立 Search API。', 'Independent Search API.'],\n brave: ['Brave Search API。', 'Brave Search API.'],\n bocha: ['中文网页搜索 API。', 'Chinese web search API.'],\n serper: ['Google 结果的 Search API。', 'Search API for Google results.'],\n firecrawl: ['网页搜索与提取 API。', 'Web search and extraction API.'],\n tavily: ['Search API;固定 basic 检索,不自动升级搜索深度。', 'Search API using basic search; search depth is never upgraded automatically.'],\n}\n\n/** Translate only manager-owned labels; externally supplied metadata stays verbatim. */\nexport function providerDisplayLabel(provider: ProviderView, locale: 'zh' | 'en'): string {\n if (!bundledCatalogId(provider)) return provider.label\n if (provider.label === 'DeepSeek 搜索') return locale === 'zh' ? provider.label : 'DeepSeek search'\n if (provider.label === '博查') return locale === 'zh' ? provider.label : 'Bocha'\n if (provider.label === '网页读取') return locale === 'zh' ? provider.label : 'Read web pages'\n return provider.label\n}\n\nfunction bundledCatalogId(provider: ProviderView): string | undefined {\n const owned = typeof provider.configurationOwned === 'boolean'\n ? provider.configurationOwned\n : !provider.configurationOwner || provider.configurationOwner === WEB_MANAGER_OWNER\n if (!owned) return\n if (provider.kind === 'fetch' && provider.id === 'firecrawl') return 'firecrawl-fetch'\n return provider.id === 'deepseek-managed' ? 'deepseek-official' : provider.id\n}\n\nfunction missingPricing(locale: 'zh' | 'en'): string {\n return locale === 'zh' ? '未提供费用信息' : 'No pricing information provided'\n}\n\nexport function usesBundledPricing(provider: ProviderView): boolean {\n if (provider.pricing?.trim()) return false\n const catalogId = bundledCatalogId(provider)\n return Boolean(catalogId && !['perplexity', 'you', 'firecrawl-fetch'].includes(catalogId) && Object.hasOwn(providerInfo, catalogId))\n}\n\nexport function searchProviderInfo(provider: ProviderView, locale: 'zh' | 'en' = 'zh'): ProviderInfo {\n const catalogId = bundledCatalogId(provider)\n const base = catalogId && Object.hasOwn(providerInfo, catalogId) ? providerInfo[catalogId] : undefined\n const known = base && locale === 'en' ? { ...base, ...englishInfo[catalogId!] } : base\n const ownedCopy = catalogId ? bundledDescriptions[catalogId] : undefined\n const description = ownedCopy && provider.description === ownedCopy[0]\n ? ownedCopy[locale === 'zh' ? 0 : 1]\n : base && provider.description === base.description ? known!.description\n : provider.description.trim() ? provider.description : (known?.description ?? provider.description)\n if (provider.pricing?.trim()) {\n return {\n description,\n pricing: provider.pricing,\n pricingUrl: provider.pricingUrl ?? known?.pricingUrl ?? provider.signupUrl,\n }\n }\n if (known) {\n return {\n description,\n pricing: known.pricing,\n pricingUrl: provider.pricingUrl ?? known.pricingUrl ?? provider.signupUrl,\n }\n }\n return {\n description,\n pricing: missingPricing(locale),\n pricingUrl: provider.pricingUrl ?? provider.signupUrl,\n }\n}\n","import { localErrorText } from './client-error.ts'\nimport { webSearchClientStyles } from './client-styles.ts'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { Button, Input, SegmentedTabs, StateDot, Switch, Tag } from '@deepseek-ai/dsh-client-ui-primitives'\nimport { createPluginReadGate } from '@klarkxy/dsh-plugin-kit/client-utils'\nimport { useCallback, useEffect, useId, useLayoutEffect, useRef, useState, useSyncExternalStore } from 'react'\nimport { PROVIDER_PRICING_CHECKED, providerDisplayLabel, searchProviderInfo, usesBundledPricing } from './provider-info.ts'\nimport {\n WEB_SEARCH_RPC_CHANNEL, WEB_MANAGER_OWNER, safeConfigurationUrl,\n providerKey, type ProviderKind, type ProviderView, type RpcResult, type WebSettings, type WebStatus,\n} from './contracts.ts'\n\nexport const name = 'dsh-web-search-manager-client'\nexport const inject = ['slots', 'connection', 'remote', 'remote.credentials', 'locale'] as const\n\ninterface Credentials {\n describe(refs: string[]): Promise<RpcResult<Record<string, { configured: boolean; writable: boolean; source?: string }>>>\n set(ref: string, value: string): Promise<RpcResult<unknown>>\n unset(ref: string): Promise<RpcResult<unknown>>\n}\ninterface Client {\n connection: { rpc: { call(channel: string, endpoint: string, payload: unknown): Promise<unknown> }; generation?: { subscribe(listener: () => void): () => void } }\n remote: { credentials: Credentials }\n /** Host UI language. Optional so bare test harnesses fall back to zh. */\n locale?: { getSnapshot(): { active: string }; subscribe(listener: () => void): () => void }\n slots: {\n inject(key: string, callback: () => unknown): () => void\n register(spec: { name: string; key: string }, render: unknown): () => void\n }\n}\n\nfunction unwrap<T>(result: RpcResult<T>): T {\n if (!result.ok) throw new Error(result.error.message)\n return result.value\n}\nfunction editable(settings: WebSettings): Omit<WebSettings, 'revision'> {\n const { revision: _revision, ...rest } = settings\n return rest\n}\n\nexport const STATUS_REFRESH_MS = 3000\n\nexport function isPageHidden(hidden?: boolean): boolean {\n if (hidden) return true\n return typeof document !== 'undefined' && document.visibilityState === 'hidden'\n}\n\nexport function canStartStatusRead(input: {\n source: 'user' | 'poll'\n mounted: boolean\n hidden: boolean\n busy: boolean\n reordering: boolean\n reading: boolean\n}): boolean {\n if (!input.mounted) return false\n if (input.source === 'poll') {\n return !input.hidden && !input.busy && !input.reordering && !input.reading\n }\n return true\n}\n\nexport function isExternalConfigurationUrl(url: string): boolean {\n return /^https:\\/\\//i.test(url)\n}\n\nexport function shouldApplyPolledStatus(input: {\n requestId: number\n latestRequestId: number\n busy: boolean\n reordering: boolean\n hidden: boolean\n currentRevision?: number\n incomingRevision: number\n}): boolean {\n if (input.hidden || input.busy || input.reordering) return false\n if (input.requestId !== input.latestRequestId) return false\n if (input.currentRevision !== undefined && input.incomingRevision < input.currentRevision) return false\n return true\n}\n\nexport function bindStatusRefresh(options: {\n intervalMs?: number\n isHidden: () => boolean\n tick: () => Promise<void>\n setIntervalFn?: typeof setInterval\n clearIntervalFn?: typeof clearInterval\n addListener?: (type: string, listener: () => void) => void\n removeListener?: (type: string, listener: () => void) => void\n}): () => void {\n const intervalMs = options.intervalMs ?? STATUS_REFRESH_MS\n let inFlight = false\n let stopped = false\n const run = async () => {\n if (stopped || inFlight || options.isHidden()) return\n inFlight = true\n try { await options.tick() }\n finally { inFlight = false }\n }\n const timer = (options.setIntervalFn ?? setInterval)(() => { void run() }, intervalMs)\n const onVisible = () => { if (!options.isHidden()) void run() }\n const add = options.addListener ?? ((type, listener) => {\n if (typeof document !== 'undefined') document.addEventListener(type, listener)\n })\n const remove = options.removeListener ?? ((type, listener) => {\n if (typeof document !== 'undefined') document.removeEventListener(type, listener)\n })\n add('visibilitychange', onVisible)\n return () => {\n stopped = true\n ;(options.clearIntervalFn ?? clearInterval)(timer)\n remove('visibilitychange', onVisible)\n }\n}\n\nexport function isProviderOn(status: WebStatus, provider: ProviderView): boolean {\n if (provider.kind === 'fetch') {\n return status.fetchActive && selectedBackend(status, 'fetch')?.id === provider.id\n }\n return status.searchActive && selectedSearchBackend(status)?.id === provider.id\n}\n\nexport function dormantSearchPlaceholder(id: string): ProviderView { return dormantPlaceholder(id, 'search') }\nfunction dormantPlaceholder(id: string, kind: ProviderKind): ProviderView {\n return { id, label: id, description: '', billing: 'unknown', kind, configured: false, calls: 0, failures: 0, configurationOwned: false }\n}\nfunction isRegistered(status: WebStatus, id: string, kind: ProviderKind): boolean {\n return status.providers.some(provider => provider.kind === kind && provider.id === id)\n}\nexport function isRegisteredSearch(status: WebStatus, id: string): boolean { return isRegistered(status, id, 'search') }\nfunction providerOrder(settings: WebSettings, kind: ProviderKind): string[] {\n return kind === 'search' ? settings.searchOrder : settings.fetchOrder\n}\nfunction catalogBackends(status: WebStatus, kind: ProviderKind): ProviderView[] {\n const byId = new Map(status.providers.filter(provider => provider.kind === kind).map(provider => [provider.id, provider]))\n const rows: ProviderView[] = []\n const seen = new Set<string>()\n for (const id of providerOrder(status.settings, kind)) {\n if (!id || seen.has(id)) continue\n seen.add(id)\n rows.push(byId.get(id) ?? dormantPlaceholder(id, kind))\n }\n for (const provider of byId.values()) if (!seen.has(provider.id)) rows.push(provider)\n return rows\n}\nexport function catalogSearchBackends(status: WebStatus): ProviderView[] { return catalogBackends(status, 'search') }\nexport function searchBackends(status: WebStatus): ProviderView[] {\n return catalogSearchBackends(status).filter(provider => status.settings.searchOrder.includes(provider.id))\n}\nfunction selectedBackend(status: WebStatus, kind: ProviderKind): ProviderView | undefined {\n return providerOrder(status.settings, kind).flatMap(id => status.providers.find(provider => provider.kind === kind && provider.id === id) ?? []).find(provider => provider.configured)\n}\nexport function selectedSearchBackend(status: WebStatus): ProviderView | undefined { return selectedBackend(status, 'search') }\nexport function droppedDormantIds(settings: WebSettings, order: readonly string[], providers: readonly ProviderView[], kind: ProviderKind = 'search'): string[] {\n const known = new Set(providers.filter(provider => provider.kind === kind).map(provider => provider.id))\n return [...new Set(providerOrder(settings, kind).filter(id => id && !known.has(id) && !order.includes(id)))]\n}\n\nexport type ConfigurationAccess =\n | { kind: 'owned' }\n | { kind: 'link'; url: string }\n | { kind: 'owner'; name: string }\n | { kind: 'unknown' }\n\nexport function configurationAccess(provider: ProviderView): ConfigurationAccess {\n if (ownsProviderConfiguration(provider)) return { kind: 'owned' }\n const url = safeConfigurationUrl(provider.configurationUrl)\n if (url) return { kind: 'link', url }\n if (provider.configurationOwner?.trim()) return { kind: 'owner', name: provider.configurationOwner }\n return { kind: 'unknown' }\n}\n\nfunction AppLink({ url, label, children }: { url: string; label: string; children: string }) {\n const external = isExternalConfigurationUrl(url)\n return <a href={url} {...(external ? { target: '_blank', rel: 'noreferrer noopener' } : {})} aria-label={label}\n onClick={event => {\n if (!external) return\n const bridge = (globalThis as { dshWindow?: { openExternal?(url: string): void } }).dshWindow\n if (bridge?.openExternal) {\n event.preventDefault()\n bridge.openExternal(url)\n }\n }}>{children}</a>\n}\n\nexport function canEnableSearch(\n order: readonly string[],\n providers: readonly ProviderView[],\n keys: Record<string, string>,\n writable: Record<string, boolean>,\n): boolean {\n return order.some(id => {\n const provider = providers.find(item => item.kind === 'search' && item.id === id)\n if (!provider) return false\n if (provider.configured) return true\n const typed = (keys[provider.id] ?? '').trim()\n return Boolean(ownsProviderConfiguration(provider) && provider.credentialRef && typed && writable[provider.credentialRef] !== false)\n })\n}\n\nexport function ownsProviderConfiguration(provider: Pick<ProviderView, 'configurationOwned' | 'configurationOwner'>): boolean {\n if (typeof provider.configurationOwned === 'boolean') return provider.configurationOwned\n return !provider.configurationOwner || provider.configurationOwner === WEB_MANAGER_OWNER\n}\n\nexport function nextSearchEnabled(\n currentlyEnabled: boolean,\n nextOrder: readonly string[],\n providers: readonly ProviderView[],\n keys: Record<string, string>,\n writable: Record<string, boolean>,\n): boolean {\n return currentlyEnabled && canEnableSearch(nextOrder, providers, keys, writable)\n}\n\nfunction ConfirmButton(props: { label: string; confirmLabel: string; disabled?: boolean; onConfirm(): void }) {\n const [armed, setArmed] = useState(false)\n const timer = useRef<ReturnType<typeof setTimeout>>(undefined)\n useEffect(() => () => clearTimeout(timer.current), [])\n function disarm() {\n clearTimeout(timer.current)\n setArmed(false)\n }\n return <Button variant=\"outline\" size=\"sm\" className=\"ws-danger\" disabled={props.disabled} data-armed={armed || undefined}\n onClick={() => {\n if (!armed) {\n setArmed(true)\n timer.current = setTimeout(() => setArmed(false), 3000)\n return\n }\n disarm()\n props.onConfirm()\n }}\n onBlur={disarm}>{armed ? props.confirmLabel : props.label}</Button>\n}\n\nconst copy = {\n zh: {\n tabsLabel: '网络搜索设置', tabSearch: 'web-search', tabFetch: 'web-fetch', tabLimits: '设置',\n loading: '正在读取设置…', loadFailed: '读取设置失败:', reconnect: '重新连接',\n failed: '操作失败。', readOnly: '凭据只读,无法保存。',\n keyDeleted: '已删除 Key。', keySaved: '已保存 Key。', saved: '已保存。',\n storageFailed: '保存失败,重新保存前网络访问已暂停。',\n empty: '暂无可用搜索后端,请启用一个供应商。', toolTitle: '联网搜索', needBackend: '需选择搜索服务并配置 Key。',\n backends: '搜索后端', dragPrefix: '拖动排序 ', dragHint: '拖动排序,或用上下方向键',\n inUse: '当前使用', available: '可用', unavailable: '暂不可用', dormant: '未加载,保留选择',\n signup: '去注册', pricing: '费用说明', website: '官网', openConfig: '打开配置',\n needsKey: '待配置 Key', sharedKey: '与其他设置共用凭据',\n keyConfigured: '已配置,留空不替换', keyPlaceholder: '输入 API Key',\n saveKey: '保存 Key', deleteKey: '删除 Key', confirmDelete: '确认删除 Key?',\n test: '测试连接(可能计费)', unsaved: '有未保存的修改', save: '保存',\n saveHint: '开关与排序立即生效;Key 与限制需保存。',\n maxResults: '每条查询结果上限', maxQueries: '每次调用查询上限',\n timeoutMs: '请求超时(毫秒)', maxFetchChars: '正文字符上限',\n fetchTitle: '网页读取', fetchProvider: '读取服务', fetchBackends: '读取后端', needFetchBackend: '需选择并配置读取服务。', ownerSettings: '配置入口:',\n unknownOwner: '请在服务所属插件中配置',\n },\n en: {\n tabsLabel: 'Web search settings', tabSearch: 'web-search', tabFetch: 'web-fetch', tabLimits: 'Settings',\n loading: 'Loading settings…', loadFailed: 'Could not load settings: ', reconnect: 'Reconnect',\n failed: 'Operation failed.', readOnly: 'Read-only credential; cannot save.',\n keyDeleted: 'Key deleted.', keySaved: 'Key saved.', saved: 'Saved.',\n storageFailed: 'Save failed; network access is paused until you save again.',\n empty: 'No search backends yet; enable a provider.', toolTitle: 'Web search', needBackend: 'Needs a search service with a key.',\n backends: 'Search backends', dragPrefix: 'Reorder ', dragHint: 'Drag to reorder, or use arrow keys',\n inUse: 'In use', available: 'Available', unavailable: 'Unavailable', dormant: 'Not loaded; selection kept',\n signup: 'Sign up', pricing: 'Pricing', website: 'Website', openConfig: 'Open configuration',\n needsKey: 'Key required', sharedKey: 'Shares a credential with other settings',\n keyConfigured: 'Configured; empty keeps it', keyPlaceholder: 'Enter API key',\n saveKey: 'Save key', deleteKey: 'Delete key', confirmDelete: 'Delete this key?',\n test: 'Test connection (may be billed)', unsaved: 'Unsaved changes', save: 'Save',\n saveHint: 'Switches and order apply now; keys and limits need saving.',\n maxResults: 'Max results/query', maxQueries: 'Max queries/call',\n timeoutMs: 'Request timeout (ms)', maxFetchChars: 'Max text chars',\n fetchTitle: 'Read web pages', fetchProvider: 'Fetch service', fetchBackends: 'Fetch backends', needFetchBackend: 'Choose and configure a fetch service.', ownerSettings: 'Configure in: ',\n unknownOwner: 'Configure this service in its plugin',\n },\n} as const\n\ntype Copy = (typeof copy)['zh'] | (typeof copy)['en']\n\n// Keep action outcomes as data so a language switch also updates existing notices.\ntype UiMessage = string | { key: keyof Copy } | { sources: number } | { moved: string; kind: ProviderKind; position: number } | { invalid: LimitKey[] }\nclass UiError extends Error {\n constructor(readonly key: keyof Copy) { super(key) }\n}\n\nfunction uiMessage(message: UiMessage, locale: 'zh' | 'en', providers: ProviderView[] = []): string {\n if (typeof message === 'string') return localErrorText(message, locale)\n if ('key' in message) return copy[locale][message.key]\n if ('sources' in message) return testOkText(locale, message.sources)\n if ('moved' in message) {\n const provider = providers.find(item => item.id === message.moved && item.kind === message.kind)\n return movedText(locale, provider ? providerDisplayLabel(provider, locale) : message.moved, message.position)\n }\n return message.invalid.map(key => {\n const [, min, max] = LIMITS.find(item => item[0] === key)!\n return limitInvalidText(locale, copy[locale][key], min, max)\n }).join(' ')\n}\n\nfunction movedText(locale: 'zh' | 'en', label: string, position: number): string {\n return locale === 'zh' ? `${label} 已移至第 ${position} 位` : `${label} moved to position ${position}`\n}\nfunction pricingCheckedText(locale: 'zh' | 'en', date: string): string {\n return locale === 'zh'\n ? `内置适配器价格核对于 ${date},额度与费用以供应商账户为准。`\n : `Bundled adapter prices checked on ${date}; your provider account is authoritative.`\n}\nfunction testOkText(locale: 'zh' | 'en', sources: number): string {\n return locale === 'zh' ? `连接正常,返回 ${sources} 条来源。` : `Connection OK: ${sources} sources returned.`\n}\nfunction limitInvalidText(locale: 'zh' | 'en', label: string, min: number, max: number): string {\n return locale === 'zh' ? `${label}需为 ${min}–${max} 之间的整数。` : `${label} must be a whole number from ${min} to ${max}.`\n}\n\nconst LIMITS = [\n ['maxResults', 1, 20],\n ['maxQueries', 1, 5],\n ['timeoutMs', 1000, 120000],\n ['maxFetchChars', 1000, 200000],\n] as const\ntype LimitKey = (typeof LIMITS)[number][0]\n\n/** A limit field's raw text, or `undefined` when it is not a whole number in range. */\nexport function parseLimit(raw: string, min: number, max: number): number | undefined {\n const text = raw.trim()\n if (!/^\\d+$/u.test(text)) return undefined\n const value = Number(text)\n return value >= min && value <= max ? value : undefined\n}\n\n/**\n * Settings written by an immediate action (a switch or a reorder). It keeps the\n * saved limits and never includes typed-but-unsaved keys or limit drafts.\n */\nexport function immediateUpdate(\n settings: WebSettings,\n on: boolean,\n order: string[],\n providers?: readonly ProviderView[],\n remove: readonly string[] = [],\n kind: ProviderKind = 'search',\n): Omit<WebSettings, 'revision'> {\n let searchOrder = order\n if (providers) {\n const known = new Set(providers.filter(provider => provider.kind === kind).map(provider => provider.id))\n const removed = new Set(remove)\n const following = order.filter(id => !removed.has(id))\n if (following.some(id => !known.has(id))) searchOrder = following\n else {\n searchOrder = providerOrder(settings, kind).flatMap(id => {\n if (removed.has(id)) return []\n return known.has(id) ? following.splice(0, 1) : [id]\n })\n searchOrder.push(...following)\n }\n }\n return { ...editable(settings), ...(kind === 'search' ? { searchOrder, searchEnabled: on } : { fetchOrder: searchOrder, fetchEnabled: on }) }\n}\n\nfunction limitTexts(settings: WebSettings): Record<LimitKey, string> {\n return {\n maxResults: String(settings.maxResults), maxQueries: String(settings.maxQueries),\n timeoutMs: String(settings.timeoutMs), maxFetchChars: String(settings.maxFetchChars),\n }\n}\n\nfunction useHostLocale(client: Client): 'zh' | 'en' {\n const subscribe = useCallback((listener: () => void) => client.locale?.subscribe(listener) ?? (() => {}), [client])\n const snapshot = useCallback(() => client.locale?.getSnapshot().active ?? 'zh', [client])\n return String(useSyncExternalStore(subscribe, snapshot, snapshot)).startsWith('zh') ? 'zh' : 'en'\n}\n\n/** Keyboard reorders are batched: persist once the arrows stop for this long. */\nconst REORDER_DEBOUNCE_MS = 500\ntype PendingOrder = { kind: ProviderKind; order: string[] }\n\nexport function NetworkSearchSettings({ client, hidden }: { client: Client; hidden?: boolean }) {\n const locale = useHostLocale(client)\n const text: Copy = copy[locale]\n const tabsId = useId()\n const [tab, setTab] = useState<'search' | 'fetch' | 'limits'>('search')\n const [status, setStatus] = useState<WebStatus>()\n const [limitDraft, setLimitDraft] = useState<Record<LimitKey, string>>()\n const [invalid, setInvalid] = useState<Partial<Record<LimitKey, boolean>>>({})\n const [keys, setKeys] = useState<Record<string, string>>({})\n const [busy, setBusy] = useState(false)\n const [pendingOrder, setPendingOrder] = useState<PendingOrder | null>(null)\n const [dragSource, setDragSource] = useState<string | null>(null)\n const [dropTarget, setDropTarget] = useState<string | null>(null)\n const [sortAnnouncement, setSortAnnouncement] = useState<UiMessage>('')\n const [note, setNote] = useState<UiMessage>('')\n const [error, setError] = useState<UiMessage>('')\n const [loadError, setLoadError] = useState<UiMessage>('')\n const [writable, setWritable] = useState<Record<string, boolean>>({})\n const statusRef = useRef(status)\n statusRef.current = status\n const pendingRef = useRef<PendingOrder | null>(null)\n const dragRef = useRef<string | null>(null)\n const timerRef = useRef<ReturnType<typeof setTimeout>>(undefined)\n const handleRefs = useRef(new Map<string, HTMLButtonElement>())\n const refocusRef = useRef<string | null>(null)\n const busyRef = useRef(false)\n const hiddenRef = useRef(hidden)\n hiddenRef.current = hidden\n const requestSeq = useRef(0)\n const mountedRef = useRef(true)\n const readingRef = useRef(false)\n const readingRequest = useRef(0)\n const statusRead = useRef(Promise.resolve())\n const readGateRef = useRef<ReturnType<typeof createPluginReadGate>>(undefined)\n if (!readGateRef.current) readGateRef.current = createPluginReadGate()\n const readGate = readGateRef.current\n\n async function call<T>(endpoint: string, payload: unknown = {}): Promise<T> {\n return unwrap(await client.connection.rpc.call(WEB_SEARCH_RPC_CHANNEL, endpoint, payload) as RpcResult<T>)\n }\n function adopt(next: WebStatus, resetDraft = false) {\n const previous = statusRef.current\n statusRef.current = next\n setStatus(next)\n setLimitDraft(current => {\n const incoming = limitTexts(next.settings)\n if (!current || !previous || resetDraft) return incoming\n const baseline = limitTexts(previous.settings)\n return Object.fromEntries(LIMITS.map(([key]) =>\n [key, current[key] === baseline[key] ? incoming[key] : current[key]],\n )) as Record<LimitKey, string>\n })\n if (resetDraft) setInvalid({})\n }\n function pollBlocked(): boolean {\n return !canStartStatusRead({\n source: 'poll',\n mounted: mountedRef.current,\n hidden: isPageHidden(hiddenRef.current),\n busy: busyRef.current,\n reordering: pendingRef.current !== null || dragRef.current !== null,\n reading: readingRef.current,\n })\n }\n async function describeOwned(next: WebStatus, mine: number) {\n const refs = next.providers.flatMap(provider => ownsProviderConfiguration(provider) && provider.credentialRef ? [provider.credentialRef] : [])\n if (!refs.length) return\n const credentials = unwrap(await readGate.run(() => client.remote.credentials.describe(refs)))\n if (!mountedRef.current || mine !== requestSeq.current) return\n setWritable(Object.fromEntries(Object.entries(credentials).map(([ref, value]) => [ref, value.writable])))\n }\n async function load(resetDraft = false, source: 'user' | 'poll' = 'user') {\n if (source === 'poll' && !readGate.canRead()) return\n if (source === 'user') { readGate.reset(); requestSeq.current += 1 }\n while (readingRef.current) {\n if (source === 'poll' || !mountedRef.current) return\n await statusRead.current\n }\n if (!mountedRef.current) return\n if (source === 'poll' && pollBlocked()) return\n const mine = ++requestSeq.current\n readingRef.current = true\n readingRequest.current = mine\n let release = () => {}\n statusRead.current = new Promise<void>(resolve => { release = resolve })\n try {\n const next = unwrap(await readGate.run(() => client.connection.rpc.call(WEB_SEARCH_RPC_CHANNEL, 'status', {})) as RpcResult<WebStatus>)\n if (!mountedRef.current || mine !== requestSeq.current) return next\n if (source === 'poll' && !shouldApplyPolledStatus({\n requestId: mine, latestRequestId: requestSeq.current,\n busy: busyRef.current, reordering: pendingRef.current !== null || dragRef.current !== null,\n hidden: isPageHidden(hiddenRef.current),\n currentRevision: statusRef.current?.settings.revision,\n incomingRevision: next.settings.revision,\n })) return next\n adopt(next, resetDraft)\n setLoadError('')\n await describeOwned(next, mine)\n return next\n } catch (cause) {\n if (!mountedRef.current || mine !== requestSeq.current) return\n setLoadError(cause instanceof Error && cause.message ? cause.message : { key: 'failed' })\n if (source === 'poll') return\n throw cause\n } finally {\n if (readingRequest.current === mine) readingRef.current = false\n release()\n }\n }\n useEffect(() => {\n mountedRef.current = true\n readGate.reset()\n void load().catch(cause => setLoadError(cause instanceof Error && cause.message ? cause.message : { key: 'failed' }))\n const bind = () => bindStatusRefresh({\n isHidden: () => isPageHidden(hiddenRef.current),\n tick: async () => { await load(false, 'poll').catch(() => {}) },\n })\n let stop = bind()\n const stopConnection = client.connection.generation?.subscribe(() => {\n readGate.reset()\n requestSeq.current += 1\n readingRef.current = false\n statusRead.current = Promise.resolve()\n stop()\n stop = bind()\n void load(false, 'poll')\n })\n return () => {\n mountedRef.current = false\n readGate.reset()\n requestSeq.current += 1\n stop()\n stopConnection?.()\n if (timerRef.current === undefined) return\n clearTimeout(timerRef.current)\n const current = statusRef.current\n const next = pendingRef.current\n if (current && next) {\n void call('update', { settings: immediateUpdate(current.settings, next.kind === 'search' ? current.settings.searchEnabled : current.settings.fetchEnabled, next.order, current.providers, [], next.kind), expectedRevision: current.settings.revision }).catch(() => {})\n }\n }\n }, [client])\n\n useEffect(() => {\n if (hidden) return\n if (!statusRef.current) return\n void load(false, 'poll').catch(() => {})\n }, [hidden])\n\n // Keep keyboard focus on the handle that was just moved.\n useLayoutEffect(() => {\n const id = refocusRef.current\n if (!id) return\n refocusRef.current = null\n const handle = handleRefs.current.get(id)\n if (handle && document.activeElement !== handle) handle.focus()\n })\n\n async function action(run: () => Promise<void>) {\n if (busyRef.current) return\n readGate.reset()\n requestSeq.current += 1\n busyRef.current = true\n setBusy(true); setNote(''); setError('')\n try { await run() }\n catch (cause) { setError(cause instanceof UiError ? { key: cause.key } : cause instanceof Error && cause.message ? cause.message : { key: 'failed' }); await load().catch(() => {}) }\n finally { busyRef.current = false; setBusy(false) }\n }\n\n /** Switches and reorders: settings only, applied at once. */\n function runImmediate(on: boolean, order: string[], remove: readonly string[] = [], kind: ProviderKind = 'search') {\n clearTimeout(timerRef.current); timerRef.current = undefined\n pendingRef.current = null\n void action(async () => {\n const current = statusRef.current\n if (!current) return\n const next = await call<WebStatus>('update', {\n settings: immediateUpdate(current.settings, on, order, current.providers, remove, kind),\n expectedRevision: current.settings.revision,\n })\n adopt(next)\n await load()\n }).then(() => { if (pendingRef.current === null) setPendingOrder(null) })\n }\n\n /** Writes every typed key. Explicit actions only (Save, Save key, Test). */\n async function saveKeys(only?: ProviderView) {\n if (!status) return\n for (const provider of status.providers) {\n if (only && providerKey(provider.kind, provider.id) !== providerKey(only.kind, only.id)) continue\n const snapshot = keys[providerKey(provider.kind, provider.id)] ?? ''\n const typed = snapshot.trim()\n if (!ownsProviderConfiguration(provider) || !provider.credentialRef || !typed) continue\n if (writable[provider.credentialRef] === false) throw new UiError('readOnly')\n unwrap(await client.remote.credentials.set(provider.credentialRef, typed))\n setKeys(current => current[providerKey(provider.kind, provider.id)] === snapshot ? { ...current, [providerKey(provider.kind, provider.id)]: '' } : current)\n }\n }\n\n async function saveKeysForKind(kind: ProviderKind) {\n for (const provider of statusRef.current?.providers ?? []) if (provider.kind === kind) await saveKeys(provider)\n }\n\n async function removeKey(provider: ProviderView) {\n if (!ownsProviderConfiguration(provider) || !provider.credentialRef || provider.credentialShared || !status) return\n unwrap(await client.remote.credentials.unset(provider.credentialRef))\n setKeys(current => ({ ...current, [providerKey(provider.kind, provider.id)]: '' }))\n await load()\n setNote({ key: 'keyDeleted' })\n }\n\n if (!status || !limitDraft) {\n return <section className=\"web-search-settings ws-root dsh-ui-page\">\n {loadError\n ? <p role=\"alert\" className=\"dsh-ui-error\">{text.loadFailed}{uiMessage(loadError, locale, status?.providers)}</p>\n : <p role=\"status\" className=\"dsh-ui-hint\">{text.loading}</p>}\n {loadError ? <div className=\"dsh-ui-actions\"><Button variant=\"outline\" size=\"sm\" disabled={busy}\n onClick={() => void action(async () => { await load() })}>{text.reconnect}</Button></div> : null}\n </section>\n }\n\n const saved = status\n const savedLimits = limitTexts(saved.settings)\n const limitsDirty = LIMITS.some(([key]) => limitDraft[key] !== savedLimits[key])\n const keysDirty = Object.values(keys).some(value => value.trim())\n const reordering = pendingOrder !== null || dragSource !== null\n\n function move(kind: ProviderKind, id: string, nextIndex: number, keyboard = false) {\n if (pendingRef.current && pendingRef.current.kind !== kind) return\n const order = pendingRef.current?.order ?? providerOrder(statusRef.current!.settings, kind)\n const index = order.indexOf(id)\n if (busy || index < 0 || nextIndex < 0 || nextIndex >= order.length || index === nextIndex) return\n const next = [...order]\n const [item] = next.splice(index, 1)\n next.splice(nextIndex, 0, item)\n pendingRef.current = { kind, order: next }\n setPendingOrder({ kind, order: next })\n if (keyboard) refocusRef.current = providerKey(kind, id)\n setSortAnnouncement({ moved: id, kind, position: nextIndex + 1 })\n clearTimeout(timerRef.current)\n const delay = keyboard ? REORDER_DEBOUNCE_MS : 0\n timerRef.current = setTimeout(() => {\n timerRef.current = undefined\n const batched = pendingRef.current\n const current = statusRef.current\n if (batched && current) runImmediate(batched.kind === 'search' ? current.settings.searchEnabled : current.settings.fetchEnabled, batched.order, [], batched.kind)\n }, delay)\n }\n\n function endDrag() {\n dragRef.current = null\n setDragSource(null)\n setDropTarget(null)\n }\n\n function saveAll() {\n const parsed: Partial<Record<LimitKey, number>> = {}\n const nextInvalid: Partial<Record<LimitKey, boolean>> = {}\n const messages: LimitKey[] = []\n for (const [key, min, max] of LIMITS) {\n const value = parseLimit(limitDraft![key], min, max)\n if (value === undefined) { nextInvalid[key] = true; messages.push(key) }\n else parsed[key] = value\n }\n setInvalid(nextInvalid)\n if (messages.length) { setNote(''); setError({ invalid: messages }); return }\n void action(async () => {\n await saveKeys()\n const current = statusRef.current!\n const next = await call<WebStatus>('update', {\n settings: { ...editable(current.settings), ...parsed },\n expectedRevision: current.settings.revision,\n })\n adopt(next, true)\n await load()\n setNote({ key: 'saved' })\n })\n }\n\n return <section className=\"web-search-settings ws-root dsh-ui-page\" data-testid=\"web-search-settings\">\n <SegmentedTabs<'search' | 'fetch' | 'limits'>\n value={tab}\n onChange={setTab}\n label={text.tabsLabel}\n items={[\n { value: 'search', label: text.tabSearch, id: tabsId + '-search-tab', panelId: tabsId + '-search-panel' },\n { value: 'fetch', label: text.tabFetch, id: tabsId + '-fetch-tab', panelId: tabsId + '-fetch-panel' },\n { value: 'limits', label: text.tabLimits, id: tabsId + '-limits-tab', panelId: tabsId + '-limits-panel' },\n ]}\n />\n <span className=\"ws-sr-only\" role=\"status\" aria-live=\"polite\">{uiMessage(sortAnnouncement, locale, saved.providers)}</span>\n {error && <p role=\"alert\" className=\"dsh-ui-error\">{uiMessage(error, locale, saved.providers)}</p>}\n {loadError ? <div className=\"dsh-ui-stack\">\n <p role=\"alert\" className=\"dsh-ui-error\">{text.loadFailed}{uiMessage(loadError, locale, status?.providers)}</p>\n <Button variant=\"outline\" size=\"sm\" disabled={busy} onClick={() => void action(async () => { await load() })}>{text.reconnect}</Button>\n </div> : null}\n {note && <p role=\"status\" className=\"dsh-ui-notice\">{uiMessage(note, locale, saved.providers)}</p>}\n {saved.storageFailed && <p role=\"alert\" className=\"dsh-ui-banner dsh-ui-banner--danger\">{text.storageFailed}</p>}\n {(['search', 'fetch'] as const).map(kind => {\n const order = pendingOrder?.kind === kind ? pendingOrder.order : providerOrder(saved.settings, kind)\n const catalog = catalogBackends(saved, kind)\n const backends = [...order.flatMap(id => catalog.find(provider => provider.id === id) ?? []), ...catalog.filter(provider => !order.includes(provider.id))]\n const selected = selectedBackend(saved, kind)\n const on = kind === 'search' ? saved.settings.searchEnabled : saved.settings.fetchEnabled\n const canEnable = order.some(id => saved.providers.some(provider => provider.kind === kind && provider.id === id && provider.configured))\n const sortingOther = pendingOrder !== null && pendingOrder.kind !== kind\n const locked = busy || sortingOther || Boolean(dragSource && !dragSource.startsWith(kind + ':'))\n const title = kind === 'search' ? text.toolTitle : text.fetchTitle\n return <div key={kind} role=\"tabpanel\"\n id={tabsId + '-' + kind + '-panel'} aria-labelledby={tabsId + '-' + kind + '-tab'}\n hidden={tab !== kind} tabIndex={0}>\n {backends.length === 0 ? (kind === 'search' ? <p className=\"dsh-ui-empty\">{text.empty}</p> : null) : <article\n className=\"web-search-card dsh-ui-card dsh-ui-card--flat\"\n data-testid={kind === 'search' ? 'web-search-tool' : 'web-search-fetch'}\n data-on={on ? 'true' : 'false'}>\n <header className=\"ws-card-head\">\n <div>\n <h3 className=\"dsh-ui-title\">{title}</h3>\n {!canEnable && <p className=\"dsh-ui-hint\">{kind === 'search' ? text.needBackend : text.needFetchBackend}</p>}\n </div>\n <Switch\n checked={on}\n label={title}\n disabled={locked || dragSource !== null || (!on && !canEnable)}\n onChange={next => runImmediate(next, order, [], kind)}\n />\n </header>\n <ol className=\"web-search-rank\" aria-label={kind === 'search' ? text.backends : text.fetchBackends}>\n {backends.map(provider => {\n const participating = order.includes(provider.id)\n const rank = order.indexOf(provider.id)\n const dormant = !isRegistered(saved, provider.id, kind)\n const active = !dormant && (kind === 'search' ? saved.searchActive : saved.fetchActive) && selected?.id === provider.id\n const info = kind === 'search' || (ownsProviderConfiguration(provider) && provider.id === 'firecrawl') ? searchProviderInfo(provider, locale) : {\n description: ownsProviderConfiguration(provider) && provider.id === 'http-managed'\n && provider.description === '优先匿名 Jina Reader 读取,失败后本机 HTTP 直连;无需 Key。' && locale === 'en'\n ? 'Reads with anonymous Jina Reader first, then direct HTTP if needed; no key required.' : provider.description,\n pricing: provider.pricing ?? '', pricingUrl: provider.pricingUrl,\n }\n const owned = ownsProviderConfiguration(provider)\n const access = configurationAccess(provider)\n const needsKey = owned && Boolean(provider.credentialRef)\n const shared = Boolean(provider.credentialShared)\n const showKey = !dormant && needsKey && participating && (!shared || !provider.configured)\n const canSort = participating && order.length > 1\n const readOnly = writable[provider.credentialRef ?? ''] === false\n const typedKey = (keys[providerKey(provider.kind, provider.id)] ?? '').trim()\n const availability = dormant ? 'dormant' : provider.configured ? 'available' : 'unavailable'\n return <li key={provider.id} className={active ? 'is-active' : undefined} data-testid={`web-${kind}-rank-${provider.id}`}\n data-availability={availability}\n data-in-use={active || undefined}\n data-dragging={dragSource === providerKey(kind, provider.id) || undefined}\n data-drop={dropTarget === providerKey(kind, provider.id) && dragSource ? (order.indexOf(dragSource.slice(kind.length + 1)) < rank ? 'after' : 'before') : undefined}\n onDragOver={event => {\n if (locked || !canSort || !dragSource?.startsWith(kind + ':') || dragSource === providerKey(kind, provider.id)) return\n event.preventDefault()\n event.dataTransfer.dropEffect = 'move'\n setDropTarget(providerKey(kind, provider.id))\n }}\n onDragLeave={event => {\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) setDropTarget(null)\n }}\n onDrop={event => {\n if (locked || !canSort || !dragSource?.startsWith(kind + ':')) return\n event.preventDefault()\n move(kind, dragSource.slice(kind.length + 1), rank)\n endDrag()\n }}>\n {canSort ? <Button variant=\"ghost\" size=\"sm\" className=\"web-search-drag-handle\"\n ref={(element: HTMLButtonElement | null) => {\n if (element) handleRefs.current.set(providerKey(kind, provider.id), element)\n else handleRefs.current.delete(providerKey(kind, provider.id))\n }}\n icon={<svg width=\"16\" height=\"20\" viewBox=\"0 0 16 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <circle cx=\"5\" cy=\"5\" r=\"1.5\" /><circle cx=\"11\" cy=\"5\" r=\"1.5\" />\n <circle cx=\"5\" cy=\"10\" r=\"1.5\" /><circle cx=\"11\" cy=\"10\" r=\"1.5\" />\n <circle cx=\"5\" cy=\"15\" r=\"1.5\" /><circle cx=\"11\" cy=\"15\" r=\"1.5\" />\n </svg>}\n draggable={!locked} aria-disabled={locked}\n aria-label={text.dragPrefix + providerDisplayLabel(provider, locale)}\n title={text.dragHint}\n onDragStart={event => {\n if (locked || (pendingRef.current && pendingRef.current.kind !== kind)) { event.preventDefault(); return }\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData(`application/x-dsh-${kind}-provider`, provider.id)\n const row = event.currentTarget.closest('li')\n if (row) event.dataTransfer.setDragImage(row, 16, 16)\n dragRef.current = providerKey(kind, provider.id)\n requestSeq.current += 1\n setDragSource(providerKey(kind, provider.id))\n setSortAnnouncement('')\n }}\n onDragEnd={endDrag}\n onKeyDown={event => {\n if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') return\n event.preventDefault()\n move(kind, provider.id, rank + (event.key === 'ArrowUp' ? -1 : 1), true)\n }} /> : <span className=\"web-search-rank-index\">{participating ? rank + 1 : '—'}</span>}\n <div className=\"web-search-provider\">\n <div className=\"web-search-provider-heading\">\n <strong className=\"dsh-ui-heading\">{providerDisplayLabel(provider, locale)}</strong>\n <span className=\"ws-provider-note\">\n <StateDot state={dormant ? 'idle' : provider.configured ? 'done' : 'warning'} />\n <span>{dormant ? text.dormant : provider.configured ? text.available : text.unavailable}</span>\n </span>\n {active && <Tag tone=\"success\">{text.inUse}</Tag>}\n {provider.signupUrl && <AppLink url={provider.signupUrl} label={providerDisplayLabel(provider, locale) + ' ' + text.signup}>{text.signup}</AppLink>}\n </div>\n <div className=\"web-search-provider-info\">\n {info.description ? <p>{info.description}</p> : null}\n {info.pricing || info.pricingUrl ? <p>{info.pricing}{info.pricingUrl && <> <AppLink url={info.pricingUrl}\n label={providerDisplayLabel(provider, locale) + ' ' + text.pricing}>{provider.id === 'ddg' ? text.website : text.pricing}</AppLink></>}</p> : null}\n </div>\n {access.kind === 'link' ? <p className=\"dsh-ui-hint\"><AppLink url={access.url} label={text.openConfig}>{text.openConfig}</AppLink></p>\n : access.kind === 'owner' ? <p className=\"dsh-ui-hint\">{text.ownerSettings}{access.name}</p>\n : access.kind === 'unknown' ? <p className=\"dsh-ui-hint\">{text.unknownOwner}</p>\n : null}\n {shared && participating && owned ? <small className=\"dsh-ui-meta\">{provider.credentialHint === '与模型设置共用 Key'\n ? locale === 'zh' ? provider.credentialHint : 'Shares a key with model settings'\n : owned && provider.id === 'firecrawl' && locale === 'en' ? 'Search and page reads share a Firecrawl key'\n : provider.credentialHint ?? text.sharedKey}</small> : null}\n {showKey ? <label className=\"dsh-ui-field\">\n <span className=\"dsh-ui-label\">API Key</span>\n <Input className=\"ws-input\" type=\"password\" autoComplete=\"off\" spellCheck={false}\n disabled={busy || reordering || readOnly}\n value={keys[providerKey(provider.kind, provider.id)] ?? ''}\n placeholder={provider.configured ? text.keyConfigured : text.keyPlaceholder}\n onChange={event => setKeys(current => ({ ...current, [providerKey(provider.kind, provider.id)]: event.target.value }))} />\n </label> : null}\n {showKey || (needsKey && !shared && provider.configured) ? <div className=\"dsh-ui-actions\">\n {showKey ? <Button variant=\"outline\" size=\"sm\" disabled={busy || reordering || readOnly || !typedKey}\n onClick={() => void action(async () => { await saveKeys(provider); await load(); setNote({ key: 'keySaved' }) })}>\n {text.saveKey}</Button> : null}\n {needsKey && !shared && provider.configured ? <ConfirmButton\n disabled={busy || reordering || readOnly}\n label={text.deleteKey} confirmLabel={text.confirmDelete}\n onConfirm={() => void action(() => removeKey(provider))} /> : null}\n </div> : null}\n </div>\n <div className=\"web-search-rank-move\">\n <Switch\n checked={participating}\n label={providerDisplayLabel(provider, locale)}\n disabled={locked || dragSource !== null}\n onChange={next => {\n const following = next ? [...order, provider.id] : order.filter(id => id !== provider.id)\n runImmediate(\n on && following.some(id => saved.providers.some(item => item.kind === kind && item.id === id && item.configured)),\n following,\n droppedDormantIds(saved.settings, following, saved.providers, kind),\n kind,\n )\n }} />\n </div>\n </li>\n })}\n </ol>\n {kind === 'search' && backends.some(usesBundledPricing) ? <p className=\"dsh-ui-hint\">{pricingCheckedText(locale, PROVIDER_PRICING_CHECKED)}</p> : null}\n {on && kind === 'search' ? <div className=\"dsh-ui-actions\">\n <Button variant=\"outline\" size=\"md\" disabled={busy || reordering} onClick={() => void action(async () => {\n // Testing is explicit, so typed keys are saved first and the request uses them.\n await saveKeysForKind('search')\n const result = await call<{ sources: number }>('test')\n await load()\n setNote({ sources: result.sources })\n })}>{text.test}</Button>\n </div> : null}\n </article>}\n </div>\n })}\n <div role=\"tabpanel\" id={tabsId + '-limits-panel'} aria-labelledby={tabsId + '-limits-tab'}\n hidden={tab !== 'limits'} tabIndex={0}>\n <fieldset className=\"dsh-ui-fields-grid\" disabled={busy || reordering} aria-label={text.tabLimits}>\n {LIMITS.map(([key, min, max]) => (\n <label key={key} className=\"dsh-ui-field\">\n <span className=\"dsh-ui-label\">{text[key]}</span>\n <Input className=\"ws-input\" type=\"number\" min={min} max={max} step={1} value={limitDraft[key]}\n aria-invalid={invalid[key] || undefined}\n onChange={event => {\n const raw = event.target.value\n setLimitDraft(current => current ? { ...current, [key]: raw } : current)\n setInvalid(current => ({ ...current, [key]: false }))\n }}\n onBlur={() => setInvalid(current => ({ ...current, [key]: parseLimit(limitDraft[key], min, max) === undefined }))} />\n {invalid[key] ? <span className=\"dsh-ui-error\">{limitInvalidText(locale, text[key], min, max)}</span> : null}\n </label>\n ))}\n </fieldset>\n </div>\n <div className=\"dsh-ui-form-footer dsh-ui-actions-end\">\n <span className=\"dsh-ui-meta\">{limitsDirty || keysDirty ? text.unsaved : text.saveHint}</span>\n <Button variant=\"primary\" size=\"md\" disabled={busy || reordering} onClick={saveAll}>{text.save}</Button>\n </div>\n </section>\n}\n\nexport function apply(ctx: Context): void {\n const client = ctx as unknown as Client\n ctx.effect(() => {\n if (typeof document === 'undefined') return () => {}\n const style = document.createElement('style')\n style.setAttribute('data-plugin', '@klarkxy/dsh-web-search-manager'); style.textContent = webSearchClientStyles\n style.dataset.dshWebSearch = ''; document.head.appendChild(style)\n return () => style.remove()\n }, 'web-search-manager.styles')\n ctx.effect(() => client.slots.inject('plugins.bundle.config', () => client.slots.register({\n name: 'plugins.bundle.config', key: '@klarkxy/dsh-web-search-manager',\n }, (props?: { hidden?: boolean }) => <NetworkSearchSettings client={client} hidden={props?.hidden} />)), 'web-search-manager.settings')\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.inner.cjs","names":["createElement","useState","useRef","Button","useCallback","useSyncExternalStore","useId","SegmentedTabs","Switch","StateDot","Tag","Input","IconGlobeOutlineRegular"],"sources":["../src/client-error.ts","../../dsh-plugin-kit/src/official-ui.ts","../src/client-styles.ts","../../dsh-plugin-kit/src/contracts.ts","../../dsh-plugin-kit/src/client-utils.ts","../src/contracts.ts","../src/provider-info.ts","../src/client.tsx"],"sourcesContent":["/** Local adapter diagnostics only; third-party descriptions remain verbatim. */\nconst localErrors: Record<string, string> = {\n '网络搜索设置格式无效。': 'Invalid web search settings format.',\n '未知操作。': 'Unknown operation.',\n '网络设置操作失败,请刷新后重试。': 'The network settings operation failed. Refresh and try again.',\n '查询须为 1 至 4000 字符。': 'The query must contain 1 to 4,000 characters.',\n '网络搜索插件已停止。': 'The web search plugin has stopped.',\n '请在设置 → 网络搜索中配置凭据并启用。': 'Configure credentials and enable web search in Settings → Web search.',\n '网络访问设置已改变,请重新发起请求。': 'Network access settings changed. Start the request again.',\n '网络请求已取消。': 'The network request was cancelled.',\n '网络请求已取消或超时。': 'The network request was cancelled or timed out.',\n '设置已被其他页面修改,请刷新后重试。': 'Settings changed in another page. Refresh and try again.',\n '供应商不支持自定义地址。': 'The provider does not support a custom address.',\n '供应商地址须为无凭据、无查询参数的 HTTPS 地址。': 'The provider address must use HTTPS without credentials or query parameters.',\n '需选择已安装的供应商并填写有效凭据。': 'Choose an installed provider and enter valid credentials.',\n '保存失败,本次运行已暂停网络访问以防意外联网。': 'Saving failed. Network access is paused for this run to prevent unintended requests.',\n '供应商未提供错误描述。': 'The provider did not supply an error description.',\n '供应商请求失败。': 'Provider request failed.',\n '搜索凭据已删除或不可用,请重新配置。': 'Search credentials were deleted or are unavailable. Configure them again.',\n '供应商配置不可用。': 'Provider configuration is unavailable.',\n '查询无效。': 'Invalid query.',\n '结果数须为 1 至 20。': 'The result count must be from 1 to 20.',\n '搜索已取消。': 'Search cancelled.',\n 'Tavily 搜索已取消。': 'Tavily search cancelled.',\n '响应格式无效。': 'Invalid response shape',\n '响应缺失。': 'Missing response',\n '响应过大。': 'Response too large',\n '地址无效。': 'Invalid endpoint',\n}\n\nexport function localErrorText(message: string, locale: 'zh' | 'en'): string {\n const prefix = message.match(/^(\\[(?:search|fetch):[^\\]]+\\] )(.*)$/s)\n if (prefix) return prefix[1] + localErrorText(prefix[2], locale)\n if (locale === 'zh') {\n const known = Object.entries(localErrors).find(([, english]) => english === message)\n return known?.[0] ?? message\n }\n if (localErrors[message]) return localErrors[message]\n const http = message.match(/^供应商请求失败(HTTP (\\d{3}))。$/)\n if (http) return `Provider request failed (HTTP ${http[1]}).`\n const config = message.match(/^(brave|bocha|firecrawl|serper|Tavily) 配置不可用。$/)\n if (config) return `${config[1]} configuration is unavailable.`\n return message\n}\n","/**\n * Shared contract that keeps every plugin's browser-side UI on the official\n * DeepSeek Harness Web design language.\n *\n * Two rules produce that language, and both are load-bearing here:\n *\n * 1. Colour, radius, elevation and focus come from the host theme's real\n * `--dsw-*` tokens, written under their own names. Plugins never invent a\n * token, never alias the official ones behind a private palette (a private\n * alias is just a second, diverging theme), and never carries a literal\n * fallback — the host publishes light and dark, and a literal would pin one\n * of them. The only non-colour custom properties used are the host's own\n * content-font axis and frame clearance.\n *\n * 2. Anything the host already ships is used, not reimplemented. Controls come\n * from `@deepseek-ai/dsh-client-ui-primitives` (`Button`, `Input`,\n * `Checkbox`, `Switch`, `Tag`, `Pill`, `SegmentedControl`, `Menu`, `Modal`,\n * `Tooltip`, `Toast`, `DisclosureRow`, `StateDot`, `PathLabel`, the settings\n * form suite). This module only styles what primitives do not cover: page\n * scaffolding, field layout, banners, empty states and scroll regions.\n *\n * The metrics below are transcribed from the primitives' own CSS modules so a\n * feature panel lines up pixel-for-pixel with a native one: the 14/22 body and\n * 13/20 compact type tiers, the 0.5px hairline scale, and the elevation family\n * that draws a surface's edge in its shadow (`border: 0`) instead of a border.\n */\n\n/**\n * Every `--dsw-*` token this repo is allowed to reference, grouped by role.\n *\n * Plugins may only colour through this list. It is exported so the guard test\n * can fail a build when a stylesheet reaches for anything else — the failure\n * mode this prevents is silent: an unknown token resolves to nothing, so a\n * border disappears or a label inherits the wrong colour with no error anywhere.\n */\nexport const OFFICIAL_THEME_TOKENS = {\n /** Surfaces, from the page up to the topmost nested card. */\n surface: [\n '--dsw-alias-bg-base',\n '--dsw-alias-bg-layer-1',\n '--dsw-alias-bg-layer-2',\n '--dsw-alias-bg-layer-3',\n '--dsw-alias-bg-layer-4',\n '--dsw-alias-bg-module-platform',\n '--dsw-alias-bg-overlay',\n ],\n /** Hairlines. l1 is near-invisible on a menu surface, so menus use l2+. */\n border: ['--dsw-alias-border-l1', '--dsw-alias-border-l2', '--dsw-alias-border-l3', '--dsw-alias-border-l4'],\n /** Text. `caption` is menu shortcut text, `dimmed` is placeholder text. */\n label: [\n '--dsw-alias-label-primary',\n '--dsw-alias-label-secondary',\n '--dsw-alias-label-tertiary',\n '--dsw-alias-label-caption',\n '--dsw-alias-label-dimmed',\n '--dsw-alias-label-error',\n '--dsw-alias-label-primary-foreground',\n ],\n /** Interactive fills. The `-danger` hover belongs to destructive rows. */\n interactive: [\n '--dsw-alias-interactive-bg-hover',\n '--dsw-alias-interactive-bg-active',\n '--dsw-alias-interactive-bg-hover-danger',\n ],\n /** Accent and status. `business` is the focus ring's default colour. */\n accent: [\n '--dsw-alias-brand-primary',\n '--dsw-alias-button-primary-fill',\n '--dsw-alias-button-primary-hover',\n '--dsw-alias-button-tool-bar-fill',\n '--dsw-alias-button-tool-bar-hover',\n '--dsw-alias-button-ghost-active-fill',\n '--dsw-alias-button-ghost-active-border',\n ],\n state: [\n '--dsw-alias-state-business-primary',\n '--dsw-alias-state-success-primary',\n '--dsw-alias-state-warn-primary',\n '--dsw-alias-state-error-primary',\n '--dsw-alias-state-idle-primary',\n ],\n /** Corner scale: sm 8, md 12, lg the large card, panel the modal. */\n radius: ['--dsw-radius-sm', '--dsw-radius-md', '--dsw-radius-lg', '--dsw-radius-panel'],\n /** An elevated surface draws its edge in the shadow, so it keeps border: 0. */\n elevation: [\n '--dsw-elevation-soft',\n '--dsw-elevation-stroke',\n '--dsw-elevation-stroke-color',\n '--dsw-elevation-panel',\n '--dsw-elevation-prominent',\n ],\n /** Floats, masks and scrollbars. */\n overlay: [\n '--dsw-menu-surface-fill',\n '--dsw-menu-backdrop-filter',\n '--dsw-mask-blur',\n '--dsw-alias-bg-mask-1',\n '--dsw-alias-menu-icon',\n '--dsw-alias-scrollbar-bg-l2',\n '--dsw-alias-scrollbar-hover-l2',\n '--dsw-shadow-lv3',\n ],\n /** Motion, focus and type axis, published by the host rather than by a component. */\n system: [\n '--dsw-focus-ring-width',\n '--dsw-focus-ring-color',\n '--dsw-font-markdown-code-font-family',\n '--ds-transition-duration',\n '--ds-ease-in-out',\n '--ds-font-family-code',\n '--dsh-scrollbar-thumb',\n '--dsh-scrollbar-thumb-hover',\n '--dsh-content-font-size-secondary',\n '--dsh-content-font-delta',\n '--dsh-frame-top-clearance',\n ],\n} as const\n\n/** Every official token name, flat. Used by the token guard test. */\nexport const OFFICIAL_THEME_TOKEN_NAMES: readonly string[] = Object.values(OFFICIAL_THEME_TOKENS).flat()\n\n/** The elevation steps a surface can be raised by, softest first. */\nexport type OfficialElevation = 'soft' | 'stroke' | 'panel' | 'prominent'\n\n/**\n * The host's focus ring shorthand, verbatim. Writing this per control is how\n * rings drift apart; a stylesheet that draws its own focus style should use\n * {@link officialFocus} instead. The colour fallback is the host's own default.\n */\nexport const OFFICIAL_FOCUS_RING =\n 'var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary))'\n\n/**\n * A complete `outline` declaration at the official geometry. `offset` defaults\n * to the 2px the native controls use; the host's inline field rules sit at 1px\n * and a segmented tab pulls the ring inside itself at -2px.\n */\nexport function officialFocus(offset = '2px'): string {\n return `outline: ${OFFICIAL_FOCUS_RING}; outline-offset: ${offset}`\n}\n\n/**\n * An elevated surface. The host draws a raised surface's edge inside its\n * shadow rather than as a border, so a card sets `border: 0` and takes the edge\n * from the elevation — which is also why its border stays off the alias-border\n * set that a flat, non-elevated rule would use.\n */\nexport function officialElevation(level: OfficialElevation): string {\n return `border: 0; box-shadow: var(--dsw-elevation-${level})`\n}\n\n/**\n * An elevated surface whose edge should read as a specific hairline. Rebinding\n * the stroke colour is how a menu card picks l1 while a panel picks l3, without\n * the two growing apart by hand.\n */\nexport function officialStrokedElevation(level: OfficialElevation, color: string): string {\n return `--dsw-elevation-stroke-color: ${color}; ${officialElevation(level)}`\n}\n\n/**\n * The shared stylesheet for a plugin UI, scoped under that plugin's root class.\n *\n * Scoping is what keeps this safe to always-bundle into every client: the rules\n * can only match inside the plugin's own subtree, so two plugins can both mount\n * `dsh-ui-card` without either styling the other's markup.\n *\n * Each recipe matches the root element *and* its descendants, because the\n * commonest arrangement is one element that is both — a surface that carries\n * the root class and `dsh-ui-card`, or a portaled dialog that is the root and\n * the `dsh-ui-surface`. A descendant-only scope would silently leave those\n * unstyled, with no error anywhere.\n *\n * A caveat worth knowing: `Modal` and `Menu` portal to `document.body`, so their\n * content leaves the plugin's subtree entirely. A portaled surface therefore\n * needs its own root class on an element inside the portal.\n *\n * Only patterns that primitives do not provide live here. If a control appears\n * below that `Button`/`Input`/`Switch`/`Tag`/`Menu` already covers, that is a\n * bug in this file: use the primitive and delete the recipe.\n */\nexport function officialUiCss(roots: string | readonly string[]): string {\n // One plugin often mounts several unrelated surfaces (a settings seat, a chat\n // card, a header menu), so the caller names every root. The rules below are\n // nested under a single selector list and use `&`, which the browser expands\n // per root — so one stylesheet covers all of them without duplicating itself.\n const scope = (typeof roots === 'string' ? [roots] : roots).map(root => `.${root}`).join(', ')\n const sheet = `\n/* Official DSH Web UI contract. Generated by @klarkxy/dsh-plugin-kit/official-ui.\n * Do not hand-edit the copy inside a plugin: change this module so every plugin\n * moves together. Scope: ${scope} */\n/* The scope rule below stays open for the whole sheet: every rule after it\n * nests with the parent selector, so one stylesheet can cover several unrelated\n * roots without leaking into a neighbouring plugin. Its closing brace is last. */\n${scope} {\n color: var(--dsw-alias-label-primary);\n font-family: inherit;\n font-size: 14px;\n line-height: 22px;\n min-width: 0;\n\n& *, & *::before, & *::after { box-sizing: border-box; }\n& h1, & h2, & h3, & h4, & p, & figure { margin: 0; }\n/* Keyboard focus only: pointer input keeps the native resting appearance. The\n * ring needs no radius of its own — the outline already follows the control's\n * own border-radius, and overriding it here would flatten every control. */\n& :focus-visible { ${officialFocus()}; }\n& [hidden] { display: none !important; }\n\n/* --- Type tiers. The same 14/22 and 13/20 steps the primitives use. --- */\n& .dsh-ui-title { font-size: 16px; line-height: 24px; font-weight: 500; color: var(--dsw-alias-label-primary); }\n& .dsh-ui-heading { font-size: 14px; line-height: 22px; font-weight: 500; color: var(--dsw-alias-label-primary); }\n& .dsh-ui-compact { font-size: 13px; line-height: 20px; color: var(--dsw-alias-label-primary); }\n& .dsh-ui-meta { font-size: 12px; line-height: 18px; color: var(--dsw-alias-label-secondary); }\n& .dsh-ui-hint { font-size: 12px; line-height: 18px; color: var(--dsw-alias-label-tertiary); }\n& .dsh-ui-error { font-size: 12px; line-height: 18px; color: var(--dsw-alias-state-error-primary); }\n& .dsh-ui-warn { color: var(--dsw-alias-state-warn-primary); }\n& .dsh-ui-success { color: var(--dsw-alias-state-success-primary); }\n& .dsh-ui-muted { color: var(--dsw-alias-label-tertiary); }\n& .dsh-ui-truncate { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n& .dsh-ui-wrap { min-width: 0; overflow-wrap: anywhere; }\n\n/* --- Layout scaffolding. --- */\n& .dsh-ui-stack { display: flex; flex-direction: column; gap: 12px; }\n& .dsh-ui-stack-lg { display: flex; flex-direction: column; gap: 20px; }\n/* Plugin detail pages share one reading column. These are layout recipes;\n * controls keep their own primitive styles. Form spacing follows the host's\n * settings-form/fields.module.css and SettingsForm.module.css. */\n& .dsh-ui-page { display: flex; flex-direction: column; gap: 16px; width: 100%; max-width: 760px; min-width: 0; }\n& .dsh-ui-form { display: flex; flex-direction: column; gap: 0; min-width: 0; }\n&.dsh-ui-form > .dsh-ui-field, & .dsh-ui-form > .dsh-ui-field { padding: 12px 0; }\n& .dsh-ui-form-footer { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; padding-top: 16px; min-width: 0; }\n& .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; }\n@media (max-width: 560px) {\n & .dsh-ui-fields-grid { grid-template-columns: minmax(0, 1fr); }\n}\n& .dsh-ui-row { display: flex; align-items: center; gap: 8px; min-width: 0; }\n& .dsh-ui-row-wrap { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }\n& .dsh-ui-spacer { flex: 1; min-width: 0; }\n/* A 0.5px l2 rule is the host's section divider; reserve the same rhythm. */\n& .dsh-ui-divider { height: 0.5px; background: var(--dsw-alias-border-l2); }\n& .dsh-ui-section { display: flex; flex-direction: column; gap: 12px; padding-top: 16px; border-top: 0.5px solid var(--dsw-alias-border-l2); }\n\n/* --- Card: a raised surface whose edge lives in the shadow. --- */\n& .dsh-ui-card {\n ${officialElevation('stroke')}\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 16px;\n border-radius: var(--dsw-radius-lg);\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n min-width: 0;\n}\n& .dsh-ui-card--flat { border: 0.5px solid var(--dsw-alias-border-l3); box-shadow: none; }\n& .dsh-ui-card--nested { background: var(--dsw-alias-bg-layer-2); }\n& .dsh-ui-card--plain { background: none; }\n\n/* --- Field: label, control, help, error. The control itself is an\n * official primitive, so this only owns the vertical rhythm. --- */\n& .dsh-ui-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }\n& .dsh-ui-field + .dsh-ui-field { border-top: 0.5px solid var(--dsw-alias-border-l2); padding-top: 12px; }\n/* Grid fields share a row, so the stacked-field divider must not win later in\n * the cascade. Reset it here, after the field recipe. */\n& .dsh-ui-fields-grid > .dsh-ui-field { padding: 0; border: 0; }\n& .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); }\n& .dsh-ui-label-row { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }\n& .dsh-ui-help { font-size: 12px; line-height: 1.6; color: var(--dsw-alias-label-secondary); }\n& .dsh-ui-help p { margin: 0; }\n& .dsh-ui-help p + p { margin-top: 8px; }\n& .dsh-ui-required { color: var(--dsw-alias-state-error-primary); }\n& .dsh-ui-control { width: 100%; min-width: 0; }\n/* An invalid control is marked on the control, not only in the message. */\n& .dsh-ui-control[aria-invalid='true'] { border-color: var(--dsw-alias-state-error-primary); }\n& .dsh-ui-readonly { display: flex; flex-direction: column; gap: 4px; min-width: 0; }\n& .dsh-ui-readonly > dt { font-size: 12px; line-height: 18px; color: var(--dsw-alias-label-tertiary); }\n& .dsh-ui-readonly > dd { margin: 0; font-size: 13px; line-height: 20px; overflow-wrap: anywhere; }\n\n/* --- Buttons row. A no-op container: it exists so actions line up on the\n * trailing edge the way every native panel does. --- */\n& .dsh-ui-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding-top: 4px; }\n& .dsh-ui-actions-end { justify-content: flex-end; }\n\n/* --- Banners. A banner is a left-rail notice, the shape used for warnings,\n * deprecations and blocking instructions. --- */\n& .dsh-ui-banner {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 10px 14px;\n border: 0.5px solid var(--dsw-alias-border-l3);\n border-left: 3px solid var(--dsw-alias-state-warn-primary);\n border-radius: var(--dsw-radius-md);\n background: var(--dsw-alias-bg-layer-1);\n font-size: 13px;\n line-height: 20px;\n color: var(--dsw-alias-label-secondary);\n}\n& .dsh-ui-banner-title { font-weight: 500; color: var(--dsw-alias-state-warn-primary); }\n& .dsh-ui-banner--info { border-left-color: var(--dsw-alias-state-business-primary); }\n& .dsh-ui-banner--danger { border-left-color: var(--dsw-alias-state-error-primary); }\n& .dsh-ui-banner--success { border-left-color: var(--dsw-alias-state-success-primary); }\n& .dsh-ui-banner p { margin: 0; }\n\n/* --- Inline notice: a transient confirmation with no chrome of its own. --- */\n& .dsh-ui-notice { display: flex; align-items: center; gap: 6px; font-size: 12px; line-height: 18px; color: var(--dsw-alias-state-success-primary); }\n& .dsh-ui-notice--error { color: var(--dsw-alias-state-error-primary); }\n\n/* --- Empty and loading states. Both sit in a dashed well so an empty panel\n * reads as \"nothing here yet\" rather than as a broken layout. --- */\n& .dsh-ui-empty {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 6px;\n padding: 20px 16px;\n border: 0.5px dashed var(--dsw-alias-border-l3);\n border-radius: var(--dsw-radius-md);\n font-size: 13px;\n line-height: 20px;\n color: var(--dsw-alias-label-secondary);\n}\n& .dsh-ui-loading { display: flex; align-items: center; gap: 8px; padding: 20px 0; font-size: 13px; line-height: 20px; color: var(--dsw-alias-label-secondary); }\n\n/* --- Selectable list. A row is a button-sized target with a hover fill and\n * a selected fill; the name carries the weight, the description does not. --- */\n& .dsh-ui-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; min-width: 0; }\n& .dsh-ui-list-scroll { max-height: min(66vh, 760px); overflow-y: auto; overflow-x: hidden; }\n& .dsh-ui-list-row { border-radius: var(--dsw-radius-md); }\n& .dsh-ui-list-row:hover { background: var(--dsw-alias-interactive-bg-hover); }\n& .dsh-ui-list-row[aria-selected='true'], & .dsh-ui-list-row[data-selected] { background: var(--dsw-alias-interactive-bg-active); }\n& .dsh-ui-list-item {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n width: 100%;\n min-width: 0;\n padding: 10px 12px;\n border: 0;\n border-radius: var(--dsw-radius-md);\n background: none;\n font: inherit;\n color: inherit;\n text-align: start;\n cursor: pointer;\n}\n& .dsh-ui-list-item:disabled { cursor: default; }\n& .dsh-ui-list-item:disabled:hover { background: none; }\n& .dsh-ui-list-name { font-size: 14px; line-height: 22px; font-weight: 500; color: var(--dsw-alias-label-primary); overflow-wrap: anywhere; }\n& .dsh-ui-list-desc { font-size: 12px; line-height: 18px; color: var(--dsw-alias-label-tertiary); }\n\n/* --- Code and structured output. Wraps rather than scrolls, because a\n * feature panel is read rather than scanned. --- */\n& .dsh-ui-code {\n padding: 10px 12px;\n border: 0.5px solid var(--dsw-alias-border-l3);\n border-radius: var(--dsw-radius-md);\n background: var(--dsw-alias-bg-layer-1);\n font-family: var(--ds-font-family-code);\n font-size: 12px;\n line-height: 20px;\n color: var(--dsw-alias-label-secondary);\n overflow-wrap: anywhere;\n white-space: pre-wrap;\n}\n& .dsh-ui-scroll { max-height: 60vh; overflow: auto; overscroll-behavior: contain; }\n\n/* --- A toggle row: label on the left, switch on the trailing edge. This is\n * layout only; the switch itself is the official primitive. --- */\n& .dsh-ui-toggle-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; min-width: 0; padding: 12px 0; }\n& .dsh-ui-toggle-row + .dsh-ui-toggle-row { border-top: 0.5px solid var(--dsw-alias-border-l2); }\n& .dsh-ui-toggle-text { display: flex; flex-direction: column; gap: 4px; min-width: 0; }\n& .dsh-ui-toggle-label { font-size: 14px; line-height: 22px; font-weight: 500; color: var(--dsw-alias-label-primary); }\n\n/* --- Floating surface: a panel anchored to a trigger. This is the material\n * a menu card is made of — the translucent fill plus its backdrop blur, with\n * the edge riding inside the prominent elevation. A plugin that hand-rolls this\n * is how tokens like --dsw-specific-menu get invented; use this instead. --- */\n& .dsh-ui-surface {\n --dsw-elevation-stroke-color: var(--dsw-alias-border-l1);\n --dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2);\n --dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2);\n position: relative;\n display: flex;\n flex-direction: column;\n border: 0;\n border-radius: var(--dsw-radius-lg);\n background: var(--dsw-menu-surface-fill);\n backdrop-filter: var(--dsw-menu-backdrop-filter);\n box-shadow: var(--dsw-elevation-prominent);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n line-height: 20px;\n min-width: 0;\n}\n& .dsh-ui-surface-body { display: flex; flex-direction: column; gap: 8px; padding: 12px 14px 14px; overflow-y: auto; }\n& .dsh-ui-surface-head { display: flex; align-items: center; gap: 8px; min-width: 0; }\n& .dsh-ui-surface-title { font-size: 14px; line-height: 22px; font-weight: 500; color: var(--dsw-alias-label-primary); }\n\n/* --- Native select. The primitives ship no select, so a plugin that offers a\n * closed list of choices keeps the platform control and matches the official\n * settings field geometry exactly (34px, 0.5px l4, radius-md, layer-3). A\n * SegmentedControl is the primitive to reach for when the choices are few\n * enough to show at once. --- */\n& .dsh-ui-select {\n box-sizing: border-box;\n width: 100%;\n min-width: 0;\n height: 34px;\n padding: 0 12px;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: var(--dsw-radius-md);\n background: var(--dsw-alias-bg-layer-3);\n font: inherit;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n}\n& .dsh-ui-select:focus-visible { outline: none; border-color: var(--dsw-alias-state-business-primary); }\n& .dsh-ui-select:disabled { color: var(--dsw-alias-label-tertiary); cursor: default; }\n& .dsh-ui-select[aria-invalid='true'] { border-color: var(--dsw-alias-state-error-primary); }\n\n/* --- Native textarea. The primitives ship no textarea either, so a settings\n * prompt keeps the platform element and the same field shell as the select:\n * 0.5px l4, radius-md, layer-3, 13/1.5 type. It grows vertically instead of\n * locking to the select's 34px row. --- */\n& .dsh-ui-textarea {\n box-sizing: border-box;\n width: 100%;\n min-width: 0;\n resize: vertical;\n padding: 8px 12px;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: var(--dsw-radius-md);\n background: var(--dsw-alias-bg-layer-3);\n font: inherit;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n}\n& .dsh-ui-textarea:focus-visible { outline: none; border-color: var(--dsw-alias-state-business-primary); }\n& .dsh-ui-textarea:disabled { color: var(--dsw-alias-label-tertiary); cursor: default; }\n& .dsh-ui-textarea::placeholder { color: var(--dsw-alias-label-dimmed); }\n& .dsh-ui-textarea[aria-invalid='true'] { border-color: var(--dsw-alias-state-error-primary); }\n\n/* Long panels scroll inside themselves instead of growing the host page. */\n& .dsh-ui-panel { display: flex; flex-direction: column; gap: 16px; min-width: 0; }\n& .dsh-ui-panel-wide { max-width: 100%; }\n\n@media (prefers-reduced-motion: reduce) {\n & *, & *::before, & *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }\n}\n}\n`\n // Widen every recipe from \"a descendant of the root\" to \"the root, or one of\n // its descendants\". Written as a transform rather than by hand so no recipe\n // can be added later with only the descendant form. The pattern is narrow on\n // purpose: a leading `&` followed by one recipe class and no combinator, so\n // sibling and child rules such as `.dsh-ui-field + .dsh-ui-field` keep their\n // own meaning and are left alone.\n return sheet.replace(/^& (\\.dsh-ui-[a-z0-9-]+)(?=\\s*\\{)/gm, (_match, className: string) =>\n `&${className}, & ${className}`)\n}\n","import { officialUiCss } from '@klarkxy/dsh-plugin-kit/official-ui'\n\n/**\n * The shared contract paints this page: the type tiers, the card, the field,\n * the button row, the notice, the focus ring and the state colours. What is\n * left here is only the geometry the contract does not cover — the reading\n * width of the panel, the ranked three-column row, the drag affordance, the\n * drop indicator, the screen-reader-only announcer and the limits grid.\n *\n * Nothing below declares a colour of its own beyond naming a host token, and\n * nothing carries a literal fallback: the host publishes both themes and a\n * literal would pin one of them.\n *\n * Every plugin rule nests under the plugin root, as the contract does, so a\n * .ws-* or .web-search-* class never paints a neighbouring plugin. Selectors\n * that start with an element name keep an explicit `&`, the form every\n * nesting implementation accepts.\n */\nexport const webSearchClientStyles = `${officialUiCss('ws-root')}\n.ws-root {\n\n.ws-card-head { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding-bottom: 8px; }\n.ws-sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n}\n.ws-provider-note { display: flex; align-items: center; gap: 6px; }\n.web-search-provider { display: grid; gap: 8px; min-width: 0; }\n.web-search-provider-info { display: grid; gap: 3px; font-size: 12px; line-height: 18px; color: var(--dsw-alias-label-secondary); overflow-wrap: anywhere; }\n.web-search-provider-heading { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; min-height: 24px; }\n& a { color: var(--dsw-alias-state-business-primary); font-size: 12px; line-height: 18px; text-underline-offset: 3px; }\n/* The primitive renders a 32px control in an inline-flex wrapper; a settings\n * field needs it to fill the column it sits in. */\n.ws-input { width: 100%; }\n.web-search-rank { margin: 0; padding: 0; list-style: none; }\n.web-search-rank li {\n position: relative;\n display: grid;\n grid-template-columns: 24px minmax(0, 1fr) auto;\n gap: 10px;\n align-items: start;\n padding: 14px 0;\n border-top: 0.5px solid var(--dsw-alias-border-l2);\n}\n& button.web-search-drag-handle {\n display: grid;\n place-items: center;\n width: 24px;\n height: 24px;\n min-height: 24px;\n padding: 0;\n cursor: grab;\n opacity: 0.65;\n}\n& button.web-search-drag-handle:hover { opacity: 1; }\n& button.web-search-drag-handle:active { cursor: grabbing; }\n& button.web-search-drag-handle[aria-disabled='true'] { cursor: wait; opacity: 0.4; }\n.web-search-rank li[data-dragging] { opacity: 0.45; }\n.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; }\n.web-search-rank li[data-drop='before']::after { top: -1px; }\n.web-search-rank li[data-drop='after']::after { bottom: -1px; }\n.web-search-rank-index { color: var(--dsw-alias-label-tertiary); font-variant-numeric: tabular-nums; line-height: 24px; }\n.web-search-rank-move { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; justify-content: flex-end; padding-top: 2px; }\n.ws-danger:hover:not(:disabled),\n.ws-danger[data-armed] { color: var(--dsw-alias-state-error-primary); border-color: var(--dsw-alias-state-error-primary); }\n.ws-danger:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover-danger); }\n@media (max-width: 560px) {\n .web-search-rank-move { flex-direction: column; align-items: flex-end; }\n}\n}\n`\n","import type { ContextFormed } from '@deepseek-ai/dsh-llm'\nimport { modelRouteOverride, type ModelRoute } from '@klarkxy/dsh-model-route'\n\n/**\n * The canonical home is @klarkxy/dsh-model-route; re-exported here so existing\n * plugin-kit consumers keep compiling.\n */\nexport type { ModelRoute }\n\n/** Public, browser-safe contracts. Host implementations stay in their owning packages. */\nexport type ProducerSourceKind = `plugin:${string}`\nexport type ProducerMessageSource = { kind: ProducerSourceKind; plugin: string } & ContextFormed\n\n/**\n * V4 sessions persist the producing extension in `kind`; the retired generic\n * `plugin` wrapper is not admitted. Each feature plugin declares its own kind.\n */\ndeclare module '@deepseek-ai/dsh-llm' {\n interface MessageSourceMap {\n [kind: ProducerSourceKind]: ProducerMessageSource\n }\n}\n\nexport function producerMessageSource(plugin: string): ProducerMessageSource {\n return { kind: `plugin:${plugin}`, plugin }\n}\n\nexport type RpcResult<T = unknown> = { ok: true; value: T } | { ok: false; error: { code: string; message: string } }\nexport const CHAT_EVENTS_SLOT = 'dsh-editor.chat.events'\nexport interface EvidenceRef { sessionId: string; seq: number; kind: 'user' | 'tool' | 'turn' | 'manual'; excerpt?: string }\nexport interface TaskContract {\n id: string\n sessionId: string\n sourceVersion: string\n revision: number\n goal: string\n deliverables: string[]\n inScope: string[]\n outOfScope: string[]\n constraints: string[]\n acceptance: string[]\n assumptions: string[]\n questions: string[]\n evidence: EvidenceRef[]\n readiness: 'pending' | 'clear-request' | 'user-confirmed' | 'disclosed-assumptions' | 'cancelled' | 'stale'\n updatedAt: number\n}\nexport interface TaskCheckpoint {\n id: string\n sessionId: string\n sourceVersion: string\n contractVersion?: number\n fromSeq: number\n toSeq: number\n revision: number\n status: 'running' | 'completed' | 'failed' | 'cancelled'\n items: Array<{ label: string; state: 'pending' | 'verified' | 'failed'; evidence: EvidenceRef[] }>\n constraints: string[]\n nextAction: string\n createdAt: number\n}\nexport type KnowledgeScope = { kind: 'global' } | { kind: 'project'; projectId: string }\nexport type KnowledgeKind = 'preference' | 'project-fact' | 'decision' | 'vocabulary' | 'activity' | 'lesson'\n/** Descriptive context, never an executable instruction or authorization. */\nexport interface ContextKnowledge {\n subject: string\n domain: string\n key: string\n aliases: string[]\n observedAt: number\n /** The user's own time expression, not an inferred completion date. */\n eventTime?: string\n activityStatus?: 'planned' | 'in-progress' | 'blocked' | 'paused' | 'completed' | 'cancelled' | 'unknown'\n}\n/** A scoped method. Observations remain candidates until explicitly accepted. */\nexport interface ProcedureKnowledge {\n origin: 'instruction' | 'observation'\n goal: string\n when: string[]\n steps: string[]\n avoid: string[]\n verify: string[]\n}\nexport interface MemoryRecord {\n id: string\n revision: number\n scope: KnowledgeScope\n kind: KnowledgeKind\n status: 'candidate' | 'active' | 'rejected' | 'superseded' | 'revoked' | 'deleted'\n title: string\n content: string\n tags: string[]\n evidence: EvidenceRef[]\n exceptions: string[]\n source: 'user' | 'memory' | 'dream' | 'self-improvement'\n context?: ContextKnowledge\n procedure?: ProcedureKnowledge\n createdAt: number\n updatedAt: number\n expiresAt?: number\n supersedes?: string[]\n /** Exact record revisions behind a derived candidate; revalidated on adoption. */\n basis?: Array<{ id: string; revision: number }>\n}\nexport type NewMemoryRecord = Omit<MemoryRecord, 'id' | 'revision' | 'createdAt' | 'updatedAt'>\nexport interface MemoryQuery { scope: KnowledgeScope; query?: string; kinds?: KnowledgeKind[]; statuses?: MemoryRecord['status'][]; limit?: number }\nexport interface MemoryMutationOptions { signal?: AbortSignal; isCurrent?: () => boolean }\nexport interface MemoryService {\n list(query: MemoryQuery): Promise<MemoryRecord[]>\n create(record: NewMemoryRecord, options?: MemoryMutationOptions): Promise<MemoryRecord>\n update(id: string, patch: Partial<Pick<MemoryRecord, 'title' | 'content' | 'tags' | 'exceptions' | 'status' | 'expiresAt'>>, expectedRevision: number, options?: MemoryMutationOptions): Promise<MemoryRecord>\n /** Explicit user promotion, persisted atomically on the same record; preserves expiry and evidence. */\n promoteToGlobal(id: string, expectedRevision: number, options?: MemoryMutationOptions): Promise<MemoryRecord>\n remove(id: string, expectedRevision: number, options?: MemoryMutationOptions): Promise<void>\n recall(query: MemoryQuery): Promise<MemoryRecord[]>\n}\n\n/**\n * The session creation metadata the host snapshots when it creates a delegated child\n * thread. Mirrors `CreateAgentOptions.meta` in `@deepseek-ai/dsh-agent`; it is durable\n * session data, so it survives a restart and needs no ancestry walk.\n */\nexport interface SubagentSessionLike {\n readonly header?: {\n readonly parentSession?: unknown\n readonly origin?: unknown\n readonly delegationDepth?: unknown\n readonly isSeeded?: unknown\n readonly classification?: unknown\n }\n}\n\n/**\n * True when `session` is a delegated child thread rather than a human-driven top-level\n * conversation.\n *\n * A message's `source.kind === 'user'` cannot answer this: a lead agent's dispatch prompt\n * is delivered as the child's own user message, so delegated work is indistinguishable\n * from a person typing once you only look at the message. Plugins use this to keep\n * automatic writes and derived model calls on top-level sessions.\n */\nexport function isSubagentSession(session: unknown): boolean {\n const header = (session as SubagentSessionLike | null | undefined)?.header\n if (!header || typeof header !== 'object') return false\n if (header.origin === 'subagent' || Number(header.delegationDepth ?? 0) > 0) return true\n // Native human forks also persist parentSession for history lineage. A fork's\n // seeded header distinguishes it from legacy children whose parent is the\n // only marker. Native rc.2 forks omit depth; patched hosts may persist zero.\n // Explicit delegation and unknown metadata still keep the child guard.\n const humanFork = header.isSeeded === true\n && (header.delegationDepth === undefined || header.delegationDepth === 0)\n && header.origin === undefined\n && (header.classification === undefined || header.classification === 'ordinary' || header.classification === 'free')\n return Boolean(header.parentSession) && !humanFork\n}\n\n/** Project keys come only from the host-validated session directory, never client input. */\nexport function projectIdFromCwd(cwd: string | undefined): string | undefined {\n if (typeof cwd !== 'string') return undefined\n const normalized = cwd.trim().replaceAll('\\\\', '/')\n if (!normalized) return undefined\n return normalized === '/' || /^[A-Za-z]:\\/$/.test(normalized) ? normalized : normalized.replace(/\\/+$/, '')\n}\n\n/**\n * One settings owner's Remote stream. Typert accepts either case in an RPC\n * segment, so the Cordis service key and the wire namespace use the same\n * Pascal segment. The scope's first letter stays uppercase.\n */\nexport interface ModelSettingsChangesIdentity {\n readonly service: string\n readonly namespace: string\n}\n\n/** `@klarkxy/dsh-git-commit` becomes `KlarkxyDshGitCommitModelSettingsChanges`. */\nexport function modelSettingsChangesIdentity(packageName: string): ModelSettingsChangesIdentity {\n const scoped = /^@([^/]+)\\/(.+)$/.exec(packageName)\n const body = scoped ? `${scoped[1]}/${scoped[2]}` : packageName\n const parts = body.split(/[^A-Za-z0-9]+/).filter(Boolean)\n if (!parts.length) throw new Error('Model settings package name has no RPC segment')\n const segment = `${parts.map(part => part.charAt(0).toUpperCase() + part.slice(1)).join('')}ModelSettingsChanges`\n if (!/^[A-Z][A-Za-z0-9]*$/.test(segment)) throw new Error(`Model settings sync name is not an RPC segment: ${segment}`)\n return { service: segment, namespace: segment }\n}\n\n/**\n * Repository extension: a native web provider announces that its local\n * availability changed. Kind is `search` or `fetch`; id is the provider id.\n * Listeners refresh management state and must not treat this as a network probe.\n */\nexport const WEB_PROVIDER_AVAILABILITY_EVENT = 'web/provider-availability-updated' as const\n\n/**\n * Optional display metadata a provider may attach when it registers directly\n * with `ctx.web`. Execution and credentials stay with the provider; this object\n * is display-only and must never carry secret values.\n */\nexport interface DshWebManagementDescriptor {\n readonly id: string\n readonly label: string\n readonly description: string\n readonly defaultBaseURL?: string\n readonly credentialRef?: string\n /** True when the key is shared with another surface. The page must not delete it. */\n readonly credentialShared?: boolean\n /** Optional notice naming the settings surface that owns a shared credential. */\n readonly credentialHint?: string\n readonly billing: 'request' | 'model-and-tools' | 'none' | 'unknown'\n /** Optional provider-supplied billing summary. */\n readonly pricing?: string\n /** Public HTTPS page explaining provider charges. */\n readonly pricingUrl?: string\n /** Public HTTPS page where the user can create an API key. */\n readonly signupUrl?: string\n /** Plugin that owns configuration. Native providers keep their settings there. */\n readonly configurationOwner?: string\n /** Explicit settings link supplied by the provider, never inferred from its id. */\n readonly configurationUrl?: string\n /** Fixed legacy route migrated by a bundle-owned adapter; not used by native plugins. */\n readonly legacyId?: string\n}\n\n/**\n * Host Creator agent-preset id (`agentPresets.composedPreset`).\n * Plugins that gate Creator-only tools or prompt sections compare against this.\n */\nexport const CREATOR_PRESET_ID = 'cordis'\n\n/**\n * Shared \"Session Management\" settings section id. The section is provided by\n * shared infrastructure (see `registerSessionSettingsItem` in `client-utils`),\n * not by any one business plugin: every participant registers only a row into\n * {@link SESSION_SETTINGS_ITEM_SLOT}, so any enabled subset yields the same page.\n */\nexport const SESSION_SETTINGS_SECTION_ID = 'session-manager'\n\n/**\n * Child list slot declared by the shared Session Management page. Neutral\n * naming on purpose: participants never import each other, they only\n * contribute `{id, order}` rows into this slot while the page is alive.\n */\nexport const SESSION_SETTINGS_ITEM_SLOT = 'dsh.session-settings.item'\n\n/**\n * Host-owned nav icon seat: a root-scoped list where each entry's `id` must\n * equal the `settings.section` id it decorates. Owner props are\n * `{size, active}`; icons stay `aria-hidden` so the section label remains the\n * accessible name. Contributors into a shared section may register the same\n * icon `id` with different priorities — the ledger keeps every entry while the\n * host renders one icon per section.\n */\nexport const SETTINGS_SECTION_ICON_SLOT = 'settings.section.icon'\n\n/* Shared auxiliary-model default. Feature plugins may store a global default\n * route that fills the tier between their own saved override and the live\n * session model. The durable state is a root-context singleton (like the\n * shared session-settings page): it exists while any participant retains it\n * and is never owned by a business plugin. See `auxiliary-model.ts`. */\n\n/** Root context service key holding the shared auxiliary-model state. */\nexport const AUXILIARY_MODEL_SERVICE = 'dshAuxiliaryModel'\n\n/** Authenticated host-RPC channel for get/set of the shared default. */\nexport const AUXILIARY_MODEL_CHANNEL = '/dsh-auxiliary-model'\n\n/** Model-settings namespace under which the shared default is published. */\nexport const AUXILIARY_MODEL_NS = '@klarkxy/dsh-plugin-kit/auxiliary-model'\n\n/**\n * Typert identity of the shared change stream. Mounted on the root context\n * with ownerNs filtering, so frames only carry {@link AUXILIARY_MODEL_NS}.\n */\nexport const AUXILIARY_MODEL_CHANGES_IDENTITY = modelSettingsChangesIdentity('@klarkxy/dsh-plugin-kit')\n\n/** The value the shared default persists: `model: null` means follow the live session/host model. */\nexport interface AuxiliaryModelDefaults {\n readonly revision: number\n readonly model: ModelRoute | null\n}\n\n/** Shape of the root singleton; produced and owned by `auxiliary-model.ts`. */\nexport interface AuxiliaryModelShared {\n readonly ready: Promise<void>\n snapshot(): AuxiliaryModelDefaults\n}\n\nfunction sharedAuxiliaryModel(host: unknown): AuxiliaryModelShared | undefined {\n const root = (host as { root?: unknown } | undefined)?.root as { get?(name: string, strict?: boolean): unknown } | undefined\n try {\n const shared = root?.get?.(AUXILIARY_MODEL_SERVICE, false) as AuxiliaryModelShared | undefined\n return shared && typeof shared.snapshot === 'function' ? shared : undefined\n } catch { return undefined }\n}\n\n/**\n * Synchronously read the shared auxiliary default; `undefined` only when no\n * participant retains the singleton or the default is legitimately unset.\n * An initialization or snapshot failure propagates: silently falling back to\n * a different model after a storage error would spend the wrong budget.\n */\nexport function getAuxiliaryModel(host: unknown): ModelRoute | undefined {\n const shared = sharedAuxiliaryModel(host)\n if (!shared) return undefined\n return modelRouteOverride(shared.snapshot().model ?? undefined)\n}\n","import type { Context } from '@deepseek-ai/cordis'\nimport { createElement, useCallback, useEffect, useRef, useSyncExternalStore, type ReactNode } from 'react'\nimport { SESSION_SETTINGS_ITEM_SLOT, SESSION_SETTINGS_SECTION_ID, SETTINGS_SECTION_ICON_SLOT } from './contracts.ts'\n\n/** Fixed pause for failing background reads; manual work is never replayed. */\nexport function createPluginReadGate(now: () => number = Date.now) {\n let pausedUntil = 0\n let epoch = 0\n return {\n canRead: () => now() >= pausedUntil,\n reset: () => { epoch++; pausedUntil = 0 },\n async run<T>(read: () => Promise<T>): Promise<T> {\n const current = epoch\n try {\n const value = await read()\n if (current === epoch) pausedUntil = 0\n return value\n } catch (error) {\n // The native Connection error is an Error with this exact HTTP text.\n // Authentication, application failures, and cancellation stay distinct.\n const message = error instanceof Error ? error.message : ''\n if (current === epoch && (/(?:^|\\s)transport failure for .+: HTTP (?:404|405|503)\\b/.test(message)\n || (error instanceof TypeError && /^(?:Failed to fetch|fetch failed|NetworkError when attempting to fetch resource\\.?)$/i.test(message)))) {\n pausedUntil = now() + 30_000\n }\n throw error\n }\n },\n }\n}\n\nexport interface NativeSurfaceClient {\n sessions: {\n binding?(sessionId: string): { eventSource: { subscribe(listener: () => void): () => void } } | undefined\n }\n uiWorkspace: { current?: { getSnapshot(): { sessionId: string } | undefined; subscribe(listener: () => void): () => void } }\n uiSession: { adapter: { current: { getSnapshot(): { key?: string }; subscribe(listener: () => void): () => void } } }\n locale: { getSnapshot(): { active: string }; subscribe(listener: () => void): () => void }\n connection?: { generation?: { subscribe(listener: () => void): () => void } }\n}\n\n/** The Editor retains its manuscript session separately; native DSH exposes the main-view binding. */\nexport function selectedSessionId(client: Pick<NativeSurfaceClient, 'uiWorkspace' | 'uiSession'>): string {\n const workspace = client.uiWorkspace?.current\n return workspace\n ? workspace.getSnapshot()?.sessionId ?? ''\n : client.uiSession.adapter.current.getSnapshot().key ?? ''\n}\n\n/**\n * Owner props are data the host composed, never a Cordis context: reading an\n * undeclared property off one throws instead of returning undefined, which\n * would crash the slot entry and abdicate its cell for good. Tolerate a\n * throwing read so a mistaken prop degrades to \"not supplied\".\n */\nfunction read(source: unknown, key: string): unknown {\n if (source === null || typeof source !== 'object') return undefined\n try {\n return (source as Record<string, unknown>)[key]\n } catch {\n return undefined\n }\n}\n\nfunction readString(source: unknown, key: string): string | undefined {\n const value = read(source, key)\n return typeof value === 'string' ? value : undefined\n}\n\n/** Owner props win; native settings only supplies close, so resolve its selected session. */\nexport function useNativeSeat(client: NativeSurfaceClient, props?: unknown) {\n const current = useSyncExternalStore(\n useCallback((fn: () => void) => (client.uiWorkspace?.current ?? client.uiSession.adapter.current).subscribe(fn), [client]),\n useCallback(() => selectedSessionId(client), [client]),\n () => '',\n )\n const language = useSyncExternalStore(\n useCallback((fn: () => void) => client.locale.subscribe(fn), [client]),\n useCallback(() => client.locale.getSnapshot().active, [client]),\n () => 'en',\n )\n const sessionId = readString(props, 'sessionId') ?? readString(read(props, 'owner'), 'sessionId') ?? current\n const requestedLocale = read(props, 'locale') ?? read(read(props, 'owner'), 'locale') ?? language\n return { sessionId, locale: (String(requestedLocale).startsWith('zh') ? 'zh' : 'en') as 'zh' | 'en',\n hidden: read(props, 'hidden') === true || read(read(props, 'owner'), 'hidden') === true }\n}\n\n/** Refresh on native log boundaries/reconnect/focus. Read-only settling is bounded to an observed job. */\nexport function useFeatureRefresh(\n client: NativeSurfaceClient, sessionId: string, refresh: () => void,\n working = false, enabled = true,\n): void {\n const refreshRef = useRef(refresh)\n refreshRef.current = refresh\n useEffect(() => {\n if (!enabled) return\n let timer: ReturnType<typeof setTimeout> | undefined\n const queue = () => {\n if (typeof document !== 'undefined' && document.visibilityState === 'hidden') return\n clearTimeout(timer)\n timer = setTimeout(() => refreshRef.current(), 150)\n }\n const source = sessionId ? client.sessions.binding?.(sessionId)?.eventSource : undefined\n const stopEvents = source?.subscribe(queue)\n const stopConnection = client.connection?.generation?.subscribe(queue)\n const visibility = () => { if (document.visibilityState === 'visible') queue() }\n window.addEventListener('focus', queue)\n document.addEventListener('visibilitychange', visibility)\n // Details are collapsed by default; opening an existing panel must get current state.\n document.addEventListener('toggle', queue, true)\n return () => {\n clearTimeout(timer)\n stopEvents?.(); stopConnection?.()\n window.removeEventListener('focus', queue)\n document.removeEventListener('visibilitychange', visibility)\n document.removeEventListener('toggle', queue, true)\n }\n }, [client, sessionId, enabled])\n useEffect(() => {\n if (!enabled || !working) return\n let delay = 250\n const until = Date.now() + 300_000\n let timer: ReturnType<typeof setTimeout>\n const tick = () => {\n if (Date.now() >= until) return\n if (document.visibilityState !== 'hidden') refreshRef.current()\n delay = Math.min(delay * 2, 2000)\n timer = setTimeout(tick, delay)\n }\n timer = setTimeout(tick, delay)\n return () => clearTimeout(timer)\n }, [client, sessionId, working, enabled])\n}\n\n/* Shared \"Session Management\" settings page. The page is root-owned shared\n * infrastructure, not a business plugin: any participant alone yields the same\n * section, and participants never import each other. Synchronization crosses\n * separately bundled copies of this helper through the Cordis root context —\n * `root.get`/`root.provide` is the only rendezvous, so no window global or\n * module singleton is needed and no participant depends on another being enabled.\n */\n\n/** A row contributed into the shared Session Management page. */\nexport interface SessionSettingsItemSpec {\n readonly id: string\n readonly order: number\n}\n\n/* Original line icons for settings-section navigation seats. The official\n * `dsh-client-ui-primitives` icon set covers generic glyphs only; these keep\n * the same conventions (16×16 viewBox, 1px stroke, round caps, currentColor)\n * so the plugin set stays visually consistent with the native nav. Colors\n * never appear here: `currentColor` lets the host style the active state, and\n * `aria-hidden` keeps the section label the single accessible name. */\n\n/** Owner props the host's `settings.section.icon` seat hands to its cell. */\nexport interface SettingsSectionIconProps {\n readonly size?: number\n /** Selection state; the nav styles active icons through color, not geometry. */\n readonly active?: boolean\n}\n\ntype IconChild = readonly [tag: string, attrs: Record<string, string | number>]\n\n/** One `<svg>` with the shared icon chrome; children are plain elements. */\nfunction lineIcon(children: IconChild[]) {\n return function SettingsSectionIcon(props: SettingsSectionIconProps): ReactNode {\n return createElement('svg', {\n 'aria-hidden': 'true',\n width: props.size ?? 16,\n height: props.size ?? 16,\n viewBox: '0 0 16 16',\n fill: 'none',\n stroke: 'currentColor',\n strokeWidth: 1,\n strokeLinecap: 'round',\n strokeLinejoin: 'round',\n }, ...children.map(([tag, attrs], index) => createElement(tag, { key: index, ...attrs })))\n }\n}\n\n/** Chat bubble for the shared Session Management section. */\nexport const IconSessionSettings = lineIcon([\n ['path', { d: 'M8.5 13a5.5 5.5 0 0 0 5.5-5.5A5.5 5.5 0 0 0 8.5 2 5.5 5.5 0 0 0 3 7.5a5.48 5.48 0 0 0 .9 3L2.8 12.6a.5.5 0 0 0 .62.62l2.9-.98a5.48 5.48 0 0 0 2.18.76Z' }],\n])\n\n/** Serif-flavoured letter A for the Font section. */\nexport const IconFontSettings = lineIcon([\n ['path', { d: 'M3.3 12.6 7.9 3.1a.5.5 0 0 1 .9 0l4.6 9.5' }],\n ['path', { d: 'M5.2 9.4h6.3' }],\n ['path', { d: 'M2.4 12.6h2.4' }],\n ['path', { d: 'M11.9 12.6h2.4' }],\n])\n\n/** Keyboard for the Shortcuts section. */\nexport const IconKeyboardSettings = lineIcon([\n ['rect', { x: 1.75, y: 4.75, width: 12.5, height: 7.5, rx: 1.2 }],\n ['path', { d: 'M4.5 7.25h0' }],\n ['path', { d: 'M8 7.25h0' }],\n ['path', { d: 'M11.5 7.25h0' }],\n ['path', { d: 'M5.25 9.75h5.5' }],\n])\n\n/** Linked model nodes for the Model Management section. */\nexport const IconModelNetworkSettings = lineIcon([\n ['line', { x1: 10.6, y1: 5.3, x2: 8.8, y2: 7.1 }],\n ['line', { x1: 7.2, y1: 8.9, x2: 5.4, y2: 10.7 }],\n ['circle', { cx: 12, cy: 4, r: 1.9 }],\n ['circle', { cx: 4, cy: 12, r: 1.9 }],\n ['circle', { cx: 8, cy: 8, r: 1.5 }],\n])\n\n/**\n * Contribute the nav icon of a `settings.section` entry. The `settings\n * .section.icon` seat is declared by the host independently of the section\n * list, so the wait runs in parallel: the icon appears as soon as both seats\n * exist and unloading removes it again, restoring the host's default glyph.\n * The `id` must equal the section id; `order`/`priority` mirror `order` so the\n * icon cell sorts like its section and, for shared sections, several\n * contributors can register the same icon id under different priorities\n * without duplicating nav entries.\n */\nexport function registerSettingsSectionIcon(\n ctx: Context,\n sectionId: string,\n order: number,\n component: (props: SettingsSectionIconProps) => ReactNode,\n): () => void {\n const slots = (ctx as unknown as { slots: SessionSettingsSlots }).slots\n return slots.inject(SETTINGS_SECTION_ICON_SLOT, () =>\n slots.register({ name: SETTINGS_SECTION_ICON_SLOT, id: sectionId, order, priority: order }, component))\n}\n\ninterface SessionSettingsSlots {\n inject(key: string, callback: () => Iterable<(() => void) | void> | (() => void) | void): () => void\n register(spec: Record<string, unknown>, render: unknown): () => void\n}\n\ninterface SessionSettingsRoot {\n get(name: string, strict?: boolean): unknown\n provide(name: string, value: unknown): () => void\n readonly slots?: SessionSettingsSlots\n}\n\n/** Private Cordis rendezvous; every bundled copy of this helper resolves the same root service. */\nconst SESSION_SETTINGS_PAGE_SERVICE = 'dshSessionSettingsPage'\n\ninterface SessionSettingsPage {\n retain(): void\n release(): void\n}\n\n/** The page component renders only its declared child list: items paint their own titles and styles. */\nfunction SessionSettingsPageView(props: { renderSlot: (key: string, owner: Record<string, never>) => ReactNode }): ReactNode {\n return props.renderSlot(SESSION_SETTINGS_ITEM_SLOT, {})\n}\n\n/**\n * Acquire the root-owned page service, creating and providing it on first use.\n * One counter covers participants, the section wait and the provider: the last\n * release removes the wait and then the provider synchronously (Cordis deletes\n * the impl before its async disposer awaits), so a later acquire sees a clean\n * get() miss and can re-establish the page.\n */\nfunction acquireSessionSettingsPage(root: SessionSettingsRoot): SessionSettingsPage {\n const existing = root.get(SESSION_SETTINGS_PAGE_SERVICE, false) as SessionSettingsPage | undefined\n if (existing) return existing\n const slots = root.slots as SessionSettingsSlots\n let users = 0\n let stopPage: (() => void) | undefined\n let unprovide: (() => void) | undefined\n const page: SessionSettingsPage = {\n retain() {\n // The reservation is counted before the throwing install, so a failure\n // lets the caller's release() undo it fully instead of leaking the service.\n users += 1\n if (!stopPage) {\n stopPage = slots.inject('settings.section', () => slots.register(\n {\n name: 'settings.section',\n id: SESSION_SETTINGS_SECTION_ID,\n order: 30,\n label: () => {\n const locale = root.get('locale', false) as { getSnapshot?(): { active?: unknown } } | undefined\n return String(locale?.getSnapshot?.().active ?? '').startsWith('zh') ? '会话管理' : 'Session Management'\n },\n children: { [SESSION_SETTINGS_ITEM_SLOT]: { kind: 'list', scope: 'root' } },\n },\n SessionSettingsPageView,\n ))\n }\n },\n release() {\n if (users <= 0) return\n users -= 1\n if (users > 0) return\n // The last contributor tears the section down; collapsing its children\n // declaration clears the item slot for every other bundle as well.\n stopPage?.()\n stopPage = undefined\n unprovide?.()\n },\n }\n unprovide = root.provide(SESSION_SETTINGS_PAGE_SERVICE, page)\n return page\n}\n\n/**\n * Contribute one row to the shared Session Management settings page.\n *\n * `ctx` is the plugin's own context: the row's slot wait unloads with the\n * plugin, while the page itself is retained on the shared root context for as\n * long as any contributor lives. There is no fallback seat — when every\n * contributor is gone the section disappears with the page. Returns a disposer.\n * Shared root-owned infrastructure (the auxiliary-model card) calls this on\n * `ctx.root` so its row, icon and page share are owned by the root lifetime\n * and released by the returned disposer, not by any plugin fiber.\n */\nexport function registerSessionSettingsItem(\n ctx: Context,\n item: SessionSettingsItemSpec,\n component: (props: never) => ReactNode,\n): () => void {\n const slots = (ctx as unknown as { slots: SessionSettingsSlots }).slots\n const root = ctx.root as unknown as SessionSettingsRoot\n const page = acquireSessionSettingsPage(root)\n let stopItem: (() => void) | undefined\n let stopIcon: (() => void) | undefined\n try {\n page.retain()\n stopItem = slots.inject(SESSION_SETTINGS_ITEM_SLOT, () => slots.register({ name: SESSION_SETTINGS_ITEM_SLOT, ...item }, component))\n // Every contributor also waits on the nav-icon seat, registering the same\n // section icon id under its own priority (its row order). Same-id entries\n // coexist on the ledger while the host renders one icon, so no contributor\n // owns the icon and none depends on another for it; the wait lives with\n // this ctx, so it fires now on fresh loads and retro-fills on HMR.\n stopIcon = slots.inject(SETTINGS_SECTION_ICON_SLOT, () => slots.register(\n { name: SETTINGS_SECTION_ICON_SLOT, id: SESSION_SETTINGS_SECTION_ID, order: item.order, priority: item.order },\n IconSessionSettings,\n ))\n } catch (error) {\n // A synchronous failure (for example a duplicate row id or a failing page\n // install) must not pin the shared page for a contributor that never joined.\n stopIcon?.()\n stopItem?.()\n page.release()\n throw error\n }\n // The disposer may be invoked by both ctx.effect and an explicit caller;\n // releasing twice would unbalance the shared refcount.\n let disposed = false\n return () => {\n if (disposed) return\n disposed = true\n stopIcon?.()\n stopItem?.()\n page.release()\n }\n}\n","import type { WebFetchProvider, WebSearchProvider } from '@deepseek-ai/dsh-web'\nimport type { DshWebManagementDescriptor } from '@klarkxy/dsh-plugin-kit/contracts'\n\nexport const WEB_SEARCH_RPC_CHANNEL = '/web-search-manager'\nexport type RpcResult<T = unknown> = { ok: true; value: T } | {\n ok: false; error: { code: string; message: string; details: Record<string, unknown> }\n}\nexport type ProviderKind = 'search' | 'fetch'\n/** Management metadata only. Field contract lives in plugin-kit; execution stays with ctx.web. */\nexport interface ProviderDescriptor extends DshWebManagementDescriptor {}\nexport const WEB_MANAGER_OWNER = '@klarkxy/dsh-web-search-manager'\n/** Optional display metadata on a provider registered directly with ctx.web. */\nexport type DiscoverableSearchProvider = WebSearchProvider & { readonly dshWebManagement?: ProviderDescriptor }\nexport type DiscoverableFetchProvider = WebFetchProvider & { readonly dshWebManagement?: ProviderDescriptor }\nexport interface ProviderOptions {\n readonly apiKey?: string\n readonly baseURL?: string\n readonly timeoutMs: number\n readonly maxFetchChars: number\n}\nexport type SearchProviderFactory = (options: ProviderOptions) => WebSearchProvider\nexport type FetchProviderFactory = (options: ProviderOptions) => WebFetchProvider\n/** Never store credential values here. Keys are resolved by reference per call. */\nexport interface WebSettings {\n revision: number\n searchEnabled: boolean\n fetchEnabled: boolean\n searchProvider: string\n fetchProvider: string\n searchOrder: string[]\n fetchOrder: string[]\n maxResults: number\n maxQueries: number\n timeoutMs: number\n maxFetchChars: number\n endpoints: Record<string, string>\n}\nexport const DEFAULT_SEARCH_ORDER = ['ddg'] as const\n\nexport const defaultSettings = (): WebSettings => ({\n revision: 0, searchEnabled: true, fetchEnabled: true,\n searchProvider: 'ddg', fetchProvider: 'http', searchOrder: [...DEFAULT_SEARCH_ORDER], fetchOrder: ['http'],\n maxResults: 5, maxQueries: 1,\n timeoutMs: 30_000, maxFetchChars: 100_000, endpoints: {},\n})\n\n/** Keyed backends first, keyless last — same idea as OpenClaw auto-detect. */\nexport function defaultSearchOrder(ids: readonly string[], keyed: (id: string) => boolean): string[] {\n return [...ids.filter(keyed), ...ids.filter(id => !keyed(id))]\n}\n\nexport function migrateSearchOrder(settings: { searchOrder?: string[]; searchProvider?: string }): string[] {\n return settings.searchOrder ?? (settings.searchProvider ? [settings.searchProvider] : [...DEFAULT_SEARCH_ORDER])\n}\n\n/** Missing order is a legacy single selection; an empty selection never opts in. */\nexport function migrateFetchOrder(settings: { fetchOrder?: string[]; fetchProvider?: string }): string[] {\n return settings.fetchOrder ?? (settings.fetchProvider ? [settings.fetchProvider] : [])\n}\n\nexport function resolveSearchOrder(\n knownIds: readonly string[],\n settings: Pick<WebSettings, 'searchOrder' | 'searchProvider'>,\n _keyed: (id: string) => boolean,\n): string[] {\n return [...new Set(settings.searchOrder.filter(id => knownIds.includes(id)))]\n}\n\nexport function pickActiveSearch(order: readonly string[], configured: (id: string) => boolean): string {\n return order.find(configured) ?? ''\n}\nexport interface ProviderView extends ProviderDescriptor {\n kind: ProviderKind\n configured: boolean\n /** True only for configuration explicitly owned by this manager bundle. */\n configurationOwned?: boolean\n baseURL?: string\n calls: number\n failures: number\n}\nexport interface WebStatus {\n settings: WebSettings\n providers: ProviderView[]\n searchActive: boolean\n fetchActive: boolean\n storageFailed: boolean\n}\nexport function providerKey(kind: ProviderKind, id: string): string { return `${kind}:${id}` }\nexport function safeConfigurationUrl(value: unknown): string | undefined {\n if (typeof value !== 'string' || !value || /[\\u0000-\\u0020\\\\]/.test(value)) return\n try {\n const url = new URL(value, 'https://dsh.invalid')\n if (url.username || url.password) return\n if ((value.startsWith('/') && !value.startsWith('//')) || value.startsWith('#')) {\n return url.origin === 'https://dsh.invalid' ? value : undefined\n }\n return /^https:\\/\\//i.test(value) && url.protocol === 'https:' ? value : undefined\n } catch { return }\n}\nexport function validateBaseURL(value: string): string {\n const url = new URL(value)\n if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash) {\n throw new Error('供应商地址必须是 HTTPS,且不能包含用户名、密码、查询参数或片段。')\n }\n return url.href.replace(/\\/+$/, '')\n}\n","import { WEB_MANAGER_OWNER, type ProviderView } from './contracts.ts'\n\n// Public prices checked on 2026-09-21; links remain available for current account terms.\n// This date applies only to bundled adapters that still use these static notes.\nexport const PROVIDER_PRICING_CHECKED = '2026-09-21'\n\ninterface ProviderInfo {\n description: string\n pricing: string\n pricingUrl?: string\n}\n\nconst providerInfo: Record<string, ProviderInfo> = {\n perplexity: {\n description: '通过 Perplexity Search API 获取网页来源,不生成回答。',\n pricing: '按 Search API 请求计费;实际价格与额度以官方账户为准。',\n pricingUrl: 'https://docs.perplexity.ai/docs/getting-started/pricing',\n },\n you: {\n description: '通过 You.com 获取网页与新闻结果,不请求额外全文提取。',\n pricing: '按 Web Search API 请求计费;实际价格与额度以官方账户为准。',\n pricingUrl: 'https://you.com/docs/administration/billing',\n },\n 'firecrawl-fetch': {\n description: '读取网页 Markdown;与 Firecrawl 搜索共用 Key。',\n pricing: '网页读取使用 Firecrawl 积分,与搜索分别计费;实际消耗以官方账户为准。',\n pricingUrl: 'https://www.firecrawl.dev/pricing',\n },\n ddg: {\n description: '搜索 DuckDuckGo 公开网页,无需注册或 API Key。',\n pricing: '免费使用,不产生搜索 API 费用。',\n pricingUrl: 'https://duckduckgo.com/',\n },\n bocha: {\n description: '面向中文内容的网页搜索,返回标题、链接和摘要。',\n pricing: '按调用次数计费,目录价 ¥0.036/次;试用额度以官方活动为准。',\n pricingUrl: 'https://aq6ky2b8nql.feishu.cn/wiki/JYSbwzdPIiFnz4kDYPXcHSDrnZb',\n },\n brave: {\n description: '使用 Brave 独立网页索引,搜索网页、新闻等内容。',\n pricing: '每月赠 $5(约 1,000 次);超出 $5/千次,激活需绑卡。',\n pricingUrl: 'https://brave.com/search/api/',\n },\n 'deepseek-official': {\n description: '由 DeepSeek 模型调用内置搜索,复用模型 API Key。',\n pricing: '默认 Flash 按 tokens 计费:每百万输入 ¥1–2(缓存命中 ¥0.02–0.04)、输出 ¥4–8,随峰谷时段变化;赠送余额与工具费用以平台为准。',\n pricingUrl: 'https://api-docs.deepseek.com/zh-cn/quick_start/pricing/',\n },\n exa: {\n description: '面向 AI 的网页搜索,提供相关网页与内容片段。',\n pricing: '注册赠 $20,之后每月赠 $10;$7/千次(每次最多 10 条),超出 10 条每条加 $1/千次。',\n pricingUrl: 'https://exa.ai/pricing',\n },\n firecrawl: {\n description: '搜索网页,并可配合网页内容提取。',\n pricing: '每月免费 1,000 积分,搜索每 10 条结果扣 2 积分;Hobby 含 5,000 积分/月,年付折合 $16/月,额外 $5/千积分。',\n pricingUrl: 'https://www.firecrawl.dev/pricing',\n },\n serper: {\n description: '获取 Google 的网页搜索结果。',\n pricing: '注册赠 2,500 次(一次性);充值 $50/5万次($1/千次),付费额度有效期 6 个月。',\n pricingUrl: 'https://serper.dev/',\n },\n tavily: {\n description: '面向 AI 的网页搜索,当前使用基础搜索。',\n pricing: '每月免费 1,000 积分,基础搜索 1 积分/次;按量付费 $0.008/积分($8/千次)。',\n pricingUrl: 'https://docs.tavily.com/documentation/api-credits',\n },\n}\n\n// The prices are the same dated notes in both languages; translation is not a price refresh.\nconst englishInfo: Record<string, Pick<ProviderInfo, 'description' | 'pricing'>> = {\n perplexity: { description: 'Get web sources through the Perplexity Search API without generating an answer.', pricing: 'Charged per Search API request; current prices and credits depend on your official account.' },\n you: { description: 'Get web and news results through You.com without requesting extra full-page extraction.', pricing: 'Charged per Web Search API request; current prices and credits depend on your official account.' },\n 'firecrawl-fetch': { description: 'Read page Markdown using the same key as Firecrawl search.', pricing: 'Page reads use Firecrawl credits and are charged separately from search; your official account determines usage.' },\n ddg: { description: 'Search public DuckDuckGo pages without registration or an API key.', pricing: 'Free to use; no search API fees.' },\n bocha: { description: 'Search Chinese web content, returning titles, links and summaries.', pricing: 'Charged per request: listed price ¥0.036/request; trial credits depend on official offers.' },\n brave: { description: 'Search web pages, news and more with Brave’s independent index.', pricing: '$5 in monthly credits (about 1,000 requests); then $5/1,000 requests. A payment card is required to activate.' },\n 'deepseek-official': { description: 'Call built-in search through a DeepSeek model using the model API key.', pricing: 'Default Flash token pricing per million: input ¥1–2 (cache hits ¥0.02–0.04), output ¥4–8, varying by peak/off-peak hours. Platform terms govern promotional balance and tool fees.' },\n exa: { description: 'Web search for AI with relevant pages and content snippets.', pricing: '$20 on signup, then $10 monthly credits; $7/1,000 requests (up to 10 results each), plus $1/1,000 additional results beyond 10.' },\n firecrawl: { description: 'Search the web and extract page content.', pricing: '1,000 free credits/month; 2 credits per 10 search results. Hobby includes 5,000 credits/month at $16/month billed annually; extra credits cost $5/1,000.' },\n serper: { description: 'Get Google web search results.', pricing: '2,500 one-time signup requests; $50/50,000 requests ($1/1,000). Paid credits expire after 6 months.' },\n tavily: { description: 'Web search for AI, currently using basic search.', pricing: '1,000 free credits/month; basic search uses 1 credit/request. Pay as you go: $0.008/credit ($8/1,000 requests).' },\n}\n\nconst bundledDescriptions: Record<string, readonly [string, string]> = {\n perplexity: ['Search API;返回网页来源,不生成回答。', 'Search API returning web sources without generating an answer.'],\n you: ['网页与新闻搜索;不额外提取全文。', 'Web and news search without extra full-page extraction.'],\n 'firecrawl-fetch': ['读取网页 Markdown;与 Firecrawl 搜索共用 Key。', 'Read page Markdown using the same key as Firecrawl search.'],\n ddg: ['无需注册。默认后端。', 'No registration required. Default backend.'],\n 'deepseek-official': ['使用已配置的 DeepSeek API Key,可能产生额外搜索费用。', 'Uses the configured DeepSeek API key; additional search fees may apply.'],\n exa: ['独立 Search API。', 'Independent Search API.'],\n brave: ['Brave Search API。', 'Brave Search API.'],\n bocha: ['中文网页搜索 API。', 'Chinese web search API.'],\n serper: ['Google 结果的 Search API。', 'Search API for Google results.'],\n firecrawl: ['网页搜索与提取 API。', 'Web search and extraction API.'],\n tavily: ['Search API;固定 basic 检索,不自动升级搜索深度。', 'Search API using basic search; search depth is never upgraded automatically.'],\n}\n\n/** Translate only manager-owned labels; externally supplied metadata stays verbatim. */\nexport function providerDisplayLabel(provider: ProviderView, locale: 'zh' | 'en'): string {\n if (!bundledCatalogId(provider)) return provider.label\n if (provider.label === 'DeepSeek 搜索') return locale === 'zh' ? provider.label : 'DeepSeek search'\n if (provider.label === '博查') return locale === 'zh' ? provider.label : 'Bocha'\n if (provider.label === '网页读取') return locale === 'zh' ? provider.label : 'Read web pages'\n return provider.label\n}\n\nfunction bundledCatalogId(provider: ProviderView): string | undefined {\n const owned = typeof provider.configurationOwned === 'boolean'\n ? provider.configurationOwned\n : !provider.configurationOwner || provider.configurationOwner === WEB_MANAGER_OWNER\n if (!owned) return\n if (provider.kind === 'fetch' && provider.id === 'firecrawl') return 'firecrawl-fetch'\n return provider.id === 'deepseek-managed' ? 'deepseek-official' : provider.id\n}\n\nfunction missingPricing(locale: 'zh' | 'en'): string {\n return locale === 'zh' ? '未提供费用信息' : 'No pricing information provided'\n}\n\nexport function usesBundledPricing(provider: ProviderView): boolean {\n if (provider.pricing?.trim()) return false\n const catalogId = bundledCatalogId(provider)\n return Boolean(catalogId && !['perplexity', 'you', 'firecrawl-fetch'].includes(catalogId) && Object.hasOwn(providerInfo, catalogId))\n}\n\nexport function searchProviderInfo(provider: ProviderView, locale: 'zh' | 'en' = 'zh'): ProviderInfo {\n const catalogId = bundledCatalogId(provider)\n const base = catalogId && Object.hasOwn(providerInfo, catalogId) ? providerInfo[catalogId] : undefined\n const known = base && locale === 'en' ? { ...base, ...englishInfo[catalogId!] } : base\n const ownedCopy = catalogId ? bundledDescriptions[catalogId] : undefined\n const description = ownedCopy && provider.description === ownedCopy[0]\n ? ownedCopy[locale === 'zh' ? 0 : 1]\n : base && provider.description === base.description ? known!.description\n : provider.description.trim() ? provider.description : (known?.description ?? provider.description)\n if (provider.pricing?.trim()) {\n return {\n description,\n pricing: provider.pricing,\n pricingUrl: provider.pricingUrl ?? known?.pricingUrl ?? provider.signupUrl,\n }\n }\n if (known) {\n return {\n description,\n pricing: known.pricing,\n pricingUrl: provider.pricingUrl ?? known.pricingUrl ?? provider.signupUrl,\n }\n }\n return {\n description,\n pricing: missingPricing(locale),\n pricingUrl: provider.pricingUrl ?? provider.signupUrl,\n }\n}\n","import { localErrorText } from './client-error.ts'\nimport { webSearchClientStyles } from './client-styles.ts'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { Button, IconGlobeOutlineRegular, Input, SegmentedTabs, StateDot, Switch, Tag } from '@deepseek-ai/dsh-client-ui-primitives'\nimport { createPluginReadGate } from '@klarkxy/dsh-plugin-kit/client-utils'\nimport { useCallback, useEffect, useId, useLayoutEffect, useRef, useState, useSyncExternalStore } from 'react'\nimport { PROVIDER_PRICING_CHECKED, providerDisplayLabel, searchProviderInfo, usesBundledPricing } from './provider-info.ts'\nimport {\n WEB_SEARCH_RPC_CHANNEL, WEB_MANAGER_OWNER, safeConfigurationUrl,\n providerKey, type ProviderKind, type ProviderView, type RpcResult, type WebSettings, type WebStatus,\n} from './contracts.ts'\n\nexport const name = 'dsh-web-search-manager-client'\nexport const inject = ['slots', 'connection', 'remote', 'remote.credentials', 'locale'] as const\n\ninterface Credentials {\n describe(refs: string[]): Promise<RpcResult<Record<string, { configured: boolean; writable: boolean; source?: string }>>>\n set(ref: string, value: string): Promise<RpcResult<unknown>>\n unset(ref: string): Promise<RpcResult<unknown>>\n}\ninterface Client {\n connection: { rpc: { call(channel: string, endpoint: string, payload: unknown): Promise<unknown> }; generation?: { subscribe(listener: () => void): () => void } }\n remote: { credentials: Credentials }\n /** Host UI language. Optional so bare test harnesses fall back to zh. */\n locale?: { getSnapshot(): { active: string }; subscribe(listener: () => void): () => void }\n slots: {\n inject(key: string, callback: () => unknown): () => void\n register(spec: { name: string; id: string; order?: number; label?: string | (() => string) }, render: unknown): () => void\n }\n}\n\nfunction unwrap<T>(result: RpcResult<T>): T {\n if (!result.ok) throw new Error(result.error.message)\n return result.value\n}\nfunction editable(settings: WebSettings): Omit<WebSettings, 'revision'> {\n const { revision: _revision, ...rest } = settings\n return rest\n}\n\nexport const STATUS_REFRESH_MS = 3000\n\nexport function isPageHidden(hidden?: boolean): boolean {\n if (hidden) return true\n return typeof document !== 'undefined' && document.visibilityState === 'hidden'\n}\n\nexport function canStartStatusRead(input: {\n source: 'user' | 'poll'\n mounted: boolean\n hidden: boolean\n busy: boolean\n reordering: boolean\n reading: boolean\n}): boolean {\n if (!input.mounted) return false\n if (input.source === 'poll') {\n return !input.hidden && !input.busy && !input.reordering && !input.reading\n }\n return true\n}\n\nexport function isExternalConfigurationUrl(url: string): boolean {\n return /^https:\\/\\//i.test(url)\n}\n\nexport function shouldApplyPolledStatus(input: {\n requestId: number\n latestRequestId: number\n busy: boolean\n reordering: boolean\n hidden: boolean\n currentRevision?: number\n incomingRevision: number\n}): boolean {\n if (input.hidden || input.busy || input.reordering) return false\n if (input.requestId !== input.latestRequestId) return false\n if (input.currentRevision !== undefined && input.incomingRevision < input.currentRevision) return false\n return true\n}\n\nexport function bindStatusRefresh(options: {\n intervalMs?: number\n isHidden: () => boolean\n tick: () => Promise<void>\n setIntervalFn?: typeof setInterval\n clearIntervalFn?: typeof clearInterval\n addListener?: (type: string, listener: () => void) => void\n removeListener?: (type: string, listener: () => void) => void\n}): () => void {\n const intervalMs = options.intervalMs ?? STATUS_REFRESH_MS\n let inFlight = false\n let stopped = false\n const run = async () => {\n if (stopped || inFlight || options.isHidden()) return\n inFlight = true\n try { await options.tick() }\n finally { inFlight = false }\n }\n const timer = (options.setIntervalFn ?? setInterval)(() => { void run() }, intervalMs)\n const onVisible = () => { if (!options.isHidden()) void run() }\n const add = options.addListener ?? ((type, listener) => {\n if (typeof document !== 'undefined') document.addEventListener(type, listener)\n })\n const remove = options.removeListener ?? ((type, listener) => {\n if (typeof document !== 'undefined') document.removeEventListener(type, listener)\n })\n add('visibilitychange', onVisible)\n return () => {\n stopped = true\n ;(options.clearIntervalFn ?? clearInterval)(timer)\n remove('visibilitychange', onVisible)\n }\n}\n\nexport function isProviderOn(status: WebStatus, provider: ProviderView): boolean {\n if (provider.kind === 'fetch') {\n return status.fetchActive && selectedBackend(status, 'fetch')?.id === provider.id\n }\n return status.searchActive && selectedSearchBackend(status)?.id === provider.id\n}\n\nexport function dormantSearchPlaceholder(id: string): ProviderView { return dormantPlaceholder(id, 'search') }\nfunction dormantPlaceholder(id: string, kind: ProviderKind): ProviderView {\n return { id, label: id, description: '', billing: 'unknown', kind, configured: false, calls: 0, failures: 0, configurationOwned: false }\n}\nfunction isRegistered(status: WebStatus, id: string, kind: ProviderKind): boolean {\n return status.providers.some(provider => provider.kind === kind && provider.id === id)\n}\nexport function isRegisteredSearch(status: WebStatus, id: string): boolean { return isRegistered(status, id, 'search') }\nfunction providerOrder(settings: WebSettings, kind: ProviderKind): string[] {\n return kind === 'search' ? settings.searchOrder : settings.fetchOrder\n}\nfunction catalogBackends(status: WebStatus, kind: ProviderKind): ProviderView[] {\n const byId = new Map(status.providers.filter(provider => provider.kind === kind).map(provider => [provider.id, provider]))\n const rows: ProviderView[] = []\n const seen = new Set<string>()\n for (const id of providerOrder(status.settings, kind)) {\n if (!id || seen.has(id)) continue\n seen.add(id)\n rows.push(byId.get(id) ?? dormantPlaceholder(id, kind))\n }\n for (const provider of byId.values()) if (!seen.has(provider.id)) rows.push(provider)\n return rows\n}\nexport function catalogSearchBackends(status: WebStatus): ProviderView[] { return catalogBackends(status, 'search') }\nexport function searchBackends(status: WebStatus): ProviderView[] {\n return catalogSearchBackends(status).filter(provider => status.settings.searchOrder.includes(provider.id))\n}\nfunction selectedBackend(status: WebStatus, kind: ProviderKind): ProviderView | undefined {\n return providerOrder(status.settings, kind).flatMap(id => status.providers.find(provider => provider.kind === kind && provider.id === id) ?? []).find(provider => provider.configured)\n}\nexport function selectedSearchBackend(status: WebStatus): ProviderView | undefined { return selectedBackend(status, 'search') }\nexport function droppedDormantIds(settings: WebSettings, order: readonly string[], providers: readonly ProviderView[], kind: ProviderKind = 'search'): string[] {\n const known = new Set(providers.filter(provider => provider.kind === kind).map(provider => provider.id))\n return [...new Set(providerOrder(settings, kind).filter(id => id && !known.has(id) && !order.includes(id)))]\n}\n\nexport type ConfigurationAccess =\n | { kind: 'owned' }\n | { kind: 'link'; url: string }\n | { kind: 'owner'; name: string }\n | { kind: 'unknown' }\n\nexport function configurationAccess(provider: ProviderView): ConfigurationAccess {\n if (ownsProviderConfiguration(provider)) return { kind: 'owned' }\n const url = safeConfigurationUrl(provider.configurationUrl)\n if (url) return { kind: 'link', url }\n if (provider.configurationOwner?.trim()) return { kind: 'owner', name: provider.configurationOwner }\n return { kind: 'unknown' }\n}\n\nfunction AppLink({ url, label, children }: { url: string; label: string; children: string }) {\n const external = isExternalConfigurationUrl(url)\n return <a href={url} {...(external ? { target: '_blank', rel: 'noreferrer noopener' } : {})} aria-label={label}\n onClick={event => {\n if (!external) return\n const bridge = (globalThis as { dshWindow?: { openExternal?(url: string): void } }).dshWindow\n if (bridge?.openExternal) {\n event.preventDefault()\n bridge.openExternal(url)\n }\n }}>{children}</a>\n}\n\nexport function canEnableSearch(\n order: readonly string[],\n providers: readonly ProviderView[],\n keys: Record<string, string>,\n writable: Record<string, boolean>,\n): boolean {\n return order.some(id => {\n const provider = providers.find(item => item.kind === 'search' && item.id === id)\n if (!provider) return false\n if (provider.configured) return true\n const typed = (keys[provider.id] ?? '').trim()\n return Boolean(ownsProviderConfiguration(provider) && provider.credentialRef && typed && writable[provider.credentialRef] !== false)\n })\n}\n\nexport function ownsProviderConfiguration(provider: Pick<ProviderView, 'configurationOwned' | 'configurationOwner'>): boolean {\n if (typeof provider.configurationOwned === 'boolean') return provider.configurationOwned\n return !provider.configurationOwner || provider.configurationOwner === WEB_MANAGER_OWNER\n}\n\nexport function nextSearchEnabled(\n currentlyEnabled: boolean,\n nextOrder: readonly string[],\n providers: readonly ProviderView[],\n keys: Record<string, string>,\n writable: Record<string, boolean>,\n): boolean {\n return currentlyEnabled && canEnableSearch(nextOrder, providers, keys, writable)\n}\n\nfunction ConfirmButton(props: { label: string; confirmLabel: string; disabled?: boolean; onConfirm(): void }) {\n const [armed, setArmed] = useState(false)\n const timer = useRef<ReturnType<typeof setTimeout>>(undefined)\n useEffect(() => () => clearTimeout(timer.current), [])\n function disarm() {\n clearTimeout(timer.current)\n setArmed(false)\n }\n return <Button variant=\"outline\" size=\"sm\" className=\"ws-danger\" disabled={props.disabled} data-armed={armed || undefined}\n onClick={() => {\n if (!armed) {\n setArmed(true)\n timer.current = setTimeout(() => setArmed(false), 3000)\n return\n }\n disarm()\n props.onConfirm()\n }}\n onBlur={disarm}>{armed ? props.confirmLabel : props.label}</Button>\n}\n\nconst copy = {\n zh: {\n tabsLabel: '网络搜索设置', tabSearch: 'web-search', tabFetch: 'web-fetch', tabLimits: '设置',\n loading: '正在读取设置…', loadFailed: '读取设置失败:', reconnect: '重新连接',\n failed: '操作失败。', readOnly: '凭据只读,无法保存。',\n keyDeleted: '已删除 Key。', keySaved: '已保存 Key。', saved: '已保存。',\n storageFailed: '保存失败,重新保存前网络访问已暂停。',\n empty: '暂无可用搜索后端,请启用一个供应商。', toolTitle: '联网搜索', needBackend: '需选择搜索服务并配置 Key。',\n backends: '搜索后端', dragPrefix: '拖动排序 ', dragHint: '拖动排序,或用上下方向键',\n inUse: '当前使用', available: '可用', unavailable: '暂不可用', dormant: '未加载,保留选择',\n signup: '去注册', pricing: '费用说明', website: '官网', openConfig: '打开配置',\n needsKey: '待配置 Key', sharedKey: '与其他设置共用凭据',\n keyConfigured: '已配置,留空不替换', keyPlaceholder: '输入 API Key',\n saveKey: '保存 Key', deleteKey: '删除 Key', confirmDelete: '确认删除 Key?',\n test: '测试连接(可能计费)', unsaved: '有未保存的修改', save: '保存',\n saveHint: '开关与排序立即生效;Key 与限制需保存。',\n maxResults: '每条查询结果上限', maxQueries: '每次调用查询上限',\n timeoutMs: '请求超时(毫秒)', maxFetchChars: '正文字符上限',\n fetchTitle: '网页读取', fetchProvider: '读取服务', fetchBackends: '读取后端', needFetchBackend: '需选择并配置读取服务。', ownerSettings: '配置入口:',\n unknownOwner: '请在服务所属插件中配置',\n },\n en: {\n tabsLabel: 'Web search settings', tabSearch: 'web-search', tabFetch: 'web-fetch', tabLimits: 'Settings',\n loading: 'Loading settings…', loadFailed: 'Could not load settings: ', reconnect: 'Reconnect',\n failed: 'Operation failed.', readOnly: 'Read-only credential; cannot save.',\n keyDeleted: 'Key deleted.', keySaved: 'Key saved.', saved: 'Saved.',\n storageFailed: 'Save failed; network access is paused until you save again.',\n empty: 'No search backends yet; enable a provider.', toolTitle: 'Web search', needBackend: 'Needs a search service with a key.',\n backends: 'Search backends', dragPrefix: 'Reorder ', dragHint: 'Drag to reorder, or use arrow keys',\n inUse: 'In use', available: 'Available', unavailable: 'Unavailable', dormant: 'Not loaded; selection kept',\n signup: 'Sign up', pricing: 'Pricing', website: 'Website', openConfig: 'Open configuration',\n needsKey: 'Key required', sharedKey: 'Shares a credential with other settings',\n keyConfigured: 'Configured; empty keeps it', keyPlaceholder: 'Enter API key',\n saveKey: 'Save key', deleteKey: 'Delete key', confirmDelete: 'Delete this key?',\n test: 'Test connection (may be billed)', unsaved: 'Unsaved changes', save: 'Save',\n saveHint: 'Switches and order apply now; keys and limits need saving.',\n maxResults: 'Max results/query', maxQueries: 'Max queries/call',\n timeoutMs: 'Request timeout (ms)', maxFetchChars: 'Max text chars',\n fetchTitle: 'Read web pages', fetchProvider: 'Fetch service', fetchBackends: 'Fetch backends', needFetchBackend: 'Choose and configure a fetch service.', ownerSettings: 'Configure in: ',\n unknownOwner: 'Configure this service in its plugin',\n },\n} as const\n\ntype Copy = (typeof copy)['zh'] | (typeof copy)['en']\n\n// Keep action outcomes as data so a language switch also updates existing notices.\ntype UiMessage = string | { key: keyof Copy } | { sources: number } | { moved: string; kind: ProviderKind; position: number } | { invalid: LimitKey[] }\nclass UiError extends Error {\n constructor(readonly key: keyof Copy) { super(key) }\n}\n\nfunction uiMessage(message: UiMessage, locale: 'zh' | 'en', providers: ProviderView[] = []): string {\n if (typeof message === 'string') return localErrorText(message, locale)\n if ('key' in message) return copy[locale][message.key]\n if ('sources' in message) return testOkText(locale, message.sources)\n if ('moved' in message) {\n const provider = providers.find(item => item.id === message.moved && item.kind === message.kind)\n return movedText(locale, provider ? providerDisplayLabel(provider, locale) : message.moved, message.position)\n }\n return message.invalid.map(key => {\n const [, min, max] = LIMITS.find(item => item[0] === key)!\n return limitInvalidText(locale, copy[locale][key], min, max)\n }).join(' ')\n}\n\nfunction movedText(locale: 'zh' | 'en', label: string, position: number): string {\n return locale === 'zh' ? `${label} 已移至第 ${position} 位` : `${label} moved to position ${position}`\n}\nfunction pricingCheckedText(locale: 'zh' | 'en', date: string): string {\n return locale === 'zh'\n ? `内置适配器价格核对于 ${date},额度与费用以供应商账户为准。`\n : `Bundled adapter prices checked on ${date}; your provider account is authoritative.`\n}\nfunction testOkText(locale: 'zh' | 'en', sources: number): string {\n return locale === 'zh' ? `连接正常,返回 ${sources} 条来源。` : `Connection OK: ${sources} sources returned.`\n}\nfunction limitInvalidText(locale: 'zh' | 'en', label: string, min: number, max: number): string {\n return locale === 'zh' ? `${label}需为 ${min}–${max} 之间的整数。` : `${label} must be a whole number from ${min} to ${max}.`\n}\n\nconst LIMITS = [\n ['maxResults', 1, 20],\n ['maxQueries', 1, 5],\n ['timeoutMs', 1000, 120000],\n ['maxFetchChars', 1000, 200000],\n] as const\ntype LimitKey = (typeof LIMITS)[number][0]\n\n/** A limit field's raw text, or `undefined` when it is not a whole number in range. */\nexport function parseLimit(raw: string, min: number, max: number): number | undefined {\n const text = raw.trim()\n if (!/^\\d+$/u.test(text)) return undefined\n const value = Number(text)\n return value >= min && value <= max ? value : undefined\n}\n\n/**\n * Settings written by an immediate action (a switch or a reorder). It keeps the\n * saved limits and never includes typed-but-unsaved keys or limit drafts.\n */\nexport function immediateUpdate(\n settings: WebSettings,\n on: boolean,\n order: string[],\n providers?: readonly ProviderView[],\n remove: readonly string[] = [],\n kind: ProviderKind = 'search',\n): Omit<WebSettings, 'revision'> {\n let searchOrder = order\n if (providers) {\n const known = new Set(providers.filter(provider => provider.kind === kind).map(provider => provider.id))\n const removed = new Set(remove)\n const following = order.filter(id => !removed.has(id))\n if (following.some(id => !known.has(id))) searchOrder = following\n else {\n searchOrder = providerOrder(settings, kind).flatMap(id => {\n if (removed.has(id)) return []\n return known.has(id) ? following.splice(0, 1) : [id]\n })\n searchOrder.push(...following)\n }\n }\n return { ...editable(settings), ...(kind === 'search' ? { searchOrder, searchEnabled: on } : { fetchOrder: searchOrder, fetchEnabled: on }) }\n}\n\nfunction limitTexts(settings: WebSettings): Record<LimitKey, string> {\n return {\n maxResults: String(settings.maxResults), maxQueries: String(settings.maxQueries),\n timeoutMs: String(settings.timeoutMs), maxFetchChars: String(settings.maxFetchChars),\n }\n}\n\nfunction useHostLocale(client: Client): 'zh' | 'en' {\n const subscribe = useCallback((listener: () => void) => client.locale?.subscribe(listener) ?? (() => {}), [client])\n const snapshot = useCallback(() => client.locale?.getSnapshot().active ?? 'zh', [client])\n return String(useSyncExternalStore(subscribe, snapshot, snapshot)).startsWith('zh') ? 'zh' : 'en'\n}\n\n/** Keyboard reorders are batched: persist once the arrows stop for this long. */\nconst REORDER_DEBOUNCE_MS = 500\ntype PendingOrder = { kind: ProviderKind; order: string[] }\n\nexport function NetworkSearchSettings({ client, hidden }: { client: Client; hidden?: boolean }) {\n const locale = useHostLocale(client)\n const text: Copy = copy[locale]\n const tabsId = useId()\n const [tab, setTab] = useState<'search' | 'fetch' | 'limits'>('search')\n const [status, setStatus] = useState<WebStatus>()\n const [limitDraft, setLimitDraft] = useState<Record<LimitKey, string>>()\n const [invalid, setInvalid] = useState<Partial<Record<LimitKey, boolean>>>({})\n const [keys, setKeys] = useState<Record<string, string>>({})\n const [busy, setBusy] = useState(false)\n const [pendingOrder, setPendingOrder] = useState<PendingOrder | null>(null)\n const [dragSource, setDragSource] = useState<string | null>(null)\n const [dropTarget, setDropTarget] = useState<string | null>(null)\n const [sortAnnouncement, setSortAnnouncement] = useState<UiMessage>('')\n const [note, setNote] = useState<UiMessage>('')\n const [error, setError] = useState<UiMessage>('')\n const [loadError, setLoadError] = useState<UiMessage>('')\n const [writable, setWritable] = useState<Record<string, boolean>>({})\n const statusRef = useRef(status)\n statusRef.current = status\n const pendingRef = useRef<PendingOrder | null>(null)\n const dragRef = useRef<string | null>(null)\n const timerRef = useRef<ReturnType<typeof setTimeout>>(undefined)\n const handleRefs = useRef(new Map<string, HTMLButtonElement>())\n const refocusRef = useRef<string | null>(null)\n const busyRef = useRef(false)\n const hiddenRef = useRef(hidden)\n hiddenRef.current = hidden\n const requestSeq = useRef(0)\n const mountedRef = useRef(true)\n const readingRef = useRef(false)\n const readingRequest = useRef(0)\n const statusRead = useRef(Promise.resolve())\n const readGateRef = useRef<ReturnType<typeof createPluginReadGate>>(undefined)\n if (!readGateRef.current) readGateRef.current = createPluginReadGate()\n const readGate = readGateRef.current\n\n async function call<T>(endpoint: string, payload: unknown = {}): Promise<T> {\n return unwrap(await client.connection.rpc.call(WEB_SEARCH_RPC_CHANNEL, endpoint, payload) as RpcResult<T>)\n }\n function adopt(next: WebStatus, resetDraft = false) {\n const previous = statusRef.current\n statusRef.current = next\n setStatus(next)\n setLimitDraft(current => {\n const incoming = limitTexts(next.settings)\n if (!current || !previous || resetDraft) return incoming\n const baseline = limitTexts(previous.settings)\n return Object.fromEntries(LIMITS.map(([key]) =>\n [key, current[key] === baseline[key] ? incoming[key] : current[key]],\n )) as Record<LimitKey, string>\n })\n if (resetDraft) setInvalid({})\n }\n function pollBlocked(): boolean {\n return !canStartStatusRead({\n source: 'poll',\n mounted: mountedRef.current,\n hidden: isPageHidden(hiddenRef.current),\n busy: busyRef.current,\n reordering: pendingRef.current !== null || dragRef.current !== null,\n reading: readingRef.current,\n })\n }\n async function describeOwned(next: WebStatus, mine: number) {\n const refs = next.providers.flatMap(provider => ownsProviderConfiguration(provider) && provider.credentialRef ? [provider.credentialRef] : [])\n if (!refs.length) return\n const credentials = unwrap(await readGate.run(() => client.remote.credentials.describe(refs)))\n if (!mountedRef.current || mine !== requestSeq.current) return\n setWritable(Object.fromEntries(Object.entries(credentials).map(([ref, value]) => [ref, value.writable])))\n }\n async function load(resetDraft = false, source: 'user' | 'poll' = 'user') {\n if (source === 'poll' && !readGate.canRead()) return\n if (source === 'user') { readGate.reset(); requestSeq.current += 1 }\n while (readingRef.current) {\n if (source === 'poll' || !mountedRef.current) return\n await statusRead.current\n }\n if (!mountedRef.current) return\n if (source === 'poll' && pollBlocked()) return\n const mine = ++requestSeq.current\n readingRef.current = true\n readingRequest.current = mine\n let release = () => {}\n statusRead.current = new Promise<void>(resolve => { release = resolve })\n try {\n const next = unwrap(await readGate.run(() => client.connection.rpc.call(WEB_SEARCH_RPC_CHANNEL, 'status', {})) as RpcResult<WebStatus>)\n if (!mountedRef.current || mine !== requestSeq.current) return next\n if (source === 'poll' && !shouldApplyPolledStatus({\n requestId: mine, latestRequestId: requestSeq.current,\n busy: busyRef.current, reordering: pendingRef.current !== null || dragRef.current !== null,\n hidden: isPageHidden(hiddenRef.current),\n currentRevision: statusRef.current?.settings.revision,\n incomingRevision: next.settings.revision,\n })) return next\n adopt(next, resetDraft)\n setLoadError('')\n await describeOwned(next, mine)\n return next\n } catch (cause) {\n if (!mountedRef.current || mine !== requestSeq.current) return\n setLoadError(cause instanceof Error && cause.message ? cause.message : { key: 'failed' })\n if (source === 'poll') return\n throw cause\n } finally {\n if (readingRequest.current === mine) readingRef.current = false\n release()\n }\n }\n useEffect(() => {\n mountedRef.current = true\n readGate.reset()\n void load().catch(cause => setLoadError(cause instanceof Error && cause.message ? cause.message : { key: 'failed' }))\n const bind = () => bindStatusRefresh({\n isHidden: () => isPageHidden(hiddenRef.current),\n tick: async () => { await load(false, 'poll').catch(() => {}) },\n })\n let stop = bind()\n const stopConnection = client.connection.generation?.subscribe(() => {\n readGate.reset()\n requestSeq.current += 1\n readingRef.current = false\n statusRead.current = Promise.resolve()\n stop()\n stop = bind()\n void load(false, 'poll')\n })\n return () => {\n mountedRef.current = false\n readGate.reset()\n requestSeq.current += 1\n stop()\n stopConnection?.()\n if (timerRef.current === undefined) return\n clearTimeout(timerRef.current)\n const current = statusRef.current\n const next = pendingRef.current\n if (current && next) {\n void call('update', { settings: immediateUpdate(current.settings, next.kind === 'search' ? current.settings.searchEnabled : current.settings.fetchEnabled, next.order, current.providers, [], next.kind), expectedRevision: current.settings.revision }).catch(() => {})\n }\n }\n }, [client])\n\n useEffect(() => {\n if (hidden) return\n if (!statusRef.current) return\n void load(false, 'poll').catch(() => {})\n }, [hidden])\n\n // Keep keyboard focus on the handle that was just moved.\n useLayoutEffect(() => {\n const id = refocusRef.current\n if (!id) return\n refocusRef.current = null\n const handle = handleRefs.current.get(id)\n if (handle && document.activeElement !== handle) handle.focus()\n })\n\n async function action(run: () => Promise<void>) {\n if (busyRef.current) return\n readGate.reset()\n requestSeq.current += 1\n busyRef.current = true\n setBusy(true); setNote(''); setError('')\n try { await run() }\n catch (cause) { setError(cause instanceof UiError ? { key: cause.key } : cause instanceof Error && cause.message ? cause.message : { key: 'failed' }); await load().catch(() => {}) }\n finally { busyRef.current = false; setBusy(false) }\n }\n\n /** Switches and reorders: settings only, applied at once. */\n function runImmediate(on: boolean, order: string[], remove: readonly string[] = [], kind: ProviderKind = 'search') {\n clearTimeout(timerRef.current); timerRef.current = undefined\n pendingRef.current = null\n void action(async () => {\n const current = statusRef.current\n if (!current) return\n const next = await call<WebStatus>('update', {\n settings: immediateUpdate(current.settings, on, order, current.providers, remove, kind),\n expectedRevision: current.settings.revision,\n })\n adopt(next)\n await load()\n }).then(() => { if (pendingRef.current === null) setPendingOrder(null) })\n }\n\n /** Writes every typed key. Explicit actions only (Save, Save key, Test). */\n async function saveKeys(only?: ProviderView) {\n if (!status) return\n for (const provider of status.providers) {\n if (only && providerKey(provider.kind, provider.id) !== providerKey(only.kind, only.id)) continue\n const snapshot = keys[providerKey(provider.kind, provider.id)] ?? ''\n const typed = snapshot.trim()\n if (!ownsProviderConfiguration(provider) || !provider.credentialRef || !typed) continue\n if (writable[provider.credentialRef] === false) throw new UiError('readOnly')\n unwrap(await client.remote.credentials.set(provider.credentialRef, typed))\n setKeys(current => current[providerKey(provider.kind, provider.id)] === snapshot ? { ...current, [providerKey(provider.kind, provider.id)]: '' } : current)\n }\n }\n\n async function saveKeysForKind(kind: ProviderKind) {\n for (const provider of statusRef.current?.providers ?? []) if (provider.kind === kind) await saveKeys(provider)\n }\n\n async function removeKey(provider: ProviderView) {\n if (!ownsProviderConfiguration(provider) || !provider.credentialRef || provider.credentialShared || !status) return\n unwrap(await client.remote.credentials.unset(provider.credentialRef))\n setKeys(current => ({ ...current, [providerKey(provider.kind, provider.id)]: '' }))\n await load()\n setNote({ key: 'keyDeleted' })\n }\n\n if (!status || !limitDraft) {\n return <section className=\"web-search-settings ws-root dsh-ui-page\">\n {loadError\n ? <p role=\"alert\" className=\"dsh-ui-error\">{text.loadFailed}{uiMessage(loadError, locale, status?.providers)}</p>\n : <p role=\"status\" className=\"dsh-ui-hint\">{text.loading}</p>}\n {loadError ? <div className=\"dsh-ui-actions\"><Button variant=\"outline\" size=\"sm\" disabled={busy}\n onClick={() => void action(async () => { await load() })}>{text.reconnect}</Button></div> : null}\n </section>\n }\n\n const saved = status\n const savedLimits = limitTexts(saved.settings)\n const limitsDirty = LIMITS.some(([key]) => limitDraft[key] !== savedLimits[key])\n const keysDirty = Object.values(keys).some(value => value.trim())\n const reordering = pendingOrder !== null || dragSource !== null\n\n function move(kind: ProviderKind, id: string, nextIndex: number, keyboard = false) {\n if (pendingRef.current && pendingRef.current.kind !== kind) return\n const order = pendingRef.current?.order ?? providerOrder(statusRef.current!.settings, kind)\n const index = order.indexOf(id)\n if (busy || index < 0 || nextIndex < 0 || nextIndex >= order.length || index === nextIndex) return\n const next = [...order]\n const [item] = next.splice(index, 1)\n next.splice(nextIndex, 0, item)\n pendingRef.current = { kind, order: next }\n setPendingOrder({ kind, order: next })\n if (keyboard) refocusRef.current = providerKey(kind, id)\n setSortAnnouncement({ moved: id, kind, position: nextIndex + 1 })\n clearTimeout(timerRef.current)\n const delay = keyboard ? REORDER_DEBOUNCE_MS : 0\n timerRef.current = setTimeout(() => {\n timerRef.current = undefined\n const batched = pendingRef.current\n const current = statusRef.current\n if (batched && current) runImmediate(batched.kind === 'search' ? current.settings.searchEnabled : current.settings.fetchEnabled, batched.order, [], batched.kind)\n }, delay)\n }\n\n function endDrag() {\n dragRef.current = null\n setDragSource(null)\n setDropTarget(null)\n }\n\n function saveAll() {\n const parsed: Partial<Record<LimitKey, number>> = {}\n const nextInvalid: Partial<Record<LimitKey, boolean>> = {}\n const messages: LimitKey[] = []\n for (const [key, min, max] of LIMITS) {\n const value = parseLimit(limitDraft![key], min, max)\n if (value === undefined) { nextInvalid[key] = true; messages.push(key) }\n else parsed[key] = value\n }\n setInvalid(nextInvalid)\n if (messages.length) { setNote(''); setError({ invalid: messages }); return }\n void action(async () => {\n await saveKeys()\n const current = statusRef.current!\n const next = await call<WebStatus>('update', {\n settings: { ...editable(current.settings), ...parsed },\n expectedRevision: current.settings.revision,\n })\n adopt(next, true)\n await load()\n setNote({ key: 'saved' })\n })\n }\n\n return <section className=\"web-search-settings ws-root dsh-ui-page\" data-testid=\"web-search-settings\">\n <SegmentedTabs<'search' | 'fetch' | 'limits'>\n value={tab}\n onChange={setTab}\n label={text.tabsLabel}\n items={[\n { value: 'search', label: text.tabSearch, id: tabsId + '-search-tab', panelId: tabsId + '-search-panel' },\n { value: 'fetch', label: text.tabFetch, id: tabsId + '-fetch-tab', panelId: tabsId + '-fetch-panel' },\n { value: 'limits', label: text.tabLimits, id: tabsId + '-limits-tab', panelId: tabsId + '-limits-panel' },\n ]}\n />\n <span className=\"ws-sr-only\" role=\"status\" aria-live=\"polite\">{uiMessage(sortAnnouncement, locale, saved.providers)}</span>\n {error && <p role=\"alert\" className=\"dsh-ui-error\">{uiMessage(error, locale, saved.providers)}</p>}\n {loadError ? <div className=\"dsh-ui-stack\">\n <p role=\"alert\" className=\"dsh-ui-error\">{text.loadFailed}{uiMessage(loadError, locale, status?.providers)}</p>\n <Button variant=\"outline\" size=\"sm\" disabled={busy} onClick={() => void action(async () => { await load() })}>{text.reconnect}</Button>\n </div> : null}\n {note && <p role=\"status\" className=\"dsh-ui-notice\">{uiMessage(note, locale, saved.providers)}</p>}\n {saved.storageFailed && <p role=\"alert\" className=\"dsh-ui-banner dsh-ui-banner--danger\">{text.storageFailed}</p>}\n {(['search', 'fetch'] as const).map(kind => {\n const order = pendingOrder?.kind === kind ? pendingOrder.order : providerOrder(saved.settings, kind)\n const catalog = catalogBackends(saved, kind)\n const backends = [...order.flatMap(id => catalog.find(provider => provider.id === id) ?? []), ...catalog.filter(provider => !order.includes(provider.id))]\n const selected = selectedBackend(saved, kind)\n const on = kind === 'search' ? saved.settings.searchEnabled : saved.settings.fetchEnabled\n const canEnable = order.some(id => saved.providers.some(provider => provider.kind === kind && provider.id === id && provider.configured))\n const sortingOther = pendingOrder !== null && pendingOrder.kind !== kind\n const locked = busy || sortingOther || Boolean(dragSource && !dragSource.startsWith(kind + ':'))\n const title = kind === 'search' ? text.toolTitle : text.fetchTitle\n return <div key={kind} role=\"tabpanel\"\n id={tabsId + '-' + kind + '-panel'} aria-labelledby={tabsId + '-' + kind + '-tab'}\n hidden={tab !== kind} tabIndex={0}>\n {backends.length === 0 ? (kind === 'search' ? <p className=\"dsh-ui-empty\">{text.empty}</p> : null) : <article\n className=\"web-search-card dsh-ui-card dsh-ui-card--flat\"\n data-testid={kind === 'search' ? 'web-search-tool' : 'web-search-fetch'}\n data-on={on ? 'true' : 'false'}>\n <header className=\"ws-card-head\">\n <div>\n <h3 className=\"dsh-ui-title\">{title}</h3>\n {!canEnable && <p className=\"dsh-ui-hint\">{kind === 'search' ? text.needBackend : text.needFetchBackend}</p>}\n </div>\n <Switch\n checked={on}\n label={title}\n disabled={locked || dragSource !== null || (!on && !canEnable)}\n onChange={next => runImmediate(next, order, [], kind)}\n />\n </header>\n <ol className=\"web-search-rank\" aria-label={kind === 'search' ? text.backends : text.fetchBackends}>\n {backends.map(provider => {\n const participating = order.includes(provider.id)\n const rank = order.indexOf(provider.id)\n const dormant = !isRegistered(saved, provider.id, kind)\n const active = !dormant && (kind === 'search' ? saved.searchActive : saved.fetchActive) && selected?.id === provider.id\n const info = kind === 'search' || (ownsProviderConfiguration(provider) && provider.id === 'firecrawl') ? searchProviderInfo(provider, locale) : {\n description: ownsProviderConfiguration(provider) && provider.id === 'http-managed'\n && provider.description === '优先匿名 Jina Reader 读取,失败后本机 HTTP 直连;无需 Key。' && locale === 'en'\n ? 'Reads with anonymous Jina Reader first, then direct HTTP if needed; no key required.' : provider.description,\n pricing: provider.pricing ?? '', pricingUrl: provider.pricingUrl,\n }\n const owned = ownsProviderConfiguration(provider)\n const access = configurationAccess(provider)\n const needsKey = owned && Boolean(provider.credentialRef)\n const shared = Boolean(provider.credentialShared)\n const showKey = !dormant && needsKey && participating && (!shared || !provider.configured)\n const canSort = participating && order.length > 1\n const readOnly = writable[provider.credentialRef ?? ''] === false\n const typedKey = (keys[providerKey(provider.kind, provider.id)] ?? '').trim()\n const availability = dormant ? 'dormant' : provider.configured ? 'available' : 'unavailable'\n return <li key={provider.id} className={active ? 'is-active' : undefined} data-testid={`web-${kind}-rank-${provider.id}`}\n data-availability={availability}\n data-in-use={active || undefined}\n data-dragging={dragSource === providerKey(kind, provider.id) || undefined}\n data-drop={dropTarget === providerKey(kind, provider.id) && dragSource ? (order.indexOf(dragSource.slice(kind.length + 1)) < rank ? 'after' : 'before') : undefined}\n onDragOver={event => {\n if (locked || !canSort || !dragSource?.startsWith(kind + ':') || dragSource === providerKey(kind, provider.id)) return\n event.preventDefault()\n event.dataTransfer.dropEffect = 'move'\n setDropTarget(providerKey(kind, provider.id))\n }}\n onDragLeave={event => {\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) setDropTarget(null)\n }}\n onDrop={event => {\n if (locked || !canSort || !dragSource?.startsWith(kind + ':')) return\n event.preventDefault()\n move(kind, dragSource.slice(kind.length + 1), rank)\n endDrag()\n }}>\n {canSort ? <Button variant=\"ghost\" size=\"sm\" className=\"web-search-drag-handle\"\n ref={(element: HTMLButtonElement | null) => {\n if (element) handleRefs.current.set(providerKey(kind, provider.id), element)\n else handleRefs.current.delete(providerKey(kind, provider.id))\n }}\n icon={<svg width=\"16\" height=\"20\" viewBox=\"0 0 16 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <circle cx=\"5\" cy=\"5\" r=\"1.5\" /><circle cx=\"11\" cy=\"5\" r=\"1.5\" />\n <circle cx=\"5\" cy=\"10\" r=\"1.5\" /><circle cx=\"11\" cy=\"10\" r=\"1.5\" />\n <circle cx=\"5\" cy=\"15\" r=\"1.5\" /><circle cx=\"11\" cy=\"15\" r=\"1.5\" />\n </svg>}\n draggable={!locked} aria-disabled={locked}\n aria-label={text.dragPrefix + providerDisplayLabel(provider, locale)}\n title={text.dragHint}\n onDragStart={event => {\n if (locked || (pendingRef.current && pendingRef.current.kind !== kind)) { event.preventDefault(); return }\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData(`application/x-dsh-${kind}-provider`, provider.id)\n const row = event.currentTarget.closest('li')\n if (row) event.dataTransfer.setDragImage(row, 16, 16)\n dragRef.current = providerKey(kind, provider.id)\n requestSeq.current += 1\n setDragSource(providerKey(kind, provider.id))\n setSortAnnouncement('')\n }}\n onDragEnd={endDrag}\n onKeyDown={event => {\n if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') return\n event.preventDefault()\n move(kind, provider.id, rank + (event.key === 'ArrowUp' ? -1 : 1), true)\n }} /> : <span className=\"web-search-rank-index\">{participating ? rank + 1 : '—'}</span>}\n <div className=\"web-search-provider\">\n <div className=\"web-search-provider-heading\">\n <strong className=\"dsh-ui-heading\">{providerDisplayLabel(provider, locale)}</strong>\n <span className=\"ws-provider-note\">\n <StateDot state={dormant ? 'idle' : provider.configured ? 'done' : 'warning'} />\n <span>{dormant ? text.dormant : provider.configured ? text.available : text.unavailable}</span>\n </span>\n {active && <Tag tone=\"success\">{text.inUse}</Tag>}\n {provider.signupUrl && <AppLink url={provider.signupUrl} label={providerDisplayLabel(provider, locale) + ' ' + text.signup}>{text.signup}</AppLink>}\n </div>\n <div className=\"web-search-provider-info\">\n {info.description ? <p>{info.description}</p> : null}\n {info.pricing || info.pricingUrl ? <p>{info.pricing}{info.pricingUrl && <> <AppLink url={info.pricingUrl}\n label={providerDisplayLabel(provider, locale) + ' ' + text.pricing}>{provider.id === 'ddg' ? text.website : text.pricing}</AppLink></>}</p> : null}\n </div>\n {access.kind === 'link' ? <p className=\"dsh-ui-hint\"><AppLink url={access.url} label={text.openConfig}>{text.openConfig}</AppLink></p>\n : access.kind === 'owner' ? <p className=\"dsh-ui-hint\">{text.ownerSettings}{access.name}</p>\n : access.kind === 'unknown' ? <p className=\"dsh-ui-hint\">{text.unknownOwner}</p>\n : null}\n {shared && participating && owned ? <small className=\"dsh-ui-meta\">{provider.credentialHint === '与模型设置共用 Key'\n ? locale === 'zh' ? provider.credentialHint : 'Shares a key with model settings'\n : owned && provider.id === 'firecrawl' && locale === 'en' ? 'Search and page reads share a Firecrawl key'\n : provider.credentialHint ?? text.sharedKey}</small> : null}\n {showKey ? <label className=\"dsh-ui-field\">\n <span className=\"dsh-ui-label\">API Key</span>\n <Input className=\"ws-input\" type=\"password\" autoComplete=\"off\" spellCheck={false}\n disabled={busy || reordering || readOnly}\n value={keys[providerKey(provider.kind, provider.id)] ?? ''}\n placeholder={provider.configured ? text.keyConfigured : text.keyPlaceholder}\n onChange={event => setKeys(current => ({ ...current, [providerKey(provider.kind, provider.id)]: event.target.value }))} />\n </label> : null}\n {showKey || (needsKey && !shared && provider.configured) ? <div className=\"dsh-ui-actions\">\n {showKey ? <Button variant=\"outline\" size=\"sm\" disabled={busy || reordering || readOnly || !typedKey}\n onClick={() => void action(async () => { await saveKeys(provider); await load(); setNote({ key: 'keySaved' }) })}>\n {text.saveKey}</Button> : null}\n {needsKey && !shared && provider.configured ? <ConfirmButton\n disabled={busy || reordering || readOnly}\n label={text.deleteKey} confirmLabel={text.confirmDelete}\n onConfirm={() => void action(() => removeKey(provider))} /> : null}\n </div> : null}\n </div>\n <div className=\"web-search-rank-move\">\n <Switch\n checked={participating}\n label={providerDisplayLabel(provider, locale)}\n disabled={locked || dragSource !== null}\n onChange={next => {\n const following = next ? [...order, provider.id] : order.filter(id => id !== provider.id)\n runImmediate(\n on && following.some(id => saved.providers.some(item => item.kind === kind && item.id === id && item.configured)),\n following,\n droppedDormantIds(saved.settings, following, saved.providers, kind),\n kind,\n )\n }} />\n </div>\n </li>\n })}\n </ol>\n {kind === 'search' && backends.some(usesBundledPricing) ? <p className=\"dsh-ui-hint\">{pricingCheckedText(locale, PROVIDER_PRICING_CHECKED)}</p> : null}\n {on && kind === 'search' ? <div className=\"dsh-ui-actions\">\n <Button variant=\"outline\" size=\"md\" disabled={busy || reordering} onClick={() => void action(async () => {\n // Testing is explicit, so typed keys are saved first and the request uses them.\n await saveKeysForKind('search')\n const result = await call<{ sources: number }>('test')\n await load()\n setNote({ sources: result.sources })\n })}>{text.test}</Button>\n </div> : null}\n </article>}\n </div>\n })}\n <div role=\"tabpanel\" id={tabsId + '-limits-panel'} aria-labelledby={tabsId + '-limits-tab'}\n hidden={tab !== 'limits'} tabIndex={0}>\n <fieldset className=\"dsh-ui-fields-grid\" disabled={busy || reordering} aria-label={text.tabLimits}>\n {LIMITS.map(([key, min, max]) => (\n <label key={key} className=\"dsh-ui-field\">\n <span className=\"dsh-ui-label\">{text[key]}</span>\n <Input className=\"ws-input\" type=\"number\" min={min} max={max} step={1} value={limitDraft[key]}\n aria-invalid={invalid[key] || undefined}\n onChange={event => {\n const raw = event.target.value\n setLimitDraft(current => current ? { ...current, [key]: raw } : current)\n setInvalid(current => ({ ...current, [key]: false }))\n }}\n onBlur={() => setInvalid(current => ({ ...current, [key]: parseLimit(limitDraft[key], min, max) === undefined }))} />\n {invalid[key] ? <span className=\"dsh-ui-error\">{limitInvalidText(locale, text[key], min, max)}</span> : null}\n </label>\n ))}\n </fieldset>\n </div>\n <div className=\"dsh-ui-form-footer dsh-ui-actions-end\">\n <span className=\"dsh-ui-meta\">{limitsDirty || keysDirty ? text.unsaved : text.saveHint}</span>\n <Button variant=\"primary\" size=\"md\" disabled={busy || reordering} onClick={saveAll}>{text.save}</Button>\n </div>\n </section>\n}\n\nexport function apply(ctx: Context): void {\n const client = ctx as unknown as Client\n ctx.effect(() => {\n if (typeof document === 'undefined') return () => {}\n const style = document.createElement('style')\n style.setAttribute('data-plugin', '@klarkxy/dsh-web-search-manager'); style.textContent = webSearchClientStyles\n style.dataset.dshWebSearch = ''; document.head.appendChild(style)\n return () => style.remove()\n }, 'web-search-manager.styles')\n // `hidden` stays a test-only prop; production visibility comes from\n // document.visibilityState inside isPageHidden.\n ctx.effect(() => client.slots.inject('settings.section', () => [\n client.slots.register({\n name: 'settings.section', id: 'web-search-manager', order: 150,\n label: () => (client.locale?.getSnapshot().active ?? 'zh').startsWith('zh') ? '网页搜索' : 'Web Search',\n }, (props?: { hidden?: boolean }) => <NetworkSearchSettings client={client} hidden={props?.hidden} />),\n // The nav icon uses the official globe glyph; its wait rides the section's.\n client.slots.inject('settings.section.icon', () => client.slots.register(\n { name: 'settings.section.icon', id: 'web-search-manager', order: 150 }, IconGlobeOutlineRegular)),\n ]), 'web-search-manager.settings')\n}\n"],"mappings":"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"}
|
package/lib/client.js
CHANGED
|
@@ -537,6 +537,21 @@ const webSearchClientStyles = `${officialUiCss("ws-root")}
|
|
|
537
537
|
}
|
|
538
538
|
`;
|
|
539
539
|
//#endregion
|
|
540
|
+
//#region ../dsh-plugin-kit/src/contracts.ts
|
|
541
|
+
/** `@klarkxy/dsh-git-commit` becomes `KlarkxyDshGitCommitModelSettingsChanges`. */
|
|
542
|
+
function modelSettingsChangesIdentity(packageName) {
|
|
543
|
+
const scoped = /^@([^/]+)\/(.+)$/.exec(packageName);
|
|
544
|
+
const parts = (scoped ? `${scoped[1]}/${scoped[2]}` : packageName).split(/[^A-Za-z0-9]+/).filter(Boolean);
|
|
545
|
+
if (!parts.length) throw new Error("Model settings package name has no RPC segment");
|
|
546
|
+
const segment = `${parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("")}ModelSettingsChanges`;
|
|
547
|
+
if (!/^[A-Z][A-Za-z0-9]*$/.test(segment)) throw new Error(`Model settings sync name is not an RPC segment: ${segment}`);
|
|
548
|
+
return {
|
|
549
|
+
service: segment,
|
|
550
|
+
namespace: segment
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
modelSettingsChangesIdentity("@klarkxy/dsh-plugin-kit");
|
|
554
|
+
//#endregion
|
|
540
555
|
//#region ../dsh-plugin-kit/src/client-utils.ts
|
|
541
556
|
/** Fixed pause for failing background reads; manual work is never replayed. */
|
|
542
557
|
function createPluginReadGate(now = Date.now) {
|
|
@@ -1908,13 +1923,19 @@ function apply(ctx) {
|
|
|
1908
1923
|
document.head.appendChild(style);
|
|
1909
1924
|
return () => style.remove();
|
|
1910
1925
|
}, "web-search-manager.styles");
|
|
1911
|
-
ctx.effect(() => client.slots.inject("
|
|
1912
|
-
name: "
|
|
1913
|
-
|
|
1926
|
+
ctx.effect(() => client.slots.inject("settings.section", () => [client.slots.register({
|
|
1927
|
+
name: "settings.section",
|
|
1928
|
+
id: "web-search-manager",
|
|
1929
|
+
order: 150,
|
|
1930
|
+
label: () => (client.locale?.getSnapshot().active ?? "zh").startsWith("zh") ? "网页搜索" : "Web Search"
|
|
1914
1931
|
}, (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NetworkSearchSettings, {
|
|
1915
1932
|
client,
|
|
1916
1933
|
hidden: props?.hidden
|
|
1917
|
-
}))
|
|
1934
|
+
})), client.slots.inject("settings.section.icon", () => client.slots.register({
|
|
1935
|
+
name: "settings.section.icon",
|
|
1936
|
+
id: "web-search-manager",
|
|
1937
|
+
order: 150
|
|
1938
|
+
}, _deepseek_ai_dsh_client_ui_primitives.IconGlobeOutlineRegular))]), "web-search-manager.settings");
|
|
1918
1939
|
}
|
|
1919
1940
|
//#endregion
|
|
1920
1941
|
exports.NetworkSearchSettings = NetworkSearchSettings;
|
package/locale/en.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"meta": {
|
|
3
3
|
"title": "Web Search",
|
|
4
|
-
"description": "Manage and rank search and page-reading
|
|
4
|
+
"description": "Manage and rank web search and page-reading providers in one settings page, including capabilities from installed plugins, for the model's web_search and web_fetch tools."
|
|
5
5
|
}
|
|
6
6
|
}
|
package/locale/zh.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@klarkxy/dsh-web-search-manager",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "Manage and rank search and page-reading
|
|
3
|
+
"version": "0.3.3",
|
|
4
|
+
"description": "Manage and rank web search and page-reading providers in one settings page—with built-in DuckDuckGo, Exa, Tavily, and others—for the model's web_search and web_fetch tools.",
|
|
5
5
|
"icon": "./icon.svg",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
7
7
|
"type": "module",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
},
|
|
81
81
|
"dependencies": {
|
|
82
82
|
"@deepseek-ai/dsh-web-search-exa": ">=0.2.0-rc.2",
|
|
83
|
-
"@klarkxy/dsh-plugin-kit": "0.
|
|
83
|
+
"@klarkxy/dsh-plugin-kit": "0.6.1",
|
|
84
84
|
"zod": "^4.1.5"
|
|
85
85
|
},
|
|
86
86
|
"peerDependencies": {
|
|
@@ -132,11 +132,22 @@
|
|
|
132
132
|
"dsh-plugin",
|
|
133
133
|
"dsh-plugins",
|
|
134
134
|
"deepseek-harness",
|
|
135
|
+
"deepseek-harness-plugin",
|
|
135
136
|
"web-search",
|
|
137
|
+
"web-fetch",
|
|
138
|
+
"web_search",
|
|
139
|
+
"web_fetch",
|
|
136
140
|
"search-provider",
|
|
137
141
|
"page-fetching",
|
|
138
142
|
"duckduckgo",
|
|
139
|
-
"
|
|
143
|
+
"exa",
|
|
144
|
+
"tavily",
|
|
145
|
+
"brave",
|
|
146
|
+
"bocha",
|
|
147
|
+
"serper",
|
|
148
|
+
"perplexity",
|
|
149
|
+
"firecrawl",
|
|
150
|
+
"jina"
|
|
140
151
|
],
|
|
141
152
|
"repository": {
|
|
142
153
|
"type": "git",
|
|
@@ -152,6 +163,6 @@
|
|
|
152
163
|
"registry": "https://registry.npmjs.org/"
|
|
153
164
|
},
|
|
154
165
|
"dshRelease": {
|
|
155
|
-
"contentHash": "
|
|
166
|
+
"contentHash": "2da4175be7618890a273cbebf20821a169bc3f942ff7a630df167ade2da67458"
|
|
156
167
|
}
|
|
157
168
|
}
|