From 73c5da8eb58da18838b7a655c28f016393357c82 Mon Sep 17 00:00:00 2001 From: mirivlad Date: Wed, 17 Jun 2026 17:07:59 +0800 Subject: [PATCH] docs: add milestone 5a report (frontend plugin host, contribution lifecycle) --- docs/MILESTONE_PLATFORM_RUNTIME_5a.md | 165 ++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 docs/MILESTONE_PLATFORM_RUNTIME_5a.md diff --git a/docs/MILESTONE_PLATFORM_RUNTIME_5a.md b/docs/MILESTONE_PLATFORM_RUNTIME_5a.md new file mode 100644 index 0000000..e43f2c1 --- /dev/null +++ b/docs/MILESTONE_PLATFORM_RUNTIME_5a.md @@ -0,0 +1,165 @@ +# 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` может объявлять (без изменений — схема существовала): + +```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 принимает `settingsPanels` prop +- "⚙️ 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 + `settingsError` state +- Ошибки логируются в `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