Skip to content

V9 — Modals + AgentPicker (NewIssue, EditIssue, Diff, AgentPicker) #340

@Luis85

Description

@Luis85

Parent epic: #326 · Depends on: F2, F3, F4 (ModalShell + Dropdown atoms), F5 · Stories: US-003, US-018, US-020, US-021, US-023, US-026, US-028

Problem

The workspace has four cross-cutting overlay surfaces. They are referenced from V1, V3, V4, V5 — but the surfaces themselves are V9's responsibility so the verticals can dispatch a single open event and not own the overlay implementation.

Solution

NewIssueModal.vue

  • Fields: Title (required) · Type (feat | fix | research | chore | docs | req) · Scope (free text with suggestions: plugin-shell | tooling | ui | infra | …) · Description (textarea) · Assign Agent (optional, populated from useLiveStreamStore.agents)
  • Submit → CreateIssueUseCase (calls GitHubPort.createIssue) → on success: inject IssueRow at top of sidebar list (via useEntitiesStore.insertIssue); numbered toast "Issue #N created" (US-003)
  • Esc / ✕ closes without creating
  • Empty title → submit disabled; inline validation hint

EditIssueModal.vue

  • Pre-fills all fields from current issue + a "Context & Notes" textarea (acceptance criteria, constraints) — US-017, US-018
  • Submit → UpdateIssueUseCase (calls GitHubPort.updateIssue); re-renders IssueHeader on success
  • Same Esc / ✕ behaviour

DiffModal.vue

  • Wide (~660px), centered
  • File header: kind badge + path + line counts
  • Line-numbered diff body using DiffViewer atom (F4); hunk header in blue
  • Two variants:
    • Proposal: footer has Accept / Reject buttons; dispatch AcceptProposalUseCase / RejectProposalUseCase (US-020, US-021)
    • PR file: footer has Close + branch info (no actions) (US-026, US-028)
  • Esc closes

AgentPicker.vue

  • Fixed-position dropdown (not modal — must position relative to the trigger element)
  • Lists all agents with gradient avatar + Name + Role + status dot (green/amber)
  • Click an agent → resolves the dispatching promise with the agent id; consumer dispatches AssignTaskToAgentUseCase
  • Outside-click dismisses (use useEventListener('click', …, { once: true }) or capture-phase listener; respect z-index)
  • Esc dismisses

Cross-cutting modal infrastructure

  • Modals open via a useModalStack() composable (built on the ModalShell atom from F4): LIFO Esc handling, single overlay, focus trap, restore focus on close
  • All four overlays expose a typed promise-returning API:
    await openNewIssueModal()   // resolves to Issue | undefined
    await openEditIssueModal(issue)
    await openDiffModal(source) // source: { kind: 'proposal'; proposal } | { kind: 'pr-file'; pr; file }
    await openAgentPicker({ anchorEl })

Acceptance criteria

  • All four overlays render via ModalShell (modals) or Dropdown (picker)
  • LIFO Esc closes only the topmost overlay
  • Focus trapped while modal open; restored to trigger on close
  • NewIssueModal: required-title validation; success injects IssueRow; numbered toast (US-003)
  • EditIssueModal: pre-fills correctly; Context & Notes field saves to issue.context (US-017, US-018)
  • DiffModal proposal variant: Accept/Reject buttons dispatch correct use case + close (US-020, US-021)
  • DiffModal PR-file variant: read-only with branch info footer (US-026, US-028)
  • AgentPicker positions to the trigger; outside click closes; selection resolves with agent id
  • PageObject tests cover all flows + LIFO Esc + focus restore
  • Storybook stories for each modal in open state (with realistic content)
  • A11y: role="dialog" + aria-modal="true" + aria-labelledby for modals; focusable trigger / restorable focus
  • npm run typecheck, npm run lint, npm run test, npm run test:storybook pass

Out of scope

  • "Assign Agent" field in NewIssueModal actually assigning (note as TODO; defer to Phase 2 since agent assignment is its own use case)
  • Diff syntax highlighting language selection (just pass through code tokens from F4's DiffViewer)

Affected files

File Action
src/ui/components/modals/NewIssueModal.vue New
src/ui/components/modals/EditIssueModal.vue New
src/ui/components/modals/DiffModal.vue New
src/ui/components/modals/AgentPicker.vue New
src/ui/composables/useModalStack.ts New
src/application/issue/CreateIssueUseCase.ts New
src/application/issue/UpdateIssueUseCase.ts New
tests/ui/components/modals/*.test.ts + .po.ts New
tests/application/issue/*.test.ts New
stories/modals/*.stories.ts New

References

  • inputs/specorator-mvp-design-2026-05/Specorator_Design_Brief.html §"Modals & Overlays"
  • inputs/specorator-mvp-design-2026-05/Specorator_Handoff.html §"Modals & Overlays" table
  • User stories US-003, US-018, US-020, US-021, US-023, US-026, US-028

Metadata

Metadata

Assignees

No one assigned

    Labels

    modalmvp-workspaceuiUser interface and frontend application work

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions