فهرست منبع

fix(tui): prevent sidebar model wrapping

Victor M Varela 2 هفته پیش
والد
کامیت
4670db0a4f
2فایلهای تغییر یافته به همراه93 افزوده شده و 2 حذف شده
  1. 83 0
      src/tui.test.ts
  2. 10 2
      src/tui.ts

+ 83 - 0
src/tui.test.ts

@@ -82,6 +82,89 @@ describe('tui sidebar agents', () => {
     expect(getSidebarActivityIndicator(true, 100)).toBe('⠙');
     expect(getSidebarActivityIndicator(true, 100)).toBe('⠙');
     expect(getSidebarActivityIndicator(true, 1_000)).toBe('⠋');
     expect(getSidebarActivityIndicator(true, 1_000)).toBe('⠋');
   });
   });
+
+  test('keeps compact agent rows single-line with truncated right-aligned model IDs', async () => {
+    const root = fs.mkdtempSync(path.join(os.tmpdir(), 'omos-compact-row-'));
+    const projectDir = path.join(root, 'project');
+    const disposers: Array<() => void> = [];
+    let slotPlugin: { slots: { sidebar_content: () => unknown } } | undefined;
+    let setup: Awaited<ReturnType<typeof testRender>> | undefined;
+
+    try {
+      fs.mkdirSync(projectDir, { recursive: true });
+      recordTuiAgentModels(
+        {
+          agentModels: {
+            explorer: 'fireworks-ai/accounts/fireworks/routers/kimi-k2p5-turbo',
+            oracle: 'openai/gpt-5.6-luna-fast',
+          },
+        },
+        projectDir,
+      );
+
+      await tuiPlugin.tui(
+        {
+          state: { path: { directory: projectDir } },
+          route: { current: { name: 'home' } },
+          lifecycle: {
+            onDispose: (callback: () => void) => {
+              disposers.push(callback);
+              return () => {};
+            },
+          },
+          renderer: { requestRender: () => {} },
+          slots: {
+            register: (plugin: typeof slotPlugin) => {
+              slotPlugin = plugin;
+              return 'test-slot';
+            },
+          },
+          theme: {
+            current: {
+              accent: '#22c55e',
+              background: '#111111',
+              borderActive: '#555555',
+              text: '#ffffff',
+              textMuted: '#aaaaaa',
+            },
+          },
+        } as Parameters<typeof tuiPlugin.tui>[0],
+        {},
+        { version: 'test' } as Parameters<typeof tuiPlugin.tui>[2],
+      );
+
+      setup = await testRender(
+        () => slotPlugin?.slots.sidebar_content() as never,
+        { width: 36, height: 14 },
+      );
+      await setup.renderOnce();
+
+      const frame = setup.captureCharFrame();
+      const lines = frame.split('\n').map((l) => l.trimEnd());
+
+      // Find the explorer and oracle lines
+      const explorerLineIdx = lines.findIndex((l) => l.includes('explorer'));
+      const oracleLineIdx = lines.findIndex((l) => l.includes('oracle'));
+
+      expect(explorerLineIdx).toBeGreaterThan(-1);
+      expect(oracleLineIdx).toBe(explorerLineIdx + 1); // Strictly adjacent consecutive rows (no multi-line wrapping)
+
+      // Explorer row should have the agent label on left and truncated model on right
+      const explorerLine = lines[explorerLineIdx];
+      expect(explorerLine).toMatch(/explorer\s+account\.\.\.p5-turbo/);
+
+      // Oracle row should be single-line with right-aligned model
+      const oracleLine = lines[oracleLineIdx];
+      expect(oracleLine).toMatch(/oracle\s+gpt-5\.6-luna-fast/);
+
+      // No unwrapped model path fragments should appear on separate lines
+      expect(frame).not.toMatch(/fireworks\/routers\//);
+    } finally {
+      setup?.renderer.destroy();
+      for (const dispose of disposers) dispose();
+      fs.rmSync(root, { recursive: true, force: true });
+    }
+  });
 });
 });
 
 
 describe('live TUI activity rendering', () => {
 describe('live TUI activity rendering', () => {

+ 10 - 2
src/tui.ts

@@ -283,11 +283,19 @@ function compactAgentRow(
       justifyContent: 'space-between',
       justifyContent: 'space-between',
     },
     },
     [
     [
-      box({ width: 16, flexDirection: 'row' }, [
+      box({ width: 16, flexShrink: 0, flexDirection: 'row' }, [
         text({ fg: theme.textMuted, width: 14 }, [label]),
         text({ fg: theme.textMuted, width: 14 }, [label]),
         activityIndicator(active, now, theme),
         activityIndicator(active, now, theme),
       ]),
       ]),
-      text({ fg: theme.textMuted }, [modelName]),
+      text(
+        {
+          fg: theme.textMuted,
+          wrapMode: 'none',
+          truncate: true,
+          flexShrink: 1,
+        },
+        [modelName],
+      ),
     ],
     ],
   );
   );
 }
 }