ClipboardAPI
Read from and write to the system clipboard. Access via context.clipboard.
Overview
The ClipboardAPI provides simple access to the system clipboard for copying and pasting text. Plugins can:
- Copy text to clipboard
- Read text from clipboard
- Integrate clipboard operations into workflows
Common Use Cases:
- Copy generated content
- Share plugin outputs
- Import clipboard content
- Clipboard history features
Methods
writeText(text)
Write text to the clipboard.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
| text | string | Yes | Text to copy to clipboard |
Returns: Promise that resolves when copied.
Example:
// Copy simple text
await context.clipboard.writeText('Hello, World!');
context.ui.showInformationMessage('Copied to clipboard');
// Copy JSON
const data = { name: 'John', age: 30 };
await context.clipboard.writeText(JSON.stringify(data, null, 2));
// Copy with user feedback
try {
await context.clipboard.writeText(generatedCode);
context.ui.showInformationMessage('Code copied!');
} catch (error) {
context.ui.showErrorMessage('Copy failed');
}read()
Read content from the clipboard.
Returns: Promise resolving to ClipboardItems array.
ClipboardItem Interface:
interface ClipboardItem {
types: string[]; // MIME types available
getType(type: string): Promise<Blob>;
}Example:
// Read clipboard content
const items = await context.clipboard.read();
if (items.length === 0) {
context.ui.showWarningMessage('Clipboard is empty');
return;
}
// Get text from first item
const item = items[0];
if (item.types.includes('text/plain')) {
const blob = await item.getType('text/plain');
const text = await blob.text();
context.logger.info('Clipboard text:', text);
}
// Check for other types
if (item.types.includes('text/html')) {
const blob = await item.getType('text/html');
const html = await blob.text();
context.logger.info('Clipboard HTML:', html);
}Complete Example
export default class ClipboardPlugin {
private context: PluginContext;
private history: string[];
constructor(context: PluginContext) {
this.context = context;
this.history = [];
}
async activate(): Promise<void> {
// Load clipboard history
this.history = await context.storage.get('clipboard_history') ?? [];
// Register commands
context.commands.register([
{
id: 'myPlugin.copyFormatted',
name: 'Copy as Formatted Text',
execute: () => this.copyFormatted()
},
{
id: 'myPlugin.pasteFromClipboard',
name: 'Paste from Clipboard',
execute: () => this.pasteFromClipboard()
},
{
id: 'myPlugin.showHistory',
name: 'Show Clipboard History',
execute: () => this.showHistory()
},
{
id: 'myPlugin.clearHistory',
name: 'Clear Clipboard History',
execute: () => this.clearHistory()
}
]);
}
async copyFormatted(): Promise<void> {
// Get current editor selection
const selection = await context.editor.getSelection();
if (!selection || !selection.text) {
context.ui.showWarningMessage('No text selected');
return;
}
// Format text
const formatted = this.formatText(selection.text);
// Copy to clipboard
await context.clipboard.writeText(formatted);
// Save to history
this.history.unshift(formatted);
if (this.history.length > 10) {
this.history = this.history.slice(0, 10);
}
await context.storage.set('clipboard_history', this.history);
context.ui.showInformationMessage('Copied formatted text');
}
formatText(text: string): string {
// Example formatting: add header and footer
return `=== Formatted Text ===\n\n${text}\n\n=== End ===`;
}
async pasteFromClipboard(): Promise<void> {
try {
const items = await context.clipboard.read();
if (items.length === 0) {
context.ui.showWarningMessage('Clipboard is empty');
return;
}
const item = items[0];
if (item.types.includes('text/plain')) {
const blob = await item.getType('text/plain');
const text = await blob.text();
// Insert into editor
await context.editor.insertNode('text', {}, text);
context.ui.showInformationMessage('Pasted from clipboard');
}
} catch (error) {
context.ui.showErrorMessage(`Paste failed: $\\{error.message\\}`);
}
}
async showHistory(): Promise<void> {
if (this.history.length === 0) {
context.ui.showWarningMessage('No clipboard history');
return;
}
const items = this.history.map((text, index) => ({
label: text.substring(0, 50) + (text.length > 50 ? '...' : ''),
description: `${text.length} chars`,
index
}));
const selected = await context.ui.showQuickPick(items, {
title: 'Clipboard History'
});
if (selected) {
const text = this.history[selected.index];
await context.clipboard.writeText(text);
context.ui.showInformationMessage('Copied to clipboard');
}
}
async clearHistory(): Promise<void> {
const confirmed = await context.ui.showConfirm({
title: 'Clear History',
message: 'Clear clipboard history?'
});
if (confirmed) {
this.history = [];
await context.storage.set('clipboard_history', []);
context.ui.showInformationMessage('History cleared');
}
}
async deactivate(): Promise<void> {
// Save history
await context.storage.set('clipboard_history', this.history);
}
}Advanced Example: Clipboard Transformer
export default class ClipboardTransformerPlugin {
private context: PluginContext;
constructor(context: PluginContext) {
this.context = context;
}
async activate(): Promise<void> {
context.commands.register([
{
id: 'myPlugin.transformClipboard',
name: 'Transform Clipboard',
execute: () => this.transformClipboard()
},
{
id: 'myPlugin.copyAsMarkdown',
name: 'Copy as Markdown',
execute: () => this.copyAsMarkdown()
},
{
id: 'myPlugin.copyAsJSON',
name: 'Copy as JSON',
execute: () => this.copyAsJSON()
}
]);
}
async transformClipboard(): Promise<void> {
// Read clipboard
const items = await context.clipboard.read();
if (items.length === 0) return;
const item = items[0];
if (!item.types.includes('text/plain')) {
context.ui.showWarningMessage('No text in clipboard');
return;
}
const blob = await item.getType('text/plain');
const text = await blob.text();
// Show transformation options
const transformations = [
{ label: 'UPPERCASE', transform: (t: string) => t.toUpperCase() },
{ label: 'lowercase', transform: (t: string) => t.toLowerCase() },
{ label: 'Title Case', transform: (t: string) => this.toTitleCase(t) },
{ label: 'Reverse', transform: (t: string) => t.split('').reverse().join('') },
{ label: 'Base64 Encode', transform: (t: string) => btoa(t) },
{ label: 'Base64 Decode', transform: (t: string) => atob(t) }
];
const selected = await context.ui.showQuickPick(transformations, {
title: 'Select Transformation'
});
if (selected) {
try {
const transformed = selected.transform(text);
await context.clipboard.writeText(transformed);
context.ui.showInformationMessage('Clipboard transformed');
} catch (error) {
context.ui.showErrorMessage(`Transform failed: $\\{error.message\\}`);
}
}
}
toTitleCase(text: string): string {
return text.replace(/\w\S*/g, (txt) =>
txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()
);
}
async copyAsMarkdown(): Promise<void> {
const selection = await context.editor.getSelection();
if (!selection?.text) {
context.ui.showWarningMessage('No text selected');
return;
}
// Convert to markdown code block
const markdown = `\`\`\`\n${selection.text}\n\`\`\``;
await context.clipboard.writeText(markdown);
context.ui.showInformationMessage('Copied as Markdown');
}
async copyAsJSON(): Promise<void> {
const selection = await context.editor.getSelection();
if (!selection?.text) {
context.ui.showWarningMessage('No text selected');
return;
}
try {
// Try to parse and format as JSON
const obj = JSON.parse(selection.text);
const formatted = JSON.stringify(obj, null, 2);
await context.clipboard.writeText(formatted);
context.ui.showInformationMessage('Copied formatted JSON');
} catch (error) {
context.ui.showErrorMessage('Invalid JSON');
}
}
async deactivate(): Promise<void> {
// Cleanup
}
}Best Practices
-
Provide User Feedback: Show success/error messages
await context.clipboard.writeText(text); context.ui.showInformationMessage('Copied!'); -
Handle Errors: Wrap in try-catch
try { await context.clipboard.writeText(text); } catch (error) { context.ui.showErrorMessage('Copy failed'); } -
Check Content Before Reading: Verify clipboard has content
const items = await context.clipboard.read(); if (items.length === 0) { return; // Empty clipboard } -
Store History: Save clipboard history for later use
const history = await context.storage.get('clipboard') ?? []; history.unshift(text); await context.storage.set('clipboard', history.slice(0, 10)); -
Validate Content: Check content type before processing
if (item.types.includes('text/plain')) { const blob = await item.getType('text/plain'); const text = await blob.text(); }
Security Notes
- User Permission: Browser may require user interaction for clipboard access
- Sensitive Data: Be careful copying sensitive information
- Privacy: Don’t log clipboard contents
- User Awareness: Always notify users when copying to clipboard
// ✓ Good - notify user
await context.clipboard.writeText(data);
context.ui.showInformationMessage('Data copied to clipboard');
// ✗ Bad - silent copy without notification
await context.clipboard.writeText(sensitiveData); // No user notificationBrowser Compatibility
The Clipboard API requires:
- HTTPS (except localhost)
- User interaction for read operations
- Permissions for accessing clipboard
// Check if clipboard API is available
if (!navigator.clipboard) {
context.ui.showErrorMessage('Clipboard API not available');
return;
}
// Clipboard read may require user permission
try {
const items = await context.clipboard.read();
} catch (error) {
if (error.name === 'NotAllowedError') {
context.ui.showErrorMessage('Clipboard access denied');
}
}Limitations
- Text Only (writeText): Only plain text supported for writing
- Read Requires Interaction: Reading may require recent user interaction
- Size Limits: Large content may fail to copy
- Format Support: Limited MIME types supported
See Also
- EditorAPI Reference - Editor content
- UIAPI Reference - User notifications
- StorageAPI Reference - Store clipboard history