📒
WindowsでコピーしたMarkdownテキストをHTMLに変換するスクリプト
PowerShellスクリプトです。
すでにコピーしているピュアなMarkdown形式のテキストをリッチなHTMLに変換できます。
これがあると、LLMに生成させたMarkdownテキストを快適に&適切に各種エディターにペーストできます。
WordPress, Ghost, Google Docs, Microsoft Wordのようなエディタを使っていて、LLMに色々書かせたい人には重宝するかと思います。LLMといってもAPIでどこか自分のDBに保存していたりしないと必要にはならないかもですが。
意外とClaudeが一発で書けなかったので何度か修正して作成しました。
# ==============================================================================
# Markdown to Rich Text Clipboard Converter
# クリップボード内のMarkdownをHTMLに変換し、リッチテキスト形式で上書きします
# ==============================================================================
# システムアセンブリの読み込み(フォーム処理用)
Add-Type -AssemblyName System.Windows.Forms
# ==============================================================================
# Win32 API定義(クリップボード操作用)
# ==============================================================================
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
public class ClipboardHelper {
// クリップボードのオープン・クローズ・初期化
[DllImport("user32.dll", SetLastError = true)]
public static extern bool OpenClipboard(IntPtr hWndNewOwner);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool CloseClipboard();
[DllImport("user32.dll", SetLastError = true)]
public static extern bool EmptyClipboard();
// クリップボードへのデータ設定
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetClipboardData(uint uFormat, IntPtr hMem);
// クリップボード形式のID取得(カスタムフォーマット用)
[DllImport("user32.dll", SetLastError = true)]
public static extern uint RegisterClipboardFormat(string lpszFormat);
// メモリ割り当て・ロック・アンロック
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GlobalAlloc(uint uFlags, UIntPtr dwBytes);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GlobalLock(IntPtr hMem);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool GlobalUnlock(IntPtr hMem);
// メモリフラグと形式定数
public const uint GMEM_MOVEABLE = 0x0002; // 移動可能なメモリ
public const uint CF_UNICODETEXT = 13; // Unicode形式のテキスト
}
"@
# ==============================================================================
# 入力値の取得と検証
# ==============================================================================
# クリップボードからMarkdownを取得
$markdown = Get-Clipboard -Raw
# クリップボードが空の場合は処理を中止
if (-not $markdown) {
Write-Host "Clipboard is empty" -ForegroundColor Red
exit
}
# ==============================================================================
# Markdown → HTML変換関数
# ==============================================================================
function Convert-MarkdownToHtml {
param([string]$md)
$html = $md
# 見出しの変換(h6~h1、最大から最小の順)
$html = $html -replace '(?m)^###### (.+)$', '<h6>$1</h6>'
$html = $html -replace '(?m)^##### (.+)$', '<h5>$1</h5>'
$html = $html -replace '(?m)^#### (.+)$', '<h4>$1</h4>'
$html = $html -replace '(?m)^### (.+)$', '<h3>$1</h3>'
$html = $html -replace '(?m)^## (.+)$', '<h2>$1</h2>'
$html = $html -replace '(?m)^# (.+)$', '<h1>$1</h1>'
# テキスト装飾の変換(***太字イタリック → ***イタリック太字)
$html = $html -replace '\*\*\*(.+?)\*\*\*', '<strong><em>$1</em></strong>'
$html = $html -replace '\*\*(.+?)\*\*', '<strong>$1</strong>'
$html = $html -replace '\*(.+?)\*', '<em>$1</em>'
# インラインコードの変換
$html = $html -replace '`([^`]+)`', '<code>$1</code>'
# リンクの変換
$html = $html -replace '\[([^\]]+)\]\(([^\)]+)\)', '<a href="$2">$1</a>'
# リストアイテムの変換(箇条書き・番号付きリスト両方)
$html = $html -replace '(?m)^- (.+)$', '<li>$1</li>'
$html = $html -replace '(?m)^\d+\. (.+)$', '<li>$1</li>'
# 水平線の変換
$html = $html -replace '(?m)^---+$', '<hr>'
# 通常のテキスト行を段落タグで囲む(既にタグがある行は除外)
$html = $html -replace '(?m)^(?!<[hlu]|<li|<hr)(.+)$', '<p>$1</p>'
# 連続したli要素をul要素で囲む
$html = $html -replace '((?:<li>.*</li>\s*)+)', '<ul>$1</ul>'
# 空の段落タグを削除
$html = $html -replace '<p>\s*</p>', ''
return $html
}
# 変換実行
$html = Convert-MarkdownToHtml $markdown
# ==============================================================================
# CF_HTML形式の作成関数
# ==============================================================================
# Windows上でリッチテキストとして認識されるHTML形式を生成します
# 参考: https://learn.microsoft.com/en-us/windows/win32/dataxchg/html-clipboard-format
function Create-CFHtml {
param([string]$body)
# HTMLテンプレートの作成
$pre = '<!DOCTYPE html><html><head><meta charset="utf-8"></head><body><!--StartFragment-->'
$post = '<!--EndFragment--></body></html>'
$htmlContent = $pre + $body + $post
# CF_HTML形式のヘッダテンプレート(バイト位置情報を含む)
$hdrFmt = "Version:0.9`r`nStartHTML:{0:D10}`r`nEndHTML:{1:D10}`r`nStartFragment:{2:D10}`r`nEndFragment:{3:D10}`r`n"
# 初期値でヘッダを作成
$hdr = [string]::Format($hdrFmt, 0, 0, 0, 0)
$hdrLen = [System.Text.Encoding]::UTF8.GetByteCount($hdr)
# HTMLコンテンツをバイト配列に変換して長さを計算
$contentBytes = [System.Text.Encoding]::UTF8.GetBytes($htmlContent)
# バイト位置を計算
$startHtml = $hdrLen
$endHtml = $hdrLen + $contentBytes.Length
# フラグメント位置を計算(<!--StartFragment-->と<!--EndFragment-->の位置)
$sfm = '<!--StartFragment-->'
$efm = '<!--EndFragment-->'
$startFragment = $hdrLen + [System.Text.Encoding]::UTF8.GetByteCount($htmlContent.Substring(0, $htmlContent.IndexOf($sfm) + $sfm.Length))
$endFragment = $hdrLen + [System.Text.Encoding]::UTF8.GetByteCount($htmlContent.Substring(0, $htmlContent.IndexOf($efm)))
# 正確なバイト位置を含むヘッダを再作成
$header = [string]::Format($hdrFmt, $startHtml, $endHtml, $startFragment, $endFragment)
return $header + $htmlContent
}
# CF_HTML形式のHTMLを生成
$cfHtml = Create-CFHtml -body $html
# ==============================================================================
# クリップボードへのデータ設定準備
# ==============================================================================
# HTMLデータをUTF-8バイト配列に変換(BOMなし)
$utf8 = New-Object System.Text.UTF8Encoding($false)
$htmlBytes = $utf8.GetBytes($cfHtml)
# 元のMarkdownをUnicodeバイト配列に変換(null終端)
$unicodeBytes = [System.Text.Encoding]::Unicode.GetBytes($markdown + "`0")
# カスタムクリップボード形式「HTML Format」のIDを取得
$cfHtmlFormat = [ClipboardHelper]::RegisterClipboardFormat("HTML Format")
# ==============================================================================
# Win32 APIでクリップボードを操作
# ==============================================================================
if ([ClipboardHelper]::OpenClipboard([IntPtr]::Zero)) {
try {
# クリップボードの内容をクリア
[ClipboardHelper]::EmptyClipboard() | Out-Null
# ========== HTML形式でデータをセット ==========
# メモリを割り当て
$hMemHtml = [ClipboardHelper]::GlobalAlloc([ClipboardHelper]::GMEM_MOVEABLE, [UIntPtr]::new($htmlBytes.Length + 1))
# メモリをロック(ポインタを取得)
$ptrHtml = [ClipboardHelper]::GlobalLock($hMemHtml)
# HTMLデータをメモリにコピー
[System.Runtime.InteropServices.Marshal]::Copy($htmlBytes, 0, $ptrHtml, $htmlBytes.Length)
# null終端文字を追加
[System.Runtime.InteropServices.Marshal]::WriteByte($ptrHtml, $htmlBytes.Length, 0)
# メモリをアンロック
[ClipboardHelper]::GlobalUnlock($hMemHtml) | Out-Null
# クリップボードにデータをセット
[ClipboardHelper]::SetClipboardData($cfHtmlFormat, $hMemHtml) | Out-Null
# ========== Unicode テキスト形式でもデータをセット ==========
# (互換性のため、HTML形式だけでなくプレーンテキストも登録)
$hMemText = [ClipboardHelper]::GlobalAlloc([ClipboardHelper]::GMEM_MOVEABLE, [UIntPtr]::new($unicodeBytes.Length))
$ptrText = [ClipboardHelper]::GlobalLock($hMemText)
[System.Runtime.InteropServices.Marshal]::Copy($unicodeBytes, 0, $ptrText, $unicodeBytes.Length)
[ClipboardHelper]::GlobalUnlock($hMemText) | Out-Null
[ClipboardHelper]::SetClipboardData([ClipboardHelper]::CF_UNICODETEXT, $hMemText) | Out-Null
}
finally {
# クリップボードをクローズ
[ClipboardHelper]::CloseClipboard() | Out-Null
}
}
# ==============================================================================
# 完了メッセージ
# ==============================================================================
Write-Host "Done! Press Ctrl+V to paste" -ForegroundColor Green
Discussion