Skip to content

Commit 4383138

Browse files
committed
feat(tui): default context gauge, short cwd chip, braille orchestrating loader
1 parent f4d9d43 commit 4383138

7 files changed

Lines changed: 51 additions & 23 deletions

File tree

apps/pythinker-code/src/tui/components/chrome/status-bar.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,17 @@ function renderModesChip(status: StatusBarStatus): string | undefined {
102102
}
103103

104104
function shortenCwd(cwd: string, homeDir: string | null): string {
105-
if (homeDir === null || homeDir.length === 0) return cwd;
106-
if (cwd === homeDir) return '~';
107-
return cwd.startsWith(`${homeDir}${sep}`) ? `~${cwd.slice(homeDir.length)}` : cwd;
105+
const path = homeDir !== null && homeDir.length > 0
106+
? cwd === homeDir
107+
? '~'
108+
: cwd.startsWith(`${homeDir}${sep}`)
109+
? `~${cwd.slice(homeDir.length)}`
110+
: cwd
111+
: cwd;
112+
const segments = path.startsWith(`~${sep}`)
113+
? path.slice(2).split(sep)
114+
: path.startsWith(sep)
115+
? path.slice(1).split(sep)
116+
: [];
117+
return segments.length > 2 ? `…${sep}${segments.slice(-2).join(sep)}` : path;
108118
}

apps/pythinker-code/src/tui/components/messages/dynamic-workflow-mission-control.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { truncateToWidth, visibleWidth, type Component } from '@earendil-works/pi-tui';
22

3-
import { DYNAMIC_WORKFLOW_RENDERING } from '#/tui/constant/rendering';
3+
import {
4+
BRAILLE_SPINNER_FRAMES,
5+
BRAILLE_SPINNER_INTERVAL_MS,
6+
DYNAMIC_WORKFLOW_RENDERING,
7+
} from '#/tui/constant/rendering';
48
import { currentTheme } from '#/tui/theme';
59
import { shimmerText } from '#/tui/utils/shimmer';
610

@@ -509,17 +513,15 @@ export class DynamicWorkflowMissionControlComponent implements Component {
509513
const terminal = isTerminalRequestPhase(this.model.requestPhase);
510514
const frame = Math.floor(
511515
Math.max(0, nowMs - this.model.startedAtMs) /
512-
DYNAMIC_WORKFLOW_RENDERING.progressFrameMs,
516+
BRAILLE_SPINNER_INTERVAL_MS,
513517
);
514518
const loader = terminal
515519
? currentTheme.fg(requestPhaseColor(this.model.requestPhase), requestPhaseSymbol(this.model.requestPhase))
516520
: this.activitySpinnerText === undefined
517521
? currentTheme.fg('primary', '●')
518522
: currentTheme.fg(
519523
'primary',
520-
DYNAMIC_WORKFLOW_RENDERING.progressFrames[
521-
frame % DYNAMIC_WORKFLOW_RENDERING.progressFrames.length
522-
] ?? DYNAMIC_WORKFLOW_RENDERING.progressFrames[0],
524+
BRAILLE_SPINNER_FRAMES[frame % BRAILLE_SPINNER_FRAMES.length]!,
523525
);
524526
const aggregateMembers = this.aggregateMembers();
525527
// All spawned agents are done but the tool result has not arrived yet:

apps/pythinker-code/src/tui/runtime/footer/footer-model.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -562,11 +562,11 @@ export function selectStatusBarExtras(
562562
): string[] {
563563
const parts = selectStatusItemParts(state, clockMs, statusLine);
564564
const items = [
565+
parts.context,
566+
parts.git,
565567
parts.update,
566568
parts.speed,
567569
parts.spend,
568-
parts.context,
569-
parts.git,
570570
parts.elapsed,
571571
parts.goal,
572572
].filter((item): item is string => item !== null);

apps/pythinker-code/test/tui/activity-pane.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ describe('updateActivityPane terminal progress', () => {
220220
expect(state.activityContainer.children).toHaveLength(0);
221221
expect(vi.getTimerCount()).toBe(timersBeforeMissionControl);
222222
const output = strip(missionControl.render(100).join('\n'));
223-
expect(output).toMatch(/[] Orchestrating/);
223+
expect(output).toMatch(/[] Orchestrating/);
224224
expect(output).not.toContain(formatThinkingSpinnerLabel());
225225

226226
state.activitySpinner?.instance.stop();
@@ -369,7 +369,9 @@ describe('updateActivityPane terminal progress', () => {
369369
state.livePane = { ...state.livePane, mode: 'tool' };
370370
driver.updateActivityPane();
371371
const missionControl = startDynamicWorkflow(driver, state);
372-
expect(strip(missionControl.render(100).join('\n'))).toMatch(/[] Orchestrating/);
372+
expect(strip(missionControl.render(100).join('\n'))).toMatch(
373+
/[] Orchestrating/,
374+
);
373375

374376
cleanup(driver);
375377
driver.updateActivityPane();

apps/pythinker-code/test/tui/components/messages/dynamic-workflow-mission-control.test.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ describe('DynamicWorkflowMissionControlComponent', () => {
356356
expect(vi.getTimerCount()).toBe(timerCount);
357357
});
358358

359-
it('shimmers Orchestrating without changing its text or creating a timer', () => {
359+
it('animates the braille header and shimmers Orchestrating without creating a timer', () => {
360360
vi.useFakeTimers();
361361
vi.setSystemTime(0);
362362
const previousLevel = chalk.level;
@@ -373,8 +373,11 @@ describe('DynamicWorkflowMissionControlComponent', () => {
373373
vi.setSystemTime(BRAILLE_SPINNER_INTERVAL_MS);
374374
const after = aggregateLine(component.render(100).join('\n'));
375375

376-
expect(strip(after)).toBe(strip(before));
376+
expect(strip(after).replace(/[]/u, '')).toBe(
377+
strip(before).replace(/[]/u, ''),
378+
);
377379
expect(after).not.toBe(before);
380+
expect(strip(after)).toMatch(/[] Orchestrating/);
378381
expect(strip(after)).toContain('Orchestrating');
379382
expect(vi.getTimerCount()).toBe(timerCount);
380383
} finally {
@@ -671,8 +674,8 @@ describe('DynamicWorkflowMissionControlComponent', () => {
671674
vi.setSystemTime(time);
672675
const line = component.render(100).find((candidate) => strip(candidate).includes('001'));
673676
expect(line).toContain(chalk.hex(darkColors.primary)(glyph));
674-
expect(aggregateLine(component.render(100).join('\n'))).toContain(
675-
chalk.hex(darkColors.primary)(glyph),
677+
expect(strip(aggregateLine(component.render(100).join('\n')))).toMatch(
678+
/[] Orchestrating/,
676679
);
677680
}
678681

apps/pythinker-code/test/tui/components/status-bar.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,22 @@ describe('StatusBarComponent', () => {
105105
expect(line).toContain('workflow');
106106
expect(line).toContain('~/project');
107107
});
108+
109+
it.each([
110+
[
111+
'/Users/test/Projects/active/pythinker-code-tsc/apps/pythinker-code',
112+
'/Users/test',
113+
'…/apps/pythinker-code',
114+
],
115+
['/Users/test/Projects/active', '/Users/test', '~/Projects/active'],
116+
['/Users/test', '/Users/test', '~'],
117+
['/a/b/c/d', '/Users/test', '…/c/d'],
118+
])('shortens cwd %s to %s', (cwd, homeDir, expected) => {
119+
const component = new StatusBarComponent();
120+
component.update(status({ cwd, homeDir }));
121+
122+
expect(stripAnsi(component.render(240)[0] ?? '')).toContain(expected);
123+
});
108124
});
109125

110126
describe('shimmerText', () => {

apps/pythinker-code/test/tui/runtime/footer-model.test.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ describe('footer model', () => {
302302
});
303303
});
304304

305-
it('projects status-bar extras without the model and modes items', () => {
305+
it('projects status-bar extras in priority order without the model and modes items', () => {
306306
const state = foldFooterEvents(
307307
createFooterState({
308308
model: 'DeepSeek V4 Flash',
@@ -317,13 +317,8 @@ describe('footer model', () => {
317317
},
318318
],
319319
);
320-
const status = selectFooterViewModel(state, CLOCK_MS).rows.at(-1);
321-
if (status?.kind !== 'status') throw new Error('Expected a status row');
322-
323320
expect(selectStatusBarExtras(state, CLOCK_MS, DEFAULT_STATUS_LINE_CONFIG)).toEqual(
324-
status.items.filter(
325-
(item) => item !== 'DeepSeek V4 Flash' && item !== 'workflow',
326-
),
321+
['▱▱▱▱▱▱▱▱ 5%', 'main ↑15', '↑ v0.11.0'],
327322
);
328323
});
329324

0 commit comments

Comments
 (0)