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..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"; @@ -9,6 +9,7 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, + DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { @@ -23,8 +24,11 @@ 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"; + type PendingRepo = { url: string; name: string; @@ -55,6 +59,15 @@ export function NewSessionView({ }: NewSessionViewProps) { const { data: runnerTypes } = useRunnerTypes(projectName); + const [menuSeenVersion, setMenuSeenVersion] = useLocalStorage("acp-menu-seen-version", null); + const showMenuDot = useMemo(() => menuSeenVersion !== MENU_VERSION, [menuSeenVersion]); + + const handleMenuOpenChange = useCallback((open: boolean) => { + if (open && menuSeenVersion !== MENU_VERSION) { + setMenuSeenVersion(MENU_VERSION); + } + }, [menuSeenVersion, setMenuSeenVersion]); + const [prompt, setPrompt] = useState(""); const [selectedRunner, setSelectedRunner] = useState(DEFAULT_RUNNER_TYPE_ID); const [selectedModel, setSelectedModel] = useState(""); @@ -177,10 +190,13 @@ export function NewSessionView({ />
- + - @@ -192,6 +208,7 @@ export function NewSessionView({ Upload File + 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