7.8 KiB
7.8 KiB
Milestone 5a — Frontend Plugin Host: Declarative UI Contributions + Plugin Settings
Цель
Создать первый UI-host слой, чтобы shell отображал UI contributions плагинов и корректно убирал их при disable/reload. Ошибка plugin UI не должна ронять shell.
Contribution Points
Реализованы в этом milestone
| Contribution Point | Backend Registry | Frontend Host | Статус |
|---|---|---|---|
sidebarItems |
✅ Registry.Register/Unregister/ListByPoint | ✅ Sidebar.svelte | Работает |
views |
✅ Registry | ✅ ViewContainer.svelte (placeholder) | Работает (declarative placeholder) |
settingsPanels |
✅ Registry | ✅ PluginManager.svelte | Работает |
commands |
✅ Registry | ContributionRegistry (UI command palette not implemented) | Registry готов, UI planned |
Планируемые (не реализованы)
fileActions,noteActions,contextMenuEntries,searchProviders,activityProviders,statusBarItems
Что сделано
1. Contribution Registry Lifecycle
internal/core/contribution/registry.go:
- Добавлен
ListByPoint(pointType)— запрос contributions по типу Register()теперь idempotent: удаляет старые записи plugin перед добавлением- Добавлены
ContributionPointTypeконстанты для всех 10 типов
internal/api/app.go — ReloadPlugins:
- Перед регистрацией contributions вызывается
Unregister(pluginID)→ предотвращает дубли при повторном reload - Disabled/failed plugins не регистрируют contributions
- Flattened ContributionSummary для фронтенда (FlatSidebarItem, FlatView, FlatSettingsPanel, FlatCommand)
2. Manifest Contributions Schema
plugin.json может объявлять (без изменений — схема существовала):
{
"contributes": {
"sidebarItems": [{ "id": "...", "title": "...", "icon": "🧪", "view": "...", "position": 100 }],
"views": [{ "id": "...", "title": "...", "component": "..." }],
"settingsPanels": [{ "id": "...", "title": "...", "component": "..." }],
"commands": [{ "id": "...", "title": "...", "icon": "⚡", "handler": "..." }]
}
}
3. UI Shell Rendering
Sidebar.svelte:
- Строит plugin sidebar items из ContributionRegistry (поле
sidebarItems) - Сортировка по
position(default 100) - Фильтрация: скрыты items от disabled/failed/incompatible плагинов
- Клик →
verstak:open-viewсобытие - Error boundary: перехват ошибок API, показ "⚠️ Plugin UI error"
ViewContainer.svelte:
- Declarative placeholder host для plugin views
- Показывает plugin name, view id, component id, статус "frontend bundle host not implemented yet"
- Error boundary:
{#key}+ catch rendering errors → "⚠️ Plugin UI failed" fallback - Empty state: "Select a plugin view from the sidebar"
4. Plugin Settings
PluginManager.svelte:
- Загружает
settingsPanelsиз ContributionRegistry - PluginCard принимает
settingsPanelsprop - "⚙️ Settings" кнопка показывается только если у plugin есть settingsPanel
- Клик →
verstak:open-settings→ открывает settings panel в modal - Disable plugin → кнопка Settings исчезает
- Error boundary:
{#key}+ error state вокруг settings panel
5. Error Boundary
- ViewContainer:
{#key activeView}+ try/catch в reactive declarations → "⚠️ Plugin UI failed" - PluginManager:
{#key}вокруг settings modal +settingsErrorstate - Ошибки логируются в
console.error
6. Platform-test Plugin
verstak-official-plugins/plugins/platform-test/plugin.json уже содержит все contribution points. Без изменений.
7. Enable/Disable Verification
| Сценарий | Результат |
|---|---|
| Plugin enabled → sidebar item visible | ✅ Sidebar items из ContributionRegistry |
| Plugin enabled → view opens | ✅ ViewContainer placeholder |
| Plugin enabled → settings button visible | ✅ Если есть settingsPanel |
| Plugin disabled → sidebar item disappears | ✅ Unregister → contributions удалены |
| Plugin disabled → view/settings unavailable | ✅ Не показываются |
| Plugin re-enabled → contributions return | ✅ При Reload — Register |
| ReloadPlugins no duplicates | ✅ Unregister перед Register + Register idempotent |
| Failed plugin → shell stable | ✅ Error boundary в ViewContainer + PluginManager |
8. Build Script
scripts/build.sh — добавлена функция global_update():
git pull --ff-onlyдля всех 6 репозиториев- Сборка official plugins (npm install + build для каждого plugin с frontend, go build для backend)
- Копирование собранных плагинов в
verstak-desktop/plugins/ - Ошибки не фатальны — собираются и показываются в конце
Изменённые файлы
verstak-desktop
| Файл | Изменение |
|---|---|
internal/core/contribution/registry.go |
Добавлен ListByPoint, ContributionPointType, Register idempotent |
internal/core/contribution/registry_test.go |
НОВЫЙ: 5 тестов (register, unregister, ListByPoint, duplicate prevention, no-side-effects) |
internal/api/app.go |
Flat типы для фронтенда, buildContributionSummary, ReloadPlugins — Unregister перед Register |
frontend/src/lib/shell/Sidebar.svelte |
Sidebar items из ContributionRegistry, фильтрация, сортировка, error boundary |
frontend/src/lib/shell/ViewContainer.svelte |
НОВЫЙ: declarative placeholder host + error boundary |
frontend/src/lib/plugin-manager/PluginCard.svelte |
Settings button по settingsPanels prop, disabled state |
frontend/src/lib/plugin-manager/PluginManager.svelte |
Загрузка settingsPanels, settings modal, error boundary |
frontend/src/App.svelte |
Обработка verstak:open-view, verstak:open-settings, verstak:close-settings |
cmd/smoke-platform/main.go |
Добавлен -test-contributions флаг с тестом lifecycle |
scripts/smoke-platform.sh |
Добавлен вызов -test-contributions |
scripts/build.sh |
Добавлена global_update() — pull всех репозиториев, сборка official plugins |
docs/PLUGIN_RUNTIME.md |
Обновлён раздел Contribution Points + Reload |
verstak-docs
| Файл | Изменение |
|---|---|
docs/MILESTONE_PLATFORM_RUNTIME_5a.md |
НОВЫЙ: этот документ |
Результаты проверок
go test ./internal/... -count=1
→ all packages PASS (включая 5 новых contribution tests)
go vet ./...
→ clean
cd frontend && npm run build
→ ✓ built in 1.43s
bash scripts/smoke-platform.sh
→ smoke-platform passed (plugin + workspace + contributions lifecycle)
→ enable/disable test passed
→ workspace test passed
→ contributions lifecycle test passed
git status --short
→ clean (все 6 репозиториев)
git log HEAD --not --remotes
→ empty (все запушено)
Non-goals (не реализовано)
- Frontend bundle loader (plugin JS bundle host). ViewContainer — declarative placeholder
- Official plugin extraction (notes/files/editor/activity)
- Backend sidecar runtime
- Secrets
- Remote plugin registry
- Sync
- Command palette UI
- Пользовательские функции в core