@fmontes/md2clip 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +56 -0
  2. package/md2clip.js +127 -0
  3. package/package.json +32 -0
package/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # md2clip
2
+
3
+ Pipe markdown into your terminal, get HTML in your clipboard with proper `text/html` MIME type.
4
+
5
+ Apps like Notion, Slack, Mail, and Google Docs will paste it as rich text.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install -g md2clip
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```bash
16
+ echo "**bold** and _italic_" | md2clip
17
+ cat README.md | md2clip
18
+ pbpaste | md2clip # convert clipboard markdown to HTML
19
+ ```
20
+
21
+ Output is also printed to stdout, so you can pipe further:
22
+
23
+ ```bash
24
+ cat doc.md | md2clip | less
25
+ ```
26
+
27
+ ## Platform support
28
+
29
+ | Platform | Method | MIME type |
30
+ |----------|--------|-----------|
31
+ | macOS | Swift + NSPasteboard | `public.html` |
32
+ | Linux | xclip / xsel / wl-copy | `text/html` |
33
+ | Windows | PowerShell + System.Windows.Forms | HTML format |
34
+
35
+ ### macOS requirement
36
+
37
+ Requires Xcode Command Line Tools for proper HTML MIME type:
38
+
39
+ ```bash
40
+ xcode-select --install
41
+ ```
42
+
43
+ Without it, falls back to `pbcopy` (plain text only).
44
+
45
+ ### Linux requirement
46
+
47
+ Install one of: `xclip`, `xsel`, or `wl-copy`.
48
+
49
+ ```bash
50
+ # Ubuntu/Debian
51
+ sudo apt install xclip
52
+ ```
53
+
54
+ ## License
55
+
56
+ MIT
package/md2clip.js ADDED
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { marked } from "marked";
4
+ import { spawnSync } from "child_process";
5
+ import { writeFileSync, unlinkSync } from "fs";
6
+ import { tmpdir } from "os";
7
+ import { join } from "path";
8
+
9
+ function getStdin() {
10
+ if (process.stdin.isTTY) {
11
+ console.error("md2clip: reads markdown from stdin\n");
12
+ console.error("Usage:");
13
+ console.error(" echo '**hello**' | md2clip");
14
+ console.error(" cat README.md | md2clip");
15
+ process.exit(1);
16
+ }
17
+
18
+ return new Promise((resolve) => {
19
+ const chunks = [];
20
+ process.stdin.setEncoding("utf8");
21
+ process.stdin.on("data", (chunk) => chunks.push(chunk));
22
+ process.stdin.on("end", () => resolve(chunks.join("")));
23
+ });
24
+ }
25
+
26
+ function copyAsHTML(html) {
27
+ const platform = process.platform;
28
+
29
+ if (platform === "darwin") {
30
+ copyMacOS(html);
31
+ } else if (platform === "linux") {
32
+ copyLinux(html);
33
+ } else if (platform === "win32") {
34
+ copyWindows(html);
35
+ } else {
36
+ console.error(`md2clip: unsupported platform: ${platform}`);
37
+ process.exit(1);
38
+ }
39
+ }
40
+
41
+ function copyMacOS(html) {
42
+ // Swift sets public.html MIME type so apps receive rich HTML on paste
43
+ const swiftCode = `
44
+ import AppKit
45
+ let html = CommandLine.arguments[1]
46
+ let data = html.data(using: .utf8)!
47
+ let pb = NSPasteboard.general
48
+ pb.clearContents()
49
+ pb.setData(data, forType: NSPasteboard.PasteboardType("public.html"))
50
+ pb.setString(html, forType: .string)
51
+ `;
52
+ const swiftPath = join(tmpdir(), `_md2clip_${Date.now()}.swift`);
53
+ try {
54
+ writeFileSync(swiftPath, swiftCode);
55
+ const result = spawnSync("swift", [swiftPath, html], { timeout: 10000 });
56
+ if (result.status !== 0) {
57
+ throw new Error(result.stderr?.toString() || "swift failed");
58
+ }
59
+ } catch (err) {
60
+ if (err.code === "ENOENT") {
61
+ // swift not found, fall back to pbcopy (plain text)
62
+ console.error("Warning: swift not found, copying as plain text only.");
63
+ console.error("Fix: xcode-select --install");
64
+ spawnSync("pbcopy", [], { input: html, encoding: "utf8" });
65
+ } else {
66
+ throw err;
67
+ }
68
+ } finally {
69
+ try { unlinkSync(swiftPath); } catch {}
70
+ }
71
+ }
72
+
73
+ function copyLinux(html) {
74
+ // Try xclip then xsel then wl-copy (Wayland)
75
+ const tools = [
76
+ { cmd: "xclip", args: ["-selection", "clipboard", "-t", "text/html"] },
77
+ { cmd: "xsel", args: ["--clipboard", "--input"] },
78
+ { cmd: "wl-copy", args: ["--type", "text/html"] },
79
+ ];
80
+
81
+ for (const tool of tools) {
82
+ const result = spawnSync(tool.cmd, tool.args, {
83
+ input: html,
84
+ encoding: "utf8",
85
+ timeout: 5000,
86
+ });
87
+ if (result.status === 0) return;
88
+ }
89
+
90
+ console.error("md2clip: install xclip, xsel, or wl-copy for clipboard support");
91
+ process.exit(1);
92
+ }
93
+
94
+ function copyWindows(html) {
95
+ // PowerShell with HTML clipboard format
96
+ const ps = `
97
+ Add-Type -Assembly System.Windows.Forms
98
+ $html = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('${Buffer.from(html).toString("base64")}'))
99
+ [System.Windows.Forms.Clipboard]::SetText($html, [System.Windows.Forms.TextDataFormat]::Html)
100
+ `;
101
+ const result = spawnSync("powershell", ["-Command", ps], { encoding: "utf8", timeout: 10000 });
102
+ if (result.status !== 0) {
103
+ throw new Error(result.stderr?.toString() || "powershell failed");
104
+ }
105
+ }
106
+
107
+ async function main() {
108
+ const markdown = await getStdin();
109
+
110
+ if (!markdown.trim()) {
111
+ console.error("md2clip: empty input");
112
+ process.exit(1);
113
+ }
114
+
115
+ const html = marked(markdown);
116
+
117
+ copyAsHTML(html);
118
+
119
+ // Also print to stdout so you can pipe further
120
+ process.stdout.write(html);
121
+ console.error("✓ Copied to clipboard as HTML");
122
+ }
123
+
124
+ main().catch((err) => {
125
+ console.error("md2clip error:", err.message);
126
+ process.exit(1);
127
+ });
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@fmontes/md2clip",
3
+ "version": "1.0.0",
4
+ "description": "Pipe markdown, get HTML in your clipboard with proper HTML MIME type",
5
+ "type": "module",
6
+ "bin": {
7
+ "md2clip": "md2clip.js"
8
+ },
9
+ "files": [
10
+ "md2clip.js"
11
+ ],
12
+ "scripts": {
13
+ "test": "echo '**hello** _world_' | node md2clip.js"
14
+ },
15
+ "dependencies": {
16
+ "marked": "^12.0.0"
17
+ },
18
+ "engines": {
19
+ "node": ">=18"
20
+ },
21
+ "keywords": [
22
+ "markdown",
23
+ "clipboard",
24
+ "html",
25
+ "cli"
26
+ ],
27
+ "license": "MIT",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "git+https://github.com/fmontes/md2clip.git"
31
+ }
32
+ }