Merge branch 'main' into rename-directory-to-workspace
@@ -193,7 +193,7 @@ runs:
|
||||
INPUTS_A2A_PACKAGE_NAME: '${{ inputs.a2a-package-name }}'
|
||||
|
||||
- name: '📦 Prepare bundled CLI for npm release'
|
||||
if: "inputs.npm-registry-url != 'https://npm.pkg.github.com/'"
|
||||
if: "inputs.npm-registry-url != 'https://npm.pkg.github.com/' && inputs.npm-tag != 'latest'"
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
[](https://github.com/google-gemini/gemini-cli/blob/main/LICENSE)
|
||||
[](https://codewiki.google/github.com/google-gemini/gemini-cli?utm_source=badge&utm_medium=github&utm_campaign=github.com/google-gemini/gemini-cli)
|
||||
|
||||

|
||||

|
||||
|
||||
Gemini CLI is an open-source AI agent that brings the power of Gemini directly
|
||||
into your terminal. It provides lightweight access to Gemini, giving you the
|
||||
|
||||
|
After Width: | Height: | Size: 154 KiB |
|
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 127 KiB |
|
After Width: | Height: | Size: 151 KiB |
|
Before Width: | Height: | Size: 128 KiB |
|
After Width: | Height: | Size: 146 KiB |
|
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 137 KiB |
|
Before Width: | Height: | Size: 128 KiB |
|
After Width: | Height: | Size: 155 KiB |
|
Before Width: | Height: | Size: 125 KiB After Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 127 KiB |
|
After Width: | Height: | Size: 156 KiB |
|
Before Width: | Height: | Size: 128 KiB |
|
After Width: | Height: | Size: 154 KiB |
|
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 135 KiB |
|
Before Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 134 KiB |
|
After Width: | Height: | Size: 163 KiB |
|
After Width: | Height: | Size: 158 KiB |
|
After Width: | Height: | Size: 146 KiB |
|
After Width: | Height: | Size: 138 KiB |
|
Before Width: | Height: | Size: 125 KiB After Width: | Height: | Size: 120 KiB |
@@ -16,6 +16,8 @@ using the `/theme` command within Gemini CLI:
|
||||
- `Default`
|
||||
- `Dracula`
|
||||
- `GitHub`
|
||||
- `Holiday`
|
||||
- `Shades Of Purple`
|
||||
- `Solarized Dark`
|
||||
- **Light themes:**
|
||||
- `ANSI Light`
|
||||
@@ -185,7 +187,7 @@ untrusted sources.
|
||||
|
||||
### Example custom theme
|
||||
|
||||
<img src="../assets/theme-custom.png" alt="Custom theme example" width="600" />
|
||||
<img src="/docs/assets/theme-custom.png" alt="Custom theme example" width="600" />
|
||||
|
||||
### Using your custom theme
|
||||
|
||||
@@ -212,58 +214,66 @@ identify their source, for example: `shades-of-green (green-extension)`.
|
||||
|
||||
### ANSI
|
||||
|
||||
<img src="/assets/theme-ansi.png" alt="ANSI theme" width="600" />
|
||||
<img src="/docs/assets/theme-ansi-dark.png" alt="ANSI theme" width="600">
|
||||
|
||||
### Atom OneDark
|
||||
### Atom One
|
||||
|
||||
<img src="/assets/theme-atom-one.png" alt="Atom One theme" width="600">
|
||||
<img src="/docs/assets/theme-atom-one-dark.png" alt="Atom One theme" width="600">
|
||||
|
||||
### Ayu
|
||||
|
||||
<img src="/assets/theme-ayu.png" alt="Ayu theme" width="600">
|
||||
<img src="/docs/assets/theme-ayu-dark.png" alt="Ayu theme" width="600">
|
||||
|
||||
### Default
|
||||
|
||||
<img src="/assets/theme-default.png" alt="Default theme" width="600">
|
||||
<img src="/docs/assets/theme-default-dark.png" alt="Default theme" width="600">
|
||||
|
||||
### Dracula
|
||||
|
||||
<img src="/assets/theme-dracula.png" alt="Dracula theme" width="600">
|
||||
<img src="/docs/assets/theme-dracula-dark.png" alt="Dracula theme" width="600">
|
||||
|
||||
### GitHub
|
||||
|
||||
<img src="/assets/theme-github.png" alt="GitHub theme" width="600">
|
||||
<img src="/docs/assets/theme-github-dark.png" alt="GitHub theme" width="600">
|
||||
|
||||
### Holiday
|
||||
|
||||
<img src="/docs/assets/theme-holiday-dark.png" alt="Holiday theme" width="600">
|
||||
|
||||
### Shades Of Purple
|
||||
|
||||
<img src="/docs/assets/theme-shades-of-purple-dark.png" alt="Shades Of Purple theme" width="600">
|
||||
|
||||
### Solarized Dark
|
||||
|
||||
<img src="/assets/theme-solarized-dark.png" alt="Solarized Dark theme" width="600">
|
||||
<img src="/docs/assets/theme-solarized-dark.png" alt="Solarized Dark theme" width="600">
|
||||
|
||||
## Light themes
|
||||
|
||||
### ANSI Light
|
||||
|
||||
<img src="/assets/theme-ansi-light.png" alt="ANSI Light theme" width="600">
|
||||
<img src="/docs/assets/theme-ansi-light.png" alt="ANSI Light theme" width="600">
|
||||
|
||||
### Ayu Light
|
||||
|
||||
<img src="/assets/theme-ayu-light.png" alt="Ayu Light theme" width="600">
|
||||
<img src="/docs/assets/theme-ayu-light.png" alt="Ayu Light theme" width="600">
|
||||
|
||||
### Default Light
|
||||
|
||||
<img src="/assets/theme-default-light.png" alt="Default Light theme" width="600">
|
||||
<img src="/docs/assets/theme-default-light.png" alt="Default Light theme" width="600">
|
||||
|
||||
### GitHub Light
|
||||
|
||||
<img src="/assets/theme-github-light.png" alt="GitHub Light theme" width="600">
|
||||
<img src="/docs/assets/theme-github-light.png" alt="GitHub Light theme" width="600">
|
||||
|
||||
### Google Code
|
||||
|
||||
<img src="/assets/theme-google-light.png" alt="Google Code theme" width="600">
|
||||
<img src="/docs/assets/theme-google-light.png" alt="Google Code theme" width="600">
|
||||
|
||||
### Solarized Light
|
||||
|
||||
<img src="/assets/theme-solarized-light.png" alt="Solarized Light theme" width="600">
|
||||
<img src="/docs/assets/theme-solarized-light.png" alt="Solarized Light theme" width="600">
|
||||
|
||||
### Xcode
|
||||
|
||||
<img src="/assets/theme-xcode-light.png" alt="Xcode Light theme" width="600">
|
||||
<img src="/docs/assets/theme-xcode-light.png" alt="Xcode Light theme" width="600">
|
||||
|
||||
@@ -103,7 +103,7 @@ describe('ApiAuthDialog', () => {
|
||||
|
||||
it.each([
|
||||
{
|
||||
keyName: 'return',
|
||||
keyName: 'enter',
|
||||
sequence: '\r',
|
||||
expectedCall: onSubmit,
|
||||
args: ['submitted-key'],
|
||||
|
||||
@@ -210,7 +210,7 @@ export const ConfigExtensionDialog: React.FC<ConfigExtensionDialogProps> = ({
|
||||
useKeypress(
|
||||
(key: Key) => {
|
||||
if (state.type === 'ASK_CONFIRMATION') {
|
||||
if (key.name === 'y' || key.name === 'return') {
|
||||
if (key.name === 'y' || key.name === 'enter') {
|
||||
state.resolve(true);
|
||||
return true;
|
||||
}
|
||||
@@ -220,7 +220,7 @@ export const ConfigExtensionDialog: React.FC<ConfigExtensionDialogProps> = ({
|
||||
}
|
||||
}
|
||||
if (state.type === 'DONE' || state.type === 'ERROR') {
|
||||
if (key.name === 'return' || key.name === 'escape') {
|
||||
if (key.name === 'enter' || key.name === 'escape') {
|
||||
onClose();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -106,6 +106,7 @@ export interface FooterRowItem {
|
||||
flexGrow?: number;
|
||||
flexShrink?: number;
|
||||
isFocused?: boolean;
|
||||
alignItems?: 'flex-start' | 'center' | 'flex-end';
|
||||
}
|
||||
|
||||
const COLUMN_GAP = 3;
|
||||
@@ -117,10 +118,17 @@ export const FooterRow: React.FC<{
|
||||
const elements: React.ReactNode[] = [];
|
||||
|
||||
items.forEach((item, idx) => {
|
||||
if (idx > 0 && !showLabels) {
|
||||
if (idx > 0) {
|
||||
elements.push(
|
||||
<Box key={`sep-${item.key}`} height={1}>
|
||||
<Text color={theme.ui.comment}> · </Text>
|
||||
<Box
|
||||
key={`sep-${item.key}`}
|
||||
flexGrow={1}
|
||||
flexShrink={1}
|
||||
minWidth={showLabels ? COLUMN_GAP : 3}
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
>
|
||||
{!showLabels && <Text color={theme.ui.comment}> · </Text>}
|
||||
</Box>,
|
||||
);
|
||||
}
|
||||
@@ -131,6 +139,7 @@ export const FooterRow: React.FC<{
|
||||
flexDirection="column"
|
||||
flexGrow={item.flexGrow ?? 0}
|
||||
flexShrink={item.flexShrink ?? 1}
|
||||
alignItems={item.alignItems}
|
||||
backgroundColor={item.isFocused ? theme.background.focus : undefined}
|
||||
>
|
||||
{showLabels && (
|
||||
@@ -148,12 +157,7 @@ export const FooterRow: React.FC<{
|
||||
});
|
||||
|
||||
return (
|
||||
<Box
|
||||
flexDirection="row"
|
||||
flexWrap="nowrap"
|
||||
width="100%"
|
||||
columnGap={showLabels ? COLUMN_GAP : 0}
|
||||
>
|
||||
<Box flexDirection="row" flexWrap="nowrap" width="100%">
|
||||
{elements}
|
||||
</Box>
|
||||
);
|
||||
@@ -441,8 +445,9 @@ export const Footer: React.FC = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const rowItems: FooterRowItem[] = columnsToRender.map((col) => {
|
||||
const rowItems: FooterRowItem[] = columnsToRender.map((col, index) => {
|
||||
const isWorkspace = col.id === 'workspace';
|
||||
const isLast = index === columnsToRender.length - 1;
|
||||
|
||||
// Calculate exact space available for growth to prevent over-estimation truncation
|
||||
const otherItemsWidth = columnsToRender
|
||||
@@ -464,8 +469,10 @@ export const Footer: React.FC = () => {
|
||||
key: col.id,
|
||||
header: col.header,
|
||||
element: col.element(estimatedWidth),
|
||||
flexGrow: isWorkspace ? 1 : 0,
|
||||
flexGrow: 0,
|
||||
flexShrink: isWorkspace ? 1 : 0,
|
||||
alignItems:
|
||||
isLast && !droppedAny && index > 0 ? 'flex-end' : 'flex-start',
|
||||
};
|
||||
});
|
||||
|
||||
@@ -476,6 +483,7 @@ export const Footer: React.FC = () => {
|
||||
element: <Text color={theme.ui.comment}>…</Text>,
|
||||
flexGrow: 0,
|
||||
flexShrink: 0,
|
||||
alignItems: 'flex-end',
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import { renderWithProviders } from '../../test-utils/render.js';
|
||||
import { waitFor } from '../../test-utils/async.js';
|
||||
import { FooterConfigDialog } from './FooterConfigDialog.js';
|
||||
import { createMockSettings } from '../../test-utils/settings.js';
|
||||
import { ALL_ITEMS } from '../../config/footerItems.js';
|
||||
import { act } from 'react';
|
||||
|
||||
describe('<FooterConfigDialog />', () => {
|
||||
@@ -213,4 +214,60 @@ describe('<FooterConfigDialog />', () => {
|
||||
expect(bIdxAfter).toBeLessThan(wIdxAfter);
|
||||
});
|
||||
});
|
||||
|
||||
it('updates the preview when Show footer labels is toggled off', async () => {
|
||||
const settings = createMockSettings();
|
||||
const renderResult = renderWithProviders(
|
||||
<FooterConfigDialog onClose={mockOnClose} />,
|
||||
{ settings },
|
||||
);
|
||||
|
||||
const { lastFrame, stdin, waitUntilReady } = renderResult;
|
||||
await waitUntilReady();
|
||||
|
||||
// By default labels are on
|
||||
expect(lastFrame()).toContain('workspace (/directory)');
|
||||
expect(lastFrame()).toContain('sandbox');
|
||||
expect(lastFrame()).toContain('/model');
|
||||
|
||||
// Move to "Show footer labels" (which is the second to last item)
|
||||
for (let i = 0; i < ALL_ITEMS.length; i++) {
|
||||
act(() => {
|
||||
stdin.write('\u001b[B'); // Down arrow
|
||||
});
|
||||
}
|
||||
|
||||
await waitFor(() => {
|
||||
expect(lastFrame()).toMatch(/> \[✓\] Show footer labels/);
|
||||
});
|
||||
|
||||
// Toggle it off
|
||||
act(() => {
|
||||
stdin.write('\r');
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(lastFrame()).toMatch(/> \[ \] Show footer labels/);
|
||||
// The headers should no longer be in the preview
|
||||
expect(lastFrame()).not.toContain('workspace (/directory)');
|
||||
expect(lastFrame()).not.toContain('/model');
|
||||
|
||||
// We can't strictly search for "sandbox" because the menu item also says "sandbox".
|
||||
// Let's assert that the spacer dots are now present in the preview instead.
|
||||
const previewLine =
|
||||
lastFrame()
|
||||
.split('\n')
|
||||
.find((line) => line.includes('Preview:')) || '';
|
||||
const nextLine =
|
||||
lastFrame().split('\n')[
|
||||
lastFrame().split('\n').indexOf(previewLine) + 1
|
||||
] || '';
|
||||
expect(nextLine).toContain('·');
|
||||
expect(nextLine).toContain('~/project/path');
|
||||
expect(nextLine).toContain('docker');
|
||||
expect(nextLine).toContain('97%');
|
||||
});
|
||||
|
||||
await expect(renderResult).toMatchSvgSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -266,7 +266,7 @@ export const FooterConfigDialog: React.FC<FooterConfigDialogProps> = ({
|
||||
key: id,
|
||||
header: ALL_ITEMS.find((i) => i.id === id)?.header ?? id,
|
||||
element: mockData[id],
|
||||
flexGrow: 1,
|
||||
flexGrow: 0,
|
||||
isFocused: id === focusKey,
|
||||
}));
|
||||
|
||||
|
||||
@@ -972,7 +972,7 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
||||
if (targetIndex < completion.suggestions.length) {
|
||||
const suggestion = completion.suggestions[targetIndex];
|
||||
|
||||
const isEnterKey = key.name === 'return' && !key.ctrl;
|
||||
const isEnterKey = key.name === 'enter' && !key.ctrl;
|
||||
|
||||
if (isEnterKey && shellModeActive) {
|
||||
if (hasUserNavigatedSuggestions.current) {
|
||||
|
||||
@@ -324,7 +324,7 @@ describe('SessionBrowser component', () => {
|
||||
await waitUntilReady();
|
||||
|
||||
// Press Enter.
|
||||
triggerKey({ name: 'return', sequence: '\r' });
|
||||
triggerKey({ name: 'enter', sequence: '\r' });
|
||||
await waitUntilReady();
|
||||
|
||||
expect(onResumeSession).toHaveBeenCalledTimes(1);
|
||||
@@ -367,7 +367,7 @@ describe('SessionBrowser component', () => {
|
||||
await waitUntilReady();
|
||||
|
||||
// Active selection is at 0 (current session).
|
||||
triggerKey({ name: 'return', sequence: '\r' });
|
||||
triggerKey({ name: 'enter', sequence: '\r' });
|
||||
await waitUntilReady();
|
||||
expect(onResumeSession).not.toHaveBeenCalled();
|
||||
|
||||
|
||||
@@ -873,7 +873,7 @@ export const useSessionBrowserInput = (
|
||||
|
||||
// Handling regardless of search mode.
|
||||
if (
|
||||
key.name === 'return' &&
|
||||
key.name === 'enter' &&
|
||||
state.filteredAndSortedSessions[state.activeIndex]
|
||||
) {
|
||||
const selectedSession =
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`<Footer /> > displays "Limit reached" message when remaining is 0 1`] = `
|
||||
" workspace (/directory) sandbox /model /stats
|
||||
~/project/foo/bar/and/some/more/directories/to/make/it/long no sandbox gemini-pro limit reached
|
||||
" workspace (/directory) sandbox /model /stats
|
||||
~/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`] = `
|
||||
" workspace (/directory) sandbox /model /stats
|
||||
~/project/foo/bar/and/some/more/directories/to/make/it/long no sandbox gemini-pro 85%
|
||||
" workspace (/directory) sandbox /model /stats
|
||||
~/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`] = `
|
||||
" workspace (/directory) sandbox /model context
|
||||
...me/more/directories/to/make/it/long no sandbox gemini-pro 14%
|
||||
" workspace (/directory) sandbox /model context
|
||||
...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`] = `
|
||||
" workspace (/directory) sandbox /model context
|
||||
~/project/foo/bar/and/some/more/directories/to/make/it/long no sandbox gemini-pro 14% used
|
||||
" workspace (/directory) sandbox /model context
|
||||
~/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 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
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<Footer /> > hides the usage indicator when usage is not near limit 1`] = `
|
||||
" workspace (/directory) sandbox /model /stats
|
||||
~/project/foo/bar/and/some/more/directories/to/make/it/long no sandbox gemini-pro 15%
|
||||
" workspace (/directory) sandbox /model /stats
|
||||
~/project/foo/bar/and/some/more/directories/to/make/it/long no sandbox gemini-pro 15%
|
||||
"
|
||||
`;
|
||||
|
||||
@@ -125,28 +125,27 @@
|
||||
<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="297" y="597" fill="#afafaf" textLength="54" lengthAdjust="spacingAndGlyphs">branch</text>
|
||||
<text x="405" y="597" fill="#afafaf" textLength="63" lengthAdjust="spacingAndGlyphs">sandbox</text>
|
||||
<text x="513" y="597" fill="#afafaf" textLength="54" lengthAdjust="spacingAndGlyphs">/model</text>
|
||||
<text x="693" y="597" fill="#afafaf" textLength="54" lengthAdjust="spacingAndGlyphs">/stats</text>
|
||||
<rect x="801" y="595" width="36" height="17" fill="#001a00" />
|
||||
<text x="801" y="597" fill="#ffffff" textLength="36" lengthAdjust="spacingAndGlyphs">diff</text>
|
||||
<rect x="837" y="595" width="18" 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>
|
||||
<text x="297" y="614" fill="#ffffff" textLength="36" lengthAdjust="spacingAndGlyphs">main</text>
|
||||
<text x="405" y="614" fill="#00cd00" textLength="54" lengthAdjust="spacingAndGlyphs">docker</text>
|
||||
<text x="513" y="614" fill="#ffffff" textLength="126" lengthAdjust="spacingAndGlyphs">gemini-2.5-pro</text>
|
||||
<text x="693" y="614" fill="#ffffff" textLength="27" lengthAdjust="spacingAndGlyphs">97%</text>
|
||||
<rect x="801" y="612" width="27" height="17" fill="#001a00" />
|
||||
<text x="801" y="614" fill="#d7ffd7" textLength="27" lengthAdjust="spacingAndGlyphs">+12</text>
|
||||
<rect x="828" y="612" width="9" height="17" fill="#001a00" />
|
||||
<rect x="837" y="612" width="18" height="17" fill="#001a00" />
|
||||
<text x="837" y="614" fill="#ff87af" textLength="18" lengthAdjust="spacingAndGlyphs">-4</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>
|
||||
|
||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
@@ -126,22 +126,21 @@
|
||||
<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="324" y="597" fill="#afafaf" textLength="54" lengthAdjust="spacingAndGlyphs">branch</text>
|
||||
<text x="459" y="597" fill="#afafaf" textLength="63" lengthAdjust="spacingAndGlyphs">sandbox</text>
|
||||
<text x="594" y="597" fill="#afafaf" textLength="54" lengthAdjust="spacingAndGlyphs">/model</text>
|
||||
<text x="801" 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>
|
||||
<rect x="171" y="612" width="72" height="17" fill="#001a00" />
|
||||
<text x="324" y="614" fill="#ffffff" textLength="36" lengthAdjust="spacingAndGlyphs">main</text>
|
||||
<text x="459" y="614" fill="#00cd00" textLength="54" lengthAdjust="spacingAndGlyphs">docker</text>
|
||||
<text x="594" y="614" fill="#ffffff" textLength="126" lengthAdjust="spacingAndGlyphs">gemini-2.5-pro</text>
|
||||
<text x="801" 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>
|
||||
|
||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
@@ -0,0 +1,143 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="683" viewBox="0 0 920 683">
|
||||
<style>
|
||||
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
|
||||
</style>
|
||||
<rect width="920" height="683" 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>
|
||||
<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>
|
||||
<rect x="27" y="442" width="9" height="17" fill="#001a00" />
|
||||
<text x="27" y="444" fill="#00cd00" textLength="9" lengthAdjust="spacingAndGlyphs">></text>
|
||||
<rect x="36" y="442" width="9" height="17" fill="#001a00" />
|
||||
<rect x="45" y="442" width="27" height="17" fill="#001a00" />
|
||||
<text x="45" y="444" fill="#afafaf" textLength="27" lengthAdjust="spacingAndGlyphs">[ ]</text>
|
||||
<rect x="72" y="442" width="171" height="17" fill="#001a00" />
|
||||
<text x="72" y="444" fill="#00cd00" textLength="171" lengthAdjust="spacingAndGlyphs"> Show footer labels</text>
|
||||
<rect x="243" y="442" width="630" height="17" fill="#001a00" />
|
||||
<text x="891" y="444" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="461" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<rect x="27" y="459" width="846" height="17" fill="#001a00" />
|
||||
<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="126" lengthAdjust="spacingAndGlyphs">~/project/path</text>
|
||||
<text x="207" y="597" fill="#afafaf" textLength="27" lengthAdjust="spacingAndGlyphs"> · </text>
|
||||
<text x="279" y="597" fill="#afafaf" textLength="36" lengthAdjust="spacingAndGlyphs">main</text>
|
||||
<text x="351" y="597" fill="#afafaf" textLength="27" lengthAdjust="spacingAndGlyphs"> · </text>
|
||||
<text x="432" y="597" fill="#00cd00" textLength="54" lengthAdjust="spacingAndGlyphs">docker</text>
|
||||
<text x="522" y="597" fill="#afafaf" textLength="27" lengthAdjust="spacingAndGlyphs"> · </text>
|
||||
<text x="594" y="597" fill="#afafaf" textLength="126" lengthAdjust="spacingAndGlyphs">gemini-2.5-pro</text>
|
||||
<text x="756" y="597" fill="#afafaf" textLength="27" lengthAdjust="spacingAndGlyphs"> · </text>
|
||||
<text x="828" y="597" fill="#afafaf" textLength="27" lengthAdjust="spacingAndGlyphs">97%</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="846" 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="891" y="631" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs">│</text>
|
||||
<text x="0" y="648" fill="#333333" textLength="900" lengthAdjust="spacingAndGlyphs">╰──────────────────────────────────────────────────────────────────────────────────────────────────╯</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 15 KiB |
@@ -36,8 +36,8 @@ exports[`<FooterConfigDialog /> > highlights the active item in the preview 1`]
|
||||
│ │
|
||||
│ ┌────────────────────────────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Preview: │ │
|
||||
│ │ workspace (/directory) branch sandbox /model /stats diff │ │
|
||||
│ │ ~/project/path main docker gemini-2.5-pro 97% +12 -4 │ │
|
||||
│ │ workspace (/directory) branch sandbox /model /stats diff │ │
|
||||
│ │ ~/project/path main docker gemini-2.5-pro 97% +12 -4 │ │
|
||||
│ └────────────────────────────────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
@@ -79,8 +79,8 @@ exports[`<FooterConfigDialog /> > renders correctly with default settings 1`] =
|
||||
│ │
|
||||
│ ┌────────────────────────────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Preview: │ │
|
||||
│ │ workspace (/directory) branch sandbox /model /stats │ │
|
||||
│ │ ~/project/path main docker gemini-2.5-pro 97% │ │
|
||||
│ │ workspace (/directory) branch sandbox /model /stats │ │
|
||||
│ │ ~/project/path main docker gemini-2.5-pro 97% │ │
|
||||
│ └────────────────────────────────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
@@ -123,8 +123,50 @@ exports[`<FooterConfigDialog /> > renders correctly with default settings 2`] =
|
||||
│ │
|
||||
│ ┌────────────────────────────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Preview: │ │
|
||||
│ │ workspace (/directory) branch sandbox /model /stats │ │
|
||||
│ │ ~/project/path main docker gemini-2.5-pro 97% │ │
|
||||
│ │ workspace (/directory) branch sandbox /model /stats │ │
|
||||
│ │ ~/project/path main docker gemini-2.5-pro 97% │ │
|
||||
│ └────────────────────────────────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
`;
|
||||
|
||||
exports[`<FooterConfigDialog /> > updates the preview when Show footer labels is toggled off 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: │ │
|
||||
│ │ ~/project/path · main · docker · gemini-2.5-pro · 97% │ │
|
||||
│ └────────────────────────────────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
|
||||
@@ -287,7 +287,7 @@ describe('TextInput', () => {
|
||||
|
||||
await act(async () => {
|
||||
keypressHandler({
|
||||
name: 'return',
|
||||
name: 'enter',
|
||||
shift: false,
|
||||
alt: false,
|
||||
ctrl: false,
|
||||
@@ -314,7 +314,7 @@ describe('TextInput', () => {
|
||||
|
||||
await act(async () => {
|
||||
keypressHandler({
|
||||
name: 'return',
|
||||
name: 'enter',
|
||||
shift: false,
|
||||
alt: false,
|
||||
ctrl: false,
|
||||
@@ -339,7 +339,7 @@ describe('TextInput', () => {
|
||||
|
||||
await act(async () => {
|
||||
keypressHandler({
|
||||
name: 'return',
|
||||
name: 'enter',
|
||||
shift: false,
|
||||
alt: false,
|
||||
ctrl: false,
|
||||
|
||||
@@ -1534,7 +1534,7 @@ describe('useTextBuffer', () => {
|
||||
const { result } = renderHook(() => useTextBuffer({ viewport }));
|
||||
act(() => {
|
||||
result.current.handleInput({
|
||||
name: 'return',
|
||||
name: 'enter',
|
||||
shift: false,
|
||||
alt: false,
|
||||
ctrl: false,
|
||||
@@ -1790,7 +1790,7 @@ describe('useTextBuffer', () => {
|
||||
const { result } = renderHook(() => useTextBuffer({ viewport }));
|
||||
act(() => {
|
||||
result.current.handleInput({
|
||||
name: 'return',
|
||||
name: 'enter',
|
||||
shift: true,
|
||||
alt: false,
|
||||
ctrl: false,
|
||||
@@ -2291,7 +2291,7 @@ Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots
|
||||
);
|
||||
act(() => {
|
||||
result.current.handleInput({
|
||||
name: 'return',
|
||||
name: 'enter',
|
||||
shift: false,
|
||||
alt: false,
|
||||
ctrl: false,
|
||||
|
||||
@@ -109,7 +109,7 @@ describe('KeypressContext', () => {
|
||||
|
||||
expect(keyHandler).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
name: 'return',
|
||||
name: 'enter',
|
||||
shift: false,
|
||||
ctrl: false,
|
||||
cmd: false,
|
||||
@@ -124,7 +124,7 @@ describe('KeypressContext', () => {
|
||||
|
||||
expect(keyHandler).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
name: 'return',
|
||||
name: 'enter',
|
||||
shift: true,
|
||||
ctrl: false,
|
||||
cmd: false,
|
||||
@@ -157,7 +157,7 @@ describe('KeypressContext', () => {
|
||||
|
||||
expect(keyHandler).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
name: 'return',
|
||||
name: 'enter',
|
||||
...expected,
|
||||
}),
|
||||
);
|
||||
@@ -186,7 +186,7 @@ describe('KeypressContext', () => {
|
||||
|
||||
expect(keyHandler).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
name: 'return',
|
||||
name: 'enter',
|
||||
shift: false,
|
||||
alt: true,
|
||||
ctrl: false,
|
||||
@@ -225,7 +225,7 @@ describe('KeypressContext', () => {
|
||||
|
||||
expect(keyHandler).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({
|
||||
name: 'return',
|
||||
name: 'enter',
|
||||
sequence: '\r',
|
||||
insertable: true,
|
||||
shift: true,
|
||||
@@ -247,7 +247,7 @@ describe('KeypressContext', () => {
|
||||
|
||||
expect(keyHandler).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({
|
||||
name: 'return',
|
||||
name: 'enter',
|
||||
shift: false,
|
||||
alt: false,
|
||||
ctrl: false,
|
||||
@@ -647,8 +647,8 @@ describe('KeypressContext', () => {
|
||||
describe('Parameterized functional keys', () => {
|
||||
it.each([
|
||||
// ModifyOtherKeys
|
||||
{ sequence: `\x1b[27;2;13~`, expected: { name: 'return', shift: true } },
|
||||
{ sequence: `\x1b[27;5;13~`, expected: { name: 'return', ctrl: true } },
|
||||
{ sequence: `\x1b[27;2;13~`, expected: { name: 'enter', shift: true } },
|
||||
{ sequence: `\x1b[27;5;13~`, expected: { name: 'enter', ctrl: true } },
|
||||
{ sequence: `\x1b[27;5;9~`, expected: { name: 'tab', ctrl: true } },
|
||||
{
|
||||
sequence: `\x1b[27;6;9~`,
|
||||
@@ -1133,7 +1133,7 @@ describe('KeypressContext', () => {
|
||||
expect(keyHandler).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expect.objectContaining({
|
||||
name: 'return',
|
||||
name: 'enter',
|
||||
}),
|
||||
);
|
||||
expect(keyHandler).toHaveBeenNthCalledWith(
|
||||
|
||||
@@ -92,11 +92,11 @@ const KEY_INFO_MAP: Record<
|
||||
'[[5~': { name: 'pageup' },
|
||||
'[[6~': { name: 'pagedown' },
|
||||
'[9u': { name: 'tab' },
|
||||
'[13u': { name: 'return' },
|
||||
'[13u': { name: 'enter' },
|
||||
'[27u': { name: 'escape' },
|
||||
'[32u': { name: 'space' },
|
||||
'[127u': { name: 'backspace' },
|
||||
'[57414u': { name: 'return' }, // Numpad Enter
|
||||
'[57414u': { name: 'enter' }, // Numpad Enter
|
||||
'[a': { name: 'up', shift: true },
|
||||
'[b': { name: 'down', shift: true },
|
||||
'[c': { name: 'right', shift: true },
|
||||
@@ -186,10 +186,10 @@ function bufferFastReturn(keypressHandler: KeypressHandler): KeypressHandler {
|
||||
let lastKeyTime = 0;
|
||||
return (key: Key) => {
|
||||
const now = Date.now();
|
||||
if (key.name === 'return' && now - lastKeyTime <= FAST_RETURN_TIMEOUT) {
|
||||
if (key.name === 'enter' && now - lastKeyTime <= FAST_RETURN_TIMEOUT) {
|
||||
keypressHandler({
|
||||
...key,
|
||||
name: 'return',
|
||||
name: 'enter',
|
||||
shift: true, // to make it a newline, not a submission
|
||||
alt: false,
|
||||
ctrl: false,
|
||||
@@ -232,7 +232,7 @@ function bufferBackslashEnter(
|
||||
|
||||
if (nextKey === null) {
|
||||
keypressHandler(key);
|
||||
} else if (nextKey.name === 'return') {
|
||||
} else if (nextKey.name === 'enter') {
|
||||
keypressHandler({
|
||||
...nextKey,
|
||||
shift: true,
|
||||
@@ -582,11 +582,11 @@ function* emitKeys(
|
||||
}
|
||||
} else if (ch === '\r') {
|
||||
// carriage return
|
||||
name = 'return';
|
||||
name = 'enter';
|
||||
alt = escaped;
|
||||
} else if (escaped && ch === '\n') {
|
||||
// Alt+Enter (linefeed), should be consistent with carriage return
|
||||
name = 'return';
|
||||
name = 'enter';
|
||||
alt = escaped;
|
||||
} else if (ch === '\t') {
|
||||
// tab
|
||||
|
||||
@@ -119,7 +119,7 @@ describe(`useKeypress`, () => {
|
||||
|
||||
it('should correctly identify alt+enter (meta key)', () => {
|
||||
renderKeypressHook(true);
|
||||
const key = { name: 'return', sequence: '\x1B\r' };
|
||||
const key = { name: 'enter', sequence: '\x1B\r' };
|
||||
act(() => stdin.write(key.sequence));
|
||||
expect(onKeypress).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
|
||||
@@ -356,7 +356,7 @@ describe('useSelectionList', () => {
|
||||
initialIndex: 2,
|
||||
onSelect: mockOnSelect,
|
||||
});
|
||||
pressKey('return');
|
||||
pressKey('enter');
|
||||
await waitUntilReady();
|
||||
expect(mockOnSelect).toHaveBeenCalledTimes(1);
|
||||
expect(mockOnSelect).toHaveBeenCalledWith('C');
|
||||
@@ -371,7 +371,7 @@ describe('useSelectionList', () => {
|
||||
act(() => result.current.setActiveIndex(1));
|
||||
await waitUntilReady();
|
||||
|
||||
pressKey('return');
|
||||
pressKey('enter');
|
||||
await waitUntilReady();
|
||||
expect(mockOnSelect).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -415,7 +415,7 @@ describe('useSelectionList', () => {
|
||||
await waitUntilReady();
|
||||
// 3. Press Enter. Should select D.
|
||||
act(() => {
|
||||
press('return');
|
||||
press('enter');
|
||||
});
|
||||
await waitUntilReady();
|
||||
|
||||
@@ -459,7 +459,7 @@ describe('useSelectionList', () => {
|
||||
// All presses happen in same render cycle - React batches the state updates
|
||||
press('down'); // Should move 0 (A) -> 2 (C)
|
||||
press('down'); // Should move 2 (C) -> 3 (D)
|
||||
press('return'); // Should select D
|
||||
press('enter'); // Should select D
|
||||
});
|
||||
await waitUntilReady();
|
||||
|
||||
@@ -759,7 +759,7 @@ describe('useSelectionList', () => {
|
||||
pressNumber('1');
|
||||
await waitUntilReady();
|
||||
|
||||
pressKey('return');
|
||||
pressKey('enter');
|
||||
await waitUntilReady();
|
||||
expect(mockOnSelect).toHaveBeenCalledTimes(1);
|
||||
|
||||
|
||||
@@ -396,7 +396,7 @@ export function useVim(buffer: TextBuffer, onSubmit?: (value: string) => void) {
|
||||
// In INSERT mode, let InputPrompt handle completion keys and special commands
|
||||
if (
|
||||
normalizedKey.name === 'tab' ||
|
||||
(normalizedKey.name === 'return' && !normalizedKey.ctrl) ||
|
||||
(normalizedKey.name === 'enter' && !normalizedKey.ctrl) ||
|
||||
normalizedKey.name === 'up' ||
|
||||
normalizedKey.name === 'down' ||
|
||||
(normalizedKey.ctrl && normalizedKey.name === 'r')
|
||||
@@ -424,7 +424,7 @@ export function useVim(buffer: TextBuffer, onSubmit?: (value: string) => void) {
|
||||
|
||||
// Special handling for Enter key to allow command submission (lower priority than completion)
|
||||
if (
|
||||
normalizedKey.name === 'return' &&
|
||||
normalizedKey.name === 'enter' &&
|
||||
!normalizedKey.alt &&
|
||||
!normalizedKey.ctrl &&
|
||||
!normalizedKey.cmd
|
||||
|
||||
@@ -80,8 +80,8 @@ describe('KeyBinding', () => {
|
||||
});
|
||||
|
||||
it('should handle named keys with modifiers', () => {
|
||||
const binding = new KeyBinding('ctrl+return');
|
||||
expect(binding.key).toBe('return');
|
||||
const binding = new KeyBinding('ctrl+enter');
|
||||
expect(binding.key).toBe('enter');
|
||||
expect(binding.ctrl).toBe(true);
|
||||
});
|
||||
|
||||
|
||||
@@ -149,11 +149,9 @@ export class KeyBinding {
|
||||
'numpad_subtract',
|
||||
'numpad_decimal',
|
||||
'numpad_divide',
|
||||
// Gemini CLI legacy/internal support
|
||||
'return',
|
||||
]);
|
||||
|
||||
/** The key name (e.g., 'a', 'return', 'tab', 'escape') */
|
||||
/** The key name (e.g., 'a', 'enter', 'tab', 'escape') */
|
||||
readonly key: string;
|
||||
readonly shift: boolean;
|
||||
readonly alt: boolean;
|
||||
@@ -238,7 +236,7 @@ export type KeyBindingConfig = {
|
||||
*/
|
||||
export const defaultKeyBindings: KeyBindingConfig = {
|
||||
// Basic Controls
|
||||
[Command.RETURN]: [new KeyBinding('return')],
|
||||
[Command.RETURN]: [new KeyBinding('enter')],
|
||||
[Command.ESCAPE]: [new KeyBinding('escape'), new KeyBinding('ctrl+[')],
|
||||
[Command.QUIT]: [new KeyBinding('ctrl+c')],
|
||||
[Command.EXIT]: [new KeyBinding('ctrl+d')],
|
||||
@@ -308,7 +306,7 @@ export const defaultKeyBindings: KeyBindingConfig = {
|
||||
[Command.HISTORY_UP]: [new KeyBinding('ctrl+p')],
|
||||
[Command.HISTORY_DOWN]: [new KeyBinding('ctrl+n')],
|
||||
[Command.REVERSE_SEARCH]: [new KeyBinding('ctrl+r')],
|
||||
[Command.SUBMIT_REVERSE_SEARCH]: [new KeyBinding('return')],
|
||||
[Command.SUBMIT_REVERSE_SEARCH]: [new KeyBinding('enter')],
|
||||
[Command.ACCEPT_SUGGESTION_REVERSE_SEARCH]: [new KeyBinding('tab')],
|
||||
|
||||
// Navigation
|
||||
@@ -325,10 +323,7 @@ export const defaultKeyBindings: KeyBindingConfig = {
|
||||
[Command.DIALOG_PREV]: [new KeyBinding('shift+tab')],
|
||||
|
||||
// Suggestions & Completions
|
||||
[Command.ACCEPT_SUGGESTION]: [
|
||||
new KeyBinding('tab'),
|
||||
new KeyBinding('return'),
|
||||
],
|
||||
[Command.ACCEPT_SUGGESTION]: [new KeyBinding('tab'), new KeyBinding('enter')],
|
||||
[Command.COMPLETION_UP]: [new KeyBinding('up'), new KeyBinding('ctrl+p')],
|
||||
[Command.COMPLETION_DOWN]: [new KeyBinding('down'), new KeyBinding('ctrl+n')],
|
||||
[Command.EXPAND_SUGGESTION]: [new KeyBinding('right')],
|
||||
@@ -336,12 +331,12 @@ export const defaultKeyBindings: KeyBindingConfig = {
|
||||
|
||||
// Text Input
|
||||
// Must also exclude shift to allow shift+enter for newline
|
||||
[Command.SUBMIT]: [new KeyBinding('return')],
|
||||
[Command.SUBMIT]: [new KeyBinding('enter')],
|
||||
[Command.NEWLINE]: [
|
||||
new KeyBinding('ctrl+return'),
|
||||
new KeyBinding('cmd+return'),
|
||||
new KeyBinding('alt+return'),
|
||||
new KeyBinding('shift+return'),
|
||||
new KeyBinding('ctrl+enter'),
|
||||
new KeyBinding('cmd+enter'),
|
||||
new KeyBinding('alt+enter'),
|
||||
new KeyBinding('shift+enter'),
|
||||
new KeyBinding('ctrl+j'),
|
||||
],
|
||||
[Command.OPEN_EXTERNAL_EDITOR]: [new KeyBinding('ctrl+x')],
|
||||
@@ -366,7 +361,7 @@ export const defaultKeyBindings: KeyBindingConfig = {
|
||||
[Command.UNFOCUS_BACKGROUND_SHELL_LIST]: [new KeyBinding('tab')],
|
||||
[Command.SHOW_BACKGROUND_SHELL_UNFOCUS_WARNING]: [new KeyBinding('tab')],
|
||||
[Command.SHOW_SHELL_INPUT_UNFOCUS_WARNING]: [new KeyBinding('tab')],
|
||||
[Command.BACKGROUND_SHELL_SELECT]: [new KeyBinding('return')],
|
||||
[Command.BACKGROUND_SHELL_SELECT]: [new KeyBinding('enter')],
|
||||
[Command.BACKGROUND_SHELL_ESCAPE]: [new KeyBinding('escape')],
|
||||
[Command.SHOW_MORE_LINES]: [new KeyBinding('ctrl+o')],
|
||||
[Command.EXPAND_PASTE]: [new KeyBinding('ctrl+o')],
|
||||
|
||||
@@ -31,7 +31,7 @@ describe('keyMatchers', () => {
|
||||
// Basic bindings
|
||||
{
|
||||
command: Command.RETURN,
|
||||
positive: [createKey('return')],
|
||||
positive: [createKey('enter')],
|
||||
negative: [createKey('r')],
|
||||
},
|
||||
{
|
||||
@@ -270,8 +270,8 @@ describe('keyMatchers', () => {
|
||||
// Auto-completion
|
||||
{
|
||||
command: Command.ACCEPT_SUGGESTION,
|
||||
positive: [createKey('tab'), createKey('return')],
|
||||
negative: [createKey('return', { ctrl: true }), createKey('space')],
|
||||
positive: [createKey('tab'), createKey('enter')],
|
||||
negative: [createKey('enter', { ctrl: true }), createKey('space')],
|
||||
},
|
||||
{
|
||||
command: Command.COMPLETION_UP,
|
||||
@@ -287,21 +287,21 @@ describe('keyMatchers', () => {
|
||||
// Text input
|
||||
{
|
||||
command: Command.SUBMIT,
|
||||
positive: [createKey('return')],
|
||||
positive: [createKey('enter')],
|
||||
negative: [
|
||||
createKey('return', { ctrl: true }),
|
||||
createKey('return', { cmd: true }),
|
||||
createKey('return', { alt: true }),
|
||||
createKey('enter', { ctrl: true }),
|
||||
createKey('enter', { cmd: true }),
|
||||
createKey('enter', { alt: true }),
|
||||
],
|
||||
},
|
||||
{
|
||||
command: Command.NEWLINE,
|
||||
positive: [
|
||||
createKey('return', { ctrl: true }),
|
||||
createKey('return', { cmd: true }),
|
||||
createKey('return', { alt: true }),
|
||||
createKey('enter', { ctrl: true }),
|
||||
createKey('enter', { cmd: true }),
|
||||
createKey('enter', { alt: true }),
|
||||
],
|
||||
negative: [createKey('return'), createKey('n')],
|
||||
negative: [createKey('enter'), createKey('n')],
|
||||
},
|
||||
|
||||
// External tools
|
||||
@@ -382,14 +382,14 @@ describe('keyMatchers', () => {
|
||||
},
|
||||
{
|
||||
command: Command.SUBMIT_REVERSE_SEARCH,
|
||||
positive: [createKey('return')],
|
||||
negative: [createKey('return', { ctrl: true }), createKey('tab')],
|
||||
positive: [createKey('enter')],
|
||||
negative: [createKey('enter', { ctrl: true }), createKey('tab')],
|
||||
},
|
||||
{
|
||||
command: Command.ACCEPT_SUGGESTION_REVERSE_SEARCH,
|
||||
positive: [createKey('tab')],
|
||||
negative: [
|
||||
createKey('return'),
|
||||
createKey('enter'),
|
||||
createKey('space'),
|
||||
createKey('tab', { ctrl: true }),
|
||||
],
|
||||
|
||||
@@ -21,7 +21,7 @@ const SPECIAL_KEYS: Record<string, string> = {
|
||||
end: '\x1b[F',
|
||||
pageup: '\x1b[5~',
|
||||
pagedown: '\x1b[6~',
|
||||
return: '\r',
|
||||
enter: '\r',
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,7 +27,7 @@ describe('keybindingUtils', () => {
|
||||
},
|
||||
{
|
||||
name: 'named key (return)',
|
||||
binding: new KeyBinding('return'),
|
||||
binding: new KeyBinding('enter'),
|
||||
expected: {
|
||||
darwin: 'Enter',
|
||||
win32: 'Enter',
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
* Maps internal key names to user-friendly display names.
|
||||
*/
|
||||
const KEY_NAME_MAP: Record<string, string> = {
|
||||
return: 'Enter',
|
||||
enter: 'Enter',
|
||||
escape: 'Esc',
|
||||
backspace: 'Backspace',
|
||||
delete: 'Delete',
|
||||
|
||||
@@ -19,23 +19,24 @@ vi.mock('../scheduler/scheduler.js', () => ({
|
||||
}));
|
||||
|
||||
describe('agent-scheduler', () => {
|
||||
let mockConfig: Mocked<Config>;
|
||||
let mockToolRegistry: Mocked<ToolRegistry>;
|
||||
let mockMessageBus: Mocked<MessageBus>;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.mocked(Scheduler).mockClear();
|
||||
mockMessageBus = {} as Mocked<MessageBus>;
|
||||
mockToolRegistry = {
|
||||
getTool: vi.fn(),
|
||||
getMessageBus: vi.fn().mockReturnValue(mockMessageBus),
|
||||
} as unknown as Mocked<ToolRegistry>;
|
||||
mockConfig = {
|
||||
getMessageBus: vi.fn().mockReturnValue(mockMessageBus),
|
||||
toolRegistry: mockToolRegistry,
|
||||
} as unknown as Mocked<Config>;
|
||||
});
|
||||
|
||||
it('should create a scheduler with agent-specific config', async () => {
|
||||
const mockConfig = {
|
||||
getMessageBus: vi.fn().mockReturnValue(mockMessageBus),
|
||||
toolRegistry: mockToolRegistry,
|
||||
} as unknown as Mocked<Config>;
|
||||
|
||||
const requests: ToolCallRequestInfo[] = [
|
||||
{
|
||||
callId: 'call-1',
|
||||
@@ -68,8 +69,46 @@ describe('agent-scheduler', () => {
|
||||
}),
|
||||
);
|
||||
|
||||
// Verify that the scheduler's config has the overridden tool registry
|
||||
const schedulerConfig = vi.mocked(Scheduler).mock.calls[0][0].config;
|
||||
expect(schedulerConfig.toolRegistry).toBe(mockToolRegistry);
|
||||
});
|
||||
|
||||
it('should override toolRegistry getter from prototype chain', async () => {
|
||||
const mainRegistry = { _id: 'main' } as unknown as Mocked<ToolRegistry>;
|
||||
const agentRegistry = {
|
||||
_id: 'agent',
|
||||
getMessageBus: vi.fn().mockReturnValue(mockMessageBus),
|
||||
} as unknown as Mocked<ToolRegistry>;
|
||||
|
||||
const config = {
|
||||
getMessageBus: vi.fn().mockReturnValue(mockMessageBus),
|
||||
} as unknown as Mocked<Config>;
|
||||
Object.defineProperty(config, 'toolRegistry', {
|
||||
get: () => mainRegistry,
|
||||
configurable: true,
|
||||
});
|
||||
|
||||
await scheduleAgentTools(
|
||||
config as unknown as Config,
|
||||
[
|
||||
{
|
||||
callId: 'c1',
|
||||
name: 'new_page',
|
||||
args: {},
|
||||
isClientInitiated: false,
|
||||
prompt_id: 'p1',
|
||||
},
|
||||
],
|
||||
{
|
||||
schedulerId: 'browser-1',
|
||||
toolRegistry: agentRegistry as unknown as ToolRegistry,
|
||||
signal: new AbortController().signal,
|
||||
},
|
||||
);
|
||||
|
||||
const schedulerConfig = vi.mocked(Scheduler).mock.calls[0][0].config;
|
||||
expect(schedulerConfig.toolRegistry).toBe(agentRegistry);
|
||||
expect(schedulerConfig.toolRegistry).not.toBe(mainRegistry);
|
||||
expect(schedulerConfig.getToolRegistry()).toBe(agentRegistry);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -58,6 +58,11 @@ export async function scheduleAgentTools(
|
||||
const agentConfig: Config = Object.create(config);
|
||||
agentConfig.getToolRegistry = () => toolRegistry;
|
||||
agentConfig.getMessageBus = () => toolRegistry.getMessageBus();
|
||||
// Override toolRegistry property so AgentLoopContext reads the agent-specific registry.
|
||||
Object.defineProperty(agentConfig, 'toolRegistry', {
|
||||
get: () => toolRegistry,
|
||||
configurable: true,
|
||||
});
|
||||
|
||||
const scheduler = new Scheduler({
|
||||
config: agentConfig,
|
||||
|
||||
@@ -209,6 +209,45 @@ describe('browserAgentFactory', () => {
|
||||
.map((t) => t.name) ?? [];
|
||||
expect(toolNames).toContain('analyze_screenshot');
|
||||
});
|
||||
|
||||
it('should include all MCP navigation tools (new_page, navigate_page) in definition', async () => {
|
||||
mockBrowserManager.getDiscoveredTools.mockResolvedValue([
|
||||
{ name: 'take_snapshot', description: 'Take snapshot' },
|
||||
{ name: 'click', description: 'Click element' },
|
||||
{ name: 'fill', description: 'Fill form field' },
|
||||
{ name: 'navigate_page', description: 'Navigate to URL' },
|
||||
{ name: 'new_page', description: 'Open a new page/tab' },
|
||||
{ name: 'close_page', description: 'Close page' },
|
||||
{ name: 'select_page', description: 'Select page' },
|
||||
{ name: 'press_key', description: 'Press key' },
|
||||
{ name: 'hover', description: 'Hover element' },
|
||||
]);
|
||||
|
||||
const { definition } = await createBrowserAgentDefinition(
|
||||
mockConfig,
|
||||
mockMessageBus,
|
||||
);
|
||||
|
||||
const toolNames =
|
||||
definition.toolConfig?.tools
|
||||
?.filter(
|
||||
(t): t is { name: string } => typeof t === 'object' && 'name' in t,
|
||||
)
|
||||
.map((t) => t.name) ?? [];
|
||||
|
||||
// All MCP tools must be present
|
||||
expect(toolNames).toContain('new_page');
|
||||
expect(toolNames).toContain('navigate_page');
|
||||
expect(toolNames).toContain('close_page');
|
||||
expect(toolNames).toContain('select_page');
|
||||
expect(toolNames).toContain('click');
|
||||
expect(toolNames).toContain('take_snapshot');
|
||||
expect(toolNames).toContain('press_key');
|
||||
// Custom composite tool must also be present
|
||||
expect(toolNames).toContain('type_text');
|
||||
// Total: 9 MCP + 1 type_text (no analyze_screenshot without visualModel)
|
||||
expect(definition.toolConfig?.tools).toHaveLength(10);
|
||||
});
|
||||
});
|
||||
|
||||
describe('cleanupBrowserAgent', () => {
|
||||
|
||||
@@ -36,7 +36,7 @@ describe('generate-keybindings-doc', () => {
|
||||
},
|
||||
{
|
||||
description: 'Submit with Enter if no modifiers are held.',
|
||||
bindings: [{ key: 'return', shift: false, ctrl: false }],
|
||||
bindings: [{ key: 'enter', shift: false, ctrl: false }],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||