diff --git a/docs/images/web-flow-01-login-first-run.png b/docs/images/web-flow-01-login-first-run.png new file mode 100644 index 00000000..21f362a3 Binary files /dev/null and b/docs/images/web-flow-01-login-first-run.png differ diff --git a/docs/images/web-flow-02-chat.png b/docs/images/web-flow-02-chat.png new file mode 100644 index 00000000..f081a3a7 Binary files /dev/null and b/docs/images/web-flow-02-chat.png differ diff --git a/docs/images/web-flow-03-dashboard.png b/docs/images/web-flow-03-dashboard.png new file mode 100644 index 00000000..36b63e66 Binary files /dev/null and b/docs/images/web-flow-03-dashboard.png differ diff --git a/docs/images/web-flow-04-memory.png b/docs/images/web-flow-04-memory.png new file mode 100644 index 00000000..796b517a Binary files /dev/null and b/docs/images/web-flow-04-memory.png differ diff --git a/docs/images/web-flow-05-documents.png b/docs/images/web-flow-05-documents.png new file mode 100644 index 00000000..60fe46da Binary files /dev/null and b/docs/images/web-flow-05-documents.png differ diff --git a/docs/images/web-flow-06-document-templates.png b/docs/images/web-flow-06-document-templates.png new file mode 100644 index 00000000..6283e55d Binary files /dev/null and b/docs/images/web-flow-06-document-templates.png differ diff --git a/docs/images/web-flow-07-artifacts.png b/docs/images/web-flow-07-artifacts.png new file mode 100644 index 00000000..5115929e Binary files /dev/null and b/docs/images/web-flow-07-artifacts.png differ diff --git a/docs/images/web-flow-08-guidelines.png b/docs/images/web-flow-08-guidelines.png new file mode 100644 index 00000000..a1391e95 Binary files /dev/null and b/docs/images/web-flow-08-guidelines.png differ diff --git a/docs/images/web-flow-09-agents.png b/docs/images/web-flow-09-agents.png new file mode 100644 index 00000000..483f309a Binary files /dev/null and b/docs/images/web-flow-09-agents.png differ diff --git a/docs/images/web-flow-10-skills.png b/docs/images/web-flow-10-skills.png new file mode 100644 index 00000000..d389c8be Binary files /dev/null and b/docs/images/web-flow-10-skills.png differ diff --git a/docs/images/web-flow-11-tools.png b/docs/images/web-flow-11-tools.png new file mode 100644 index 00000000..a941e574 Binary files /dev/null and b/docs/images/web-flow-11-tools.png differ diff --git a/docs/images/web-flow-12-tool-templates.png b/docs/images/web-flow-12-tool-templates.png new file mode 100644 index 00000000..f0ff29e6 Binary files /dev/null and b/docs/images/web-flow-12-tool-templates.png differ diff --git a/docs/images/web-flow-13-orchestration.png b/docs/images/web-flow-13-orchestration.png new file mode 100644 index 00000000..70c3d950 Binary files /dev/null and b/docs/images/web-flow-13-orchestration.png differ diff --git a/docs/images/web-flow-14-projects.png b/docs/images/web-flow-14-projects.png new file mode 100644 index 00000000..91dc0a7d Binary files /dev/null and b/docs/images/web-flow-14-projects.png differ diff --git a/docs/images/web-flow-15-workspaces.png b/docs/images/web-flow-15-workspaces.png new file mode 100644 index 00000000..e2ee0e7a Binary files /dev/null and b/docs/images/web-flow-15-workspaces.png differ diff --git a/docs/images/web-flow-16-code.png b/docs/images/web-flow-16-code.png new file mode 100644 index 00000000..8b56b1b0 Binary files /dev/null and b/docs/images/web-flow-16-code.png differ diff --git a/docs/images/web-flow-17-settings.png b/docs/images/web-flow-17-settings.png new file mode 100644 index 00000000..7071d02a Binary files /dev/null and b/docs/images/web-flow-17-settings.png differ diff --git a/docs/images/web-flow-18-provider-setup.png b/docs/images/web-flow-18-provider-setup.png new file mode 100644 index 00000000..8048d5d2 Binary files /dev/null and b/docs/images/web-flow-18-provider-setup.png differ diff --git a/docs/images/web-flow-19-command-center.png b/docs/images/web-flow-19-command-center.png new file mode 100644 index 00000000..0838c8ca Binary files /dev/null and b/docs/images/web-flow-19-command-center.png differ diff --git a/docs/images/web-flow-20-admin.png b/docs/images/web-flow-20-admin.png new file mode 100644 index 00000000..d329242f Binary files /dev/null and b/docs/images/web-flow-20-admin.png differ diff --git a/docs/images/web-flow-21-admin-providers.png b/docs/images/web-flow-21-admin-providers.png new file mode 100644 index 00000000..91038d1f Binary files /dev/null and b/docs/images/web-flow-21-admin-providers.png differ diff --git a/docs/images/web-flow-22-admin-integrations.png b/docs/images/web-flow-22-admin-integrations.png new file mode 100644 index 00000000..4ab52873 Binary files /dev/null and b/docs/images/web-flow-22-admin-integrations.png differ diff --git a/docs/images/web-flow-23-admin-system-integrations.png b/docs/images/web-flow-23-admin-system-integrations.png new file mode 100644 index 00000000..7b1e2442 Binary files /dev/null and b/docs/images/web-flow-23-admin-system-integrations.png differ diff --git a/docs/images/web-flow-24-admin-workspaces.png b/docs/images/web-flow-24-admin-workspaces.png new file mode 100644 index 00000000..56a30ba6 Binary files /dev/null and b/docs/images/web-flow-24-admin-workspaces.png differ diff --git a/docs/images/web-flow-25-governance.png b/docs/images/web-flow-25-governance.png new file mode 100644 index 00000000..77fb3c07 Binary files /dev/null and b/docs/images/web-flow-25-governance.png differ diff --git a/docs/images/web-flow-26-diagnostics.png b/docs/images/web-flow-26-diagnostics.png new file mode 100644 index 00000000..0ed4a722 Binary files /dev/null and b/docs/images/web-flow-26-diagnostics.png differ diff --git a/docs/images/web-flow-27-trust-boundary.png b/docs/images/web-flow-27-trust-boundary.png new file mode 100644 index 00000000..7771d64b Binary files /dev/null and b/docs/images/web-flow-27-trust-boundary.png differ diff --git a/docs/userflow.md b/docs/userflow.md new file mode 100644 index 00000000..7d192b85 --- /dev/null +++ b/docs/userflow.md @@ -0,0 +1,239 @@ +# Web App User Flow + +A screen-by-screen walkthrough of the Sovrant Web app (Blazor Server, `http://localhost:5100`), captured from a live run of version 1.4.0 in embedded mode with a fresh database. Every screenshot lives in [`docs/images/`](images/) with the `web-flow-` prefix. + +The flow follows the order a new user encounters the product: first-run setup → chat home → the rail-nav sections (Dashboard, Knowledge, Agents, Projects) → settings and provider setup → the admin surfaces. + +--- + +## 1. First-run: create the administrator account + +**Route:** `/login` + +On a fresh install no accounts exist, so the login page switches to first-time setup. The first account created becomes the administrator with full control over registration, approvals, and server settings. On subsequent visits this page shows the normal sign-in form (plus self-registration, if the admin has enabled it). + +![Login — first-run setup](images/web-flow-01-login-first-run.png) + +## 2. Chat (home) + +**Route:** `/` + +After registering, the user lands on Chat — the home screen. The empty state asks "What are you working on?" and offers starter prompts (create a custom agent, build a team, set up a mission, connect an MCP server). The left sidebar holds **+ New Chat** and conversation search; the top context bar exposes the model picker ("No model" until a provider is configured), workspace selector, project selector, and integrations menu. The composer includes a per-record **Private** toggle and a **+ Remember** control. + +![Chat home](images/web-flow-02-chat.png) + +## 3. User Dashboard + +**Route:** `/dashboard` (rail nav: chart icon) + +Cross-workspace activity for the signed-in user: missions, team runs, agent runs, sessions, shared items, and Claws, with quick actions to start a chat or run an agent. + +![User Dashboard](images/web-flow-03-dashboard.png) + +## Knowledge section + +### 4. Memory + +**Route:** `/memory` + +Per-user memory entries the agents can recall across sessions, with privacy scoping. + +![Memory](images/web-flow-04-memory.png) + +### 5. Documents + +**Route:** `/documents` + +Document library for the current workspace — files agents can read, produce, and update. + +![Documents](images/web-flow-05-documents.png) + +### 6. Document Templates + +**Route:** `/documents/templates` + +Reusable document templates users can instantiate. + +![Document Templates](images/web-flow-06-document-templates.png) + +### 7. Artifacts + +**Route:** `/artifacts` + +Artifacts produced by agent runs (reports, files, outputs), browsable per workspace. + +![Artifacts](images/web-flow-07-artifacts.png) + +### 8. Guidelines + +**Route:** `/guidelines` + +Behavioral guidelines that steer agents (the CLAUDE.md-style instruction layer). + +![Guidelines](images/web-flow-08-guidelines.png) + +## Agents section + +### 9. Agents + +**Route:** `/agents` + +The 25 built-in agent templates (architect, coder, code-reviewer, data-analyst, …) in a master–detail layout showing each template's role, effort level, and tool grants, plus a **+ New** button for custom agents and a recent-runs strip. + +![Agents](images/web-flow-09-agents.png) + +### 10. Skills + +**Route:** `/skills` + +The 32 built-in skills agents can invoke, with search and detail view. + +![Skills](images/web-flow-10-skills.png) + +### 11. Tools + +**Route:** `/tools` + +The 58 tools available to agents (file I/O, shell, web search, delegation, …), including enablement and permission info. + +![Tools](images/web-flow-11-tools.png) + +### 12. Tool Templates + +**Route:** `/tools/templates` + +User-defined tool templates. + +![Tool Templates](images/web-flow-12-tool-templates.png) + +### 13. Orchestration + +**Route:** `/orchestration` + +Teams, swarms, and missions — the multi-agent orchestration surfaces with per-team run profiles. + +![Orchestration](images/web-flow-13-orchestration.png) + +## Projects section + +### 14. Projects + +**Route:** `/projects` + +Projects within the current workspace; conversations and runs can be filed under a project via the top context bar. + +![Projects](images/web-flow-14-projects.png) + +### 15. Workspaces + +**Route:** `/workspaces` + +The user's workspaces (a personal workspace is seeded on first login) and membership management. + +![Workspaces](images/web-flow-15-workspaces.png) + +### 16. Code + +**Route:** `/code` + +Code-focused view for repository-oriented sessions. + +![Code](images/web-flow-16-code.png) + +## Settings & provider setup + +### 17. Settings + +**Route:** `/settings` + +Per-user preferences: appearance, defaults, and account controls (including sign-out). + +![Settings](images/web-flow-17-settings.png) + +### 18. Provider Setup + +**Route:** `/setup` + +Connect an LLM provider (API key + model) so chats and agent runs have a model to route to. Reachable from the "Set up →" link in the model picker; models can be switched anytime from the sidebar. + +![Provider Setup](images/web-flow-18-provider-setup.png) + +## Admin section (admin role only) + +### 19. Command Center + +**Route:** `/command` + +The live cockpit: missions, team runs, agent runs, sessions, and Claws currently in flight, auto-refreshing while runs are active. + +![Command Center](images/web-flow-19-command-center.png) + +### 20. Admin — Users + +**Route:** `/admin` + +User management with tabs for Users, Registration (open/closed, approval required), and Password Reset. The first-run admin account appears as active with the admin role. + +![Admin Users](images/web-flow-20-admin.png) + +### 21. Admin — Providers + +**Route:** `/admin/providers` + +Server-wide LLM provider configuration. + +![Admin Providers](images/web-flow-21-admin-providers.png) + +### 22. Admin — Integrations + +**Route:** `/admin/integrations` + +Webhook/chat integrations (Slack, Teams, Discord, custom). + +![Admin Integrations](images/web-flow-22-admin-integrations.png) + +### 23. Admin — System Integrations + +**Route:** `/admin/system-integrations` + +System-level integrations such as MCP servers and Claw runtimes. + +![Admin System Integrations](images/web-flow-23-admin-system-integrations.png) + +### 24. Admin — Workspaces + +**Route:** `/admin/workspaces` + +Administration across all workspaces on the server. + +![Admin Workspaces](images/web-flow-24-admin-workspaces.png) + +### 25. Governance + +**Route:** `/governance` + +Governance controls: policies and limits that bound what agents may do. + +![Governance](images/web-flow-25-governance.png) + +### 26. Diagnostics + +**Route:** `/diagnostics` + +Runtime diagnostics: health, logs, and environment information. + +![Diagnostics](images/web-flow-26-diagnostics.png) + +### 27. Trust Boundary + +**Route:** `/trust-boundary` + +The trust-boundary view — what data and capabilities are exposed where, in line with the security architecture (see [security-architecture.md](security-architecture.md)). + +![Trust Boundary](images/web-flow-27-trust-boundary.png) + +--- + +## How these were captured + +Screenshots were taken with Playwright/Chromium at 1440×900 against a debug build (`dotnet run --project src/Sovrant.Web`) with a fresh SQLite database: the script registers the first-run admin account through the real login form, then visits each route as that user. Re-running the capture on an existing database signs in instead of registering. diff --git a/src/Sovrant.Desktop/ViewModels/LoginViewModel.cs b/src/Sovrant.Desktop/ViewModels/LoginViewModel.cs index 29f46200..4bf369e7 100644 --- a/src/Sovrant.Desktop/ViewModels/LoginViewModel.cs +++ b/src/Sovrant.Desktop/ViewModels/LoginViewModel.cs @@ -17,8 +17,18 @@ public partial class LoginViewModel : ObservableObject [ObservableProperty] private string _email = string.Empty; [ObservableProperty] private string _password = string.Empty; [ObservableProperty] private string _errorMessage = string.Empty; + [ObservableProperty] private string _infoMessage = string.Empty; [ObservableProperty] private bool _isBusy; + [ObservableProperty] private string _busyLabel = string.Empty; [ObservableProperty] private bool _isRegistrationOpen; + [ObservableProperty] private bool _isFirstRun; + [ObservableProperty] private bool _isApprovalRequired; + + /// Registration section is only offered once an admin exists and registration is open. + public bool ShowRegistrationSection => !IsFirstRun && IsRegistrationOpen; + + partial void OnIsFirstRunChanged(bool value) => OnPropertyChanged(nameof(ShowRegistrationSection)); + partial void OnIsRegistrationOpenChanged(bool value) => OnPropertyChanged(nameof(ShowRegistrationSection)); public event Action? LoginSucceeded; // (userId, role, email) @@ -31,20 +41,31 @@ public LoginViewModel(IIdentityService identity, ITokenService tokens, ICredenti public async Task InitializeAsync() { + IsFirstRun = await _identity.IsFirstRunAsync().ConfigureAwait(true); IsRegistrationOpen = await _identity.IsRegistrationOpenAsync().ConfigureAwait(true); + IsApprovalRequired = !IsFirstRun && IsRegistrationOpen + && await _identity.IsApprovalRequiredAsync().ConfigureAwait(true); } - [RelayCommand] - private async Task LoginAsync() + private bool ValidateInput() { ErrorMessage = string.Empty; + InfoMessage = string.Empty; if (string.IsNullOrWhiteSpace(Email) || string.IsNullOrWhiteSpace(Password)) { ErrorMessage = "Email and password are required."; - return; + return false; } + return true; + } + + [RelayCommand] + private async Task LoginAsync() + { + if (!ValidateInput()) return; IsBusy = true; + BusyLabel = "Signing you in…"; try { var result = await _identity.LoginAsync(Email, Password).ConfigureAwait(true); @@ -66,14 +87,10 @@ private async Task LoginAsync() [RelayCommand] private async Task RegisterAsync() { - ErrorMessage = string.Empty; - if (string.IsNullOrWhiteSpace(Email) || string.IsNullOrWhiteSpace(Password)) - { - ErrorMessage = "Email and password are required."; - return; - } + if (!ValidateInput()) return; IsBusy = true; + BusyLabel = IsFirstRun ? "Creating your administrator account…" : "Creating your account…"; try { var result = await _identity.RegisterAsync(Email, Password).ConfigureAwait(true); @@ -85,7 +102,7 @@ private async Task RegisterAsync() if (result.IsPendingApproval) { - ErrorMessage = "Account created. An administrator must approve it before you can sign in."; + InfoMessage = "Account created. An administrator must approve it before you can sign in."; return; } diff --git a/src/Sovrant.Desktop/Views/LoginWindow.axaml b/src/Sovrant.Desktop/Views/LoginWindow.axaml index d1e23c0c..1727589e 100644 --- a/src/Sovrant.Desktop/Views/LoginWindow.axaml +++ b/src/Sovrant.Desktop/Views/LoginWindow.axaml @@ -4,7 +4,7 @@ x:Class="Sovrant.Desktop.Views.LoginWindow" x:DataType="vm:LoginViewModel" Title="Sovrant — Sign in" - Width="420" Height="460" + Width="420" SizeToContent="Height" CanResize="False" WindowStartupLocation="CenterScreen"> @@ -17,7 +17,22 @@ + Margin="0,0,0,4" + IsVisible="{Binding !IsFirstRun}"/> + + + + + + + @@ -42,15 +57,31 @@ TextWrapping="Wrap" IsVisible="{Binding ErrorMessage, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/> + + + + + } + } } @@ -204,6 +213,12 @@ await OnNavigate.InvokeAsync("/"); } + private async Task NewChat() + { + ActiveContext.StartNewSession(); + await OnNavigate.InvokeAsync("/"); + } + private async Task DeleteSession(string sessionId) { await SessionStore.DeleteAsync(sessionId, ownerUserId: Sovrant.Web.Program.SovrantUserId); @@ -215,4 +230,24 @@ private static string FormatElapsed(TimeSpan t) => t.TotalSeconds < 60 ? $"{(int)t.TotalSeconds}s" : $"{(int)t.TotalMinutes}m {t.Seconds}s"; + + private record SessionGroup(string Label, List Sessions); + + private IEnumerable GetGroupedSessions() + { + var now = DateTimeOffset.Now; + var todayStart = now.Date; + var yesterdayStart = todayStart.AddDays(-1); + var weekStart = todayStart.AddDays(-7); + + var today = _filteredSessions.Where(s => s.Timestamp >= todayStart).ToList(); + var yesterday = _filteredSessions.Where(s => s.Timestamp >= yesterdayStart && s.Timestamp < todayStart).ToList(); + var thisWeek = _filteredSessions.Where(s => s.Timestamp >= weekStart && s.Timestamp < yesterdayStart).ToList(); + var older = _filteredSessions.Where(s => s.Timestamp < weekStart).ToList(); + + if (today.Count > 0) yield return new SessionGroup("TODAY", today); + if (yesterday.Count > 0) yield return new SessionGroup("YESTERDAY", yesterday); + if (thisWeek.Count > 0) yield return new SessionGroup("THIS WEEK", thisWeek); + if (older.Count > 0) yield return new SessionGroup("OLDER", older); + } } diff --git a/src/Sovrant.Web/Components/Pages/Login.razor b/src/Sovrant.Web/Components/Pages/Login.razor index 63987602..7f491e4d 100644 --- a/src/Sovrant.Web/Components/Pages/Login.razor +++ b/src/Sovrant.Web/Components/Pages/Login.razor @@ -13,13 +13,30 @@ @@ -66,8 +100,12 @@ private string _email = string.Empty; private string _password = string.Empty; private string? _errorMessage; + private string? _infoMessage; private bool _busy; + private string _busyLabel = string.Empty; private bool _registrationOpen; + private bool _firstRun; + private bool _approvalRequired; protected override async Task OnInitializedAsync() { @@ -76,23 +114,37 @@ Nav.NavigateTo("/", forceLoad: true); return; } + _firstRun = await IdentityService.IsFirstRunAsync().ConfigureAwait(false); _registrationOpen = await IdentityService.IsRegistrationOpenAsync().ConfigureAwait(false); + _approvalRequired = !_firstRun && _registrationOpen + && await IdentityService.IsApprovalRequiredAsync().ConfigureAwait(false); } private async Task OnKeyDown(KeyboardEventArgs e) { - if (e.Key == "Enter") await LoginAsync(); + if (e.Key != "Enter") return; + // On first run there is nobody to sign in as yet — Enter creates the admin account. + if (_firstRun) await RegisterAsync(); + else await LoginAsync(); } - private async Task LoginAsync() + private bool ValidateInput() { _errorMessage = null; + _infoMessage = null; if (string.IsNullOrWhiteSpace(_email) || string.IsNullOrWhiteSpace(_password)) { _errorMessage = "Email and password are required."; - return; + return false; } + return true; + } + + private async Task LoginAsync() + { + if (!ValidateInput()) return; _busy = true; + _busyLabel = "Signing you in…"; try { var result = await IdentityService.LoginAsync(_email, _password).ConfigureAwait(false); @@ -120,13 +172,9 @@ private async Task RegisterAsync() { - _errorMessage = null; - if (string.IsNullOrWhiteSpace(_email) || string.IsNullOrWhiteSpace(_password)) - { - _errorMessage = "Email and password are required."; - return; - } + if (!ValidateInput()) return; _busy = true; + _busyLabel = _firstRun ? "Creating your administrator account…" : "Creating your account…"; try { var result = await IdentityService.RegisterAsync(_email, _password).ConfigureAwait(false); @@ -137,7 +185,7 @@ } if (result.IsPendingApproval) { - _errorMessage = "Account created. An administrator must approve it before you can sign in."; + _infoMessage = "Account created. An administrator must approve it before you can sign in."; return; } await CredentialStore.StoreAsync(StoredTokenKey, result.Token!).ConfigureAwait(false);