# OpenSpec Change: enterprise-browser-clipboard - **Change ID**: enterprise-browser-clipboard - **Package**: `@suaveplan/clipboard` - **Category**: browser - **Target path**: `browser/platform/clipboard` - **Status**: IN PROGRESS - **Date**: 2026-04-24 - **Reference spec**: `/mnt/s/web/suaveplan/enterprise/clipboard.md` --- ## Summary Deliver `@suaveplan/clipboard` on the genesis branch at enterprise quality. The package provides `ClipboardManager` with injectable `ClipboardNavigator` seam, plain-text and rich copy/read, permission probing, and `execCommand` fallback. Genesis adds image and arbitrary-blob clipboard I/O, typed errors via `@suaveplan/error`, secure-context guard, paste-event fallback, permission-change subscription, async queue for concurrent operations, OTel counters/histograms, and Playwright E2E across Chromium/WebKit/Firefox. --- ## Current-state audit (genesis) Fresh build — does not exist on genesis yet. --- ## Available workspace deps on genesis | Package | Role | |---|---| | `@suaveplan/error` (`workspace:^`) | `CLIPBOARD_API_UNAVAILABLE`, `CLIPBOARD_PERMISSION_DENIED`, etc. | | `@suaveplan/logger` (`workspace:^`) | Structured logging | | `@suaveplan/schema-registry` (`workspace:^`) | `ClipboardOptions` schema | | `zod` (`catalog:forms`) | `ClipboardOptions` Zod schema | | `@opentelemetry/api` (`catalog:observability`) | Copy/read/fallback metrics | | `tslib` (`catalog:build`) | Runtime helpers | Dev: `vitest`, `@vitest/coverage-istanbul`, `happy-dom`, `@playwright/test`, `@suaveplan/testing`. --- ## Preserved public API surface ```ts class ClipboardManager { constructor(navigator?: ClipboardNavigator) copyText(text: string, options?: ClipboardOptions): Promise readText(options?: ClipboardOptions): Promise copyRichContent(data: ClipboardData, options?: ClipboardOptions): Promise readRichContent(options?: ClipboardOptions): Promise checkPermission(name: 'clipboard-read' | 'clipboard-write'): Promise onPermissionChange(name: 'clipboard-read' | 'clipboard-write', cb: (s: PermissionState) => void): () => void } // Types interface ClipboardData { text?: string html?: string image?: Blob // new blobs?: Record // new — arbitrary MIME } interface ClipboardResult { success: boolean; method: 'api' | 'fallback'; payload?: ClipboardData; error?: SuaveplanError } interface PermissionResult { state: PermissionState; supported: boolean } ``` Subpath exports: `"."`, `"./types"`. --- ## New additions for genesis ### 1. Image and arbitrary-blob clipboard I/O `copyRichContent` and `readRichContent` accept/return `image?: Blob` and `blobs?: Record` via `ClipboardItem` for modern Clipboard API. ### 2. Typed errors `CLIPBOARD_API_UNAVAILABLE`, `CLIPBOARD_PERMISSION_DENIED`, `CLIPBOARD_FALLBACK_FAILED`, `CLIPBOARD_INSECURE_CONTEXT` — all extend `@suaveplan/error`. ### 3. Secure-context guard `requireSecureContext()` probes `window.isSecureContext`; throws `CLIPBOARD_INSECURE_CONTEXT` outside HTTPS/localhost before attempting clipboard access. ### 4. Paste-event fallback `readText` in non-API contexts orchestrates focus + `paste` event capture as best-effort fallback. ### 5. Permission-change subscription `onPermissionChange(name, cb)` using `PermissionStatus.onchange`; returns unsubscribe function. ### 6. Async operation queue Concurrent `copy`/`read` calls are serialized via an internal queue to prevent "Document is not focused" races. ### 7. Focus-aware retry When `Document is not focused` error fires, waits for the next focus event and retries once. ### 8. Error codes `CLIPBOARD_API_UNAVAILABLE`, `CLIPBOARD_PERMISSION_DENIED`, `CLIPBOARD_FALLBACK_FAILED`, `CLIPBOARD_INSECURE_CONTEXT`. ### 9. OTel metrics Counter `clipboard.copy_total{method}`, counter `clipboard.read_total{method}`, counter `clipboard.fallback_used_total`, counter `clipboard.error_total{code}`, histogram `clipboard.op_latency_ms`. --- ## File layout ```text browser/platform/clipboard/ package.json tsconfig.json vitest.config.ts src/ index.ts error-codes.ts clipboard/ clipboard/ clipboard.ts (ClipboardManager, ≤250 lines) clipboard.test.ts clipboard.md clipboard.types.ts index.ts queue/ queue.ts (async operation serializer) queue.test.ts queue.md index.ts fallback/ fallback.ts (execCommand + paste-event fallback) fallback.test.ts fallback.md index.ts telemetry/ metrics/ metrics.ts metrics.md index.ts types/ types/ types.ts types.md index.ts schema/ clipboard-schema/ clipboard-schema.ts clipboard-schema.md index.ts constants/ index.ts tests/ integration.test.ts e2e/ clipboard.spec.ts (Chromium/WebKit/Firefox: text, image, permissions) ``` --- ## Impact **Downstream**: any Suaveplan browser app using clipboard operations. **Breaking changes**: None — all additions are additive. Error types changed from bare strings to `SuaveplanError` (structured), but result shape is unchanged. **Prerequisites**: `@suaveplan/error`, `@suaveplan/logger` on Verdaccio.