Skip to content

Commit 7cccc99

Browse files
authored
Merge pull request #21 from semantic-developer/feature/sd-22
updates for information for new environments
2 parents c429543 + 57cccf7 commit 7cccc99

File tree

4 files changed

+107
-17
lines changed

4 files changed

+107
-17
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ A cross‑platform desktop UI (Avalonia/.NET 8) for driving the Codex CLI app se
99
- Send user input that is wrapped as protocol `Submission`s (app server)
1010
- Auto‑approve exec/patch requests (automatic)
1111
- Select a Codex profile (from `config.toml`) and load MCP servers from a JSON config
12+
- Keep multiple Codex sessions active at once using the tabbed header (each tab title shows its live status, e.g., `Session 2 – thinking…`)
1213
– See live token usage and estimated context remaining in the header
1314

1415
> Important: This app runs Codex through the `app-server` subcommand.
@@ -43,6 +44,7 @@ A cross‑platform desktop UI (Avalonia/.NET 8) for driving the Codex CLI app se
4344
- Use API Key for Codex CLI (pipes the key to `codex login --with-api-key` before sessions; does not rely on existing CLI auth)
4445
- Allow network access for tools (sets sandbox_policy.network_access=true on turns so MCP tools can reach the network)
4546
- Without API key enabled, the app proactively authenticates with `codex auth login` (falling back to `codex login`) before sessions so your chat/GPT token is used.
47+
5. Need a second workspace or want to keep another Codex stream alive? Hit the **+** button next to the session tabs to spin up a parallel session—tab titles update in real time so you can see whether each workspace is `disconnected`, `thinking…`, or `idle`.
4648

4749
### Directory Guardrails with `AGENTS.md`
4850

SemanticDeveloper/SemanticDeveloper/MainWindow.axaml.cs

Lines changed: 62 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
using System.Runtime.CompilerServices;
66
using Avalonia.Controls;
77
using Avalonia.Interactivity;
8+
using Avalonia.Threading;
89
using SemanticDeveloper.Models;
910
using SemanticDeveloper.Services;
1011
using SemanticDeveloper.Views;
12+
using System.Threading.Tasks;
1113

1214
namespace SemanticDeveloper;
1315

@@ -17,6 +19,7 @@ public partial class MainWindow : Window, INotifyPropertyChanged
1719
private SessionTab? _selectedSession;
1820
private int _sessionCounter = 0;
1921
private AppSettings _sharedSettings;
22+
private bool _profileCheckPerformed;
2023

2124
public MainWindow()
2225
{
@@ -82,17 +85,7 @@ private async void OnOpenCliSettingsClick(object? sender, RoutedEventArgs e)
8285
if (SelectedSession is null)
8386
return;
8487

85-
var updated = await SelectedSession.View.ShowCliSettingsDialogAsync(_sharedSettings);
86-
if (updated is null)
87-
return;
88-
89-
_sharedSettings = CloneAppSettings(updated);
90-
foreach (var session in _sessions)
91-
{
92-
if (session == SelectedSession)
93-
continue;
94-
session.View.ApplySettingsSnapshot(_sharedSettings);
95-
}
88+
await OpenCliSettingsAsync(SelectedSession);
9689
}
9790

9891
private async void OnOpenAboutClick(object? sender, RoutedEventArgs e)
@@ -129,6 +122,58 @@ private void OnOpenReadmeClick(object? sender, RoutedEventArgs e)
129122
private void OnPropertyChanged([CallerMemberName] string? name = null)
130123
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
131124

125+
protected override void OnOpened(EventArgs e)
126+
{
127+
base.OnOpened(e);
128+
Dispatcher.UIThread.Post(async () => await EnsureProfilesConfiguredAsync());
129+
}
130+
131+
private async Task EnsureProfilesConfiguredAsync()
132+
{
133+
if (_profileCheckPerformed)
134+
return;
135+
_profileCheckPerformed = true;
136+
137+
try
138+
{
139+
var profiles = CodexConfigService.TryGetProfiles();
140+
if (profiles.Count > 0)
141+
return;
142+
143+
var info = new InfoDialog
144+
{
145+
Title = "Add a Codex Profile",
146+
Message = "No Codex CLI profiles were found in ~/.codex/config.toml.\n\nCreate a profile under [profiles.<name>] with the model you want to use, for example:\n\n[profiles.default]\nmodel = \"gpt-5-codex\"\nmodel_provider = \"openai\"\n\nAfter you add a profile, select it in CLI Settings so sessions know which model to run."
147+
};
148+
149+
await info.ShowDialog(this);
150+
151+
if (SelectedSession is not null)
152+
{
153+
await OpenCliSettingsAsync(SelectedSession);
154+
}
155+
}
156+
catch (Exception ex)
157+
{
158+
Debug.WriteLine($"Failed to prompt for profile setup: {ex.Message}");
159+
}
160+
}
161+
162+
private async Task OpenCliSettingsAsync(SessionTab session)
163+
{
164+
var updated = await session.View.ShowCliSettingsDialogAsync(_sharedSettings);
165+
if (updated is null)
166+
return;
167+
168+
_sharedSettings = CloneAppSettings(updated);
169+
foreach (var other in _sessions)
170+
{
171+
if (other == session)
172+
continue;
173+
other.View.ApplySettingsSnapshot(_sharedSettings);
174+
}
175+
}
176+
132177
private static AppSettings CloneAppSettings(AppSettings source) => new()
133178
{
134179
Command = source.Command,
@@ -169,11 +214,11 @@ private set
169214

170215
public void UpdateHeader()
171216
{
172-
Header = $"{Title} - {View.SessionStatus}";
173-
}
174-
175-
public event PropertyChangedEventHandler? PropertyChanged;
176-
private void OnPropertyChanged([CallerMemberName] string? name = null)
177-
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
217+
Header = $"{Title} - {View.SessionStatus}";
178218
}
219+
220+
public event PropertyChangedEventHandler? PropertyChanged;
221+
private void OnPropertyChanged([CallerMemberName] string? name = null)
222+
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
223+
}
179224
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<Window xmlns="https://github.com/avaloniaui"
2+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
3+
x:Class="SemanticDeveloper.Views.InfoDialog"
4+
Width="460" Height="260"
5+
CanResize="False"
6+
WindowStartupLocation="CenterOwner"
7+
Title="Information"
8+
Background="#2E2E2E"
9+
Icon="avares://SemanticDeveloper/Images/semantic-developer-logo-large.png">
10+
<Grid RowDefinitions="*,Auto" Margin="16">
11+
<ScrollViewer Grid.Row="0" VerticalScrollBarVisibility="Auto">
12+
<TextBlock x:Name="MessageText"
13+
TextWrapping="Wrap"
14+
Foreground="#E6E6E6"
15+
Margin="0,0,0,8"/>
16+
</ScrollViewer>
17+
18+
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right">
19+
<Button Width="96" Click="OnClose" IsDefault="True">OK</Button>
20+
</StackPanel>
21+
</Grid>
22+
</Window>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using Avalonia.Controls;
2+
using Avalonia.Interactivity;
3+
4+
namespace SemanticDeveloper.Views;
5+
6+
public partial class InfoDialog : Window
7+
{
8+
public InfoDialog()
9+
{
10+
InitializeComponent();
11+
}
12+
13+
public string Message
14+
{
15+
get => MessageText.Text ?? string.Empty;
16+
set => MessageText.Text = value;
17+
}
18+
19+
private void OnClose(object? sender, RoutedEventArgs e)
20+
=> Close();
21+
}

0 commit comments

Comments
 (0)