Skip to content

Instantly share code, notes, and snippets.

@steipete
Created August 2, 2026 22:18
Show Gist options
  • Select an option

  • Save steipete/b9922938687a045e3067940555401817 to your computer and use it in GitHub Desktop.

Select an option

Save steipete/b9922938687a045e3067940555401817 to your computer and use it in GitHub Desktop.
openclaw PR 118217 sidebar context-menu proof
// Live UI proof for openclaw PR #118217 — sidebar context menus + hideable catalogs.
// Drives the repo mock harness (pnpm dev:ui:mock) with real Chromium via Playwright.
import { chromium } from "playwright";
const BASE = process.env.PROOF_BASE ?? "http://127.0.0.1:5199";
const OUT = process.env.PROOF_OUT ?? ".";
const results = [];
const check = (name, ok, detail) => {
results.push({ name, ok, detail });
console.log(`${ok ? "PASS" : "FAIL"} ${name}${detail ? ` — ${detail}` : ""}`);
};
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1360, height: 900 } });
await page.goto(BASE, { waitUntil: "domcontentloaded", timeout: 120_000 });
await page.waitForSelector("aside.sidebar", { timeout: 120_000 });
// Catalog sections load through the idle-import boundary; wait for the header.
await page.waitForSelector('[data-session-section="catalog:codex"]', { timeout: 60_000 });
// 1. Native context menu suppressed on plain sidebar chrome (footer).
const footerPrevented = await page.$eval(".sidebar-shell__footer", (el) => {
const ev = new MouseEvent("contextmenu", { bubbles: true, cancelable: true });
el.dispatchEvent(ev);
return ev.defaultPrevented;
});
check("native context menu suppressed on sidebar chrome", footerPrevented === true);
// 2. Right-click on the agent card opens the agent menu (custom, not native).
await page.click("openclaw-sidebar-agent-card", { button: "right" });
await page.waitForSelector(".sidebar-agent-menu[open]", { state: "attached", timeout: 10_000 });
check("agent card right-click opens agent menu", true);
await page.waitForTimeout(500);
await page.screenshot({ path: `${OUT}/1-agent-card-context-menu.png` });
await page.keyboard.press("Escape");
await page.waitForTimeout(300);
// 3. Right-click on the Codex catalog header opens the catalog view menu with Hide.
const codexHeader = page.locator(
'[data-session-section="catalog:codex"] .sidebar-recent-sessions__head',
);
await codexHeader.click({ button: "right" });
await page.waitForSelector(".sidebar-catalog-view-menu[open]", { state: "attached", timeout: 10_000 });
const hideItem = page.locator(
'.sidebar-catalog-view-menu wa-dropdown-item[value="hide-catalog"]',
);
check("catalog header right-click opens view menu with Hide item", (await hideItem.count()) === 1);
await page.waitForTimeout(500);
await page.screenshot({ path: `${OUT}/2-catalog-header-context-menu.png` });
// 4. Hide removes the Codex section and persists the preference.
await hideItem.click({ force: true });
await page.waitForSelector('[data-session-section="catalog:codex"]', {
state: "detached",
timeout: 10_000,
});
const stored = await page.evaluate(() =>
localStorage.getItem("openclaw:sidebar:sessions:hidden-catalogs"),
);
check("Hide removes section and persists", stored === '["codex"]', `stored=${stored}`);
await page.screenshot({ path: `${OUT}/3-codex-section-hidden.png` });
// 5. Settings → Appearance lists the hidden section with a Show control.
await page.goto(`${BASE}/settings/appearance`, { waitUntil: "domcontentloaded" });
const heading = page.locator("h3", { hasText: "Hidden session sections" });
await heading.waitFor({ timeout: 60_000 });
const showRow = page.locator(".settings-row", { hasText: "codex" });
check("settings lists hidden section", (await showRow.count()) >= 1);
await showRow.first().scrollIntoViewIfNeeded();
await page.screenshot({ path: `${OUT}/4-settings-hidden-sections.png` });
// 6. Show restores the section live (event-synced, no reload).
await showRow.first().locator("button", { hasText: "Show" }).click({ force: true });
await page.waitForTimeout(300);
const storedAfter = await page.evaluate(() =>
localStorage.getItem("openclaw:sidebar:sessions:hidden-catalogs"),
);
check("Show clears preference", storedAfter === "[]", `stored=${storedAfter}`);
// Settings replaces the app sidebar with its own workspace nav; return to the
// app via SPA navigation (no reload) to observe the restored section.
await page.getByText("Back to app").click();
await page.waitForSelector("aside.sidebar", { timeout: 30_000 });
await page.waitForSelector('[data-session-section="catalog:codex"]', { timeout: 30_000 });
check("Codex section restored in sidebar after SPA navigation, no reload", true);
await page.screenshot({ path: `${OUT}/5-codex-section-restored.png` });
await browser.close();
const failed = results.filter((r) => !r.ok);
console.log(JSON.stringify({ verdict: failed.length === 0 ? "PASS" : "FAIL", results }, null, 2));
process.exit(failed.length === 0 ? 0 : 1);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment