@prettier-ai/dsh-client-ui-settings-plugin-inventory 0.1.2-alpha.1 → 0.1.2-alpha.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.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: c44e150e528f20349429f9a49d416367f1ca3c41
6
- README.zh.md: ae2e2cd1105464f0fb262a07e96b66082a6e7166
5
+ README.md: 65cff2936a7af95a0fdc767bb33c550deb411ed4
6
+ README.zh.md: 6e22fb7deb9d38b7fa823a893a42fd945fe6d47c
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "Read-only Cordis Loader inventory tab in Web Plugins settings for the dsh web client: searchable plugin catalog with enablement state and configuration."
2
+ description: "Scope-grouped read-only plugin inventory tab in Web Plugins settings for the dsh web client: agent-preset compositions first, the global plane behind a disclosure, search across both."
3
3
  kind: "package-reference"
4
4
  ---
5
5
 
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
9
9
 
10
10
  ## Summary
11
11
 
12
- `dsh-client-ui-settings-plugin-inventory` contributes the read-only **Plugin list** tab to the Web Settings Plugins section. The tab lazily calls `ctx.remote.pluginInventory.list()` the first time it is selected and renders a searchable two-column catalog of compact disclosure cards: each collapsed card shows the short module name, an effective-enablement tag, and (for enabled entries) a colored root-fiber status dot; expanding a card reveals the Loader-tree entry id, effective configuration, and Cordis status. Loading, empty, no-match, and generic failure states stay local to the mounted component, and a failed read can be retried without exposing transport details.
12
+ `dsh-client-ui-settings-plugin-inventory` contributes the read-only **Plugin list** tab to the Web Settings Plugins section. The tab lazily calls `ctx.remote.pluginInventory.list()` the first time it is selected and renders the inventory in two collapsible groups. The agent-preset group comes first, open by default: a display-only switcher pill over the roster opens on the default preset, and each composition row is a compact disclosure card carrying its enablement — including `conditional` for a disabled gate the Host could not evaluate — with provenance facts behind the disclosure. The global group follows collapsed, its header carrying the entry count and a failure count; expanded, failures float first, and an entry disabled globally but enabled by at least one preset is marked as preset-provided in place its details name the enabling presets — instead of reading as plainly disabled. Search filters both groups, forces the collapsed groups open, and points at matches sitting in unselected presets. Loading, empty, no-match, and generic failure states stay local to the mounted component, and a failed read can be retried without exposing transport details; without a roster the tab renders the global plane alone, expanded.
13
13
 
14
14
  ## Table of Contents
15
15
 
@@ -29,7 +29,11 @@ Open the Plugins section in Settings and select the **Plugin list** tab to inspe
29
29
 
30
30
  ### Reading a card
31
31
 
32
- Each collapsed card uses the short module name as its title and a small effective-enablement tag; enabled entries also show a colored root-fiber status dot. Expanding one card reveals its Loader-tree entry id, followed by the effective configuration and, for enabled entries, Cordis status; disabled entries omit the redundant unmounted runtime state. Search filters the catalog by name and entry id.
32
+ Each collapsed card uses the short module name as its title and a small enablement tag; enabled entries also show a colored root-fiber status dot. 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.
33
+
34
+ ### The preset switcher
35
+
36
+ The switcher is the same selector-pill-plus-menu control the General settings rows use. It lists every roster preset — the default suffixed as such, broken ones marked — and changes only what the list shows: it writes no settings, and selecting a broken preset shows the discovery-reported reason in place of rows. Choosing the default preset or a session's preset stays where it was: the Agent presets section and the new-session screen.
33
37
 
34
38
  ### Retrying a failed read
35
39
 
@@ -51,7 +55,7 @@ The browser plugin registers one localized `settings.plugins.tab` contribution w
51
55
 
52
56
  ### Rendering
53
57
 
54
- The entry id remains the React key, disclosure identity, detail value, and an additional search target; it is never classified by string shape.
58
+ Row keys are scope-qualified (`global:`, `preset:<id>:<index>`), so one module appearing in both scopes keeps distinct disclosure state; an entry id is shown as detail only when the row declares one and is never classified by string shape. The preset-provided marking is derived client-side: a global entry carries it when it is disabled there while at least one preset row for the same module specifier is actually enabled, so a module every preset gates off (or declares only conditionally) stays plainly disabled rather than over-claiming provision.
55
59
 
56
60
  </details>
57
61
 
@@ -86,7 +90,7 @@ None; this package neither assembles nor sends a provider request.
86
90
  These limits define the freshness and reach of the inventory view; they are current package constraints.
87
91
 
88
92
  - **One snapshot per Settings mount or retry** — the tab does not subscribe to Loader changes or automatically refetch after reconnect; switching tabs preserves the current snapshot, while reopening Settings obtains a new one.
89
- - **Read-only Loader view** — local search does not add provenance, current-browser activation diagnosis, grouping by source, or plugin mutation controls.
93
+ - **Read-only in both planes** — the tab shows global and preset enablement but mutates neither; enable/disable controls that write a custom preset's own composition file are deliberate follow-up work.
90
94
 
91
95
  <a id="dev-note"></a>
92
96
  ### Dev Note
package/README.zh.md CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "dsh Web 客户端设置中的只读 Cordis Loader 清单标签页:可搜索的插件目录,含启停状态与配置。"
2
+ description: "dsh Web 客户端设置中按作用域分组的只读插件清单标签页:Agent 预设组合在前,全局平面收在折叠分组里,搜索跨两组。"
3
3
  kind: "package-reference"
4
4
  ---
5
5
 
@@ -9,7 +9,7 @@ kind: "package-reference"
9
9
 
10
10
  ## 概述
11
11
 
12
- `dsh-client-ui-settings-plugin-inventory` 向 Web 设置的「插件」分区贡献只读的**插件列表**标签页。该标签页在首次被选择时懒调用 `ctx.remote.pluginInventory.list()`,并以可搜索的双列紧凑折叠卡片展示清单:每张收起的卡片显示模块短名称、有效启停标签,以及(对已启用条目)彩色根 fiber 状态圆点;展开卡片会显示 Loader 树条目 id、有效配置与 Cordis 状态。加载、空结果、无匹配与通用失败状态只属于已挂载组件,读取失败后可以重试,且不会暴露传输细节。
12
+ `dsh-client-ui-settings-plugin-inventory` 向 Web 设置的「插件」分区贡献只读的**插件列表**标签页。该标签页在首次被选择时懒调用 `ctx.remote.pluginInventory.list()`,并把清单分成两个可折叠分组渲染。Agent 预设组在前、默认展开:一个只改显示的切换器胶囊覆盖 roster、初始停在默认预设,每个组合行是一张紧凑折叠卡片,携带其启停状态——含宿主无法求值的 disabled 门对应的 `conditional`——出处事实收在折叠里。全局组随后且默认收起,组头带条目计数与失败计数;展开后失败行浮在最前,全局停用但被至少一个预设启用的条目就地标记为预设提供——详情列出启用它的预设——而不是读作单纯的已停用。搜索同时过滤两组、强制撑开收起的分组,并指出未选中预设里的匹配。加载、空结果、无匹配与通用失败状态只属于已挂载组件,读取失败后可以重试,且不会暴露传输细节;没有 roster 时标签页只渲染全局平面并保持展开。
13
13
 
14
14
  ## 目录
15
15
 
@@ -29,7 +29,11 @@ kind: "package-reference"
29
29
 
30
30
  ### 阅读卡片
31
31
 
32
- 每张收起的卡片使用模块短名称作为标题,并以小标签表示有效启停状态;已启用的条目还会显示彩色根 fiber 状态圆点。展开卡片后会直接展示 Loader 树条目 id、有效配置,已启用条目还会显示 Cordis 状态;已停用条目省略重复的「未挂载」运行状态。搜索按名称与条目 id 过滤目录。
32
+ 每张收起的卡片使用模块短名称作为标题,并以小标签表示启停状态;已启用的条目还会显示彩色根 fiber 状态圆点。展开卡片后会显示声明的条目 id、完整模块标识与状态事实:预设行说明它来自哪个预设、组合存活时的运行状态,以及它携带的禁用条件;被预设提供的全局行说明它由 Agent 预设按会话提供、列出启用它的预设,并提供跳转到预设组的入口。预设名经共享的 `presetDisplayText` 纯函数(`dsh-agent-presets/display`)叠在 [`ui-agent-preset`](../ui-agent-preset/README.zh.md) 的字典上解析:内置预设走当前语言,用户自建预设保留自己的元数据,因此英文界面不会回显预设文件里的中文名。搜索按模块名称与条目 id 过滤两组。
33
+
34
+ ### 预设切换器
35
+
36
+ 切换器与通用设置各行使用同一种「选择胶囊 + 菜单」控件。它列出 roster 的每个预设——默认项带后缀、坏预设带标记——并且只改变列表显示什么:它不写任何设置,选中坏预设时在行的位置展示 discovery 报告的原因。选默认预设或某个会话的预设仍在原处:Agent 预设分区与新会话页。
33
37
 
34
38
  ### 重试失败的读取
35
39
 
@@ -51,7 +55,7 @@ kind: "package-reference"
51
55
 
52
56
  ### 渲染
53
57
 
54
- 条目 id 仍作为 React key、展开标识、详情值与额外的搜索目标;代码不按字符串形状对它分类。
58
+ key 按作用域限定(`global:`、`preset:<id>:<index>`),因此同一模块出现在两个作用域时保持各自的展开状态;条目 id 只在行声明了它时作为详情展示,代码不按字符串形状对它分类。预设提供标记在客户端推导:一个全局条目在全局被停用、且至少一个预设行对同一模块标识实际启用时才携带它,因此被所有预设关掉(或仅条件声明)的模块保持单纯的已停用,而不是夸大提供关系。
55
59
 
56
60
  </details>
57
61
 
@@ -86,7 +90,7 @@ kind: "package-reference"
86
90
  这些限制定义清单视图的新鲜度与触达范围;它们是当前包约束。
87
91
 
88
92
  - **每次 Settings 挂载或重试只读取一份快照**:标签页不订阅 Loader 变化,也不会在重连后自动重新读取;切换标签页会保留当前快照,重新打开 Settings 则会取得新快照。
89
- - **只读 Loader 视图**:本地搜索不会额外引入来源、按来源分组、当前浏览器激活诊断或插件修改控件。
93
+ - **两个平面都只读**:标签页展示全局与预设的启停状态但都不修改;写回自定义预设组合文件的启停控件是刻意留作后续的工作。
90
94
 
91
95
  <a id="dev-note"></a>
92
96
  ### 开发备注
package/lib/client.js CHANGED
@@ -7,8 +7,45 @@ window.__ModuleLoader__.load({
7
7
  let react_jsx_runtime = require("react/jsx-runtime");
8
8
  let react = require("react");
9
9
  let _prettier_ai_dsh_client_ui_primitives = require("@prettier-ai/dsh-client-ui-primitives");
10
+ //#region ../../preset/agent-presets/src/display.ts
11
+ const BUILT_IN_PRESET_KEYS = {
12
+ standard: {
13
+ name: "presetStandardName",
14
+ description: "presetStandardDescription"
15
+ },
16
+ ptc: {
17
+ name: "presetPtcName",
18
+ description: "presetPtcDescription"
19
+ },
20
+ minimal: {
21
+ name: "presetMinimalName",
22
+ description: "presetMinimalDescription"
23
+ },
24
+ cordis: {
25
+ name: "presetCordisName",
26
+ description: "presetCordisDescription"
27
+ }
28
+ };
29
+ /**
30
+ * Resolve preset display copy without making user-authored metadata translatable.
31
+ * @param preset - roster row whose copy is being rendered.
32
+ * @param t - active locale lookup covering {@link BuiltInPresetCopyKey}.
33
+ * @returns localized copy for a known shipped preset, otherwise file metadata.
34
+ */
35
+ function presetDisplayText(preset, t) {
36
+ const keys = preset.trust === "system" ? BUILT_IN_PRESET_KEYS[preset.id] : void 0;
37
+ if (keys !== void 0) return {
38
+ name: t(keys.name),
39
+ description: t(keys.description)
40
+ };
41
+ return {
42
+ name: preset.name ?? preset.id,
43
+ ...preset.description === void 0 ? {} : { description: preset.description }
44
+ };
45
+ }
46
+ //#endregion
10
47
  //#region \0dsh-css:/home/runner/work/dsh-publisher/dsh-publisher/upstream/packages/client/ui-settings-plugin-inventory/src/client/PluginInventorySettingsTab.module.css.mjs
11
- const css = "._4AfKUq_section{width:100%;max-width:760px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:14px;display:flex}._4AfKUq_catalogHeading h3,._4AfKUq_status,._4AfKUq_failure p{margin:0}._4AfKUq_status,._4AfKUq_failure{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px}._4AfKUq_failure{color:var(--dsw-alias-state-error-primary);align-items:center;gap:10px;display:flex}._4AfKUq_failure button{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;background:0 0;border-radius:6px;padding:4px 10px}._4AfKUq_catalog{flex-direction:column;gap:12px;display:flex}._4AfKUq_search{width:100%;color:var(--dsw-alias-label-tertiary);align-items:center;display:flex;position:relative}._4AfKUq_search>svg{pointer-events:none;position:absolute;left:12px}._4AfKUq_search input{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);width:100%;height:36px;color:var(--dsw-alias-label-primary);font:inherit;border-radius:8px;outline:none;padding:0 34px 0 36px;font-size:13px}._4AfKUq_search input::placeholder{color:var(--dsw-alias-label-tertiary)}._4AfKUq_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)}._4AfKUq_catalogHeading{align-items:baseline;gap:7px;padding:0 2px;display:flex}._4AfKUq_catalogHeading h3{font-size:13px;font-weight:600;line-height:20px}._4AfKUq_catalogHeading span{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:12px;line-height:18px}._4AfKUq_cards{grid-template-columns:repeat(2,minmax(0,1fr));align-items:start;gap:10px;margin:0;padding:0;list-style:none;display:grid}._4AfKUq_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;min-width:0;overflow:hidden}._4AfKUq_card[data-open=true]{border-color:var(--dsw-alias-border-l1);box-shadow:var(--dsw-shadow-lv1)}._4AfKUq_cardContent{box-sizing:border-box;width:100%;min-height:52px;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;justify-content:space-between;align-items:center;gap:12px;padding:12px 14px;display:flex}._4AfKUq_cardContent:hover,._4AfKUq_card[data-open=true]>._4AfKUq_cardContent{background:var(--dsw-alias-interactive-bg-hover)}._4AfKUq_cardContent:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-2px}._4AfKUq_cardTitle{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:14px;font-weight:600;line-height:20px;overflow:hidden}._4AfKUq_cardTrailing{color:var(--dsw-alias-label-tertiary);flex:none;align-items:center;gap:7px;display:inline-flex}._4AfKUq_statusDot{background:var(--dsw-alias-label-tertiary);border-radius:999px;flex:none;width:7px;height:7px;display:inline-block}._4AfKUq_statusDot[data-phase=active]{background:var(--dsw-alias-state-success-primary)}._4AfKUq_statusDot[data-phase=failed]{background:var(--dsw-alias-state-error-primary)}._4AfKUq_statusDot[data-phase=loading]{background:var(--dsw-alias-state-business-primary)}._4AfKUq_configTag{background:var(--dsw-alias-bg-layer-1);min-height:20px;color:var(--dsw-alias-label-secondary);white-space:nowrap;border-radius:5px;align-items:center;padding:1px 6px;font-size:11px;line-height:16px;display:inline-flex}._4AfKUq_configTag[data-enabled=true]{background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 10%, transparent);color:var(--dsw-alias-state-success-primary)}._4AfKUq_chevron{color:var(--dsw-alias-label-tertiary);flex:none}._4AfKUq_card[data-open=true] ._4AfKUq_chevron{transform:rotate(180deg)}._4AfKUq_cardDetails{border-top:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-module-platform);padding:10px 14px 12px}._4AfKUq_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}._4AfKUq_details{grid-template-columns:76px minmax(0,1fr);gap:6px 10px;margin:8px 0 0;display:grid}._4AfKUq_details div{display:contents}._4AfKUq_details dt{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:17px}._4AfKUq_details dd{overflow-wrap:anywhere;min-width:0;color:var(--dsw-alias-label-secondary);margin:0;font-size:12px;line-height:17px}._4AfKUq_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){._4AfKUq_chevron{transition:transform .14s var(--ds-ease-in-out)}}@media (width<=680px){._4AfKUq_cards{grid-template-columns:minmax(0,1fr)}}";
48
+ const css = "._4AfKUq_section{width:100%;max-width:760px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:14px;display:flex}._4AfKUq_catalogHeading h3,._4AfKUq_status,._4AfKUq_failure p{margin:0}._4AfKUq_status,._4AfKUq_failure{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px}._4AfKUq_failure{color:var(--dsw-alias-state-error-primary);align-items:center;gap:10px;display:flex}._4AfKUq_failure button{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;background:0 0;border-radius:6px;padding:4px 10px}._4AfKUq_catalog{flex-direction:column;gap:12px;display:flex}._4AfKUq_search{width:100%;color:var(--dsw-alias-label-tertiary);align-items:center;display:flex;position:relative}._4AfKUq_search>svg{pointer-events:none;position:absolute;left:12px}._4AfKUq_search input{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);width:100%;height:36px;color:var(--dsw-alias-label-primary);font:inherit;border-radius:8px;outline:none;padding:0 34px 0 36px;font-size:13px}._4AfKUq_search input::placeholder{color:var(--dsw-alias-label-tertiary)}._4AfKUq_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)}._4AfKUq_catalogHeading{align-items:baseline;gap:7px;padding:0 2px;display:flex}._4AfKUq_catalogHeading h3{font-size:13px;font-weight:600;line-height:20px}._4AfKUq_catalogHeading span{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:12px;line-height:18px}._4AfKUq_cards{grid-template-columns:repeat(2,minmax(0,1fr));align-items:start;gap:10px;margin:0;padding:0;list-style:none;display:grid}._4AfKUq_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;min-width:0;overflow:hidden}._4AfKUq_card[data-open=true]{border-color:var(--dsw-alias-border-l1);box-shadow:var(--dsw-shadow-lv1)}._4AfKUq_cardContent{box-sizing:border-box;width:100%;min-height:52px;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;justify-content:space-between;align-items:center;gap:12px;padding:12px 14px;display:flex}._4AfKUq_cardContent:hover,._4AfKUq_card[data-open=true]>._4AfKUq_cardContent{background:var(--dsw-alias-interactive-bg-hover)}._4AfKUq_cardContent:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-2px}._4AfKUq_cardTitle{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:14px;font-weight:600;line-height:20px;overflow:hidden}._4AfKUq_cardTrailing{color:var(--dsw-alias-label-tertiary);flex:none;align-items:center;gap:7px;display:inline-flex}._4AfKUq_statusDot{background:var(--dsw-alias-label-tertiary);border-radius:999px;flex:none;width:7px;height:7px;display:inline-block}._4AfKUq_statusDot[data-phase=active]{background:var(--dsw-alias-state-success-primary)}._4AfKUq_statusDot[data-phase=failed]{background:var(--dsw-alias-state-error-primary)}._4AfKUq_statusDot[data-phase=loading]{background:var(--dsw-alias-state-business-primary)}._4AfKUq_configTag{background:var(--dsw-alias-bg-layer-1);min-height:20px;color:var(--dsw-alias-label-secondary);white-space:nowrap;border-radius:5px;align-items:center;padding:1px 6px;font-size:11px;line-height:16px;display:inline-flex}._4AfKUq_configTag[data-kind=enabled]{background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 10%, transparent);color:var(--dsw-alias-state-success-primary)}._4AfKUq_configTag[data-kind=preset]{background:color-mix(in srgb, var(--dsw-alias-state-business-primary) 10%, transparent);color:var(--dsw-alias-state-business-primary)}._4AfKUq_configTag[data-kind=conditional]{background:color-mix(in srgb, var(--dsw-alias-state-warning-primary,#b45309) 12%, transparent);color:var(--dsw-alias-state-warning-primary,#b45309)}._4AfKUq_configTag[data-kind=failed]{background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);color:var(--dsw-alias-state-error-primary)}._4AfKUq_group{flex-direction:column;gap:10px;display:flex}._4AfKUq_groupTitleRow{align-items:center;gap:8px;min-height:32px;display:flex}._4AfKUq_group+._4AfKUq_group{border-top:1px solid var(--dsw-alias-border-l2);padding-top:14px}._4AfKUq_headerEnd{margin-left:auto}._4AfKUq_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}._4AfKUq_groupToggle:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}._4AfKUq_groupToggle>._4AfKUq_chevron{transform:rotate(-90deg)}._4AfKUq_groupToggle[aria-expanded=true]>._4AfKUq_chevron{transform:none}._4AfKUq_groupTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}._4AfKUq_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}._4AfKUq_failedCount{color:var(--dsw-alias-state-error-primary)}._4AfKUq_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}._4AfKUq_switcher:hover{background:var(--dsw-alias-interactive-bg-hover)}._4AfKUq_switcher:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}._4AfKUq_switcher>._4AfKUq_chevron{flex:none}._4AfKUq_switcherLabel{text-overflow:ellipsis;max-width:240px;overflow:hidden}._4AfKUq_groupBody{flex-direction:column;gap:10px;display:flex}._4AfKUq_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}._4AfKUq_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}._4AfKUq_jumpLink{color:var(--dsw-alias-state-business-primary);font:inherit;cursor:pointer;background:0 0;border:0;padding:0;font-size:12.5px}._4AfKUq_enabledIn{flex-wrap:wrap;align-items:baseline;gap:4px 10px;display:flex}._4AfKUq_card[data-failed=true]{border-color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 45%, transparent)}._4AfKUq_chevron{color:var(--dsw-alias-label-tertiary);flex:none}._4AfKUq_card[data-open=true] ._4AfKUq_chevron{transform:rotate(180deg)}._4AfKUq_cardDetails{border-top:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-module-platform);padding:10px 14px 12px}._4AfKUq_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}._4AfKUq_details{grid-template-columns:76px minmax(0,1fr);gap:6px 10px;margin:8px 0 0;display:grid}._4AfKUq_details div{display:contents}._4AfKUq_details dt{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:17px}._4AfKUq_details dd{overflow-wrap:anywhere;min-width:0;color:var(--dsw-alias-label-secondary);margin:0;font-size:12px;line-height:17px}._4AfKUq_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){._4AfKUq_chevron{transition:transform .14s var(--ds-ease-in-out)}}@media (width<=680px){._4AfKUq_cards{grid-template-columns:minmax(0,1fr)}}";
12
49
  const tagId = "@prettier-ai/dsh-client-ui-settings-plugin-inventory/PluginInventorySettingsTab.module.css";
13
50
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
14
51
  const tag = document.createElement("style");
@@ -18,6 +55,7 @@ window.__ModuleLoader__.load({
18
55
  document.head.appendChild(tag);
19
56
  }
20
57
  var PluginInventorySettingsTab_module_css_default = {
58
+ "brokenNote": "_4AfKUq_brokenNote",
21
59
  "card": "_4AfKUq_card",
22
60
  "cardContent": "_4AfKUq_cardContent",
23
61
  "cardDetails": "_4AfKUq_cardDetails",
@@ -29,12 +67,25 @@ window.__ModuleLoader__.load({
29
67
  "chevron": "_4AfKUq_chevron",
30
68
  "configTag": "_4AfKUq_configTag",
31
69
  "details": "_4AfKUq_details",
70
+ "enabledIn": "_4AfKUq_enabledIn",
32
71
  "entryValue": "_4AfKUq_entryValue",
72
+ "failedCount": "_4AfKUq_failedCount",
33
73
  "failure": "_4AfKUq_failure",
74
+ "group": "_4AfKUq_group",
75
+ "groupBody": "_4AfKUq_groupBody",
76
+ "groupSub": "_4AfKUq_groupSub",
77
+ "groupTitle": "_4AfKUq_groupTitle",
78
+ "groupTitleRow": "_4AfKUq_groupTitleRow",
79
+ "groupToggle": "_4AfKUq_groupToggle",
80
+ "headerEnd": "_4AfKUq_headerEnd",
81
+ "hint": "_4AfKUq_hint",
82
+ "jumpLink": "_4AfKUq_jumpLink",
34
83
  "search": "_4AfKUq_search",
35
84
  "section": "_4AfKUq_section",
36
85
  "status": "_4AfKUq_status",
37
86
  "statusDot": "_4AfKUq_statusDot",
87
+ "switcher": "_4AfKUq_switcher",
88
+ "switcherLabel": "_4AfKUq_switcherLabel",
38
89
  "visuallyHidden": "_4AfKUq_visuallyHidden"
39
90
  };
40
91
  //#endregion
@@ -54,17 +105,100 @@ window.__ModuleLoader__.load({
54
105
  function moduleShortName(moduleName) {
55
106
  return (moduleName.startsWith("@") ? moduleName.slice(moduleName.indexOf("/") + 1) : moduleName).replace(/^cordis:/, "").replace(/^cordis-plugin-/, "").replace(/^dsh-(?:host-|client-)?/, "");
56
107
  }
57
- /** Whether an inventory row matches the local catalog query. */
58
- function matches(entry, normalizedQuery) {
108
+ /** Whether one row's module name or entry id matches the catalog query. */
109
+ function matches(moduleName, entryId, normalizedQuery) {
59
110
  if (normalizedQuery.length === 0) return true;
60
- return [entry.moduleName, entry.entryId].some((value) => value.toLocaleLowerCase().includes(normalizedQuery));
111
+ return [moduleName, ...entryId === null ? [] : [entryId]].some((value) => value.toLocaleLowerCase().includes(normalizedQuery));
112
+ }
113
+ /** The roster row shown when the preset switcher has no explicit choice. */
114
+ function fallbackPreset(presets) {
115
+ return presets.find((preset) => preset.isDefault) ?? presets[0];
116
+ }
117
+ /** The switcher's display label for one preset. */
118
+ function presetLabel(preset, t, presetName) {
119
+ const name = presetName(preset);
120
+ if (preset.broken !== void 0) return t("presetOptionBroken", { name });
121
+ if (preset.isDefault) return t("presetOptionDefault", { name });
122
+ return name;
123
+ }
124
+ /** One expandable plugin card; the caller owns the trailing status content. */
125
+ function PluginCard({ rowKey, moduleName, entryId, trailing, ariaLabel, failed, expanded, onToggle, children }) {
126
+ const open = expanded === rowKey;
127
+ const detailId = `plugin-details-${encodeURIComponent(rowKey)}`;
128
+ return (0, react_jsx_runtime.jsxs)("li", {
129
+ className: PluginInventorySettingsTab_module_css_default.card,
130
+ "data-plugin-entry": entryId ?? void 0,
131
+ "data-plugin-module": moduleName,
132
+ "data-failed": failed ? "true" : void 0,
133
+ "data-open": open ? "true" : void 0,
134
+ children: [(0, react_jsx_runtime.jsxs)("button", {
135
+ className: PluginInventorySettingsTab_module_css_default.cardContent,
136
+ type: "button",
137
+ "aria-expanded": open,
138
+ "aria-controls": detailId,
139
+ "aria-label": ariaLabel,
140
+ onClick: () => {
141
+ onToggle(rowKey);
142
+ },
143
+ children: [(0, react_jsx_runtime.jsx)("strong", {
144
+ className: PluginInventorySettingsTab_module_css_default.cardTitle,
145
+ title: moduleName,
146
+ children: moduleShortName(moduleName)
147
+ }), (0, react_jsx_runtime.jsxs)("span", {
148
+ className: PluginInventorySettingsTab_module_css_default.cardTrailing,
149
+ children: [trailing, (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {
150
+ className: PluginInventorySettingsTab_module_css_default.chevron,
151
+ size: 12,
152
+ "aria-hidden": "true"
153
+ })]
154
+ })]
155
+ }), open ? (0, react_jsx_runtime.jsx)("div", {
156
+ className: PluginInventorySettingsTab_module_css_default.cardDetails,
157
+ id: detailId,
158
+ children
159
+ }) : null]
160
+ });
61
161
  }
62
- /** Render the read-only current Loader inventory. */
63
- function PluginInventorySettingsTab({ list, t }) {
64
- const catalogId = (0, react.useId)();
162
+ /** Detail rows shared by every card: the Loader identity, then labeled facts. */
163
+ function CardFacts({ moduleName, moduleLabel, entryId, facts }) {
164
+ return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [entryId === null ? null : (0, react_jsx_runtime.jsx)("code", {
165
+ className: PluginInventorySettingsTab_module_css_default.entryValue,
166
+ "data-loader-entry": true,
167
+ children: entryId
168
+ }), (0, react_jsx_runtime.jsxs)("dl", {
169
+ className: PluginInventorySettingsTab_module_css_default.details,
170
+ children: [(0, react_jsx_runtime.jsxs)("div", { children: [(0, react_jsx_runtime.jsx)("dt", { children: moduleLabel }), (0, react_jsx_runtime.jsx)("dd", { children: moduleName })] }), facts.map(([label, value]) => (0, react_jsx_runtime.jsxs)("div", { children: [(0, react_jsx_runtime.jsx)("dt", { children: label }), (0, react_jsx_runtime.jsx)("dd", { children: value })] }, label))]
171
+ })] });
172
+ }
173
+ /** Status dot naming a live root-fiber phase; rows with no live fiber show none. */
174
+ function PhaseDot({ phase, t }) {
175
+ const status = phaseLabel(phase, t);
176
+ return (0, react_jsx_runtime.jsx)("span", {
177
+ className: PluginInventorySettingsTab_module_css_default.statusDot,
178
+ "data-phase": phase,
179
+ role: "img",
180
+ "aria-label": status,
181
+ title: status
182
+ });
183
+ }
184
+ /** Enablement tag; `kind` selects the palette. */
185
+ function StateTag({ kind, label }) {
186
+ return (0, react_jsx_runtime.jsx)("span", {
187
+ className: PluginInventorySettingsTab_module_css_default.configTag,
188
+ "data-kind": kind,
189
+ children: label
190
+ });
191
+ }
192
+ /** Render the read-only plugin inventory: agent presets first, then the global plane. */
193
+ function PluginInventorySettingsTab({ list, presetName, t }) {
194
+ const sectionId = (0, react.useId)();
65
195
  const [request, setRequest] = (0, react.useState)(0);
66
196
  const [query, setQuery] = (0, react.useState)("");
67
197
  const [expanded, setExpanded] = (0, react.useState)(null);
198
+ const [chosenPreset, setChosenPreset] = (0, react.useState)(null);
199
+ const [switcherOpen, setSwitcherOpen] = (0, react.useState)(false);
200
+ const [presetOpen, setPresetOpen] = (0, react.useState)(null);
201
+ const [globalOpen, setGlobalOpen] = (0, react.useState)(null);
68
202
  const [state, setState] = (0, react.useState)({ status: "loading" });
69
203
  (0, react.useEffect)(() => {
70
204
  let current = true;
@@ -81,14 +215,119 @@ window.__ModuleLoader__.load({
81
215
  };
82
216
  }, [list, request]);
83
217
  const normalizedQuery = query.trim().toLocaleLowerCase();
84
- const filteredEntries = (0, react.useMemo)(() => state.status === "ready" ? state.snapshot.entries.filter((entry) => matches(entry, normalizedQuery)) : [], [normalizedQuery, state]);
85
- (0, react.useEffect)(() => {
86
- if (expanded !== null && !filteredEntries.some((entry) => entry.entryId === expanded)) setExpanded(null);
87
- }, [expanded, filteredEntries]);
218
+ const searching = normalizedQuery.length > 0;
219
+ const snapshot = state.status === "ready" ? state.snapshot : void 0;
220
+ const presets = snapshot?.agentPresets ?? [];
221
+ const selected = presets.find((preset) => preset.id === chosenPreset) ?? fallbackPreset(presets);
222
+ /** Presets that actually enable a module, keyed by module name. */
223
+ const enabledIn = (0, react.useMemo)(() => {
224
+ const found = /* @__PURE__ */ new Map();
225
+ for (const preset of presets) for (const row of preset.rows) {
226
+ if (row.enabled !== true) continue;
227
+ const groups = found.get(row.moduleName);
228
+ if (groups === void 0) found.set(row.moduleName, [preset]);
229
+ else if (!groups.includes(preset)) groups.push(preset);
230
+ }
231
+ return found;
232
+ }, [presets]);
233
+ const entries = snapshot?.entries ?? [];
234
+ const failedEntries = [];
235
+ const regularEntries = [];
236
+ for (const entry of entries) if (entry.fiberPhase === "failed") failedEntries.push(entry);
237
+ else regularEntries.push(entry);
238
+ const entryMatch = (entry) => matches(entry.moduleName, entry.entryId, normalizedQuery);
239
+ const rowMatch = (row) => matches(row.moduleName, row.entryId, normalizedQuery);
240
+ const filteredFailed = failedEntries.filter(entryMatch);
241
+ const filteredRegular = regularEntries.filter(entryMatch);
242
+ const globalCount = filteredFailed.length + filteredRegular.length;
243
+ const selectedRows = selected === void 0 ? [] : selected.rows.filter(rowMatch);
244
+ const otherPresetMatches = searching ? presets.filter((preset) => preset !== selected && preset.rows.some(rowMatch)) : [];
245
+ const otherMatchCount = otherPresetMatches.reduce((total, preset) => total + preset.rows.filter(rowMatch).length, 0);
246
+ const presetEffectiveOpen = searching || (presetOpen ?? true);
247
+ const globalEffectiveOpen = searching || (globalOpen ?? presets.length === 0);
248
+ const nothingMatches = searching && globalCount === 0 && selectedRows.length === 0 && otherPresetMatches.length === 0;
88
249
  const retry = () => {
89
250
  setState({ status: "loading" });
90
251
  setRequest((value) => value + 1);
91
252
  };
253
+ const toggleRow = (key) => {
254
+ setExpanded((current) => current === key ? null : key);
255
+ };
256
+ /** Trailing status and detail facts for one row of the selected preset. */
257
+ const presetRowCard = (preset, row, index) => {
258
+ const key = `preset:${preset.id}:${String(index)}`;
259
+ const title = moduleShortName(row.moduleName);
260
+ const failed = row.fiberPhase === "failed";
261
+ const stateText = failed ? t("failedTag") : row.enabled === true ? t("enabledTag") : row.enabled === false ? t("disabledTag") : t("conditionalTag");
262
+ const kind = failed ? "failed" : row.enabled === true ? "enabled" : row.enabled === false ? "disabled" : "conditional";
263
+ return (0, react_jsx_runtime.jsx)(PluginCard, {
264
+ rowKey: key,
265
+ moduleName: row.moduleName,
266
+ entryId: row.entryId,
267
+ failed,
268
+ expanded,
269
+ onToggle: toggleRow,
270
+ ariaLabel: `${title}, ${stateText}`,
271
+ trailing: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [row.enabled === true && !failed && row.fiberPhase !== null ? (0, react_jsx_runtime.jsx)(PhaseDot, {
272
+ phase: row.fiberPhase,
273
+ t
274
+ }) : null, (0, react_jsx_runtime.jsx)(StateTag, {
275
+ kind,
276
+ label: stateText
277
+ })] }),
278
+ children: (0, react_jsx_runtime.jsx)(CardFacts, {
279
+ moduleName: row.moduleName,
280
+ moduleLabel: t("moduleLabel"),
281
+ entryId: row.entryId,
282
+ facts: [
283
+ [t("fromPreset"), presetName(preset)],
284
+ [t("configuration"), stateText],
285
+ ...row.fiberPhase === null ? [] : [[t("runtime"), phaseLabel(row.fiberPhase, t)]],
286
+ ...row.condition === void 0 ? [] : [[t("condition"), (0, react_jsx_runtime.jsx)("code", { children: row.condition }, "condition")]]
287
+ ]
288
+ })
289
+ }, key);
290
+ };
291
+ /** One global-plane row; a preset-provided row carries the presets that enable it. */
292
+ const globalRowCard = (entry, providers) => {
293
+ const key = `global:${entry.entryId}`;
294
+ const title = moduleShortName(entry.moduleName);
295
+ const failed = entry.fiberPhase === "failed";
296
+ const stateText = failed ? t("failedTag") : providers !== void 0 ? t("presetEnabledTag") : t(entry.enabled ? "enabledTag" : "disabledTag");
297
+ const kind = failed ? "failed" : providers !== void 0 ? "preset" : entry.enabled ? "enabled" : "disabled";
298
+ return (0, react_jsx_runtime.jsx)(PluginCard, {
299
+ rowKey: key,
300
+ moduleName: entry.moduleName,
301
+ entryId: entry.entryId,
302
+ failed,
303
+ expanded,
304
+ onToggle: toggleRow,
305
+ ariaLabel: `${title}, ${stateText}`,
306
+ trailing: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [entry.enabled && !failed && entry.fiberPhase !== null ? (0, react_jsx_runtime.jsx)(PhaseDot, {
307
+ phase: entry.fiberPhase,
308
+ t
309
+ }) : null, (0, react_jsx_runtime.jsx)(StateTag, {
310
+ kind,
311
+ label: stateText
312
+ })] }),
313
+ children: (0, react_jsx_runtime.jsx)(CardFacts, {
314
+ moduleName: entry.moduleName,
315
+ moduleLabel: t("moduleLabel"),
316
+ entryId: entry.entryId,
317
+ facts: providers !== void 0 ? [[t("configuration"), t("presetProvidedDetail")], [t("enabledIn"), (0, react_jsx_runtime.jsxs)("span", {
318
+ className: PluginInventorySettingsTab_module_css_default.enabledIn,
319
+ children: [(0, react_jsx_runtime.jsx)("span", { children: providers.map((preset) => presetName(preset)).join(" · ") }), (0, react_jsx_runtime.jsx)("button", {
320
+ type: "button",
321
+ className: PluginInventorySettingsTab_module_css_default.jumpLink,
322
+ onClick: () => {
323
+ setChosenPreset(providers[0].id);
324
+ },
325
+ children: t("viewInPreset")
326
+ })]
327
+ })]] : [[t("configuration"), t(entry.enabled ? "enabledTag" : "disabledTag")], ...entry.enabled ? [[t("runtime"), phaseLabel(entry.fiberPhase, t)]] : []]
328
+ })
329
+ }, key);
330
+ };
92
331
  return (0, react_jsx_runtime.jsxs)("div", {
93
332
  className: PluginInventorySettingsTab_module_css_default.section,
94
333
  "aria-busy": state.status === "loading",
@@ -108,7 +347,7 @@ window.__ModuleLoader__.load({
108
347
  children: t("retry")
109
348
  })]
110
349
  }) : null,
111
- state.status === "ready" ? (0, react_jsx_runtime.jsxs)("div", {
350
+ snapshot !== void 0 ? (0, react_jsx_runtime.jsxs)("div", {
112
351
  className: PluginInventorySettingsTab_module_css_default.catalog,
113
352
  children: [
114
353
  (0, react_jsx_runtime.jsxs)("label", {
@@ -130,82 +369,158 @@ window.__ModuleLoader__.load({
130
369
  })
131
370
  ]
132
371
  }),
133
- (0, react_jsx_runtime.jsxs)("div", {
134
- className: PluginInventorySettingsTab_module_css_default.catalogHeading,
135
- children: [(0, react_jsx_runtime.jsx)("h3", { children: t("catalog") }), (0, react_jsx_runtime.jsx)("span", {
136
- "data-plugin-count": filteredEntries.length,
137
- children: filteredEntries.length
138
- })]
139
- }),
140
- state.snapshot.entries.length === 0 ? (0, react_jsx_runtime.jsx)("p", {
372
+ entries.length === 0 && presets.length === 0 ? (0, react_jsx_runtime.jsx)("p", {
141
373
  className: PluginInventorySettingsTab_module_css_default.status,
142
374
  children: t("empty")
143
375
  }) : null,
144
- state.snapshot.entries.length > 0 && filteredEntries.length === 0 ? (0, react_jsx_runtime.jsx)("p", {
376
+ nothingMatches ? (0, react_jsx_runtime.jsx)("p", {
145
377
  className: PluginInventorySettingsTab_module_css_default.status,
146
378
  children: t("emptySearch")
147
379
  }) : null,
148
- filteredEntries.length > 0 ? (0, react_jsx_runtime.jsx)("ul", {
149
- className: PluginInventorySettingsTab_module_css_default.cards,
150
- children: filteredEntries.map((entry) => {
151
- const status = phaseLabel(entry.fiberPhase, t);
152
- const title = moduleShortName(entry.moduleName);
153
- const configuration = t(entry.enabled ? "enabledTag" : "disabledTag");
154
- const open = expanded === entry.entryId;
155
- const detailId = `${catalogId}-details-${encodeURIComponent(entry.entryId)}`;
156
- return (0, react_jsx_runtime.jsxs)("li", {
157
- className: PluginInventorySettingsTab_module_css_default.card,
158
- "data-plugin-entry": entry.entryId,
159
- "data-open": open ? "true" : void 0,
380
+ selected !== void 0 ? (0, react_jsx_runtime.jsxs)("section", {
381
+ className: PluginInventorySettingsTab_module_css_default.group,
382
+ "data-plugin-scope": "preset",
383
+ "data-preset-id": selected.id,
384
+ children: [
385
+ (0, react_jsx_runtime.jsxs)("div", {
386
+ className: PluginInventorySettingsTab_module_css_default.groupTitleRow,
160
387
  children: [(0, react_jsx_runtime.jsxs)("button", {
161
- className: PluginInventorySettingsTab_module_css_default.cardContent,
162
388
  type: "button",
163
- "aria-expanded": open,
164
- "aria-controls": detailId,
165
- "aria-label": entry.enabled ? `${title}, ${status}, ${configuration}` : `${title}, ${configuration}`,
389
+ className: PluginInventorySettingsTab_module_css_default.groupToggle,
390
+ "aria-expanded": presetEffectiveOpen,
391
+ "aria-controls": `${sectionId}-preset`,
166
392
  onClick: () => {
167
- setExpanded((current) => current === entry.entryId ? null : entry.entryId);
393
+ setPresetOpen(!presetEffectiveOpen);
168
394
  },
169
- children: [(0, react_jsx_runtime.jsx)("strong", {
170
- className: PluginInventorySettingsTab_module_css_default.cardTitle,
171
- title: entry.moduleName,
172
- children: title
173
- }), (0, react_jsx_runtime.jsxs)("span", {
174
- className: PluginInventorySettingsTab_module_css_default.cardTrailing,
175
- children: [
176
- entry.enabled ? (0, react_jsx_runtime.jsx)("span", {
177
- className: PluginInventorySettingsTab_module_css_default.statusDot,
178
- "data-phase": entry.fiberPhase ?? "unobserved",
179
- role: "img",
180
- "aria-label": status,
181
- title: status
182
- }) : null,
183
- (0, react_jsx_runtime.jsx)("span", {
184
- className: PluginInventorySettingsTab_module_css_default.configTag,
185
- "data-enabled": entry.enabled ? "true" : "false",
186
- children: configuration
187
- }),
188
- (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {
395
+ children: [(0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {
396
+ className: PluginInventorySettingsTab_module_css_default.chevron,
397
+ size: 12,
398
+ "aria-hidden": "true"
399
+ }), (0, react_jsx_runtime.jsx)("span", {
400
+ className: PluginInventorySettingsTab_module_css_default.groupTitle,
401
+ children: t("presetTitle")
402
+ })]
403
+ }), (0, react_jsx_runtime.jsx)("div", {
404
+ className: PluginInventorySettingsTab_module_css_default.headerEnd,
405
+ children: (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.Menu, {
406
+ open: switcherOpen,
407
+ onClose: () => {
408
+ setSwitcherOpen(false);
409
+ },
410
+ items: presets.map((preset) => ({
411
+ id: preset.id,
412
+ label: presetLabel(preset, t, presetName)
413
+ })),
414
+ selectedId: selected.id,
415
+ onSelect: (id) => {
416
+ setSwitcherOpen(false);
417
+ setChosenPreset(id);
418
+ },
419
+ align: "end",
420
+ portal: true,
421
+ anchor: (0, react_jsx_runtime.jsxs)("button", {
422
+ type: "button",
423
+ className: PluginInventorySettingsTab_module_css_default.switcher,
424
+ "aria-haspopup": "menu",
425
+ "aria-expanded": switcherOpen,
426
+ "aria-label": t("switcherLabel"),
427
+ onClick: () => {
428
+ setSwitcherOpen((value) => !value);
429
+ },
430
+ children: [(0, react_jsx_runtime.jsx)("span", {
431
+ className: PluginInventorySettingsTab_module_css_default.switcherLabel,
432
+ children: presetLabel(selected, t, presetName)
433
+ }), (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {
189
434
  className: PluginInventorySettingsTab_module_css_default.chevron,
190
- size: 12,
191
435
  "aria-hidden": "true"
192
- })
193
- ]
194
- })]
195
- }), open ? (0, react_jsx_runtime.jsxs)("div", {
196
- className: PluginInventorySettingsTab_module_css_default.cardDetails,
197
- id: detailId,
198
- children: [(0, react_jsx_runtime.jsx)("code", {
199
- className: PluginInventorySettingsTab_module_css_default.entryValue,
200
- "data-loader-entry": true,
201
- children: entry.entryId
202
- }), (0, react_jsx_runtime.jsxs)("dl", {
203
- className: PluginInventorySettingsTab_module_css_default.details,
204
- children: [(0, react_jsx_runtime.jsxs)("div", { children: [(0, react_jsx_runtime.jsx)("dt", { children: t("configuration") }), (0, react_jsx_runtime.jsx)("dd", { children: configuration })] }), entry.enabled ? (0, react_jsx_runtime.jsxs)("div", { children: [(0, react_jsx_runtime.jsx)("dt", { children: t("cordis") }), (0, react_jsx_runtime.jsx)("dd", { children: status })] }) : null]
436
+ })]
437
+ })
438
+ })
439
+ })]
440
+ }),
441
+ (0, react_jsx_runtime.jsxs)("p", {
442
+ className: PluginInventorySettingsTab_module_css_default.groupSub,
443
+ children: [t("presetSubtitle"), (0, react_jsx_runtime.jsx)("span", {
444
+ "data-preset-plugin-count": selectedRows.length,
445
+ children: ` · ${String(selectedRows.length)} ${t("countUnit")}`
446
+ })]
447
+ }),
448
+ presetEffectiveOpen ? (0, react_jsx_runtime.jsxs)("div", {
449
+ id: `${sectionId}-preset`,
450
+ className: PluginInventorySettingsTab_module_css_default.groupBody,
451
+ children: [
452
+ selected.broken !== void 0 ? (0, react_jsx_runtime.jsx)("p", {
453
+ className: PluginInventorySettingsTab_module_css_default.brokenNote,
454
+ role: "alert",
455
+ children: selected.broken
456
+ }) : null,
457
+ selectedRows.length > 0 ? (0, react_jsx_runtime.jsx)("ul", {
458
+ className: PluginInventorySettingsTab_module_css_default.cards,
459
+ children: selectedRows.map((row, index) => presetRowCard(selected, row, index))
460
+ }) : null,
461
+ otherMatchCount > 0 ? (0, react_jsx_runtime.jsxs)("p", {
462
+ className: PluginInventorySettingsTab_module_css_default.hint,
463
+ children: [t("matchesInOtherPresets", { count: String(otherMatchCount) }), otherPresetMatches.map((preset) => (0, react_jsx_runtime.jsx)("button", {
464
+ type: "button",
465
+ className: PluginInventorySettingsTab_module_css_default.jumpLink,
466
+ onClick: () => {
467
+ setChosenPreset(preset.id);
468
+ },
469
+ children: presetName(preset)
470
+ }, preset.id))]
471
+ }) : null
472
+ ]
473
+ }) : null
474
+ ]
475
+ }) : null,
476
+ entries.length > 0 ? (0, react_jsx_runtime.jsxs)("section", {
477
+ className: PluginInventorySettingsTab_module_css_default.group,
478
+ "data-plugin-scope": "global",
479
+ children: [
480
+ (0, react_jsx_runtime.jsx)("div", {
481
+ className: PluginInventorySettingsTab_module_css_default.groupTitleRow,
482
+ children: (0, react_jsx_runtime.jsxs)("button", {
483
+ type: "button",
484
+ className: PluginInventorySettingsTab_module_css_default.groupToggle,
485
+ "aria-expanded": globalEffectiveOpen,
486
+ "aria-controls": `${sectionId}-global`,
487
+ onClick: () => {
488
+ setGlobalOpen(!globalEffectiveOpen);
489
+ },
490
+ children: [(0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {
491
+ className: PluginInventorySettingsTab_module_css_default.chevron,
492
+ size: 12,
493
+ "aria-hidden": "true"
494
+ }), (0, react_jsx_runtime.jsx)("span", {
495
+ className: PluginInventorySettingsTab_module_css_default.groupTitle,
496
+ children: t("globalTitle")
205
497
  })]
206
- }) : null]
207
- }, entry.entryId);
208
- })
498
+ })
499
+ }),
500
+ (0, react_jsx_runtime.jsxs)("p", {
501
+ className: PluginInventorySettingsTab_module_css_default.groupSub,
502
+ children: [
503
+ t("globalSubtitle"),
504
+ (0, react_jsx_runtime.jsx)("span", {
505
+ "data-plugin-count": globalCount,
506
+ children: ` · ${String(globalCount)} ${t("countUnit")}`
507
+ }),
508
+ filteredFailed.length > 0 ? (0, react_jsx_runtime.jsxs)("span", {
509
+ className: PluginInventorySettingsTab_module_css_default.failedCount,
510
+ children: [
511
+ filteredFailed.length,
512
+ " ",
513
+ t("failedCountLabel")
514
+ ]
515
+ }) : null
516
+ ]
517
+ }),
518
+ globalEffectiveOpen && globalCount > 0 ? (0, react_jsx_runtime.jsxs)("ul", {
519
+ className: PluginInventorySettingsTab_module_css_default.cards,
520
+ id: `${sectionId}-global`,
521
+ children: [filteredFailed.map((entry) => globalRowCard(entry)), filteredRegular.map((entry) => globalRowCard(entry, entry.enabled ? void 0 : enabledIn.get(entry.moduleName)))]
522
+ }) : null
523
+ ]
209
524
  }) : null
210
525
  ]
211
526
  }) : null
@@ -222,18 +537,36 @@ window.__ModuleLoader__.load({
222
537
  error: "暂时无法读取插件。",
223
538
  retry: "重试",
224
539
  search: "搜索插件",
225
- catalog: "插件列表",
226
540
  empty: "暂无插件。",
227
541
  emptySearch: "没有匹配的插件。",
542
+ presetTitle: "会话插件",
543
+ presetSubtitle: "由 Agent 预设按会话组成",
544
+ countUnit: "个",
545
+ switcherLabel: "选择要查看的 Agent 预设",
546
+ presetOptionDefault: "{name}(默认)",
547
+ presetOptionBroken: "{name}(加载失败)",
548
+ globalTitle: "全局插件",
549
+ globalSubtitle: "系统与所有会话共用",
550
+ presetProvidedDetail: "全局已停用,由 Agent 预设按会话提供",
551
+ enabledIn: "启用于",
552
+ viewInPreset: "去预设分组查看",
553
+ matchesInOtherPresets: "其他预设中还有 {count} 个匹配:",
554
+ failedCountLabel: "个失败",
228
555
  enabledTag: "已启用",
229
556
  disabledTag: "已停用",
557
+ conditionalTag: "条件启用",
558
+ presetEnabledTag: "预设中启用",
559
+ failedTag: "启动失败",
560
+ moduleLabel: "完整名称",
561
+ fromPreset: "来自",
562
+ condition: "禁用条件",
230
563
  configuration: "配置状态",
231
- cordis: "Cordis 状态",
232
- unobserved: "未挂载",
564
+ runtime: "运行状态",
565
+ unobserved: "未运行",
233
566
  pending: "等待依赖",
234
567
  loadingPhase: "加载中",
235
- active: "已挂载",
236
- failed: "挂载失败",
568
+ active: "运行中",
569
+ failed: "启动失败",
237
570
  unloading: "卸载中"
238
571
  };
239
572
  /** English dictionary checked against the Chinese key set. */
@@ -243,18 +576,36 @@ window.__ModuleLoader__.load({
243
576
  error: "Plugins are temporarily unavailable.",
244
577
  retry: "Retry",
245
578
  search: "Search plugins",
246
- catalog: "Plugin list",
247
579
  empty: "No plugins are available.",
248
580
  emptySearch: "No matching plugins.",
581
+ presetTitle: "Session plugins",
582
+ presetSubtitle: "Composed per session by agent presets",
583
+ countUnit: "plugins",
584
+ switcherLabel: "Choose the agent preset to inspect",
585
+ presetOptionDefault: "{name} (default)",
586
+ presetOptionBroken: "{name} (failed to load)",
587
+ globalTitle: "Global plugins",
588
+ globalSubtitle: "Shared by the system and every session",
589
+ presetProvidedDetail: "Disabled globally; agent presets provide it per session",
590
+ enabledIn: "Enabled in",
591
+ viewInPreset: "View in the preset group",
592
+ matchesInOtherPresets: "{count} more matches in other presets: ",
593
+ failedCountLabel: "failed",
249
594
  enabledTag: "Enabled",
250
595
  disabledTag: "Disabled",
596
+ conditionalTag: "Conditional",
597
+ presetEnabledTag: "Enabled via presets",
598
+ failedTag: "Failed",
599
+ moduleLabel: "Module",
600
+ fromPreset: "From",
601
+ condition: "Disabled when",
251
602
  configuration: "Configuration",
252
- cordis: "Cordis status",
253
- unobserved: "Not mounted",
603
+ runtime: "Status",
604
+ unobserved: "Not running",
254
605
  pending: "Waiting for dependencies",
255
606
  loadingPhase: "Loading",
256
- active: "Mounted",
257
- failed: "Mount failed",
607
+ active: "Running",
608
+ failed: "Failed to start",
258
609
  unloading: "Unloading"
259
610
  };
260
611
  //#endregion
@@ -281,7 +632,12 @@ window.__ModuleLoader__.load({
281
632
  if (!result.ok) throw new Error(`pluginInventory.list failed: ${result.error.code}: ${result.error.message}`);
282
633
  return result.value;
283
634
  };
284
- const injected = () => ({ list });
635
+ const agentPresetCopy = ctx.locale.bind("settings.agentPreset");
636
+ const presetName = (preset) => presetDisplayText(preset, agentPresetCopy).name;
637
+ const injected = () => ({
638
+ list,
639
+ presetName
640
+ });
285
641
  ctx.slots.inject("settings.plugins.tab", () => ctx.slots.register({
286
642
  name: "settings.plugins.tab",
287
643
  id: "all",
@@ -1,13 +1,20 @@
1
1
  import { type ReactNode } from 'react';
2
2
  import type { PluginInventorySnapshot } from '@prettier-ai/dsh-api-remotes/client';
3
3
  import type { InjectFace, PropsLocale, PropsRuntime } from '@prettier-ai/dsh-client-ui-slots';
4
+ type AgentPresetGroup = NonNullable<PluginInventorySnapshot['agentPresets']>[number];
4
5
  /** Registration-side Remote face used by the section. */
5
6
  export interface PluginInventorySettingsTabInjected {
6
7
  /** Read a current Host inventory snapshot. */
7
8
  list: () => Promise<PluginInventorySnapshot>;
9
+ /**
10
+ * Display name for one preset: shipped presets resolve through the
11
+ * agent-preset dictionaries, user-authored ones keep their own metadata.
12
+ */
13
+ presetName: (preset: AgentPresetGroup) => string;
8
14
  }
9
15
  /** Full component props assembled by the Settings slot renderer. */
10
16
  export type PluginInventorySettingsTabProps = PropsRuntime<'settings.plugins.tab'> & PropsLocale<'settings.pluginInventory'> & InjectFace<PluginInventorySettingsTabInjected>;
11
- /** Render the read-only current Loader inventory. */
12
- export declare function PluginInventorySettingsTab({ list, t }: PluginInventorySettingsTabProps): ReactNode;
17
+ /** Render the read-only plugin inventory: agent presets first, then the global plane. */
18
+ export declare function PluginInventorySettingsTab({ list, presetName, t }: PluginInventorySettingsTabProps): ReactNode;
19
+ export {};
13
20
  //# sourceMappingURL=PluginInventorySettingsTab.d.ts.map
@@ -6,13 +6,31 @@ export declare const zh: {
6
6
  error: string;
7
7
  retry: string;
8
8
  search: string;
9
- catalog: string;
10
9
  empty: string;
11
10
  emptySearch: string;
11
+ presetTitle: string;
12
+ presetSubtitle: string;
13
+ countUnit: string;
14
+ switcherLabel: string;
15
+ presetOptionDefault: string;
16
+ presetOptionBroken: string;
17
+ globalTitle: string;
18
+ globalSubtitle: string;
19
+ presetProvidedDetail: string;
20
+ enabledIn: string;
21
+ viewInPreset: string;
22
+ matchesInOtherPresets: string;
23
+ failedCountLabel: string;
12
24
  enabledTag: string;
13
25
  disabledTag: string;
26
+ conditionalTag: string;
27
+ presetEnabledTag: string;
28
+ failedTag: string;
29
+ moduleLabel: string;
30
+ fromPreset: string;
31
+ condition: string;
14
32
  configuration: string;
15
- cordis: string;
33
+ runtime: string;
16
34
  unobserved: string;
17
35
  pending: string;
18
36
  loadingPhase: string;
@@ -29,13 +47,31 @@ export declare const en: {
29
47
  error: string;
30
48
  retry: string;
31
49
  search: string;
32
- catalog: string;
33
50
  empty: string;
34
51
  emptySearch: string;
52
+ presetTitle: string;
53
+ presetSubtitle: string;
54
+ countUnit: string;
55
+ switcherLabel: string;
56
+ presetOptionDefault: string;
57
+ presetOptionBroken: string;
58
+ globalTitle: string;
59
+ globalSubtitle: string;
60
+ presetProvidedDetail: string;
61
+ enabledIn: string;
62
+ viewInPreset: string;
63
+ matchesInOtherPresets: string;
64
+ failedCountLabel: string;
35
65
  enabledTag: string;
36
66
  disabledTag: string;
67
+ conditionalTag: string;
68
+ presetEnabledTag: string;
69
+ failedTag: string;
70
+ moduleLabel: string;
71
+ fromPreset: string;
72
+ condition: string;
37
73
  configuration: string;
38
- cordis: string;
74
+ runtime: string;
39
75
  unobserved: string;
40
76
  pending: string;
41
77
  loadingPhase: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@prettier-ai/dsh-client-ui-settings-plugin-inventory",
3
3
  "description": "Read-only Cordis Loader inventory tab in Web Plugins settings",
4
- "version": "0.1.2-alpha.1",
4
+ "version": "0.1.2-alpha.3",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -34,34 +34,32 @@
34
34
  "inject": [
35
35
  "@prettier-ai/dsh-api-remotes",
36
36
  "@prettier-ai/dsh-client-ui-settings",
37
- "@prettier-ai/dsh-client-locale"
37
+ "@prettier-ai/dsh-client-locale",
38
+ "@prettier-ai/dsh-client-ui-agent-preset"
38
39
  ],
39
40
  "platform": "web"
40
41
  }
41
42
  },
42
43
  "license": "MIT",
43
44
  "peerDependencies": {
44
- "@prettier-ai/dsh-client-ui-settings": "^0.1.2-alpha.1",
45
- "@prettier-ai/dsh-invariants": "^0.1.2-alpha.1",
46
- "@prettier-ai/cordis": "^4.0.1",
47
- "@prettier-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1",
48
- "@prettier-ai/dsh-client-locale": "^0.1.2-alpha.1",
49
- "@prettier-ai/dsh-api-remotes": "^0.1.2-alpha.1"
45
+ "@prettier-ai/cordis": "^4.0.2"
50
46
  },
51
47
  "devDependencies": {
52
48
  "@testing-library/react": "^16.1.0",
53
49
  "@types/react": "~18.3.1",
54
50
  "react": "^18.2.0",
55
51
  "react-dom": "^18.2.0",
56
- "@prettier-ai/dsh-api-remotes": "^0.1.2-alpha.1",
57
- "@prettier-ai/dsh-client-test-runtime": "^0.1.2-alpha.1",
58
- "@prettier-ai/dsh-client-locale": "^0.1.2-alpha.1",
59
- "@prettier-ai/dsh-client-ui-primitives": "^0.1.2-alpha.1",
60
- "@prettier-ai/dsh-client-ui-settings": "^0.1.2-alpha.1",
61
- "@prettier-ai/dsh-client-ui-slots": "^0.1.2-alpha.1",
62
- "@prettier-ai/dsh-invariants": "^0.1.2-alpha.1",
63
- "@prettier-ai/cordis": "^4.0.1",
64
- "@prettier-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1"
52
+ "@prettier-ai/dsh-api-remotes": "^0.1.2-alpha.3",
53
+ "@prettier-ai/dsh-client-locale": "^0.1.2-alpha.3",
54
+ "@prettier-ai/dsh-client-test-runtime": "^0.1.2-alpha.3",
55
+ "@prettier-ai/dsh-client-ui-primitives": "^0.1.2-alpha.3",
56
+ "@prettier-ai/dsh-client-ui-settings": "^0.1.2-alpha.3",
57
+ "@prettier-ai/dsh-client-ui-agent-preset": "^0.1.2-alpha.3",
58
+ "@prettier-ai/dsh-invariants": "^0.1.2-alpha.3",
59
+ "@prettier-ai/cordis": "^4.0.2",
60
+ "@prettier-ai/dsh-client-ui-slots": "^0.1.2-alpha.3",
61
+ "@prettier-ai/dsh-client-ui-renderer": "^0.1.2-alpha.3",
62
+ "@prettier-ai/dsh-agent-presets": "^0.1.2-alpha.3"
65
63
  },
66
64
  "files": [
67
65
  "lib/index.js",