فهرست منبع

fix(web): trim composition marker from plugin subtitles

Turtle 2 هفته پیش
والد
کامیت
2e2489b888

+ 2 - 2
packages/client/ui-settings-plugin-inventory/README.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write packages/client/ui-settings-plugin-inventory/README.md
-README.md: 2a7a00f037905c9e70edb283c22946a2bc6c0247
-README.zh.md: 7be131a8a1a3f1e1fbbf2fbff1ac65c1829f4c10
+README.md: d56e7bcd2bc8a1590910045319db5cd3b6307e85
+README.zh.md: c83a21d6bc63e9e3f4c135d00f7d4926c6588144

+ 2 - 2
packages/client/ui-settings-plugin-inventory/README.md

@@ -29,7 +29,7 @@ Open the Plugins section in Settings and select the **Plugin list** tab to inspe
 
 ### Reading a card
 
-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. 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.
+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.
 
 ### The preset switcher
 
@@ -55,7 +55,7 @@ The browser plugin registers one localized `settings.plugins.tab` contribution w
 
 ### Rendering
 
-Row keys are scope-qualified (`global:`, `preset:<id>:<index>`), so one module appearing in both scopes keeps distinct disclosure state; a declared entry id appears in the collapsed row and expanded detail, while a row without one stays unlabeled, and the renderer never classifies ids 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.
+Row keys are scope-qualified (`global:`, `preset:<id>:<index>`), so one module appearing in both scopes keeps distinct disclosure state; a declared entry id appears in expanded details and supplies the collapsed subtitle after removal of a leading composition `include:` marker, while a row without one stays unlabeled. 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.
 
 </details>
 

+ 2 - 2
packages/client/ui-settings-plugin-inventory/README.zh.md

@@ -29,7 +29,7 @@ kind: "package-reference"
 
 ### 阅读卡片
 
-每张收起的卡片使用模块短名称作为主标题,在下方显示稳定的条目 id,并以小标签表示启停状态;已启用的条目还会显示彩色根 fiber 状态圆点。长条目 id 会在行内截断,悬停时仍可查看完整值。展开卡片后会显示声明的条目 id、完整模块标识与状态事实:预设行说明它来自哪个预设、组合存活时的运行状态,以及它携带的禁用条件;被预设提供的全局行说明它由 Agent 预设按会话提供、列出启用它的预设,并提供跳转到预设组的入口。预设名经共享的 `presetDisplayText` 纯函数(`dsh-agent-presets/display`)叠在 [`ui-agent-preset`](../ui-agent-preset/README.zh.md) 的字典上解析:内置预设走当前语言,用户自建预设保留自己的元数据,因此英文界面不会回显预设文件里的中文名。搜索按模块名称与条目 id 过滤两组。
+每张收起的卡片使用模块短名称作为主标题,在下方显示稳定的条目 id,并以小标签表示启停状态;已启用的条目还会显示彩色根 fiber 状态圆点。组合生成的次标题省略开头的 `include:` 标记;悬停、搜索、无障碍名称与展开详情仍保留完整 id。长条目 id 会在行内截断,悬停时仍可查看完整值。展开卡片后会显示声明的条目 id、完整模块标识与状态事实:预设行说明它来自哪个预设、组合存活时的运行状态,以及它携带的禁用条件;被预设提供的全局行说明它由 Agent 预设按会话提供、列出启用它的预设,并提供跳转到预设组的入口。预设名经共享的 `presetDisplayText` 纯函数(`dsh-agent-presets/display`)叠在 [`ui-agent-preset`](../ui-agent-preset/README.zh.md) 的字典上解析:内置预设走当前语言,用户自建预设保留自己的元数据,因此英文界面不会回显预设文件里的中文名。搜索按模块名称与条目 id 过滤两组。
 
 ### 预设切换器
 
@@ -55,7 +55,7 @@ kind: "package-reference"
 
 ### 渲染
 
-行 key 按作用域限定(`global:`、`preset:<id>:<index>`),因此同一模块出现在两个作用域时保持各自的展开状态;声明的条目 id 同时出现在收起行与展开详情中,没有 id 的行不显示次级标签,渲染器也不按字符串形状对 id 分类。预设提供标记在客户端推导:一个全局条目在全局被停用、且至少一个预设行对同一模块标识实际启用时才携带它,因此被所有预设关掉(或仅条件声明)的模块保持单纯的已停用,而不是夸大提供关系。
+行 key 按作用域限定(`global:`、`preset:<id>:<index>`),因此同一模块出现在两个作用域时保持各自的展开状态;声明的条目 id 出现在展开详情中,并在去掉开头的组合 `include:` 标记后作为收起次标题,没有 id 的行不显示次级标签。预设提供标记在客户端推导:一个全局条目在全局被停用、且至少一个预设行对同一模块标识实际启用时才携带它,因此被所有预设关掉(或仅条件声明)的模块保持单纯的已停用,而不是夸大提供关系。
 
 </details>
 

+ 6 - 1
packages/client/ui-settings-plugin-inventory/src/client/PluginInventorySettingsTab.tsx

@@ -63,6 +63,11 @@ function moduleShortName(moduleName: string): string {
     .replace(/^dsh-(?:host-|client-)?/, '')
 }
 
+/** Display an entry identity without the composition-only `include:` marker. */
+function entrySubtitle(entryId: string): string {
+  return entryId.replace(/^include:/, '')
+}
+
 /** Whether one row's module name or entry id matches the catalog query. */
 function matches(moduleName: string, entryId: string | null, normalizedQuery: string): boolean {
   if (normalizedQuery.length === 0) return true
@@ -120,7 +125,7 @@ function PluginCard({ rowKey, moduleName, entryId, trailing, ariaLabel, failed,
             <IconChevronDownOutline14 className={css.chevron} size={12} aria-hidden="true" />
           </span>
         </span>
-        {entryId === null ? null : <code className={css.cardIdentity} title={entryId}>{entryId}</code>}
+        {entryId === null ? null : <code className={css.cardIdentity} title={entryId}>{entrySubtitle(entryId)}</code>}
       </button>
       {open ? <div className={css.cardDetails} id={detailId}>{children}</div> : null}
     </li>

+ 5 - 4
packages/client/ui-settings-plugin-inventory/tests/components.client.spec.tsx

@@ -135,7 +135,8 @@ describe('PluginInventorySettingsTab', () => {
   })
 
   it('distinguishes collapsed same-module rows by stable entry id', async () => {
-    const longId = 'tool-subagent-secondary-with-a-complete-stable-identity'
+    const longId = 'include:agent-presets:tool-subagent-secondary-with-a-complete-stable-identity'
+    const subtitle = 'agent-presets:tool-subagent-secondary-with-a-complete-stable-identity'
     await renderReady({
       entries: [],
       agentPresets: [{
@@ -156,10 +157,10 @@ describe('PluginInventorySettingsTab', () => {
     expect(secondary.children).toHaveLength(2)
     expect(secondary.children[0]?.textContent).toContain('tool-subagent')
     expect(secondary.children[0]?.textContent).toContain('Disabled')
-    expect(secondary.children[1]?.textContent).toBe(longId)
-    expect(screen.getByTitle(longId).textContent).toBe(longId)
+    expect(secondary.children[1]?.textContent).toBe(subtitle)
+    expect(screen.getByTitle(longId).textContent).toBe(subtitle)
 
-    fireEvent.change(screen.getByRole('searchbox', { name: en.search }), { target: { value: 'secondary' } })
+    fireEvent.change(screen.getByRole('searchbox', { name: en.search }), { target: { value: 'include:agent-presets:tool-subagent-secondary' } })
     expect(screen.queryByRole('button', { name: 'tool-subagent, tool-subagent-primary, Enabled' })).toBeNull()
     const filteredSecondary = screen.getByRole('button', { name: `tool-subagent, ${longId}, Disabled` })
     fireEvent.click(filteredSecondary)