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,99 @@
1
+ export const DEFAULT_STYLE = {
2
+ fg: "default",
3
+ bg: "default",
4
+ bold: false,
5
+ italic: false,
6
+ reverse: false,
7
+ underline: false,
8
+ };
9
+
10
+ const COLOR_LINK = /color-link\s+(\S*)\s+"(.*)"/;
11
+ const INCLUDE = /include\s+"(.*)"/;
12
+
13
+ export class Colorscheme {
14
+ constructor(runtime) {
15
+ this.runtime = runtime;
16
+ this.styles = new Map();
17
+ this.defaultStyle = { ...DEFAULT_STYLE };
18
+ }
19
+
20
+ async load(name = "default", parsed = new Set()) {
21
+ const file = this.runtime.find(0, name);
22
+ if (!file) throw new Error(`${name} is not a valid colorscheme`);
23
+ parsed.add(name);
24
+ const parsedStyles = await this.parse(name, await file.text(), parsed);
25
+ this.styles = parsedStyles;
26
+ return this;
27
+ }
28
+
29
+ async parse(name, text, parsed = new Set()) {
30
+ const styles = new Map();
31
+ for (const rawLine of String(text).split("\n")) {
32
+ const line = rawLine.trim();
33
+ if (!line || line.startsWith("#")) continue;
34
+
35
+ const include = line.match(INCLUDE);
36
+ if (include) {
37
+ const includeName = include[1];
38
+ if (!parsed.has(includeName)) {
39
+ const file = this.runtime.find(0, includeName);
40
+ if (!file) throw new Error(`${includeName} is not a valid colorscheme`);
41
+ parsed.add(includeName);
42
+ const includedStyles = await this.parse(includeName, await file.text(), parsed);
43
+ for (const [key, value] of includedStyles) styles.set(key, value);
44
+ }
45
+ continue;
46
+ }
47
+
48
+ const match = line.match(COLOR_LINK);
49
+ if (!match) throw new Error(`Color-link statement is not valid: ${rawLine}`);
50
+ const style = stringToStyle(match[2], this.defaultStyle);
51
+ styles.set(match[1], style);
52
+ if (match[1] === "default") this.defaultStyle = style;
53
+ }
54
+ if (styles.has("default")) this.defaultStyle = styles.get("default");
55
+ return styles;
56
+ }
57
+
58
+ get(group) {
59
+ if (!group) return this.defaultStyle;
60
+ const parts = String(group).split(".");
61
+ let style = this.styles.get(group);
62
+ if (parts.length > 1) {
63
+ let cur = "";
64
+ for (const part of parts) {
65
+ cur = cur ? `${cur}.${part}` : part;
66
+ if (this.styles.has(cur)) style = this.styles.get(cur);
67
+ }
68
+ }
69
+ return style ?? stringToStyle(group, this.defaultStyle);
70
+ }
71
+ }
72
+
73
+ export function stringToStyle(input, base = DEFAULT_STYLE) {
74
+ const text = String(input);
75
+ const colorPart = text.split(/\s+/).at(-1) ?? "";
76
+ const [fgRaw = "default", bgRaw = "default"] = colorPart.split(",");
77
+ return {
78
+ fg: stringToColor(fgRaw.trim(), base.fg),
79
+ bg: stringToColor(bgRaw.trim(), base.bg),
80
+ bold: base.bold || text.includes("bold"),
81
+ italic: base.italic || text.includes("italic"),
82
+ reverse: base.reverse || text.includes("reverse"),
83
+ underline: base.underline || text.includes("underline"),
84
+ };
85
+ }
86
+
87
+ export function stringToColor(value, fallback = "default") {
88
+ const color = String(value || "default").trim();
89
+ if (color === "default" || color === "") return fallback;
90
+ if (/^\d+$/.test(color)) return Number(color);
91
+ if (/^#[0-9a-fA-F]{6}$/.test(color)) return color;
92
+ const names = new Set([
93
+ "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white",
94
+ "brightblack", "brightred", "brightgreen", "brightyellow", "brightblue",
95
+ "brightmagenta", "brightcyan", "brightwhite", "lightblack", "lightred",
96
+ "lightgreen", "lightyellow", "lightblue", "lightmagenta", "lightcyan", "lightwhite",
97
+ ]);
98
+ return names.has(color) ? color : fallback;
99
+ }
@@ -0,0 +1,122 @@
1
+ import { existsSync } from "node:fs";
2
+ import { mkdir, readFile } from "node:fs/promises";
3
+ import { homedir } from "node:os";
4
+ import { join } from "node:path";
5
+ //import { Glob } from "bun";
6
+ import { defaultAllSettings, OPTION_CHOICES, LOCAL_SETTINGS } from "./defaults.js";
7
+ import { isHex3Encoding, isMdcuiEncoding } from "../runtime/encodings.js";
8
+
9
+ export class Config {
10
+ constructor({ configDir = "" } = {}) {
11
+ this.configDir = configDir || defaultConfigDir();
12
+ this.globalSettings = defaultAllSettings();
13
+ this.volatileSettings = new Set();
14
+ this.modifiedSettings = new Set();
15
+ this.parsedSettings = {};
16
+ }
17
+
18
+ async init() {
19
+ await mkdir(this.configDir, { recursive: true });
20
+ await this.readSettings();
21
+ return this;
22
+ }
23
+
24
+ async readSettings() {
25
+ const path = join(this.configDir, "settings.json");
26
+ if (!existsSync(path)) return;
27
+ const text = await readFile(path, "utf8");
28
+ if (text.trimStart().startsWith("null") || text.trim() === "") return;
29
+ this.parsedSettings = Bun.JSON5.parse(text);
30
+ this.applyParsedSettings(this.parsedSettings);
31
+ }
32
+
33
+ applyCliSettings(settings) {
34
+ for (const [key, raw] of settings) {
35
+ if (!(key in this.globalSettings)) continue;
36
+ this.setGlobalOptionNative(key, parseSetting(raw), { volatile: true });
37
+ }
38
+ }
39
+
40
+ applyParsedSettings(parsed) {
41
+ for (const [key, value] of Object.entries(parsed)) {
42
+ if (key.startsWith("ft:")) continue;
43
+ if (key.startsWith("glob:")) {
44
+ //new Glob(key.slice(5));
45
+ continue;
46
+ }
47
+ if (!(key in this.globalSettings)) continue;
48
+ this.setGlobalOptionNative(key, normalizeSetting(key, value), { modified: false });
49
+ }
50
+ }
51
+
52
+ registerCommonOption(plugin, option, value) {
53
+ this.registerGlobalOption(`${plugin}.${option}`, value);
54
+ }
55
+
56
+ registerGlobalOption(option, value) {
57
+ if (!(option in this.globalSettings)) this.globalSettings[option] = value;
58
+ if (Object.hasOwn(this.parsedSettings, option)) {
59
+ this.setGlobalOptionNative(option, normalizeSetting(option, this.parsedSettings[option]), { modified: false });
60
+ }
61
+ }
62
+
63
+ getGlobalOption(option) {
64
+ return this.globalSettings[option];
65
+ }
66
+
67
+ setGlobalOptionNative(option, value, { volatile = false, modified = true } = {}) {
68
+ if (!(option in this.globalSettings)) throw new Error(`Invalid option: ${option}`);
69
+ validateOption(option, value);
70
+ this.globalSettings[option] = value;
71
+ if (volatile) this.volatileSettings.add(option);
72
+ if (modified) this.modifiedSettings.add(option);
73
+ }
74
+
75
+ async saveSettings() {
76
+ const path = join(this.configDir, "settings.json");
77
+ const out = { ...this.parsedSettings };
78
+ for (const key of this.modifiedSettings) {
79
+ if (!this.volatileSettings.has(key) && !LOCAL_SETTINGS.has(key)) out[key] = this.globalSettings[key];
80
+ }
81
+ // Remove any local-only settings that may have leaked into parsedSettings previously.
82
+ for (const key of LOCAL_SETTINGS) delete out[key];
83
+ await Bun.write(path, JSON.stringify(out, null, " ") + "\n");
84
+ this.parsedSettings = { ...out };
85
+ }
86
+ }
87
+
88
+ export function defaultConfigDir() {
89
+ if (process.env.MICRO_CONFIG_HOME) return process.env.MICRO_CONFIG_HOME;
90
+ const xdg = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
91
+ return join(xdg, "micro");
92
+ }
93
+
94
+ export function parseSetting(value) {
95
+ if (value === "true" || value === "on") return true;
96
+ if (value === "false" || value === "off") return false;
97
+ if (/^-?\d+(\.\d+)?$/.test(value)) return Number(value);
98
+ return value;
99
+ }
100
+
101
+ function normalizeSetting(key, value) {
102
+ if (key === "autosave" && typeof value === "boolean") return value ? 8 : 0;
103
+ return value;
104
+ }
105
+
106
+ function validateOption(option, value) {
107
+ if (option === "encoding") {
108
+ const encoding = String(value || "utf-8");
109
+ if (isMdcuiEncoding(encoding)) return;
110
+ if (isHex3Encoding(encoding)) return;
111
+ try { new TextDecoder(encoding); }
112
+ catch { throw new Error(`Invalid encoding: ${value}`); }
113
+ }
114
+ const choices = OPTION_CHOICES[option];
115
+ if (choices && !choices.includes(value)) {
116
+ throw new Error(`Invalid value for ${option}: ${value}`);
117
+ }
118
+ if (["autosave", "colorcolumn", "detectlimit", "pageoverlap", "scrollmargin", "scrollspeed"].includes(option) && Number(value) < 0) {
119
+ throw new Error(`${option} must be non-negative`);
120
+ }
121
+ if (option === "tabsize" && Number(value) <= 0) throw new Error("tabsize must be positive");
122
+ }
@@ -0,0 +1,109 @@
1
+ export const DEFAULT_COMMON_SETTINGS = {
2
+ autoindent: true,
3
+ autosu: false,
4
+ backup: true,
5
+ backupdir: "",
6
+ basename: false,
7
+ colorcolumn: 0,
8
+ cursorline: true,
9
+ detectlimit: 100,
10
+ diffgutter: false,
11
+ encoding: "utf-8",
12
+ eofnewline: true,
13
+ fastdirty: false,
14
+ fileformat: defaultFileFormat(),
15
+ filetype: "unknown",
16
+ hlsearch: false,
17
+ hltaberrors: false,
18
+ hltrailingws: false,
19
+ ignorecase: true,
20
+ incsearch: true,
21
+ indentchar: " ",
22
+ keepautoindent: false,
23
+ matchbrace: true,
24
+ matchbraceleft: true,
25
+ matchbracestyle: "underline",
26
+ mkparents: false,
27
+ pageoverlap: 2,
28
+ permbackup: false,
29
+ readonly: false,
30
+ relativeruler: false,
31
+ reload: "prompt",
32
+ rmtrailingws: false,
33
+ ruler: true,
34
+ savecursor: false,
35
+ saveundo: false,
36
+ scrollbar: false,
37
+ scrollmargin: 3,
38
+ scrollspeed: 2,
39
+ showchars: "",
40
+ smartpaste: true,
41
+ softwrap: false,
42
+ splitbottom: true,
43
+ splitright: true,
44
+ statusformatl: "$(filename) $(modified)$(overwrite)($(line),$(col)) $(status.paste)| ft:$(opt:filetype) | $(opt:fileformat) | $(opt:encoding)",
45
+ statusformatr: "$(bind:ToggleKeyMenu): bindings, $(bind:ToggleHelp): help",
46
+ statusline: true,
47
+ syntax: true,
48
+ tabmovement: false,
49
+ tabsize: 4,
50
+ tabstospaces: false,
51
+ truecolor: "auto",
52
+ useprimary: true,
53
+ wordwrap: false,
54
+ };
55
+
56
+ export const DEFAULT_GLOBAL_ONLY_SETTINGS = {
57
+ autosave: 0,
58
+ clipboard: "external",
59
+ colorscheme: "default",
60
+ cursorshape: "block",
61
+ savehistory: true,
62
+ divchars: "|-",
63
+ divreverse: true,
64
+ fakecursor: process.platform === "win32",
65
+ helpsplit: "hsplit",
66
+ infobar: true,
67
+ keymenu: false,
68
+ lockbindings: false,
69
+ mouse: true,
70
+ multiopen: "tab",
71
+ parsecursor: false,
72
+ paste: false,
73
+ pluginchannels: ["https://raw.githubusercontent.com/micro-editor/plugin-channel/master/channel.json"],
74
+ pluginrepos: [],
75
+ savehistory: true,
76
+ scrollbarchar: "|",
77
+ sucmd: "sudo",
78
+ tabhighlight: false,
79
+ tabreverse: true,
80
+ xterm: false,
81
+ };
82
+
83
+ export const OPTION_CHOICES = {
84
+ clipboard: ["internal", "external", "terminal"],
85
+ cursorshape: [
86
+ "default",
87
+ "block", "blinking-block",
88
+ "underline", "blinking-underline",
89
+ "bar", "blinking-bar",
90
+ ],
91
+ fileformat: ["unix", "dos"],
92
+ helpsplit: ["hsplit", "vsplit"],
93
+ matchbracestyle: ["underline", "highlight"],
94
+ multiopen: ["tab", "hsplit", "vsplit"],
95
+ reload: ["prompt", "auto", "disabled"],
96
+ truecolor: ["auto", "on", "off"],
97
+ };
98
+
99
+ // Settings that are buffer-local only and must never be written to the global config file.
100
+ // Mirrors Go micro's config.LocalSettings.
101
+ export const LOCAL_SETTINGS = new Set(["readonly", "filetype", "fileformat"]);
102
+
103
+ export function defaultAllSettings() {
104
+ return { ...DEFAULT_COMMON_SETTINGS, ...DEFAULT_GLOBAL_ONLY_SETTINGS };
105
+ }
106
+
107
+ function defaultFileFormat() {
108
+ return process.platform === "win32" ? "dos" : "unix";
109
+ }
@@ -0,0 +1,259 @@
1
+
2
+
3
+
4
+
5
+
6
+ const crlf="\r\n"
7
+
8
+ let apilist = new Map()
9
+
10
+ export const jss = JSON.stringify
11
+
12
+
13
+ export async function evalBack(backmod, qjson)
14
+ {
15
+
16
+ try{
17
+
18
+
19
+
20
+ /* Contract of requestJson(qjson)
21
+ type RpcPacket = [
22
+ func: string,
23
+ argv: unknown[],
24
+ envp?: Record<string, unknown>,
25
+ ];
26
+ */
27
+
28
+ // DiscoverApi ApiCaller
29
+
30
+
31
+ let [ func, argv, envp ] = qjson
32
+
33
+ func = (func || '')+'' ;
34
+
35
+ if(process.env.RPC_DEBUG)
36
+ console.log(func);
37
+
38
+ if(!apilist.get(backmod))
39
+ apilist.set(backmod,DiscoverApi(backmod));
40
+
41
+ const apilistMod = apilist.get(backmod)
42
+
43
+ if(func=="_discover")
44
+ {
45
+ return apilistMod ;
46
+ }
47
+ else if(apilistMod[func])
48
+ {
49
+ if(Array.isArray(argv))
50
+ return await backmod[func]?.apply?.(envp,argv);
51
+ else
52
+ return await ApiCaller(backmod,func,argv,envp);
53
+ }
54
+ else
55
+ {
56
+ return "Unknown func 未知函式: "+func+
57
+ crlf+JSON.stringify(argv);
58
+ }
59
+
60
+
61
+
62
+ }catch(e)
63
+ {
64
+ console.log(e);
65
+ return (e.stack);
66
+ }
67
+
68
+ } // end of evalBack
69
+
70
+
71
+ const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;
72
+
73
+ function safeFrontError(e)
74
+ {
75
+ return {
76
+ ok: false,
77
+ error: e?.stack || String(e),
78
+ };
79
+ }
80
+
81
+ function safeFrontValue(value)
82
+ {
83
+ if (typeof value !== "function") return value;
84
+ return function(...args) {
85
+ try {
86
+ const result = value.apply(this, args);
87
+ if (result && typeof result.then === "function")
88
+ return result.catch(safeFrontError);
89
+ return result;
90
+ } catch(e) {
91
+ return safeFrontError(e);
92
+ }
93
+ };
94
+ }
95
+
96
+ export async function evalFront(mod, text)
97
+ {
98
+ try{
99
+
100
+ text = text.replace(/^javascript:/, "");
101
+
102
+ const names = Object.keys(mod);
103
+ const values = Object.values(mod).map(safeFrontValue);
104
+
105
+ try {
106
+ return await new AsyncFunction(...names, `return await (${text})`)(...values);
107
+ } catch(e) {
108
+ if (e instanceof SyntaxError)
109
+ return await new AsyncFunction(...names, text)(...values);
110
+ throw e;
111
+ }
112
+
113
+ }catch(e)
114
+ {
115
+ return safeFrontError(e);
116
+ }
117
+
118
+ }
119
+
120
+
121
+ export const rpcraw = async (func,argv,envp)=>{
122
+
123
+ const apilistMod = await FrontendDiscoverApi()
124
+
125
+ if(apilistMod[func])
126
+ return await fetch("rpc", {
127
+ method: "POST",
128
+ body: JSON.stringify([
129
+ func,argv,envp
130
+ ])
131
+ }).then(r=>r.json()).catch(e=>e) ;
132
+ else if(func=="_discover")
133
+ {
134
+ if(argv[0])
135
+ return jss(apilistMod,null,1) ;
136
+ else
137
+ return apilistMod ;
138
+ }
139
+ else
140
+ return "Unknown func 未知函式: "+func+
141
+ crlf+JSON.stringify(argv);
142
+ }
143
+
144
+ export const rpcproxy = new Proxy(rpcraw,{
145
+ get(target, prop, receiver) {
146
+ if (prop in target) {
147
+ return Reflect.get(target, prop, receiver);
148
+ }
149
+
150
+ if (typeof prop !== "string") {
151
+ return undefined;
152
+ }
153
+
154
+ target[prop] = async function(...argv) {
155
+ await FrontendDiscoverApi()
156
+ return await target(prop, argv, this);
157
+ };
158
+
159
+ return target[prop];
160
+ },
161
+ ownKeys() {
162
+ return Object.keys(apilist.get(0)||{});
163
+ },
164
+ getOwnPropertyDescriptor(target, prop) {
165
+ const apilistMod = apilist.get(0)||{};
166
+
167
+ if(prop in apilistMod) {
168
+ return {
169
+ enumerable: true,
170
+ configurable: true,
171
+ };
172
+ }
173
+
174
+ return Reflect.getOwnPropertyDescriptor(target, prop);
175
+ }
176
+ });
177
+
178
+ export var rpc = rpcproxy
179
+
180
+
181
+
182
+ // functions cannot contain ( ) ,
183
+ export function getfuncparams( func )
184
+ {
185
+ if( typeof(func) != "function" )
186
+ return [];
187
+
188
+ let rs = func.toString();
189
+ let m = rs.match( /\(([\s\S]*?)\)/ );
190
+ if( !m ) return [];
191
+ rs = m[1] ;
192
+
193
+ return rs.split(",").map(i=>i.trim()) ;
194
+ }
195
+
196
+
197
+ export async function FrontendDiscoverApi()
198
+ {
199
+ if(!apilist.get(0))
200
+ apilist.set(0, await fetch("rpc", {
201
+ method: "POST",
202
+ body: JSON.stringify(["_discover"])
203
+ }).then(r=>r.json()).catch(e=>e) ) ;
204
+
205
+ return apilist.get(0);
206
+ }
207
+
208
+
209
+ // filters out _ starting functions
210
+ export function DiscoverApi( obj ) // module_obj
211
+ {
212
+ let karr = Object.keys( obj );
213
+ karr = karr
214
+ .filter( i=>( !i.startsWith('_') &&
215
+ typeof(obj[i]) == "function" ) )
216
+ .map(i=>{
217
+ const fname = obj[i].name || obj[i].Name ;
218
+
219
+ if( fname && fname != i )
220
+ return [ i, [[ fname ]] ];
221
+
222
+ return [ i, getfuncparams(obj[i]) ];
223
+ });
224
+
225
+ return Object.fromEntries( karr ) ;
226
+ }
227
+
228
+
229
+
230
+ /*
231
+ * Module object from import * as modobj from 'mod.mjs'
232
+ * Parameters object
233
+ */
234
+ export async function ApiCaller( modobj, cmd, pobj,envp )
235
+ {
236
+ const func = modobj[cmd] ;
237
+
238
+ if(!pobj) pobj = {} ;
239
+
240
+ if( func && typeof(func) == "function" )
241
+ {
242
+ if(!func.args)
243
+ {
244
+ func.args = getfuncparams( func ) ;
245
+ func.args = func.args.map(i=>{
246
+ let ret = i.split("=")[0]
247
+ ret=(ret||"")+""
248
+ return ret.trim();
249
+ });
250
+ }
251
+
252
+ let parr = func.args.map(i=>pobj[i]) ;
253
+
254
+ return await func.apply(envp,parr) ;
255
+ }
256
+ else
257
+ return "Failed: No such function as" +
258
+ crlf + cmd ;
259
+ }
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { evalBack } from "./rpc.mjs";
4
+
5
+ const csl = console.log
6
+ const mda = Bun.markdown.ansi
7
+
8
+
9
+ import homepage from "./testapp.md.html"
10
+ import * as backmod from "./testapp.md.back.js";
11
+
12
+
13
+ /*
14
+ // Wait until Bun fixes dynamic html import bundle
15
+
16
+ process.env.PUBLIC_MDPATH ||= "testapp.md"
17
+
18
+ const { default: homepage } =
19
+ await import(
20
+ "./" +
21
+ process.env.PUBLIC_MDPATH +
22
+ ".html"
23
+ ) ;
24
+
25
+ const backmod =
26
+ await import(
27
+ './' +
28
+ process.env.PUBLIC_MDPATH +
29
+ ".back.js"
30
+ ) ;
31
+
32
+ */
33
+
34
+
35
+ function json(data, init = {}) {
36
+ return Response.json(data, {
37
+ headers: {
38
+ "access-control-allow-origin": "*",
39
+ ...init.headers,
40
+ },
41
+ ...init,
42
+ });
43
+ }
44
+
45
+
46
+ export function main()
47
+ {
48
+
49
+
50
+
51
+ const basePath = "/" + crypto.randomUUID()
52
+ const pathname = basePath + "/"
53
+ const server = Bun.serve({
54
+ port: Number(process.env.PORT || 3000),
55
+ development: {
56
+ hmr: true,
57
+ console: true,
58
+ },
59
+ routes: {
60
+ [basePath]: (req) => Response.redirect(new URL(pathname, req.url), 308),
61
+ [pathname]: homepage,
62
+
63
+ [pathname + "rpc"]: async (req) => {
64
+ csl("\n"+mda("## "+req.method+" "+req.url))
65
+
66
+ if (req.method === "OPTIONS") {
67
+ return new Response(null, {
68
+ headers: {
69
+ "access-control-allow-origin": "*",
70
+ "access-control-allow-methods": "POST,OPTIONS",
71
+ "access-control-allow-headers": "content-type",
72
+ },
73
+ });
74
+ }
75
+
76
+ if (req.method !== "POST") {
77
+ csl('illegal method')
78
+ return new Response("Method Not Allowed", { status: 405 });
79
+ }
80
+
81
+ try {
82
+ const packet = await req.json();
83
+
84
+ csl('req json:',packet)
85
+
86
+ const result = await evalBack(backmod, packet);
87
+
88
+ return json(result ?? null);
89
+ } catch (error) {
90
+ return json(
91
+ {
92
+ ok: false,
93
+ error: error?.stack || String(error),
94
+ },
95
+ { status: 400 },
96
+ );
97
+ }
98
+ },
99
+ },
100
+
101
+ fetch() {
102
+ return new Response("Not Found", { status: 404 });
103
+ },
104
+ });
105
+
106
+ console.log(mda("- Bun RPC server listening on"));
107
+ console.log(`http://localhost:${server.port}${pathname}`);
108
+
109
+
110
+
111
+ return server
112
+
113
+ } // main
114
+
115
+ if(import.meta.main)
116
+ main()