Sfoglia il codice sorgente

Show module names for dynamic plugins

ZiyaZhang 1 mese fa
parent
commit
eea356e785

+ 13 - 3
packages/client/ui-plugins/src/client/PluginSettingsSection.tsx

@@ -45,10 +45,19 @@ function phaseLabel(
   return phase === null ? t('unobserved') : t(PHASE_KEYS[phase])
 }
 
+/** Compact a module specifier without guessing whether its Loader id was generated. */
+function moduleShortName(moduleName: string): string {
+  const unscoped = moduleName.startsWith('@') ? moduleName.slice(moduleName.indexOf('/') + 1) : moduleName
+  return unscoped
+    .replace(/^cordis:/, '')
+    .replace(/^cordis-plugin-/, '')
+    .replace(/^dsh-(?:host-|client-)?/, '')
+}
+
 /** Whether an inventory row matches the local catalog query. */
 function matches(entry: PluginInventoryEntry, normalizedQuery: string): boolean {
   if (normalizedQuery.length === 0) return true
-  return [entry.displayId, entry.entryId]
+  return [entry.moduleName, entry.entryId]
     .some(value => value.toLocaleLowerCase().includes(normalizedQuery))
 }
 
@@ -125,6 +134,7 @@ export function PluginSettingsSection({ list, t }: PluginSettingsSectionProps):
             <ul className={css.cards}>
               {filteredEntries.map((entry) => {
                 const status = phaseLabel(entry.fiberPhase, t)
+                const title = moduleShortName(entry.moduleName)
                 const open = expanded === entry.entryId
                 const detailId = `${titleId}-details-${encodeURIComponent(entry.entryId)}`
                 return (
@@ -139,12 +149,12 @@ export function PluginSettingsSection({ list, t }: PluginSettingsSectionProps):
                       type="button"
                       aria-expanded={open}
                       aria-controls={detailId}
-                      aria-label={`${entry.displayId}, ${status}, ${t(entry.enabled ? 'enabledTag' : 'disabledTag')}`}
+                      aria-label={`${title}, ${status}, ${t(entry.enabled ? 'enabledTag' : 'disabledTag')}`}
                       onClick={() => {
                         setExpanded(current => current === entry.entryId ? null : entry.entryId)
                       }}
                     >
-                      <strong className={css.cardTitle}>{entry.displayId}</strong>
+                      <strong className={css.cardTitle} title={entry.moduleName}>{title}</strong>
                       <span className={css.cardTrailing}>
                         <span
                           className={css.statusDot}

+ 12 - 12
packages/client/ui-plugins/tests/components.spec.tsx

@@ -26,12 +26,12 @@ function props(list: PluginSettingsSectionInjected['list']): PluginSettingsSecti
 
 const SNAPSHOT = {
   entries: [
-    { entryId: 'active', displayId: 'active-name', enabled: true, fiberPhase: 'active' },
-    { entryId: 'pending', displayId: 'pending-name', enabled: true, fiberPhase: 'pending' },
-    { entryId: 'loading', displayId: 'loading-name', enabled: true, fiberPhase: 'loading' },
-    { entryId: 'failed', displayId: 'failed-name', enabled: true, fiberPhase: 'failed' },
-    { entryId: 'unloading', displayId: 'unloading-name', enabled: true, fiberPhase: 'unloading' },
-    { entryId: 'disabled-entry', displayId: 'disabled-name', enabled: false, fiberPhase: null },
+    { entryId: '8a1b2c3d', moduleName: '@deepseek-ai/cordis-plugin-hmr', enabled: true, fiberPhase: 'active' },
+    { entryId: 'pending', moduleName: '@fixture/pending-name', enabled: true, fiberPhase: 'pending' },
+    { entryId: 'loading', moduleName: '@fixture/loading-name', enabled: true, fiberPhase: 'loading' },
+    { entryId: 'failed', moduleName: '@fixture/failed-name', enabled: true, fiberPhase: 'failed' },
+    { entryId: 'unloading', moduleName: '@fixture/unloading-name', enabled: true, fiberPhase: 'unloading' },
+    { entryId: 'disabled-entry', moduleName: '@deepseek-ai/dsh-host-directory-picker-native', enabled: false, fiberPhase: null },
   ],
 } as unknown as Snapshot
 
@@ -60,28 +60,28 @@ describe('PluginSettingsSection', () => {
     ]) {
       expect(screen.getByRole('img', { name: value })).toBeTruthy()
     }
-    const active = screen.getByRole('button', { name: 'active-name, Mounted, Enabled' })
+    const active = screen.getByRole('button', { name: 'hmr, Mounted, Enabled' })
     expect(active.getAttribute('aria-expanded')).toBe('false')
     fireEvent.click(active)
     expect(active.getAttribute('aria-expanded')).toBe('true')
-    expect(view.container.querySelector('[data-loader-entry]')?.textContent).toBe('active')
+    expect(view.container.querySelector('[data-loader-entry]')?.textContent).toBe('8a1b2c3d')
     expect(screen.getByText(en.configuration)).toBeTruthy()
     expect(screen.getByText(en.cordis)).toBeTruthy()
     fireEvent.click(active)
     expect(view.container.querySelector('[data-loader-entry]')).toBeNull()
   })
 
-  it('filters by local id or Loader entry id', async () => {
+  it('filters by module name or Loader entry id', async () => {
     render(<PluginSettingsSection {...props(async () => SNAPSHOT)} />)
     const search = await screen.findByRole('searchbox', { name: en.search })
 
     fireEvent.change(search, { target: { value: 'disabled-entry' } })
     expect(screen.getAllByRole('listitem')).toHaveLength(1)
-    expect(screen.getByText('disabled-name')).toBeTruthy()
+    expect(screen.getByText('directory-picker-native')).toBeTruthy()
 
-    fireEvent.change(search, { target: { value: 'pending' } })
+    fireEvent.change(search, { target: { value: 'cordis-plugin-hmr' } })
     expect(screen.getAllByRole('listitem')).toHaveLength(1)
-    expect(screen.getByText('pending-name')).toBeTruthy()
+    expect(screen.getByText('hmr')).toBeTruthy()
 
     fireEvent.change(search, { target: { value: 'not-a-plugin' } })
     expect(screen.queryAllByRole('listitem')).toHaveLength(0)

+ 2 - 2
packages/host/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/host/plugin-inventory/README.md
-README.md: d7a50824d337c66a30ba4332ffb1e36f9ad46547
-README.zh.md: e424b968f1f692fb30b3b0e49efe6c3cd5fadf70
+README.md: 23fbf07d7900ecc881f81b5da3f8cbe6a45669de
+README.zh.md: 87058cde595b83e980b8f3cec4192e6099b8d9ea

+ 1 - 1
packages/host/plugin-inventory/README.md

@@ -2,7 +2,7 @@
 
 English | [中文](README.zh.md)
 
-Read-only Host projection of the current Cordis Loader tree. `PluginInventoryService` registers the `pluginInventory` service and publishes one generated direct Remote, `pluginInventory/list`. Every call reads `ctx.loader.entries()` directly, skips structural group rows, and returns the remaining entries in Loader order with only their Loader entry id, local display id, effective enablement, and current root Fiber phase.
+Read-only Host projection of the current Cordis Loader tree. `PluginInventoryService` registers the `pluginInventory` service and publishes one generated direct Remote, `pluginInventory/list`. Every call reads `ctx.loader.entries()` directly, skips structural group rows, and returns the remaining entries in Loader order with only their Loader entry id, module specifier, effective enablement, and current root Fiber phase.
 
 The phase is `pending`, `loading`, `active`, `failed`, or `unloading`; it is `null` when the entry has no live root Fiber. The snapshot is intentionally point-in-time: Loader remains the sole lifecycle authority, while this package owns no cache, history, provenance model, event stream, or mutation path. Its public payload types live under `./types`, and TypeRT generates the Host and Client Remote artifacts exposed by `./typert` and `./remote`.
 

+ 1 - 1
packages/host/plugin-inventory/README.zh.md

@@ -2,7 +2,7 @@
 
 [English](README.md) | 中文
 
-当前 Cordis Loader 树的只读 Host 投影。`PluginInventoryService` 注册 `pluginInventory` 服务,并发布一个由 TypeRT 生成的直接 Remote:`pluginInventory/list`。每次调用都直接读取 `ctx.loader.entries()`,跳过结构性的 group 行,再按 Loader 顺序返回其余条目,并且只包含 Loader 条目 id、本地展示 id、有效启用状态与当前根 Fiber 阶段。
+当前 Cordis Loader 树的只读 Host 投影。`PluginInventoryService` 注册 `pluginInventory` 服务,并发布一个由 TypeRT 生成的直接 Remote:`pluginInventory/list`。每次调用都直接读取 `ctx.loader.entries()`,跳过结构性的 group 行,再按 Loader 顺序返回其余条目,并且只包含 Loader 条目 id、模块标识、有效启用状态与当前根 Fiber 阶段。
 
 阶段为 `pending`、`loading`、`active`、`failed` 或 `unloading`;条目没有存活的根 Fiber 时则为 `null`。该快照刻意只表示调用当下:Loader 仍是唯一的生命周期权威,本包不拥有缓存、历史、来源模型、事件流或修改路径。公开 payload 类型位于 `./types`,TypeRT 生成由 `./typert` 与 `./remote` 导出的 Host 和 Client Remote 产物。
 

+ 1 - 1
packages/host/plugin-inventory/src/index.ts

@@ -60,7 +60,7 @@ export class PluginInventoryService extends GatewayService {
       if (entry.options.group) continue
       entries.push({
         entryId: pluginEntryId(entry.id),
-        displayId: entry.options.id,
+        moduleName: entry.options.name,
         enabled: !entry.disabled,
         fiberPhase: entry.fiber === undefined ? null : FIBER_PHASE[entry.fiber.state],
       })

+ 2 - 2
packages/host/plugin-inventory/src/types.ts

@@ -15,8 +15,8 @@ export type PluginFiberPhase =
 /** One non-group Loader entry exposed to trusted clients. */
 export interface PluginInventoryEntry {
   readonly entryId: PluginEntryId
-  /** Local Loader id used as the compact card title. */
-  readonly displayId: string
+  /** Exact module specifier imported by the Loader entry. */
+  readonly moduleName: string
   /** Effective Loader enablement, including disabled ancestor groups. */
   readonly enabled: boolean
   readonly fiberPhase: PluginFiberPhase

+ 4 - 4
packages/host/plugin-inventory/tests/inventory.spec.ts

@@ -56,19 +56,19 @@ describe('PluginInventoryService', () => {
       entries: [
         {
           entryId: activeId,
-          displayId: activeId,
+          moduleName: 'cordis:active',
           enabled: true,
           fiberPhase: 'active',
         },
         {
           entryId: pendingId,
-          displayId: pendingId,
+          moduleName: 'cordis:pending',
           enabled: true,
           fiberPhase: 'pending',
         },
         {
           entryId: disabledId,
-          displayId: disabledId,
+          moduleName: 'cordis:not-installed',
           enabled: false,
           fiberPhase: null,
         },
@@ -78,7 +78,7 @@ describe('PluginInventoryService', () => {
     await ctx.loader.update(activeId, { disabled: true })
     expect(inventory.list().entries.find(entry => entry.entryId === activeId)).toEqual({
       entryId: activeId,
-      displayId: activeId,
+      moduleName: 'cordis:active',
       enabled: false,
       fiberPhase: null,
     })