Skip to content

Commit 62e285b

Browse files
committed
✨ Add theme selection and custom instructions to settings modal
Reorganize settings into logical sections (Provider, Appearance, Behavior) with visual polish including section headers and box-drawing separators. New settings fields: - Theme selector with live preview and light/dark indicator - Custom instructions field for user-defined prompts UI improvements: - Theme color swatch strip with gradient preview - Row highlighting for selected fields - Bidirectional cycling with Left/Right arrows for all cycle fields - Proper background fills and visual hierarchy Refactor backward cycling logic into SettingsState to reduce duplication and ensure consistent behavior across Provider, Theme, and Preset fields.
1 parent 372b7dc commit 62e285b

File tree

7 files changed

+376
-110
lines changed

7 files changed

+376
-110
lines changed

src/studio/app/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1634,6 +1634,10 @@ impl StudioApp {
16341634
config
16351635
.instruction_preset
16361636
.clone_from(&settings.instruction_preset);
1637+
config
1638+
.instructions
1639+
.clone_from(&settings.custom_instructions);
1640+
config.theme.clone_from(&settings.theme);
16371641

16381642
// Update provider config
16391643
if let Some(provider_config) = config.providers.get_mut(&settings.provider) {

src/studio/handlers/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,9 @@ fn handle_global_key(state: &mut StudioState, key: KeyEvent) -> Option<Vec<SideE
9898

9999
// Settings
100100
KeyCode::Char('S') if key.modifiers.contains(KeyModifiers::SHIFT) => {
101-
state.modal = Some(Modal::Settings(SettingsState::from_config(&state.config)));
101+
state.modal = Some(Modal::Settings(Box::new(SettingsState::from_config(
102+
&state.config,
103+
))));
102104
Some(vec![])
103105
}
104106

src/studio/handlers/modals/settings.rs

Lines changed: 2 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use crossterm::event::{KeyCode, KeyEvent};
44

55
use crate::studio::events::SideEffect;
6-
use crate::studio::state::{Modal, SettingsField, StudioState};
6+
use crate::studio::state::{Modal, StudioState};
77

88
/// Handle key events in settings modal
99
pub fn handle(state: &mut StudioState, key: KeyEvent) -> Vec<SideEffect> {
@@ -89,44 +89,7 @@ fn handle_navigation_mode(state: &mut StudioState, key: KeyEvent) -> Vec<SideEff
8989
KeyCode::Left | KeyCode::Char('h') => {
9090
// Cycle backwards for cyclable fields
9191
if let Some(Modal::Settings(settings)) = &mut state.modal {
92-
let field = settings.current_field();
93-
match field {
94-
SettingsField::Provider => {
95-
if let Some(idx) = settings
96-
.available_providers
97-
.iter()
98-
.position(|p| p == &settings.provider)
99-
{
100-
let prev = if idx == 0 {
101-
settings.available_providers.len() - 1
102-
} else {
103-
idx - 1
104-
};
105-
settings.provider = settings.available_providers[prev].clone();
106-
settings.modified = true;
107-
}
108-
}
109-
SettingsField::UseGitmoji => {
110-
settings.use_gitmoji = !settings.use_gitmoji;
111-
settings.modified = true;
112-
}
113-
SettingsField::InstructionPreset => {
114-
if let Some(idx) = settings
115-
.available_presets
116-
.iter()
117-
.position(|p| p == &settings.instruction_preset)
118-
{
119-
let prev = if idx == 0 {
120-
settings.available_presets.len() - 1
121-
} else {
122-
idx - 1
123-
};
124-
settings.instruction_preset = settings.available_presets[prev].clone();
125-
settings.modified = true;
126-
}
127-
}
128-
_ => {}
129-
}
92+
settings.cycle_current_field_back();
13093
}
13194
state.mark_dirty();
13295
vec![]

src/studio/reducer/modal.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ pub fn create_modal(state: &StudioState, modal_type: ModalType) -> Modal {
1111
match modal_type {
1212
ModalType::Help => Modal::Help,
1313
ModalType::Chat => Modal::Chat,
14-
ModalType::Settings => Modal::Settings(SettingsState::from_config(&state.config)),
14+
ModalType::Settings => Modal::Settings(Box::new(SettingsState::from_config(&state.config))),
1515
ModalType::PresetSelector => {
1616
let presets = state.get_commit_presets();
1717
Modal::PresetSelector {

src/studio/render/modals/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ fn modal_size(modal: &Modal, area: Rect) -> (u16, u16) {
5959
}
6060
// Emoji selector grid
6161
Modal::EmojiSelector { .. } => (55.min(max_width), 26.min(max_height)),
62-
// Settings modal
63-
Modal::Settings(_) => (60.min(max_width), 20.min(max_height)),
62+
// Settings modal - full width for fields, compact preview strip
63+
Modal::Settings(_) => (70.min(max_width), 24.min(max_height)),
6464
}
6565
}
6666

0 commit comments

Comments
 (0)