feat(cli): unify loading phrase settings into single layout control

- Replace ui.loadingPhrases and ui.wittyPhrasePosition with ui.loadingPhraseLayout
- Supported layouts: none, tips, wit_status, wit_inline, wit_ambient, all_inline, all_ambient
- Set 'all_inline' as the new default (tips on right, wit inline)
- Update migration logic to map deprecated enableLoadingPhrases to 'none'
- Synchronize all unit tests and documentation with the new configuration model
This commit is contained in:
Keith Guerin
2026-02-28 23:37:40 -08:00
parent 3bd36ce4f0
commit e1e863dba2
12 changed files with 126 additions and 98 deletions

View File

@@ -402,13 +402,13 @@ describe('Composer', () => {
expect(output).not.toContain('ShortcutsHint');
});
it('renders LoadingIndicator with thought when loadingPhrases is off', async () => {
it('renders LoadingIndicator with thought when loadingPhraseLayout is none', async () => {
const uiState = createMockUIState({
streamingState: StreamingState.Responding,
thought: { subject: 'Hidden', description: 'Should not show' },
});
const settings = createMockSettings({
merged: { ui: { loadingPhrases: 'off' } },
merged: { ui: { loadingPhraseLayout: 'none' } },
});
const { lastFrame } = await renderComposer(uiState, settings);

View File

@@ -59,7 +59,14 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
const isAlternateBuffer = useAlternateBuffer();
const { showApprovalModeIndicator } = uiState;
const newLayoutSetting = settings.merged.ui.newFooterLayout;
const wittyPosition = settings.merged.ui.wittyPhrasePosition;
const { loadingPhraseLayout } = settings.merged.ui;
const wittyPosition: 'status' | 'inline' | 'ambient' =
loadingPhraseLayout === 'wit_status'
? 'status'
: loadingPhraseLayout === 'wit_inline' ||
loadingPhraseLayout === 'all_inline'
? 'inline'
: 'ambient';
const isExperimentalLayout = newLayoutSetting !== 'legacy';
const showUiDetails = uiState.cleanUiDetailsVisible;
const suggestionsPosition = isAlternateBuffer ? 'above' : 'below';
@@ -198,8 +205,15 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
const ambientText = isInteractiveShellWaiting
? undefined
: uiState.currentTip ||
(wittyPosition === 'ambient' ? uiState.currentWittyPhrase : undefined);
: (loadingPhraseLayout === 'tips' ||
loadingPhraseLayout === 'all_inline' ||
loadingPhraseLayout === 'all_ambient'
? uiState.currentTip
: undefined) ||
(loadingPhraseLayout === 'wit_ambient' ||
loadingPhraseLayout === 'all_ambient'
? uiState.currentWittyPhrase
: undefined);
let estimatedStatusLength = 0;
if (
@@ -239,6 +253,7 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
uiState.streamingState !== StreamingState.Idle &&
!hasPendingActionRequired &&
ambientText &&
loadingPhraseLayout !== 'none' &&
!willCollideAmbient &&
!isNarrow;
@@ -299,10 +314,9 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
: uiState.thought
}
currentLoadingPhrase={
uiState.currentLoadingPhrase?.includes('press tab to focus shell')
uiState.currentLoadingPhrase?.includes('Tab to focus')
? uiState.currentLoadingPhrase
: !isExperimentalLayout &&
settings.merged.ui.loadingPhrases !== 'off'
: !isExperimentalLayout && loadingPhraseLayout !== 'none'
? uiState.currentLoadingPhrase
: isExperimentalLayout &&
uiState.streamingState === StreamingState.Responding &&
@@ -376,7 +390,7 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
: uiState.thought
}
currentLoadingPhrase={
settings.merged.ui.loadingPhrases === 'off'
loadingPhraseLayout === 'none'
? undefined
: uiState.currentLoadingPhrase
}
@@ -419,7 +433,7 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
: uiState.thought
}
currentLoadingPhrase={
settings.merged.ui.loadingPhrases === 'off'
loadingPhraseLayout === 'none'
? undefined
: uiState.currentLoadingPhrase
}