← Back to tools
Utility

TypePaste

Paste formatted text as plain keystrokes. Strips rich formatting from Claude, ChatGPT, Notion, and pastes clean text into Slack, Teams, Discord — or converts Markdown to each app's native format.

Implementation

// TypePaste - Clipboard → Keystrokes
// Solves: pasting bold/italic/bullets from AI tools into Slack breaks formatting

interface TypePasteConfig {
  mode: 'plain' | 'slack' | 'discord' | 'teams' | 'markdown';
  delay: number;        // ms between keystrokes (default: 5)
  preserveNewlines: boolean;
}

interface FormatRule {
  pattern: RegExp;
  replace: string;
}

// Format conversion rules per platform
const formatRules: Record<string, FormatRule[]> = {
  plain: [
    { pattern: /\*\*(.+?)\*\*/g, replace: '$1' },
    { pattern: /\*(.+?)\*/g, replace: '$1' },
    { pattern: /_(.+?)_/g, replace: '$1' },
    { pattern: /~~(.+?)~~/g, replace: '$1' },
    { pattern: /`(.+?)`/g, replace: '$1' },
    { pattern: /^#{1,6}\s+/gm, replace: '' },
    { pattern: /^[\-\*]\s+/gm, replace: '• ' },
    { pattern: /^\d+\.\s+/gm, replace: '' },
    { pattern: /\[(.+?)\]\((.+?)\)/g, replace: '$1 ($2)' },
  ],
  slack: [
    { pattern: /\*\*(.+?)\*\*/g, replace: '*$1*' },        // bold
    { pattern: /(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, replace: '_$1_' }, // italic
    { pattern: /~~(.+?)~~/g, replace: '~$1~' },            // strike
    { pattern: /```(\w+)?\n([\s\S]+?)```/g, replace: '```$2```' },
    { pattern: /^#{1,6}\s+(.+)/gm, replace: '*$1*' },     // headings → bold
    { pattern: /^[\-\*]\s+/gm, replace: '• ' },
    { pattern: /\[(.+?)\]\((.+?)\)/g, replace: '<$2|$1>' }, // links
  ],
  discord: [
    // Discord uses same markdown, mostly passthrough
    { pattern: /^[\-\*]\s+/gm, replace: '• ' },
    { pattern: /\[(.+?)\]\((.+?)\)/g, replace: '$1 (<$2>)' },
  ],
};

function convertClipboard(text: string, mode: string): string {
  const rules = formatRules[mode] || formatRules.plain;
  let result = text;
  for (const rule of rules) {
    result = result.replace(rule.pattern, rule.replace);
  }
  // Strip any remaining HTML tags
  result = result.replace(/<[^>]+>/g, '');
  // Normalize whitespace
  result = result.replace(/\r\n/g, '\n').replace(/[ \t]+$/gm, '');
  return result;
}

// Simulate typing via keyboard events (Electron/native context)
async function typeText(text: string, delayMs = 5): Promise<void> {
  for (const char of text) {
    // In Electron: use robotjs or @nut-tree/nut-js
    // In browser extension: use document.execCommand or Input Events
    await simulateKeystroke(char);
    await sleep(delayMs);
  }
}

// System tray hotkey handler
function registerHotkey(combo: string, mode: string): void {
  // Ctrl+Shift+V → paste as plain
  // Ctrl+Shift+S → paste as Slack format
  // Ctrl+Shift+D → paste as Discord format
  globalShortcut.register(combo, async () => {
    const clipboard = await readClipboard();
    const converted = convertClipboard(clipboard, mode);
    await typeText(converted);
  });
}

// Usage
const slackReady = convertClipboard(
  '**Bold title**\n- Item one\n- Item two\n[Link](https://example.com)',
  'slack'
);
// => "*Bold title*\n• Item one\n• Item two\n<https://example.com|Link>"

Usage

Use convertClipboard(text, mode) to transform formatted text. Modes: "plain", "slack", "discord", "teams". As a desktop app, register Ctrl+Shift+V for instant type-paste.

Examples

Input: convertClipboard("**hello** _world_", "plain")
Output: "hello world"
Input: convertClipboard("**bold** and *italic*", "slack")
Output: "*bold* and _italic_"
Input: convertClipboard("[Link](https://x.com)", "slack")
Output: "<https://x.com|Link>"