jsmdcui 0.3.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 (258) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/LICENSE +22 -0
  3. package/README.md +285 -0
  4. package/package.json +17 -0
  5. package/project_structure.txt +35 -0
  6. package/runmd.mjs +240 -0
  7. package/runtime/colorschemes/atom-dark.micro +33 -0
  8. package/runtime/colorschemes/bubblegum.micro +31 -0
  9. package/runtime/colorschemes/cmc-16.micro +47 -0
  10. package/runtime/colorschemes/cmc-tc.micro +43 -0
  11. package/runtime/colorschemes/darcula.micro +34 -0
  12. package/runtime/colorschemes/default.micro +1 -0
  13. package/runtime/colorschemes/dracula-tc.micro +49 -0
  14. package/runtime/colorschemes/dukedark-tc.micro +38 -0
  15. package/runtime/colorschemes/dukelight-tc.micro +38 -0
  16. package/runtime/colorschemes/dukeubuntu-tc.micro +38 -0
  17. package/runtime/colorschemes/geany.micro +29 -0
  18. package/runtime/colorschemes/gotham.micro +29 -0
  19. package/runtime/colorschemes/gruvbox-tc.micro +29 -0
  20. package/runtime/colorschemes/gruvbox.micro +26 -0
  21. package/runtime/colorschemes/material-tc.micro +36 -0
  22. package/runtime/colorschemes/monokai-dark.micro +28 -0
  23. package/runtime/colorschemes/monokai.micro +34 -0
  24. package/runtime/colorschemes/one-dark.micro +39 -0
  25. package/runtime/colorschemes/railscast.micro +37 -0
  26. package/runtime/colorschemes/simple.micro +33 -0
  27. package/runtime/colorschemes/solarized-tc.micro +31 -0
  28. package/runtime/colorschemes/solarized.micro +30 -0
  29. package/runtime/colorschemes/sunny-day.micro +29 -0
  30. package/runtime/colorschemes/twilight.micro +40 -0
  31. package/runtime/colorschemes/zenburn.micro +30 -0
  32. package/runtime/help/actions.md +161 -0
  33. package/runtime/help/cdp.md +119 -0
  34. package/runtime/help/colors.md +421 -0
  35. package/runtime/help/commands.md +161 -0
  36. package/runtime/help/copypaste.md +149 -0
  37. package/runtime/help/defaultkeys.md +148 -0
  38. package/runtime/help/help.md +285 -0
  39. package/runtime/help/keybindings.md +355 -0
  40. package/runtime/help/linter.md +90 -0
  41. package/runtime/help/options.md +709 -0
  42. package/runtime/help/plugins.md +544 -0
  43. package/runtime/help/tutorial.md +112 -0
  44. package/runtime/jsplugins/cdp/cdp-server.js +1161 -0
  45. package/runtime/jsplugins/cdp/cdp.js +192 -0
  46. package/runtime/jsplugins/chapter/chapter.js +108 -0
  47. package/runtime/jsplugins/diff/diff.js +46 -0
  48. package/runtime/jsplugins/example/example.js +110 -0
  49. package/runtime/jsplugins/linter/linter.js +281 -0
  50. package/runtime/plugins/autoclose/autoclose.lua +75 -0
  51. package/runtime/plugins/ftoptions/ftoptions.lua +17 -0
  52. package/runtime/plugins/literate/README.md +5 -0
  53. package/runtime/plugins/literate/literate.lua +55 -0
  54. package/runtime/plugins/status/help/status.md +21 -0
  55. package/runtime/plugins/status/status.lua +62 -0
  56. package/runtime/syntax/LICENSE +22 -0
  57. package/runtime/syntax/PowerShell.yaml +128 -0
  58. package/runtime/syntax/README.md +63 -0
  59. package/runtime/syntax/ada.yaml +43 -0
  60. package/runtime/syntax/apacheconf.yaml +59 -0
  61. package/runtime/syntax/arduino.yaml +101 -0
  62. package/runtime/syntax/asciidoc.yaml +51 -0
  63. package/runtime/syntax/asm.yaml +123 -0
  64. package/runtime/syntax/ats.yaml +99 -0
  65. package/runtime/syntax/awk.yaml +44 -0
  66. package/runtime/syntax/b.yaml +87 -0
  67. package/runtime/syntax/bat.yaml +57 -0
  68. package/runtime/syntax/c.yaml +60 -0
  69. package/runtime/syntax/caddyfile.yaml +23 -0
  70. package/runtime/syntax/cake.yaml +7 -0
  71. package/runtime/syntax/clojure.yaml +38 -0
  72. package/runtime/syntax/cmake.yaml +42 -0
  73. package/runtime/syntax/coffeescript.yaml +56 -0
  74. package/runtime/syntax/colortest.yaml +19 -0
  75. package/runtime/syntax/conky.yaml +17 -0
  76. package/runtime/syntax/cpp.yaml +91 -0
  77. package/runtime/syntax/crontab.yaml +36 -0
  78. package/runtime/syntax/crystal.yaml +72 -0
  79. package/runtime/syntax/csharp.yaml +52 -0
  80. package/runtime/syntax/css.yaml +44 -0
  81. package/runtime/syntax/csx.yaml +8 -0
  82. package/runtime/syntax/cuda.yaml +68 -0
  83. package/runtime/syntax/cython.yaml +52 -0
  84. package/runtime/syntax/d.yaml +121 -0
  85. package/runtime/syntax/dart.yaml +46 -0
  86. package/runtime/syntax/default.yaml +10 -0
  87. package/runtime/syntax/dockerfile.yaml +36 -0
  88. package/runtime/syntax/dot.yaml +29 -0
  89. package/runtime/syntax/elixir.yaml +30 -0
  90. package/runtime/syntax/elm.yaml +38 -0
  91. package/runtime/syntax/erb.yaml +42 -0
  92. package/runtime/syntax/erlang.yaml +45 -0
  93. package/runtime/syntax/fish.yaml +48 -0
  94. package/runtime/syntax/forth.yaml +34 -0
  95. package/runtime/syntax/fortran.yaml +64 -0
  96. package/runtime/syntax/freebsd-kernel.yaml +14 -0
  97. package/runtime/syntax/fsharp.yaml +48 -0
  98. package/runtime/syntax/gdscript.yaml +61 -0
  99. package/runtime/syntax/gemini.yaml +19 -0
  100. package/runtime/syntax/gentoo-ebuild.yaml +48 -0
  101. package/runtime/syntax/gentoo-etc-portage.yaml +23 -0
  102. package/runtime/syntax/git-commit.yaml +35 -0
  103. package/runtime/syntax/git-config.yaml +14 -0
  104. package/runtime/syntax/git-rebase-todo.yaml +19 -0
  105. package/runtime/syntax/gleam.yaml +69 -0
  106. package/runtime/syntax/glsl.yaml +26 -0
  107. package/runtime/syntax/gnuplot.yaml +15 -0
  108. package/runtime/syntax/go.yaml +62 -0
  109. package/runtime/syntax/godoc.yaml +17 -0
  110. package/runtime/syntax/golo.yaml +73 -0
  111. package/runtime/syntax/gomod.yaml +31 -0
  112. package/runtime/syntax/graphql.yaml +47 -0
  113. package/runtime/syntax/groff.yaml +30 -0
  114. package/runtime/syntax/groovy.yaml +111 -0
  115. package/runtime/syntax/haml.yaml +16 -0
  116. package/runtime/syntax/hare.yaml +52 -0
  117. package/runtime/syntax/haskell.yaml +52 -0
  118. package/runtime/syntax/hc.yaml +52 -0
  119. package/runtime/syntax/html.yaml +70 -0
  120. package/runtime/syntax/html4.yaml +25 -0
  121. package/runtime/syntax/html5.yaml +25 -0
  122. package/runtime/syntax/ini.yaml +23 -0
  123. package/runtime/syntax/inputrc.yaml +14 -0
  124. package/runtime/syntax/java.yaml +37 -0
  125. package/runtime/syntax/javascript.yaml +124 -0
  126. package/runtime/syntax/jinja2.yaml +19 -0
  127. package/runtime/syntax/json.yaml +39 -0
  128. package/runtime/syntax/jsonnet.yaml +92 -0
  129. package/runtime/syntax/julia.yaml +57 -0
  130. package/runtime/syntax/justfile.yaml +40 -0
  131. package/runtime/syntax/keymap.yaml +27 -0
  132. package/runtime/syntax/kickstart.yaml +16 -0
  133. package/runtime/syntax/kotlin.yaml +66 -0
  134. package/runtime/syntax/kvlang.yaml +67 -0
  135. package/runtime/syntax/ledger.yaml +14 -0
  136. package/runtime/syntax/lfe.yaml +17 -0
  137. package/runtime/syntax/lilypond.yaml +26 -0
  138. package/runtime/syntax/lisp.yaml +17 -0
  139. package/runtime/syntax/log.yaml +92 -0
  140. package/runtime/syntax/lua.yaml +111 -0
  141. package/runtime/syntax/mail.yaml +25 -0
  142. package/runtime/syntax/makefile.yaml +38 -0
  143. package/runtime/syntax/man.yaml +12 -0
  144. package/runtime/syntax/markdown.yaml +49 -0
  145. package/runtime/syntax/mc.yaml +23 -0
  146. package/runtime/syntax/meson.yaml +51 -0
  147. package/runtime/syntax/micro.yaml +34 -0
  148. package/runtime/syntax/mpdconf.yaml +13 -0
  149. package/runtime/syntax/msbuild.yaml +6 -0
  150. package/runtime/syntax/nanorc.yaml +16 -0
  151. package/runtime/syntax/nftables.yaml +30 -0
  152. package/runtime/syntax/nginx.yaml +22 -0
  153. package/runtime/syntax/nim.yaml +27 -0
  154. package/runtime/syntax/nix.yaml +32 -0
  155. package/runtime/syntax/nu.yaml +114 -0
  156. package/runtime/syntax/objc.yaml +60 -0
  157. package/runtime/syntax/ocaml.yaml +43 -0
  158. package/runtime/syntax/octave.yaml +83 -0
  159. package/runtime/syntax/odin.yaml +64 -0
  160. package/runtime/syntax/pascal.yaml +45 -0
  161. package/runtime/syntax/patch.yaml +14 -0
  162. package/runtime/syntax/peg.yaml +16 -0
  163. package/runtime/syntax/perl.yaml +58 -0
  164. package/runtime/syntax/php.yaml +60 -0
  165. package/runtime/syntax/pkg-config.yaml +12 -0
  166. package/runtime/syntax/po.yaml +12 -0
  167. package/runtime/syntax/pony.yaml +37 -0
  168. package/runtime/syntax/pov.yaml +21 -0
  169. package/runtime/syntax/privoxy-action.yaml +14 -0
  170. package/runtime/syntax/privoxy-config.yaml +10 -0
  171. package/runtime/syntax/privoxy-filter.yaml +12 -0
  172. package/runtime/syntax/proto.yaml +40 -0
  173. package/runtime/syntax/prql.yaml +84 -0
  174. package/runtime/syntax/puppet.yaml +22 -0
  175. package/runtime/syntax/python2.yaml +60 -0
  176. package/runtime/syntax/python3.yaml +80 -0
  177. package/runtime/syntax/r.yaml +32 -0
  178. package/runtime/syntax/raku.yaml +42 -0
  179. package/runtime/syntax/reST.yaml +18 -0
  180. package/runtime/syntax/renpy.yaml +15 -0
  181. package/runtime/syntax/rpmspec.yaml +43 -0
  182. package/runtime/syntax/ruby.yaml +73 -0
  183. package/runtime/syntax/rust.yaml +78 -0
  184. package/runtime/syntax/sage.yaml +60 -0
  185. package/runtime/syntax/scad.yaml +53 -0
  186. package/runtime/syntax/scala.yaml +33 -0
  187. package/runtime/syntax/sed.yaml +13 -0
  188. package/runtime/syntax/sh.yaml +69 -0
  189. package/runtime/syntax/sls.yaml +15 -0
  190. package/runtime/syntax/smalltalk.yaml +55 -0
  191. package/runtime/syntax/solidity.yaml +41 -0
  192. package/runtime/syntax/sql.yaml +35 -0
  193. package/runtime/syntax/stata.yaml +67 -0
  194. package/runtime/syntax/svelte.yaml +27 -0
  195. package/runtime/syntax/swift.yaml +103 -0
  196. package/runtime/syntax/systemd.yaml +16 -0
  197. package/runtime/syntax/tcl.yaml +18 -0
  198. package/runtime/syntax/terraform.yaml +87 -0
  199. package/runtime/syntax/tex.yaml +32 -0
  200. package/runtime/syntax/toml.yaml +56 -0
  201. package/runtime/syntax/twig.yaml +55 -0
  202. package/runtime/syntax/typescript.yaml +49 -0
  203. package/runtime/syntax/v.yaml +80 -0
  204. package/runtime/syntax/vala.yaml +26 -0
  205. package/runtime/syntax/verilog.yaml +60 -0
  206. package/runtime/syntax/vhdl.yaml +37 -0
  207. package/runtime/syntax/vi.yaml +31 -0
  208. package/runtime/syntax/vue.yaml +64 -0
  209. package/runtime/syntax/xml.yaml +37 -0
  210. package/runtime/syntax/xresources.yaml +14 -0
  211. package/runtime/syntax/yaml.yaml +34 -0
  212. package/runtime/syntax/yum.yaml +12 -0
  213. package/runtime/syntax/zig.yaml +52 -0
  214. package/runtime/syntax/zscript.yaml +72 -0
  215. package/runtime/syntax/zsh.yaml +52 -0
  216. package/single-exe/README.md +80 -0
  217. package/single-exe/assetsHelper.js +90 -0
  218. package/single-exe/assetsLoader.mjs +85 -0
  219. package/single-exe/compiled.js +149 -0
  220. package/single-exe/entry.mjs +9 -0
  221. package/single-exe/packAssets.sh +7 -0
  222. package/src/buffer/backup.js +160 -0
  223. package/src/buffer/buffer.js +126 -0
  224. package/src/buffer/fixed3-codec.js +140 -0
  225. package/src/buffer/loc.js +38 -0
  226. package/src/buffer/message.js +29 -0
  227. package/src/config/clean.js +172 -0
  228. package/src/config/colorscheme.js +99 -0
  229. package/src/config/config.js +122 -0
  230. package/src/config/defaults.js +109 -0
  231. package/src/cui/rpc.mjs +259 -0
  232. package/src/cui/server.mjs +116 -0
  233. package/src/display/ansi-style.js +60 -0
  234. package/src/highlight/highlighter.js +243 -0
  235. package/src/highlight/parser.js +225 -0
  236. package/src/index.js +8009 -0
  237. package/src/lua/engine.js +71 -0
  238. package/src/platform/archive.js +50 -0
  239. package/src/platform/clipboard.js +278 -0
  240. package/src/platform/commands.js +137 -0
  241. package/src/plugins/js-bridge.js +983 -0
  242. package/src/plugins/manager.js +674 -0
  243. package/src/runtime/assets.js +90 -0
  244. package/src/runtime/compiled.js +25 -0
  245. package/src/runtime/encodings.js +10 -0
  246. package/src/runtime/registry.js +134 -0
  247. package/src/screen/cell-buffer.js +81 -0
  248. package/src/screen/events.js +263 -0
  249. package/src/screen/screen.js +142 -0
  250. package/src/screen/vt100.js +571 -0
  251. package/src/shell/shell.js +70 -0
  252. package/testapp.md +55 -0
  253. package/tests/backup.test.js +133 -0
  254. package/tests/cmphex3b64.js +95 -0
  255. package/tests/pty-demo.js +492 -0
  256. package/tests/wv-client.js +96 -0
  257. package/tui +5 -0
  258. package/wui +5 -0
@@ -0,0 +1,71 @@
1
+ import { existsSync } from "node:fs";
2
+ import { readInternalAssetBytes } from "../runtime/assets.js";
3
+ import { join } from "node:path";
4
+ import { tmpdir } from "node:os";
5
+ import { isCompiledBinary, resolveCompiledBaseDir } from "../runtime/compiled.js";
6
+ import { REPO_ROOT } from "../../single-exe/compiled.js";
7
+
8
+ export async function createLuaEngine() {
9
+ let wasmoon = null;
10
+ try {
11
+ wasmoon = await import("wasmoon");
12
+ } catch (error) {
13
+ if (
14
+ error?.code !== "ERR_MODULE_NOT_FOUND" &&
15
+ !/Cannot find package/.test(String(error?.message))
16
+ ) {
17
+ throw error;
18
+ }
19
+ }
20
+ if (!wasmoon?.LuaFactory) {
21
+ throw new Error("Lua support requires the WASM runtime `wasmoon`. Install it with `bun add wasmoon`.");
22
+ }
23
+
24
+ const wasmLocation = await resolveLuaWasmLocation();
25
+ const factory = new wasmoon.LuaFactory(wasmLocation);
26
+ const lua = await factory.createEngine();
27
+ return new WasmoonEngine(lua);
28
+ }
29
+
30
+ async function resolveLuaWasmLocation() {
31
+ const wasmAssetPath = "runtime/wasmoon_glue.wasm";
32
+ const wasmBytes = readInternalAssetBytes(wasmAssetPath);
33
+ if (wasmBytes) {
34
+ const wasmPath = join(tmpdir(), "bunmicro-wasmoon-glue.wasm");
35
+ try {
36
+ const existing = await Bun.file(wasmPath).bytes().catch(() => null);
37
+ if (!existing || existing.byteLength !== wasmBytes.byteLength) {
38
+ await Bun.write(wasmPath, wasmBytes);
39
+ }
40
+ return wasmPath;
41
+ } catch (error) {
42
+ console.error("# failed to stage bundled wasmoon wasm");
43
+ console.error(error);
44
+ }
45
+ }
46
+
47
+ const fallbackPath = isCompiledBinary(process.argv)
48
+ ? join(resolveCompiledBaseDir({ argv: process.argv }), wasmAssetPath)
49
+ : join(REPO_ROOT, wasmAssetPath);
50
+
51
+ return existsSync(fallbackPath) ? fallbackPath : undefined;
52
+ }
53
+
54
+ class WasmoonEngine {
55
+ constructor(lua) {
56
+ this.kind = "wasmoon";
57
+ this.lua = lua;
58
+ }
59
+
60
+ async doString(source, chunkName = "chunk") {
61
+ return this.lua.doString(source, chunkName);
62
+ }
63
+
64
+ setGlobal(name, value) {
65
+ this.lua.global.set(name, value);
66
+ }
67
+
68
+ getGlobal(name) {
69
+ return this.lua.global.get(name);
70
+ }
71
+ }
@@ -0,0 +1,50 @@
1
+ import { mkdir, readdir, rename, rm } from "node:fs/promises";
2
+ import { join, dirname } from "node:path";
3
+ import { hasCommand, isLinuxLike, platformId, run } from "./commands.js";
4
+
5
+ export async function extractZip(zipPath, destDir) {
6
+ await mkdir(destDir, { recursive: true });
7
+ const platform = platformId();
8
+
9
+ if (platform === "darwin" || platform === "win32") {
10
+ await run(["tar", "-xf", zipPath, "-C", destDir]);
11
+ return;
12
+ }
13
+
14
+ if (isLinuxLike()) {
15
+ if (!hasCommand("unzip")) {
16
+ throw new Error("Installing zip plugins requires `unzip`. Please install unzip first.");
17
+ }
18
+ await run(["unzip", "-q", zipPath, "-d", destDir]);
19
+ return;
20
+ }
21
+
22
+ if (hasCommand("unzip")) {
23
+ await run(["unzip", "-q", zipPath, "-d", destDir]);
24
+ return;
25
+ }
26
+
27
+ throw new Error(`Unsupported platform for zip extraction: ${platform}`);
28
+ }
29
+
30
+ // Extract zip to destDir, stripping a single top-level prefix directory if present
31
+ // (matches Go micro behavior: zips containing e.g. autoclose-1.0.0/ are stripped).
32
+ export async function extractAndStrip(zipPath, destDir) {
33
+ const tmpDir = destDir + ".__install_tmp__";
34
+ await rm(tmpDir, { recursive: true, force: true });
35
+ await extractZip(zipPath, tmpDir);
36
+
37
+ const entries = await readdir(tmpDir, { withFileTypes: true });
38
+ const subdirs = entries.filter((e) => e.isDirectory());
39
+ const files = entries.filter((e) => e.isFile());
40
+
41
+ await rm(destDir, { recursive: true, force: true });
42
+ await mkdir(dirname(destDir), { recursive: true });
43
+
44
+ if (subdirs.length === 1 && files.length === 0) {
45
+ await rename(join(tmpDir, subdirs[0].name), destDir);
46
+ await rm(tmpDir, { recursive: true, force: true });
47
+ } else {
48
+ await rename(tmpDir, destDir);
49
+ }
50
+ }
@@ -0,0 +1,278 @@
1
+ import { firstCommand, isLinuxLike, platformId, runSync } from "./commands.js";
2
+
3
+ const CLIPBOARD_TIMEOUT_MS = 2000;
4
+ const internalRegisters = new Map();
5
+
6
+ export class ClipboardManager {
7
+ constructor() {
8
+ this.backend = detectClipboardBackend();
9
+ this._writeBackend = null;
10
+ this._altBackend = null;
11
+ this._readFromInternal = false;
12
+ }
13
+
14
+ methodName() {
15
+ return (this._writeBackend ?? this.backend).name;
16
+ }
17
+
18
+ readMethodName(register = "clipboard") {
19
+ if (register !== "clipboard" && register !== "primary") return "internal";
20
+ if (register === "clipboard" && this._readFromInternal) return "internal";
21
+ if (register === "primary" && !this.backend.supportsPrimary) return "internal";
22
+ return this.backend.name;
23
+ }
24
+
25
+ altMethodName() {
26
+ return this._altBackend?.name ?? null;
27
+ }
28
+
29
+ fallbackToInternal() {
30
+ this.backend = internalClipboard();
31
+ this._writeBackend = null;
32
+ this._altBackend = null;
33
+ this._readFromInternal = false;
34
+ return this.backend;
35
+ }
36
+
37
+ read(register = "clipboard") {
38
+ if (register !== "clipboard" && register !== "primary") {
39
+ return internalRegisters.get(register) ?? "";
40
+ }
41
+ if (register === "primary" && !this.backend.supportsPrimary) {
42
+ return internalRegisters.get(register) ?? "";
43
+ }
44
+ // terminal mode: paste from internal to avoid OSC 52 read issues over SSH
45
+ if (register === "clipboard" && this._readFromInternal) {
46
+ return internalRegisters.get(register) ?? "";
47
+ }
48
+ try {
49
+ const text = this.backend.read?.(register);
50
+ if (text == null) return internalRegisters.get(register) ?? "";
51
+ return text;
52
+ } catch {
53
+ return this.fallbackToInternal().read(register);
54
+ }
55
+ }
56
+
57
+ write(text, register = "clipboard") {
58
+ internalRegisters.set(register, text);
59
+ if (register !== "clipboard" && register !== "primary") return true;
60
+ const wb = this._writeBackend ?? this.backend;
61
+ if (register === "primary" && !wb.supportsPrimary) return true;
62
+ try {
63
+ const ok = wb.write?.(text, register) ?? true;
64
+ if (!ok) {
65
+ if (wb === this._writeBackend) this._writeBackend = null;
66
+ else this.fallbackToInternal();
67
+ }
68
+ return true;
69
+ } catch {
70
+ if (wb === this._writeBackend) this._writeBackend = null;
71
+ else this.fallbackToInternal();
72
+ return true;
73
+ }
74
+ }
75
+
76
+ writeAlt(text, register = "clipboard") {
77
+ if (!this._altBackend) return false;
78
+ internalRegisters.set(register, text);
79
+ try {
80
+ return this._altBackend.write?.(text, register) ?? true;
81
+ } catch {
82
+ return false;
83
+ }
84
+ }
85
+
86
+ async initFromSetting(setting, ttyIn, ttyOut, timeoutMs = 150) {
87
+ this.backend = detectClipboardBackend();
88
+ this._writeBackend = null;
89
+ this._altBackend = null;
90
+ this._readFromInternal = false;
91
+ if (setting === "internal") {
92
+ this.fallbackToInternal();
93
+ return;
94
+ }
95
+ if (setting === "terminal") {
96
+ // skip probe — directly enable OSC 52 write (handles write-only terminals)
97
+ if (ttyOut) {
98
+ this._altBackend = this.backend; // external as clickable alt
99
+ this._writeBackend = osc52Clipboard(ttyOut);
100
+ this._readFromInternal = true; // paste via internal (SSH-safe)
101
+ }
102
+ } else {
103
+ // "external" (default): probe OSC 52 as optional alt
104
+ if (ttyIn && ttyOut && process.stdout?.isTTY) {
105
+ const ok = await probeOSC52(ttyIn, ttyOut, timeoutMs);
106
+ if (ok) this._altBackend = osc52Clipboard(ttyOut);
107
+ }
108
+ }
109
+ }
110
+
111
+ // kept for backward compatibility (--version probe etc.)
112
+ async probeAndUpgradeOSC52(ttyIn, ttyOut, timeoutMs = 150) {
113
+ const ok = await probeOSC52(ttyIn, ttyOut, timeoutMs);
114
+ if (ok) this._writeBackend = osc52Clipboard(ttyOut);
115
+ }
116
+ }
117
+
118
+ function detectClipboardBackend() {
119
+ const platform = platformId();
120
+
121
+ if (platform === "android") {
122
+ const termuxSet = firstCommand(["termux-clipboard-set"]);
123
+ const termuxGet = firstCommand(["termux-clipboard-get"]);
124
+ if (termuxSet && termuxGet) return termuxClipboard(termuxSet, termuxGet);
125
+ }
126
+
127
+ if (isLinuxLike()) {
128
+ const wlCopy = firstCommand(["wl-copy"]);
129
+ const wlPaste = firstCommand(["wl-paste"]);
130
+ if (wlCopy && wlPaste) return wlClipboard(wlCopy, wlPaste);
131
+
132
+ const xclip = firstCommand(["xclip"]);
133
+ if (xclip) return xclipClipboard(xclip);
134
+
135
+ const xsel = firstCommand(["xsel"]);
136
+ if (xsel) return xselClipboard(xsel);
137
+
138
+ return internalClipboard();
139
+ }
140
+
141
+ if (platform === "darwin") {
142
+ const pbcopy = firstCommand(["pbcopy"]);
143
+ const pbpaste = firstCommand(["pbpaste"]);
144
+ if (pbcopy && pbpaste) return commandClipboard("pbcopy/pbpaste", [pbcopy], [pbpaste]);
145
+ return internalClipboard();
146
+ }
147
+
148
+ if (platform === "win32") {
149
+ const shell = firstCommand(["pwsh.exe", "powershell.exe", "pwsh", "powershell"]);
150
+ if (shell) return powershellClipboard(shell);
151
+ return internalClipboard();
152
+ }
153
+
154
+ return internalClipboard();
155
+ }
156
+
157
+ function internalClipboard() {
158
+ return {
159
+ name: "internal",
160
+ read: (register) => internalRegisters.get(register) ?? "",
161
+ write: (text, register) => {
162
+ internalRegisters.set(register, text);
163
+ return true;
164
+ },
165
+ };
166
+ }
167
+
168
+ function commandClipboard(name, writeCommand, readCommand) {
169
+ return {
170
+ name,
171
+ read: () => outputOrThrow(runSync(readCommand, { timeout: CLIPBOARD_TIMEOUT_MS })),
172
+ write: (text) => runSync(writeCommand, { stdin: text, stdout: "ignore", timeout: CLIPBOARD_TIMEOUT_MS }).ok,
173
+ };
174
+ }
175
+
176
+ function termuxClipboard(set, get) {
177
+ return commandClipboard("termux", [set], [get]);
178
+ }
179
+
180
+ function wlClipboard(wlCopy, wlPaste) {
181
+ return {
182
+ name: "wl-clipboard",
183
+ supportsPrimary: true,
184
+ read: (register) => {
185
+ const args = [wlPaste, "--no-newline"];
186
+ if (register === "primary") args.push("--primary");
187
+ return outputOrThrow(runSync(args, { timeout: CLIPBOARD_TIMEOUT_MS }));
188
+ },
189
+ write: (text, register) => {
190
+ const args = [wlCopy];
191
+ if (register === "primary") args.push("--primary");
192
+ return runSync(args, { stdin: text, stdout: "ignore", timeout: CLIPBOARD_TIMEOUT_MS }).ok;
193
+ },
194
+ };
195
+ }
196
+
197
+ function xclipClipboard(xclip) {
198
+ return {
199
+ name: "xclip",
200
+ supportsPrimary: true,
201
+ read: (register) => {
202
+ const selection = register === "primary" ? "primary" : "clipboard";
203
+ return outputOrThrow(runSync([xclip, "-selection", selection, "-o"], { timeout: CLIPBOARD_TIMEOUT_MS }));
204
+ },
205
+ write: (text, register) => {
206
+ const selection = register === "primary" ? "primary" : "clipboard";
207
+ return runSync([xclip, "-selection", selection], { stdin: text, stdout: "ignore", timeout: CLIPBOARD_TIMEOUT_MS }).ok;
208
+ },
209
+ };
210
+ }
211
+
212
+ function xselClipboard(xsel) {
213
+ return {
214
+ name: "xsel",
215
+ supportsPrimary: true,
216
+ read: (register) => {
217
+ const selection = register === "primary" ? "--primary" : "--clipboard";
218
+ return outputOrThrow(runSync([xsel, selection, "--output"], { timeout: CLIPBOARD_TIMEOUT_MS }));
219
+ },
220
+ write: (text, register) => {
221
+ const selection = register === "primary" ? "--primary" : "--clipboard";
222
+ return runSync([xsel, selection, "--input"], { stdin: text, stdout: "ignore", timeout: CLIPBOARD_TIMEOUT_MS }).ok;
223
+ },
224
+ };
225
+ }
226
+
227
+ function powershellClipboard(shell) {
228
+ return {
229
+ name: "powershell",
230
+ // Get-Clipboard -Raw appends \r\n to stdout; strip exactly one trailing line ending.
231
+ read: () => outputOrThrow(runSync([shell, "-NoProfile", "-Command", "Get-Clipboard -Raw"], {})).replace(/\r?\n$/, ""),
232
+ write: (text) => {
233
+ text=(text+'').replaceAll("'","''") ;
234
+ return runSync([shell, "-NoProfile", "-Command", `Set-Clipboard '${text}'`], { stdout: "ignore" }).ok ;
235
+ },
236
+ };
237
+ }
238
+
239
+ function outputOrThrow(result) {
240
+ if (!result.ok) throw new Error(result.stderr || result.stdout || "clipboard command failed");
241
+ return result.stdout;
242
+ }
243
+
244
+ export function osc52Clipboard(stdout) {
245
+ const inTmux = !!process.env.TMUX;
246
+ return {
247
+ name: "OSC 52",
248
+ write(text) {
249
+ const b64 = Buffer.from(text, "utf-8").toString("base64");
250
+ stdout.write(inTmux
251
+ ? `\x1bPtmux;\x1b\x1b]52;c;${b64}\x07\x1b\\`
252
+ : `\x1b]52;c;${b64}\x07`);
253
+ return true;
254
+ },
255
+ };
256
+ }
257
+
258
+ export async function probeOSC52(ttyIn, ttyOut, timeoutMs) {
259
+ return true;
260
+ // Almost no terminal reliably supports probing OSC 52, so treat it as
261
+ // available and let the actual write path fail or work on its own.
262
+ if (process.env.TMUX) return true;
263
+ return new Promise((resolve) => {
264
+ let done = false;
265
+ const timer = setTimeout(() => {
266
+ if (!done) { done = true; ttyIn.removeListener("data", onData); resolve(false); }
267
+ }, timeoutMs);
268
+ function onData(chunk) {
269
+ if (done) return;
270
+ const s = Buffer.isBuffer(chunk) ? chunk.toString("latin1") : String(chunk);
271
+ if (s.includes("\x1b]52;")) {
272
+ done = true; clearTimeout(timer); ttyIn.removeListener("data", onData); resolve(true);
273
+ }
274
+ }
275
+ ttyIn.on("data", onData);
276
+ ttyOut.write("\x1b]52;c;?\x07");
277
+ });
278
+ }
@@ -0,0 +1,137 @@
1
+ import process from "node:process";
2
+
3
+ const decoder = new TextDecoder();
4
+
5
+ let _httpBackend = null;
6
+
7
+ export function detectHttpBackend() {
8
+ if (_httpBackend !== null) return _httpBackend;
9
+ if (Bun.which("curl")) { _httpBackend = "curl"; return _httpBackend; }
10
+ if (Bun.which("wget")) { _httpBackend = "wget"; return _httpBackend; }
11
+ _httpBackend = "fetch";
12
+ return _httpBackend;
13
+ }
14
+
15
+ export async function fetchHttp(url) {
16
+ return decoder.decode(await fetchHttpBytes(url));
17
+ }
18
+
19
+ export async function fetchHttpBytes(url) {
20
+ const b = detectHttpBackend();
21
+ if (b === "curl") {
22
+ const r = await runBytes(["curl", "-kL", "--silent", "--fail", url], { allowFailure: true });
23
+ if (!r.ok) throw new Error(`curl: ${r.stderr.trim() || "failed"} (${url})`);
24
+ return r.stdout;
25
+ }
26
+ if (b === "wget") {
27
+ const r = await runBytes(["wget", "--no-check-certificate", "-q", "-O", "-", url], { allowFailure: true });
28
+ if (!r.ok) throw new Error(`wget: ${r.stderr.trim() || "failed"} (${url})`);
29
+ return r.stdout;
30
+ }
31
+ const resp = await fetch(url);
32
+ if (!resp.ok) throw new Error(`HTTP ${resp.status}: ${url}`);
33
+ return new Uint8Array(await resp.arrayBuffer());
34
+ }
35
+
36
+ export async function downloadFile(url, outPath) {
37
+ const b = detectHttpBackend();
38
+ if (b === "curl") {
39
+ await run(["curl", "-kL", "--silent", "--fail", "-o", outPath, url]);
40
+ return;
41
+ }
42
+ if (b === "wget") {
43
+ await run(["wget", "--no-check-certificate", "-q", "-O", outPath, url]);
44
+ return;
45
+ }
46
+ const resp = await fetch(url);
47
+ if (!resp.ok) throw new Error(`HTTP ${resp.status}: ${url}`);
48
+ await Bun.write(outPath, resp);
49
+ }
50
+
51
+ export function platformId() {
52
+ return process.platform;
53
+ }
54
+
55
+ export function isLinuxLike() {
56
+ const platform = platformId();
57
+ return platform === "linux" || platform === "android";
58
+ }
59
+
60
+ export function runSync(command, options = {}) {
61
+ try {
62
+ const spawnOpts = {
63
+ stdio: [stdioInput(options.stdin), options.stdout ?? "pipe", options.stderr ?? "pipe"],
64
+ env: options.env ?? process.env,
65
+ cwd: options.cwd,
66
+ };
67
+ if (options.timeout != null) spawnOpts.timeout = options.timeout;
68
+ const proc = Bun.spawnSync(command, spawnOpts);
69
+ return {
70
+ ok: proc.success,
71
+ code: proc.exitCode,
72
+ stdout: proc.stdout ? decoder.decode(proc.stdout) : "",
73
+ stderr: proc.stderr ? decoder.decode(proc.stderr) : "",
74
+ };
75
+ } catch (error) {
76
+ return {
77
+ ok: false,
78
+ code: -1,
79
+ stdout: "",
80
+ stderr: String(error?.message || error),
81
+ };
82
+ }
83
+ }
84
+
85
+ export async function run(command, options = {}) {
86
+ const proc = Bun.spawn(command, {
87
+ stdio: [stdioInput(options.stdin), options.stdout ?? "pipe", options.stderr ?? "pipe"],
88
+ env: options.env ?? process.env,
89
+ cwd: options.cwd,
90
+ });
91
+ const [stdout, stderr, code] = await Promise.all([
92
+ proc.stdout ? new Response(proc.stdout).text() : Promise.resolve(""),
93
+ proc.stderr ? new Response(proc.stderr).text() : Promise.resolve(""),
94
+ proc.exited,
95
+ ]);
96
+ if (code !== 0 && !options.allowFailure) {
97
+ throw new Error(`${command[0]} exited with ${code}: ${stderr || stdout}`);
98
+ }
99
+ return { ok: code === 0, code, stdout, stderr };
100
+ }
101
+
102
+ export async function runBytes(command, options = {}) {
103
+ const proc = Bun.spawn(command, {
104
+ stdio: [stdioInput(options.stdin), options.stdout ?? "pipe", options.stderr ?? "pipe"],
105
+ env: options.env ?? process.env,
106
+ cwd: options.cwd,
107
+ });
108
+ const [stdoutBuf, stderr, code] = await Promise.all([
109
+ proc.stdout ? new Response(proc.stdout).arrayBuffer() : Promise.resolve(new ArrayBuffer(0)),
110
+ proc.stderr ? new Response(proc.stderr).text() : Promise.resolve(""),
111
+ proc.exited,
112
+ ]);
113
+ const stdout = new Uint8Array(stdoutBuf);
114
+ if (code !== 0 && !options.allowFailure) {
115
+ throw new Error(`${command[0]} exited with ${code}: ${stderr || decoder.decode(stdout)}`);
116
+ }
117
+ return { ok: code === 0, code, stdout, stderr };
118
+ }
119
+
120
+ export function hasCommand(name) {
121
+ return Bun.which(name);
122
+ }
123
+
124
+ export function firstCommand(names) {
125
+ return names.find((name) => hasCommand(name)) ?? null;
126
+ }
127
+
128
+ export function shellQuote(value) {
129
+ return `'${String(value).replaceAll("'", "'\\''")}'`;
130
+ }
131
+
132
+ function stdioInput(input) {
133
+ if (input == null) return "ignore";
134
+ if (input === "pipe" || input === "inherit" || input === "ignore") return input;
135
+ if (typeof input === "string" || input instanceof Uint8Array || input instanceof ArrayBuffer) return new Blob([input]);
136
+ return input;
137
+ }