diff --git a/abi/proto/v1/manifest.proto b/abi/proto/v1/manifest.proto index 1fd166b..15a564d 100644 --- a/abi/proto/v1/manifest.proto +++ b/abi/proto/v1/manifest.proto @@ -222,6 +222,9 @@ message AdminPage { string title = 2; string icon = 3; string route = 4; + // Optional host sidebar category ID. Empty or unknown values use the + // host's fallback Plugins category. + string category = 5; } // AiAction mirrors plugin.AIAction. diff --git a/abi/v1/manifest.pb.go b/abi/v1/manifest.pb.go index a870f31..7583f2d 100644 --- a/abi/v1/manifest.pb.go +++ b/abi/v1/manifest.pb.go @@ -862,11 +862,14 @@ func (x *PageTemplateMeta) GetSlots() []string { // AdminPage mirrors plugin.AdminPage. type AdminPage struct { - state protoimpl.MessageState `protogen:"open.v1"` - Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` - Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"` - Icon string `protobuf:"bytes,3,opt,name=icon,proto3" json:"icon,omitempty"` - Route string `protobuf:"bytes,4,opt,name=route,proto3" json:"route,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"` + Icon string `protobuf:"bytes,3,opt,name=icon,proto3" json:"icon,omitempty"` + Route string `protobuf:"bytes,4,opt,name=route,proto3" json:"route,omitempty"` + // Optional host sidebar category ID. Empty or unknown values use the + // host's fallback Plugins category. + Category string `protobuf:"bytes,5,opt,name=category,proto3" json:"category,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -929,6 +932,13 @@ func (x *AdminPage) GetRoute() string { return "" } +func (x *AdminPage) GetCategory() string { + if x != nil { + return x.Category + } + return "" +} + // AiAction mirrors plugin.AIAction. type AiAction struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -1490,12 +1500,13 @@ const file_v1_manifest_proto_rawDesc = "" + "\x03key\x18\x02 \x01(\tR\x03key\x12\x14\n" + "\x05title\x18\x03 \x01(\tR\x05title\x12 \n" + "\vdescription\x18\x04 \x01(\tR\vdescription\x12\x14\n" + - "\x05slots\x18\x05 \x03(\tR\x05slots\"]\n" + + "\x05slots\x18\x05 \x03(\tR\x05slots\"y\n" + "\tAdminPage\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05title\x18\x02 \x01(\tR\x05title\x12\x12\n" + "\x04icon\x18\x03 \x01(\tR\x04icon\x12\x14\n" + - "\x05route\x18\x04 \x01(\tR\x05route\"\xa4\x01\n" + + "\x05route\x18\x04 \x01(\tR\x05route\x12\x1a\n" + + "\bcategory\x18\x05 \x01(\tR\bcategory\"\xa4\x01\n" + "\bAiAction\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05title\x18\x02 \x01(\tR\x05title\x12 \n" + diff --git a/plugin/types.go b/plugin/types.go index 69ae990..82992e9 100644 --- a/plugin/types.go +++ b/plugin/types.go @@ -30,10 +30,11 @@ type EmailSender interface { // AdminPage defines a page that a plugin registers in the admin sidebar. type AdminPage struct { - Key string - Title string - Icon string - Route string + Key string + Title string + Icon string + Route string + Category string // Optional host sidebar category ID; unknown values fall back to Plugins. } // AIAction declares an AI task registered in the AI Action Matrix. diff --git a/plugin/wasmguest/describe.go b/plugin/wasmguest/describe.go index 266fa13..f20cec3 100644 --- a/plugin/wasmguest/describe.go +++ b/plugin/wasmguest/describe.go @@ -108,7 +108,7 @@ func (g *guest) buildManifest() *abiv1.PluginManifest { if reg.AdminPages != nil { for _, p := range reg.AdminPages() { m.AdminPages = append(m.AdminPages, &abiv1.AdminPage{ - Key: p.Key, Title: p.Title, Icon: p.Icon, Route: p.Route, + Key: p.Key, Title: p.Title, Icon: p.Icon, Route: p.Route, Category: p.Category, }) } } diff --git a/plugin/wasmguest/dispatch_test.go b/plugin/wasmguest/dispatch_test.go index a252fae..1e26989 100644 --- a/plugin/wasmguest/dispatch_test.go +++ b/plugin/wasmguest/dispatch_test.go @@ -66,7 +66,7 @@ func fixtureRegistration() plugin.PluginRegistration { }) }, AdminPages: func() []plugin.AdminPage { - return []plugin.AdminPage{{Key: "fixture", Title: "Fixture Admin", Icon: "puzzle", Route: "/admin/fixture"}} + return []plugin.AdminPage{{Key: "fixture", Title: "Fixture Admin", Icon: "puzzle", Route: "/admin/fixture", Category: "content"}} }, AIActions: func() []plugin.AIAction { return []plugin.AIAction{{Key: "fixture.summarize", Title: "Summarize", DefaultProvider: "openai", DefaultModel: "gpt-4o"}} @@ -168,7 +168,7 @@ func TestDescribeManifest(t *testing.T) { m.GetPageTemplates()[0].GetKey() != "fixture-page" { t.Errorf("page_templates = %v", m.GetPageTemplates()) } - if len(m.GetAdminPages()) != 1 || m.GetAdminPages()[0].GetRoute() != "/admin/fixture" { + if len(m.GetAdminPages()) != 1 || m.GetAdminPages()[0].GetRoute() != "/admin/fixture" || m.GetAdminPages()[0].GetCategory() != "content" { t.Errorf("admin_pages = %v", m.GetAdminPages()) } if len(m.GetAiActions()) != 1 || m.GetAiActions()[0].GetKey() != "fixture.summarize" { diff --git a/plugin/wasmguest/testdata/fixture/registration.go b/plugin/wasmguest/testdata/fixture/registration.go index 6339c31..faf64f7 100644 --- a/plugin/wasmguest/testdata/fixture/registration.go +++ b/plugin/wasmguest/testdata/fixture/registration.go @@ -84,7 +84,7 @@ var Registration = plugin.PluginRegistration{ }, AdminPages: func() []plugin.AdminPage { return []plugin.AdminPage{{ - Key: "wasmfixture", Title: "Wasm Fixture", Icon: "flask", Route: "/admin/wasmfixture", + Key: "wasmfixture", Title: "Wasm Fixture", Icon: "flask", Route: "/admin/wasmfixture", Category: "content", }} }, } diff --git a/plugin/wasmguest/wasmhost_test.go b/plugin/wasmguest/wasmhost_test.go index a21542b..d9fc11f 100644 --- a/plugin/wasmguest/wasmhost_test.go +++ b/plugin/wasmguest/wasmhost_test.go @@ -151,7 +151,7 @@ func TestWasmFixtureDescribeRoundTrip(t *testing.T) { if len(m.GetTemplateKeys()) != 1 || m.GetTemplateKeys()[0] != "wasmfixture-page" { t.Errorf("template_keys = %v", m.GetTemplateKeys()) } - if len(m.GetAdminPages()) != 1 || m.GetAdminPages()[0].GetRoute() != "/admin/wasmfixture" { + if len(m.GetAdminPages()) != 1 || m.GetAdminPages()[0].GetRoute() != "/admin/wasmfixture" || m.GetAdminPages()[0].GetCategory() != "content" { t.Errorf("admin_pages = %v", m.GetAdminPages()) }