mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-23 17:33:51 +02:00
Add triage plugin sidebar install path
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
409c0f863f
commit
e2ce944e9f
3 files changed
+59
-23
No files matched your search
@@ -490,22 +490,49 @@ function allowedTransitions(fromStateKey: string) {
|
||||
// Sidebar link slot.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const triageSidebarIcon = (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
style={{ width: 16, height: 16, display: "block", flexShrink: 0 }}
|
||||
>
|
||||
<path d="M8 6h13" />
|
||||
<path d="M8 12h13" />
|
||||
<path d="M8 18h13" />
|
||||
<path d="M3 6h.01" />
|
||||
<path d="M3 12h.01" />
|
||||
<path d="M3 18h.01" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
export function SidebarLink({ context }: PluginSidebarProps) {
|
||||
const nav = useHostNavigation();
|
||||
const location = useHostLocation();
|
||||
const active = location.pathname.split("/").filter(Boolean).includes("triage");
|
||||
return (
|
||||
<a
|
||||
{...nav.linkProps("/triage")}
|
||||
style={{
|
||||
color: "inherit",
|
||||
textDecoration: "none",
|
||||
display: "block",
|
||||
padding: "6px 8px",
|
||||
borderRadius: 6,
|
||||
fontSize: 14,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 10,
|
||||
padding: "8px 12px",
|
||||
fontSize: 13,
|
||||
fontWeight: 500,
|
||||
background: active ? tokens.accent : "transparent",
|
||||
}}
|
||||
data-triage-sidebar-active={active ? "true" : "false"}
|
||||
aria-label={`Open Triage for ${context.companyPrefix ?? "company"}`}
|
||||
>
|
||||
Triage
|
||||
{triageSidebarIcon}
|
||||
<span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>Triage</span>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in new issue
Block a user