diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..7f391a2
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,73 @@
+# Contributing to playwright-clipboard
+
+We love your input! We want to make contributing to playwright-clipboard as easy and transparent as possible, whether it's:
+
+- Reporting a bug
+- Discussing the current state of the code
+- Submitting a fix
+- Proposing new features
+- Becoming a maintainer
+
+## We Develop with GitHub
+We use GitHub to host code, to track issues and feature requests, as well as accept pull requests.
+
+## We Use [Github Flow](https://guides.github.com/introduction/flow/index.html)
+Pull requests are the best way to propose changes to the codebase. We actively welcome your pull requests:
+
+1. Fork the repo and create your branch from `main`.
+2. If you've added code that should be tested, add tests.
+3. If you've changed APIs, update the documentation.
+4. Ensure the test suite passes.
+5. Make sure your code lints.
+6. Issue that pull request!
+
+## Any contributions you make will be under the MIT Software License
+In short, when you submit code changes, your submissions are understood to be under the same [MIT License](http://choosealicense.com/licenses/mit/) that covers the project. Feel free to contact the maintainers if that's a concern.
+
+## Report bugs using GitHub's [issue tracker](https://github.com/vrknetha/playwright-clipboard/issues)
+We use GitHub issues to track public bugs. Report a bug by [opening a new issue](https://github.com/vrknetha/playwright-clipboard/issues/new); it's that easy!
+
+## Write bug reports with detail, background, and sample code
+
+**Great Bug Reports** tend to have:
+
+- A quick summary and/or background
+- Steps to reproduce
+ - Be specific!
+ - Give sample code if you can.
+- What you expected would happen
+- What actually happens
+- Notes (possibly including why you think this might be happening, or stuff you tried that didn't work)
+
+## Development Process
+
+1. Clone the repository:
+```bash
+git clone https://github.com/vrknetha/playwright-clipboard.git
+cd playwright-clipboard
+```
+
+2. Install dependencies:
+```bash
+npm install
+```
+
+3. Run tests:
+```bash
+npm test
+```
+
+4. Run linting:
+```bash
+npm run lint
+```
+
+## Testing
+We use Playwright Test for our test suite. Please ensure all tests pass before submitting a PR:
+
+```bash
+npm test
+```
+
+## License
+By contributing, you agree that your contributions will be licensed under its MIT License.
\ No newline at end of file
diff --git a/README.md b/README.md
index a84da24..c2e9c6a 100644
--- a/README.md
+++ b/README.md
@@ -11,80 +11,166 @@ npm install --save-dev playwright-clipboard
## Features
- Basic clipboard operations (copy, paste, cut)
-- Text selection operations
-- Word-level operations
-- Clipboard content management
-- Cross-browser compatibility
-- TypeScript support
+- Rich text operations with HTML preservation
+- Text selection operations with character and word-level control
+- Word-level operations for precise text manipulation
+- Clipboard content management with direct access
+- Cross-browser compatibility (Chromium, Firefox, WebKit)
+- TypeScript support with full type definitions
+- Comprehensive error handling
+- Fallback mechanisms for browser-specific limitations
-## Usage
+## Examples
### Basic Operations
```typescript
-import { test } from '@playwright/test';
+import { test, expect } from '@playwright/test';
import { PlaywrightClipboard } from 'playwright-clipboard';
-test('basic clipboard operations', async ({ page }) => {
+test('basic copy/paste operations', async ({ page }) => {
const clipboard = new PlaywrightClipboard(page);
-
- // Copy text from source
+
+ await page.goto('http://localhost:8080');
+
+ // Copy from source input and paste to target
await clipboard.copy('#source');
+ await clipboard.paste('#target');
+
+ const result = await page.inputValue('#target');
+ expect(result).toBe('Hello World');
+});
+
+test('cut operations', async ({ page }) => {
+ const clipboard = new PlaywrightClipboard(page);
+ const initialText = 'Test Content';
- // Paste text to target
+ await page.fill('#source', initialText);
+ await clipboard.cut('#source');
await clipboard.paste('#target');
- // Cut text
- await clipboard.cut('#editor');
+ // Source should be empty after cut
+ const sourceContent = await page.inputValue('#source');
+ expect(sourceContent).toBe('');
- // Get clipboard content
- const content = await clipboard.getClipboardContent();
+ // Target should have the cut content
+ const targetContent = await page.inputValue('#target');
+ expect(targetContent).toBe(initialText);
+});
+```
+
+### Rich Text Operations
- // Set clipboard content
- await clipboard.setClipboardContent('Hello World');
+```typescript
+test('rich text operations', async ({ page, browserName }) => {
+ const clipboard = new PlaywrightClipboard(page);
+
+ // Copy rich text content
+ await clipboard.copyRichText('#richSource');
+ await clipboard.pasteRichText('#richTarget');
+
+ const result = await page.$eval('#richTarget', el => el.innerHTML.trim());
+
+ if (browserName === 'webkit') {
+ // WebKit may handle rich text differently
+ const plainText = await page.$eval('#richTarget',
+ el => el.textContent?.trim() || '');
+ expect(plainText).toBe('This is bold text');
+ } else {
+ // Other browsers preserve HTML structure
+ expect(result).toContain('bold');
+ }
});
```
-### Word-Level Operations
+### Text Selection
```typescript
-test('word-level operations', async ({ page }) => {
+test('text selection', async ({ page }) => {
const clipboard = new PlaywrightClipboard(page);
+ const testText = 'Select this text';
- // Copy specific words
- await clipboard.copyBetweenWords('#editor', 1, 3);
+ await page.fill('#text', testText);
+ await clipboard.select('#text', 7, 11); // Selects "this"
+
+ const selectedText = await clipboard.getSelectedText();
+ expect(selectedText).toBe('this');
+});
+```
+
+### Word Operations
- // Paste after a specific word
- await clipboard.pasteAfterWord('#editor', 2);
+```typescript
+test('word operations', async ({ page }) => {
+ const clipboard = new PlaywrightClipboard(page);
+ const testText = 'The quick brown fox jumps';
- // Paste before a specific word
- await clipboard.pasteBeforeWord('#editor', 0);
+ await page.fill('#editor', testText);
+ await clipboard.copyBetweenWords('#editor', 2, 3); // Copy "brown fox"
+ await clipboard.paste('#target');
- // Replace a word
- await clipboard.replaceWord('#editor', 1);
+ const targetContent = await page.inputValue('#target');
+ expect(targetContent).toBe('brown fox');
});
```
-### Selection Operations
+### Special Characters and Multiline Text
```typescript
-test('selection operations', async ({ page }) => {
+test('special characters', async ({ page }) => {
const clipboard = new PlaywrightClipboard(page);
+ const testText = 'Special @#$% characters!';
- // Select all text
- await clipboard.selectAll('#editor');
+ await page.fill('#source', testText);
+ await clipboard.copy('#source');
+ await clipboard.paste('#target');
+
+ const result = await page.inputValue('#target');
+ expect(result).toBe(testText);
+});
- // Select specific range
- await clipboard.select('#editor', 0, 10);
+test('multiline text', async ({ page }) => {
+ const clipboard = new PlaywrightClipboard(page);
+ const testText = 'Line 1\nLine 2\nLine 3';
- // Select word range
- await clipboard.selectWordRange('#editor', 1, 3);
+ await page.fill('#editor', testText);
+ await clipboard.copy('#editor');
+ await clipboard.paste('#target');
- // Get selected text
- const selectedText = await clipboard.getSelectedWords();
+ const result = await page.inputValue('#target');
+ expect(result).toBe(testText);
});
```
+## Browser Support
+
+| Feature | Chromium | Firefox | WebKit (Safari) |
+|---------|----------|---------|-----------------|
+| Basic Operations | Native Clipboard API | Keyboard Shortcuts | Clipboard API + Fallback |
+| Rich Text | Full Support | Full Support | Full Support* |
+| Word Operations | Full Support | Full Support | Full Support |
+
+\* Uses `execCommand` fallback for some operations
+
+## Technical Details
+
+The package implements several fallback mechanisms to ensure consistent behavior across browsers:
+
+1. **Clipboard Access**:
+ - Primary: Native Clipboard API
+ - Fallback: Keyboard shortcuts (Meta+C, Meta+V, Meta+X)
+ - Last Resort: execCommand for WebKit
+
+2. **Text Selection**:
+ - Input/Textarea: Uses `setSelectionRange`
+ - Rich Text: Uses `Range` and `Selection` APIs
+ - Word-Level: Custom boundary detection
+
+3. **Rich Text Handling**:
+ - Preserves HTML structure where supported
+ - Graceful degradation to plain text
+ - Browser-specific optimizations
+
## API Reference
### Constructor
@@ -93,41 +179,69 @@ test('selection operations', async ({ page }) => {
constructor(page: Page, options?: ClipboardOptions)
```
+Options:
+- `timeout?: number` - Operation timeout in milliseconds (default: 5000)
+
### Methods
-- `copy(selector: string): Promise`
-- `paste(selector: string): Promise`
-- `cut(selector: string): Promise`
-- `getClipboardContent(): Promise`
-- `setClipboardContent(text: string): Promise`
-- `selectAll(selector: string): Promise`
-- `select(selector: string, start: number, end: number): Promise`
+#### Basic Operations
+- `copy(selector: string): Promise` - Copy content from element
+- `paste(selector: string): Promise` - Paste content to element
+- `cut(selector: string): Promise` - Cut content from element
+
+#### Rich Text Operations
+- `copyRichText(selector: string): Promise` - Copy with HTML preservation
+- `pasteRichText(selector: string): Promise` - Paste with HTML preservation
+- `cutRichText(selector: string): Promise` - Cut with HTML preservation
+
+#### Selection Operations
+- `selectAll(selector: string): Promise` - Select all content
+- `select(selector: string, start: number, end: number): Promise` - Select range
+- `getSelectedText(): Promise` - Get selected text
+
+#### Word Operations
- `copyBetweenWords(selector: string, startWordIndex: number, endWordIndex: number): Promise`
- `pasteAfterWord(selector: string, wordIndex: number): Promise`
- `pasteBeforeWord(selector: string, wordIndex: number): Promise`
- `replaceWord(selector: string, wordIndex: number): Promise`
- `selectWordRange(selector: string, startIndex: number, endIndex: number): Promise`
- `getSelectedWords(): Promise`
-- `getWordBoundaries(selector: string, wordIndex: number): Promise<{ start: number; end: number }>`
+
+#### Clipboard Management
+- `getClipboardContent(): Promise` - Get current clipboard content
+- `setClipboardContent(text: string): Promise` - Set clipboard content
## Error Handling
The package includes comprehensive error handling with specific error types:
-- `COPY_ERROR`
-- `PASTE_ERROR`
-- `CLIPBOARD_ACCESS_DENIED`
-- `SELECTION_FAILED`
-- `INVALID_WORD_INDEX`
-- `WORD_BOUNDARY_ERROR`
-- `EMPTY_SELECTION`
-- `PASTE_POSITION_ERROR`
+```typescript
+export enum ClipboardError {
+ COPY_ERROR = 'Copy operation failed',
+ PASTE_ERROR = 'Paste operation failed',
+ CLIPBOARD_ACCESS_DENIED = 'Cannot access clipboard',
+ SELECTION_FAILED = 'Text selection failed',
+ INVALID_WORD_INDEX = 'Invalid word index specified',
+ WORD_BOUNDARY_ERROR = 'Cannot determine word boundaries',
+ EMPTY_SELECTION = 'No text selected',
+ PASTE_POSITION_ERROR = 'Invalid paste position'
+}
+```
## Requirements
- Node.js >= 18.0.0
- Playwright >= 1.40.0
+- TypeScript >= 4.5.0 (for TypeScript users)
+
+## Contributing
+
+Contributions are welcome! Please read our [Contributing Guide](./CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.
## License
-MIT
\ No newline at end of file
+MIT
+
+## Support
+
+For bugs and feature requests, please [open an issue](https://github.com/vrknetha/playwright-clipboard/issues).
\ No newline at end of file