diff --git a/src/design/App.Android/MainActivity.cs b/src/design/App.Android/MainActivity.cs
index fbb97ff1f..a8355c477 100644
--- a/src/design/App.Android/MainActivity.cs
+++ b/src/design/App.Android/MainActivity.cs
@@ -18,7 +18,8 @@ namespace App.Android;
[Activity(
Label = "Angor",
Theme = "@style/MyTheme.NoActionBar",
- Icon = "@drawable/icon",
+ Icon = "@mipmap/icon",
+ RoundIcon = "@mipmap/icon_round",
MainLauncher = true,
ScreenOrientation = ScreenOrientation.Portrait,
WindowSoftInputMode = SoftInput.AdjustNothing,
diff --git a/src/design/App.Android/Resources/drawable/icon_foreground.xml b/src/design/App.Android/Resources/drawable/icon_foreground.xml
new file mode 100644
index 000000000..2b06cbae5
--- /dev/null
+++ b/src/design/App.Android/Resources/drawable/icon_foreground.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/design/App.Android/Resources/mipmap-anydpi-v26/icon.xml b/src/design/App.Android/Resources/mipmap-anydpi-v26/icon.xml
new file mode 100644
index 000000000..4186747fc
--- /dev/null
+++ b/src/design/App.Android/Resources/mipmap-anydpi-v26/icon.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/src/design/App.Android/Resources/mipmap-anydpi-v26/icon_round.xml b/src/design/App.Android/Resources/mipmap-anydpi-v26/icon_round.xml
new file mode 100644
index 000000000..4186747fc
--- /dev/null
+++ b/src/design/App.Android/Resources/mipmap-anydpi-v26/icon_round.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/src/design/App.Android/Resources/mipmap/icon.png b/src/design/App.Android/Resources/mipmap/icon.png
new file mode 100644
index 000000000..fb866e30e
Binary files /dev/null and b/src/design/App.Android/Resources/mipmap/icon.png differ
diff --git a/src/design/App.Android/Resources/values/colors.xml b/src/design/App.Android/Resources/values/colors.xml
index 6ae9cea61..dc04eab73 100644
--- a/src/design/App.Android/Resources/values/colors.xml
+++ b/src/design/App.Android/Resources/values/colors.xml
@@ -3,4 +3,6 @@
#FFFFFF
@color/splash_background
+
+ #5FAF78
diff --git a/src/design/App.Test.Integration/LayoutRegression/ResponsivenessRegressionTests.cs b/src/design/App.Test.Integration/LayoutRegression/ResponsivenessRegressionTests.cs
index b6dcddaea..ca8098dea 100644
--- a/src/design/App.Test.Integration/LayoutRegression/ResponsivenessRegressionTests.cs
+++ b/src/design/App.Test.Integration/LayoutRegression/ResponsivenessRegressionTests.cs
@@ -80,7 +80,7 @@ public void ManageProjectContentView_stays_responsive_after_detach_and_reattach(
}
[AvaloniaFact]
- public void MyProjectsView_sidebar_strips_desktop_chrome_on_mobile()
+ public void MyProjectsView_sidebar_hidden_on_mobile()
{
LayoutModeService.Instance.UpdateWidth(1280);
@@ -96,13 +96,13 @@ public void MyProjectsView_sidebar_strips_desktop_chrome_on_mobile()
sidebar.Should().NotBeNull();
SetWidth(window, 390);
- // On mobile the sidebar hero card must not render as an empty Surface box:
- // background/border stripped, hero elements hidden, mobile actions shown.
- sidebar!.Background.Should().BeNull("mobile sidebar must not render an empty background card");
- view.FindControl("SidebarStats")!.IsVisible.Should().BeFalse();
+ // On mobile the sidebar is removed entirely — no empty box; the content
+ // column shows the mobile action buttons instead.
+ sidebar!.IsVisible.Should().BeFalse("mobile must not render the sidebar at all");
view.FindControl("MobileActionPanel")!.IsVisible.Should().BeTrue();
SetWidth(window, 1280);
+ sidebar.IsVisible.Should().BeTrue("desktop sidebar must come back");
sidebar.Background.Should().NotBeNull("desktop sidebar chrome must come back");
view.FindControl("SidebarStats")!.IsVisible.Should().BeTrue();
}
diff --git a/src/design/App/UI/Sections/Funders/FundersView.axaml b/src/design/App/UI/Sections/Funders/FundersView.axaml
index b48ccab18..16f8448e5 100644
--- a/src/design/App/UI/Sections/Funders/FundersView.axaml
+++ b/src/design/App/UI/Sections/Funders/FundersView.axaml
@@ -706,6 +706,7 @@
@@ -749,6 +750,7 @@
@@ -820,7 +822,7 @@
IsVisible="{Binding IsExpanded}"
BorderThickness="0,1,0,0"
BorderBrush="{DynamicResource Stroke}"
- Padding="0,16,0,0">
+ Padding="0,16,0,16">
brush,
- Avalonia.Media.Color color => new Avalonia.Media.Immutable.ImmutableSolidColorBrush(color),
- _ => _myProjectsSidebar.Background,
- };
- }
- if (this.TryFindResource("ItemShadow", ActualThemeVariant, out var shadowRes) && shadowRes is Avalonia.Media.BoxShadows shadow)
- _myProjectsSidebar.BoxShadow = shadow;
- _myProjectsSidebar.BorderThickness = new Avalonia.Thickness(1);
+ RestoreSidebarChrome();
_myProjectsSidebar.Padding = new Avalonia.Thickness(24);
Grid.SetColumn(_myProjectsContent, 1);
@@ -195,6 +174,28 @@ private void ApplyResponsiveLayout(LayoutMode mode)
}
}
+ ///
+ /// Restore card styling on the sidebar Border. ClearValue does NOT work here: the
+ /// XAML DynamicResource assignment is itself a local value, so clearing it removes
+ /// the binding permanently. Resolve the theme resources explicitly instead.
+ ///
+ private void RestoreSidebarChrome()
+ {
+ if (_myProjectsSidebar == null) return;
+ if (this.TryFindResource("Surface", ActualThemeVariant, out var surfaceRes))
+ {
+ _myProjectsSidebar.Background = surfaceRes switch
+ {
+ Avalonia.Media.IBrush brush => brush,
+ Avalonia.Media.Color color => new Avalonia.Media.Immutable.ImmutableSolidColorBrush(color),
+ _ => _myProjectsSidebar.Background,
+ };
+ }
+ if (this.TryFindResource("ItemShadow", ActualThemeVariant, out var shadowRes) && shadowRes is Avalonia.Media.BoxShadows shadow)
+ _myProjectsSidebar.BoxShadow = shadow;
+ _myProjectsSidebar.BorderThickness = new Avalonia.Thickness(1);
+ }
+
///
/// Mobile perf: force a measure pass on the hidden ManageProjectView so its
/// template, resources, and layout are realized before the first drill-down.
diff --git a/src/design/App/UI/Shared/Controls/ProjectCard.axaml b/src/design/App/UI/Shared/Controls/ProjectCard.axaml
index 5b95028e9..f363b600c 100644
--- a/src/design/App/UI/Shared/Controls/ProjectCard.axaml
+++ b/src/design/App/UI/Shared/Controls/ProjectCard.axaml
@@ -174,6 +174,7 @@
DockPanel.Dock="Left"
IsVisible="False">
diff --git a/src/design/App/UI/Shared/SectionPanel.cs b/src/design/App/UI/Shared/SectionPanel.cs
index 6d9eee67a..96bd40929 100644
--- a/src/design/App/UI/Shared/SectionPanel.cs
+++ b/src/design/App/UI/Shared/SectionPanel.cs
@@ -36,6 +36,24 @@ public void AddSection(string key, Control view)
{
if (_sections.ContainsKey(key)) return;
+ // Android activity relaunch (config change outside ConfigurationChanges)
+ // rebuilds ShellView + SectionPanel, but the section views come from the
+ // ViewModel's cache and may still be parented to the previous (dead)
+ // SectionPanel. Detach from the old parent first or Children.Add throws
+ // "already has a visual parent" and crashes the app on relaunch.
+ switch (view.Parent)
+ {
+ case Panel oldPanel when oldPanel != this:
+ oldPanel.Children.Remove(view);
+ break;
+ case ContentControl oldContent:
+ oldContent.Content = null;
+ break;
+ case Decorator oldDecorator:
+ oldDecorator.Child = null;
+ break;
+ }
+
view.IsVisible = false;
_sections[key] = view;
diff --git a/src/design/App/UI/Shell/ShellViewModel.cs b/src/design/App/UI/Shell/ShellViewModel.cs
index 60f3f0a20..01c640f61 100644
--- a/src/design/App/UI/Shell/ShellViewModel.cs
+++ b/src/design/App/UI/Shell/ShellViewModel.cs
@@ -243,6 +243,27 @@ public PrototypeSettings(IStore store, ILogger logger)
? result.Value.IsDarkTheme
: null;
}
+
+ // Icon-bake workaround: Optris i:Icon snapshots its Foreground brush
+ // into the rendered image at construction — before the theme
+ // dictionaries are reachable it bakes the black fallback. A
+ // RequestedThemeVariant change re-fires every DynamicResource
+ // foreground and re-bakes all icons. Settings-load with a theme
+ // override triggers that flip via IsDarkTheme; first launch (no
+ // settings) never does, leaving black icons in dark mode. Force
+ // one flip once startup theming has settled.
+ if ((!result.IsSuccess || !result.Value.HasThemeOverride) && Application.Current != null)
+ {
+ Avalonia.Threading.Dispatcher.UIThread.Post(() =>
+ {
+ if (Application.Current is not { } app) return;
+ var osIsDark = app.ActualThemeVariant == Avalonia.Styling.ThemeVariant.Dark;
+ app.RequestedThemeVariant = osIsDark
+ ? Avalonia.Styling.ThemeVariant.Light
+ : Avalonia.Styling.ThemeVariant.Dark;
+ app.RequestedThemeVariant = Avalonia.Styling.ThemeVariant.Default;
+ }, Avalonia.Threading.DispatcherPriority.Loaded);
+ }
});
});
diff --git a/src/design/App/UI/Themes/V2/Styles/Icons.axaml b/src/design/App/UI/Themes/V2/Styles/Icons.axaml
index 24bef893f..af67c30f4 100644
--- a/src/design/App/UI/Themes/V2/Styles/Icons.axaml
+++ b/src/design/App/UI/Themes/V2/Styles/Icons.axaml
@@ -10,6 +10,23 @@
+
+
+