Skip to content

Commit 8b55fdb

Browse files
committed
fix(modules): remove dev import, correct status, fix home installed count
1 parent 0862007 commit 8b55fdb

File tree

29 files changed

+1164
-531
lines changed

29 files changed

+1164
-531
lines changed

src/Hosts/Modulus.Host.Avalonia/App.axaml.cs

Lines changed: 12 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -96,26 +96,12 @@ public override void OnFrameworkInitializationCompleted()
9696
// Module Directories - explicit module installation paths
9797
var moduleDirectories = new System.Collections.Generic.List<ModuleDirectory>();
9898

99-
#if DEBUG
100-
// Development: Load from artifacts/Modules/ (populated by nuke build-module)
101-
var solutionRoot = FindSolutionRoot(AppContext.BaseDirectory);
102-
if (solutionRoot != null)
103-
{
104-
var artifactsModules = Path.Combine(solutionRoot, "artifacts", "Modules");
105-
if (Directory.Exists(artifactsModules))
106-
{
107-
// User modules from artifacts - NOT system modules
108-
moduleDirectories.Add(new ModuleDirectory(artifactsModules, IsSystem: false));
109-
}
110-
}
111-
#else
112-
// Production: Load from {AppBaseDir}/Modules/
99+
// Built-in modules: ALWAYS load from {AppBaseDir}/Modules/ (dev/prod consistent)
113100
var appModules = Path.Combine(AppContext.BaseDirectory, "Modules");
114101
if (Directory.Exists(appModules))
115102
{
116103
moduleDirectories.Add(new ModuleDirectory(appModules, IsSystem: true));
117104
}
118-
#endif
119105

120106
// User-installed modules (for runtime installation)
121107
var userModules = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Modulus", "Modules");
@@ -179,8 +165,17 @@ await ModulusApplicationFactory.CreateAsync<AvaloniaHostModule>(services, module
179165
var shellVm = Services.GetRequiredService<ShellViewModel>();
180166
mainWindow.DataContext = shellVm;
181167

182-
// Navigate to default view (Home)
183-
shellVm.NavigateToRoute("Modulus.Modules.Home.ViewModels.HomeViewModel");
168+
// Navigate to default view: first main menu item by Order (typically Home).
169+
// Avoid hard-coded legacy NavigationKey.
170+
var menuRegistry = Services.GetRequiredService<IMenuRegistry>();
171+
var defaultMenu = menuRegistry.GetItems(MenuLocation.Main)
172+
.OrderBy(m => m.Order)
173+
.FirstOrDefault();
174+
175+
if (defaultMenu != null && !string.IsNullOrWhiteSpace(defaultMenu.NavigationKey))
176+
{
177+
shellVm.NavigateToRoute(defaultMenu.NavigationKey);
178+
}
184179
}
185180

186181
base.OnFrameworkInitializationCompleted();
@@ -196,20 +191,4 @@ public void ToggleTheme()
196191
var current = RequestedThemeVariant;
197192
RequestedThemeVariant = current == ThemeVariant.Dark ? ThemeVariant.Light : ThemeVariant.Dark;
198193
}
199-
200-
#if DEBUG
201-
private static string? FindSolutionRoot(string startPath)
202-
{
203-
var dir = new DirectoryInfo(startPath);
204-
while (dir != null)
205-
{
206-
if (File.Exists(Path.Combine(dir.FullName, "Modulus.sln")))
207-
{
208-
return dir.FullName;
209-
}
210-
dir = dir.Parent;
211-
}
212-
return null;
213-
}
214-
#endif
215194
}

src/Hosts/Modulus.Host.Avalonia/Services/AvaloniaNavigationService.cs

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,7 @@ public AvaloniaNavigationService(
6060

6161
public async Task<bool> NavigateToAsync(string navigationKey, NavigationOptions? options = null)
6262
{
63-
_logger.LogInformation("NavigateToAsync called: {NavigationKey}", navigationKey);
64-
63+
// Intentionally no logs for normal navigation; only Warning/Error logs for abnormal behavior are emitted.
6564
try
6665
{
6766
return await NavigateToAsyncCore(navigationKey, options);
@@ -98,9 +97,11 @@ private async Task<bool> NavigateToAsyncCore(string navigationKey, NavigationOpt
9897
// Lazy load module if needed
9998
if (menuItem?.ModuleId != null)
10099
{
101-
_logger.LogInformation("Lazy loading module {ModuleId}...", menuItem.ModuleId);
102100
var loaded = await _lazyModuleLoader.EnsureModuleLoadedAsync(menuItem.ModuleId);
103-
_logger.LogInformation("Module {ModuleId} load result: {Loaded}", menuItem.ModuleId, loaded);
101+
if (!loaded)
102+
{
103+
_logger.LogWarning("Lazy loading module failed: {ModuleId}", menuItem.ModuleId);
104+
}
104105
}
105106

106107
// Resolve ViewModel type
@@ -139,7 +140,6 @@ private async Task<bool> NavigateToAsyncCore(string navigationKey, NavigationOpt
139140
}
140141

141142
// Create view
142-
_logger.LogInformation("Creating view for ViewModel {ViewModelType}...", vmType.Name);
143143
object? view;
144144
try
145145
{
@@ -159,8 +159,6 @@ private async Task<bool> NavigateToAsyncCore(string navigationKey, NavigationOpt
159159
return false;
160160
}
161161

162-
_logger.LogInformation("View created: {ViewType}", view.GetType().Name);
163-
164162
// Update state
165163
var previousKey = _currentNavigationKey;
166164
_currentNavigationKey = navigationKey;
@@ -179,7 +177,6 @@ private async Task<bool> NavigateToAsyncCore(string navigationKey, NavigationOpt
179177
ViewModel = viewModel
180178
});
181179

182-
_logger.LogInformation("Navigation successful: {NavigationKey} -> {ViewType}", navigationKey, view.GetType().Name);
183180
return true;
184181
}
185182

@@ -266,13 +263,10 @@ private async Task<bool> EvaluateGuardsAsync(NavigationContext context)
266263

267264
private Type? ResolveViewModelType(string navigationKey)
268265
{
269-
_logger.LogDebug("ResolveViewModelType: {NavigationKey}", navigationKey);
270-
271266
// Try direct type resolution
272267
var vmType = Type.GetType(navigationKey);
273268
if (vmType != null)
274269
{
275-
_logger.LogDebug("Resolved via Type.GetType: {Type}", vmType.FullName);
276270
return vmType;
277271
}
278272

@@ -287,7 +281,6 @@ private async Task<bool> EvaluateGuardsAsync(NavigationContext context)
287281

288282
if (vmType != null)
289283
{
290-
_logger.LogDebug("Resolved via AppDomain assemblies: {Type}", vmType.FullName);
291284
return vmType;
292285
}
293286

@@ -355,8 +348,6 @@ private async Task<bool> EvaluateGuardsAsync(NavigationContext context)
355348
// Use execution guard for module ViewModels
356349
if (moduleId != null)
357350
{
358-
_logger.LogDebug("Creating module ViewModel {ViewModelType} for module {ModuleId}", vmType.Name, moduleId);
359-
360351
var result = _executionGuard.ExecuteSafe(
361352
moduleId,
362353
() => CreateViewModelCore(vmType),
@@ -376,7 +367,6 @@ private async Task<bool> EvaluateGuardsAsync(NavigationContext context)
376367
}
377368

378369
// Host ViewModels don't need guard
379-
_logger.LogDebug("Creating host ViewModel {ViewModelType}", vmType.Name);
380370
return CreateViewModelCore(vmType);
381371
}
382372

src/Hosts/Modulus.Host.Avalonia/Shell/ViewModels/ModuleListViewModel.cs

Lines changed: 46 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Linq;
66
using System.Threading;
77
using System.Threading.Tasks;
8+
using Avalonia.Threading;
89
using CommunityToolkit.Mvvm.ComponentModel;
910
using CommunityToolkit.Mvvm.Input;
1011
using CommunityToolkit.Mvvm.Messaging;
@@ -38,9 +39,6 @@ public partial class ModuleListViewModel : ViewModelBase
3839

3940
public ObservableCollection<ModuleViewModel> Modules { get; } = new();
4041

41-
[ObservableProperty]
42-
private string _importPath = string.Empty;
43-
4442
[ObservableProperty]
4543
[NotifyPropertyChangedFor(nameof(FilteredModules))]
4644
[NotifyPropertyChangedFor(nameof(EnabledModules))]
@@ -133,6 +131,13 @@ private async Task LoadModuleDetailsAsync(ModuleViewModel module, CancellationTo
133131
[RelayCommand]
134132
private async Task RefreshModulesAsync()
135133
{
134+
foreach (var vm in Modules)
135+
{
136+
if (vm is IDisposable disposable)
137+
{
138+
disposable.Dispose();
139+
}
140+
}
136141
Modules.Clear();
137142

138143
var dbModules = await _moduleRepository.GetAllAsync();
@@ -172,6 +177,13 @@ private async Task ToggleModuleAsync(ModuleViewModel moduleVm)
172177
{
173178
// System modules can be disabled but not uninstalled
174179
if (moduleVm == null) return;
180+
if (moduleVm.IsSystem)
181+
{
182+
await (_notificationService?.ShowErrorAsync(
183+
"Not Supported",
184+
$"Built-in module '{moduleVm.Name}' cannot be enabled/disabled.") ?? Task.CompletedTask);
185+
return;
186+
}
175187

176188
try
177189
{
@@ -311,40 +323,6 @@ await _notificationService.ShowInfoAsync("Module Removed",
311323
}
312324
}
313325

314-
[RelayCommand]
315-
private async Task ImportModuleAsync()
316-
{
317-
if (string.IsNullOrWhiteSpace(ImportPath)) return;
318-
319-
// ImportPath could be a directory or extension.vsixmanifest
320-
var path = ImportPath;
321-
if (File.Exists(path) && Path.GetFileName(path) == SystemModuleInstaller.VsixManifestFileName)
322-
{
323-
// ok
324-
}
325-
else if (Directory.Exists(path))
326-
{
327-
path = Path.Combine(path, SystemModuleInstaller.VsixManifestFileName);
328-
}
329-
else
330-
{
331-
_notificationService?.ShowErrorAsync("Error", "Invalid path.");
332-
return;
333-
}
334-
335-
try
336-
{
337-
await _moduleInstaller.RegisterDevelopmentModuleAsync(path, hostType: _runtimeContext.HostType);
338-
ImportPath = string.Empty;
339-
await RefreshModulesAsync();
340-
_notificationService?.ShowInfoAsync("Success", "Module imported.");
341-
}
342-
catch (Exception ex)
343-
{
344-
_notificationService?.ShowErrorAsync("Error", ex.Message);
345-
}
346-
}
347-
348326
/// <summary>
349327
/// Installs a module from a .modpkg package file.
350328
/// Called from View after file picker selection.
@@ -426,7 +404,7 @@ public async Task InstallPackageAsync(string packagePath)
426404
}
427405
}
428406

429-
public partial class ModuleViewModel : ObservableObject
407+
public partial class ModuleViewModel : ObservableObject, IDisposable
430408
{
431409
public ModuleEntity Entity { get; }
432410
public RuntimeModule? RuntimeModule { get; }
@@ -435,6 +413,10 @@ public ModuleViewModel(ModuleEntity entity, RuntimeModule? runtimeModule)
435413
{
436414
Entity = entity;
437415
RuntimeModule = runtimeModule;
416+
if (RuntimeModule != null)
417+
{
418+
RuntimeModule.StateChanged += OnRuntimeModuleStateChanged;
419+
}
438420
}
439421

440422
public string Id => Entity.Id;
@@ -453,7 +435,7 @@ public ModuleViewModel(ModuleEntity entity, RuntimeModule? runtimeModule)
453435
/// <summary>
454436
/// Whether the module is actually loaded and running in the runtime.
455437
/// </summary>
456-
public bool IsLoaded => RuntimeModule?.State == RuntimeModuleState.Active;
438+
public bool IsLoaded => RuntimeModule?.State is RuntimeModuleState.Loaded or RuntimeModuleState.Active;
457439

458440
// Status Logic
459441
public string StatusText
@@ -462,8 +444,9 @@ public string StatusText
462444
{
463445
if (Entity.State == DataModuleState.MissingFiles) return "Missing Files";
464446
if (Entity.State == DataModuleState.Disabled || !Entity.IsEnabled) return "Disabled";
447+
if (RuntimeModule?.State == RuntimeModuleState.Error) return "Error";
465448
if (IsLoaded) return "Running";
466-
return "Ready"; // Enabled but not yet loaded
449+
return "Ready"; // Enabled but not yet loaded/initialized
467450
}
468451
}
469452

@@ -480,10 +463,32 @@ public string StatusText
480463
/// Whether the toggle button should be shown.
481464
/// All modules can be toggled (disabled/enabled), including system modules.
482465
/// </summary>
483-
public bool ShowToggle => true;
466+
public bool ShowToggle => !IsSystem;
467+
468+
public bool ShowEnableButton => ShowToggle && !IsEnabled;
469+
public bool ShowDisableButton => ShowToggle && IsEnabled;
484470

485471
/// <summary>
486472
/// Whether this module can be removed (only non-system modules).
487473
/// </summary>
488474
public bool CanRemove => !IsSystem;
475+
476+
private void OnRuntimeModuleStateChanged(object? sender, RuntimeModuleStateChangedEventArgs e)
477+
{
478+
// Runtime transitions can happen off the UI thread (e.g., during initialization).
479+
Dispatcher.UIThread.Post(() =>
480+
{
481+
OnPropertyChanged(nameof(IsLoaded));
482+
OnPropertyChanged(nameof(StatusText));
483+
OnPropertyChanged(nameof(StatusColor));
484+
});
485+
}
486+
487+
public void Dispose()
488+
{
489+
if (RuntimeModule != null)
490+
{
491+
RuntimeModule.StateChanged -= OnRuntimeModuleStateChanged;
492+
}
493+
}
489494
}

0 commit comments

Comments
 (0)