Skip to content

Commit fe2c8d7

Browse files
committed
Group 46 correction: embed integration control centre
1 parent 303bc1a commit fe2c8d7

6 files changed

Lines changed: 125 additions & 79 deletions

File tree

LifeOS.Desktop/IntegrationControlCentreWindow.xaml renamed to LifeOS.Desktop/IntegrationControlCentreView.xaml

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,11 @@
1-
<Window x:Class="LifeOS.Desktop.IntegrationControlCentreWindow"
1+
<UserControl x:Class="LifeOS.Desktop.IntegrationControlCentreView"
22
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
33
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4-
Title="LifeOS — Integration Control Centre"
5-
Width="1500"
6-
Height="900"
7-
MinWidth="1100"
8-
MinHeight="720"
9-
WindowStartupLocation="CenterOwner"
10-
Background="{DynamicResource LifeOS.Brush.Window}"
4+
Background="Transparent"
115
Foreground="{DynamicResource LifeOS.Brush.TextPrimary}"
6+
Focusable="True"
127
AutomationProperties.AutomationId="IntegrationControlCentre.Root">
13-
<Window.Resources>
8+
<UserControl.Resources>
149
<Style x:Key="Integration.Heading" TargetType="TextBlock">
1510
<Setter Property="FontSize" Value="20" />
1611
<Setter Property="FontWeight" Value="SemiBold" />
@@ -71,9 +66,9 @@
7166
</Trigger>
7267
</Style.Triggers>
7368
</Style>
74-
</Window.Resources>
69+
</UserControl.Resources>
7570

76-
<Grid x:Name="RootGrid" Margin="24">
71+
<Grid x:Name="RootGrid" Margin="0" MinHeight="720">
7772
<Grid.RowDefinitions>
7873
<RowDefinition Height="Auto" />
7974
<RowDefinition Height="Auto" />
@@ -114,7 +109,7 @@
114109
</Border>
115110
</WrapPanel>
116111

117-
<TabControl Grid.Row="2" x:Name="ControlTabs" Background="Transparent" BorderBrush="{DynamicResource LifeOS.Brush.Border}" AutomationProperties.AutomationId="IntegrationControlCentre.Tabs">
112+
<TabControl Grid.Row="2" x:Name="ControlTabs" Height="560" Background="Transparent" BorderBrush="{DynamicResource LifeOS.Brush.Border}" AutomationProperties.AutomationId="IntegrationControlCentre.Tabs">
118113
<TabItem Header="Overview" AutomationProperties.AutomationId="IntegrationControlCentre.Tab.Overview">
119114
<ScrollViewer VerticalScrollBarVisibility="Auto" Padding="16">
120115
<StackPanel>
@@ -285,7 +280,7 @@
285280
<Grid Grid.Row="3" Margin="0,14,0,0">
286281
<Grid.ColumnDefinitions><ColumnDefinition Width="*" /><ColumnDefinition Width="Auto" /></Grid.ColumnDefinitions>
287282
<TextBlock Text="Local state: %LOCALAPPDATA%\LifeOS\integration-control-centre.json · No OAuth token, client secret or authorization code is stored in this model." VerticalAlignment="Center" Style="{StaticResource Integration.Caption}" />
288-
<Button Grid.Column="1" Content="Close" Click="CloseButton_Click" Style="{StaticResource Integration.Button}" Margin="12,0,0,0" AutomationProperties.AutomationId="IntegrationControlCentre.Close" />
283+
<Button Grid.Column="1" Content="Back to Settings" Click="BackButton_Click" Style="{StaticResource Integration.Button}" Margin="12,0,0,0" AutomationProperties.AutomationId="IntegrationControlCentre.Back" />
289284
</Grid>
290285
</Grid>
291-
</Window>
286+
</UserControl>

LifeOS.Desktop/IntegrationControlCentreWindow.xaml.cs renamed to LifeOS.Desktop/IntegrationControlCentreView.xaml.cs

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55

66
namespace LifeOS.Desktop;
77

8-
public partial class IntegrationControlCentreWindow : Window
8+
public partial class IntegrationControlCentreView : UserControl
99
{
1010
private readonly string _storePath;
1111
private readonly IntegrationControlCentreService _service;
1212
private string? _selectedAccountId;
1313

14-
public IntegrationControlCentreWindow(bool compactDensity)
14+
public event EventHandler? BackRequested;
15+
16+
public IntegrationControlCentreView(bool compactDensity)
1517
{
1618
InitializeComponent();
1719

@@ -21,7 +23,7 @@ public IntegrationControlCentreWindow(bool compactDensity)
2123
_storePath);
2224
_service = new IntegrationControlCentreService(state);
2325

24-
RootGrid.Margin = compactDensity ? new Thickness(16) : new Thickness(24);
26+
ApplyDensity(compactDensity);
2527
RefreshView();
2628
}
2729

@@ -255,10 +257,8 @@ private void ReconnectButton_Click(object sender, RoutedEventArgs e)
255257
IntegrationConnectionReviewDialog dialog = new(
256258
account.DisplayName,
257259
account.ConnectionState,
258-
IntegrationConnectionReviewMode.Reconnect)
259-
{
260-
Owner = this
261-
};
260+
IntegrationConnectionReviewMode.Reconnect);
261+
SetDialogOwner(dialog);
262262

263263
if (dialog.ShowDialog() != true)
264264
{
@@ -281,10 +281,8 @@ private void RevokeButton_Click(object sender, RoutedEventArgs e)
281281
IntegrationConnectionReviewDialog dialog = new(
282282
account.DisplayName,
283283
account.ConnectionState,
284-
IntegrationConnectionReviewMode.Revoke)
285-
{
286-
Owner = this
287-
};
284+
IntegrationConnectionReviewMode.Revoke);
285+
SetDialogOwner(dialog);
288286

289287
if (dialog.ShowDialog() != true)
290288
{
@@ -304,10 +302,8 @@ private void DisconnectButton_Click(object sender, RoutedEventArgs e)
304302
}
305303

306304
ConnectedIntegrationAccount account = _service.GetRequiredAccount(_selectedAccountId);
307-
IntegrationDisconnectReviewDialog dialog = new(account.DisplayName)
308-
{
309-
Owner = this
310-
};
305+
IntegrationDisconnectReviewDialog dialog = new(account.DisplayName);
306+
SetDialogOwner(dialog);
311307

312308
if (dialog.ShowDialog() != true || dialog.SelectedChoice is null)
313309
{
@@ -341,7 +337,22 @@ exception is IOException or
341337
}
342338
}
343339

344-
private void CloseButton_Click(object sender, RoutedEventArgs e) => Close();
340+
public void ApplyDensity(bool compactDensity)
341+
{
342+
ControlTabs.Height = compactDensity ? 520 : 560;
343+
}
344+
345+
private void SetDialogOwner(Window dialog)
346+
{
347+
Window? owner = Window.GetWindow(this) ?? Application.Current?.MainWindow;
348+
if (owner is { IsVisible: true })
349+
{
350+
dialog.Owner = owner;
351+
}
352+
}
353+
354+
private void BackButton_Click(object sender, RoutedEventArgs e) =>
355+
BackRequested?.Invoke(this, EventArgs.Empty);
345356

346357
private static string FormatTimestamp(DateTimeOffset? timestampUtc) =>
347358
timestampUtc is null

LifeOS.Desktop/V8ShellWindow.xaml

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Window x:Class="LifeOS.Desktop.V8ShellWindow"
1+
<Window x:Class="LifeOS.Desktop.V8ShellWindow"
22
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
33
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
44
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
@@ -220,7 +220,7 @@
220220
HorizontalScrollBarVisibility="Disabled">
221221
<Grid>
222222
<StackPanel x:Name="WorkspaceRoot" Margin="24">
223-
<Grid>
223+
<Grid x:Name="WorkspaceHeaderGrid">
224224
<Grid.ColumnDefinitions><ColumnDefinition Width="*" /><ColumnDefinition Width="Auto" /></Grid.ColumnDefinitions>
225225
<StackPanel>
226226
<TextBlock x:Name="WorkspaceEyebrow" Text="TODAY / PRESSURE / WORK / MONEY" Style="{StaticResource LifeOS.Text.Caption}" />
@@ -283,7 +283,8 @@
283283
</ItemsControl.ItemTemplate>
284284
</ItemsControl>
285285

286-
<StackPanel x:Name="SettingsRoot" Visibility="Collapsed" Margin="0,8,0,0">
286+
<Grid x:Name="SettingsRoot" Visibility="Collapsed" Margin="0,8,0,0">
287+
<StackPanel x:Name="SettingsOverviewPanel">
287288
<TextBlock Text="Appearance" Style="{StaticResource LifeOS.Text.Heading}" />
288289
<WrapPanel Margin="0,10,0,12">
289290
<Border Style="{StaticResource LifeOS.Card}" Width="330"><StackPanel><TextBlock Text="Theme" Style="{StaticResource LifeOS.Text.Body}" /><ComboBox x:Name="ThemeComboBox" Style="{StaticResource LifeOS.ComboBoxStyle}" AutomationProperties.AutomationId="Settings.Appearance.Theme" /><TextBlock Text="Light, Dark, System or High Contrast." Margin="0,6,0,0" Style="{StaticResource LifeOS.Text.Caption}" /></StackPanel></Border>
@@ -352,7 +353,14 @@
352353
<Button Content="Reset Approved Defaults" Click="ResetSettingsButton_Click" Style="{StaticResource LifeOS.Button}" AutomationProperties.AutomationId="Settings.Reset" />
353354
<TextBlock x:Name="SettingsSaveStatusText" Margin="16,10,0,0" Style="{StaticResource LifeOS.Text.Body}" />
354355
</WrapPanel>
355-
</StackPanel>
356+
</StackPanel>
357+
358+
<ContentControl x:Name="SettingsSubpageHost"
359+
Visibility="Collapsed"
360+
HorizontalContentAlignment="Stretch"
361+
VerticalContentAlignment="Top"
362+
AutomationProperties.AutomationId="Settings.SubpageHost" />
363+
</Grid>
356364
</StackPanel>
357365
</Grid>
358366
</ScrollViewer>

LifeOS.Desktop/V8ShellWindow.xaml.cs

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Collections.Generic;
33
using System.ComponentModel;
44
using System.Linq;
@@ -30,7 +30,7 @@ public partial class V8ShellWindow : Window
3030
private bool _contextOpen;
3131
private IInputElement? _focusBeforeCommand;
3232
private IInputElement? _focusBeforeContext;
33-
private IntegrationControlCentreWindow? _integrationControlCentreWindow;
33+
private IntegrationControlCentreView? _integrationControlCentreView;
3434
private WorkspaceSnapshot _snapshot = WorkspaceSnapshot.Load();
3535

3636
private bool IsCommandOpen => CommandOverlay.Visibility == Visibility.Visible;
@@ -201,6 +201,8 @@ private void ApplyDensity()
201201
: new Thickness(0, 0, 12, 12);
202202
}
203203
}
204+
205+
_integrationControlCentreView?.ApplyDensity(compact);
204206
}
205207

206208
private void WorkspaceNav_Click(object sender, RoutedEventArgs e)
@@ -248,6 +250,7 @@ private void NavigateTo(string? requestedWorkspace, bool persist = true)
248250
SettingsRoot.Visibility = definition.Name == "Settings"
249251
? Visibility.Visible
250252
: Visibility.Collapsed;
253+
ShowSettingsOverview(scrollToTop: false);
251254

252255
ContextTitle.Text = $"{definition.Name} context";
253256
ContextBody.Text = definition.ContextSummary;
@@ -340,6 +343,13 @@ private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
340343
return;
341344
}
342345

346+
if (e.Key == Key.Escape && IsIntegrationControlCentreOpen)
347+
{
348+
ShowSettingsOverview(scrollToTop: true);
349+
e.Handled = true;
350+
return;
351+
}
352+
343353
if (controlK)
344354
{
345355
OpenCommand();
@@ -579,29 +589,44 @@ private void RestoreContextFocus()
579589
Keyboard.Focus(ContextButton);
580590
}
581591

592+
private bool IsIntegrationControlCentreOpen =>
593+
SettingsSubpageHost.Visibility == Visibility.Visible;
594+
582595
private void OpenIntegrationControlCentre_Click(object sender, RoutedEventArgs e)
583596
{
584-
if (_integrationControlCentreWindow is { IsLoaded: true } existingWindow)
585-
{
586-
if (existingWindow.WindowState == WindowState.Minimized)
587-
{
588-
existingWindow.WindowState = WindowState.Normal;
589-
}
597+
_integrationControlCentreView ??= CreateIntegrationControlCentreView();
598+
_integrationControlCentreView.ApplyDensity(
599+
_preferences.Density == V8Density.Compact || ActualWidth <= 980);
590600

591-
existingWindow.Activate();
592-
return;
593-
}
601+
SettingsOverviewPanel.Visibility = Visibility.Collapsed;
602+
SettingsSubpageHost.Content = _integrationControlCentreView;
603+
SettingsSubpageHost.Visibility = Visibility.Visible;
604+
WorkspaceHeaderGrid.Visibility = Visibility.Collapsed;
605+
MetricItems.Visibility = Visibility.Collapsed;
606+
WorkspaceScrollViewer.ScrollToTop();
607+
Keyboard.Focus(_integrationControlCentreView);
608+
}
594609

595-
IntegrationControlCentreWindow window = new(
596-
_preferences.Density == V8Density.Compact)
597-
{
598-
Owner = this
599-
};
610+
private IntegrationControlCentreView CreateIntegrationControlCentreView()
611+
{
612+
IntegrationControlCentreView view = new(
613+
_preferences.Density == V8Density.Compact || ActualWidth <= 980);
614+
view.BackRequested += (_, _) => ShowSettingsOverview(scrollToTop: true);
615+
return view;
616+
}
617+
618+
private void ShowSettingsOverview(bool scrollToTop)
619+
{
620+
SettingsSubpageHost.Visibility = Visibility.Collapsed;
621+
SettingsOverviewPanel.Visibility = Visibility.Visible;
622+
WorkspaceHeaderGrid.Visibility = Visibility.Visible;
623+
MetricItems.Visibility = Visibility.Visible;
600624

601-
_integrationControlCentreWindow = window;
602-
window.Closed += (_, _) => _integrationControlCentreWindow = null;
603-
window.Show();
604-
window.Activate();
625+
if (scrollToTop)
626+
{
627+
WorkspaceScrollViewer.ScrollToTop();
628+
Keyboard.Focus(SettingsNav);
629+
}
605630
}
606631

607632
private void OpenModule_Click(object sender, RoutedEventArgs e)

docs/manual-tests/group-46/README.md

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
# Group 46 manual verification — Integration Control Centre
22

3-
Group 46 starts the LifeOS v9 integration lane at `v9.0.0-alpha.1`. It adds the permanent system-level Integration Control Centre under Settings. This group uses deterministic fictional providers and accounts only.
3+
Group 46 starts the LifeOS v9 integration lane at `v9.0.0-alpha.1`. It adds the permanent system-level Integration Control Centre as an embedded Settings subpage. This group uses deterministic fictional providers and accounts only.
44

55
## Entry conditions
66

7-
- Repository is on the exact Group 45 baseline before Pack 1 is applied.
8-
- Pack 1 tests, builds, scans, commit, push and synchronization have completed successfully.
7+
- Group 46 Pack 1 and both Settings ComboBox corrections are committed, pushed and synchronized.
98
- Desktop launches through the permanent eight-workspace v8 shell.
109
- No live Microsoft or Google credential, token, account or external provider read is used.
1110

@@ -22,26 +21,27 @@ Close LifeOS and delete only that file to restore the deterministic fictional pr
2221
## Required manual checks
2322

2423
1. Open **Settings** and confirm **Integration Control Centre** appears inside the Integrations section rather than as a ninth workspace.
25-
2. Confirm Theme, Accent, Density and Startup workspace retain the established dark custom ComboBox presentation and remain enabled.
26-
3. Open the Control Centre and confirm it opens modelessly: Settings remains enabled, the four dropdowns do not switch to native white rendering, and reopening the command activates the existing Control Centre instead of creating duplicates.
27-
4. Confirm all provider cards state **FICTIONAL** and the top boundary states **NO LIVE CREDENTIALS**.
28-
5. Confirm the provider catalogue exposes Microsoft 365, Google Workspace and Local connectors through one shared lifecycle/permission/health contract.
29-
6. Confirm the Work account and Personal account are visibly separate, with classification, provider identity and connection state shown independently.
30-
7. Open **Capabilities & permissions** and confirm Required/Optional is separate from Granted/Missing/Revoked/Not Requested.
31-
8. Confirm capability health is calculated separately: healthy Mail, stale Calendar and Needs Attention Files must be simultaneously visible for the fictional work account.
32-
9. Run Refresh and confirm bounded, explicit audit output; no external data or write action occurs.
33-
10. Open Reconnect review, cancel once, then explicitly confirm and verify ordered audit entries.
34-
11. Open Revoke review and confirm the account, current state, action effect and no-live-provider boundary are visible before confirmation.
35-
12. Open Disconnect review and verify all three explicit retention choices:
36-
- keep accepted LifeOS records;
37-
- archive provider links;
38-
- remove unaccepted imported candidates.
39-
13. Confirm accepted LifeOS records are never described as silently deleted by disconnect or revoke.
40-
14. Open Audit history and verify consent, sync, permission, reconnect, revoke and disconnect events are ordered, timestamped and sanitized.
41-
15. Verify the Control Centre in Light, Dark and High Contrast themes.
42-
16. Verify Comfortable and Compact density plus 100%, 110%, 125% and 140% text scale remain usable.
43-
17. Verify keyboard focus is visible and the key controls expose stable AutomationIds.
44-
18. Confirm the local JSON contains no client secret, access token, refresh token, authorization code or password field.
24+
2. Confirm Theme, Accent, Density, Startup workspace and Text scale retain the established dark custom ComboBox presentation.
25+
3. Select **Open Integration Control Centre** and confirm it replaces the Settings overview inside the existing LifeOS shell.
26+
4. Confirm the left workspace rail, top bar, main window title bar and normal window movement/resizing remain available.
27+
5. Confirm no second LifeOS window, borderless overlay or laptop-sized immovable surface is created.
28+
6. Select **Back to Settings** and confirm the normal Settings overview returns at the top of the page.
29+
7. Reopen the Control Centre and confirm only one embedded instance is used.
30+
8. Press **Escape** while the Control Centre is open and confirm it returns to the Settings overview.
31+
9. Navigate directly to another workspace while the Control Centre is open and confirm the normal workspace header and metrics are restored.
32+
10. Confirm all provider cards state **FICTIONAL** and the boundary states **NO LIVE CREDENTIALS**.
33+
11. Confirm Microsoft 365, Google Workspace and Local connectors use the shared account, permission, health, audit and recovery contract.
34+
12. Confirm the Work account and Personal account are visibly separate.
35+
13. Open **Capabilities & permissions** and confirm Required/Optional is separate from Granted/Missing/Revoked/Not Requested.
36+
14. Confirm healthy, stale and Needs Attention capability states can be visible simultaneously.
37+
15. Run Refresh and confirm no external read or write occurs.
38+
16. Open Reconnect, Revoke and Disconnect reviews and confirm only those review confirmations open as modal dialogs owned by the main LifeOS window.
39+
17. Verify all three disconnect retention choices.
40+
18. Open Audit history and verify ordered, timestamped and sanitized entries.
41+
19. Verify Light, Dark and High Contrast themes.
42+
20. Verify Comfortable and Compact density plus 100%, 110%, 125% and 140% text scale.
43+
21. Verify keyboard focus, AutomationIds and inner scrolling remain usable on the laptop display.
44+
22. Confirm the local JSON contains no client secret, access token, refresh token, authorization code or password field.
4545

4646
## Stop rule
4747

docs/release-notes/v9.0-alpha-group-46-integration-control-centre.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,15 @@ Group 46 creates the permanent system-level control surface and shared contracts
1717
- Provider-neutral adapter and normalized connector-result contracts.
1818
- Atomic local JSON persistence outside Git.
1919
- Deterministic fictional Microsoft, Google and local proof providers/accounts.
20-
- Modeless Control Centre launch that keeps Settings active and reuses the existing window.
21-
- Restored v8 custom ComboBox presentation for Theme, Accent, Density and Startup workspace.
20+
21+
## Corrected before screenshot approval
22+
23+
- Replaced the separate borderless Control Centre window with an embedded Settings subpage inside the existing LifeOS shell.
24+
- Preserved the normal title bar, workspace rail, top bar, resizing and window movement.
25+
- Added **Back to Settings** and Escape navigation.
26+
- Restored the normal workspace header and metrics when leaving the embedded subpage.
27+
- Kept only reconnect, revoke and disconnect confirmations as owned modal dialogs.
28+
- Preserved the established custom ComboBox presentation for Theme, Accent, Density, Startup workspace and Text scale.
2229

2330
## Safety boundary
2431

0 commit comments

Comments
 (0)