From fb68c54409059d12f178a66c4a919f16e392d985 Mon Sep 17 00:00:00 2001 From: mirivlad Date: Sat, 27 Jun 2026 13:49:09 +0800 Subject: [PATCH] Route open providers by request mode --- docs/DEV_PLUGINS.md | 4 +-- docs/PLUGIN_RUNTIME.md | 5 +-- frontend/src/lib/test/wails-mock.js | 2 ++ frontend/wailsjs/go/models.ts | 5 ++- internal/api/app.go | 3 +- internal/core/plugin/plugin.go | 1 + internal/core/workbench/router.go | 16 ++++++++++ internal/core/workbench/routing_test.go | 41 +++++++++++++++++++++++++ 8 files changed, 71 insertions(+), 6 deletions(-) diff --git a/docs/DEV_PLUGINS.md b/docs/DEV_PLUGINS.md index 909558f..250a369 100644 --- a/docs/DEV_PLUGINS.md +++ b/docs/DEV_PLUGINS.md @@ -97,8 +97,8 @@ Frontend bundles are mounted with a plugin-scoped API created by a concrete editor plugin. Editor/viewer plugins contribute providers with `contributes.openProviders`. -Workbench selects by resource kind, extension/mime, context (`generic-text`, -`generic-markdown`, `notes-markdown`), user preference, priority, then +Workbench selects by resource kind, request mode, extension/mime, context +(`generic-text`, `generic-markdown`, `notes-markdown`), user preference, priority, then deterministic `pluginId/providerId` tie-break. If nothing matches, Workbench shows `no-provider` fallback instead of a core editor. diff --git a/docs/PLUGIN_RUNTIME.md b/docs/PLUGIN_RUNTIME.md index 38c54d7..0d136cc 100644 --- a/docs/PLUGIN_RUNTIME.md +++ b/docs/PLUGIN_RUNTIME.md @@ -317,7 +317,8 @@ the Workbench helper. { "kind": "vault-file", "extensions": [".md", ".markdown"], - "contexts": ["generic-markdown", "notes-markdown"] + "contexts": ["generic-markdown", "notes-markdown"], + "modes": ["view"] }, { "kind": "vault-file", @@ -333,7 +334,7 @@ the Workbench helper. ``` Selection uses enabled loaded/degraded provider plugins, resource kind, -extension/mime, context, user preference, priority, then deterministic +request mode, extension/mime, context, user preference, priority, then deterministic `pluginId/providerId` fallback. If nothing matches, Workbench returns `status: "no-provider"` and shows the fallback view instead of a core editor. diff --git a/frontend/src/lib/test/wails-mock.js b/frontend/src/lib/test/wails-mock.js index c3a599f..2c7f105 100644 --- a/frontend/src/lib/test/wails-mock.js +++ b/frontend/src/lib/test/wails-mock.js @@ -339,8 +339,10 @@ function providerSupports(provider, request) { var ext = requestExtension(request); var contextName = requestContextName(request); + var mode = String((request && request.mode) || 'view').toLowerCase(); return (provider.supports || []).some(function (support) { if (support.kind && support.kind !== request.kind) return false; + if (support.modes && support.modes.length && support.modes.map(function (m) { return String(m).toLowerCase(); }).indexOf(mode) === -1) return false; if (support.extensions && support.extensions.length && support.extensions.map(function (e) { return String(e).toLowerCase(); }).indexOf(ext) === -1) return false; if (support.contexts && support.contexts.length && support.contexts.indexOf(contextName) === -1) return false; return true; diff --git a/frontend/wailsjs/go/models.ts b/frontend/wailsjs/go/models.ts index b8a18ac..e8047e2 100755 --- a/frontend/wailsjs/go/models.ts +++ b/frontend/wailsjs/go/models.ts @@ -25,6 +25,7 @@ export namespace api { mime?: string[]; extensions?: string[]; contexts?: string[]; + modes?: string[]; static createFrom(source: any = {}) { return new FlatOpenProviderSupport(source); @@ -36,6 +37,7 @@ export namespace api { this.mime = source["mime"]; this.extensions = source["extensions"]; this.contexts = source["contexts"]; + this.modes = source["modes"]; } } export class FlatOpenProvider { @@ -534,6 +536,7 @@ export namespace plugin { mime?: string[]; extensions?: string[]; contexts?: string[]; + modes?: string[]; static createFrom(source: any = {}) { return new OpenProviderSupport(source); @@ -545,6 +548,7 @@ export namespace plugin { this.mime = source["mime"]; this.extensions = source["extensions"]; this.contexts = source["contexts"]; + this.modes = source["modes"]; } } export class ContributionOpenProvider { @@ -1159,4 +1163,3 @@ export namespace workspace { } } - diff --git a/internal/api/app.go b/internal/api/app.go index 3228b02..1417f7f 100644 --- a/internal/api/app.go +++ b/internal/api/app.go @@ -249,6 +249,7 @@ type FlatOpenProviderSupport struct { Mime []string `json:"mime,omitempty"` Extensions []string `json:"extensions,omitempty"` Contexts []string `json:"contexts,omitempty"` + Modes []string `json:"modes,omitempty"` } type FlatOpenProvider struct { @@ -316,7 +317,7 @@ func buildContributionSummary(r *contribution.Registry) ContributionSummary { for i, v := range regOpenProviders { supports := make([]FlatOpenProviderSupport, len(v.Item.Supports)) for j, s := range v.Item.Supports { - supports[j] = FlatOpenProviderSupport{Kind: s.Kind, Mime: s.Mime, Extensions: s.Extensions, Contexts: s.Contexts} + supports[j] = FlatOpenProviderSupport{Kind: s.Kind, Mime: s.Mime, Extensions: s.Extensions, Contexts: s.Contexts, Modes: s.Modes} } openProviders[i] = FlatOpenProvider{ PluginID: v.PluginID, diff --git a/internal/core/plugin/plugin.go b/internal/core/plugin/plugin.go index c082dae..60be7f8 100644 --- a/internal/core/plugin/plugin.go +++ b/internal/core/plugin/plugin.go @@ -152,6 +152,7 @@ type OpenProviderSupport struct { Mime []string `json:"mime,omitempty"` Extensions []string `json:"extensions,omitempty"` Contexts []string `json:"contexts,omitempty"` + Modes []string `json:"modes,omitempty"` } // ContributionOpenProvider represents an editor/viewer provider contribution. diff --git a/internal/core/workbench/router.go b/internal/core/workbench/router.go index fcae3e4..327cce2 100644 --- a/internal/core/workbench/router.go +++ b/internal/core/workbench/router.go @@ -168,6 +168,9 @@ func providerMatches(request OpenResourceRequest, provider plugin.ContributionOp if support.Kind != request.Kind { continue } + if !supportMatchesMode(request, support) { + continue + } if !supportMatchesExtensionOrMime(request, support) { continue } @@ -179,6 +182,19 @@ func providerMatches(request OpenResourceRequest, provider plugin.ContributionOp return false } +func supportMatchesMode(request OpenResourceRequest, support plugin.OpenProviderSupport) bool { + if len(support.Modes) == 0 { + return true + } + mode := strings.ToLower(request.Mode) + for _, supported := range support.Modes { + if strings.ToLower(supported) == mode { + return true + } + } + return false +} + func supportMatchesExtensionOrMime(request OpenResourceRequest, support plugin.OpenProviderSupport) bool { hasExtensionRules := len(support.Extensions) > 0 hasMimeRules := len(support.Mime) > 0 diff --git a/internal/core/workbench/routing_test.go b/internal/core/workbench/routing_test.go index 84e4e5e..d74013b 100644 --- a/internal/core/workbench/routing_test.go +++ b/internal/core/workbench/routing_test.go @@ -91,6 +91,47 @@ func TestSelectProviderFallsBackByPriorityThenID(t *testing.T) { } } +func TestSelectProviderHonorsSupportModes(t *testing.T) { + r := NewRouter(Preferences{}) + providers := []contribution.ContributionOpenProvider{ + provider("preview.plugin", "markdown.preview", 100, "MarkdownPreview", plugin.OpenProviderSupport{ + Kind: "vault-file", + Extensions: []string{".md"}, + Contexts: []string{ContextGenericMarkdown}, + Modes: []string{"view"}, + }), + provider("editor.plugin", "markdown.editor", 50, "MarkdownEditor", plugin.OpenProviderSupport{ + Kind: "vault-file", + Extensions: []string{".md"}, + Contexts: []string{ContextGenericMarkdown}, + }), + } + + viewProvider, err := r.SelectProvider(OpenResourceRequest{ + Kind: "vault-file", + Path: "Docs/readme.md", + Mode: "view", + }, providers) + if err != nil { + t.Fatalf("SelectProvider(view): %v", err) + } + if viewProvider.Item.ID != "markdown.preview" { + t.Fatalf("view provider = %q, want markdown.preview", viewProvider.Item.ID) + } + + editProvider, err := r.SelectProvider(OpenResourceRequest{ + Kind: "vault-file", + Path: "Docs/readme.md", + Mode: "edit", + }, providers) + if err != nil { + t.Fatalf("SelectProvider(edit): %v", err) + } + if editProvider.Item.ID != "markdown.editor" { + t.Fatalf("edit provider = %q, want markdown.editor", editProvider.Item.ID) + } +} + func TestSelectProviderTieBreaksByPluginIDThenProviderID(t *testing.T) { r := NewRouter(Preferences{}) providers := []contribution.ContributionOpenProvider{