bunmicro 0.8.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 (237) hide show
  1. package/LICENSE +22 -0
  2. package/PORTING.md +34 -0
  3. package/README.md +153 -0
  4. package/bmi +5 -0
  5. package/bun.lock +17 -0
  6. package/bunmicro +5 -0
  7. package/hlw.md +5 -0
  8. package/package.json +18 -0
  9. package/runtime/colorschemes/atom-dark.micro +33 -0
  10. package/runtime/colorschemes/bubblegum.micro +31 -0
  11. package/runtime/colorschemes/cmc-16.micro +47 -0
  12. package/runtime/colorschemes/cmc-tc.micro +43 -0
  13. package/runtime/colorschemes/darcula.micro +34 -0
  14. package/runtime/colorschemes/default.micro +1 -0
  15. package/runtime/colorschemes/dracula-tc.micro +49 -0
  16. package/runtime/colorschemes/dukedark-tc.micro +38 -0
  17. package/runtime/colorschemes/dukelight-tc.micro +38 -0
  18. package/runtime/colorschemes/dukeubuntu-tc.micro +38 -0
  19. package/runtime/colorschemes/geany.micro +29 -0
  20. package/runtime/colorschemes/gotham.micro +29 -0
  21. package/runtime/colorschemes/gruvbox-tc.micro +29 -0
  22. package/runtime/colorschemes/gruvbox.micro +26 -0
  23. package/runtime/colorschemes/material-tc.micro +36 -0
  24. package/runtime/colorschemes/monokai-dark.micro +28 -0
  25. package/runtime/colorschemes/monokai.micro +34 -0
  26. package/runtime/colorschemes/one-dark.micro +39 -0
  27. package/runtime/colorschemes/railscast.micro +37 -0
  28. package/runtime/colorschemes/simple.micro +33 -0
  29. package/runtime/colorschemes/solarized-tc.micro +31 -0
  30. package/runtime/colorschemes/solarized.micro +30 -0
  31. package/runtime/colorschemes/sunny-day.micro +29 -0
  32. package/runtime/colorschemes/twilight.micro +40 -0
  33. package/runtime/colorschemes/zenburn.micro +30 -0
  34. package/runtime/help/actions.md +161 -0
  35. package/runtime/help/colors.md +421 -0
  36. package/runtime/help/commands.md +161 -0
  37. package/runtime/help/copypaste.md +149 -0
  38. package/runtime/help/defaultkeys.md +141 -0
  39. package/runtime/help/help.md +63 -0
  40. package/runtime/help/keybindings.md +760 -0
  41. package/runtime/help/linter.md +90 -0
  42. package/runtime/help/options.md +701 -0
  43. package/runtime/help/plugins.md +544 -0
  44. package/runtime/help/tutorial.md +112 -0
  45. package/runtime/jsplugins/chapter/chapter.js +108 -0
  46. package/runtime/jsplugins/diff/diff.js +46 -0
  47. package/runtime/jsplugins/example/example.js +108 -0
  48. package/runtime/jsplugins/linter/linter.js +281 -0
  49. package/runtime/plugins/autoclose/autoclose.lua +75 -0
  50. package/runtime/plugins/ftoptions/ftoptions.lua +17 -0
  51. package/runtime/plugins/literate/README.md +5 -0
  52. package/runtime/plugins/literate/literate.lua +55 -0
  53. package/runtime/plugins/status/help/status.md +21 -0
  54. package/runtime/plugins/status/status.lua +62 -0
  55. package/runtime/syntax/LICENSE +22 -0
  56. package/runtime/syntax/PowerShell.yaml +128 -0
  57. package/runtime/syntax/README.md +63 -0
  58. package/runtime/syntax/ada.yaml +43 -0
  59. package/runtime/syntax/apacheconf.yaml +59 -0
  60. package/runtime/syntax/arduino.yaml +101 -0
  61. package/runtime/syntax/asciidoc.yaml +51 -0
  62. package/runtime/syntax/asm.yaml +123 -0
  63. package/runtime/syntax/ats.yaml +99 -0
  64. package/runtime/syntax/awk.yaml +44 -0
  65. package/runtime/syntax/b.yaml +87 -0
  66. package/runtime/syntax/bat.yaml +57 -0
  67. package/runtime/syntax/c.yaml +60 -0
  68. package/runtime/syntax/caddyfile.yaml +23 -0
  69. package/runtime/syntax/cake.yaml +7 -0
  70. package/runtime/syntax/clojure.yaml +38 -0
  71. package/runtime/syntax/cmake.yaml +42 -0
  72. package/runtime/syntax/coffeescript.yaml +56 -0
  73. package/runtime/syntax/colortest.yaml +19 -0
  74. package/runtime/syntax/conky.yaml +17 -0
  75. package/runtime/syntax/cpp.yaml +91 -0
  76. package/runtime/syntax/crontab.yaml +36 -0
  77. package/runtime/syntax/crystal.yaml +72 -0
  78. package/runtime/syntax/csharp.yaml +52 -0
  79. package/runtime/syntax/css.yaml +44 -0
  80. package/runtime/syntax/csx.yaml +8 -0
  81. package/runtime/syntax/cuda.yaml +68 -0
  82. package/runtime/syntax/cython.yaml +52 -0
  83. package/runtime/syntax/d.yaml +121 -0
  84. package/runtime/syntax/dart.yaml +46 -0
  85. package/runtime/syntax/default.yaml +10 -0
  86. package/runtime/syntax/dockerfile.yaml +36 -0
  87. package/runtime/syntax/dot.yaml +29 -0
  88. package/runtime/syntax/elixir.yaml +30 -0
  89. package/runtime/syntax/elm.yaml +38 -0
  90. package/runtime/syntax/erb.yaml +42 -0
  91. package/runtime/syntax/erlang.yaml +45 -0
  92. package/runtime/syntax/fish.yaml +48 -0
  93. package/runtime/syntax/forth.yaml +34 -0
  94. package/runtime/syntax/fortran.yaml +64 -0
  95. package/runtime/syntax/freebsd-kernel.yaml +14 -0
  96. package/runtime/syntax/fsharp.yaml +48 -0
  97. package/runtime/syntax/gdscript.yaml +61 -0
  98. package/runtime/syntax/gemini.yaml +19 -0
  99. package/runtime/syntax/gentoo-ebuild.yaml +48 -0
  100. package/runtime/syntax/gentoo-etc-portage.yaml +23 -0
  101. package/runtime/syntax/git-commit.yaml +35 -0
  102. package/runtime/syntax/git-config.yaml +14 -0
  103. package/runtime/syntax/git-rebase-todo.yaml +19 -0
  104. package/runtime/syntax/gleam.yaml +69 -0
  105. package/runtime/syntax/glsl.yaml +26 -0
  106. package/runtime/syntax/gnuplot.yaml +15 -0
  107. package/runtime/syntax/go.yaml +62 -0
  108. package/runtime/syntax/godoc.yaml +17 -0
  109. package/runtime/syntax/golo.yaml +73 -0
  110. package/runtime/syntax/gomod.yaml +31 -0
  111. package/runtime/syntax/graphql.yaml +47 -0
  112. package/runtime/syntax/groff.yaml +30 -0
  113. package/runtime/syntax/groovy.yaml +111 -0
  114. package/runtime/syntax/haml.yaml +16 -0
  115. package/runtime/syntax/hare.yaml +52 -0
  116. package/runtime/syntax/haskell.yaml +52 -0
  117. package/runtime/syntax/hc.yaml +52 -0
  118. package/runtime/syntax/html.yaml +70 -0
  119. package/runtime/syntax/html4.yaml +25 -0
  120. package/runtime/syntax/html5.yaml +25 -0
  121. package/runtime/syntax/ini.yaml +23 -0
  122. package/runtime/syntax/inputrc.yaml +14 -0
  123. package/runtime/syntax/java.yaml +37 -0
  124. package/runtime/syntax/javascript.yaml +76 -0
  125. package/runtime/syntax/jinja2.yaml +19 -0
  126. package/runtime/syntax/json.yaml +39 -0
  127. package/runtime/syntax/jsonnet.yaml +92 -0
  128. package/runtime/syntax/julia.yaml +57 -0
  129. package/runtime/syntax/justfile.yaml +40 -0
  130. package/runtime/syntax/keymap.yaml +27 -0
  131. package/runtime/syntax/kickstart.yaml +16 -0
  132. package/runtime/syntax/kotlin.yaml +66 -0
  133. package/runtime/syntax/kvlang.yaml +67 -0
  134. package/runtime/syntax/ledger.yaml +14 -0
  135. package/runtime/syntax/lfe.yaml +17 -0
  136. package/runtime/syntax/lilypond.yaml +26 -0
  137. package/runtime/syntax/lisp.yaml +17 -0
  138. package/runtime/syntax/log.yaml +92 -0
  139. package/runtime/syntax/lua.yaml +111 -0
  140. package/runtime/syntax/mail.yaml +25 -0
  141. package/runtime/syntax/makefile.yaml +38 -0
  142. package/runtime/syntax/man.yaml +12 -0
  143. package/runtime/syntax/markdown.yaml +49 -0
  144. package/runtime/syntax/mc.yaml +23 -0
  145. package/runtime/syntax/meson.yaml +51 -0
  146. package/runtime/syntax/micro.yaml +34 -0
  147. package/runtime/syntax/mpdconf.yaml +13 -0
  148. package/runtime/syntax/msbuild.yaml +6 -0
  149. package/runtime/syntax/nanorc.yaml +16 -0
  150. package/runtime/syntax/nftables.yaml +30 -0
  151. package/runtime/syntax/nginx.yaml +22 -0
  152. package/runtime/syntax/nim.yaml +27 -0
  153. package/runtime/syntax/nix.yaml +32 -0
  154. package/runtime/syntax/nu.yaml +114 -0
  155. package/runtime/syntax/objc.yaml +60 -0
  156. package/runtime/syntax/ocaml.yaml +43 -0
  157. package/runtime/syntax/octave.yaml +83 -0
  158. package/runtime/syntax/odin.yaml +64 -0
  159. package/runtime/syntax/pascal.yaml +45 -0
  160. package/runtime/syntax/patch.yaml +14 -0
  161. package/runtime/syntax/peg.yaml +16 -0
  162. package/runtime/syntax/perl.yaml +58 -0
  163. package/runtime/syntax/php.yaml +60 -0
  164. package/runtime/syntax/pkg-config.yaml +12 -0
  165. package/runtime/syntax/po.yaml +12 -0
  166. package/runtime/syntax/pony.yaml +37 -0
  167. package/runtime/syntax/pov.yaml +21 -0
  168. package/runtime/syntax/privoxy-action.yaml +14 -0
  169. package/runtime/syntax/privoxy-config.yaml +10 -0
  170. package/runtime/syntax/privoxy-filter.yaml +12 -0
  171. package/runtime/syntax/proto.yaml +40 -0
  172. package/runtime/syntax/prql.yaml +84 -0
  173. package/runtime/syntax/puppet.yaml +22 -0
  174. package/runtime/syntax/python2.yaml +60 -0
  175. package/runtime/syntax/python3.yaml +62 -0
  176. package/runtime/syntax/r.yaml +32 -0
  177. package/runtime/syntax/raku.yaml +42 -0
  178. package/runtime/syntax/reST.yaml +18 -0
  179. package/runtime/syntax/renpy.yaml +15 -0
  180. package/runtime/syntax/rpmspec.yaml +43 -0
  181. package/runtime/syntax/ruby.yaml +73 -0
  182. package/runtime/syntax/rust.yaml +78 -0
  183. package/runtime/syntax/sage.yaml +60 -0
  184. package/runtime/syntax/scad.yaml +53 -0
  185. package/runtime/syntax/scala.yaml +33 -0
  186. package/runtime/syntax/sed.yaml +13 -0
  187. package/runtime/syntax/sh.yaml +69 -0
  188. package/runtime/syntax/sls.yaml +15 -0
  189. package/runtime/syntax/smalltalk.yaml +55 -0
  190. package/runtime/syntax/solidity.yaml +41 -0
  191. package/runtime/syntax/sql.yaml +35 -0
  192. package/runtime/syntax/stata.yaml +67 -0
  193. package/runtime/syntax/svelte.yaml +27 -0
  194. package/runtime/syntax/swift.yaml +103 -0
  195. package/runtime/syntax/systemd.yaml +16 -0
  196. package/runtime/syntax/tcl.yaml +18 -0
  197. package/runtime/syntax/terraform.yaml +87 -0
  198. package/runtime/syntax/tex.yaml +32 -0
  199. package/runtime/syntax/toml.yaml +56 -0
  200. package/runtime/syntax/twig.yaml +55 -0
  201. package/runtime/syntax/typescript.yaml +49 -0
  202. package/runtime/syntax/v.yaml +80 -0
  203. package/runtime/syntax/vala.yaml +26 -0
  204. package/runtime/syntax/verilog.yaml +60 -0
  205. package/runtime/syntax/vhdl.yaml +37 -0
  206. package/runtime/syntax/vi.yaml +31 -0
  207. package/runtime/syntax/vue.yaml +64 -0
  208. package/runtime/syntax/xml.yaml +37 -0
  209. package/runtime/syntax/xresources.yaml +14 -0
  210. package/runtime/syntax/yaml.yaml +34 -0
  211. package/runtime/syntax/yum.yaml +12 -0
  212. package/runtime/syntax/zig.yaml +52 -0
  213. package/runtime/syntax/zscript.yaml +72 -0
  214. package/runtime/syntax/zsh.yaml +52 -0
  215. package/src/buffer/buffer.js +126 -0
  216. package/src/buffer/loc.js +38 -0
  217. package/src/buffer/message.js +29 -0
  218. package/src/config/colorscheme.js +109 -0
  219. package/src/config/config.js +118 -0
  220. package/src/config/defaults.js +102 -0
  221. package/src/display/ansi-style.js +60 -0
  222. package/src/highlight/highlighter.js +237 -0
  223. package/src/highlight/parser.js +137 -0
  224. package/src/index.js +5942 -0
  225. package/src/lua/engine.js +38 -0
  226. package/src/platform/archive.js +50 -0
  227. package/src/platform/clipboard.js +160 -0
  228. package/src/platform/commands.js +140 -0
  229. package/src/plugins/js-bridge.js +902 -0
  230. package/src/plugins/manager.js +619 -0
  231. package/src/runtime/registry.js +89 -0
  232. package/src/screen/cell-buffer.js +81 -0
  233. package/src/screen/events.js +263 -0
  234. package/src/screen/screen.js +118 -0
  235. package/src/screen/vt100.js +391 -0
  236. package/src/shell/shell.js +70 -0
  237. package/todo.txt +359 -0
@@ -0,0 +1,89 @@
1
+ import { existsSync } from "node:fs";
2
+ import { readdir, readFile } from "node:fs/promises";
3
+ import { basename, extname, join } from "node:path";
4
+
5
+ export const RTColorscheme = 0;
6
+ export const RTSyntax = 1;
7
+ export const RTHelp = 2;
8
+ export const RTPlugin = 3;
9
+ export const RTSyntaxHeader = 4;
10
+
11
+ export class RuntimeRegistry {
12
+ constructor({ repoRoot, configDir }) {
13
+ this.repoRoot = repoRoot;
14
+ this.configDir = configDir;
15
+ this.files = [[], [], [], [], []];
16
+ this.realFiles = [[], [], [], [], []];
17
+ }
18
+
19
+ async init({ user = true } = {}) {
20
+ this.files = [[], [], [], [], []];
21
+ this.realFiles = [[], [], [], [], []];
22
+ await this.addRuntimeKind(RTColorscheme, "colorschemes", ".micro", user);
23
+ await this.addRuntimeKind(RTSyntax, "syntax", ".yaml", user);
24
+ await this.addRuntimeKind(RTSyntaxHeader, "syntax", ".hdr", user);
25
+ await this.addRuntimeKind(RTHelp, "help", ".md", user);
26
+ }
27
+
28
+ async addRuntimeKind(kind, dir, extension, user) {
29
+ if (user) await this.addDirectory(kind, join(this.configDir, dir), extension, true);
30
+ await this.addDirectory(kind, join(this.repoRoot, "runtime", dir), extension, false);
31
+ }
32
+
33
+ async addDirectory(kind, dir, extension, real) {
34
+ if (!existsSync(dir)) return;
35
+ const entries = await readdir(dir, { withFileTypes: true });
36
+ for (const entry of entries) {
37
+ if (entry.isDirectory() || !entry.name.endsWith(extension)) continue;
38
+ const file = new RuntimeFile(join(dir, entry.name), real);
39
+ if (!real && this.realFiles[kind].some((f) => f.name === file.name)) continue;
40
+ this.files[kind].push(file);
41
+ if (real) this.realFiles[kind].push(file);
42
+ }
43
+ }
44
+
45
+ addMemoryFile(kind, name, data) {
46
+ this.files[kind].push(new MemoryRuntimeFile(name, data));
47
+ }
48
+
49
+ list(kind) {
50
+ return this.files[kind] ?? [];
51
+ }
52
+
53
+ find(kind, name) {
54
+ return this.list(kind).find((file) => file.name === name) ?? null;
55
+ }
56
+ }
57
+
58
+ class RuntimeFile {
59
+ constructor(path, real) {
60
+ this.path = path;
61
+ this.real = real;
62
+ this.name = basename(path, extname(path));
63
+ }
64
+
65
+ async data() {
66
+ return readFile(this.path);
67
+ }
68
+
69
+ async text() {
70
+ return readFile(this.path, "utf8");
71
+ }
72
+ }
73
+
74
+ class MemoryRuntimeFile {
75
+ constructor(name, data) {
76
+ this.name = basename(name, extname(name));
77
+ this.path = name;
78
+ this.real = false;
79
+ this._data = String(data);
80
+ }
81
+
82
+ async data() {
83
+ return new TextEncoder().encode(this._data);
84
+ }
85
+
86
+ async text() {
87
+ return this._data;
88
+ }
89
+ }
@@ -0,0 +1,81 @@
1
+ export class Cell {
2
+ constructor(ch = " ", style = null, combining = [], filler = false) {
3
+ this.ch = ch;
4
+ this.style = style ? { ...style } : null;
5
+ this.combining = [...combining];
6
+ this.filler = filler; // right-half placeholder for a wide (double-width) character
7
+ this.styleKey = styleKey(this.style);
8
+ }
9
+
10
+ equals(other) {
11
+ return this.ch === other?.ch && this.styleKey === other?.styleKey && this.filler === other?.filler && arrayEquals(this.combining, other?.combining);
12
+ }
13
+
14
+ clone() {
15
+ return new Cell(this.ch, this.style, this.combining, this.filler);
16
+ }
17
+ }
18
+
19
+ export class CellBuffer {
20
+ constructor(cols, rows, style = null) {
21
+ this.cols = cols;
22
+ this.rows = rows;
23
+ this.cells = Array.from({ length: rows }, () => Array.from({ length: cols }, () => new Cell(" ", style)));
24
+ }
25
+
26
+ resize(cols, rows, style = null) {
27
+ if (cols === this.cols && rows === this.rows) return;
28
+ this.cols = cols;
29
+ this.rows = rows;
30
+ this.cells = Array.from({ length: rows }, (_, y) =>
31
+ Array.from({ length: cols }, (_, x) => this.cells[y]?.[x] ?? new Cell(" ", style)));
32
+ }
33
+
34
+ setContent(x, y, ch, style = null, combining = [], filler = false) {
35
+ if (x < 0 || y < 0 || x >= this.cols || y >= this.rows) return;
36
+ this.cells[y][x] = new Cell(ch, style, combining, filler);
37
+ }
38
+
39
+ setFiller(x, y, style = null) {
40
+ if (x < 0 || y < 0 || x >= this.cols || y >= this.rows) return;
41
+ this.cells[y][x] = new Cell(" ", style, [], true);
42
+ }
43
+
44
+ getContent(x, y) {
45
+ if (x < 0 || y < 0 || x >= this.cols || y >= this.rows) return new Cell();
46
+ return this.cells[y][x];
47
+ }
48
+
49
+ fill(ch = " ", style = null) {
50
+ for (let y = 0; y < this.rows; y++) {
51
+ for (let x = 0; x < this.cols; x++) this.setContent(x, y, ch, style);
52
+ }
53
+ }
54
+
55
+ diff(previous) {
56
+ const changes = [];
57
+ for (let y = 0; y < this.rows; y++) {
58
+ for (let x = 0; x < this.cols; x++) {
59
+ const cell = this.getContent(x, y);
60
+ if (!cell.equals(previous?.getContent(x, y))) changes.push({ x, y, cell });
61
+ }
62
+ }
63
+ return changes;
64
+ }
65
+
66
+ clone() {
67
+ const copy = new CellBuffer(this.cols, this.rows);
68
+ copy.cells = this.cells.map((row) => row.map((cell) => cell.clone()));
69
+ return copy;
70
+ }
71
+ }
72
+
73
+ function styleKey(style) {
74
+ if (!style) return "";
75
+ return JSON.stringify(Object.keys(style).sort().map((key) => [key, style[key]]));
76
+ }
77
+
78
+ function arrayEquals(a = [], b = []) {
79
+ if (a.length !== b.length) return false;
80
+ return a.every((value, index) => value === b[index]);
81
+ }
@@ -0,0 +1,263 @@
1
+ const decoder = new TextDecoder();
2
+
3
+ export const ENABLE_MOUSE = "\x1b[?1000h\x1b[?1002h\x1b[?1006h";
4
+ export const DISABLE_MOUSE = "\x1b[?1006l\x1b[?1002l\x1b[?1000l";
5
+ export const ENABLE_PASTE = "\x1b[?2004h";
6
+ export const DISABLE_PASTE = "\x1b[?2004l";
7
+
8
+ export class KeyEvent {
9
+ constructor(key, raw) {
10
+ this.type = "key";
11
+ this.key = key;
12
+ this.raw = raw;
13
+ }
14
+ }
15
+
16
+ export class MouseEvent {
17
+ constructor({ x, y, button, action, modifiers = 0, raw = "" }) {
18
+ this.type = "mouse";
19
+ this.x = x;
20
+ this.y = y;
21
+ this.button = button;
22
+ this.action = action;
23
+ this.modifiers = modifiers;
24
+ this.raw = raw;
25
+ }
26
+ }
27
+
28
+ export class PasteEvent {
29
+ constructor(text, raw) {
30
+ this.type = "paste";
31
+ this.text = text;
32
+ this.raw = raw;
33
+ }
34
+ }
35
+
36
+ export class ResizeEvent {
37
+ constructor(cols, rows) {
38
+ this.type = "resize";
39
+ this.cols = cols;
40
+ this.rows = rows;
41
+ }
42
+ }
43
+
44
+ export function parseInputEvents(data) {
45
+ const bytes = typeof data === "string" ? new TextEncoder().encode(data) : new Uint8Array(data);
46
+ const text = typeof data === "string" ? data : decoder.decode(data);
47
+ const paste = parseBracketedPaste(text);
48
+ if (paste) return [paste];
49
+ const events = [...parseSgrMouseEvents(text), ...parseX10MouseEvents(bytes)];
50
+ if (events.length > 0) return events.sort((a, b) => text.indexOf(a.raw) - text.indexOf(b.raw));
51
+ return parseKeyEvents(text);
52
+ }
53
+
54
+ function parseBracketedPaste(text) {
55
+ const start = text.indexOf("\x1b[200~");
56
+ const end = text.indexOf("\x1b[201~");
57
+ if (start === -1 || end === -1 || end < start) return null;
58
+ const raw = text.slice(start, end + 7);
59
+ return new PasteEvent(text.slice(start + 6, end), raw);
60
+ }
61
+
62
+ export function parseSgrMouse(text) {
63
+ return parseSgrMouseSequence(text);
64
+ }
65
+
66
+ function parseSgrMouseEvents(text) {
67
+ const events = [];
68
+ const re = /\x1b\[<(\d+);(\d+);(\d+)([mM])/g;
69
+ for (const match of text.matchAll(re)) {
70
+ const event = sgrMatchToEvent(match);
71
+ if (event) events.push(event);
72
+ }
73
+ return events;
74
+ }
75
+
76
+ function parseSgrMouseSequence(text) {
77
+ const match = /^\x1b\[<(\d+);(\d+);(\d+)([mM])$/.exec(text);
78
+ return match ? sgrMatchToEvent(match) : null;
79
+ }
80
+
81
+ function sgrMatchToEvent(match) {
82
+ const code = Number(match[1]);
83
+ const x = Number(match[2]) - 1;
84
+ const y = Number(match[3]) - 1;
85
+ const release = match[4] === "m";
86
+ const buttonCode = code & 0b11;
87
+ const wheel = (code & 64) !== 0;
88
+ const drag = (code & 32) !== 0;
89
+ const modifiers = code & (4 | 8 | 16);
90
+ let button = "left";
91
+ if (wheel) button = buttonCode === 0 ? "wheel-up" : "wheel-down";
92
+ else if (buttonCode === 1) button = "middle";
93
+ else if (buttonCode === 2) button = "right";
94
+ else if (buttonCode === 3) button = "none";
95
+ const action = release ? "up" : drag ? "drag" : "down";
96
+ return new MouseEvent({ x, y, button, action, modifiers, raw: match[0] });
97
+ }
98
+
99
+ function parseX10MouseEvents(bytes) {
100
+ const events = [];
101
+ for (let i = 0; i + 5 < bytes.length; i++) {
102
+ if (bytes[i] !== 0x1b || bytes[i + 1] !== 0x5b || bytes[i + 2] !== 0x4d) continue;
103
+ const code = bytes[i + 3] - 32;
104
+ const x = bytes[i + 4] - 33;
105
+ const y = bytes[i + 5] - 33;
106
+ if (x < 0 || y < 0) continue;
107
+ const buttonCode = code & 0b11;
108
+ const wheel = (code & 64) !== 0;
109
+ const drag = (code & 32) !== 0;
110
+ let button = "left";
111
+ if (wheel) button = buttonCode === 0 ? "wheel-up" : "wheel-down";
112
+ else if (buttonCode === 1) button = "middle";
113
+ else if (buttonCode === 2) button = "right";
114
+ else if (buttonCode === 3) button = "none";
115
+ const action = buttonCode === 3 ? "up" : drag ? "drag" : "down";
116
+ events.push(new MouseEvent({ x, y, button, action, modifiers: code & (4 | 8 | 16), raw: decoder.decode(bytes.slice(i, i + 6)) }));
117
+ i += 5;
118
+ }
119
+ return events;
120
+ }
121
+
122
+ const KEY_MAP = {
123
+ "\x01": "ctrl-a",
124
+ "\x02": "ctrl-b",
125
+ "\x03": "ctrl-c",
126
+ "\x04": "ctrl-d",
127
+ "\x05": "ctrl-e",
128
+ "\x0c": "ctrl-l",
129
+ "\x06": "ctrl-f",
130
+ "\x07": "ctrl-g",
131
+ "\t": "tab",
132
+ "\x0b": "ctrl-k",
133
+ "\x0e": "ctrl-n",
134
+ "\x0f": "ctrl-o",
135
+ "\x10": "ctrl-p",
136
+ "\x11": "ctrl-q",
137
+ "\x12": "ctrl-r",
138
+ "\x13": "ctrl-s",
139
+ "\x14": "ctrl-t",
140
+ "\x15": "ctrl-u",
141
+ "\x16": "ctrl-v",
142
+ "\x17": "ctrl-w",
143
+ "\x18": "ctrl-x",
144
+ "\x19": "ctrl-y",
145
+ "\x1a": "ctrl-z",
146
+ "\x1f": "ctrl-underscore",
147
+ "\x7f": "backspace",
148
+ "\b": "backspace",
149
+ "\r": "enter",
150
+ "\n": "enter",
151
+ "\x1b": "escape",
152
+ "\x1b[A": "up",
153
+ "\x1b[B": "down",
154
+ "\x1b[C": "right",
155
+ "\x1b[D": "left",
156
+ "\x1b[1;2A": "shift-up",
157
+ "\x1b[1;2B": "shift-down",
158
+ "\x1b[1;2C": "shift-right",
159
+ "\x1b[1;2D": "shift-left",
160
+ "\x1b[2A": "shift-up",
161
+ "\x1b[2B": "shift-down",
162
+ "\x1b[2C": "shift-right",
163
+ "\x1b[2D": "shift-left",
164
+ "\x1b[1;5A": "ctrl-up",
165
+ "\x1b[1;5B": "ctrl-down",
166
+ "\x1b[1;5C": "ctrl-right",
167
+ "\x1b[1;5D": "ctrl-left",
168
+ "\x1b[1;5H": "ctrl-home",
169
+ "\x1b[1;5F": "ctrl-end",
170
+ "\x1b[5H": "ctrl-home",
171
+ "\x1b[5F": "ctrl-end",
172
+ "\x1b[5A": "ctrl-up",
173
+ "\x1b[5B": "ctrl-down",
174
+ "\x1b[5C": "ctrl-right",
175
+ "\x1b[5D": "ctrl-left",
176
+ "\x1b[1;6A": "shift-ctrl-up",
177
+ "\x1b[1;6B": "shift-ctrl-down",
178
+ "\x1b[1;6C": "shift-ctrl-right",
179
+ "\x1b[1;6D": "shift-ctrl-left",
180
+ // Shift+Home/End
181
+ "\x1b[1;2H": "shift-home",
182
+ "\x1b[2;1H": "shift-home",
183
+ "\x1b[1;2F": "shift-end",
184
+ "\x1b[2;1F": "shift-end",
185
+ // Shift+PageUp/Down
186
+ "\x1b[5;2~": "shift-pageup",
187
+ "\x1b[6;2~": "shift-pagedown",
188
+ // Alt+arrows
189
+ "\x1b[1;3A": "alt-up",
190
+ "\x1b[1;3B": "alt-down",
191
+ "\x1b[1;3C": "alt-right",
192
+ "\x1b[1;3D": "alt-left",
193
+ // Alt+Shift+arrows
194
+ "\x1b[1;4A": "alt-shift-up",
195
+ "\x1b[1;4B": "alt-shift-down",
196
+ "\x1b[1;4C": "alt-shift-right",
197
+ "\x1b[1;4D": "alt-shift-left",
198
+ "\x1b[H": "home",
199
+ "\x1b[F": "end",
200
+ "\x1b[1~": "home",
201
+ "\x1b[4~": "end",
202
+ "\x1b[3~": "delete",
203
+ "\x1b[5~": "pageup",
204
+ "\x1b[6~": "pagedown",
205
+ "\x1b[5;5~": "ctrl-pageup",
206
+ "\x1b[6;5~": "ctrl-pagedown",
207
+ "\x1b[7;5~": "ctrl-home",
208
+ "\x1b[8;5~": "ctrl-end",
209
+ "\x1b[Z": "backtab",
210
+ "\x1b,": "alt-comma",
211
+ "\x1b.": "alt-period",
212
+ "\x1b/": "alt-/",
213
+ "\x1b[": "alt-[",
214
+ "\x1b]": "alt-]",
215
+ "\x1b\t": "alt-tab",
216
+ "\x1b\r": "alt-enter",
217
+ };
218
+
219
+ const KEY_SEQUENCES = Object.keys(KEY_MAP)
220
+ .filter((seq) => seq !== "\x1b")
221
+ .sort((a, b) => b.length - a.length);
222
+
223
+ function parseKeyEvents(text) {
224
+ const events = [];
225
+ let i = 0;
226
+ while (i < text.length) {
227
+ const match = KEY_SEQUENCES.find((seq) => text.startsWith(seq, i));
228
+ if (match) {
229
+ events.push(new KeyEvent(parseKey(match), match));
230
+ i += match.length;
231
+ continue;
232
+ }
233
+
234
+ if (text.charCodeAt(i) === 0x1b) {
235
+ const alt = text.slice(i, i + 2);
236
+ if (alt.length === 2 && alt[1] >= " " && alt[1] <= "~") {
237
+ events.push(new KeyEvent(parseKey(alt), alt));
238
+ i += 2;
239
+ } else {
240
+ events.push(new KeyEvent("escape", "\x1b"));
241
+ i++;
242
+ }
243
+ continue;
244
+ }
245
+
246
+ let j = i + 1;
247
+ while (j < text.length && text.charCodeAt(j) !== 0x1b && !KEY_MAP[text[j]]) j++;
248
+ const raw = text.slice(i, j);
249
+ events.push(new KeyEvent(parseKey(raw), raw));
250
+ i = j;
251
+ }
252
+ return events;
253
+ }
254
+
255
+ export function parseKey(text) {
256
+ if (KEY_MAP[text]) return KEY_MAP[text];
257
+ // ESC + single printable ASCII -> alt-{char}
258
+ if (text.length === 2 && text.charCodeAt(0) === 0x1b) {
259
+ const ch = text[1];
260
+ if (ch >= " " && ch <= "~") return `alt-${ch}`;
261
+ }
262
+ return text;
263
+ }
@@ -0,0 +1,118 @@
1
+ import process from "node:process";
2
+ import { styleToAnsi } from "../display/ansi-style.js";
3
+ import { CellBuffer } from "./cell-buffer.js";
4
+ import { DISABLE_MOUSE, DISABLE_PASTE, ENABLE_MOUSE, ENABLE_PASTE, ResizeEvent } from "./events.js";
5
+
6
+ export class Screen {
7
+ constructor({ mouse = true } = {}) {
8
+ this.mouse = mouse;
9
+ this.cols = process.stdout.columns || 80;
10
+ this.rows = process.stdout.rows || 24;
11
+ this.cells = new CellBuffer(this.cols, this.rows);
12
+ this.previous = null;
13
+ this.cursor = null;
14
+ this.cursorVisible = false;
15
+ }
16
+
17
+ init() {
18
+ this.write("\x1b[?1049h\x1b[?25l");
19
+ if (this.mouse) this.write(ENABLE_MOUSE);
20
+ this.write(ENABLE_PASTE);
21
+ }
22
+
23
+ fini() {
24
+ if (this.mouse) this.write(DISABLE_MOUSE);
25
+ this.write(DISABLE_PASTE);
26
+ this.write("\x1b[?25h\x1b[?1049l\x1b[0m");
27
+ }
28
+
29
+ SetContent(x, y, ch, combining = [], style = null) {
30
+ this.cells.setContent(x, y, ch, style, combining);
31
+ }
32
+
33
+ setContent(x, y, ch, style = null, combining = []) {
34
+ this.SetContent(x, y, ch, combining, style);
35
+ }
36
+
37
+ setFillerContent(x, y, style = null) {
38
+ this.cells.setFiller(x, y, style);
39
+ }
40
+
41
+ GetContent(x, y) {
42
+ return this.cells.getContent(x, y);
43
+ }
44
+
45
+ getContent(x, y) {
46
+ return this.GetContent(x, y);
47
+ }
48
+
49
+ Fill(ch = " ", style = null) {
50
+ this.cells.fill(ch, style);
51
+ }
52
+
53
+ fill(ch = " ", style = null) {
54
+ this.Fill(ch, style);
55
+ }
56
+
57
+ Show() {
58
+ const changes = this.cells.diff(this.previous);
59
+ let out = "\x1b[?25l";
60
+ let activeStyleKey = null;
61
+ for (const { x, y, cell } of changes) {
62
+ if (cell.filler) continue; // right-half of a wide char; the char itself already covers this column
63
+ out += this.move(y + 1, x + 1);
64
+ if (cell.styleKey !== activeStyleKey) {
65
+ out += styleToAnsi(cell.style ?? {});
66
+ activeStyleKey = cell.styleKey;
67
+ }
68
+ out += cell.ch + cell.combining.join("");
69
+ }
70
+ out += "\x1b[0m";
71
+ if (this.cursor && this.cursorVisible) {
72
+ // cursor shape: 1/2=block, 3/4=underline, 5/6=bar; odd=blink even=steady
73
+ const shape = this.cursor.shape === "bar" ? "\x1b[5 q" : this.cursor.shape === "steady-block" ? "\x1b[2 q" : "\x1b[1 q";
74
+ out += shape + this.move(this.cursor.y + 1, this.cursor.x + 1) + "\x1b[?25h";
75
+ } else out += "\x1b[?25l";
76
+ this.write(out);
77
+ this.previous = this.cells.clone();
78
+ }
79
+
80
+ show() {
81
+ this.Show();
82
+ }
83
+
84
+ setCursor(x, y, visible = true, shape = "block") {
85
+ this.cursor = { x, y, shape };
86
+ this.cursorVisible = visible;
87
+ }
88
+
89
+ hideCursor() {
90
+ this.cursorVisible = false;
91
+ this.write("\x1b[?25l");
92
+ }
93
+
94
+ showCursor() {
95
+ this.cursorVisible = true;
96
+ this.write("\x1b[?25h");
97
+ }
98
+
99
+ move(row, col) {
100
+ return `\x1b[${row};${col}H`;
101
+ }
102
+
103
+ clearToEndOfLine() {
104
+ return "\x1b[K";
105
+ }
106
+
107
+ updateSize() {
108
+ this.cols = process.stdout.columns || this.cols;
109
+ this.rows = process.stdout.rows || this.rows;
110
+ this.cells.resize(this.cols, this.rows);
111
+ this.previous = null;
112
+ return new ResizeEvent(this.cols, this.rows);
113
+ }
114
+
115
+ write(data) {
116
+ process.stdout.write(data);
117
+ }
118
+ }