diff --git a/crates/web/src/assets/css/components.css b/crates/web/src/assets/css/components.css index e3731813b..094a7f03b 100644 --- a/crates/web/src/assets/css/components.css +++ b/crates/web/src/assets/css/components.css @@ -1113,6 +1113,10 @@ -webkit-mask-image: url("../icons/masks/mask-2f9b873ab7f9e17f.svg"); mask-image: url("../icons/masks/mask-2f9b873ab7f9e17f.svg"); } +.settings-nav-item[data-section="projects"]::before { + -webkit-mask-image: url("../icons/masks/mask-9efc91f08692af1a.svg"); + mask-image: url("../icons/masks/mask-9efc91f08692af1a.svg"); +} .settings-nav-item[data-section="environment"]::before, .settings-nav-item[data-section="terminal"]::before { -webkit-mask-image: url("../icons/masks/mask-2920334234a725b2.svg"); diff --git a/crates/web/src/assets/js/page-projects.js b/crates/web/src/assets/js/page-projects.js index dde144a03..3f031b8d6 100644 --- a/crates/web/src/assets/js/page-projects.js +++ b/crates/web/src/assets/js/page-projects.js @@ -17,6 +17,7 @@ var completions = signal([]); var editingProject = signal(null); var detecting = signal(false); var clearing = signal(false); +var _projectsContainer = null; function PathInput(props) { var inputRef = useRef(null); @@ -316,17 +317,19 @@ function ProjectsPage() { `; } -registerPage( - routes.projects, - function initProjects(container) { - container.style.cssText = "flex-direction:column;padding:0;overflow:hidden;"; - editingProject.value = null; - completions.value = []; - detecting.value = false; - render(html`<${ProjectsPage} />`, container); - }, - function teardownProjects() { - var container = S.$("pageContent"); - if (container) render(null, container); - }, -); +export function initProjects(container) { + _projectsContainer = container; + container.style.cssText = "flex-direction:column;padding:0;overflow:hidden;"; + editingProject.value = null; + completions.value = []; + detecting.value = false; + clearing.value = false; + render(html`<${ProjectsPage} />`, container); +} + +export function teardownProjects() { + if (_projectsContainer) render(null, _projectsContainer); + _projectsContainer = null; +} + +registerPage(routes.projects, initProjects, teardownProjects); diff --git a/crates/web/src/assets/js/page-settings.js b/crates/web/src/assets/js/page-settings.js index 7c12d64db..bbebf2448 100644 --- a/crates/web/src/assets/js/page-settings.js +++ b/crates/web/src/assets/js/page-settings.js @@ -21,6 +21,7 @@ import { initMcp, teardownMcp } from "./page-mcp.js"; import { initMonitoring, teardownMonitoring } from "./page-metrics.js"; import { initNetworkAudit, teardownNetworkAudit } from "./page-network-audit.js"; import { initNodes, teardownNodes } from "./page-nodes.js"; +import { initProjects, teardownProjects } from "./page-projects.js"; import { initProviders, teardownProviders } from "./page-providers.js"; import { initSkills, teardownSkills } from "./page-skills.js"; import { initTerminal, teardownTerminal } from "./page-terminal.js"; @@ -119,6 +120,12 @@ var sections = [ icon: html``, page: true, }, + { + id: "projects", + label: "Projects", + icon: html``, + page: true, + }, { id: "environment", label: "Environment", @@ -5333,6 +5340,7 @@ var pageSectionHandlers = { channels: { init: initChannels, teardown: teardownChannels }, mcp: { init: initMcp, teardown: teardownMcp }, nodes: { init: initNodes, teardown: teardownNodes }, + projects: { init: initProjects, teardown: teardownProjects }, hooks: { init: initHooks, teardown: teardownHooks }, skills: { init: initSkills, teardown: teardownSkills }, agents: { init: initAgents, teardown: teardownAgents }, diff --git a/crates/web/ui/e2e/specs/projects.spec.js b/crates/web/ui/e2e/specs/projects.spec.js index 9206d1bf8..047ba9dd2 100644 --- a/crates/web/ui/e2e/specs/projects.spec.js +++ b/crates/web/ui/e2e/specs/projects.spec.js @@ -32,6 +32,14 @@ test.describe("Projects page", () => { await expect(page.locator('a.nav-link[href="/projects"]')).toHaveCount(0); }); + test("projects accessible from settings sidebar", async ({ page }) => { + const pageErrors = watchPageErrors(page); + await navigateAndWait(page, "/settings/projects"); + + await expect(page.getByRole("heading", { name: "Repositories", exact: true })).toBeVisible(); + expect(pageErrors).toEqual([]); + }); + test("page has no JS errors", async ({ page }) => { const pageErrors = watchPageErrors(page); await navigateAndWait(page, "/projects"); diff --git a/crates/web/ui/e2e/specs/settings-nav.spec.js b/crates/web/ui/e2e/specs/settings-nav.spec.js index 8e8274561..1c6fcabbd 100644 --- a/crates/web/ui/e2e/specs/settings-nav.spec.js +++ b/crates/web/ui/e2e/specs/settings-nav.spec.js @@ -184,6 +184,7 @@ test.describe("Settings navigation", () => { { id: "mcp", heading: "MCP" }, { id: "hooks", heading: "Hooks" }, { id: "skills", heading: "Skills" }, + { id: "projects", heading: "Repositories" }, { id: "sandboxes", heading: "Sandboxes" }, { id: "monitoring", heading: "Monitoring" }, { id: "logs", heading: "Logs" },