@deepseek-ai/dsh-client-ui-settings-plugin-inventory 0.1.6-alpha.1 → 0.1.7-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/client/ui-settings-plugin-inventory/README.md
5
- README.md: 40b7e41cd2d6953fdf3b9155fd6adff9d9d65391
6
- README.zh.md: 45f3444ea4fa56fe4a55517eb5d981913c790b15
5
+ README.md: 7353f14d111aa1c89c3331a3d4977ddfbf950294
6
+ README.zh.md: 9078f8074a10688cb7370ec7ee5fd8af16ca48c1
package/README.md CHANGED
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
9
9
 
10
10
  ## Summary
11
11
 
12
- The **Plugin list** tab lets Web users inspect plugins without changing their configuration. It presents agent presets first and collapses the global inventory until needed. Cards retain the package name as the primary title, identify instances by stable entry id, and expose enablement, source details, runtime status, disabled conditions, and discovery failures; preset-provided global entries name their presets. Search covers both groups and points to matches in other presets. The tab handles loading, empty, no-match, failure, and retry states without exposing transport details, and still shows the global inventory without a preset roster.
12
+ The **Plugin list** tab lets Web users inspect plugins without changing their configuration. It lists agent presets, then the global inventory; both start collapsed and open on demand or during a search. Cards show localized titles and descriptions when available, identify instances by stable entry id, and expose enablement, source details, runtime status, disabled conditions, and discovery failures; preset-provided global entries name their presets. Search covers both groups and points to matches in other presets. The tab handles loading, empty, no-match, failure, and retry states without exposing transport details, and still shows the global inventory without a preset roster.
13
13
 
14
14
  ## Table of Contents
15
15
 
@@ -27,9 +27,13 @@ The **Plugin list** tab lets Web users inspect plugins without changing their co
27
27
 
28
28
  Open the Plugins section in Settings and select the **Plugin list** tab to inspect the Host's plugin inventory. The tab reads no Remote during plugin activation — selecting it for the first time mounts the component and lazily calls `ctx.remote.pluginInventory.list()` through `api-remotes`.
29
29
 
30
+ A failed installation with pending pnpm build permissions offers **Allow these scripts and retry**. The action displays the exact package names and persistent permission scope, then retries the original package spec and activation choice. One click approves the entire displayed group, which can include pending packages from earlier attempts. Closing the page grants no permission.
31
+
30
32
  ### Reading a card
31
33
 
32
- Each collapsed card uses the short module name as its primary title, shows the stable entry id underneath, and carries a small enablement tag; enabled entries also show a colored root-fiber status dot. A composition-generated subtitle omits its leading `include:` marker, while hover, search, the accessible name, and expanded details retain the complete id. Long entry ids truncate in the row and remain available on hover. Expanding one card reveals the declared entry id, the full module specifier, and the state facts: a preset row names the preset it comes from, its runtime status when the composition is live, and its disable condition when it carries one; a preset-provided global row explains that agent presets provide it per session, names the presets that enable it, and offers a jump into the preset group. Preset names resolve through the shared `presetDisplayText` fold (`dsh-agent-presets/display`) over [`ui-agent-preset`](../ui-agent-preset/README.md)'s dictionaries: shipped presets follow the active locale while user-authored ones keep their own metadata, so an English surface never echoes the preset files' Chinese names. Search filters both groups by module name and entry id.
34
+ Each collapsed card uses the plugin's localized title and description when available. Settings shortens literal package-name or module-name fallbacks by removing the npm scope and the `cordis:`, `cordis-plugin-`, or `dsh-`/`dsh-host-`/`dsh-client-` prefix; locale titles remain unchanged. The Host supplies locale and package-field fallbacks in `meta`; the Client selects the current language. Full module specifiers remain available in details and search. Missing descriptions are omitted, and metadata diagnostics appear with the card. This compact naming applies only to Settings, not the sidebar's Plugins page.
35
+
36
+ The stable entry id appears underneath the title, alongside a small enablement tag; a colored root-fiber status dot marks only the phases the tag cannot state: `pending` maps to idle, while `loading` and `unloading` map to ongoing. An `active` or `failed` fiber shows its tag without a dot. A composition-generated subtitle omits its leading `include:` marker, while hover, search, the accessible name, and expanded details retain the complete id. Long entry ids truncate in the row and remain available on hover. Expanding one card reveals the declared entry id, the full module specifier, and the state facts: a preset row names the preset it comes from, its runtime status when the composition is live, and its disable condition when it carries one; a preset-provided global row explains that agent presets provide it per session, names the presets that enable it, and offers a jump into the preset group. Preset names resolve through the shared `presetDisplayText` fold (`dsh-agent-preset-registry/display`) over [`ui-agent-preset`](../ui-agent-preset/README.md)'s dictionaries: shipped presets follow the active locale while user-authored ones keep their own metadata, so an English surface never echoes the preset declarations' Chinese names. Search filters both groups by localized title and description, module name, and entry id.
33
37
 
34
38
  ### The preset switcher
35
39
 
@@ -37,7 +41,9 @@ The switcher is the same selector-pill-plus-menu control the General settings ro
37
41
 
38
42
  ### Retrying a failed read
39
43
 
40
- A failed read renders a generic failure state inside the tab; retrying re-runs the lazy `list()` call without exposing transport details.
44
+ A failed read renders the shared error marker inside the tab; loading and current-page synchronization use the shared ongoing loader. Retrying re-runs the lazy `list()` call without exposing transport details.
45
+
46
+ The Plugin list also shows synchronization failures on the current page. Its retry reapplies the latest client graph without changing Host enablement or refreshing the page.
41
47
 
42
48
  -----
43
49
 
package/README.zh.md CHANGED
@@ -9,7 +9,7 @@ kind: "package-reference"
9
9
 
10
10
  ## 概述
11
11
 
12
- **插件列表**标签页让 Web 用户查看插件,而不改变其配置。它优先展示 agent(智能体)预设,并让全局清单保持收起,仅在需要时展开。卡片保留包名作为主标题,以稳定的条目 id 标识实例,并展示启停状态、出处、运行状态、禁用条件与发现失败;由预设提供的全局条目会列出对应预设。搜索覆盖两个分组,并指出其他预设中的匹配。标签页处理加载、空结果、无匹配、失败与重试状态,且不暴露传输细节;没有预设 roster 时仍会展示全局清单。
12
+ **插件列表**标签页让 Web 用户查看插件,而不改变其配置。它先列 agent(智能体)预设分组,再列全局清单;两个分组默认都收起,按需展开,搜索期间保持展开。卡片显示可用的本地化标题与描述,以稳定的条目 id 标识实例,并展示启停状态、出处、运行状态、禁用条件与发现失败;由预设提供的全局条目会列出对应预设。搜索覆盖两个分组,并指出其他预设中的匹配。标签页处理加载、空结果、无匹配、失败与重试状态,且不暴露传输细节;没有预设 roster 时仍会展示全局清单。
13
13
 
14
14
  ## 目录
15
15
 
@@ -27,9 +27,13 @@ kind: "package-reference"
27
27
 
28
28
  打开设置中的「插件」分区并选择**插件列表**标签页,即可查看宿主的插件清单。插件激活期间不会读取 Remote——首次选择该标签页时才挂载组件,并通过 `api-remotes` 懒调用 `ctx.remote.pluginInventory.list()`。
29
29
 
30
+ 安装失败且存在待审批的 pnpm 构建权限时,页面提供“允许这些脚本并重试”。该操作展示准确包名和持久授权范围,再使用原来的包规格及启用选项重试。一次点击批准整个列表,其中可能包括此前安装留下的待审批包。关闭页面不会授予权限。
31
+
30
32
  ### 阅读卡片
31
33
 
32
- 每张收起的卡片使用模块短名称作为主标题,在下方显示稳定的条目 id,并以小标签表示启停状态;已启用的条目还会显示彩色根 fiber 状态圆点。组合生成的次标题省略开头的 `include:` 标记;悬停、搜索、无障碍名称与展开详情仍保留完整 id。长条目 id 会在行内截断,悬停时仍可查看完整值。展开卡片后会显示声明的条目 id、完整模块标识与状态事实:预设行说明它来自哪个预设、组合存活时的运行状态,以及它携带的禁用条件;被预设提供的全局行说明它由 Agent 预设按会话提供、列出启用它的预设,并提供跳转到预设组的入口。预设名经共享的 `presetDisplayText` 纯函数(`dsh-agent-presets/display`)叠在 [`ui-agent-preset`](../ui-agent-preset/README.zh.md) 的字典上解析:内置预设走当前语言,用户自建预设保留自己的元数据,因此英文界面不会回显预设文件里的中文名。搜索按模块名称与条目 id 过滤两组。
34
+ 每张收起的卡片使用插件可用的本地化标题与描述。设置页会缩短字面的包名或模块名回退值,移除 npm scope 以及 `cordis:`、`cordis-plugin-` 或 `dsh-`/`dsh-host-`/`dsh-client-` 前缀;locale 标题保持原样。Host 在 `meta` 中提供 locale 与包字段的回退结果,Client 选择当前语言。详情与搜索仍保留完整模块标识。缺失的描述不显示,元信息诊断随卡片展示。这种短名显示仅用于设置页,不影响侧栏的插件页。
35
+
36
+ 稳定的条目 id 显示在标题下方,卡片带有表示启停状态的小标签;彩色根 fiber 状态圆点只标记标签无法表达的阶段:`pending` 映射为 idle,`loading` 与 `unloading` 映射为 ongoing。`active` 或 `failed` fiber 只显示标签,不显示圆点。组合生成的次标题省略开头的 `include:` 标记;悬停、搜索、无障碍名称与展开详情仍保留完整 id。长条目 id 会在行内截断,悬停时仍可查看完整值。展开卡片后会显示声明的条目 id、完整模块标识与状态事实:预设行说明它来自哪个预设、组合存活时的运行状态,以及它携带的禁用条件;被预设提供的全局行说明它由 Agent 预设按会话提供、列出启用它的预设,并提供跳转到预设组的入口。预设名经共享的 `presetDisplayText` 纯函数(`dsh-agent-preset-registry/display`)叠在 [`ui-agent-preset`](../ui-agent-preset/README.zh.md) 的字典上解析:内置预设走当前语言,用户自建预设保留自己的元数据,因此英文界面不会回显预设声明里的中文名。搜索按本地化标题与描述、模块名称及条目 id 过滤两组。
33
37
 
34
38
  ### 预设切换器
35
39
 
@@ -37,7 +41,9 @@ kind: "package-reference"
37
41
 
38
42
  ### 重试失败的读取
39
43
 
40
- 读取失败会在标签页内渲染通用失败状态;重试会重新执行懒 `list()` 调用,且不会暴露传输细节。
44
+ 读取失败会在标签页内渲染共享 error 标记;加载与当前页面同步使用共享 ongoing loading。重试会重新执行懒 `list()` 调用,且不会暴露传输细节。
45
+
46
+ 插件列表还显示当前页面的同步失败。重试会重新应用最新客户端图,不改变 Host 启用状态,也不刷新页面。
41
47
 
42
48
  -----
43
49
 
package/lib/client.js CHANGED
@@ -7,7 +7,7 @@ window.__ModuleLoader__.load({
7
7
  let react_jsx_runtime = require("react/jsx-runtime");
8
8
  let react = require("react");
9
9
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
10
- //#region ../../preset/agent-presets/src/display.ts
10
+ //#region ../../preset/agent-preset-registry/src/display.ts
11
11
  const BUILT_IN_PRESET_KEYS = {
12
12
  standard: {
13
13
  name: "presetStandardName",
@@ -27,13 +27,22 @@ window.__ModuleLoader__.load({
27
27
  }
28
28
  };
29
29
  /**
30
+ * Whether a roster row is one of the shipped presets whose copy the dictionaries carry.
31
+ * A shipped preset publishes no `name`; a declaration that names itself owns its copy.
32
+ * @param preset - roster row.
33
+ * @returns true for a shipped preset id without a published name.
34
+ */
35
+ function isBuiltInPreset(preset) {
36
+ return preset.name === void 0 && BUILT_IN_PRESET_KEYS[preset.id] !== void 0;
37
+ }
38
+ /**
30
39
  * Resolve preset display copy without making user-authored metadata translatable.
31
40
  * @param preset - roster row whose copy is being rendered.
32
41
  * @param t - active locale lookup covering {@link BuiltInPresetCopyKey}.
33
- * @returns localized copy for a known shipped preset, otherwise file metadata.
42
+ * @returns localized copy for a known shipped preset, otherwise declaration metadata.
34
43
  */
35
44
  function presetDisplayText(preset, t) {
36
- const keys = preset.trust === "system" ? BUILT_IN_PRESET_KEYS[preset.id] : void 0;
45
+ const keys = isBuiltInPreset(preset) ? BUILT_IN_PRESET_KEYS[preset.id] : void 0;
37
46
  if (keys !== void 0) return {
38
47
  name: t(keys.name),
39
48
  description: t(keys.description)
@@ -45,7 +54,7 @@ window.__ModuleLoader__.load({
45
54
  }
46
55
  //#endregion
47
56
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-settings-plugin-inventory/src/client/PluginInventorySettingsTab.module.css.mjs
48
- const css = ".qSYn7G_section{width:100%;max-width:760px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:14px;display:flex}.qSYn7G_catalogHeading h3,.qSYn7G_status,.qSYn7G_failure p{margin:0}.qSYn7G_status,.qSYn7G_failure{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px}.qSYn7G_failure{color:var(--dsw-alias-state-error-primary);align-items:center;gap:10px;display:flex}.qSYn7G_failure button{border:.5px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;background:0 0;border-radius:6px;padding:4px 10px}.qSYn7G_catalog{flex-direction:column;gap:12px;display:flex}.qSYn7G_search{width:100%;color:var(--dsw-alias-label-tertiary);align-items:center;display:flex;position:relative}.qSYn7G_search>svg{pointer-events:none;position:absolute;left:12px}.qSYn7G_search input{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-1);width:100%;height:36px;color:var(--dsw-alias-label-primary);font:inherit;border-radius:10px;outline:none;padding:0 34px 0 36px;font-size:13px}.qSYn7G_search input::placeholder{color:var(--dsw-alias-label-tertiary)}.qSYn7G_search input:focus-visible{border-color:var(--dsw-alias-state-business-primary);box-shadow:0 0 0 2px color-mix(in srgb, var(--dsw-alias-state-business-primary) 18%, transparent)}.qSYn7G_catalogHeading{align-items:baseline;gap:7px;padding:0 2px;display:flex}.qSYn7G_catalogHeading h3{font-size:13px;font-weight:600;line-height:20px}.qSYn7G_catalogHeading span{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:12px;line-height:18px}.qSYn7G_cards{grid-template-columns:repeat(2,minmax(0,1fr));align-items:start;gap:10px;margin:0;padding:0;list-style:none;display:grid}.qSYn7G_card{min-width:0;box-shadow:var(--dsw-elevation-stroke);background:var(--dsw-alias-bg-layer-3);border:0;border-radius:14px;overflow:hidden}.qSYn7G_card[data-open=true]{--dsw-elevation-stroke-color:var(--dsw-alias-border-l1);box-shadow:var(--dsw-elevation-panel)}.qSYn7G_cardContent{box-sizing:border-box;width:100%;min-height:52px;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;flex-direction:column;align-items:stretch;padding:12px 14px;display:flex}.qSYn7G_cardContent:hover,.qSYn7G_card[data-open=true]>.qSYn7G_cardContent{background:var(--dsw-alias-interactive-bg-hover)}.qSYn7G_cardContent:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-2px}.qSYn7G_cardMainRow{justify-content:space-between;align-items:center;gap:12px;min-width:0;display:flex}.qSYn7G_cardTitle,.qSYn7G_cardIdentity{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.qSYn7G_cardTitle{flex:1;min-width:0;font-size:14px;font-weight:600;line-height:20px}.qSYn7G_cardIdentity{width:100%;color:var(--dsw-alias-label-tertiary);font-family:var(--ds-font-family-code);font-size:11px;line-height:16px;display:block}.qSYn7G_cardTrailing{color:var(--dsw-alias-label-tertiary);flex:none;align-items:center;gap:7px;display:inline-flex}.qSYn7G_phaseDot{flex:none;display:inline-flex}.qSYn7G_group{flex-direction:column;gap:10px;display:flex}.qSYn7G_groupTitleRow{align-items:center;gap:8px;min-height:32px;display:flex}.qSYn7G_group+.qSYn7G_group{border-top:.5px solid var(--dsw-alias-border-l2);padding-top:14px}.qSYn7G_headerEnd{margin-left:auto}.qSYn7G_groupToggle{color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;flex:none;align-items:center;gap:8px;padding:0;display:flex}.qSYn7G_groupToggle:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}.qSYn7G_groupToggle>.qSYn7G_chevron{transform:rotate(-90deg)}.qSYn7G_groupToggle[aria-expanded=true]>.qSYn7G_chevron{transform:none}.qSYn7G_groupTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}.qSYn7G_groupSub{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;flex-wrap:wrap;gap:4px 8px;margin:-6px 0 0 20px;font-size:12px;line-height:18px;display:flex}.qSYn7G_failedCount{color:var(--dsw-alias-state-error-primary)}.qSYn7G_switcher{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);height:36px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;border:none;border-radius:18px;flex:none;align-items:center;gap:12px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.qSYn7G_switcher:hover{background:var(--dsw-alias-interactive-bg-hover)}.qSYn7G_switcher:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}.qSYn7G_switcher>.qSYn7G_chevron{flex:none}.qSYn7G_switcherLabel{text-overflow:ellipsis;max-width:240px;overflow:hidden}.qSYn7G_groupBody{flex-direction:column;gap:10px;display:flex}.qSYn7G_brokenNote{background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 8%, transparent);color:var(--dsw-alias-state-error-primary);overflow-wrap:anywhere;white-space:pre-line;border-radius:8px;margin:0;padding:8px 10px;font-size:12.5px;line-height:18px}.qSYn7G_hint{color:var(--dsw-alias-label-tertiary);flex-wrap:wrap;align-items:baseline;gap:4px 8px;margin:0;font-size:12.5px;line-height:18px;display:flex}.qSYn7G_jumpLink{color:var(--dsw-alias-state-business-primary);font:inherit;cursor:pointer;background:0 0;border:0;padding:0;font-size:12.5px}.qSYn7G_enabledIn{flex-wrap:wrap;align-items:baseline;gap:4px 10px;display:flex}.qSYn7G_card[data-failed=true]{border-color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 45%, transparent)}.qSYn7G_chevron{color:var(--dsw-alias-label-tertiary);flex:none}.qSYn7G_card[data-open=true] .qSYn7G_chevron{transform:rotate(180deg)}.qSYn7G_cardDetails{border-top:.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-module-platform);padding:10px 14px 12px}.qSYn7G_entryValue{overflow-wrap:anywhere;color:var(--dsw-alias-label-primary);font-family:var(--ds-font-family-code);font-size:12px;line-height:18px;display:block}.qSYn7G_details{grid-template-columns:76px minmax(0,1fr);gap:6px 10px;margin:8px 0 0;display:grid}.qSYn7G_details div{display:contents}.qSYn7G_details dt{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:17px}.qSYn7G_details dd{overflow-wrap:anywhere;min-width:0;color:var(--dsw-alias-label-secondary);margin:0;font-size:12px;line-height:17px}.qSYn7G_visuallyHidden{clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}@media (prefers-reduced-motion:no-preference){.qSYn7G_chevron{transition:transform .14s var(--ds-ease-in-out)}}@media (width<=680px){.qSYn7G_cards{grid-template-columns:minmax(0,1fr)}}";
57
+ const css = ".qSYn7G_section{width:100%;max-width:760px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:14px;display:flex}.qSYn7G_catalogHeading h3,.qSYn7G_status,.qSYn7G_failure p{margin:0}.qSYn7G_status,.qSYn7G_failure{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px}.qSYn7G_failure{color:var(--dsw-alias-state-error-primary);align-items:center;gap:10px;display:flex}.qSYn7G_statusWithDot{align-items:center;gap:6px;display:inline-flex}.qSYn7G_failure button{border:.5px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;background:0 0;border-radius:6px;padding:4px 10px}.qSYn7G_catalog{flex-direction:column;gap:12px;display:flex}.qSYn7G_search{width:100%;color:var(--dsw-alias-label-tertiary);align-items:center;display:flex;position:relative}.qSYn7G_search>svg{pointer-events:none;position:absolute;left:12px}.qSYn7G_search input{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-1);width:100%;height:36px;color:var(--dsw-alias-label-primary);font:inherit;border-radius:10px;outline:none;padding:0 34px 0 36px;font-size:13px}.qSYn7G_search input::placeholder{color:var(--dsw-alias-label-tertiary)}.qSYn7G_search input:focus-visible{border-color:var(--dsw-alias-state-business-primary);box-shadow:0 0 0 2px color-mix(in srgb, var(--dsw-alias-state-business-primary) 18%, transparent)}.qSYn7G_catalogHeading{align-items:baseline;gap:7px;padding:0 2px;display:flex}.qSYn7G_catalogHeading h3{font-size:13px;font-weight:600;line-height:20px}.qSYn7G_catalogHeading span{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:12px;line-height:18px}.qSYn7G_cards{grid-template-columns:repeat(2,minmax(0,1fr));align-items:start;gap:10px;margin:0;padding:0;list-style:none;display:grid}.qSYn7G_card{min-width:0;box-shadow:var(--dsw-elevation-stroke);background:var(--dsw-alias-bg-layer-3);border:0;border-radius:14px;overflow:hidden}.qSYn7G_card[data-open=true]{--dsw-elevation-stroke-color:var(--dsw-alias-border-l1);box-shadow:var(--dsw-elevation-panel)}.qSYn7G_cardContent{box-sizing:border-box;width:100%;min-height:52px;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;flex-direction:column;align-items:stretch;padding:12px 14px;display:flex}.qSYn7G_cardContent:hover,.qSYn7G_card[data-open=true]>.qSYn7G_cardContent{background:var(--dsw-alias-interactive-bg-hover)}.qSYn7G_cardContent:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-2px}.qSYn7G_cardMainRow{justify-content:space-between;align-items:center;gap:12px;min-width:0;display:flex}.qSYn7G_cardTitle,.qSYn7G_cardIdentity{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.qSYn7G_cardTitle{flex:1;min-width:0;font-size:14px;font-weight:600;line-height:20px}.qSYn7G_cardIdentity,.qSYn7G_entryValue{font-family:var(--ds-font-family-code);font-size:12px;line-height:18px}.qSYn7G_cardIdentity{width:100%;color:var(--dsw-alias-label-tertiary);display:block}.qSYn7G_cardTrailing{color:var(--dsw-alias-label-tertiary);flex:none;align-items:center;gap:7px;display:inline-flex}.qSYn7G_phaseDot{flex:none;display:inline-flex}.qSYn7G_group{flex-direction:column;gap:10px;display:flex}.qSYn7G_groupTitleRow{align-items:center;gap:8px;min-height:32px;display:flex}.qSYn7G_group+.qSYn7G_group{border-top:.5px solid var(--dsw-alias-border-l2);padding-top:14px}.qSYn7G_headerEnd{margin-left:auto}.qSYn7G_groupToggle{color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;flex:none;align-items:center;gap:8px;padding:0;display:flex}.qSYn7G_groupToggle:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}.qSYn7G_groupToggle>.qSYn7G_chevron{transform:rotate(-90deg)}.qSYn7G_groupToggle[aria-expanded=true]>.qSYn7G_chevron{transform:none}.qSYn7G_groupTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}.qSYn7G_groupSub{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;flex-wrap:wrap;gap:4px 8px;margin:-6px 0 0 20px;font-size:12px;line-height:18px;display:flex}.qSYn7G_failedCount{color:var(--dsw-alias-state-error-primary)}.qSYn7G_switcher{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);height:36px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;border:none;border-radius:18px;flex:none;align-items:center;gap:12px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.qSYn7G_switcher:hover{background:var(--dsw-alias-interactive-bg-hover)}.qSYn7G_switcher:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}.qSYn7G_switcher>.qSYn7G_chevron{flex:none}.qSYn7G_switcherLabel{text-overflow:ellipsis;max-width:240px;overflow:hidden}.qSYn7G_groupBody{flex-direction:column;gap:10px;display:flex}.qSYn7G_brokenNote{background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 8%, transparent);color:var(--dsw-alias-state-error-primary);overflow-wrap:anywhere;white-space:pre-line;border-radius:8px;margin:0;padding:8px 10px;font-size:12.5px;line-height:18px}.qSYn7G_hint{color:var(--dsw-alias-label-tertiary);flex-wrap:wrap;align-items:baseline;gap:4px 8px;margin:0;font-size:12.5px;line-height:18px;display:flex}.qSYn7G_jumpLink{color:var(--dsw-alias-state-business-primary);font:inherit;cursor:pointer;background:0 0;border:0;padding:0;font-size:12.5px}.qSYn7G_enabledIn{flex-wrap:wrap;align-items:baseline;gap:4px 10px;display:flex}.qSYn7G_card[data-failed=true]{border-color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 45%, transparent)}.qSYn7G_chevron{color:var(--dsw-alias-label-tertiary);flex:none}.qSYn7G_card[data-open=true] .qSYn7G_chevron{transform:rotate(180deg)}.qSYn7G_cardDetails{border-top:.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-module-platform);padding:10px 14px 12px}.qSYn7G_entryValue{overflow-wrap:anywhere;color:var(--dsw-alias-label-primary);display:block}.qSYn7G_details{grid-template-columns:76px minmax(0,1fr);gap:6px 10px;margin:8px 0 0;display:grid}.qSYn7G_details div{display:contents}.qSYn7G_details dt{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:17px}.qSYn7G_details dd{overflow-wrap:anywhere;min-width:0;color:var(--dsw-alias-label-secondary);margin:0;font-size:12px;line-height:17px}.qSYn7G_visuallyHidden{clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}@media (prefers-reduced-motion:no-preference){.qSYn7G_chevron{transition:transform .14s var(--ds-ease-in-out)}}@media (width<=680px){.qSYn7G_cards{grid-template-columns:minmax(0,1fr)}}";
49
58
  const tagId = "@deepseek-ai/dsh-client-ui-settings-plugin-inventory/PluginInventorySettingsTab.module.css";
50
59
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
51
60
  const tag = document.createElement("style");
@@ -85,6 +94,7 @@ window.__ModuleLoader__.load({
85
94
  "search": "qSYn7G_search",
86
95
  "section": "qSYn7G_section",
87
96
  "status": "qSYn7G_status",
97
+ "statusWithDot": "qSYn7G_statusWithDot",
88
98
  "switcher": "qSYn7G_switcher",
89
99
  "switcherLabel": "qSYn7G_switcherLabel",
90
100
  "visuallyHidden": "qSYn7G_visuallyHidden"
@@ -102,7 +112,7 @@ window.__ModuleLoader__.load({
102
112
  function phaseLabel(phase, t) {
103
113
  return phase === null ? t("unobserved") : t(PHASE_KEYS[phase]);
104
114
  }
105
- /** Compact a module specifier without guessing whether its Loader id was generated. */
115
+ /** Compact technical names for Settings without changing their module identity. */
106
116
  function moduleShortName(moduleName) {
107
117
  return (moduleName.startsWith("@") ? moduleName.slice(moduleName.indexOf("/") + 1) : moduleName).replace(/^cordis:/, "").replace(/^cordis-plugin-/, "").replace(/^dsh-(?:host-|client-)?/, "");
108
118
  }
@@ -110,10 +120,24 @@ window.__ModuleLoader__.load({
110
120
  function entrySubtitle(entryId) {
111
121
  return entryId.replace(/^include:/, "");
112
122
  }
113
- /** Whether one row's module name or entry id matches the catalog query. */
114
- function matches(moduleName, entryId, normalizedQuery) {
123
+ /** Preserve translated titles and shorten literal package or module name fallbacks in Settings. */
124
+ function pluginText(row, resolveText) {
125
+ const title = row.meta?.title;
126
+ return {
127
+ title: typeof title === "object" ? resolveText(title) : moduleShortName(title ?? row.moduleName),
128
+ description: row.meta?.description === void 0 ? void 0 : resolveText(row.meta.description) || void 0
129
+ };
130
+ }
131
+ /** Match translated text alongside the row's technical module and entry identities. */
132
+ function matches(row, normalizedQuery, resolveText) {
115
133
  if (normalizedQuery.length === 0) return true;
116
- return [moduleName, ...entryId === null ? [] : [entryId]].some((value) => value.toLocaleLowerCase().includes(normalizedQuery));
134
+ const { title, description } = pluginText(row, resolveText);
135
+ return [
136
+ row.moduleName,
137
+ row.entryId,
138
+ title,
139
+ description
140
+ ].some((value) => value?.toLocaleLowerCase().includes(normalizedQuery));
117
141
  }
118
142
  /** The roster row shown when the preset switcher has no explicit choice. */
119
143
  function fallbackPreset(presets) {
@@ -127,48 +151,67 @@ window.__ModuleLoader__.load({
127
151
  return name;
128
152
  }
129
153
  /** One expandable plugin card; the caller owns the trailing status content. */
130
- function PluginCard({ rowKey, moduleName, entryId, trailing, ariaLabel, failed, expanded, onToggle, children }) {
154
+ function PluginCard({ rowKey, moduleName, title, description, metadataError, entryId, trailing, ariaLabel, failed, expanded, onToggle, children }) {
131
155
  const open = expanded === rowKey;
132
156
  const detailId = `plugin-details-${encodeURIComponent(rowKey)}`;
157
+ const descriptionId = (0, react.useId)();
133
158
  return (0, react_jsx_runtime.jsxs)("li", {
134
159
  className: PluginInventorySettingsTab_module_css_default.card,
135
160
  "data-plugin-entry": entryId ?? void 0,
136
161
  "data-plugin-module": moduleName,
137
162
  "data-failed": failed ? "true" : void 0,
138
163
  "data-open": open ? "true" : void 0,
139
- children: [(0, react_jsx_runtime.jsxs)("button", {
140
- className: PluginInventorySettingsTab_module_css_default.cardContent,
141
- type: "button",
142
- "aria-expanded": open,
143
- "aria-controls": detailId,
144
- "aria-label": ariaLabel,
145
- onClick: () => {
146
- onToggle(rowKey);
147
- },
148
- children: [(0, react_jsx_runtime.jsxs)("span", {
149
- className: PluginInventorySettingsTab_module_css_default.cardMainRow,
150
- children: [(0, react_jsx_runtime.jsx)("strong", {
151
- className: PluginInventorySettingsTab_module_css_default.cardTitle,
152
- title: moduleName,
153
- children: moduleShortName(moduleName)
154
- }), (0, react_jsx_runtime.jsxs)("span", {
155
- className: PluginInventorySettingsTab_module_css_default.cardTrailing,
156
- children: [trailing, (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {
157
- className: PluginInventorySettingsTab_module_css_default.chevron,
158
- size: 12,
159
- "aria-hidden": "true"
160
- })]
161
- })]
162
- }), entryId === null ? null : (0, react_jsx_runtime.jsx)("code", {
163
- className: PluginInventorySettingsTab_module_css_default.cardIdentity,
164
- title: entryId,
165
- children: entrySubtitle(entryId)
166
- })]
167
- }), open ? (0, react_jsx_runtime.jsx)("div", {
168
- className: PluginInventorySettingsTab_module_css_default.cardDetails,
169
- id: detailId,
170
- children
171
- }) : null]
164
+ children: [
165
+ (0, react_jsx_runtime.jsxs)("button", {
166
+ className: PluginInventorySettingsTab_module_css_default.cardContent,
167
+ type: "button",
168
+ "aria-expanded": open,
169
+ "aria-controls": detailId,
170
+ "aria-label": ariaLabel,
171
+ "aria-describedby": description === void 0 ? void 0 : descriptionId,
172
+ onClick: () => {
173
+ onToggle(rowKey);
174
+ },
175
+ children: [
176
+ (0, react_jsx_runtime.jsxs)("span", {
177
+ className: PluginInventorySettingsTab_module_css_default.cardMainRow,
178
+ children: [(0, react_jsx_runtime.jsx)("strong", {
179
+ className: PluginInventorySettingsTab_module_css_default.cardTitle,
180
+ title: moduleName,
181
+ children: title
182
+ }), (0, react_jsx_runtime.jsxs)("span", {
183
+ className: PluginInventorySettingsTab_module_css_default.cardTrailing,
184
+ children: [trailing, (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, {
185
+ className: PluginInventorySettingsTab_module_css_default.chevron,
186
+ size: 12,
187
+ "aria-hidden": "true"
188
+ })]
189
+ })]
190
+ }),
191
+ description === void 0 ? null : (0, react_jsx_runtime.jsx)("span", {
192
+ className: PluginInventorySettingsTab_module_css_default.hint,
193
+ id: descriptionId,
194
+ children: description
195
+ }),
196
+ entryId === null ? null : (0, react_jsx_runtime.jsx)("code", {
197
+ className: PluginInventorySettingsTab_module_css_default.cardIdentity,
198
+ title: entryId,
199
+ children: entrySubtitle(entryId)
200
+ })
201
+ ]
202
+ }),
203
+ metadataError === void 0 ? null : (0, react_jsx_runtime.jsx)("p", {
204
+ className: PluginInventorySettingsTab_module_css_default.brokenNote,
205
+ role: "status",
206
+ "data-package-meta-error": true,
207
+ children: metadataError
208
+ }),
209
+ open ? (0, react_jsx_runtime.jsx)("div", {
210
+ className: PluginInventorySettingsTab_module_css_default.cardDetails,
211
+ id: detailId,
212
+ children
213
+ }) : null
214
+ ]
172
215
  });
173
216
  }
174
217
  /** Detail rows shared by every card: the Loader identity, then labeled facts. */
@@ -185,11 +228,13 @@ window.__ModuleLoader__.load({
185
228
  const PHASE_DOT_STATES = {
186
229
  pending: "idle",
187
230
  loading: "ongoing",
188
- active: "done",
189
- failed: "error",
190
231
  unloading: "ongoing"
191
232
  };
192
- /** Status dot naming a live root-fiber phase; rows with no live fiber show none. */
233
+ /** Whether a live root-fiber phase carries a dot of its own. */
234
+ function showsPhaseDot(phase) {
235
+ return phase === "pending" || phase === "loading" || phase === "unloading";
236
+ }
237
+ /** Status dot naming a live root-fiber phase; rows without a dotted phase show none. */
193
238
  function PhaseDot({ phase, t }) {
194
239
  const status = phaseLabel(phase, t);
195
240
  return (0, react_jsx_runtime.jsx)("span", {
@@ -215,7 +260,8 @@ window.__ModuleLoader__.load({
215
260
  });
216
261
  }
217
262
  /** Render the read-only plugin inventory: agent presets first, then the global plane. */
218
- function PluginInventorySettingsTab({ list, presetName, t }) {
263
+ function PluginInventorySettingsTab({ list, presetName, resolveText, t, useClientSync, retryClient }) {
264
+ const clientSync = useClientSync((snapshot) => snapshot);
219
265
  const sectionId = (0, react.useId)();
220
266
  const [request, setRequest] = (0, react.useState)(0);
221
267
  const [query, setQuery] = (0, react.useState)("");
@@ -260,16 +306,16 @@ window.__ModuleLoader__.load({
260
306
  const regularEntries = [];
261
307
  for (const entry of entries) if (entry.fiberPhase === "failed") failedEntries.push(entry);
262
308
  else regularEntries.push(entry);
263
- const entryMatch = (entry) => matches(entry.moduleName, entry.entryId, normalizedQuery);
264
- const rowMatch = (row) => matches(row.moduleName, row.entryId, normalizedQuery);
309
+ const entryMatch = (entry) => matches(entry, normalizedQuery, resolveText);
310
+ const rowMatch = (row) => matches(row, normalizedQuery, resolveText);
265
311
  const filteredFailed = failedEntries.filter(entryMatch);
266
312
  const filteredRegular = regularEntries.filter(entryMatch);
267
313
  const globalCount = filteredFailed.length + filteredRegular.length;
268
314
  const selectedRows = selected === void 0 ? [] : selected.rows.filter(rowMatch);
269
315
  const otherPresetMatches = searching ? presets.filter((preset) => preset !== selected && preset.rows.some(rowMatch)) : [];
270
316
  const otherMatchCount = otherPresetMatches.reduce((total, preset) => total + preset.rows.filter(rowMatch).length, 0);
271
- const presetEffectiveOpen = searching || (presetOpen ?? true);
272
- const globalEffectiveOpen = searching || (globalOpen ?? presets.length === 0);
317
+ const presetEffectiveOpen = searching || (presetOpen ?? false);
318
+ const globalEffectiveOpen = searching || (globalOpen ?? false);
273
319
  const nothingMatches = searching && globalCount === 0 && selectedRows.length === 0 && otherPresetMatches.length === 0;
274
320
  const retry = () => {
275
321
  setState({ status: "loading" });
@@ -281,19 +327,22 @@ window.__ModuleLoader__.load({
281
327
  /** Trailing status and detail facts for one row of the selected preset. */
282
328
  const presetRowCard = (preset, row, index) => {
283
329
  const key = `preset:${preset.id}:${String(index)}`;
284
- const title = moduleShortName(row.moduleName);
330
+ const { title, description } = pluginText(row, resolveText);
285
331
  const failed = row.fiberPhase === "failed";
286
332
  const stateText = failed ? t("failedTag") : row.enabled === true ? t("enabledTag") : row.enabled === false ? t("disabledTag") : t("conditionalTag");
287
333
  const kind = failed ? "failed" : row.enabled === true ? "enabled" : row.enabled === false ? "disabled" : "conditional";
288
334
  return (0, react_jsx_runtime.jsx)(PluginCard, {
289
335
  rowKey: key,
290
336
  moduleName: row.moduleName,
337
+ title,
338
+ description,
339
+ metadataError: row.meta?.error === void 0 ? void 0 : t("metadataError", { error: row.meta.error }),
291
340
  entryId: row.entryId,
292
341
  failed,
293
342
  expanded,
294
343
  onToggle: toggleRow,
295
344
  ariaLabel: `${title}${row.entryId === null ? "" : `, ${row.entryId}`}, ${stateText}`,
296
- trailing: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [row.enabled === true && !failed && row.fiberPhase !== null ? (0, react_jsx_runtime.jsx)(PhaseDot, {
345
+ trailing: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [row.enabled === true && showsPhaseDot(row.fiberPhase) ? (0, react_jsx_runtime.jsx)(PhaseDot, {
297
346
  phase: row.fiberPhase,
298
347
  t
299
348
  }) : null, (0, react_jsx_runtime.jsx)(StateTag, {
@@ -316,19 +365,22 @@ window.__ModuleLoader__.load({
316
365
  /** One global-plane row; a preset-provided row carries the presets that enable it. */
317
366
  const globalRowCard = (entry, providers) => {
318
367
  const key = `global:${entry.entryId}`;
319
- const title = moduleShortName(entry.moduleName);
368
+ const { title, description } = pluginText(entry, resolveText);
320
369
  const failed = entry.fiberPhase === "failed";
321
370
  const stateText = failed ? t("failedTag") : providers !== void 0 ? t("presetEnabledTag") : t(entry.enabled ? "enabledTag" : "disabledTag");
322
371
  const kind = failed ? "failed" : providers !== void 0 ? "preset" : entry.enabled ? "enabled" : "disabled";
323
372
  return (0, react_jsx_runtime.jsx)(PluginCard, {
324
373
  rowKey: key,
325
374
  moduleName: entry.moduleName,
375
+ title,
376
+ description,
377
+ metadataError: entry.meta?.error === void 0 ? void 0 : t("metadataError", { error: entry.meta.error }),
326
378
  entryId: entry.entryId,
327
379
  failed,
328
380
  expanded,
329
381
  onToggle: toggleRow,
330
382
  ariaLabel: `${title}, ${entry.entryId}, ${stateText}`,
331
- trailing: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [entry.enabled && !failed && entry.fiberPhase !== null ? (0, react_jsx_runtime.jsx)(PhaseDot, {
383
+ trailing: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [entry.enabled && showsPhaseDot(entry.fiberPhase) ? (0, react_jsx_runtime.jsx)(PhaseDot, {
332
384
  phase: entry.fiberPhase,
333
385
  t
334
386
  }) : null, (0, react_jsx_runtime.jsx)(StateTag, {
@@ -357,15 +409,44 @@ window.__ModuleLoader__.load({
357
409
  className: PluginInventorySettingsTab_module_css_default.section,
358
410
  "aria-busy": state.status === "loading",
359
411
  children: [
360
- state.status === "loading" ? (0, react_jsx_runtime.jsx)("p", {
361
- className: PluginInventorySettingsTab_module_css_default.status,
362
- children: t("loading")
412
+ clientSync.syncing ? (0, react_jsx_runtime.jsxs)("p", {
413
+ className: `${PluginInventorySettingsTab_module_css_default.status} ${PluginInventorySettingsTab_module_css_default.statusWithDot}`,
414
+ role: "status",
415
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "ongoing" }), t("clientSyncing")]
416
+ }) : null,
417
+ clientSync.failures.length === 0 ? null : (0, react_jsx_runtime.jsxs)("div", {
418
+ className: PluginInventorySettingsTab_module_css_default.failure,
419
+ "data-client-sync-failure": true,
420
+ children: [
421
+ (0, react_jsx_runtime.jsxs)("p", {
422
+ className: PluginInventorySettingsTab_module_css_default.statusWithDot,
423
+ role: "alert",
424
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "error" }), t("clientSyncFailed")]
425
+ }),
426
+ (0, react_jsx_runtime.jsx)("ul", { children: clientSync.failures.map((failure) => (0, react_jsx_runtime.jsxs)("li", { children: [
427
+ failure.id,
428
+ ": ",
429
+ failure.message
430
+ ] }, failure.id)) }),
431
+ (0, react_jsx_runtime.jsx)("button", {
432
+ type: "button",
433
+ disabled: clientSync.syncing,
434
+ onClick: retryClient,
435
+ children: t("clientSyncRetry")
436
+ })
437
+ ]
438
+ }),
439
+ state.status === "loading" ? (0, react_jsx_runtime.jsxs)("p", {
440
+ className: `${PluginInventorySettingsTab_module_css_default.status} ${PluginInventorySettingsTab_module_css_default.statusWithDot}`,
441
+ role: "status",
442
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "ongoing" }), t("loading")]
363
443
  }) : null,
364
444
  state.status === "error" ? (0, react_jsx_runtime.jsxs)("div", {
365
445
  className: PluginInventorySettingsTab_module_css_default.failure,
366
- children: [(0, react_jsx_runtime.jsx)("p", {
446
+ children: [(0, react_jsx_runtime.jsxs)("p", {
447
+ className: PluginInventorySettingsTab_module_css_default.statusWithDot,
367
448
  role: "alert",
368
- children: t("error")
449
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "error" }), t("error")]
369
450
  }), (0, react_jsx_runtime.jsx)("button", {
370
451
  type: "button",
371
452
  onClick: retry,
@@ -378,7 +459,7 @@ window.__ModuleLoader__.load({
378
459
  (0, react_jsx_runtime.jsxs)("label", {
379
460
  className: PluginInventorySettingsTab_module_css_default.search,
380
461
  children: [
381
- (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSearchOutline16, { "aria-hidden": "true" }),
462
+ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSearchOutlineRegular, { "aria-hidden": "true" }),
382
463
  (0, react_jsx_runtime.jsx)("span", {
383
464
  className: PluginInventorySettingsTab_module_css_default.visuallyHidden,
384
465
  children: t("search")
@@ -417,7 +498,7 @@ window.__ModuleLoader__.load({
417
498
  onClick: () => {
418
499
  setPresetOpen(!presetEffectiveOpen);
419
500
  },
420
- children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {
501
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, {
421
502
  className: PluginInventorySettingsTab_module_css_default.chevron,
422
503
  size: 12,
423
504
  "aria-hidden": "true"
@@ -455,7 +536,7 @@ window.__ModuleLoader__.load({
455
536
  children: [(0, react_jsx_runtime.jsx)("span", {
456
537
  className: PluginInventorySettingsTab_module_css_default.switcherLabel,
457
538
  children: presetLabel(selected, t, presetName)
458
- }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {
539
+ }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, {
459
540
  className: PluginInventorySettingsTab_module_css_default.chevron,
460
541
  "aria-hidden": "true"
461
542
  })]
@@ -512,7 +593,7 @@ window.__ModuleLoader__.load({
512
593
  onClick: () => {
513
594
  setGlobalOpen(!globalEffectiveOpen);
514
595
  },
515
- children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {
596
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, {
516
597
  className: PluginInventorySettingsTab_module_css_default.chevron,
517
598
  size: 12,
518
599
  "aria-hidden": "true"
@@ -559,7 +640,11 @@ window.__ModuleLoader__.load({
559
640
  const zh = {
560
641
  tab: "插件列表",
561
642
  loading: "正在读取插件…",
643
+ clientSyncing: "正在同步本页面的插件…",
644
+ clientSyncFailed: "本页面的插件未能完成同步;服务端的启用状态保持不变。",
645
+ clientSyncRetry: "重试本页面同步",
562
646
  error: "暂时无法读取插件。",
647
+ metadataError: "包元信息错误:{error}",
563
648
  retry: "重试",
564
649
  search: "搜索插件",
565
650
  empty: "暂无插件。",
@@ -598,7 +683,11 @@ window.__ModuleLoader__.load({
598
683
  const en = {
599
684
  tab: "Plugin list",
600
685
  loading: "Reading plugins…",
686
+ clientSyncing: "Syncing plugins on this page…",
687
+ clientSyncFailed: "Some plugins could not sync on this page. Host enablement is unchanged.",
688
+ clientSyncRetry: "Retry this page",
601
689
  error: "Plugins are temporarily unavailable.",
690
+ metadataError: "Package metadata error: {error}",
602
691
  retry: "Retry",
603
692
  search: "Search plugins",
604
693
  empty: "No plugins are available.",
@@ -643,7 +732,8 @@ window.__ModuleLoader__.load({
643
732
  "slots",
644
733
  "locale",
645
734
  "remote",
646
- "remote.pluginInventory"
735
+ "remote.pluginInventory",
736
+ "modules"
647
737
  ];
648
738
  /** Contribute the lazy inventory tab to the Plugins settings section. */
649
739
  function apply(ctx) {
@@ -661,7 +751,14 @@ window.__ModuleLoader__.load({
661
751
  const presetName = (preset) => presetDisplayText(preset, agentPresetCopy).name;
662
752
  const injected = () => ({
663
753
  list,
664
- presetName
754
+ presetName,
755
+ resolveText: (text) => ctx.locale.resolveText(text),
756
+ hooks: { clientSync: ctx.modules.entries.state },
757
+ retryClient: () => {
758
+ ctx.modules.entries.retry().catch((error) => {
759
+ ctx.logger.error(error);
760
+ });
761
+ }
665
762
  });
666
763
  ctx.slots.inject("settings.plugins.tab", () => ctx.slots.register({
667
764
  name: "settings.plugins.tab",
@@ -1,9 +1,20 @@
1
1
  import { type ReactNode } from 'react';
2
+ import type { ClientEntryState } from '@deepseek-ai/dsh-client-modules/client';
3
+ import type { ObservableSnapshot } from '@deepseek-ai/dsh-client-store';
2
4
  import type { PluginInventorySnapshot } from '@deepseek-ai/dsh-api-remotes/client';
5
+ import type { LocalizedText } from '@deepseek-ai/dsh-package-manifest';
3
6
  import type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
4
7
  type AgentPresetGroup = NonNullable<PluginInventorySnapshot['agentPresets']>[number];
5
8
  /** Registration-side Remote face used by the section. */
6
9
  export interface PluginInventorySettingsTabInjected {
10
+ /** Resolve local package text in the current Client locale at render time. */
11
+ resolveText: (text: LocalizedText) => string;
12
+ /** Page-local module synchronization, independent from the Host inventory. */
13
+ hooks: {
14
+ clientSync: ObservableSnapshot<ClientEntryState>;
15
+ };
16
+ /** Retry the latest client graph without changing the Host composition. */
17
+ retryClient: () => void;
7
18
  /** Read a current Host inventory snapshot. */
8
19
  list: () => Promise<PluginInventorySnapshot>;
9
20
  /**
@@ -15,6 +26,6 @@ export interface PluginInventorySettingsTabInjected {
15
26
  /** Full component props assembled by the Settings slot renderer. */
16
27
  export type PluginInventorySettingsTabProps = PropsRuntime<'settings.plugins.tab'> & PropsLocale<'settings.pluginInventory'> & InjectFace<PluginInventorySettingsTabInjected>;
17
28
  /** Render the read-only plugin inventory: agent presets first, then the global plane. */
18
- export declare function PluginInventorySettingsTab({ list, presetName, t }: PluginInventorySettingsTabProps): ReactNode;
29
+ export declare function PluginInventorySettingsTab({ list, presetName, resolveText, t, useClientSync, retryClient }: PluginInventorySettingsTabProps): ReactNode;
19
30
  export {};
20
31
  //# sourceMappingURL=PluginInventorySettingsTab.d.ts.map
@@ -3,7 +3,11 @@
3
3
  export declare const zh: {
4
4
  tab: string;
5
5
  loading: string;
6
+ clientSyncing: string;
7
+ clientSyncFailed: string;
8
+ clientSyncRetry: string;
6
9
  error: string;
10
+ metadataError: string;
7
11
  retry: string;
8
12
  search: string;
9
13
  empty: string;
@@ -44,7 +48,11 @@ export type PluginInventoryLocaleKey = keyof typeof zh;
44
48
  export declare const en: {
45
49
  tab: string;
46
50
  loading: string;
51
+ clientSyncing: string;
52
+ clientSyncFailed: string;
53
+ clientSyncRetry: string;
47
54
  error: string;
55
+ metadataError: string;
48
56
  retry: string;
49
57
  search: string;
50
58
  empty: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-client-ui-settings-plugin-inventory",
3
3
  "description": "Read-only Cordis Loader inventory tab in Web Plugins settings",
4
- "version": "0.1.6-alpha.1",
4
+ "version": "0.1.7-alpha.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -31,30 +31,34 @@
31
31
  "@deepseek-ai/dsh-api-remotes",
32
32
  "@deepseek-ai/dsh-client-ui-settings",
33
33
  "@deepseek-ai/dsh-client-locale",
34
- "@deepseek-ai/dsh-client-ui-agent-preset"
34
+ "@deepseek-ai/dsh-client-ui-agent-preset",
35
+ "@deepseek-ai/dsh-client-modules"
35
36
  ],
36
37
  "platform": "web"
37
38
  }
38
39
  },
39
40
  "license": "MIT",
40
41
  "peerDependencies": {
41
- "@deepseek-ai/cordis": "^4.0.2"
42
+ "@deepseek-ai/cordis": "^4.0.3"
42
43
  },
43
44
  "devDependencies": {
44
45
  "@testing-library/react": "^16.1.0",
45
46
  "@types/react": "~18.3.1",
46
47
  "react": "^18.2.0",
47
48
  "react-dom": "^18.2.0",
48
- "@deepseek-ai/dsh-api-remotes": "^0.1.6-alpha.1",
49
- "@deepseek-ai/dsh-client-locale": "^0.1.6-alpha.1",
50
- "@deepseek-ai/dsh-client-ui-agent-preset": "^0.1.6-alpha.1",
51
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.6-alpha.1",
52
- "@deepseek-ai/dsh-client-ui-settings": "^0.1.6-alpha.1",
53
- "@deepseek-ai/dsh-client-test-runtime": "^0.1.6-alpha.1",
54
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.6-alpha.1",
55
- "@deepseek-ai/cordis": "^4.0.2",
56
- "@deepseek-ai/dsh-client-ui-renderer": "^0.1.6-alpha.1",
57
- "@deepseek-ai/dsh-agent-presets": "^0.1.6-alpha.1"
49
+ "@deepseek-ai/dsh-client-ui-agent-preset": "^0.1.7-alpha.1",
50
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.7-alpha.1",
51
+ "@deepseek-ai/dsh-client-ui-settings": "^0.1.7-alpha.1",
52
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.7-alpha.1",
53
+ "@deepseek-ai/dsh-package-manifest": "^0.1.7-alpha.1",
54
+ "@deepseek-ai/cordis": "^4.0.3",
55
+ "@deepseek-ai/dsh-agent-preset-registry": "^0.1.7-alpha.1",
56
+ "@deepseek-ai/dsh-client-modules": "^0.1.7-alpha.1",
57
+ "@deepseek-ai/dsh-client-store": "^0.1.7-alpha.1",
58
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.7-alpha.1",
59
+ "@deepseek-ai/dsh-client-locale": "^0.1.7-alpha.1",
60
+ "@deepseek-ai/dsh-client-test-runtime": "^0.1.7-alpha.1",
61
+ "@deepseek-ai/dsh-api-remotes": "^0.1.7-alpha.1"
58
62
  },
59
63
  "files": [
60
64
  "lib/index.js",