From dd7941f76d96f0aa7fac9729f38bc19c97f294cb Mon Sep 17 00:00:00 2001 From: Ambient Code Bot Date: Thu, 16 Apr 2026 14:16:42 -0400 Subject: [PATCH 1/3] docs: add spec-kit artifacts for session options menu (011) Co-Authored-By: Claude Opus 4.6 (1M context) --- .../2026-04-16-session-options-menu-design.md | 59 ++++++++++++++++ specs/011-session-options-menu/plan.md | 22 ++++++ specs/011-session-options-menu/spec.md | 69 +++++++++++++++++++ specs/011-session-options-menu/tasks.md | 40 +++++++++++ 4 files changed, 190 insertions(+) create mode 100644 docs/superpowers/specs/2026-04-16-session-options-menu-design.md create mode 100644 specs/011-session-options-menu/plan.md create mode 100644 specs/011-session-options-menu/spec.md create mode 100644 specs/011-session-options-menu/tasks.md diff --git a/docs/superpowers/specs/2026-04-16-session-options-menu-design.md b/docs/superpowers/specs/2026-04-16-session-options-menu-design.md new file mode 100644 index 000000000..575b4bab1 --- /dev/null +++ b/docs/superpowers/specs/2026-04-16-session-options-menu-design.md @@ -0,0 +1,59 @@ +# Session Options Menu Infrastructure + +**Branch**: `feat/advanced-options-menu` +**Date**: 2026-04-16 +**Status**: Draft + +## Overview + +Restructure the `+` dropdown in `new-session-view.tsx` to serve as the unified entry point for all per-session configuration. Add a separator dividing context actions (Add Repository, Upload File) from session options (toggles, form-heavy config). Add a discovery dot on the `+` button to highlight new menu items users haven't seen. + +This PR adds no new menu items — it establishes the infrastructure that PR #1328 (project intelligence toggle) and PR #1326 (SDK options modal) will use. + +## Menu Structure + +``` +[+] button (discovery dot when new items exist) +├── Add Repository → existing AddContextModal +├── Upload File → existing (disabled until PR #1282 merges) +├── DropdownMenuSeparator +└── (future items added here by other PRs) +``` + +## Patterns Established + +### Boolean toggles +Use `DropdownMenuCheckboxItem`. State lives in `new-session-view.tsx` as `useState`. Example (PR #1328 will add): +```tsx + setDisableIntelligence(checked === true)} +> + Disable project intelligence + +``` + +### Form-heavy config +Use `DropdownMenuItem` with `onClick` that opens a `Dialog`. The dialog contains the full form with save/preview flow. Example (PR #1326 will add): +```tsx + setSdkOptionsOpen(true)}> + SDK Options... + +``` + +### New item badges +Menu items can render a `Badge` with "New" to indicate features the user hasn't interacted with yet. Tracked client-side. + +### Discovery dot +A small circle indicator (`h-2 w-2 rounded-full bg-primary`) positioned on the top-right of the `+` button. Visible when the menu contains items newer than the user's last-seen version. Cleared when the dropdown opens. Tracked via `localStorage` key `acp-menu-seen-version` compared against a constant `MENU_VERSION` string. + +## Components Changed + +- `new-session-view.tsx` — add separator, discovery dot logic, import new dropdown primitives (`DropdownMenuSeparator`, `DropdownMenuCheckboxItem`) + +## What This PR Does NOT Include + +- No new menu items (those come from #1328 and #1326) +- No backend changes +- No new feature flags +- No modals or dialogs (those come with the items that need them) diff --git a/specs/011-session-options-menu/plan.md b/specs/011-session-options-menu/plan.md new file mode 100644 index 000000000..17c07d901 --- /dev/null +++ b/specs/011-session-options-menu/plan.md @@ -0,0 +1,22 @@ +# Implementation Plan: Session Options Menu Infrastructure + +**Branch**: `011-session-options-menu` | **Date**: 2026-04-16 | **Spec**: [spec.md](spec.md) + +## Summary + +Add a separator and discovery dot to the `+` dropdown in `new-session-view.tsx`. Frontend-only change. One file modified, one test file updated. + +## Technical Context + +**Language**: TypeScript/React (Next.js 14) +**Dependencies**: Shadcn/ui (`DropdownMenuSeparator`, `DropdownMenuCheckboxItem`), localStorage +**Testing**: vitest +**Target**: `components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/new-session-view.tsx` + +## Files + +``` +components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/ +├── new-session-view.tsx # MODIFY: add separator, discovery dot, imports +└── __tests__/new-session-view.test.tsx # MODIFY: add tests for separator and dot +``` diff --git a/specs/011-session-options-menu/spec.md b/specs/011-session-options-menu/spec.md new file mode 100644 index 000000000..23f114c37 --- /dev/null +++ b/specs/011-session-options-menu/spec.md @@ -0,0 +1,69 @@ +# Feature Specification: Session Options Menu Infrastructure + +**Feature Branch**: `feat/advanced-options-menu` +**Created**: 2026-04-16 +**Status**: Draft +**Input**: Restructure the + dropdown as the unified entry point for per-session configuration + +## Overview + +The `+` button dropdown in `new-session-view.tsx` becomes the single entry point for all per-session options. A separator divides context actions (Add Repository, Upload File) from session options. A discovery dot on the button highlights new menu items. This PR adds no new items — it establishes the patterns for PRs #1326 and #1328 to use. + +## User Scenarios & Testing + +### User Story 1 - Menu Structure with Separator (Priority: P1) + +The `+` menu currently has two items (Add Repository, Upload File). Add a separator after them to visually group future session option items below. + +**Why this priority**: Foundation that all other PRs build on. + +**Independent Test**: Open the `+` menu, verify the separator renders below Upload File. + +**Acceptance Scenarios**: + +1. **Given** a user opens the `+` menu, **When** the menu renders, **Then** Add Repository and Upload File appear above a separator line. +2. **Given** no session options are registered yet, **When** the menu renders, **Then** only the separator appears below the context actions (no empty section). + +--- + +### User Story 2 - Discovery Dot (Priority: P1) + +A small dot indicator on the `+` button when the menu contains items the user hasn't seen. Clears when they open the menu. + +**Why this priority**: Feature discovery mechanism needed before new items are added. + +**Independent Test**: Set localStorage to an old version, verify dot appears. Open menu, verify dot disappears. + +**Acceptance Scenarios**: + +1. **Given** `MENU_VERSION` is newer than the user's `acp-menu-seen-version` in localStorage, **When** the page loads, **Then** a small dot appears on the `+` button. +2. **Given** the discovery dot is visible, **When** the user opens the dropdown, **Then** the dot disappears and `acp-menu-seen-version` is updated in localStorage. +3. **Given** `acp-menu-seen-version` matches `MENU_VERSION`, **When** the page loads, **Then** no dot is shown. + +--- + +### Edge Cases + +- localStorage unavailable (private browsing) → no dot, fail silently. +- Multiple tabs → each tab checks localStorage independently; opening in one tab clears for all. + +## Requirements + +### Functional Requirements + +- **FR-001**: `+` dropdown MUST render a `DropdownMenuSeparator` between context actions and session options. +- **FR-002**: `+` button MUST show a discovery dot when `MENU_VERSION` is newer than `localStorage.getItem("acp-menu-seen-version")`. +- **FR-003**: Discovery dot MUST clear when the dropdown opens, updating localStorage. +- **FR-004**: Discovery dot MUST fail silently if localStorage is unavailable. +- **FR-005**: New dropdown primitives (`DropdownMenuSeparator`, `DropdownMenuCheckboxItem`) MUST be imported and available for use by downstream PRs. + +### Key Entities + +- **MENU_VERSION**: A string constant in `new-session-view.tsx` (e.g., `"2026-04-16"`). Bumped when new menu items are added. + +## Success Criteria + +- **SC-001**: The separator renders visually in the `+` dropdown. +- **SC-002**: Discovery dot appears/disappears based on localStorage version comparison. +- **SC-003**: No regressions in existing menu behavior (Add Repository still works). +- **SC-004**: Frontend test suite passes, build clean. diff --git a/specs/011-session-options-menu/tasks.md b/specs/011-session-options-menu/tasks.md new file mode 100644 index 000000000..5c5cf43d2 --- /dev/null +++ b/specs/011-session-options-menu/tasks.md @@ -0,0 +1,40 @@ +# Tasks: Session Options Menu Infrastructure + +**Input**: Design documents from `/specs/011-session-options-menu/` + +## Phase 1: Separator + Imports + +- [ ] T001 [US1] In `new-session-view.tsx`, import `DropdownMenuSeparator` and `DropdownMenuCheckboxItem` from `@/components/ui/dropdown-menu` +- [ ] T002 [US1] Add `` after the Upload File menu item in the `+` dropdown + +### Commit: `feat(frontend): add separator to + dropdown for session options` + +--- + +## Phase 2: Discovery Dot (TDD) + +- [ ] T010 [US2] Add `MENU_VERSION` constant (e.g., `"2026-04-16"`) to `new-session-view.tsx` +- [ ] T011 [US2] Add state + effect for discovery dot: compare `MENU_VERSION` against `localStorage.getItem("acp-menu-seen-version")`, set `showDot` state. Wrap in try/catch for localStorage unavailability. +- [ ] T012 [US2] On `DropdownMenu` `onOpenChange(open)`: when `open` is true, clear the dot and write `MENU_VERSION` to localStorage +- [ ] T013 [US2] Render the dot: when `showDot` is true, render a `` inside the `+` button wrapper (make wrapper `relative`) +- [ ] T014 [US2] Add tests in `__tests__/new-session-view.test.tsx`: dot visible when localStorage version is old, dot clears on menu open, dot hidden when versions match + +### Commit: `feat(frontend): add discovery dot to + button for new menu items` + +--- + +## Phase 3: Verify + +- [ ] T020 Run frontend tests: `cd components/frontend && npx vitest run` +- [ ] T021 Run frontend build: `cd components/frontend && npm run build` +- [ ] T022 Grep for `any` types in changed files + +### Commit (if fixes needed): `chore: lint fixes` + +--- + +## Dependencies + +- Phase 1 → independent +- Phase 2 → independent (but logically follows Phase 1) +- Phase 3 → depends on Phases 1 + 2 From b22fd61e5c516699f3ec2e4fff55dbd4d10c0cba Mon Sep 17 00:00:00 2001 From: Ambient Code Bot Date: Thu, 16 Apr 2026 14:18:33 -0400 Subject: [PATCH 2/3] feat(frontend): add separator and discovery dot to + dropdown Restructure the + dropdown as the unified entry point for per-session options. Add DropdownMenuSeparator between context actions and session options section. Add discovery dot (localStorage-based) on the + button to highlight new menu items. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../components/new-session-view.tsx | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/new-session-view.tsx b/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/new-session-view.tsx index 7aa909b1b..32c78912f 100755 --- a/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/new-session-view.tsx +++ b/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/new-session-view.tsx @@ -9,6 +9,7 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, + DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { @@ -25,6 +26,8 @@ import { useModels } from "@/services/queries/use-models"; import { DEFAULT_RUNNER_TYPE_ID } from "@/services/api/runner-types"; import type { WorkflowConfig } from "../lib/types"; +const MENU_VERSION = "2026-04-16"; + type PendingRepo = { url: string; name: string; @@ -55,6 +58,28 @@ export function NewSessionView({ }: NewSessionViewProps) { const { data: runnerTypes } = useRunnerTypes(projectName); + const [showMenuDot, setShowMenuDot] = useState(false); + + useEffect(() => { + try { + const seen = localStorage.getItem("acp-menu-seen-version"); + if (seen !== MENU_VERSION) setShowMenuDot(true); + } catch { + // localStorage unavailable (private browsing) — no dot + } + }, []); + + const handleMenuOpenChange = useCallback((open: boolean) => { + if (open && showMenuDot) { + setShowMenuDot(false); + try { + localStorage.setItem("acp-menu-seen-version", MENU_VERSION); + } catch { + // localStorage unavailable + } + } + }, [showMenuDot]); + const [prompt, setPrompt] = useState(""); const [selectedRunner, setSelectedRunner] = useState(DEFAULT_RUNNER_TYPE_ID); const [selectedModel, setSelectedModel] = useState(""); @@ -177,10 +202,13 @@ export function NewSessionView({ />
- + - @@ -192,6 +220,7 @@ export function NewSessionView({ Upload File + Date: Thu, 16 Apr 2026 14:21:37 -0400 Subject: [PATCH 3/3] refactor(frontend): use useLocalStorage hook for discovery dot Replace manual localStorage + useEffect with the existing useLocalStorage hook. Eliminates extra render cycle, manual try/catch, and cross-tab sync comes free. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../components/new-session-view.tsx | 26 +++++-------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/new-session-view.tsx b/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/new-session-view.tsx index 32c78912f..eada72b71 100755 --- a/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/new-session-view.tsx +++ b/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/new-session-view.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState, useRef, useCallback, useEffect } from "react"; +import { useState, useRef, useCallback, useEffect, useMemo } from "react"; import { MessageSquarePlus, ArrowUp, Loader2, Plus, GitBranch, Upload, X } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; @@ -24,6 +24,7 @@ import { AddContextModal } from "./modals/add-context-modal"; import { useRunnerTypes } from "@/services/queries/use-runner-types"; import { useModels } from "@/services/queries/use-models"; import { DEFAULT_RUNNER_TYPE_ID } from "@/services/api/runner-types"; +import { useLocalStorage } from "@/hooks/use-local-storage"; import type { WorkflowConfig } from "../lib/types"; const MENU_VERSION = "2026-04-16"; @@ -58,27 +59,14 @@ export function NewSessionView({ }: NewSessionViewProps) { const { data: runnerTypes } = useRunnerTypes(projectName); - const [showMenuDot, setShowMenuDot] = useState(false); - - useEffect(() => { - try { - const seen = localStorage.getItem("acp-menu-seen-version"); - if (seen !== MENU_VERSION) setShowMenuDot(true); - } catch { - // localStorage unavailable (private browsing) — no dot - } - }, []); + const [menuSeenVersion, setMenuSeenVersion] = useLocalStorage("acp-menu-seen-version", null); + const showMenuDot = useMemo(() => menuSeenVersion !== MENU_VERSION, [menuSeenVersion]); const handleMenuOpenChange = useCallback((open: boolean) => { - if (open && showMenuDot) { - setShowMenuDot(false); - try { - localStorage.setItem("acp-menu-seen-version", MENU_VERSION); - } catch { - // localStorage unavailable - } + if (open && menuSeenVersion !== MENU_VERSION) { + setMenuSeenVersion(MENU_VERSION); } - }, [showMenuDot]); + }, [menuSeenVersion, setMenuSeenVersion]); const [prompt, setPrompt] = useState(""); const [selectedRunner, setSelectedRunner] = useState(DEFAULT_RUNNER_TYPE_ID);