fix(plugin-manager): sync UI state with plugin lifecycle + sidebar click fix

Root cause fixes:
- Sidebar: handleSidebarItem used item.id instead of item.view for viewId.
  Platform Test sidebar item has id=verstak.platform-test.sidebar but
  view=verstak.platform-test.diagnostics. Click now dispatches correct viewId.
- PluginManager: EnablePlugin/DisablePlugin only wrote to plugins.json but
  never re-discovered plugins. UI showed stale state (no Enable button after
  Disable, no Disable after Enable). Now calls ReloadPlugins() + loadAll()
  after each toggle.
- PluginManager: loadAll() fired async loads (GetCapabilities etc) without
  awaiting — loading spinner disappeared before data was ready. Now awaits
  all via Promise.all.
- PluginCard: no loading feedback on Enable/Disable buttons. Added
  actionFeedback prop — buttons show '⟳ Enabling...' / '⟳ Disabling...'
  and are disabled during operation.
- PluginManager: no visible result after Reload/Enable/Disable. Added
  toast notifications (success/error/info) with auto-dismiss.
- Settings: openSettingsFromProps didn't handle missing panel — now shows
  visible error in modal.
This commit is contained in:
2026-06-17 19:40:05 +08:00
parent 6d2f7858eb
commit a100f5a441
3 changed files with 115 additions and 160 deletions
@@ -60,8 +60,12 @@
!capabilities.some(c => c.name === opt)
);
export let actionFeedback = {}; // { [pluginId]: 'enabling' | 'disabling' | null }
$: isDisabled = p.status === 'disabled' || !p.enabled;
$: canToggle = p.status !== 'failed' && p.status !== 'incompatible' && p.status !== 'missing-required-capability' && p.status !== 'discovered';
$: isBusy = actionFeedback[pluginId] != null;
$: busyAction = actionFeedback[pluginId] || null;
</script>
<div class="plugin-card" class:disabled={isDisabled} class:failed={p.status === 'failed'}>
@@ -181,12 +185,12 @@
{/if}
{#if vaultOpen && canToggle}
{#if isDisabled}
<button class="btn-enable" on:click={() => onEnable(m.id)} type="button">
▶ Enable
<button class="btn-enable" on:click={() => onEnable(m.id)} type="button" disabled={isBusy}>
{#if busyAction === 'enabling'}⟳ Enabling...{:else}▶ Enable{/if}
</button>
{:else}
<button class="btn-disable" on:click={() => onDisable(m.id)} type="button">
⏸ Disable
<button class="btn-disable" on:click={() => onDisable(m.id)} type="button" disabled={isBusy}>
{#if busyAction === 'disabling'}⟳ Disabling...{:else}⏸ Disable{/if}
</button>
{/if}
{/if}