feat: milestone 5a — frontend plugin host, contribution lifecycle, UI shell

- Contribution Registry: ListByPoint, idempotent Register (Unregister-before-add)
- Flat ContributionSummary types for frontend (no nested .item.)
- Sidebar.svelte: items from ContributionRegistry, sort by position, error boundary
- ViewContainer.svelte: declarative placeholder host with error boundary
- PluginManager.svelte: settings panels from registry, knoppka only with settingsPanel
- PluginCard.svelte: settingsPanels prop, disabled state for Settings button
- Error boundary: ViewContainer + PluginManager catch errors, shell stays stable
- ReloadPlugins: Unregister before Register contributions (no duplicates)
- Smoke: -test-contributions flag, enable/disable/reload lifecycle verification
- Build: global_update() — pull all repos, build official plugins, install to desktop
This commit is contained in:
2026-06-17 17:07:52 +08:00
parent 9bb35a9fd0
commit 86eeadd2a9
12 changed files with 1108 additions and 122 deletions
@@ -4,13 +4,13 @@
export let permissions = [];
export let contributions = {};
export let vaultOpen = false;
export let onSettings = () => {};
export let settingsPanels = [];
export let onEnable = () => {};
export let onDisable = () => {};
$: m = p.manifest || {};
$: pluginId = m.id || 'unknown';
$: hasSettingsPanel = (contributions.settingsPanels || []).some(sp => sp.pluginId === pluginId);
$: hasSettingsPanel = settingsPanels.length > 0;
$: hasUIPermission = (m.permissions || []).includes('ui.register');
$: hasStoragePermission = (m.permissions || []).includes('storage.namespace');
$: hasCommandsPermission = (m.permissions || []).includes('commands.register');
@@ -174,7 +174,7 @@
<!-- Actions -->
<div class="card-actions">
{#if hasSettingsPanel}
<button class="btn-settings" on:click={() => onSettings(m.id)} type="button">
<button class="btn-settings" on:click={() => window.dispatchEvent(new CustomEvent('verstak:open-settings', { detail: { pluginId: m.id, panelId: settingsPanels[0]?.id } }))} type="button" disabled={isDisabled || p.status === 'failed'}>
⚙ Settings
</button>
{/if}