feat: add plugin UI host (sidebar, view container, settings panel) + storage API
- internal/core/storage/api.go — plugin namespace JSON storage (settings/data/cache) - internal/core/storage/api_test.go — 8 tests (write/read, path traversal, atomic) - internal/api/app.go — Wails bindings for storage (Read/WritePluginSettings, Read/WritePluginDataJSON) - main.go — initialize storage service, pass to NewApp - Sidebar.svelte — plugin sidebar items from contributions (filtered by ui.register) - ViewContainer.svelte — plugin view host with degraded status - PluginCard.svelte — Settings button + permission warnings - PluginManager.svelte — settings panel modal with test form - App.svelte — integrated sidebar + view container layout
This commit is contained in:
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/verstak/verstak-desktop/internal/core/events"
|
||||
"github.com/verstak/verstak-desktop/internal/core/permissions"
|
||||
"github.com/verstak/verstak-desktop/internal/core/plugin"
|
||||
"github.com/verstak/verstak-desktop/internal/core/storage"
|
||||
"github.com/verstak/verstak-desktop/internal/core/vault"
|
||||
)
|
||||
|
||||
@@ -24,6 +25,7 @@ type App struct {
|
||||
eventBus *events.Bus
|
||||
plugins []plugin.Plugin
|
||||
vault *vault.Vault
|
||||
storage *storage.Storage
|
||||
}
|
||||
|
||||
// NewApp creates a new App instance.
|
||||
@@ -34,6 +36,7 @@ func NewApp(
|
||||
bus *events.Bus,
|
||||
plugins []plugin.Plugin,
|
||||
vaultService *vault.Vault,
|
||||
storageService *storage.Storage,
|
||||
) *App {
|
||||
return &App{
|
||||
capRegistry: capReg,
|
||||
@@ -42,6 +45,7 @@ func NewApp(
|
||||
eventBus: bus,
|
||||
plugins: plugins,
|
||||
vault: vaultService,
|
||||
storage: storageService,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,6 +252,83 @@ func (a *App) CloseVault() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ─── Storage API ────────────────────────────────────────────
|
||||
|
||||
// ReadPluginSettings returns all settings for a plugin.
|
||||
func (a *App) ReadPluginSettings(pluginID string) map[string]interface{} {
|
||||
if a.storage == nil {
|
||||
return make(map[string]interface{})
|
||||
}
|
||||
data, err := a.storage.ReadPluginSettings(pluginID)
|
||||
if err != nil {
|
||||
log.Printf("[api] ReadPluginSettings(%s): %v", pluginID, err)
|
||||
return make(map[string]interface{})
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// WritePluginSettings writes all settings for a plugin.
|
||||
func (a *App) WritePluginSettings(pluginID string, data map[string]interface{}) string {
|
||||
if a.storage == nil {
|
||||
return "storage not initialized"
|
||||
}
|
||||
if err := a.storage.WritePluginSettings(pluginID, data); err != nil {
|
||||
log.Printf("[api] WritePluginSettings(%s): %v", pluginID, err)
|
||||
return err.Error()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ReadPluginSetting returns a single setting value.
|
||||
func (a *App) ReadPluginSetting(pluginID, key string) interface{} {
|
||||
if a.storage == nil {
|
||||
return nil
|
||||
}
|
||||
val, err := a.storage.ReadPluginSetting(pluginID, key)
|
||||
if err != nil {
|
||||
log.Printf("[api] ReadPluginSetting(%s, %s): %v", pluginID, key, err)
|
||||
return nil
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// WritePluginSetting writes a single setting value.
|
||||
func (a *App) WritePluginSetting(pluginID, key string, value interface{}) string {
|
||||
if a.storage == nil {
|
||||
return "storage not initialized"
|
||||
}
|
||||
if err := a.storage.WritePluginSetting(pluginID, key, value); err != nil {
|
||||
log.Printf("[api] WritePluginSetting(%s, %s): %v", pluginID, key, err)
|
||||
return err.Error()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ReadPluginDataJSON reads a named JSON data file for a plugin.
|
||||
func (a *App) ReadPluginDataJSON(pluginID, name string) map[string]interface{} {
|
||||
if a.storage == nil {
|
||||
return make(map[string]interface{})
|
||||
}
|
||||
data, err := a.storage.ReadPluginDataJSON(pluginID, name)
|
||||
if err != nil {
|
||||
log.Printf("[api] ReadPluginDataJSON(%s, %s): %v", pluginID, name, err)
|
||||
return make(map[string]interface{})
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// WritePluginDataJSON writes a named JSON data file for a plugin.
|
||||
func (a *App) WritePluginDataJSON(pluginID, name string, data map[string]interface{}) string {
|
||||
if a.storage == nil {
|
||||
return "storage not initialized"
|
||||
}
|
||||
if err := a.storage.WritePluginDataJSON(pluginID, name, data); err != nil {
|
||||
log.Printf("[api] WritePluginDataJSON(%s, %s): %v", pluginID, name, err)
|
||||
return err.Error()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ContributionSummary aggregates all contribution types for the frontend.
|
||||
type ContributionSummary struct {
|
||||
Views []contribution.ContributionView `json:"views"`
|
||||
|
||||
Reference in New Issue
Block a user