# Implementation Plan: Copy URL & Toast Notifications **Branch**: `016-copy-url-toast` | **Date**: 2026-05-09 | **Spec**: [spec.md](spec.md) **Input**: Feature specification from `specs/016-copy-url-toast/spec.md` ## Summary Add a "Copy URL" button to the image detail page that copies the image's direct file URL to the clipboard, with a reusable toast notification service wired to confirm success or failure. All changes are UI-only; no API changes are required. ## Technical Context **Language/Version**: TypeScript (strict mode), Angular latest stable **Primary Dependencies**: Angular (`@angular/core`, `@angular/common`), RxJS (`BehaviorSubject`), browser Clipboard API (`navigator.clipboard.writeText`) **Storage**: N/A **Testing**: Karma/Jasmine (`ng test`) **Target Platform**: Browser (modern; Clipboard API requires HTTPS — already in place) **Project Type**: Angular standalone SPA **Performance Goals**: Copy action completes in < 100ms perceived latency; toast appears within 300ms of action **Constraints**: TypeScript strict mode, `ChangeDetectionStrategy.OnPush` on all components, no new npm dependencies **Scale/Scope**: Two new files (service + component), two modified files (detail + app component) ## Constitution Check *GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.* | Principle | Status | Notes | |-----------|--------|-------| | §2.1 Strict separation of concerns | ✓ PASS | Pure UI change; no API knowledge in UI beyond what's already in `ImageRecord.file_url` | | §2.6 No speculative abstraction | ✓ PASS | Toast service is justified: used immediately by this feature and explicitly planned for reuse (upload confirmation, delete confirmation, filter feedback). Two concrete use cases exist. | | §5.1 Tests alongside implementation | ✓ PASS | Tests required for `ToastService` and the copy button on `DetailComponent` | | §5.2 Test pyramid | ✓ PASS | Unit tests only (no API/DB involved); Karma/Jasmine | | §6 Tech stack | ✓ PASS | Angular, TypeScript strict — no new dependencies | | §7.3 Linting | ✓ PASS | `ng lint` must pass before task is done | | §8 Scope boundaries | ✓ PASS | No multi-user, no embeds, no public sharing infrastructure — just a clipboard copy | **Post-design re-check**: No violations. Feature is entirely additive. ## Project Structure ### Documentation (this feature) ```text specs/016-copy-url-toast/ ├── plan.md ← this file ├── research.md ← Phase 0 output ├── quickstart.md ← Phase 1 output ├── contracts/ │ └── toast-service.md ← Phase 1 output └── tasks.md ← /speckit-tasks output ``` ### Source Code ```text ui/src/app/ ├── app.component.ts ← modified: add to template ├── services/ │ └── toast.service.ts ← new: singleton toast service ├── toast/ │ └── toast.component.ts ← new: toast display component └── detail/ └── detail.component.ts ← modified: add Copy URL button + inject ToastService ui/src/app/services/ toast.service.spec.ts ← new: unit tests for ToastService ui/src/app/toast/ toast.component.spec.ts ← new: unit tests for ToastComponent ui/src/app/detail/ detail.component.spec.ts ← modified: tests for copy button behaviour ``` **Structure Decision**: Single-project Angular SPA. Toast service lives in `services/` alongside `ImageService` and `TagService`. Toast component gets its own `toast/` directory following the existing component-per-directory convention.