diff --git a/frontend/src/lib/plugin-manager/PluginCard.svelte b/frontend/src/lib/plugin-manager/PluginCard.svelte index 30f94db..0c03a72 100644 --- a/frontend/src/lib/plugin-manager/PluginCard.svelte +++ b/frontend/src/lib/plugin-manager/PluginCard.svelte @@ -30,6 +30,7 @@ $: contribCounts = { views: (contributions.views || []).filter(v => v.pluginId === pluginId).length, commands: (contributions.commands || []).filter(c => c.pluginId === pluginId).length, + searchProviders: (contributions.searchProviders || []).filter(s => s.pluginId === pluginId).length, sidebar: (contributions.sidebarItems || []).filter(s => s.pluginId === pluginId).length, statusbar: (contributions.statusBarItems || []).filter(s => s.pluginId === pluginId).length, openProviders: (contributions.openProviders || []).filter(o => o.pluginId === pluginId).length, @@ -40,6 +41,7 @@ const parts = []; if (contribCounts.views > 0) parts.push(contribCounts.views + ' view' + (contribCounts.views !== 1 ? 's' : '')); if (contribCounts.commands > 0) parts.push(contribCounts.commands + ' command' + (contribCounts.commands !== 1 ? 's' : '')); + if (contribCounts.searchProviders > 0) parts.push(contribCounts.searchProviders + ' searchProvider' + (contribCounts.searchProviders !== 1 ? 's' : '')); if (contribCounts.sidebar > 0) parts.push(contribCounts.sidebar + ' sidebar' + (contribCounts.sidebar !== 1 ? 's' : '')); if (contribCounts.statusbar > 0) parts.push(contribCounts.statusbar + ' statusbar' + (contribCounts.statusbar !== 1 ? 's' : '')); if (contribCounts.openProviders > 0) parts.push(contribCounts.openProviders + ' openProvider' + (contribCounts.openProviders !== 1 ? 's' : '')); diff --git a/frontend/src/lib/test/wails-mock.js b/frontend/src/lib/test/wails-mock.js index 59e89ab..a08bfdb 100644 --- a/frontend/src/lib/test/wails-mock.js +++ b/frontend/src/lib/test/wails-mock.js @@ -374,19 +374,20 @@ } function allContributions() { - var views = [], commands = [], sidebarItems = [], statusBarItems = [], settingsPanels = [], openProviders = [], workspaceItems = []; + var views = [], commands = [], searchProviders = [], sidebarItems = [], statusBarItems = [], settingsPanels = [], openProviders = [], workspaceItems = []; for (var id in pluginStates) { var s = pluginStates[id]; var c = (s.manifest && s.manifest.contributes) || {}; if (c.views) c.views.forEach(function (v) { views.push(Object.assign({}, v, { pluginId: id })); }); if (c.commands) c.commands.forEach(function (cmd) { commands.push(Object.assign({}, cmd, { pluginId: id })); }); + if (c.searchProviders) c.searchProviders.forEach(function (sp) { searchProviders.push(Object.assign({}, sp, { pluginId: id })); }); if (c.sidebarItems) c.sidebarItems.forEach(function (sb) { sidebarItems.push(Object.assign({}, sb, { pluginId: id })); }); if (c.statusBarItems) c.statusBarItems.forEach(function (st) { statusBarItems.push(Object.assign({}, st, { pluginId: id })); }); if (c.settingsPanels) c.settingsPanels.forEach(function (sp) { settingsPanels.push(Object.assign({}, sp, { pluginId: id })); }); if (c.openProviders) c.openProviders.forEach(function (op) { openProviders.push(Object.assign({}, op, { pluginId: id })); }); if (c.workspaceItems) c.workspaceItems.forEach(function (wi) { workspaceItems.push(Object.assign({}, wi, { pluginId: id })); }); } - return { views: views, commands: commands, sidebarItems: sidebarItems, statusBarItems: statusBarItems, settingsPanels: settingsPanels, openProviders: openProviders, workspaceItems: workspaceItems }; + return { views: views, commands: commands, searchProviders: searchProviders, sidebarItems: sidebarItems, statusBarItems: statusBarItems, settingsPanels: settingsPanels, openProviders: openProviders, workspaceItems: workspaceItems }; } function requestExtension(request) { diff --git a/frontend/wailsjs/go/models.ts b/frontend/wailsjs/go/models.ts index e8047e2..83c97fe 100755 --- a/frontend/wailsjs/go/models.ts +++ b/frontend/wailsjs/go/models.ts @@ -80,6 +80,44 @@ export namespace api { return a; } } + export class FlatSearchProvider { + pluginId: string; + id: string; + label: string; + handler: string; + + static createFrom(source: any = {}) { + return new FlatSearchProvider(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.pluginId = source["pluginId"]; + this.id = source["id"]; + this.label = source["label"]; + this.handler = source["handler"]; + } + } + export class FlatStatusBarItem { + pluginId: string; + id: string; + label: string; + position?: string; + handler?: string; + + static createFrom(source: any = {}) { + return new FlatStatusBarItem(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.pluginId = source["pluginId"]; + this.id = source["id"]; + this.label = source["label"]; + this.position = source["position"]; + this.handler = source["handler"]; + } + } export class FlatSidebarItem { pluginId: string; id: string; @@ -165,8 +203,10 @@ export namespace api { export class ContributionSummary { views: FlatView[]; commands: FlatCommand[]; + searchProviders: FlatSearchProvider[]; settingsPanels: FlatSettingsPanel[]; sidebarItems: FlatSidebarItem[]; + statusBarItems: FlatStatusBarItem[]; openProviders: FlatOpenProvider[]; workspaceItems: FlatWorkspaceItem[]; @@ -178,8 +218,10 @@ export namespace api { if ('string' === typeof source) source = JSON.parse(source); this.views = this.convertValues(source["views"], FlatView); this.commands = this.convertValues(source["commands"], FlatCommand); + this.searchProviders = this.convertValues(source["searchProviders"], FlatSearchProvider); this.settingsPanels = this.convertValues(source["settingsPanels"], FlatSettingsPanel); this.sidebarItems = this.convertValues(source["sidebarItems"], FlatSidebarItem); + this.statusBarItems = this.convertValues(source["statusBarItems"], FlatStatusBarItem); this.openProviders = this.convertValues(source["openProviders"], FlatOpenProvider); this.workspaceItems = this.convertValues(source["workspaceItems"], FlatWorkspaceItem); } @@ -209,6 +251,7 @@ export namespace api { + export class SyncStatusDTO { configured: boolean; serverUrl: string; diff --git a/internal/api/app.go b/internal/api/app.go index dd67d8f..46af268 100644 --- a/internal/api/app.go +++ b/internal/api/app.go @@ -241,6 +241,13 @@ type FlatCommand struct { Handler string `json:"handler,omitempty"` } +type FlatSearchProvider struct { + PluginID string `json:"pluginId"` + ID string `json:"id"` + Label string `json:"label"` + Handler string `json:"handler"` +} + type FlatStatusBarItem struct { PluginID string `json:"pluginId"` ID string `json:"id"` @@ -276,13 +283,14 @@ type FlatWorkspaceItem struct { // ContributionSummary aggregates all contribution types for the frontend. type ContributionSummary struct { - Views []FlatView `json:"views"` - Commands []FlatCommand `json:"commands"` - SettingsPanels []FlatSettingsPanel `json:"settingsPanels"` - SidebarItems []FlatSidebarItem `json:"sidebarItems"` - StatusBarItems []FlatStatusBarItem `json:"statusBarItems"` - OpenProviders []FlatOpenProvider `json:"openProviders"` - WorkspaceItems []FlatWorkspaceItem `json:"workspaceItems"` + Views []FlatView `json:"views"` + Commands []FlatCommand `json:"commands"` + SearchProviders []FlatSearchProvider `json:"searchProviders"` + SettingsPanels []FlatSettingsPanel `json:"settingsPanels"` + SidebarItems []FlatSidebarItem `json:"sidebarItems"` + StatusBarItems []FlatStatusBarItem `json:"statusBarItems"` + OpenProviders []FlatOpenProvider `json:"openProviders"` + WorkspaceItems []FlatWorkspaceItem `json:"workspaceItems"` } // buildContributionSummary creates a ContributionSummary from the registry. @@ -292,6 +300,7 @@ func buildContributionSummary(r *contribution.Registry) ContributionSummary { } regViews := r.Views() regCmds := r.Commands() + regSearchProviders := r.SearchProviders() regPanels := r.SettingsPanels() regSidebar := r.SidebarItems() regStatusBar := r.StatusBarItems() @@ -306,6 +315,10 @@ func buildContributionSummary(r *contribution.Registry) ContributionSummary { for i, v := range regCmds { cmds[i] = FlatCommand{PluginID: v.PluginID, ID: v.Item.ID, Title: v.Item.Title, Icon: v.Item.Icon, Handler: v.Item.Handler} } + searchProviders := make([]FlatSearchProvider, len(regSearchProviders)) + for i, v := range regSearchProviders { + searchProviders[i] = FlatSearchProvider{PluginID: v.PluginID, ID: v.Item.ID, Label: v.Item.Label, Handler: v.Item.Handler} + } panels := make([]FlatSettingsPanel, len(regPanels)) for i, v := range regPanels { panels[i] = FlatSettingsPanel{PluginID: v.PluginID, ID: v.Item.ID, Title: v.Item.Title, Icon: v.Item.Icon, Component: v.Item.Component} @@ -337,7 +350,7 @@ func buildContributionSummary(r *contribution.Registry) ContributionSummary { for i, v := range regWorkspaceItems { workspaceItems[i] = FlatWorkspaceItem{PluginID: v.PluginID, ID: v.Item.ID, Title: v.Item.Title, Icon: v.Item.Icon, Component: v.Item.Component} } - return ContributionSummary{Views: views, Commands: cmds, SettingsPanels: panels, SidebarItems: sidebar, StatusBarItems: statusBarItems, OpenProviders: openProviders, WorkspaceItems: workspaceItems} + return ContributionSummary{Views: views, Commands: cmds, SearchProviders: searchProviders, SettingsPanels: panels, SidebarItems: sidebar, StatusBarItems: statusBarItems, OpenProviders: openProviders, WorkspaceItems: workspaceItems} } // GetContributions returns all registered contributions flattened for the frontend. @@ -350,8 +363,8 @@ func (a *App) GetContributions() ContributionSummary { } summary := buildContributionSummary(a.contribRegistry) if a.debug { - debug.Logf("[api] GetContributions: returning views=%d commands=%d sidebar=%d statusBar=%d settings=%d openProviders=%d", - len(summary.Views), len(summary.Commands), len(summary.SidebarItems), len(summary.StatusBarItems), len(summary.SettingsPanels), len(summary.OpenProviders)) + debug.Logf("[api] GetContributions: returning views=%d commands=%d searchProviders=%d sidebar=%d statusBar=%d settings=%d openProviders=%d", + len(summary.Views), len(summary.Commands), len(summary.SearchProviders), len(summary.SidebarItems), len(summary.StatusBarItems), len(summary.SettingsPanels), len(summary.OpenProviders)) } return summary } diff --git a/internal/api/app_test.go b/internal/api/app_test.go index 95d0364..5dab1ea 100644 --- a/internal/api/app_test.go +++ b/internal/api/app_test.go @@ -1026,6 +1026,9 @@ func newBridgeTestApp(t *testing.T) *App { StatusBarItems: []plugin.ContributionStatusBarItem{ {ID: "bridge.status", Label: "Bridge Ready", Position: "right", Handler: "openBridgeStatus"}, }, + SearchProviders: []plugin.ContributionSearchProvider{ + {ID: "bridge.search", Label: "Bridge Search", Handler: "searchVault"}, + }, OpenProviders: []plugin.ContributionOpenProvider{ { ID: "bridge.markdown", @@ -1113,6 +1116,19 @@ func TestContributionSummaryIncludesStatusBarItems(t *testing.T) { } } +func TestContributionSummaryIncludesSearchProviders(t *testing.T) { + app := newBridgeTestApp(t) + + summary := app.GetContributions() + if len(summary.SearchProviders) != 1 { + t.Fatalf("SearchProviders count = %d, want 1", len(summary.SearchProviders)) + } + provider := summary.SearchProviders[0] + if provider.PluginID != "bridge.plugin" || provider.ID != "bridge.search" || provider.Label != "Bridge Search" || provider.Handler != "searchVault" { + t.Fatalf("search provider = %+v", provider) + } +} + func TestWorkbenchOpenAndEditResourceRouteToProvider(t *testing.T) { app := newBridgeTestApp(t) app.contribRegistry.Register("disabled.plugin", &plugin.Contributions{