diff --git a/frontend/src/components/PluginsPanel.jsx b/frontend/src/components/PluginsPanel.jsx
index 90f80e2..22c72b2 100644
--- a/frontend/src/components/PluginsPanel.jsx
+++ b/frontend/src/components/PluginsPanel.jsx
@@ -72,7 +72,7 @@ export default function PluginsPanel({ sessionId, onClose }) {
}
});
- // State for contextual menu and action toast feedback (#605)
+ // State for contextual menu and action toast feedback (#602, #605)
const [activeMenuId, setActiveMenuId] = useState(null);
const [notification, setNotification] = useState("");
@@ -188,7 +188,7 @@ export default function PluginsPanel({ sessionId, onClose }) {
}
}, [input, sessionId]);
- // Close contextual action menu on global click or Escape key (#605)
+ // Close contextual action menu on global click or Escape key (#602, #605)
useEffect(() => {
const handleGlobalClick = () => setActiveMenuId(null);
const handleKeyDown = (e) => {
@@ -395,7 +395,7 @@ export default function PluginsPanel({ sessionId, onClose }) {
/>
- {/* Plugin selector with favorite & pin toggles (#601), compatibility badges (#597), & contextual menus (#605) */}
+ {/* Plugin selector with favorite & pin toggles (#601), compatibility badges (#597), & contextual menus (#602, #605) */}
{filteredPlugins.length > 0 ? (
filteredPlugins.map((p) => {
@@ -426,7 +426,7 @@ export default function PluginsPanel({ sessionId, onClose }) {
{isPinned ? "★" : "☆"}
- {/* Context Menu Trigger Button */}
+ {/* Context Menu Trigger Button (#602) */}
- {/* Context Dropdown Menu (#605) */}
+ {/* Context Dropdown Menu (#602, #605) */}
{isMenuOpen && (
e.stopPropagation()}
diff --git a/frontend/src/components/PluginsPanel.test.jsx b/frontend/src/components/PluginsPanel.test.jsx
index cecb3e5..94bc845 100644
--- a/frontend/src/components/PluginsPanel.test.jsx
+++ b/frontend/src/components/PluginsPanel.test.jsx
@@ -319,6 +319,44 @@ describe("PluginsPanel Export & Share Suite (#605)", () => {
});
});
+describe("PluginsPanel Favorite & Pin Support (#601)", () => {
+ beforeEach(() => {
+ localStorage.clear();
+ vi.clearAllMocks();
+ api.getPlugins.mockResolvedValue({ plugins: mockPluginsList });
+ api.getPluginLogs.mockResolvedValue({ logs: [] });
+ });
+
+ afterEach(() => {
+ cleanup();
+ });
+
+ test("toggles pin state and saves to localStorage", async () => {
+ render(
);
+
+ await waitFor(() => {
+ expect(screen.getByText("Summarizer")).toBeInTheDocument();
+ });
+
+ const pinBtn = screen.getByLabelText("Pin Summarizer");
+ fireEvent.click(pinBtn);
+
+ expect(screen.getByLabelText("Unpin Summarizer")).toBeInTheDocument();
+ expect(JSON.parse(localStorage.getItem("plugins-panel-pinned:session-601"))).toContain("summarizer");
+ });
+
+ test("restores pinned favorites from localStorage on mount", async () => {
+ localStorage.setItem("plugins-panel-pinned:session-601", JSON.stringify(["summarizer"]));
+
+ render(
);
+
+ await waitFor(() => {
+ expect(screen.getByLabelText("Unpin Summarizer")).toBeInTheDocument();
+ expect(screen.getByLabelText("Pin Calculator")).toBeInTheDocument();
+ });
+ });
+});
+
describe("PluginsPanel View State & Persistence Suite (#592)", () => {
let store = {};