feat: add CLI Apps settings MVP

This commit is contained in:
Xubin Ren
2026-05-23 00:33:31 +08:00
parent a5a956d9af
commit e2d00ffc8f
44 changed files with 4338 additions and 77 deletions
+83 -1
View File
@@ -2,7 +2,42 @@ import { act, fireEvent, render, screen, waitFor } from "@testing-library/react"
import { describe, expect, it, vi } from "vitest";
import { MessageBubble } from "@/components/MessageBubble";
import type { UIMessage } from "@/lib/types";
import type { CliAppInfo, UIMessage } from "@/lib/types";
const CLI_APPS: CliAppInfo[] = [
{
name: "zoom",
display_name: "Zoom",
category: "productivity",
description: "Meetings",
requires: "",
source: "harness",
entry_point: "cli-anything-zoom",
install_supported: true,
installed: true,
available: true,
status: "installed",
logo_url: "https://example.invalid/zoom.svg",
brand_color: "#0B5CFF",
skill_installed: true,
},
{
name: "krita",
display_name: "Krita",
category: "image",
description: "Painting",
requires: "",
source: "harness",
entry_point: "cli-anything-krita",
install_supported: true,
installed: false,
available: false,
status: "not_installed",
logo_url: null,
brand_color: "#3BABFF",
skill_installed: false,
},
];
describe("MessageBubble", () => {
it("renders user messages as right-aligned pills", () => {
@@ -22,6 +57,53 @@ describe("MessageBubble", () => {
expect(screen.queryByRole("button", { name: "Copy reply" })).not.toBeInTheDocument();
});
it("renders installed CLI app mentions inside sent user messages", () => {
const message: UIMessage = {
id: "u-cli",
role: "user",
content: "Hi nano, please use @zoom to book a meeting, not @krita",
createdAt: Date.now(),
};
render(<MessageBubble message={message} cliApps={CLI_APPS} />);
const token = screen.getByTestId("message-cli-mention-zoom");
expect(token).toHaveTextContent("@zoom");
expect(token.className).not.toContain("rounded");
expect(token.className).not.toContain("px-");
expect(token.getAttribute("style")).toContain("color: #0B5CFF");
expect(token.getAttribute("style")).toContain("text-shadow");
expect(screen.getByTestId("message-cli-mention-logo-zoom")).toBeInTheDocument();
expect(screen.queryByTestId("message-cli-mention-krita")).not.toBeInTheDocument();
expect(screen.getByText(/not @krita/)).toBeInTheDocument();
});
it("renders structured CLI app attachments even without the installed catalog", () => {
const message: UIMessage = {
id: "u-cli-attached",
role: "user",
content: "Please use @drawio for the diagram",
createdAt: Date.now(),
cliApps: [{
name: "drawio",
display_name: "Draw.io",
category: "diagram",
entry_point: "cli-anything-drawio",
logo_url: "https://example.invalid/drawio.svg",
brand_color: "#F08705",
}],
};
render(<MessageBubble message={message} cliApps={[]} />);
const token = screen.getByTestId("message-cli-mention-drawio");
expect(token).toHaveTextContent("@drawio");
expect(token.className).not.toContain("rounded");
expect(token.className).not.toContain("px-");
expect(token.getAttribute("style")).toContain("color: #F08705");
expect(screen.getByTestId("message-cli-mention-logo-drawio")).toBeInTheDocument();
});
it("copies completed assistant replies from the action row", async () => {
const writeText = vi.fn().mockResolvedValue(undefined);
Object.defineProperty(navigator, "clipboard", {