feat: add assistant board styling tools
This commit is contained in:
1 parent
a5fa6d3afe
commit
5458e7d647
21 files changed
+811
-18
No files matched your search
@@ -52,6 +52,7 @@ import {
|
||||
boardSavePartialSettingsSchema,
|
||||
boardSavePermissionsSchema,
|
||||
boardSaveSchema,
|
||||
boardSettingsSchema,
|
||||
boardSummarySchema,
|
||||
} from "@homarr/validation/board";
|
||||
import { byIdSchema } from "@homarr/validation/common";
|
||||
@@ -682,6 +683,57 @@ export const boardRouter = createTRPCRouter({
|
||||
|
||||
return await getFullBoardWithWhereAsync(ctx.db, boardWhere, ctx.session?.user.id ?? null);
|
||||
}),
|
||||
getBoardSettings: protectedProcedure
|
||||
.meta({
|
||||
mcp: {
|
||||
enabled: true,
|
||||
description:
|
||||
"Read the editable visual and behavior settings for one board, including its current custom CSS. Requires modify permission. REQUIRED: id (board ID). Call this before proposing board settings or custom CSS changes",
|
||||
},
|
||||
})
|
||||
.input(z.object({ id: z.string() }))
|
||||
.output(boardSettingsSchema)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const boardWhere = eq(boards.id, input.id);
|
||||
await throwIfActionForbiddenAsync(ctx, boardWhere, "modify");
|
||||
|
||||
const board = await ctx.db.query.boards.findFirst({
|
||||
columns: {
|
||||
id: true,
|
||||
name: true,
|
||||
pageTitle: true,
|
||||
metaTitle: true,
|
||||
logoImageUrl: true,
|
||||
faviconImageUrl: true,
|
||||
backgroundImageUrl: true,
|
||||
backgroundImageAttachment: true,
|
||||
backgroundImageRepeat: true,
|
||||
backgroundImageSize: true,
|
||||
primaryColor: true,
|
||||
secondaryColor: true,
|
||||
opacity: true,
|
||||
customCss: true,
|
||||
iconColor: true,
|
||||
itemRadius: true,
|
||||
disableStatus: true,
|
||||
},
|
||||
where: boardWhere,
|
||||
});
|
||||
if (!board) {
|
||||
throw new TRPCError({ code: "NOT_FOUND", message: "Board not found" });
|
||||
}
|
||||
|
||||
return {
|
||||
...board,
|
||||
pageTitle: board.pageTitle ?? "",
|
||||
metaTitle: board.metaTitle ?? "",
|
||||
logoImageUrl: board.logoImageUrl ?? "",
|
||||
faviconImageUrl: board.faviconImageUrl ?? "",
|
||||
backgroundImageUrl: board.backgroundImageUrl ?? "",
|
||||
customCss: board.customCss ?? "",
|
||||
iconColor: board.iconColor ?? "",
|
||||
};
|
||||
}),
|
||||
saveLayouts: protectedProcedure.input(boardSaveLayoutsSchema).mutation(async ({ ctx, input }) => {
|
||||
await throwIfActionForbiddenAsync(ctx, eq(boards.id, input.id), "modify");
|
||||
|
||||
|
||||
@@ -586,6 +586,28 @@ describe("getBoardByName should return board by name", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("getBoardSettings should return editable board settings", () => {
|
||||
test("returns current custom CSS and requires modify permission", async () => {
|
||||
const spy = vi.spyOn(boardAccess, "throwIfActionForbiddenAsync");
|
||||
const db = createDb();
|
||||
const caller = boardRouter.createCaller({ db, deviceType: undefined, session: defaultSession });
|
||||
const { boardId } = await createFullBoardAsync(db, "default");
|
||||
const customCss = ".dashboard-card { border-radius: 0.5rem; }";
|
||||
await db.update(boards).set({ customCss, primaryColor: "#123456", pageTitle: null }).where(eq(boards.id, boardId));
|
||||
|
||||
const result = await caller.getBoardSettings({ id: boardId });
|
||||
|
||||
expect(result).toMatchObject({
|
||||
id: boardId,
|
||||
name: "default",
|
||||
customCss,
|
||||
primaryColor: "#123456",
|
||||
pageTitle: null,
|
||||
});
|
||||
expect(spy).toHaveBeenCalledWith(expect.anything(), expect.anything(), "modify");
|
||||
});
|
||||
});
|
||||
|
||||
describe("savePartialBoardSettings should save general settings", () => {
|
||||
it("should save general settings", async () => {
|
||||
// Arrange
|
||||
|
||||
@@ -54,6 +54,7 @@ test("MCP tools should contain expected procedures", () => {
|
||||
expect(toolNames).toContain("app_byId");
|
||||
expect(toolNames).toContain("app_create");
|
||||
expect(toolNames).toContain("board_savePartialBoardSettings");
|
||||
expect(toolNames).toContain("board_getBoardSettings");
|
||||
expect(toolNames).toContain("board_duplicateBoard");
|
||||
expect(toolNames).toContain("icon_findIcons");
|
||||
expect(toolNames).toContain("info_getInfo");
|
||||
|
||||
Reference in new issue
Block a user