From e2ce944e9f6262349b806c2a4e46e9283ed56ea2 Mon Sep 17 00:00:00 2001 From: Dotta Date: Sat, 23 May 2026 20:47:30 +0000 Subject: [PATCH] Add triage plugin sidebar install path Co-Authored-By: Paperclip --- packages/plugins/plugin-triage/src/ui/app.tsx | 37 ++++++++++++++++--- .../plugins/plugin-triage/tests/ui.spec.tsx | 37 ++++++++++--------- server/src/routes/plugins.ts | 8 ++++ 3 files changed, 59 insertions(+), 23 deletions(-) diff --git a/packages/plugins/plugin-triage/src/ui/app.tsx b/packages/plugins/plugin-triage/src/ui/app.tsx index 82da5f3c64d..e8d718f0ea4 100644 --- a/packages/plugins/plugin-triage/src/ui/app.tsx +++ b/packages/plugins/plugin-triage/src/ui/app.tsx @@ -490,22 +490,49 @@ function allowedTransitions(fromStateKey: string) { // Sidebar link slot. // --------------------------------------------------------------------------- +const triageSidebarIcon = ( + +); + export function SidebarLink({ context }: PluginSidebarProps) { const nav = useHostNavigation(); + const location = useHostLocation(); + const active = location.pathname.split("/").filter(Boolean).includes("triage"); return ( - Triage + {triageSidebarIcon} + Triage ); } diff --git a/packages/plugins/plugin-triage/tests/ui.spec.tsx b/packages/plugins/plugin-triage/tests/ui.spec.tsx index 4fa6b44052e..1a6ce65f730 100644 --- a/packages/plugins/plugin-triage/tests/ui.spec.tsx +++ b/packages/plugins/plugin-triage/tests/ui.spec.tsx @@ -1,6 +1,7 @@ // @vitest-environment jsdom -import { act, createElement } from "react"; +import { createElement } from "react"; +import { flushSync } from "react-dom"; import { createRoot, type Root } from "react-dom/client"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { @@ -162,12 +163,12 @@ describe("ReflectionDiff", () => { root = createRoot(container); }); afterEach(() => { - act(() => root.unmount()); + flushSync(() => root.unmount()); container.remove(); }); it("marks added, removed, and unchanged lines", () => { - act(() => { + flushSync(() => { root.render( createElement(ReflectionDiff, { baseContent: "Header\nKeep launch posts under 150 words.", @@ -183,7 +184,7 @@ describe("ReflectionDiff", () => { expect(dels.length).toBe(0); expect(adds.map((node) => node.textContent?.trim()).join("\n")).toContain("Flag vague benefits before approving."); - act(() => { + flushSync(() => { root.render( createElement(ReflectionDiff, { baseContent: "Keep posts short.", @@ -208,12 +209,12 @@ describe("SidebarLink", () => { root = createRoot(container); }); afterEach(() => { - act(() => root.unmount()); + flushSync(() => root.unmount()); container.remove(); }); it("links to /triage with company-prefixed aria label", () => { - act(() => { + flushSync(() => { root.render( createElement(SidebarLink, { context: { companyId: COMPANY_ID, companyPrefix: "PAP", projectId: null, entityId: null, entityType: null, userId: null }, @@ -243,12 +244,12 @@ describe("TriageRouteSidebar", () => { root = createRoot(container); }); afterEach(() => { - act(() => root.unmount()); + flushSync(() => root.unmount()); container.remove(); }); it("renders the queue list and highlights the active queue", () => { - act(() => { + flushSync(() => { root.render( createElement(TriageRouteSidebar, { context: { companyId: COMPANY_ID, companyPrefix: "PAP", projectId: null, entityId: null, entityType: null, userId: null }, @@ -281,12 +282,12 @@ describe("TriagePage — queue list home", () => { root = createRoot(container); }); afterEach(() => { - act(() => root.unmount()); + flushSync(() => root.unmount()); container.remove(); }); it("shows the queue card with title, key, and item counts", () => { - act(() => { + flushSync(() => { root.render(createElement(TriagePage, PAGE_PROPS)); }); const text = container.textContent ?? ""; @@ -318,12 +319,12 @@ describe("TriagePage — queue overview lists items by state", () => { root = createRoot(container); }); afterEach(() => { - act(() => root.unmount()); + flushSync(() => root.unmount()); container.remove(); }); it("groups items by state and links each item to the workbench", () => { - act(() => { + flushSync(() => { root.render(createElement(TriagePage, PAGE_PROPS)); }); const text = container.textContent ?? ""; @@ -390,12 +391,12 @@ describe("TriagePage — item workbench two-column layout", () => { root = createRoot(container); }); afterEach(() => { - act(() => root.unmount()); + flushSync(() => root.unmount()); container.remove(); }); it("renders chat pane (center/left) and document pane (right) with transition buttons and an open proposal", () => { - act(() => { + flushSync(() => { root.render(createElement(TriagePage, PAGE_PROPS)); }); @@ -468,12 +469,12 @@ describe("TriagePage — transition action editor", () => { root = createRoot(container); }); afterEach(() => { - act(() => root.unmount()); + flushSync(() => root.unmount()); container.remove(); }); it("lists existing transition actions and their template fields", () => { - act(() => { + flushSync(() => { root.render(createElement(TriagePage, PAGE_PROPS)); }); const text = container.textContent ?? ""; @@ -508,12 +509,12 @@ describe("SettingsPage — managed resource panel", () => { root = createRoot(container); }); afterEach(() => { - act(() => root.unmount()); + flushSync(() => root.unmount()); container.remove(); }); it("renders the managed resource health rows and reconcile button", () => { - act(() => { + flushSync(() => { root.render( createElement(SettingsPage, { context: { companyId: COMPANY_ID, companyPrefix: "PAP", projectId: null, entityId: null, entityType: null, userId: null }, diff --git a/server/src/routes/plugins.ts b/server/src/routes/plugins.ts index f3b674234b3..6d56f5aa0c0 100644 --- a/server/src/routes/plugins.ts +++ b/server/src/routes/plugins.ts @@ -160,6 +160,14 @@ const BUNDLED_PLUGIN_EXAMPLES: AvailablePluginExample[] = [ localPath: "packages/plugins/plugin-workspace-diff", tag: "first-party", }, + { + packageName: "@paperclipai/plugin-triage", + pluginKey: "paperclipai.plugin-triage", + displayName: "Paperclip Triage", + description: "First-party queue workbench for triaging arbitrary items with a teachable assistant.", + localPath: "packages/plugins/plugin-triage", + tag: "first-party", + }, { packageName: "@paperclipai/plugin-hello-world-example", pluginKey: "paperclip.hello-world-example",