mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-12 12:54:07 -07:00
Code review comments as a pr (#21209)
This commit is contained in:
@@ -132,9 +132,7 @@ describe('<Footer />', () => {
|
|||||||
const output = lastFrame();
|
const output = lastFrame();
|
||||||
expect(output).toBeDefined();
|
expect(output).toBeDefined();
|
||||||
// Should contain some part of the path, likely shortened
|
// Should contain some part of the path, likely shortened
|
||||||
expect(output).toContain(
|
expect(output).toContain(path.join('make', 'it'));
|
||||||
path.join('directories', 'to', 'make', 'it', 'long'),
|
|
||||||
);
|
|
||||||
unmount();
|
unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -149,9 +147,38 @@ describe('<Footer />', () => {
|
|||||||
await waitUntilReady();
|
await waitUntilReady();
|
||||||
const output = lastFrame();
|
const output = lastFrame();
|
||||||
expect(output).toBeDefined();
|
expect(output).toBeDefined();
|
||||||
expect(output).toContain(
|
expect(output).toContain(path.join('make', 'it'));
|
||||||
path.join('directories', 'to', 'make', 'it', 'long'),
|
unmount();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not truncate high-priority items on narrow terminals (regression)', async () => {
|
||||||
|
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
|
||||||
|
<Footer />,
|
||||||
|
{
|
||||||
|
width: 60,
|
||||||
|
uiState: {
|
||||||
|
sessionStats: mockSessionStats,
|
||||||
|
},
|
||||||
|
settings: createMockSettings({
|
||||||
|
general: {
|
||||||
|
vimMode: true,
|
||||||
|
},
|
||||||
|
ui: {
|
||||||
|
footer: {
|
||||||
|
showLabels: true,
|
||||||
|
items: ['workspace', 'model-name'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
await waitUntilReady();
|
||||||
|
const output = lastFrame();
|
||||||
|
// [INSERT] is high priority and should be fully visible
|
||||||
|
// (Note: VimModeProvider defaults to 'INSERT' mode when enabled)
|
||||||
|
expect(output).toContain('[INSERT]');
|
||||||
|
// Other items should be present but might be shortened
|
||||||
|
expect(output).toContain('gemini-pro');
|
||||||
unmount();
|
unmount();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -103,6 +103,9 @@ export interface FooterRowItem {
|
|||||||
key: string;
|
key: string;
|
||||||
header: string;
|
header: string;
|
||||||
element: React.ReactNode;
|
element: React.ReactNode;
|
||||||
|
flexGrow?: number;
|
||||||
|
flexShrink?: number;
|
||||||
|
isFocused?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const COLUMN_GAP = 3;
|
const COLUMN_GAP = 3;
|
||||||
@@ -123,10 +126,20 @@ export const FooterRow: React.FC<{
|
|||||||
}
|
}
|
||||||
|
|
||||||
elements.push(
|
elements.push(
|
||||||
<Box key={item.key} flexDirection="column">
|
<Box
|
||||||
|
key={item.key}
|
||||||
|
flexDirection="column"
|
||||||
|
flexGrow={item.flexGrow ?? 0}
|
||||||
|
flexShrink={item.flexShrink ?? 1}
|
||||||
|
backgroundColor={item.isFocused ? theme.background.focus : undefined}
|
||||||
|
>
|
||||||
{showLabels && (
|
{showLabels && (
|
||||||
<Box height={1}>
|
<Box height={1}>
|
||||||
<Text color={theme.ui.comment}>{item.header}</Text>
|
<Text
|
||||||
|
color={item.isFocused ? theme.text.primary : theme.ui.comment}
|
||||||
|
>
|
||||||
|
{item.header}
|
||||||
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<Box height={1}>{item.element}</Box>
|
<Box height={1}>{item.element}</Box>
|
||||||
@@ -138,6 +151,7 @@ export const FooterRow: React.FC<{
|
|||||||
<Box
|
<Box
|
||||||
flexDirection="row"
|
flexDirection="row"
|
||||||
flexWrap="nowrap"
|
flexWrap="nowrap"
|
||||||
|
width="100%"
|
||||||
columnGap={showLabels ? COLUMN_GAP : 0}
|
columnGap={showLabels ? COLUMN_GAP : 0}
|
||||||
>
|
>
|
||||||
{elements}
|
{elements}
|
||||||
@@ -408,41 +422,50 @@ export const Footer: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- Width Fitting Logic ---
|
// --- Width Fitting Logic ---
|
||||||
let currentWidth = 2; // Initial padding
|
|
||||||
const columnsToRender: FooterColumn[] = [];
|
const columnsToRender: FooterColumn[] = [];
|
||||||
let droppedAny = false;
|
let droppedAny = false;
|
||||||
|
let currentUsedWidth = 2; // Initial padding
|
||||||
|
|
||||||
for (let i = 0; i < potentialColumns.length; i++) {
|
for (const col of potentialColumns) {
|
||||||
const col = potentialColumns[i];
|
const gap = columnsToRender.length > 0 ? (showLabels ? COLUMN_GAP : 3) : 0;
|
||||||
const gap = columnsToRender.length > 0 ? (showLabels ? COLUMN_GAP : 3) : 0; // Use 3 for dot separator width
|
|
||||||
const budgetWidth = col.id === 'workspace' ? 20 : col.width;
|
const budgetWidth = col.id === 'workspace' ? 20 : col.width;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
col.isHighPriority ||
|
col.isHighPriority ||
|
||||||
currentWidth + gap + budgetWidth <= terminalWidth - 2
|
currentUsedWidth + gap + budgetWidth <= terminalWidth - 2
|
||||||
) {
|
) {
|
||||||
columnsToRender.push(col);
|
columnsToRender.push(col);
|
||||||
currentWidth += gap + budgetWidth;
|
currentUsedWidth += gap + budgetWidth;
|
||||||
} else {
|
} else {
|
||||||
droppedAny = true;
|
droppedAny = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const totalBudgeted = columnsToRender.reduce(
|
|
||||||
(sum, c, idx) =>
|
|
||||||
sum +
|
|
||||||
(c.id === 'workspace' ? 20 : c.width) +
|
|
||||||
(idx > 0 ? (showLabels ? COLUMN_GAP : 3) : 0),
|
|
||||||
2,
|
|
||||||
);
|
|
||||||
const excessSpace = Math.max(0, terminalWidth - totalBudgeted);
|
|
||||||
|
|
||||||
const rowItems: FooterRowItem[] = columnsToRender.map((col) => {
|
const rowItems: FooterRowItem[] = columnsToRender.map((col) => {
|
||||||
const maxWidth = col.id === 'workspace' ? 20 + excessSpace : col.width;
|
const isWorkspace = col.id === 'workspace';
|
||||||
|
|
||||||
|
// Calculate exact space available for growth to prevent over-estimation truncation
|
||||||
|
const otherItemsWidth = columnsToRender
|
||||||
|
.filter((c) => c.id !== 'workspace')
|
||||||
|
.reduce((sum, c) => sum + c.width, 0);
|
||||||
|
const numItems = columnsToRender.length + (droppedAny ? 1 : 0);
|
||||||
|
const numGaps = numItems > 1 ? numItems - 1 : 0;
|
||||||
|
const gapsWidth = numGaps * (showLabels ? COLUMN_GAP : 3);
|
||||||
|
const ellipsisWidth = droppedAny ? 1 : 0;
|
||||||
|
|
||||||
|
const availableForWorkspace = Math.max(
|
||||||
|
20,
|
||||||
|
terminalWidth - 2 - gapsWidth - otherItemsWidth - ellipsisWidth,
|
||||||
|
);
|
||||||
|
|
||||||
|
const estimatedWidth = isWorkspace ? availableForWorkspace : col.width;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
key: col.id,
|
key: col.id,
|
||||||
header: col.header,
|
header: col.header,
|
||||||
element: col.element(maxWidth),
|
element: col.element(estimatedWidth),
|
||||||
|
flexGrow: isWorkspace ? 1 : 0,
|
||||||
|
flexShrink: isWorkspace ? 1 : 0,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -451,6 +474,8 @@ export const Footer: React.FC = () => {
|
|||||||
key: 'ellipsis',
|
key: 'ellipsis',
|
||||||
header: '',
|
header: '',
|
||||||
element: <Text color={theme.ui.comment}>…</Text>,
|
element: <Text color={theme.ui.comment}>…</Text>,
|
||||||
|
flexGrow: 0,
|
||||||
|
flexShrink: 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,13 +24,14 @@ describe('<FooterConfigDialog />', () => {
|
|||||||
|
|
||||||
it('renders correctly with default settings', async () => {
|
it('renders correctly with default settings', async () => {
|
||||||
const settings = createMockSettings();
|
const settings = createMockSettings();
|
||||||
const { lastFrame, waitUntilReady } = renderWithProviders(
|
const renderResult = renderWithProviders(
|
||||||
<FooterConfigDialog onClose={mockOnClose} />,
|
<FooterConfigDialog onClose={mockOnClose} />,
|
||||||
{ settings },
|
{ settings },
|
||||||
);
|
);
|
||||||
|
|
||||||
await waitUntilReady();
|
await renderResult.waitUntilReady();
|
||||||
expect(lastFrame()).toMatchSnapshot();
|
expect(renderResult.lastFrame()).toMatchSnapshot();
|
||||||
|
await expect(renderResult).toMatchSvgSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('toggles an item when enter is pressed', async () => {
|
it('toggles an item when enter is pressed', async () => {
|
||||||
@@ -66,7 +67,7 @@ describe('<FooterConfigDialog />', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await waitUntilReady();
|
await waitUntilReady();
|
||||||
// Initial order: workspace, branch, ...
|
// Initial order: workspace, git-branch, ...
|
||||||
const output = lastFrame();
|
const output = lastFrame();
|
||||||
const cwdIdx = output.indexOf('] workspace');
|
const cwdIdx = output.indexOf('] workspace');
|
||||||
const branchIdx = output.indexOf('] git-branch');
|
const branchIdx = output.indexOf('] git-branch');
|
||||||
@@ -108,22 +109,40 @@ describe('<FooterConfigDialog />', () => {
|
|||||||
|
|
||||||
it('highlights the active item in the preview', async () => {
|
it('highlights the active item in the preview', async () => {
|
||||||
const settings = createMockSettings();
|
const settings = createMockSettings();
|
||||||
const { lastFrame, stdin, waitUntilReady } = renderWithProviders(
|
const renderResult = renderWithProviders(
|
||||||
<FooterConfigDialog onClose={mockOnClose} />,
|
<FooterConfigDialog onClose={mockOnClose} />,
|
||||||
{ settings },
|
{ settings },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { lastFrame, stdin, waitUntilReady } = renderResult;
|
||||||
|
|
||||||
await waitUntilReady();
|
await waitUntilReady();
|
||||||
expect(lastFrame()).toContain('~/project/path');
|
expect(lastFrame()).toContain('~/project/path');
|
||||||
|
|
||||||
// Move focus down to 'git-branch'
|
// Move focus down to 'code-changes' (which has colored elements)
|
||||||
|
for (let i = 0; i < 8; i++) {
|
||||||
|
act(() => {
|
||||||
|
stdin.write('\u001b[B'); // Down arrow
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
// The selected indicator should be next to 'code-changes'
|
||||||
|
expect(lastFrame()).toMatch(/> \[ \] code-changes/);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Toggle it on
|
||||||
act(() => {
|
act(() => {
|
||||||
stdin.write('\u001b[B'); // Down arrow
|
stdin.write('\r');
|
||||||
});
|
});
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(lastFrame()).toContain('main');
|
// It should now be checked and appear in the preview
|
||||||
|
expect(lastFrame()).toMatch(/> \[✓\] code-changes/);
|
||||||
|
expect(lastFrame()).toContain('+12 -4');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await expect(renderResult).toMatchSvgSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows an empty preview when all items are deselected', async () => {
|
it('shows an empty preview when all items are deselected', async () => {
|
||||||
@@ -134,20 +153,64 @@ describe('<FooterConfigDialog />', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await waitUntilReady();
|
await waitUntilReady();
|
||||||
for (let i = 0; i < 10; i++) {
|
|
||||||
|
// Default items are the first 5. We toggle them off.
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
act(() => {
|
||||||
|
stdin.write('\r'); // Toggle off
|
||||||
|
});
|
||||||
act(() => {
|
act(() => {
|
||||||
stdin.write('\r'); // Toggle (deselect)
|
|
||||||
stdin.write('\u001b[B'); // Down arrow
|
stdin.write('\u001b[B'); // Down arrow
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await waitFor(
|
||||||
|
() => {
|
||||||
|
const output = lastFrame();
|
||||||
|
expect(output).toContain('Preview:');
|
||||||
|
expect(output).not.toContain('~/project/path');
|
||||||
|
expect(output).not.toContain('docker');
|
||||||
|
},
|
||||||
|
{ timeout: 2000 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('moves item correctly after trying to move up at the top', async () => {
|
||||||
|
const settings = createMockSettings();
|
||||||
|
const { lastFrame, stdin, waitUntilReady } = renderWithProviders(
|
||||||
|
<FooterConfigDialog onClose={mockOnClose} />,
|
||||||
|
{ settings },
|
||||||
|
);
|
||||||
|
await waitUntilReady();
|
||||||
|
|
||||||
|
// Default initial items in mock settings are 'git-branch', 'workspace', ...
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
const output = lastFrame();
|
const output = lastFrame();
|
||||||
expect(output).toContain('Preview:');
|
expect(output).toContain('] git-branch');
|
||||||
expect(output).not.toContain('~/project/path');
|
expect(output).toContain('] workspace');
|
||||||
expect(output).not.toContain('docker');
|
});
|
||||||
expect(output).not.toContain('gemini-2.5-pro');
|
|
||||||
expect(output).not.toContain('1.2k left');
|
const output = lastFrame();
|
||||||
|
const branchIdx = output.indexOf('] git-branch');
|
||||||
|
const workspaceIdx = output.indexOf('] workspace');
|
||||||
|
expect(workspaceIdx).toBeLessThan(branchIdx);
|
||||||
|
|
||||||
|
// Try to move workspace up (left arrow) while it's at the top
|
||||||
|
act(() => {
|
||||||
|
stdin.write('\u001b[D'); // Left arrow
|
||||||
|
});
|
||||||
|
|
||||||
|
// Move workspace down (right arrow)
|
||||||
|
act(() => {
|
||||||
|
stdin.write('\u001b[C'); // Right arrow
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
const outputAfter = lastFrame();
|
||||||
|
const bIdxAfter = outputAfter.indexOf('] git-branch');
|
||||||
|
const wIdxAfter = outputAfter.indexOf('] workspace');
|
||||||
|
// workspace should now be after git-branch
|
||||||
|
expect(bIdxAfter).toBeLessThan(wIdxAfter);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,119 +5,75 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import { useCallback, useMemo, useReducer } from 'react';
|
import { useCallback, useMemo, useReducer, useState } from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { theme } from '../semantic-colors.js';
|
||||||
import { useSettingsStore } from '../contexts/SettingsContext.js';
|
import { useSettingsStore } from '../contexts/SettingsContext.js';
|
||||||
|
import { useUIState } from '../contexts/UIStateContext.js';
|
||||||
import { useKeypress, type Key } from '../hooks/useKeypress.js';
|
import { useKeypress, type Key } from '../hooks/useKeypress.js';
|
||||||
import { keyMatchers, Command } from '../keyMatchers.js';
|
import { keyMatchers, Command } from '../keyMatchers.js';
|
||||||
import { FooterRow, type FooterRowItem } from './Footer.js';
|
import { FooterRow, type FooterRowItem } from './Footer.js';
|
||||||
import { ALL_ITEMS, resolveFooterState } from '../../config/footerItems.js';
|
import { ALL_ITEMS, resolveFooterState } from '../../config/footerItems.js';
|
||||||
import { SettingScope } from '../../config/settings.js';
|
import { SettingScope } from '../../config/settings.js';
|
||||||
|
import { BaseSelectionList } from './shared/BaseSelectionList.js';
|
||||||
|
import type { SelectionListItem } from '../hooks/useSelectionList.js';
|
||||||
|
import { DialogFooter } from './shared/DialogFooter.js';
|
||||||
|
|
||||||
interface FooterConfigDialogProps {
|
interface FooterConfigDialogProps {
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface FooterConfigItem {
|
||||||
|
key: string;
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
description?: string;
|
||||||
|
type: 'config' | 'labels-toggle' | 'reset';
|
||||||
|
}
|
||||||
|
|
||||||
interface FooterConfigState {
|
interface FooterConfigState {
|
||||||
orderedIds: string[];
|
orderedIds: string[];
|
||||||
selectedIds: Set<string>;
|
selectedIds: Set<string>;
|
||||||
activeIndex: number;
|
|
||||||
scrollOffset: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type FooterConfigAction =
|
type FooterConfigAction =
|
||||||
| { type: 'MOVE_UP'; itemCount: number; maxToShow: number }
|
| { type: 'MOVE_ITEM'; id: string; direction: number }
|
||||||
| { type: 'MOVE_DOWN'; itemCount: number; maxToShow: number }
|
| { type: 'TOGGLE_ITEM'; id: string }
|
||||||
| {
|
| { type: 'SET_STATE'; payload: Partial<FooterConfigState> };
|
||||||
type: 'MOVE_LEFT';
|
|
||||||
items: Array<{ key: string }>;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'MOVE_RIGHT';
|
|
||||||
items: Array<{ key: string }>;
|
|
||||||
}
|
|
||||||
| { type: 'TOGGLE_ITEM'; items: Array<{ key: string }> }
|
|
||||||
| { type: 'SET_STATE'; payload: Partial<FooterConfigState> }
|
|
||||||
| { type: 'RESET_INDEX' };
|
|
||||||
|
|
||||||
function footerConfigReducer(
|
function footerConfigReducer(
|
||||||
state: FooterConfigState,
|
state: FooterConfigState,
|
||||||
action: FooterConfigAction,
|
action: FooterConfigAction,
|
||||||
): FooterConfigState {
|
): FooterConfigState {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'MOVE_UP': {
|
case 'MOVE_ITEM': {
|
||||||
const { itemCount, maxToShow } = action;
|
const currentIndex = state.orderedIds.indexOf(action.id);
|
||||||
const totalSlots = itemCount + 2; // +1 for showLabels, +1 for reset
|
const newIndex = currentIndex + action.direction;
|
||||||
const newIndex =
|
if (
|
||||||
state.activeIndex > 0 ? state.activeIndex - 1 : totalSlots - 1;
|
currentIndex === -1 ||
|
||||||
let newOffset = state.scrollOffset;
|
newIndex < 0 ||
|
||||||
|
newIndex >= state.orderedIds.length
|
||||||
if (newIndex < itemCount) {
|
|
||||||
if (newIndex === itemCount - 1) {
|
|
||||||
newOffset = Math.max(0, itemCount - maxToShow);
|
|
||||||
} else if (newIndex < state.scrollOffset) {
|
|
||||||
newOffset = newIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { ...state, activeIndex: newIndex, scrollOffset: newOffset };
|
|
||||||
}
|
|
||||||
case 'MOVE_DOWN': {
|
|
||||||
const { itemCount, maxToShow } = action;
|
|
||||||
const totalSlots = itemCount + 2;
|
|
||||||
const newIndex =
|
|
||||||
state.activeIndex < totalSlots - 1 ? state.activeIndex + 1 : 0;
|
|
||||||
let newOffset = state.scrollOffset;
|
|
||||||
|
|
||||||
if (newIndex === 0) {
|
|
||||||
newOffset = 0;
|
|
||||||
} else if (
|
|
||||||
newIndex < itemCount &&
|
|
||||||
newIndex >= state.scrollOffset + maxToShow
|
|
||||||
) {
|
) {
|
||||||
newOffset = newIndex - maxToShow + 1;
|
return state;
|
||||||
}
|
}
|
||||||
return { ...state, activeIndex: newIndex, scrollOffset: newOffset };
|
|
||||||
}
|
|
||||||
case 'MOVE_LEFT':
|
|
||||||
case 'MOVE_RIGHT': {
|
|
||||||
const direction = action.type === 'MOVE_LEFT' ? -1 : 1;
|
|
||||||
const currentItem = action.items[state.activeIndex];
|
|
||||||
if (!currentItem) return state;
|
|
||||||
|
|
||||||
const currentId = currentItem.key;
|
|
||||||
const currentIndex = state.orderedIds.indexOf(currentId);
|
|
||||||
const newIndex = currentIndex + direction;
|
|
||||||
|
|
||||||
if (newIndex < 0 || newIndex >= state.orderedIds.length) return state;
|
|
||||||
|
|
||||||
const newOrderedIds = [...state.orderedIds];
|
const newOrderedIds = [...state.orderedIds];
|
||||||
[newOrderedIds[currentIndex], newOrderedIds[newIndex]] = [
|
[newOrderedIds[currentIndex], newOrderedIds[newIndex]] = [
|
||||||
newOrderedIds[newIndex],
|
newOrderedIds[newIndex],
|
||||||
newOrderedIds[currentIndex],
|
newOrderedIds[currentIndex],
|
||||||
];
|
];
|
||||||
|
return { ...state, orderedIds: newOrderedIds };
|
||||||
return { ...state, orderedIds: newOrderedIds, activeIndex: newIndex };
|
|
||||||
}
|
}
|
||||||
case 'TOGGLE_ITEM': {
|
case 'TOGGLE_ITEM': {
|
||||||
const isSystemFocused = state.activeIndex >= action.items.length;
|
|
||||||
if (isSystemFocused) return state;
|
|
||||||
|
|
||||||
const item = action.items[state.activeIndex];
|
|
||||||
if (!item) return state;
|
|
||||||
|
|
||||||
const nextSelected = new Set(state.selectedIds);
|
const nextSelected = new Set(state.selectedIds);
|
||||||
if (nextSelected.has(item.key)) {
|
if (nextSelected.has(action.id)) {
|
||||||
nextSelected.delete(item.key);
|
nextSelected.delete(action.id);
|
||||||
} else {
|
} else {
|
||||||
nextSelected.add(item.key);
|
nextSelected.add(action.id);
|
||||||
}
|
}
|
||||||
return { ...state, selectedIds: nextSelected };
|
return { ...state, selectedIds: nextSelected };
|
||||||
}
|
}
|
||||||
case 'SET_STATE':
|
case 'SET_STATE':
|
||||||
return { ...state, ...action.payload };
|
return { ...state, ...action.payload };
|
||||||
case 'RESET_INDEX':
|
|
||||||
return { ...state, activeIndex: 0, scrollOffset: 0 };
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
@@ -127,40 +83,54 @@ export const FooterConfigDialog: React.FC<FooterConfigDialogProps> = ({
|
|||||||
onClose,
|
onClose,
|
||||||
}) => {
|
}) => {
|
||||||
const { settings, setSetting } = useSettingsStore();
|
const { settings, setSetting } = useSettingsStore();
|
||||||
const maxItemsToShow = 10;
|
const { constrainHeight, terminalHeight, staticExtraHeight } = useUIState();
|
||||||
|
const [state, dispatch] = useReducer(footerConfigReducer, undefined, () =>
|
||||||
|
resolveFooterState(settings.merged),
|
||||||
|
);
|
||||||
|
|
||||||
const [state, dispatch] = useReducer(footerConfigReducer, undefined, () => ({
|
const { orderedIds, selectedIds } = state;
|
||||||
...resolveFooterState(settings.merged),
|
const [focusKey, setFocusKey] = useState<string | undefined>(orderedIds[0]);
|
||||||
activeIndex: 0,
|
|
||||||
scrollOffset: 0,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const { orderedIds, selectedIds, activeIndex, scrollOffset } = state;
|
const listItems = useMemo((): Array<SelectionListItem<FooterConfigItem>> => {
|
||||||
|
const items: Array<SelectionListItem<FooterConfigItem>> = orderedIds
|
||||||
// Prepare items
|
.map((id: string) => {
|
||||||
const listItems = useMemo(
|
const item = ALL_ITEMS.find((i) => i.id === id);
|
||||||
() =>
|
if (!item) return null;
|
||||||
orderedIds
|
return {
|
||||||
.map((id: string) => {
|
key: id,
|
||||||
const item = ALL_ITEMS.find((i) => i.id === id);
|
value: {
|
||||||
if (!item) return null;
|
|
||||||
return {
|
|
||||||
key: id,
|
key: id,
|
||||||
|
id,
|
||||||
label: item.id,
|
label: item.id,
|
||||||
description: item.description as string,
|
description: item.description as string,
|
||||||
};
|
type: 'config' as const,
|
||||||
})
|
},
|
||||||
.filter((i): i is NonNullable<typeof i> => i !== null),
|
};
|
||||||
[orderedIds],
|
})
|
||||||
);
|
.filter((i): i is NonNullable<typeof i> => i !== null);
|
||||||
|
|
||||||
const maxLabelWidth = useMemo(
|
items.push({
|
||||||
() => listItems.reduce((max, item) => Math.max(max, item.label.length), 0),
|
key: 'show-labels',
|
||||||
[listItems],
|
value: {
|
||||||
);
|
key: 'show-labels',
|
||||||
|
id: 'show-labels',
|
||||||
|
label: 'Show footer labels',
|
||||||
|
type: 'labels-toggle',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const isResetFocused = activeIndex === listItems.length + 1;
|
items.push({
|
||||||
const isShowLabelsFocused = activeIndex === listItems.length;
|
key: 'reset',
|
||||||
|
value: {
|
||||||
|
key: 'reset',
|
||||||
|
id: 'reset',
|
||||||
|
label: 'Reset to default footer',
|
||||||
|
type: 'reset',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}, [orderedIds]);
|
||||||
|
|
||||||
const handleSaveAndClose = useCallback(() => {
|
const handleSaveAndClose = useCallback(() => {
|
||||||
const finalItems = orderedIds.filter((id: string) => selectedIds.has(id));
|
const finalItems = orderedIds.filter((id: string) => selectedIds.has(id));
|
||||||
@@ -179,14 +149,9 @@ export const FooterConfigDialog: React.FC<FooterConfigDialogProps> = ({
|
|||||||
|
|
||||||
const handleResetToDefaults = useCallback(() => {
|
const handleResetToDefaults = useCallback(() => {
|
||||||
setSetting(SettingScope.User, 'ui.footer.items', undefined);
|
setSetting(SettingScope.User, 'ui.footer.items', undefined);
|
||||||
dispatch({
|
const newState = resolveFooterState(settings.merged);
|
||||||
type: 'SET_STATE',
|
dispatch({ type: 'SET_STATE', payload: newState });
|
||||||
payload: {
|
setFocusKey(newState.orderedIds[0]);
|
||||||
...resolveFooterState(settings.merged),
|
|
||||||
activeIndex: 0,
|
|
||||||
scrollOffset: 0,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}, [setSetting, settings.merged]);
|
}, [setSetting, settings.merged]);
|
||||||
|
|
||||||
const handleToggleLabels = useCallback(() => {
|
const handleToggleLabels = useCallback(() => {
|
||||||
@@ -194,6 +159,23 @@ export const FooterConfigDialog: React.FC<FooterConfigDialogProps> = ({
|
|||||||
setSetting(SettingScope.User, 'ui.footer.showLabels', !current);
|
setSetting(SettingScope.User, 'ui.footer.showLabels', !current);
|
||||||
}, [setSetting, settings.merged.ui.footer.showLabels]);
|
}, [setSetting, settings.merged.ui.footer.showLabels]);
|
||||||
|
|
||||||
|
const handleSelect = useCallback(
|
||||||
|
(item: FooterConfigItem) => {
|
||||||
|
if (item.type === 'config') {
|
||||||
|
dispatch({ type: 'TOGGLE_ITEM', id: item.id });
|
||||||
|
} else if (item.type === 'labels-toggle') {
|
||||||
|
handleToggleLabels();
|
||||||
|
} else if (item.type === 'reset') {
|
||||||
|
handleResetToDefaults();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[handleResetToDefaults, handleToggleLabels],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleHighlight = useCallback((item: FooterConfigItem) => {
|
||||||
|
setFocusKey(item.key);
|
||||||
|
}, []);
|
||||||
|
|
||||||
useKeypress(
|
useKeypress(
|
||||||
(key: Key) => {
|
(key: Key) => {
|
||||||
if (keyMatchers[Command.ESCAPE](key)) {
|
if (keyMatchers[Command.ESCAPE](key)) {
|
||||||
@@ -201,43 +183,18 @@ export const FooterConfigDialog: React.FC<FooterConfigDialogProps> = ({
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keyMatchers[Command.DIALOG_NAVIGATION_UP](key)) {
|
|
||||||
dispatch({
|
|
||||||
type: 'MOVE_UP',
|
|
||||||
itemCount: listItems.length,
|
|
||||||
maxToShow: maxItemsToShow,
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyMatchers[Command.DIALOG_NAVIGATION_DOWN](key)) {
|
|
||||||
dispatch({
|
|
||||||
type: 'MOVE_DOWN',
|
|
||||||
itemCount: listItems.length,
|
|
||||||
maxToShow: maxItemsToShow,
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyMatchers[Command.MOVE_LEFT](key)) {
|
if (keyMatchers[Command.MOVE_LEFT](key)) {
|
||||||
dispatch({ type: 'MOVE_LEFT', items: listItems });
|
if (focusKey && orderedIds.includes(focusKey)) {
|
||||||
return true;
|
dispatch({ type: 'MOVE_ITEM', id: focusKey, direction: -1 });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keyMatchers[Command.MOVE_RIGHT](key)) {
|
if (keyMatchers[Command.MOVE_RIGHT](key)) {
|
||||||
dispatch({ type: 'MOVE_RIGHT', items: listItems });
|
if (focusKey && orderedIds.includes(focusKey)) {
|
||||||
return true;
|
dispatch({ type: 'MOVE_ITEM', id: focusKey, direction: 1 });
|
||||||
}
|
return true;
|
||||||
|
|
||||||
if (keyMatchers[Command.RETURN](key) || key.name === 'space') {
|
|
||||||
if (isResetFocused) {
|
|
||||||
handleResetToDefaults();
|
|
||||||
} else if (isShowLabelsFocused) {
|
|
||||||
handleToggleLabels();
|
|
||||||
} else {
|
|
||||||
dispatch({ type: 'TOGGLE_ITEM', items: listItems });
|
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -245,17 +202,11 @@ export const FooterConfigDialog: React.FC<FooterConfigDialogProps> = ({
|
|||||||
{ isActive: true, priority: true },
|
{ isActive: true, priority: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
const visibleItems = listItems.slice(
|
|
||||||
scrollOffset,
|
|
||||||
scrollOffset + maxItemsToShow,
|
|
||||||
);
|
|
||||||
|
|
||||||
const activeId = listItems[activeIndex]?.key;
|
|
||||||
const showLabels = settings.merged.ui.footer.showLabels !== false;
|
const showLabels = settings.merged.ui.footer.showLabels !== false;
|
||||||
|
|
||||||
// Preview logic
|
// Preview logic
|
||||||
const previewContent = useMemo(() => {
|
const previewContent = useMemo(() => {
|
||||||
if (isResetFocused) {
|
if (focusKey === 'reset') {
|
||||||
return (
|
return (
|
||||||
<Text color={theme.ui.comment} italic>
|
<Text color={theme.ui.comment} italic>
|
||||||
Default footer (uses legacy settings)
|
Default footer (uses legacy settings)
|
||||||
@@ -269,8 +220,9 @@ export const FooterConfigDialog: React.FC<FooterConfigDialogProps> = ({
|
|||||||
if (itemsToPreview.length === 0) return null;
|
if (itemsToPreview.length === 0) return null;
|
||||||
|
|
||||||
const itemColor = showLabels ? theme.text.primary : theme.ui.comment;
|
const itemColor = showLabels ? theme.text.primary : theme.ui.comment;
|
||||||
|
|
||||||
const getColor = (id: string, defaultColor?: string) =>
|
const getColor = (id: string, defaultColor?: string) =>
|
||||||
id === activeId ? 'white' : defaultColor || itemColor;
|
defaultColor || itemColor;
|
||||||
|
|
||||||
// Mock data for preview (headers come from ALL_ITEMS)
|
// Mock data for preview (headers come from ALL_ITEMS)
|
||||||
const mockData: Record<string, React.ReactNode> = {
|
const mockData: Record<string, React.ReactNode> = {
|
||||||
@@ -312,16 +264,43 @@ export const FooterConfigDialog: React.FC<FooterConfigDialogProps> = ({
|
|||||||
key: id,
|
key: id,
|
||||||
header: ALL_ITEMS.find((i) => i.id === id)?.header ?? id,
|
header: ALL_ITEMS.find((i) => i.id === id)?.header ?? id,
|
||||||
element: mockData[id],
|
element: mockData[id],
|
||||||
|
flexGrow: 1,
|
||||||
|
isFocused: id === focusKey,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box overflow="hidden" flexWrap="nowrap">
|
<Box overflow="hidden" flexWrap="nowrap" width="100%">
|
||||||
<Box flexShrink={0}>
|
<FooterRow items={rowItems} showLabels={showLabels} />
|
||||||
<FooterRow items={rowItems} showLabels={showLabels} />
|
|
||||||
</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}, [orderedIds, selectedIds, activeId, isResetFocused, showLabels]);
|
}, [orderedIds, selectedIds, focusKey, showLabels]);
|
||||||
|
|
||||||
|
const availableTerminalHeight = constrainHeight
|
||||||
|
? terminalHeight - staticExtraHeight
|
||||||
|
: Number.MAX_SAFE_INTEGER;
|
||||||
|
|
||||||
|
const BORDER_HEIGHT = 2; // Outer round border
|
||||||
|
const STATIC_ELEMENTS = 13; // Text, margins, preview box, dialog footer
|
||||||
|
|
||||||
|
// Default padding adds 2 lines (top and bottom)
|
||||||
|
let includePadding = true;
|
||||||
|
if (availableTerminalHeight < BORDER_HEIGHT + 2 + STATIC_ELEMENTS + 6) {
|
||||||
|
includePadding = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const effectivePaddingY = includePadding ? 2 : 0;
|
||||||
|
const availableListSpace = Math.max(
|
||||||
|
0,
|
||||||
|
availableTerminalHeight -
|
||||||
|
BORDER_HEIGHT -
|
||||||
|
effectivePaddingY -
|
||||||
|
STATIC_ELEMENTS,
|
||||||
|
);
|
||||||
|
|
||||||
|
const maxItemsToShow = Math.max(
|
||||||
|
1,
|
||||||
|
Math.min(listItems.length, Math.floor(availableListSpace / 2)),
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@@ -329,7 +308,7 @@ export const FooterConfigDialog: React.FC<FooterConfigDialogProps> = ({
|
|||||||
borderStyle="round"
|
borderStyle="round"
|
||||||
borderColor={theme.border.default}
|
borderColor={theme.border.default}
|
||||||
paddingX={2}
|
paddingX={2}
|
||||||
paddingY={1}
|
paddingY={includePadding ? 1 : 0}
|
||||||
width="100%"
|
width="100%"
|
||||||
>
|
>
|
||||||
<Text bold>Configure Footer{'\n'}</Text>
|
<Text bold>Configure Footer{'\n'}</Text>
|
||||||
@@ -337,59 +316,65 @@ export const FooterConfigDialog: React.FC<FooterConfigDialogProps> = ({
|
|||||||
Select which items to display in the footer.
|
Select which items to display in the footer.
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Box flexDirection="column" marginTop={1} minHeight={maxItemsToShow}>
|
<Box flexDirection="column" marginTop={1} flexGrow={1}>
|
||||||
{visibleItems.length === 0 ? (
|
<BaseSelectionList<FooterConfigItem>
|
||||||
<Text color={theme.text.secondary}>No items found.</Text>
|
items={listItems}
|
||||||
) : (
|
onSelect={handleSelect}
|
||||||
visibleItems.map((item, idx) => {
|
onHighlight={handleHighlight}
|
||||||
const index = scrollOffset + idx;
|
focusKey={focusKey}
|
||||||
const isFocused = index === activeIndex;
|
showNumbers={false}
|
||||||
const isChecked = selectedIds.has(item.key);
|
maxItemsToShow={maxItemsToShow}
|
||||||
|
showScrollArrows={true}
|
||||||
|
selectedIndicator=">"
|
||||||
|
renderItem={(item, { isSelected, titleColor }) => {
|
||||||
|
const configItem = item.value;
|
||||||
|
const isChecked =
|
||||||
|
configItem.type === 'config'
|
||||||
|
? selectedIds.has(configItem.id)
|
||||||
|
: configItem.type === 'labels-toggle'
|
||||||
|
? showLabels
|
||||||
|
: false;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box key={item.key} flexDirection="row">
|
<Box flexDirection="column" minHeight={2}>
|
||||||
<Text color={isFocused ? theme.status.success : undefined}>
|
<Box flexDirection="row">
|
||||||
{isFocused ? '> ' : ' '}
|
{configItem.type !== 'reset' && (
|
||||||
</Text>
|
<Text
|
||||||
<Text
|
color={
|
||||||
color={isFocused ? theme.status.success : theme.text.primary}
|
isChecked ? theme.status.success : theme.text.secondary
|
||||||
>
|
}
|
||||||
[{isChecked ? '✓' : ' '}]{' '}
|
>
|
||||||
{item.label.padEnd(maxLabelWidth + 1)}
|
[{isChecked ? '✓' : ' '}]
|
||||||
</Text>
|
</Text>
|
||||||
<Text color={theme.text.secondary}> {item.description}</Text>
|
)}
|
||||||
|
<Text
|
||||||
|
color={
|
||||||
|
configItem.type === 'reset' && isSelected
|
||||||
|
? theme.status.warning
|
||||||
|
: titleColor
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{configItem.type !== 'reset' ? ' ' : ''}
|
||||||
|
{configItem.label}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
{configItem.description && (
|
||||||
|
<Text color={theme.text.secondary} wrap="wrap">
|
||||||
|
{' '}
|
||||||
|
{configItem.description}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
})
|
}}
|
||||||
)}
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box marginTop={1} flexDirection="column">
|
<DialogFooter
|
||||||
<Box flexDirection="row">
|
primaryAction="Enter to select"
|
||||||
<Text color={isShowLabelsFocused ? theme.status.success : undefined}>
|
navigationActions="↑/↓ to navigate · ←/→ to reorder"
|
||||||
{isShowLabelsFocused ? '> ' : ' '}
|
cancelAction="Esc to close"
|
||||||
</Text>
|
/>
|
||||||
<Text color={isShowLabelsFocused ? theme.status.success : undefined}>
|
|
||||||
[{showLabels ? '✓' : ' '}] Show footer labels
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
<Box flexDirection="row">
|
|
||||||
<Text color={isResetFocused ? theme.status.warning : undefined}>
|
|
||||||
{isResetFocused ? '> ' : ' '}
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
color={isResetFocused ? theme.status.warning : theme.text.secondary}
|
|
||||||
>
|
|
||||||
Reset to default footer
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box marginTop={1} flexDirection="column">
|
|
||||||
<Text color={theme.text.secondary}>
|
|
||||||
↑/↓ navigate · ←/→ reorder · enter/space select · esc close
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
marginTop={1}
|
marginTop={1}
|
||||||
@@ -399,7 +384,9 @@ export const FooterConfigDialog: React.FC<FooterConfigDialogProps> = ({
|
|||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
>
|
>
|
||||||
<Text bold>Preview:</Text>
|
<Text bold>Preview:</Text>
|
||||||
<Box flexDirection="row">{previewContent}</Box>
|
<Box flexDirection="row" width="100%">
|
||||||
|
{previewContent}
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,26 +1,26 @@
|
|||||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
exports[`<Footer /> > displays "Limit reached" message when remaining is 0 1`] = `
|
exports[`<Footer /> > displays "Limit reached" message when remaining is 0 1`] = `
|
||||||
" workspace (/directory) sandbox /model /stats
|
" workspace (/directory) sandbox /model /stats
|
||||||
~/project/foo/bar/and/some/more/directories/to/make/it/long no sandbox gemini-pro limit reached
|
~/project/foo/bar/and/some/more/directories/to/make/it/long no sandbox gemini-pro limit reached
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`<Footer /> > displays the usage indicator when usage is low 1`] = `
|
exports[`<Footer /> > displays the usage indicator when usage is low 1`] = `
|
||||||
" workspace (/directory) sandbox /model /stats
|
" workspace (/directory) sandbox /model /stats
|
||||||
~/project/foo/bar/and/some/more/directories/to/make/it/long no sandbox gemini-pro 85%
|
~/project/foo/bar/and/some/more/directories/to/make/it/long no sandbox gemini-pro 85%
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`<Footer /> > footer configuration filtering (golden snapshots) > renders complete footer in narrow terminal (baseline narrow) > complete-footer-narrow 1`] = `
|
exports[`<Footer /> > footer configuration filtering (golden snapshots) > renders complete footer in narrow terminal (baseline narrow) > complete-footer-narrow 1`] = `
|
||||||
" workspace (/directory) sandbox /model context
|
" workspace (/directory) sandbox /model context
|
||||||
...me/more/directories/to/make/it/long no sandbox gemini-pro 14%
|
...me/more/directories/to/make/it/long no sandbox gemini-pro 14%
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`<Footer /> > footer configuration filtering (golden snapshots) > renders complete footer with all sections visible (baseline) > complete-footer-wide 1`] = `
|
exports[`<Footer /> > footer configuration filtering (golden snapshots) > renders complete footer with all sections visible (baseline) > complete-footer-wide 1`] = `
|
||||||
" workspace (/directory) sandbox /model context
|
" workspace (/directory) sandbox /model context
|
||||||
~/project/foo/bar/and/some/more/directories/to/make/it/long no sandbox gemini-pro 14% used
|
~/project/foo/bar/and/some/more/directories/to/make/it/long no sandbox gemini-pro 14% used
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -33,13 +33,13 @@ exports[`<Footer /> > footer configuration filtering (golden snapshots) > render
|
|||||||
exports[`<Footer /> > footer configuration filtering (golden snapshots) > renders footer with all optional sections hidden (minimal footer) > footer-minimal 1`] = `""`;
|
exports[`<Footer /> > footer configuration filtering (golden snapshots) > renders footer with all optional sections hidden (minimal footer) > footer-minimal 1`] = `""`;
|
||||||
|
|
||||||
exports[`<Footer /> > footer configuration filtering (golden snapshots) > renders footer with only model info hidden (partial filtering) > footer-no-model 1`] = `
|
exports[`<Footer /> > footer configuration filtering (golden snapshots) > renders footer with only model info hidden (partial filtering) > footer-no-model 1`] = `
|
||||||
" workspace (/directory) sandbox
|
" workspace (/directory) sandbox
|
||||||
~/project/foo/bar/and/some/more/directories/to/make/it/long no sandbox
|
~/project/foo/bar/and/some/more/directories/to/make/it/long no sandbox
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`<Footer /> > hides the usage indicator when usage is not near limit 1`] = `
|
exports[`<Footer /> > hides the usage indicator when usage is not near limit 1`] = `
|
||||||
" workspace (/directory) sandbox /model /stats
|
" workspace (/directory) sandbox /model /stats
|
||||||
~/project/foo/bar/and/some/more/directories/to/make/it/long no sandbox gemini-pro 15%
|
~/project/foo/bar/and/some/more/directories/to/make/it/long no sandbox gemini-pro 15%
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|||||||
+159
@@ -0,0 +1,159 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="700" viewBox="0 0 920 700">
|
||||||
|
<style>
|
||||||
|
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
|
||||||
|
</style>
|
||||||
|
<rect width="920" height="700" fill="#000000" />
|
||||||
|
<g transform="translate(10, 10)">
|
||||||
|
<text x="0" y="2" fill="#333333" textLength="900" lengthAdjust="spacingAndGlyphs">╭──────────────────────────────────────────────────────────────────────────────────────────────────╮</text>
|
||||||
|
<text x="0" y="19" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="891" y="19" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="27" y="36" fill="#ffffff" textLength="144" lengthAdjust="spacingAndGlyphs" font-weight="bold">Configure Footer</text>
|
||||||
|
<text x="891" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="53" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="891" y="53" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="27" y="70" fill="#afafaf" textLength="396" lengthAdjust="spacingAndGlyphs">Select which items to display in the footer.</text>
|
||||||
|
<text x="891" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="87" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="891" y="87" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="104" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="104" fill="#d7ffd7" textLength="27" lengthAdjust="spacingAndGlyphs">[✓]</text>
|
||||||
|
<text x="72" y="104" fill="#ffffff" textLength="90" lengthAdjust="spacingAndGlyphs"> workspace</text>
|
||||||
|
<text x="891" y="104" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="121" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="121" fill="#afafaf" textLength="234" lengthAdjust="spacingAndGlyphs"> Current working directory</text>
|
||||||
|
<text x="891" y="121" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="138" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="138" fill="#d7ffd7" textLength="27" lengthAdjust="spacingAndGlyphs">[✓]</text>
|
||||||
|
<text x="72" y="138" fill="#ffffff" textLength="99" lengthAdjust="spacingAndGlyphs"> git-branch</text>
|
||||||
|
<text x="891" y="138" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="155" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="155" fill="#afafaf" textLength="477" lengthAdjust="spacingAndGlyphs"> Current git branch name (not shown when unavailable)</text>
|
||||||
|
<text x="891" y="155" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="172" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="172" fill="#d7ffd7" textLength="27" lengthAdjust="spacingAndGlyphs">[✓]</text>
|
||||||
|
<text x="72" y="172" fill="#ffffff" textLength="72" lengthAdjust="spacingAndGlyphs"> sandbox</text>
|
||||||
|
<text x="891" y="172" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="189" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="189" fill="#afafaf" textLength="297" lengthAdjust="spacingAndGlyphs"> Sandbox type and trust indicator</text>
|
||||||
|
<text x="891" y="189" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="206" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="206" fill="#d7ffd7" textLength="27" lengthAdjust="spacingAndGlyphs">[✓]</text>
|
||||||
|
<text x="72" y="206" fill="#ffffff" textLength="99" lengthAdjust="spacingAndGlyphs"> model-name</text>
|
||||||
|
<text x="891" y="206" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="223" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="223" fill="#afafaf" textLength="225" lengthAdjust="spacingAndGlyphs"> Current model identifier</text>
|
||||||
|
<text x="891" y="223" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="240" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="240" fill="#d7ffd7" textLength="27" lengthAdjust="spacingAndGlyphs">[✓]</text>
|
||||||
|
<text x="72" y="240" fill="#ffffff" textLength="54" lengthAdjust="spacingAndGlyphs"> quota</text>
|
||||||
|
<text x="891" y="240" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="257" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="257" fill="#afafaf" textLength="540" lengthAdjust="spacingAndGlyphs"> Remaining usage on daily limit (not shown when unavailable)</text>
|
||||||
|
<text x="891" y="257" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="274" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="274" fill="#afafaf" textLength="27" lengthAdjust="spacingAndGlyphs">[ ]</text>
|
||||||
|
<text x="72" y="274" fill="#ffffff" textLength="117" lengthAdjust="spacingAndGlyphs"> context-used</text>
|
||||||
|
<text x="891" y="274" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="291" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="291" fill="#afafaf" textLength="306" lengthAdjust="spacingAndGlyphs"> Percentage of context window used</text>
|
||||||
|
<text x="891" y="291" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="308" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="308" fill="#afafaf" textLength="27" lengthAdjust="spacingAndGlyphs">[ ]</text>
|
||||||
|
<text x="72" y="308" fill="#ffffff" textLength="117" lengthAdjust="spacingAndGlyphs"> memory-usage</text>
|
||||||
|
<text x="891" y="308" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="325" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="325" fill="#afafaf" textLength="279" lengthAdjust="spacingAndGlyphs"> Memory used by the application</text>
|
||||||
|
<text x="891" y="325" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="342" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="342" fill="#afafaf" textLength="27" lengthAdjust="spacingAndGlyphs">[ ]</text>
|
||||||
|
<text x="72" y="342" fill="#ffffff" textLength="99" lengthAdjust="spacingAndGlyphs"> session-id</text>
|
||||||
|
<text x="891" y="342" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="359" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="359" fill="#afafaf" textLength="378" lengthAdjust="spacingAndGlyphs"> Unique identifier for the current session</text>
|
||||||
|
<text x="891" y="359" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="376" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<rect x="27" y="374" width="9" height="17" fill="#001a00" />
|
||||||
|
<text x="27" y="376" fill="#00cd00" textLength="9" lengthAdjust="spacingAndGlyphs">></text>
|
||||||
|
<rect x="36" y="374" width="9" height="17" fill="#001a00" />
|
||||||
|
<rect x="45" y="374" width="27" height="17" fill="#001a00" />
|
||||||
|
<text x="45" y="376" fill="#d7ffd7" textLength="27" lengthAdjust="spacingAndGlyphs">[✓]</text>
|
||||||
|
<rect x="72" y="374" width="117" height="17" fill="#001a00" />
|
||||||
|
<text x="72" y="376" fill="#00cd00" textLength="117" lengthAdjust="spacingAndGlyphs"> code-changes</text>
|
||||||
|
<rect x="189" y="374" width="684" height="17" fill="#001a00" />
|
||||||
|
<text x="891" y="376" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="393" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<rect x="27" y="391" width="18" height="17" fill="#001a00" />
|
||||||
|
<rect x="45" y="391" width="513" height="17" fill="#001a00" />
|
||||||
|
<text x="45" y="393" fill="#afafaf" textLength="513" lengthAdjust="spacingAndGlyphs"> Lines added/removed in the session (not shown when zero)</text>
|
||||||
|
<rect x="558" y="391" width="315" height="17" fill="#001a00" />
|
||||||
|
<text x="891" y="393" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="410" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="410" fill="#afafaf" textLength="27" lengthAdjust="spacingAndGlyphs">[ ]</text>
|
||||||
|
<text x="72" y="410" fill="#ffffff" textLength="108" lengthAdjust="spacingAndGlyphs"> token-count</text>
|
||||||
|
<text x="891" y="410" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="427" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="427" fill="#afafaf" textLength="495" lengthAdjust="spacingAndGlyphs"> Total tokens used in the session (not shown when zero)</text>
|
||||||
|
<text x="891" y="427" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="444" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="444" fill="#d7ffd7" textLength="27" lengthAdjust="spacingAndGlyphs">[✓]</text>
|
||||||
|
<text x="72" y="444" fill="#ffffff" textLength="171" lengthAdjust="spacingAndGlyphs"> Show footer labels</text>
|
||||||
|
<text x="891" y="444" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="461" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="891" y="461" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="478" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="478" fill="#ffffff" textLength="207" lengthAdjust="spacingAndGlyphs">Reset to default footer</text>
|
||||||
|
<text x="891" y="478" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="495" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="891" y="495" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="512" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="891" y="512" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="529" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="27" y="529" fill="#afafaf" textLength="585" lengthAdjust="spacingAndGlyphs">Enter to select · ↑/↓ to navigate · ←/→ to reorder · Esc to close</text>
|
||||||
|
<text x="891" y="529" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="546" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="891" y="546" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="563" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="27" y="563" fill="#333333" textLength="846" lengthAdjust="spacingAndGlyphs">┌────────────────────────────────────────────────────────────────────────────────────────────┐</text>
|
||||||
|
<text x="891" y="563" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="580" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="27" y="580" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="580" fill="#ffffff" textLength="72" lengthAdjust="spacingAndGlyphs" font-weight="bold">Preview:</text>
|
||||||
|
<text x="864" y="580" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="891" y="580" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="597" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="27" y="597" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="597" fill="#afafaf" textLength="198" lengthAdjust="spacingAndGlyphs">workspace (/directory)</text>
|
||||||
|
<text x="288" y="597" fill="#afafaf" textLength="54" lengthAdjust="spacingAndGlyphs">branch</text>
|
||||||
|
<text x="396" y="597" fill="#afafaf" textLength="63" lengthAdjust="spacingAndGlyphs">sandbox</text>
|
||||||
|
<text x="504" y="597" fill="#afafaf" textLength="54" lengthAdjust="spacingAndGlyphs">/model</text>
|
||||||
|
<text x="675" y="597" fill="#afafaf" textLength="54" lengthAdjust="spacingAndGlyphs">/stats</text>
|
||||||
|
<rect x="783" y="595" width="36" height="17" fill="#001a00" />
|
||||||
|
<text x="783" y="597" fill="#ffffff" textLength="36" lengthAdjust="spacingAndGlyphs">diff</text>
|
||||||
|
<rect x="819" y="595" width="36" height="17" fill="#001a00" />
|
||||||
|
<text x="864" y="597" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="891" y="597" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="614" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="27" y="614" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="614" fill="#ffffff" textLength="126" lengthAdjust="spacingAndGlyphs">~/project/path</text>
|
||||||
|
<text x="288" y="614" fill="#ffffff" textLength="36" lengthAdjust="spacingAndGlyphs">main</text>
|
||||||
|
<text x="396" y="614" fill="#00cd00" textLength="54" lengthAdjust="spacingAndGlyphs">docker</text>
|
||||||
|
<text x="504" y="614" fill="#ffffff" textLength="126" lengthAdjust="spacingAndGlyphs">gemini-2.5-pro</text>
|
||||||
|
<text x="675" y="614" fill="#ffffff" textLength="27" lengthAdjust="spacingAndGlyphs">97%</text>
|
||||||
|
<rect x="783" y="612" width="27" height="17" fill="#001a00" />
|
||||||
|
<text x="783" y="614" fill="#d7ffd7" textLength="27" lengthAdjust="spacingAndGlyphs">+12</text>
|
||||||
|
<rect x="810" y="612" width="9" height="17" fill="#001a00" />
|
||||||
|
<rect x="819" y="612" width="18" height="17" fill="#001a00" />
|
||||||
|
<text x="819" y="614" fill="#ff87af" textLength="18" lengthAdjust="spacingAndGlyphs">-4</text>
|
||||||
|
<rect x="837" y="612" width="18" height="17" fill="#001a00" />
|
||||||
|
<text x="864" y="614" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="891" y="614" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="631" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="27" y="631" fill="#333333" textLength="846" lengthAdjust="spacingAndGlyphs">└────────────────────────────────────────────────────────────────────────────────────────────┘</text>
|
||||||
|
<text x="891" y="631" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="648" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="891" y="648" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="665" fill="#333333" textLength="900" lengthAdjust="spacingAndGlyphs">╰──────────────────────────────────────────────────────────────────────────────────────────────────╯</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 16 KiB |
+154
@@ -0,0 +1,154 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="700" viewBox="0 0 920 700">
|
||||||
|
<style>
|
||||||
|
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
|
||||||
|
</style>
|
||||||
|
<rect width="920" height="700" fill="#000000" />
|
||||||
|
<g transform="translate(10, 10)">
|
||||||
|
<text x="0" y="2" fill="#333333" textLength="900" lengthAdjust="spacingAndGlyphs">╭──────────────────────────────────────────────────────────────────────────────────────────────────╮</text>
|
||||||
|
<text x="0" y="19" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="891" y="19" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="27" y="36" fill="#ffffff" textLength="144" lengthAdjust="spacingAndGlyphs" font-weight="bold">Configure Footer</text>
|
||||||
|
<text x="891" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="53" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="891" y="53" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="27" y="70" fill="#afafaf" textLength="396" lengthAdjust="spacingAndGlyphs">Select which items to display in the footer.</text>
|
||||||
|
<text x="891" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="87" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="891" y="87" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="104" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<rect x="27" y="102" width="9" height="17" fill="#001a00" />
|
||||||
|
<text x="27" y="104" fill="#00cd00" textLength="9" lengthAdjust="spacingAndGlyphs">></text>
|
||||||
|
<rect x="36" y="102" width="9" height="17" fill="#001a00" />
|
||||||
|
<rect x="45" y="102" width="27" height="17" fill="#001a00" />
|
||||||
|
<text x="45" y="104" fill="#d7ffd7" textLength="27" lengthAdjust="spacingAndGlyphs">[✓]</text>
|
||||||
|
<rect x="72" y="102" width="90" height="17" fill="#001a00" />
|
||||||
|
<text x="72" y="104" fill="#00cd00" textLength="90" lengthAdjust="spacingAndGlyphs"> workspace</text>
|
||||||
|
<rect x="162" y="102" width="711" height="17" fill="#001a00" />
|
||||||
|
<text x="891" y="104" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="121" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<rect x="27" y="119" width="18" height="17" fill="#001a00" />
|
||||||
|
<rect x="45" y="119" width="234" height="17" fill="#001a00" />
|
||||||
|
<text x="45" y="121" fill="#afafaf" textLength="234" lengthAdjust="spacingAndGlyphs"> Current working directory</text>
|
||||||
|
<rect x="279" y="119" width="594" height="17" fill="#001a00" />
|
||||||
|
<text x="891" y="121" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="138" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="138" fill="#d7ffd7" textLength="27" lengthAdjust="spacingAndGlyphs">[✓]</text>
|
||||||
|
<text x="72" y="138" fill="#ffffff" textLength="99" lengthAdjust="spacingAndGlyphs"> git-branch</text>
|
||||||
|
<text x="891" y="138" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="155" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="155" fill="#afafaf" textLength="477" lengthAdjust="spacingAndGlyphs"> Current git branch name (not shown when unavailable)</text>
|
||||||
|
<text x="891" y="155" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="172" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="172" fill="#d7ffd7" textLength="27" lengthAdjust="spacingAndGlyphs">[✓]</text>
|
||||||
|
<text x="72" y="172" fill="#ffffff" textLength="72" lengthAdjust="spacingAndGlyphs"> sandbox</text>
|
||||||
|
<text x="891" y="172" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="189" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="189" fill="#afafaf" textLength="297" lengthAdjust="spacingAndGlyphs"> Sandbox type and trust indicator</text>
|
||||||
|
<text x="891" y="189" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="206" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="206" fill="#d7ffd7" textLength="27" lengthAdjust="spacingAndGlyphs">[✓]</text>
|
||||||
|
<text x="72" y="206" fill="#ffffff" textLength="99" lengthAdjust="spacingAndGlyphs"> model-name</text>
|
||||||
|
<text x="891" y="206" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="223" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="223" fill="#afafaf" textLength="225" lengthAdjust="spacingAndGlyphs"> Current model identifier</text>
|
||||||
|
<text x="891" y="223" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="240" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="240" fill="#d7ffd7" textLength="27" lengthAdjust="spacingAndGlyphs">[✓]</text>
|
||||||
|
<text x="72" y="240" fill="#ffffff" textLength="54" lengthAdjust="spacingAndGlyphs"> quota</text>
|
||||||
|
<text x="891" y="240" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="257" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="257" fill="#afafaf" textLength="540" lengthAdjust="spacingAndGlyphs"> Remaining usage on daily limit (not shown when unavailable)</text>
|
||||||
|
<text x="891" y="257" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="274" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="274" fill="#afafaf" textLength="27" lengthAdjust="spacingAndGlyphs">[ ]</text>
|
||||||
|
<text x="72" y="274" fill="#ffffff" textLength="117" lengthAdjust="spacingAndGlyphs"> context-used</text>
|
||||||
|
<text x="891" y="274" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="291" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="291" fill="#afafaf" textLength="306" lengthAdjust="spacingAndGlyphs"> Percentage of context window used</text>
|
||||||
|
<text x="891" y="291" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="308" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="308" fill="#afafaf" textLength="27" lengthAdjust="spacingAndGlyphs">[ ]</text>
|
||||||
|
<text x="72" y="308" fill="#ffffff" textLength="117" lengthAdjust="spacingAndGlyphs"> memory-usage</text>
|
||||||
|
<text x="891" y="308" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="325" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="325" fill="#afafaf" textLength="279" lengthAdjust="spacingAndGlyphs"> Memory used by the application</text>
|
||||||
|
<text x="891" y="325" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="342" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="342" fill="#afafaf" textLength="27" lengthAdjust="spacingAndGlyphs">[ ]</text>
|
||||||
|
<text x="72" y="342" fill="#ffffff" textLength="99" lengthAdjust="spacingAndGlyphs"> session-id</text>
|
||||||
|
<text x="891" y="342" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="359" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="359" fill="#afafaf" textLength="378" lengthAdjust="spacingAndGlyphs"> Unique identifier for the current session</text>
|
||||||
|
<text x="891" y="359" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="376" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="376" fill="#afafaf" textLength="27" lengthAdjust="spacingAndGlyphs">[ ]</text>
|
||||||
|
<text x="72" y="376" fill="#ffffff" textLength="117" lengthAdjust="spacingAndGlyphs"> code-changes</text>
|
||||||
|
<text x="891" y="376" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="393" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="393" fill="#afafaf" textLength="513" lengthAdjust="spacingAndGlyphs"> Lines added/removed in the session (not shown when zero)</text>
|
||||||
|
<text x="891" y="393" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="410" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="410" fill="#afafaf" textLength="27" lengthAdjust="spacingAndGlyphs">[ ]</text>
|
||||||
|
<text x="72" y="410" fill="#ffffff" textLength="108" lengthAdjust="spacingAndGlyphs"> token-count</text>
|
||||||
|
<text x="891" y="410" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="427" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="427" fill="#afafaf" textLength="495" lengthAdjust="spacingAndGlyphs"> Total tokens used in the session (not shown when zero)</text>
|
||||||
|
<text x="891" y="427" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="444" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="444" fill="#d7ffd7" textLength="27" lengthAdjust="spacingAndGlyphs">[✓]</text>
|
||||||
|
<text x="72" y="444" fill="#ffffff" textLength="171" lengthAdjust="spacingAndGlyphs"> Show footer labels</text>
|
||||||
|
<text x="891" y="444" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="461" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="891" y="461" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="478" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="478" fill="#ffffff" textLength="207" lengthAdjust="spacingAndGlyphs">Reset to default footer</text>
|
||||||
|
<text x="891" y="478" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="495" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="891" y="495" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="512" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="891" y="512" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="529" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="27" y="529" fill="#afafaf" textLength="585" lengthAdjust="spacingAndGlyphs">Enter to select · ↑/↓ to navigate · ←/→ to reorder · Esc to close</text>
|
||||||
|
<text x="891" y="529" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="546" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="891" y="546" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="563" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="27" y="563" fill="#333333" textLength="846" lengthAdjust="spacingAndGlyphs">┌────────────────────────────────────────────────────────────────────────────────────────────┐</text>
|
||||||
|
<text x="891" y="563" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="580" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="27" y="580" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="45" y="580" fill="#ffffff" textLength="72" lengthAdjust="spacingAndGlyphs" font-weight="bold">Preview:</text>
|
||||||
|
<text x="864" y="580" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="891" y="580" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="597" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="27" y="597" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<rect x="45" y="595" width="198" height="17" fill="#001a00" />
|
||||||
|
<text x="45" y="597" fill="#ffffff" textLength="198" lengthAdjust="spacingAndGlyphs">workspace (/directory)</text>
|
||||||
|
<rect x="243" y="595" width="45" height="17" fill="#001a00" />
|
||||||
|
<text x="315" y="597" fill="#afafaf" textLength="54" lengthAdjust="spacingAndGlyphs">branch</text>
|
||||||
|
<text x="432" y="597" fill="#afafaf" textLength="63" lengthAdjust="spacingAndGlyphs">sandbox</text>
|
||||||
|
<text x="567" y="597" fill="#afafaf" textLength="54" lengthAdjust="spacingAndGlyphs">/model</text>
|
||||||
|
<text x="756" y="597" fill="#afafaf" textLength="54" lengthAdjust="spacingAndGlyphs">/stats</text>
|
||||||
|
<text x="864" y="597" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="891" y="597" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="614" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="27" y="614" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<rect x="45" y="612" width="126" height="17" fill="#001a00" />
|
||||||
|
<text x="45" y="614" fill="#ffffff" textLength="126" lengthAdjust="spacingAndGlyphs">~/project/path</text>
|
||||||
|
<rect x="171" y="612" width="117" height="17" fill="#001a00" />
|
||||||
|
<text x="315" y="614" fill="#ffffff" textLength="36" lengthAdjust="spacingAndGlyphs">main</text>
|
||||||
|
<text x="432" y="614" fill="#00cd00" textLength="54" lengthAdjust="spacingAndGlyphs">docker</text>
|
||||||
|
<text x="567" y="614" fill="#ffffff" textLength="126" lengthAdjust="spacingAndGlyphs">gemini-2.5-pro</text>
|
||||||
|
<text x="756" y="614" fill="#ffffff" textLength="27" lengthAdjust="spacingAndGlyphs">97%</text>
|
||||||
|
<text x="864" y="614" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="891" y="614" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="631" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="27" y="631" fill="#333333" textLength="846" lengthAdjust="spacingAndGlyphs">└────────────────────────────────────────────────────────────────────────────────────────────┘</text>
|
||||||
|
<text x="891" y="631" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="648" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="891" y="648" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||||
|
<text x="0" y="665" fill="#333333" textLength="900" lengthAdjust="spacingAndGlyphs">╰──────────────────────────────────────────────────────────────────────────────────────────────────╯</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 16 KiB |
@@ -1,5 +1,48 @@
|
|||||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`<FooterConfigDialog /> > highlights the active item in the preview 1`] = `
|
||||||
|
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ │
|
||||||
|
│ Configure Footer │
|
||||||
|
│ │
|
||||||
|
│ Select which items to display in the footer. │
|
||||||
|
│ │
|
||||||
|
│ [✓] workspace │
|
||||||
|
│ Current working directory │
|
||||||
|
│ [✓] git-branch │
|
||||||
|
│ Current git branch name (not shown when unavailable) │
|
||||||
|
│ [✓] sandbox │
|
||||||
|
│ Sandbox type and trust indicator │
|
||||||
|
│ [✓] model-name │
|
||||||
|
│ Current model identifier │
|
||||||
|
│ [✓] quota │
|
||||||
|
│ Remaining usage on daily limit (not shown when unavailable) │
|
||||||
|
│ [ ] context-used │
|
||||||
|
│ Percentage of context window used │
|
||||||
|
│ [ ] memory-usage │
|
||||||
|
│ Memory used by the application │
|
||||||
|
│ [ ] session-id │
|
||||||
|
│ Unique identifier for the current session │
|
||||||
|
│ > [✓] code-changes │
|
||||||
|
│ Lines added/removed in the session (not shown when zero) │
|
||||||
|
│ [ ] token-count │
|
||||||
|
│ Total tokens used in the session (not shown when zero) │
|
||||||
|
│ [✓] Show footer labels │
|
||||||
|
│ │
|
||||||
|
│ Reset to default footer │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ Enter to select · ↑/↓ to navigate · ←/→ to reorder · Esc to close │
|
||||||
|
│ │
|
||||||
|
│ ┌────────────────────────────────────────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Preview: │ │
|
||||||
|
│ │ workspace (/directory) branch sandbox /model /stats diff │ │
|
||||||
|
│ │ ~/project/path main docker gemini-2.5-pro 97% +12 -4 │ │
|
||||||
|
│ └────────────────────────────────────────────────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`<FooterConfigDialog /> > renders correctly with default settings 1`] = `
|
exports[`<FooterConfigDialog /> > renders correctly with default settings 1`] = `
|
||||||
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
│ │
|
│ │
|
||||||
@@ -7,28 +50,82 @@ exports[`<FooterConfigDialog /> > renders correctly with default settings 1`] =
|
|||||||
│ │
|
│ │
|
||||||
│ Select which items to display in the footer. │
|
│ Select which items to display in the footer. │
|
||||||
│ │
|
│ │
|
||||||
│ > [✓] workspace Current working directory │
|
│ > [✓] workspace │
|
||||||
│ [✓] git-branch Current git branch name (not shown when unavailable) │
|
│ Current working directory │
|
||||||
│ [✓] sandbox Sandbox type and trust indicator │
|
│ [✓] git-branch │
|
||||||
│ [✓] model-name Current model identifier │
|
│ Current git branch name (not shown when unavailable) │
|
||||||
│ [✓] quota Remaining usage on daily limit (not shown when unavailable) │
|
│ [✓] sandbox │
|
||||||
│ [ ] context-used Percentage of context window used │
|
│ Sandbox type and trust indicator │
|
||||||
│ [ ] memory-usage Memory used by the application │
|
│ [✓] model-name │
|
||||||
│ [ ] session-id Unique identifier for the current session │
|
│ Current model identifier │
|
||||||
│ [ ] code-changes Lines added/removed in the session (not shown when zero) │
|
│ [✓] quota │
|
||||||
│ [ ] token-count Total tokens used in the session (not shown when zero) │
|
│ Remaining usage on daily limit (not shown when unavailable) │
|
||||||
│ │
|
│ [ ] context-used │
|
||||||
|
│ Percentage of context window used │
|
||||||
|
│ [ ] memory-usage │
|
||||||
|
│ Memory used by the application │
|
||||||
|
│ [ ] session-id │
|
||||||
|
│ Unique identifier for the current session │
|
||||||
|
│ [ ] code-changes │
|
||||||
|
│ Lines added/removed in the session (not shown when zero) │
|
||||||
|
│ [ ] token-count │
|
||||||
|
│ Total tokens used in the session (not shown when zero) │
|
||||||
│ [✓] Show footer labels │
|
│ [✓] Show footer labels │
|
||||||
|
│ │
|
||||||
│ Reset to default footer │
|
│ Reset to default footer │
|
||||||
│ │
|
│ │
|
||||||
│ ↑/↓ navigate · ←/→ reorder · enter/space select · esc close │
|
│ │
|
||||||
|
│ Enter to select · ↑/↓ to navigate · ←/→ to reorder · Esc to close │
|
||||||
│ │
|
│ │
|
||||||
│ ┌────────────────────────────────────────────────────────────────────────────────────────────┐ │
|
│ ┌────────────────────────────────────────────────────────────────────────────────────────────┐ │
|
||||||
│ │ Preview: │ │
|
│ │ Preview: │ │
|
||||||
│ │ workspace (/directory) branch sandbox /model /stats │ │
|
│ │ workspace (/directory) branch sandbox /model /stats │ │
|
||||||
│ │ ~/project/path main docker gemini-2.5-pro 97% │ │
|
│ │ ~/project/path main docker gemini-2.5-pro 97% │ │
|
||||||
│ └────────────────────────────────────────────────────────────────────────────────────────────┘ │
|
│ └────────────────────────────────────────────────────────────────────────────────────────────┘ │
|
||||||
│ │
|
│ │
|
||||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`<FooterConfigDialog /> > renders correctly with default settings 2`] = `
|
||||||
|
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ │
|
||||||
|
│ Configure Footer │
|
||||||
|
│ │
|
||||||
|
│ Select which items to display in the footer. │
|
||||||
|
│ │
|
||||||
|
│ > [✓] workspace │
|
||||||
|
│ Current working directory │
|
||||||
|
│ [✓] git-branch │
|
||||||
|
│ Current git branch name (not shown when unavailable) │
|
||||||
|
│ [✓] sandbox │
|
||||||
|
│ Sandbox type and trust indicator │
|
||||||
|
│ [✓] model-name │
|
||||||
|
│ Current model identifier │
|
||||||
|
│ [✓] quota │
|
||||||
|
│ Remaining usage on daily limit (not shown when unavailable) │
|
||||||
|
│ [ ] context-used │
|
||||||
|
│ Percentage of context window used │
|
||||||
|
│ [ ] memory-usage │
|
||||||
|
│ Memory used by the application │
|
||||||
|
│ [ ] session-id │
|
||||||
|
│ Unique identifier for the current session │
|
||||||
|
│ [ ] code-changes │
|
||||||
|
│ Lines added/removed in the session (not shown when zero) │
|
||||||
|
│ [ ] token-count │
|
||||||
|
│ Total tokens used in the session (not shown when zero) │
|
||||||
|
│ [✓] Show footer labels │
|
||||||
|
│ │
|
||||||
|
│ Reset to default footer │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ Enter to select · ↑/↓ to navigate · ←/→ to reorder · Esc to close │
|
||||||
|
│ │
|
||||||
|
│ ┌────────────────────────────────────────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Preview: │ │
|
||||||
|
│ │ workspace (/directory) branch sandbox /model /stats │ │
|
||||||
|
│ │ ~/project/path main docker gemini-2.5-pro 97% │ │
|
||||||
|
│ └────────────────────────────────────────────────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||||
|
`;
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export interface BaseSelectionListProps<
|
|||||||
wrapAround?: boolean;
|
wrapAround?: boolean;
|
||||||
focusKey?: string;
|
focusKey?: string;
|
||||||
priority?: boolean;
|
priority?: boolean;
|
||||||
|
selectedIndicator?: string;
|
||||||
renderItem: (item: TItem, context: RenderItemContext) => React.ReactNode;
|
renderItem: (item: TItem, context: RenderItemContext) => React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,6 +66,7 @@ export function BaseSelectionList<
|
|||||||
wrapAround = true,
|
wrapAround = true,
|
||||||
focusKey,
|
focusKey,
|
||||||
priority,
|
priority,
|
||||||
|
selectedIndicator = '●',
|
||||||
renderItem,
|
renderItem,
|
||||||
}: BaseSelectionListProps<T, TItem>): React.JSX.Element {
|
}: BaseSelectionListProps<T, TItem>): React.JSX.Element {
|
||||||
const { activeIndex } = useSelectionList({
|
const { activeIndex } = useSelectionList({
|
||||||
@@ -148,7 +150,7 @@ export function BaseSelectionList<
|
|||||||
color={isSelected ? theme.ui.focus : theme.text.primary}
|
color={isSelected ? theme.ui.focus : theme.text.primary}
|
||||||
aria-hidden
|
aria-hidden
|
||||||
>
|
>
|
||||||
{isSelected ? '●' : ' '}
|
{isSelected ? selectedIndicator : ' '}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|||||||
@@ -81,6 +81,8 @@ describe('useSelectionList', () => {
|
|||||||
isFocused?: boolean;
|
isFocused?: boolean;
|
||||||
showNumbers?: boolean;
|
showNumbers?: boolean;
|
||||||
wrapAround?: boolean;
|
wrapAround?: boolean;
|
||||||
|
focusKey?: string;
|
||||||
|
priority?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
let hookResult: ReturnType<typeof useSelectionList>;
|
let hookResult: ReturnType<typeof useSelectionList>;
|
||||||
function TestComponent(props: typeof initialProps) {
|
function TestComponent(props: typeof initialProps) {
|
||||||
@@ -771,6 +773,67 @@ describe('useSelectionList', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Programmatic Focus (focusKey)', () => {
|
||||||
|
it('should change the activeIndex when a valid focusKey is provided', async () => {
|
||||||
|
const { result, rerender, waitUntilReady } =
|
||||||
|
await renderSelectionListHook({
|
||||||
|
items,
|
||||||
|
onSelect: mockOnSelect,
|
||||||
|
});
|
||||||
|
expect(result.current.activeIndex).toBe(0);
|
||||||
|
|
||||||
|
await rerender({ focusKey: 'C' });
|
||||||
|
await waitUntilReady();
|
||||||
|
expect(result.current.activeIndex).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ignore a focusKey that does not exist', async () => {
|
||||||
|
const { result, rerender, waitUntilReady } =
|
||||||
|
await renderSelectionListHook({
|
||||||
|
items,
|
||||||
|
onSelect: mockOnSelect,
|
||||||
|
});
|
||||||
|
expect(result.current.activeIndex).toBe(0);
|
||||||
|
|
||||||
|
await rerender({ focusKey: 'UNKNOWN' });
|
||||||
|
await waitUntilReady();
|
||||||
|
expect(result.current.activeIndex).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ignore a focusKey that points to a disabled item', async () => {
|
||||||
|
const { result, rerender, waitUntilReady } =
|
||||||
|
await renderSelectionListHook({
|
||||||
|
items, // B is disabled
|
||||||
|
onSelect: mockOnSelect,
|
||||||
|
});
|
||||||
|
expect(result.current.activeIndex).toBe(0);
|
||||||
|
|
||||||
|
await rerender({ focusKey: 'B' });
|
||||||
|
await waitUntilReady();
|
||||||
|
expect(result.current.activeIndex).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle clearing the focusKey', async () => {
|
||||||
|
const { result, rerender, waitUntilReady } =
|
||||||
|
await renderSelectionListHook({
|
||||||
|
items,
|
||||||
|
onSelect: mockOnSelect,
|
||||||
|
focusKey: 'C',
|
||||||
|
});
|
||||||
|
expect(result.current.activeIndex).toBe(2);
|
||||||
|
|
||||||
|
await rerender({ focusKey: undefined });
|
||||||
|
await waitUntilReady();
|
||||||
|
// Should remain at 2
|
||||||
|
expect(result.current.activeIndex).toBe(2);
|
||||||
|
|
||||||
|
// We can then change it again to something else
|
||||||
|
await rerender({ focusKey: 'D' });
|
||||||
|
await waitUntilReady();
|
||||||
|
expect(result.current.activeIndex).toBe(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Reactivity (Dynamic Updates)', () => {
|
describe('Reactivity (Dynamic Updates)', () => {
|
||||||
it('should update activeIndex when initialIndex prop changes', async () => {
|
it('should update activeIndex when initialIndex prop changes', async () => {
|
||||||
const { result, rerender } = await renderSelectionListHook({
|
const { result, rerender } = await renderSelectionListHook({
|
||||||
|
|||||||
@@ -213,8 +213,7 @@ function selectionListReducer(
|
|||||||
case 'INITIALIZE': {
|
case 'INITIALIZE': {
|
||||||
const { initialIndex, items, wrapAround } = action.payload;
|
const { initialIndex, items, wrapAround } = action.payload;
|
||||||
const activeKey =
|
const activeKey =
|
||||||
initialIndex === state.initialIndex &&
|
initialIndex === state.initialIndex
|
||||||
state.activeIndex !== state.initialIndex
|
|
||||||
? state.items[state.activeIndex]?.key
|
? state.items[state.activeIndex]?.key
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user