mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-10 22:21:22 -07:00
implement fuzzy search inside settings (#13864)
This commit is contained in:
@@ -48,6 +48,7 @@ enum TerminalKeys {
|
||||
LEFT_ARROW = '\u001B[D',
|
||||
RIGHT_ARROW = '\u001B[C',
|
||||
ESCAPE = '\u001B',
|
||||
BACKSPACE = '\u0008',
|
||||
}
|
||||
|
||||
const createMockSettings = (
|
||||
@@ -1105,6 +1106,195 @@ describe('SettingsDialog', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Search Functionality', () => {
|
||||
it('should enter search mode when "/" is pressed', async () => {
|
||||
const settings = createMockSettings();
|
||||
const onSelect = vi.fn();
|
||||
|
||||
const { lastFrame, stdin, unmount } = renderDialog(settings, onSelect);
|
||||
|
||||
// Wait for initial render and verify that search is not active
|
||||
await waitFor(() => {
|
||||
expect(lastFrame()).not.toContain('> Search:');
|
||||
});
|
||||
expect(lastFrame()).toContain('(press / to search)');
|
||||
|
||||
// Press '/' to enter search mode
|
||||
act(() => {
|
||||
stdin.write('/');
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(lastFrame()).toContain('> Search:');
|
||||
expect(lastFrame()).not.toContain('(press / to search)');
|
||||
});
|
||||
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should show search query and filter settings as user types', async () => {
|
||||
const settings = createMockSettings();
|
||||
const onSelect = vi.fn();
|
||||
|
||||
const { lastFrame, stdin, unmount } = renderDialog(settings, onSelect);
|
||||
|
||||
// Enter search mode
|
||||
act(() => {
|
||||
stdin.write('/');
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(lastFrame()).toContain('> Search:');
|
||||
});
|
||||
|
||||
// Type "vim"
|
||||
act(() => {
|
||||
stdin.write('yolo');
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(lastFrame()).toContain('> Search: yolo');
|
||||
expect(lastFrame()).toContain('Disable YOLO Mode'); // Should be filtered to show Vim Mode
|
||||
});
|
||||
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should exit search mode when Escape is pressed', async () => {
|
||||
const settings = createMockSettings();
|
||||
const onSelect = vi.fn();
|
||||
|
||||
const { lastFrame, stdin, unmount } = renderDialog(settings, onSelect);
|
||||
|
||||
act(() => {
|
||||
stdin.write('/');
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(lastFrame()).toContain('> Search:');
|
||||
});
|
||||
|
||||
act(() => {
|
||||
stdin.write('vim');
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(lastFrame()).toContain('> Search: vim');
|
||||
});
|
||||
|
||||
// Press Escape
|
||||
act(() => {
|
||||
stdin.write(TerminalKeys.ESCAPE);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(lastFrame()).not.toContain('> Search:');
|
||||
expect(lastFrame()).toContain('(press / to search)');
|
||||
expect(lastFrame()).toContain('Vim Mode'); // All settings should be visible again
|
||||
expect(lastFrame()).toContain('Disable Auto Update'); // All settings should be visible again
|
||||
});
|
||||
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should handle backspace to modify search query', async () => {
|
||||
const settings = createMockSettings();
|
||||
const onSelect = vi.fn();
|
||||
|
||||
const { lastFrame, stdin, unmount } = renderDialog(settings, onSelect);
|
||||
|
||||
act(() => {
|
||||
stdin.write('/');
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(lastFrame()).toContain('> Search:');
|
||||
});
|
||||
|
||||
act(() => {
|
||||
stdin.write('vimm');
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(lastFrame()).toContain('> Search: vimm');
|
||||
});
|
||||
|
||||
// Press backspace
|
||||
act(() => {
|
||||
stdin.write(TerminalKeys.BACKSPACE);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(lastFrame()).toContain('> Search: vim');
|
||||
expect(lastFrame()).toContain('Vim Mode');
|
||||
expect(lastFrame()).not.toContain(
|
||||
'Codebase Investigator Max Num Turns',
|
||||
);
|
||||
});
|
||||
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should clear search query and show all settings when exiting search mode', async () => {
|
||||
const settings = createMockSettings();
|
||||
const onSelect = vi.fn();
|
||||
|
||||
const { lastFrame, stdin, unmount } = renderDialog(settings, onSelect);
|
||||
|
||||
act(() => {
|
||||
stdin.write('/');
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(lastFrame()).toContain('> Search:');
|
||||
});
|
||||
|
||||
act(() => {
|
||||
stdin.write('test');
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(lastFrame()).toContain('> Search: test');
|
||||
});
|
||||
|
||||
// Press Escape
|
||||
act(() => {
|
||||
stdin.write(TerminalKeys.ESCAPE);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(lastFrame()).not.toContain('> Search:');
|
||||
expect(lastFrame()).toContain('(press / to search)');
|
||||
expect(lastFrame()).toContain('Vim Mode');
|
||||
expect(lastFrame()).toContain('Disable Auto Update');
|
||||
});
|
||||
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should display "No matches found." when search yields no results', async () => {
|
||||
const settings = createMockSettings();
|
||||
const onSelect = vi.fn();
|
||||
|
||||
const { lastFrame, stdin, unmount } = renderDialog(settings, onSelect);
|
||||
|
||||
// Enter search mode
|
||||
act(() => {
|
||||
stdin.write('/');
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(lastFrame()).toContain('> Search:');
|
||||
});
|
||||
|
||||
// Type a search query that won't match any settings
|
||||
act(() => {
|
||||
stdin.write('nonexistentsetting');
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(lastFrame()).toContain('> Search: nonexistentsetting');
|
||||
expect(lastFrame()).toContain('No matches found.');
|
||||
expect(lastFrame()).not.toContain('Vim Mode'); // Should not contain any settings
|
||||
expect(lastFrame()).not.toContain('Disable Auto Update'); // Should not contain any settings
|
||||
});
|
||||
|
||||
unmount();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Snapshot Tests', () => {
|
||||
/**
|
||||
* Snapshot tests for SettingsDialog component using ink-testing-library.
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { AsyncFzf } from 'fzf';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import type {
|
||||
LoadableSettingScope,
|
||||
@@ -45,6 +46,14 @@ import { debugLogger } from '@google/gemini-cli-core';
|
||||
import { keyMatchers, Command } from '../keyMatchers.js';
|
||||
import type { Config } from '@google/gemini-cli-core';
|
||||
|
||||
interface FzfResult {
|
||||
item: string;
|
||||
start: number;
|
||||
end: number;
|
||||
score: number;
|
||||
positions?: number[];
|
||||
}
|
||||
|
||||
interface SettingsDialogProps {
|
||||
settings: LoadedSettings;
|
||||
onSelect: (settingName: string | undefined, scope: SettingScope) => void;
|
||||
@@ -79,6 +88,62 @@ export function SettingsDialog({
|
||||
const [scrollOffset, setScrollOffset] = useState(0);
|
||||
const [showRestartPrompt, setShowRestartPrompt] = useState(false);
|
||||
|
||||
// Search state
|
||||
const [isSearching, setIsSearching] = useState(false);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [filteredKeys, setFilteredKeys] = useState<string[]>(() =>
|
||||
getDialogSettingKeys(),
|
||||
);
|
||||
const { fzfInstance, searchMap } = useMemo(() => {
|
||||
const keys = getDialogSettingKeys();
|
||||
const map = new Map<string, string>();
|
||||
const searchItems: string[] = [];
|
||||
|
||||
keys.forEach((key) => {
|
||||
const def = getSettingDefinition(key);
|
||||
if (def?.label) {
|
||||
searchItems.push(def.label);
|
||||
map.set(def.label.toLowerCase(), key);
|
||||
}
|
||||
});
|
||||
|
||||
const fzf = new AsyncFzf(searchItems, {
|
||||
fuzzy: 'v2',
|
||||
casing: 'case-insensitive',
|
||||
});
|
||||
return { fzfInstance: fzf, searchMap: map };
|
||||
}, []);
|
||||
|
||||
// Perform search
|
||||
useEffect(() => {
|
||||
let active = true;
|
||||
if (!searchQuery.trim() || !fzfInstance) {
|
||||
setFilteredKeys(getDialogSettingKeys());
|
||||
return;
|
||||
}
|
||||
|
||||
const doSearch = async () => {
|
||||
const results = await fzfInstance.find(searchQuery);
|
||||
|
||||
if (!active) return;
|
||||
|
||||
const matchedKeys = new Set<string>();
|
||||
results.forEach((res: FzfResult) => {
|
||||
const key = searchMap.get(res.item.toLowerCase());
|
||||
if (key) matchedKeys.add(key);
|
||||
});
|
||||
setFilteredKeys(Array.from(matchedKeys));
|
||||
setActiveSettingIndex(0); // Reset cursor
|
||||
setScrollOffset(0);
|
||||
};
|
||||
|
||||
doSearch();
|
||||
|
||||
return () => {
|
||||
active = false;
|
||||
};
|
||||
}, [searchQuery, fzfInstance, searchMap]);
|
||||
|
||||
// Local pending settings state for the selected scope
|
||||
const [pendingSettings, setPendingSettings] = useState<Settings>(() =>
|
||||
// Deep clone to avoid mutation
|
||||
@@ -127,7 +192,8 @@ export function SettingsDialog({
|
||||
}, [selectedScope, settings, globalPendingChanges]);
|
||||
|
||||
const generateSettingsItems = () => {
|
||||
const settingKeys = getDialogSettingKeys();
|
||||
const settingKeys =
|
||||
isSearching || searchQuery ? filteredKeys : getDialogSettingKeys();
|
||||
|
||||
return settingKeys.map((key: string) => {
|
||||
const definition = getSettingDefinition(key);
|
||||
@@ -493,6 +559,38 @@ export function SettingsDialog({
|
||||
useKeypress(
|
||||
(key) => {
|
||||
const { name } = key;
|
||||
|
||||
if (isSearching) {
|
||||
if (keyMatchers[Command.ESCAPE](key)) {
|
||||
setIsSearching(false);
|
||||
setSearchQuery('');
|
||||
return;
|
||||
}
|
||||
if (keyMatchers[Command.RETURN](key)) {
|
||||
setIsSearching(false);
|
||||
return;
|
||||
}
|
||||
if (name === 'backspace') {
|
||||
setSearchQuery((prev) => prev.slice(0, -1));
|
||||
return;
|
||||
}
|
||||
if (
|
||||
key.sequence &&
|
||||
key.sequence.length === 1 &&
|
||||
!key.ctrl &&
|
||||
!key.meta &&
|
||||
!keyMatchers[Command.DIALOG_NAVIGATION_UP](key) &&
|
||||
!keyMatchers[Command.DIALOG_NAVIGATION_DOWN](key)
|
||||
) {
|
||||
setSearchQuery((prev) => prev + key.sequence);
|
||||
return;
|
||||
}
|
||||
} else if (!editingKey && key.sequence === '/') {
|
||||
setIsSearching(true);
|
||||
setSearchQuery('');
|
||||
return;
|
||||
}
|
||||
|
||||
if (name === 'tab' && showScopeSelection) {
|
||||
setFocusSection((prev) => (prev === 'settings' ? 'scope' : 'settings'));
|
||||
}
|
||||
@@ -768,127 +866,154 @@ export function SettingsDialog({
|
||||
height="100%"
|
||||
>
|
||||
<Box flexDirection="column" flexGrow={1}>
|
||||
<Text bold={focusSection === 'settings'} wrap="truncate">
|
||||
{focusSection === 'settings' ? '> ' : ' '}Settings
|
||||
</Text>
|
||||
{isSearching || searchQuery ? (
|
||||
<Text bold color={theme.text.accent} wrap="truncate">
|
||||
{isSearching ? '> ' : ' '}Search: {searchQuery}
|
||||
{isSearching ? '_' : ''}
|
||||
</Text>
|
||||
) : (
|
||||
<Text bold={focusSection === 'settings'} wrap="truncate">
|
||||
{focusSection === 'settings' ? '> ' : ' '}Settings{' '}
|
||||
<Text color={theme.text.secondary}>(press / to search)</Text>
|
||||
</Text>
|
||||
)}
|
||||
<Box height={1} />
|
||||
{showScrollUp && <Text color={theme.text.secondary}>▲</Text>}
|
||||
{visibleItems.map((item, idx) => {
|
||||
const isActive =
|
||||
focusSection === 'settings' &&
|
||||
activeSettingIndex === idx + scrollOffset;
|
||||
{isSearching && visibleItems.length === 0 ? (
|
||||
<Box height={1} flexDirection="column">
|
||||
<Text color={theme.text.secondary}>No matches found.</Text>
|
||||
</Box>
|
||||
) : (
|
||||
<>
|
||||
{showScrollUp && <Text color={theme.text.secondary}>▲</Text>}
|
||||
{visibleItems.map((item, idx) => {
|
||||
const isActive =
|
||||
focusSection === 'settings' &&
|
||||
activeSettingIndex === idx + scrollOffset;
|
||||
|
||||
const scopeSettings = settings.forScope(selectedScope).settings;
|
||||
const mergedSettings = settings.merged;
|
||||
const scopeSettings = settings.forScope(selectedScope).settings;
|
||||
const mergedSettings = settings.merged;
|
||||
|
||||
let displayValue: string;
|
||||
if (editingKey === item.value) {
|
||||
// Show edit buffer with advanced cursor highlighting
|
||||
if (cursorVisible && editCursorPos < cpLen(editBuffer)) {
|
||||
// Cursor is in the middle or at start of text
|
||||
const beforeCursor = cpSlice(editBuffer, 0, editCursorPos);
|
||||
const atCursor = cpSlice(
|
||||
editBuffer,
|
||||
editCursorPos,
|
||||
editCursorPos + 1,
|
||||
let displayValue: string;
|
||||
if (editingKey === item.value) {
|
||||
// Show edit buffer with advanced cursor highlighting
|
||||
if (cursorVisible && editCursorPos < cpLen(editBuffer)) {
|
||||
// Cursor is in the middle or at start of text
|
||||
const beforeCursor = cpSlice(editBuffer, 0, editCursorPos);
|
||||
const atCursor = cpSlice(
|
||||
editBuffer,
|
||||
editCursorPos,
|
||||
editCursorPos + 1,
|
||||
);
|
||||
const afterCursor = cpSlice(editBuffer, editCursorPos + 1);
|
||||
displayValue =
|
||||
beforeCursor + chalk.inverse(atCursor) + afterCursor;
|
||||
} else if (
|
||||
cursorVisible &&
|
||||
editCursorPos >= cpLen(editBuffer)
|
||||
) {
|
||||
// Cursor is at the end - show inverted space
|
||||
displayValue = editBuffer + chalk.inverse(' ');
|
||||
} else {
|
||||
// Cursor not visible
|
||||
displayValue = editBuffer;
|
||||
}
|
||||
} else if (item.type === 'number' || item.type === 'string') {
|
||||
// For numbers/strings, get the actual current value from pending settings
|
||||
const path = item.value.split('.');
|
||||
const currentValue = getNestedValue(pendingSettings, path);
|
||||
|
||||
const defaultValue = getDefaultValue(item.value);
|
||||
|
||||
if (currentValue !== undefined && currentValue !== null) {
|
||||
displayValue = String(currentValue);
|
||||
} else {
|
||||
displayValue =
|
||||
defaultValue !== undefined && defaultValue !== null
|
||||
? String(defaultValue)
|
||||
: '';
|
||||
}
|
||||
|
||||
// Add * if value differs from default OR if currently being modified
|
||||
const isModified = modifiedSettings.has(item.value);
|
||||
const effectiveCurrentValue =
|
||||
currentValue !== undefined && currentValue !== null
|
||||
? currentValue
|
||||
: defaultValue;
|
||||
const isDifferentFromDefault =
|
||||
effectiveCurrentValue !== defaultValue;
|
||||
|
||||
if (isDifferentFromDefault || isModified) {
|
||||
displayValue += '*';
|
||||
}
|
||||
} else {
|
||||
// For booleans and other types, use existing logic
|
||||
displayValue = getDisplayValue(
|
||||
item.value,
|
||||
scopeSettings,
|
||||
mergedSettings,
|
||||
modifiedSettings,
|
||||
pendingSettings,
|
||||
);
|
||||
}
|
||||
const shouldBeGreyedOut = isDefaultValue(
|
||||
item.value,
|
||||
scopeSettings,
|
||||
);
|
||||
const afterCursor = cpSlice(editBuffer, editCursorPos + 1);
|
||||
displayValue =
|
||||
beforeCursor + chalk.inverse(atCursor) + afterCursor;
|
||||
} else if (cursorVisible && editCursorPos >= cpLen(editBuffer)) {
|
||||
// Cursor is at the end - show inverted space
|
||||
displayValue = editBuffer + chalk.inverse(' ');
|
||||
} else {
|
||||
// Cursor not visible
|
||||
displayValue = editBuffer;
|
||||
}
|
||||
} else if (item.type === 'number' || item.type === 'string') {
|
||||
// For numbers/strings, get the actual current value from pending settings
|
||||
const path = item.value.split('.');
|
||||
const currentValue = getNestedValue(pendingSettings, path);
|
||||
|
||||
const defaultValue = getDefaultValue(item.value);
|
||||
// Generate scope message for this setting
|
||||
const scopeMessage = getScopeMessageForSetting(
|
||||
item.value,
|
||||
selectedScope,
|
||||
settings,
|
||||
);
|
||||
|
||||
if (currentValue !== undefined && currentValue !== null) {
|
||||
displayValue = String(currentValue);
|
||||
} else {
|
||||
displayValue =
|
||||
defaultValue !== undefined && defaultValue !== null
|
||||
? String(defaultValue)
|
||||
: '';
|
||||
}
|
||||
|
||||
// Add * if value differs from default OR if currently being modified
|
||||
const isModified = modifiedSettings.has(item.value);
|
||||
const effectiveCurrentValue =
|
||||
currentValue !== undefined && currentValue !== null
|
||||
? currentValue
|
||||
: defaultValue;
|
||||
const isDifferentFromDefault =
|
||||
effectiveCurrentValue !== defaultValue;
|
||||
|
||||
if (isDifferentFromDefault || isModified) {
|
||||
displayValue += '*';
|
||||
}
|
||||
} else {
|
||||
// For booleans and other types, use existing logic
|
||||
displayValue = getDisplayValue(
|
||||
item.value,
|
||||
scopeSettings,
|
||||
mergedSettings,
|
||||
modifiedSettings,
|
||||
pendingSettings,
|
||||
);
|
||||
}
|
||||
const shouldBeGreyedOut = isDefaultValue(item.value, scopeSettings);
|
||||
|
||||
// Generate scope message for this setting
|
||||
const scopeMessage = getScopeMessageForSetting(
|
||||
item.value,
|
||||
selectedScope,
|
||||
settings,
|
||||
);
|
||||
|
||||
return (
|
||||
<React.Fragment key={item.value}>
|
||||
<Box flexDirection="row" alignItems="center">
|
||||
<Box minWidth={2} flexShrink={0}>
|
||||
<Text
|
||||
color={
|
||||
isActive ? theme.status.success : theme.text.secondary
|
||||
}
|
||||
>
|
||||
{isActive ? '●' : ''}
|
||||
</Text>
|
||||
</Box>
|
||||
<Box minWidth={50}>
|
||||
<Text
|
||||
color={isActive ? theme.status.success : theme.text.primary}
|
||||
>
|
||||
{item.label}
|
||||
{scopeMessage && (
|
||||
<Text color={theme.text.secondary}> {scopeMessage}</Text>
|
||||
)}
|
||||
</Text>
|
||||
</Box>
|
||||
<Box minWidth={3} />
|
||||
<Text
|
||||
color={
|
||||
isActive
|
||||
? theme.status.success
|
||||
: shouldBeGreyedOut
|
||||
? theme.text.secondary
|
||||
: theme.text.primary
|
||||
}
|
||||
>
|
||||
{displayValue}
|
||||
</Text>
|
||||
</Box>
|
||||
<Box height={1} />
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
{showScrollDown && <Text color={theme.text.secondary}>▼</Text>}
|
||||
return (
|
||||
<React.Fragment key={item.value}>
|
||||
<Box flexDirection="row" alignItems="center">
|
||||
<Box minWidth={2} flexShrink={0}>
|
||||
<Text
|
||||
color={
|
||||
isActive ? theme.status.success : theme.text.secondary
|
||||
}
|
||||
>
|
||||
{isActive ? '●' : ''}
|
||||
</Text>
|
||||
</Box>
|
||||
<Box minWidth={50}>
|
||||
<Text
|
||||
color={
|
||||
isActive ? theme.status.success : theme.text.primary
|
||||
}
|
||||
>
|
||||
{item.label}
|
||||
{scopeMessage && (
|
||||
<Text color={theme.text.secondary}>
|
||||
{' '}
|
||||
{scopeMessage}
|
||||
</Text>
|
||||
)}
|
||||
</Text>
|
||||
</Box>
|
||||
<Box minWidth={3} />
|
||||
<Text
|
||||
color={
|
||||
isActive
|
||||
? theme.status.success
|
||||
: shouldBeGreyedOut
|
||||
? theme.text.secondary
|
||||
: theme.text.primary
|
||||
}
|
||||
>
|
||||
{displayValue}
|
||||
</Text>
|
||||
</Box>
|
||||
<Box height={1} />
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
{showScrollDown && <Text color={theme.text.secondary}>▼</Text>}
|
||||
</>
|
||||
)}
|
||||
|
||||
<Box height={1} />
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
exports[`SettingsDialog > Initial Rendering > should render settings list with visual indicators 1`] = `
|
||||
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ > Settings │
|
||||
│ > Settings (press / to search) │
|
||||
│ │
|
||||
│ ▲ │
|
||||
│ ● Preview Features (e.g., models) false │
|
||||
@@ -38,7 +38,7 @@ exports[`SettingsDialog > Initial Rendering > should render settings list with v
|
||||
exports[`SettingsDialog > Snapshot Tests > should render 'accessibility settings enabled' correctly 1`] = `
|
||||
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ > Settings │
|
||||
│ > Settings (press / to search) │
|
||||
│ │
|
||||
│ ▲ │
|
||||
│ ● Preview Features (e.g., models) false │
|
||||
@@ -73,7 +73,7 @@ exports[`SettingsDialog > Snapshot Tests > should render 'accessibility settings
|
||||
exports[`SettingsDialog > Snapshot Tests > should render 'all boolean settings disabled' correctly 1`] = `
|
||||
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ > Settings │
|
||||
│ > Settings (press / to search) │
|
||||
│ │
|
||||
│ ▲ │
|
||||
│ ● Preview Features (e.g., models) false │
|
||||
@@ -108,7 +108,7 @@ exports[`SettingsDialog > Snapshot Tests > should render 'all boolean settings d
|
||||
exports[`SettingsDialog > Snapshot Tests > should render 'default state' correctly 1`] = `
|
||||
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ > Settings │
|
||||
│ > Settings (press / to search) │
|
||||
│ │
|
||||
│ ▲ │
|
||||
│ ● Preview Features (e.g., models) false │
|
||||
@@ -143,7 +143,7 @@ exports[`SettingsDialog > Snapshot Tests > should render 'default state' correct
|
||||
exports[`SettingsDialog > Snapshot Tests > should render 'file filtering settings configured' correctly 1`] = `
|
||||
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ > Settings │
|
||||
│ > Settings (press / to search) │
|
||||
│ │
|
||||
│ ▲ │
|
||||
│ ● Preview Features (e.g., models) false │
|
||||
@@ -178,7 +178,7 @@ exports[`SettingsDialog > Snapshot Tests > should render 'file filtering setting
|
||||
exports[`SettingsDialog > Snapshot Tests > should render 'focused on scope selector' correctly 1`] = `
|
||||
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ Settings │
|
||||
│ Settings (press / to search) │
|
||||
│ │
|
||||
│ ▲ │
|
||||
│ Preview Features (e.g., models) false │
|
||||
@@ -213,7 +213,7 @@ exports[`SettingsDialog > Snapshot Tests > should render 'focused on scope selec
|
||||
exports[`SettingsDialog > Snapshot Tests > should render 'mixed boolean and number settings' correctly 1`] = `
|
||||
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ > Settings │
|
||||
│ > Settings (press / to search) │
|
||||
│ │
|
||||
│ ▲ │
|
||||
│ ● Preview Features (e.g., models) false │
|
||||
@@ -248,7 +248,7 @@ exports[`SettingsDialog > Snapshot Tests > should render 'mixed boolean and numb
|
||||
exports[`SettingsDialog > Snapshot Tests > should render 'tools and security settings' correctly 1`] = `
|
||||
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ > Settings │
|
||||
│ > Settings (press / to search) │
|
||||
│ │
|
||||
│ ▲ │
|
||||
│ ● Preview Features (e.g., models) false │
|
||||
@@ -283,7 +283,7 @@ exports[`SettingsDialog > Snapshot Tests > should render 'tools and security set
|
||||
exports[`SettingsDialog > Snapshot Tests > should render 'various boolean settings enabled' correctly 1`] = `
|
||||
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ > Settings │
|
||||
│ > Settings (press / to search) │
|
||||
│ │
|
||||
│ ▲ │
|
||||
│ ● Preview Features (e.g., models) false │
|
||||
|
||||
Reference in New Issue
Block a user