feat(manifest): allow admin pages to target sidebar categories

This commit is contained in:
Alex Dunmow 2026-08-07 15:24:27 +08:00
parent 6fd4c2f65d
commit bf72cd160a
7 changed files with 31 additions and 16 deletions

View File

@ -222,6 +222,9 @@ message AdminPage {
string title = 2; string title = 2;
string icon = 3; string icon = 3;
string route = 4; 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. // AiAction mirrors plugin.AIAction.

View File

@ -862,11 +862,14 @@ func (x *PageTemplateMeta) GetSlots() []string {
// AdminPage mirrors plugin.AdminPage. // AdminPage mirrors plugin.AdminPage.
type AdminPage struct { type AdminPage struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,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"` Icon string `protobuf:"bytes,3,opt,name=icon,proto3" json:"icon,omitempty"`
Route string `protobuf:"bytes,4,opt,name=route,proto3" json:"route,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 unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@ -929,6 +932,13 @@ func (x *AdminPage) GetRoute() string {
return "" return ""
} }
func (x *AdminPage) GetCategory() string {
if x != nil {
return x.Category
}
return ""
}
// AiAction mirrors plugin.AIAction. // AiAction mirrors plugin.AIAction.
type AiAction struct { type AiAction struct {
state protoimpl.MessageState `protogen:"open.v1"` 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" + "\x03key\x18\x02 \x01(\tR\x03key\x12\x14\n" +
"\x05title\x18\x03 \x01(\tR\x05title\x12 \n" + "\x05title\x18\x03 \x01(\tR\x05title\x12 \n" +
"\vdescription\x18\x04 \x01(\tR\vdescription\x12\x14\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" + "\tAdminPage\x12\x10\n" +
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
"\x05title\x18\x02 \x01(\tR\x05title\x12\x12\n" + "\x05title\x18\x02 \x01(\tR\x05title\x12\x12\n" +
"\x04icon\x18\x03 \x01(\tR\x04icon\x12\x14\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" + "\bAiAction\x12\x10\n" +
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
"\x05title\x18\x02 \x01(\tR\x05title\x12 \n" + "\x05title\x18\x02 \x01(\tR\x05title\x12 \n" +

View File

@ -30,10 +30,11 @@ type EmailSender interface {
// AdminPage defines a page that a plugin registers in the admin sidebar. // AdminPage defines a page that a plugin registers in the admin sidebar.
type AdminPage struct { type AdminPage struct {
Key string Key string
Title string Title string
Icon string Icon string
Route 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. // AIAction declares an AI task registered in the AI Action Matrix.

View File

@ -108,7 +108,7 @@ func (g *guest) buildManifest() *abiv1.PluginManifest {
if reg.AdminPages != nil { if reg.AdminPages != nil {
for _, p := range reg.AdminPages() { for _, p := range reg.AdminPages() {
m.AdminPages = append(m.AdminPages, &abiv1.AdminPage{ 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,
}) })
} }
} }

View File

@ -66,7 +66,7 @@ func fixtureRegistration() plugin.PluginRegistration {
}) })
}, },
AdminPages: func() []plugin.AdminPage { 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 { AIActions: func() []plugin.AIAction {
return []plugin.AIAction{{Key: "fixture.summarize", Title: "Summarize", DefaultProvider: "openai", DefaultModel: "gpt-4o"}} 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" { m.GetPageTemplates()[0].GetKey() != "fixture-page" {
t.Errorf("page_templates = %v", m.GetPageTemplates()) 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()) t.Errorf("admin_pages = %v", m.GetAdminPages())
} }
if len(m.GetAiActions()) != 1 || m.GetAiActions()[0].GetKey() != "fixture.summarize" { if len(m.GetAiActions()) != 1 || m.GetAiActions()[0].GetKey() != "fixture.summarize" {

View File

@ -84,7 +84,7 @@ var Registration = plugin.PluginRegistration{
}, },
AdminPages: func() []plugin.AdminPage { AdminPages: func() []plugin.AdminPage {
return []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",
}} }}
}, },
} }

View File

@ -151,7 +151,7 @@ func TestWasmFixtureDescribeRoundTrip(t *testing.T) {
if len(m.GetTemplateKeys()) != 1 || m.GetTemplateKeys()[0] != "wasmfixture-page" { if len(m.GetTemplateKeys()) != 1 || m.GetTemplateKeys()[0] != "wasmfixture-page" {
t.Errorf("template_keys = %v", m.GetTemplateKeys()) 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()) t.Errorf("admin_pages = %v", m.GetAdminPages())
} }