From 57cccf7d9750d58cb4d4a80140cf729e70ab3044 Mon Sep 17 00:00:00 2001 From: jabbott72 Date: Thu, 9 Oct 2025 12:38:55 -0400 Subject: [PATCH] updates for information for new environments --- README.md | 2 + .../SemanticDeveloper/MainWindow.axaml.cs | 79 +++++++++++++++---- .../SemanticDeveloper/Views/InfoDialog.axaml | 22 ++++++ .../Views/InfoDialog.axaml.cs | 21 +++++ 4 files changed, 107 insertions(+), 17 deletions(-) create mode 100644 SemanticDeveloper/SemanticDeveloper/Views/InfoDialog.axaml create mode 100644 SemanticDeveloper/SemanticDeveloper/Views/InfoDialog.axaml.cs diff --git a/README.md b/README.md index 2a08c42..d6fecce 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ A cross‑platform desktop UI (Avalonia/.NET 8) for driving the Codex CLI app se - Send user input that is wrapped as protocol `Submission`s (app server) - Auto‑approve exec/patch requests (automatic) - Select a Codex profile (from `config.toml`) and load MCP servers from a JSON config +- Keep multiple Codex sessions active at once using the tabbed header (each tab title shows its live status, e.g., `Session 2 – thinking…`) – See live token usage and estimated context remaining in the header > 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 - Use API Key for Codex CLI (pipes the key to `codex login --with-api-key` before sessions; does not rely on existing CLI auth) - Allow network access for tools (sets sandbox_policy.network_access=true on turns so MCP tools can reach the network) - 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. +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`. ### Directory Guardrails with `AGENTS.md` diff --git a/SemanticDeveloper/SemanticDeveloper/MainWindow.axaml.cs b/SemanticDeveloper/SemanticDeveloper/MainWindow.axaml.cs index 9468fc3..0d38976 100644 --- a/SemanticDeveloper/SemanticDeveloper/MainWindow.axaml.cs +++ b/SemanticDeveloper/SemanticDeveloper/MainWindow.axaml.cs @@ -5,9 +5,11 @@ using System.Runtime.CompilerServices; using Avalonia.Controls; using Avalonia.Interactivity; +using Avalonia.Threading; using SemanticDeveloper.Models; using SemanticDeveloper.Services; using SemanticDeveloper.Views; +using System.Threading.Tasks; namespace SemanticDeveloper; @@ -17,6 +19,7 @@ public partial class MainWindow : Window, INotifyPropertyChanged private SessionTab? _selectedSession; private int _sessionCounter = 0; private AppSettings _sharedSettings; + private bool _profileCheckPerformed; public MainWindow() { @@ -82,17 +85,7 @@ private async void OnOpenCliSettingsClick(object? sender, RoutedEventArgs e) if (SelectedSession is null) return; - var updated = await SelectedSession.View.ShowCliSettingsDialogAsync(_sharedSettings); - if (updated is null) - return; - - _sharedSettings = CloneAppSettings(updated); - foreach (var session in _sessions) - { - if (session == SelectedSession) - continue; - session.View.ApplySettingsSnapshot(_sharedSettings); - } + await OpenCliSettingsAsync(SelectedSession); } private async void OnOpenAboutClick(object? sender, RoutedEventArgs e) @@ -129,6 +122,58 @@ private void OnOpenReadmeClick(object? sender, RoutedEventArgs e) private void OnPropertyChanged([CallerMemberName] string? name = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); + protected override void OnOpened(EventArgs e) + { + base.OnOpened(e); + Dispatcher.UIThread.Post(async () => await EnsureProfilesConfiguredAsync()); + } + + private async Task EnsureProfilesConfiguredAsync() + { + if (_profileCheckPerformed) + return; + _profileCheckPerformed = true; + + try + { + var profiles = CodexConfigService.TryGetProfiles(); + if (profiles.Count > 0) + return; + + var info = new InfoDialog + { + Title = "Add a Codex Profile", + Message = "No Codex CLI profiles were found in ~/.codex/config.toml.\n\nCreate a profile under [profiles.] 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." + }; + + await info.ShowDialog(this); + + if (SelectedSession is not null) + { + await OpenCliSettingsAsync(SelectedSession); + } + } + catch (Exception ex) + { + Debug.WriteLine($"Failed to prompt for profile setup: {ex.Message}"); + } + } + + private async Task OpenCliSettingsAsync(SessionTab session) + { + var updated = await session.View.ShowCliSettingsDialogAsync(_sharedSettings); + if (updated is null) + return; + + _sharedSettings = CloneAppSettings(updated); + foreach (var other in _sessions) + { + if (other == session) + continue; + other.View.ApplySettingsSnapshot(_sharedSettings); + } + } + private static AppSettings CloneAppSettings(AppSettings source) => new() { Command = source.Command, @@ -169,11 +214,11 @@ private set public void UpdateHeader() { - Header = $"{Title} - {View.SessionStatus}"; - } - - public event PropertyChangedEventHandler? PropertyChanged; - private void OnPropertyChanged([CallerMemberName] string? name = null) - => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); + Header = $"{Title} - {View.SessionStatus}"; } + + public event PropertyChangedEventHandler? PropertyChanged; + private void OnPropertyChanged([CallerMemberName] string? name = null) + => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); +} } diff --git a/SemanticDeveloper/SemanticDeveloper/Views/InfoDialog.axaml b/SemanticDeveloper/SemanticDeveloper/Views/InfoDialog.axaml new file mode 100644 index 0000000..a578d78 --- /dev/null +++ b/SemanticDeveloper/SemanticDeveloper/Views/InfoDialog.axaml @@ -0,0 +1,22 @@ + + + + + + + + + + + diff --git a/SemanticDeveloper/SemanticDeveloper/Views/InfoDialog.axaml.cs b/SemanticDeveloper/SemanticDeveloper/Views/InfoDialog.axaml.cs new file mode 100644 index 0000000..65f0a3e --- /dev/null +++ b/SemanticDeveloper/SemanticDeveloper/Views/InfoDialog.axaml.cs @@ -0,0 +1,21 @@ +using Avalonia.Controls; +using Avalonia.Interactivity; + +namespace SemanticDeveloper.Views; + +public partial class InfoDialog : Window +{ + public InfoDialog() + { + InitializeComponent(); + } + + public string Message + { + get => MessageText.Text ?? string.Empty; + set => MessageText.Text = value; + } + + private void OnClose(object? sender, RoutedEventArgs e) + => Close(); +}