summaryrefslogtreecommitdiff
path: root/packages/cli/src/ui/components/EditorSettingsDialog.tsx
diff options
context:
space:
mode:
authorLeo <[email protected]>2025-06-12 02:21:54 +0100
committerGitHub <[email protected]>2025-06-11 18:21:54 -0700
commit1ef68e061213b6b170bd979d31d4805da2357272 (patch)
treeddd91ec2a7841e763676e09765adf6f21880c2c3 /packages/cli/src/ui/components/EditorSettingsDialog.tsx
parentdd53e5c96aa01708a3bdb675c8a8e0d71be35651 (diff)
feat: External editor settings (#882)
Diffstat (limited to 'packages/cli/src/ui/components/EditorSettingsDialog.tsx')
-rw-r--r--packages/cli/src/ui/components/EditorSettingsDialog.tsx168
1 files changed, 168 insertions, 0 deletions
diff --git a/packages/cli/src/ui/components/EditorSettingsDialog.tsx b/packages/cli/src/ui/components/EditorSettingsDialog.tsx
new file mode 100644
index 00000000..304354b5
--- /dev/null
+++ b/packages/cli/src/ui/components/EditorSettingsDialog.tsx
@@ -0,0 +1,168 @@
+/**
+ * @license
+ * Copyright 2025 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { useState } from 'react';
+import { Box, Text, useInput } from 'ink';
+import { Colors } from '../colors.js';
+import {
+ EDITOR_DISPLAY_NAMES,
+ editorSettingsManager,
+ type EditorDisplay,
+} from '../editors/editorSettingsManager.js';
+import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
+import { LoadedSettings, SettingScope } from '../../config/settings.js';
+import { EditorType, isEditorAvailable } from '@gemini-cli/core';
+
+interface EditorDialogProps {
+ onSelect: (editorType: EditorType | undefined, scope: SettingScope) => void;
+ settings: LoadedSettings;
+ onExit: () => void;
+}
+
+export function EditorSettingsDialog({
+ onSelect,
+ settings,
+ onExit,
+}: EditorDialogProps): React.JSX.Element {
+ const [selectedScope, setSelectedScope] = useState<SettingScope>(
+ SettingScope.User,
+ );
+ const [focusedSection, setFocusedSection] = useState<'editor' | 'scope'>(
+ 'editor',
+ );
+ useInput((_, key) => {
+ if (key.tab) {
+ setFocusedSection((prev) => (prev === 'editor' ? 'scope' : 'editor'));
+ }
+ if (key.escape) {
+ onExit();
+ }
+ });
+
+ const editorItems: EditorDisplay[] =
+ editorSettingsManager.getAvailableEditorDisplays();
+
+ const currentPreference =
+ settings.forScope(selectedScope).settings.preferredEditor;
+ let editorIndex = currentPreference
+ ? editorItems.findIndex(
+ (item: EditorDisplay) => item.type === currentPreference,
+ )
+ : 0;
+ if (editorIndex === -1) {
+ console.error(`Editor is not supported: ${currentPreference}`);
+ editorIndex = 0;
+ }
+
+ const scopeItems = [
+ { label: 'User Settings', value: SettingScope.User },
+ { label: 'Workspace Settings', value: SettingScope.Workspace },
+ ];
+
+ const handleEditorSelect = (editorType: EditorType | 'not_set') => {
+ if (editorType === 'not_set') {
+ onSelect(undefined, selectedScope);
+ return;
+ }
+ onSelect(editorType, selectedScope);
+ };
+
+ const handleScopeSelect = (scope: SettingScope) => {
+ setSelectedScope(scope);
+ setFocusedSection('editor');
+ };
+
+ let otherScopeModifiedMessage = '';
+ const otherScope =
+ selectedScope === SettingScope.User
+ ? SettingScope.Workspace
+ : SettingScope.User;
+ if (settings.forScope(otherScope).settings.preferredEditor !== undefined) {
+ otherScopeModifiedMessage =
+ settings.forScope(selectedScope).settings.preferredEditor !== undefined
+ ? `(Also modified in ${otherScope})`
+ : `(Modified in ${otherScope})`;
+ }
+
+ let mergedEditorName = 'None';
+ if (
+ settings.merged.preferredEditor &&
+ isEditorAvailable(settings.merged.preferredEditor)
+ ) {
+ mergedEditorName =
+ EDITOR_DISPLAY_NAMES[settings.merged.preferredEditor as EditorType];
+ }
+
+ return (
+ <Box
+ borderStyle="round"
+ borderColor={Colors.Gray}
+ flexDirection="row"
+ padding={1}
+ width="100%"
+ >
+ <Box flexDirection="column" width="45%" paddingRight={2}>
+ <Text bold={focusedSection === 'editor'}>
+ {focusedSection === 'editor' ? '> ' : ' '}Select Editor{' '}
+ <Text color={Colors.Gray}>{otherScopeModifiedMessage}</Text>
+ </Text>
+ <RadioButtonSelect
+ items={editorItems.map((item) => ({
+ label: item.name,
+ value: item.type,
+ disabled: item.disabled,
+ }))}
+ initialIndex={editorIndex}
+ onSelect={handleEditorSelect}
+ isFocused={focusedSection === 'editor'}
+ key={selectedScope}
+ />
+
+ <Box marginTop={1} flexDirection="column">
+ <Text bold={focusedSection === 'scope'}>
+ {focusedSection === 'scope' ? '> ' : ' '}Apply To
+ </Text>
+ <RadioButtonSelect
+ items={scopeItems}
+ initialIndex={0}
+ onSelect={handleScopeSelect}
+ isFocused={focusedSection === 'scope'}
+ />
+ </Box>
+
+ <Box marginTop={1}>
+ <Text color={Colors.Gray}>
+ (Use Enter to select, Tab to change focus)
+ </Text>
+ </Box>
+ </Box>
+
+ <Box flexDirection="column" width="55%" paddingLeft={2}>
+ <Text bold>Editor Preference</Text>
+ <Box flexDirection="column" gap={1} marginTop={1}>
+ <Text color={Colors.Gray}>
+ These editors are currently supported. Please note that some editors
+ cannot be used in sandbox mode.
+ </Text>
+ <Text color={Colors.Gray}>
+ Your preferred editor is:{' '}
+ <Text
+ color={
+ mergedEditorName === 'None'
+ ? Colors.AccentRed
+ : Colors.AccentCyan
+ }
+ bold
+ >
+ {mergedEditorName}
+ </Text>
+ .
+ </Text>
+ </Box>
+ </Box>
+ </Box>
+ );
+}