herdr-remote 0.2.39 → 0.3.32

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 (205) hide show
  1. package/README.md +4 -2
  2. package/README.zh-CN.md +2 -2
  3. package/dist/chunks/3FQAXVGC.js +3 -0
  4. package/dist/chunks/42KARAL4.js +31 -0
  5. package/dist/chunks/44IGVPMA.js +5 -0
  6. package/dist/chunks/5LZFJ6UZ.js +3 -0
  7. package/dist/chunks/7SMZ43LI.js +9 -0
  8. package/dist/chunks/AX4EPJCP.js +4 -0
  9. package/dist/chunks/B76DFI4H.js +16 -0
  10. package/dist/chunks/BYGDPZZD.js +3 -0
  11. package/dist/chunks/C2UUZY6J.js +2 -0
  12. package/dist/chunks/CVJ4HBMP.js +3 -0
  13. package/dist/chunks/DGUZCGQN.js +3 -0
  14. package/dist/chunks/FONUO5O5.js +4 -0
  15. package/dist/chunks/H24ZP5D5.js +16 -0
  16. package/dist/chunks/I6NRCYLI.js +3 -0
  17. package/dist/chunks/JJGEV4EZ.js +3 -0
  18. package/dist/chunks/K5RFPHSF.js +5 -0
  19. package/dist/chunks/KGOQKOHH.js +5 -0
  20. package/dist/chunks/KRVF37OW.js +3 -0
  21. package/dist/chunks/KUDCC7ZO.js +6 -0
  22. package/dist/chunks/LPR662JH.js +77 -0
  23. package/dist/chunks/LY2ZGCXN.js +4 -0
  24. package/dist/chunks/M52LJEES.js +6 -0
  25. package/dist/chunks/MCUVFSSK.js +3 -0
  26. package/dist/chunks/MEFDV375.js +10 -0
  27. package/dist/chunks/MKAEXKXG.js +10 -0
  28. package/dist/chunks/O2LNVFUT.js +3 -0
  29. package/dist/chunks/PBMBFWLQ.js +3 -0
  30. package/dist/chunks/PBRQ3HEG.js +3 -0
  31. package/dist/chunks/QCSILMFB.js +3 -0
  32. package/dist/chunks/QRGNEKME.js +5 -0
  33. package/dist/chunks/RMHH3IAX.js +63 -0
  34. package/dist/chunks/TNLCNVA2.js +3 -0
  35. package/dist/chunks/U6QAYG3E.js +3 -0
  36. package/dist/chunks/VD54DF7R.js +45 -0
  37. package/dist/chunks/WY7WHEJ4.js +3 -0
  38. package/dist/cli.js +21 -294
  39. package/dist/connector/admin-broker.js +3 -303
  40. package/dist/connector/main.js +20 -18
  41. package/dist/harfbuzz-subset.wasm +0 -0
  42. package/dist/postinstall.js +17 -0
  43. package/herdr-plugin.toml +1 -1
  44. package/node_modules/herdr-remote-relay/bin/herdr-remote-relay.js +33 -92
  45. package/node_modules/herdr-remote-relay/package.json +3 -46
  46. package/package.json +8 -27
  47. package/dist/agent-status.js +0 -130
  48. package/dist/binary-plist.js +0 -157
  49. package/dist/config.js +0 -278
  50. package/dist/connector/admin-broker-setup.js +0 -40
  51. package/dist/connector/admin-shell.js +0 -67
  52. package/dist/connector/admin-tab.js +0 -90
  53. package/dist/connector/adminBrokerEndpoint.js +0 -68
  54. package/dist/connector/agent-watch.js +0 -123
  55. package/dist/connector/elevatedPty.js +0 -141
  56. package/dist/connector/font-server.js +0 -165
  57. package/dist/connector/herdr-prefix-keys.js +0 -28
  58. package/dist/connector/host-connector.js +0 -433
  59. package/dist/connector/host-hello.js +0 -32
  60. package/dist/connector/lock.js +0 -73
  61. package/dist/connector/sessions.js +0 -381
  62. package/dist/connector/update-report.js +0 -70
  63. package/dist/connector/wire.js +0 -42
  64. package/dist/exit-codes.js +0 -13
  65. package/dist/font-subset.js +0 -116
  66. package/dist/herdr-api.js +0 -254
  67. package/dist/herdr-command.js +0 -245
  68. package/dist/herdr-plugin.js +0 -120
  69. package/dist/herdr-server.js +0 -190
  70. package/dist/i18n/en.js +0 -257
  71. package/dist/i18n/index.js +0 -48
  72. package/dist/i18n/zh.js +0 -256
  73. package/dist/keepalive/detached.js +0 -97
  74. package/dist/keepalive/environment.js +0 -44
  75. package/dist/keepalive/index.js +0 -129
  76. package/dist/keepalive/launchd.js +0 -117
  77. package/dist/keepalive/systemd.js +0 -112
  78. package/dist/keepalive/types.js +0 -1
  79. package/dist/keepalive/windows-service-setup.js +0 -138
  80. package/dist/keepalive/windows-service.js +0 -272
  81. package/dist/keepalive/windows-task.js +0 -191
  82. package/dist/lib/process.js +0 -132
  83. package/dist/lifecycle.js +0 -79
  84. package/dist/net-interfaces.js +0 -82
  85. package/dist/node-pty-loader.js +0 -80
  86. package/dist/pasted-files.js +0 -115
  87. package/dist/paths.js +0 -74
  88. package/dist/pty-session.js +0 -133
  89. package/dist/relay-client.js +0 -106
  90. package/dist/relay-urls.js +0 -99
  91. package/dist/runtime.js +0 -107
  92. package/dist/service.js +0 -267
  93. package/dist/settings-model.js +0 -298
  94. package/dist/socket-discovery.js +0 -70
  95. package/dist/supervisor.js +0 -323
  96. package/dist/terminal-font/deps.js +0 -83
  97. package/dist/terminal-font/files.js +0 -357
  98. package/dist/terminal-font/formats.js +0 -355
  99. package/dist/terminal-font/index.js +0 -187
  100. package/dist/terminal-font/readers.js +0 -373
  101. package/dist/terminal-font/terminals.js +0 -147
  102. package/dist/terminal-palette.js +0 -399
  103. package/dist/tui/App.js +0 -199
  104. package/dist/tui/api.js +0 -91
  105. package/dist/tui/components/TextField.js +0 -50
  106. package/dist/tui/components/common.js +0 -80
  107. package/dist/tui/index.js +0 -36
  108. package/dist/tui/mouse/index.js +0 -164
  109. package/dist/tui/mouse/protocol.js +0 -96
  110. package/dist/tui/mouse/source.js +0 -79
  111. package/dist/tui/screens/About.js +0 -172
  112. package/dist/tui/screens/Herdr.js +0 -75
  113. package/dist/tui/screens/Keepalive.js +0 -141
  114. package/dist/tui/screens/Overview.js +0 -36
  115. package/dist/tui/screens/Pair.js +0 -74
  116. package/dist/tui/screens/Relay.js +0 -243
  117. package/dist/tui/screens/Services.js +0 -63
  118. package/dist/tui/screens/Wizard.js +0 -241
  119. package/dist/tui/terminal.js +0 -39
  120. package/dist/tui/theme.js +0 -33
  121. package/dist/update-command.js +0 -118
  122. package/dist/updater/in-place.js +0 -83
  123. package/dist/updater/npm.js +0 -62
  124. package/dist/updater.js +0 -401
  125. package/node_modules/herdr-remote-relay/README.md +0 -76
  126. package/node_modules/herdr-remote-relay/README.zh-CN.md +0 -76
  127. package/node_modules/herdr-remote-relay/config.example.json +0 -18
  128. package/node_modules/herdr-remote-relay/deploy/cloudflared/config.yml.example +0 -9
  129. package/node_modules/herdr-remote-relay/deploy/docker/compose.yaml +0 -23
  130. package/node_modules/herdr-remote-relay/deploy/nginx/herdr-remote-relay.conf.example +0 -49
  131. package/node_modules/herdr-remote-relay/deploy/systemd/herdr-remote-relay.service +0 -41
  132. package/node_modules/herdr-remote-relay/deploy/systemd/relay.env.example +0 -25
  133. package/node_modules/herdr-remote-relay/dist/auth-store.d.ts +0 -114
  134. package/node_modules/herdr-remote-relay/dist/auth-store.js +0 -289
  135. package/node_modules/herdr-remote-relay/dist/metrics.d.ts +0 -76
  136. package/node_modules/herdr-remote-relay/dist/metrics.js +0 -249
  137. package/node_modules/herdr-remote-relay/dist/protocol/frames.d.ts +0 -27
  138. package/node_modules/herdr-remote-relay/dist/protocol/frames.js +0 -110
  139. package/node_modules/herdr-remote-relay/dist/protocol/herdr-keys.d.ts +0 -8
  140. package/node_modules/herdr-remote-relay/dist/protocol/herdr-keys.js +0 -34
  141. package/node_modules/herdr-remote-relay/dist/protocol/http.d.ts +0 -165
  142. package/node_modules/herdr-remote-relay/dist/protocol/http.js +0 -3
  143. package/node_modules/herdr-remote-relay/dist/protocol/index.d.ts +0 -7
  144. package/node_modules/herdr-remote-relay/dist/protocol/index.js +0 -9
  145. package/node_modules/herdr-remote-relay/dist/protocol/messages.d.ts +0 -362
  146. package/node_modules/herdr-remote-relay/dist/protocol/messages.js +0 -20
  147. package/node_modules/herdr-remote-relay/dist/protocol/paste.d.ts +0 -14
  148. package/node_modules/herdr-remote-relay/dist/protocol/paste.js +0 -50
  149. package/node_modules/herdr-remote-relay/dist/protocol/release.d.ts +0 -8
  150. package/node_modules/herdr-remote-relay/dist/protocol/release.js +0 -17
  151. package/node_modules/herdr-remote-relay/dist/protocol/terminal.d.ts +0 -79
  152. package/node_modules/herdr-remote-relay/dist/protocol/terminal.js +0 -150
  153. package/node_modules/herdr-remote-relay/dist/relay-config.d.ts +0 -74
  154. package/node_modules/herdr-remote-relay/dist/relay-config.js +0 -316
  155. package/node_modules/herdr-remote-relay/dist/relay-server.d.ts +0 -67
  156. package/node_modules/herdr-remote-relay/dist/relay-server.js +0 -196
  157. package/node_modules/herdr-remote-relay/dist/scroll-input.d.ts +0 -17
  158. package/node_modules/herdr-remote-relay/dist/scroll-input.js +0 -124
  159. package/node_modules/herdr-remote-relay/dist/server/client-channel.d.ts +0 -3
  160. package/node_modules/herdr-remote-relay/dist/server/client-channel.js +0 -396
  161. package/node_modules/herdr-remote-relay/dist/server/font-proxy.d.ts +0 -19
  162. package/node_modules/herdr-remote-relay/dist/server/font-proxy.js +0 -155
  163. package/node_modules/herdr-remote-relay/dist/server/host-broadcasts.d.ts +0 -27
  164. package/node_modules/herdr-remote-relay/dist/server/host-broadcasts.js +0 -83
  165. package/node_modules/herdr-remote-relay/dist/server/host-channel.d.ts +0 -3
  166. package/node_modules/herdr-remote-relay/dist/server/host-channel.js +0 -271
  167. package/node_modules/herdr-remote-relay/dist/server/http-api.d.ts +0 -4
  168. package/node_modules/herdr-remote-relay/dist/server/http-api.js +0 -219
  169. package/node_modules/herdr-remote-relay/dist/server/maintenance.d.ts +0 -5
  170. package/node_modules/herdr-remote-relay/dist/server/maintenance.js +0 -64
  171. package/node_modules/herdr-remote-relay/dist/server/requests.d.ts +0 -34
  172. package/node_modules/herdr-remote-relay/dist/server/requests.js +0 -142
  173. package/node_modules/herdr-remote-relay/dist/server/sessions.d.ts +0 -36
  174. package/node_modules/herdr-remote-relay/dist/server/sessions.js +0 -230
  175. package/node_modules/herdr-remote-relay/dist/server/sockets.d.ts +0 -12
  176. package/node_modules/herdr-remote-relay/dist/server/sockets.js +0 -64
  177. package/node_modules/herdr-remote-relay/dist/server/status.d.ts +0 -11
  178. package/node_modules/herdr-remote-relay/dist/server/status.js +0 -99
  179. package/node_modules/herdr-remote-relay/dist/server/transport.d.ts +0 -16
  180. package/node_modules/herdr-remote-relay/dist/server/transport.js +0 -110
  181. package/node_modules/herdr-remote-relay/dist/server/types.d.ts +0 -133
  182. package/node_modules/herdr-remote-relay/dist/server/types.js +0 -3
  183. package/node_modules/herdr-remote-relay/dist/server/version.d.ts +0 -2
  184. package/node_modules/herdr-remote-relay/dist/server/version.js +0 -5
  185. package/node_modules/herdr-remote-relay/dist/state.d.ts +0 -6
  186. package/node_modules/herdr-remote-relay/dist/state.js +0 -47
  187. package/node_modules/ws/LICENSE +0 -20
  188. package/node_modules/ws/README.md +0 -548
  189. package/node_modules/ws/browser.js +0 -8
  190. package/node_modules/ws/index.js +0 -22
  191. package/node_modules/ws/lib/buffer-util.js +0 -131
  192. package/node_modules/ws/lib/constants.js +0 -19
  193. package/node_modules/ws/lib/event-target.js +0 -292
  194. package/node_modules/ws/lib/extension.js +0 -203
  195. package/node_modules/ws/lib/limiter.js +0 -55
  196. package/node_modules/ws/lib/permessage-deflate.js +0 -530
  197. package/node_modules/ws/lib/receiver.js +0 -743
  198. package/node_modules/ws/lib/sender.js +0 -607
  199. package/node_modules/ws/lib/stream.js +0 -161
  200. package/node_modules/ws/lib/subprotocol.js +0 -62
  201. package/node_modules/ws/lib/validation.js +0 -152
  202. package/node_modules/ws/lib/websocket-server.js +0 -562
  203. package/node_modules/ws/lib/websocket.js +0 -1407
  204. package/node_modules/ws/package.json +0 -74
  205. package/node_modules/ws/wrapper.mjs +0 -21
@@ -1,355 +0,0 @@
1
- // The formats terminal settings come in: INI, GVariant, Pango and Qt font
2
- // strings, fontconfig patterns, JSONC, a TOML subset and X resources.
3
- import { pointsToPx, positiveNumber } from './deps.js';
4
- /** `[section]` / `key=value` files: konsolerc, kdeglobals, terminalrc, foot.ini. */
5
- export function parseIni(text) {
6
- const sections = { '': {} };
7
- let current = sections[''];
8
- for (const rawLine of String(text || '').split(/\r?\n/)) {
9
- const line = rawLine.trim();
10
- if (!line || line.startsWith('#') || line.startsWith(';'))
11
- continue;
12
- const header = /^\[(.+)\]$/.exec(line);
13
- if (header) {
14
- current = sections[header[1].trim()] ||= {};
15
- continue;
16
- }
17
- const equals = line.indexOf('=');
18
- if (equals === -1)
19
- continue;
20
- current[line.slice(0, equals).trim()] = line.slice(equals + 1).trim();
21
- }
22
- return sections;
23
- }
24
- /** A GSettings value as printed by `gsettings get`: `'text'`, `true`, `@as []`. */
25
- export function parseGVariantString(output) {
26
- const text = String(output || '').trim();
27
- const quoted = /^'((?:[^'\\]|\\.)*)'$/.exec(text) || /^"((?:[^"\\]|\\.)*)"$/.exec(text);
28
- if (!quoted)
29
- return null;
30
- return quoted[1].replace(/\\(.)/g, '$1');
31
- }
32
- const PANGO_STYLE_WORDS = new Set([
33
- 'normal',
34
- 'roman',
35
- 'oblique',
36
- 'italic',
37
- 'small-caps',
38
- 'all-small-caps',
39
- 'petite-caps',
40
- 'all-petite-caps',
41
- 'unicase',
42
- 'title-caps',
43
- 'ultra-condensed',
44
- 'extra-condensed',
45
- 'condensed',
46
- 'semi-condensed',
47
- 'semi-expanded',
48
- 'expanded',
49
- 'extra-expanded',
50
- 'ultra-expanded',
51
- 'thin',
52
- 'ultra-light',
53
- 'extra-light',
54
- 'light',
55
- 'semi-light',
56
- 'demi-light',
57
- 'book',
58
- 'regular',
59
- 'medium',
60
- 'semi-bold',
61
- 'demi-bold',
62
- 'bold',
63
- 'ultra-bold',
64
- 'extra-bold',
65
- 'heavy',
66
- 'black',
67
- 'ultra-black',
68
- 'extra-black',
69
- 'ultra-heavy',
70
- 'extra-heavy',
71
- 'not-rotated',
72
- 'south',
73
- 'upside-down',
74
- 'north',
75
- 'rotated-left',
76
- 'east',
77
- 'rotated-right',
78
- 'west',
79
- ]);
80
- /**
81
- * `JetBrainsMono Nerd Font Bold 9` → family and size.
82
- *
83
- * Pango writes `FAMILY-LIST [STYLE-OPTIONS] [SIZE[px]] [@VARIATIONS]`: the
84
- * size and the style words are peeled off the end, and what is left is the
85
- * family list, whose first entry is the face the terminal asked for.
86
- */
87
- export function parsePangoFontDescription(description) {
88
- if (typeof description !== 'string')
89
- return null;
90
- const words = description.trim().split(/\s+/).filter(Boolean);
91
- while (words.length && words[words.length - 1].startsWith('@'))
92
- words.pop();
93
- let sizePx;
94
- const size = /^(\d+(?:\.\d+)?)(px)?$/i.exec(words[words.length - 1] || '');
95
- if (size && words.length > 1) {
96
- words.pop();
97
- sizePx = size[2] ? positiveNumber(size[1]) : pointsToPx(size[1]);
98
- }
99
- while (words.length > 1) {
100
- const last = words[words.length - 1].toLowerCase();
101
- if (!PANGO_STYLE_WORDS.has(last) && !/^weight=\d+$/.test(last))
102
- break;
103
- words.pop();
104
- }
105
- const family = words.join(' ').split(',')[0].trim();
106
- return family ? { family, sizePx } : null;
107
- }
108
- /** Qt's `QFont::toString()`: `JetBrains Mono,10,-1,5,400,0,...`. */
109
- export function parseQtFontString(value) {
110
- if (typeof value !== 'string')
111
- return null;
112
- const parts = value.split(',');
113
- const family = (parts[0] || '').trim();
114
- if (!family)
115
- return null;
116
- const points = Number(parts[1]);
117
- const pixels = Number(parts[2]);
118
- const sizePx = points > 0 ? pointsToPx(points) : pixels > 0 ? pixels : undefined;
119
- return { family, sizePx };
120
- }
121
- /** A fontconfig pattern as foot writes it: `JetBrains Mono:size=10:weight=bold`. */
122
- export function parseFontconfigPattern(value) {
123
- if (typeof value !== 'string')
124
- return null;
125
- const first = value.split(/(?<!\\),/)[0];
126
- const [rawFamily, ...properties] = first.split(/(?<!\\):/);
127
- const family = rawFamily.replace(/\\(.)/g, '$1').trim();
128
- if (!family)
129
- return null;
130
- let sizePx;
131
- for (const property of properties) {
132
- const [key, raw] = property.split('=');
133
- if (key === 'size')
134
- sizePx = pointsToPx(raw);
135
- else if (key === 'pixelsize')
136
- sizePx = positiveNumber(raw);
137
- }
138
- return { family, sizePx };
139
- }
140
- /** The first entry of a CSS font-family list: `'Fira Code', monospace` → `Fira Code`. */
141
- export function firstCssFamily(value) {
142
- if (typeof value !== 'string')
143
- return null;
144
- const first = value
145
- .split(',')[0]
146
- .trim()
147
- .replace(/^(['"])(.*)\1$/, '$2')
148
- .trim();
149
- return first || null;
150
- }
151
- /** JSON with comments and trailing commas, as VS Code writes its settings. */
152
- export function parseJsonc(text) {
153
- if (typeof text !== 'string')
154
- return null;
155
- let out = '';
156
- let inString = false;
157
- for (let i = 0; i < text.length; i += 1) {
158
- const char = text[i];
159
- if (inString) {
160
- out += char;
161
- if (char === '\\') {
162
- out += text[i + 1] || '';
163
- i += 1;
164
- }
165
- else if (char === '"')
166
- inString = false;
167
- continue;
168
- }
169
- if (char === '"') {
170
- inString = true;
171
- out += char;
172
- continue;
173
- }
174
- if (char === '/' && text[i + 1] === '/') {
175
- while (i < text.length && text[i] !== '\n')
176
- i += 1;
177
- out += '\n';
178
- continue;
179
- }
180
- if (char === '/' && text[i + 1] === '*') {
181
- i += 2;
182
- while (i < text.length && !(text[i] === '*' && text[i + 1] === '/'))
183
- i += 1;
184
- i += 1;
185
- continue;
186
- }
187
- out += char;
188
- }
189
- try {
190
- return JSON.parse(out.replace(/,(\s*[}\]])/g, '$1'));
191
- }
192
- catch {
193
- return null;
194
- }
195
- }
196
- /** `line` up to a `#` that starts a comment, not one inside a string. */
197
- function withoutTomlComment(line) {
198
- let quote = '';
199
- for (let i = 0; i < line.length; i += 1) {
200
- const char = line[i];
201
- if (quote) {
202
- if (char === '\\' && quote === '"')
203
- i += 1;
204
- else if (char === quote)
205
- quote = '';
206
- }
207
- else if (char === '"' || char === "'")
208
- quote = char;
209
- else if (char === '#')
210
- return line.slice(0, i);
211
- }
212
- return line;
213
- }
214
- /**
215
- * Just enough TOML for Alacritty's font keys and Herdr's `keys.prefix`:
216
- * tables, dotted keys, strings, numbers, inline tables and (possibly
217
- * multi-line) string arrays. Returns a flat `dotted.key → value` map.
218
- */
219
- export function parseTomlSubset(text) {
220
- const values = {};
221
- const lines = String(text || '').split(/\r?\n/);
222
- let table = '';
223
- const unquote = (raw) => {
224
- const value = raw.trim();
225
- const basic = /^"((?:[^"\\]|\\.)*)"/.exec(value);
226
- if (basic)
227
- return basic[1].replace(/\\(.)/g, '$1');
228
- const literal = /^'([^']*)'/.exec(value);
229
- if (literal)
230
- return literal[1];
231
- const number = Number(value.replace(/#.*$/, '').trim());
232
- return Number.isFinite(number) ? number : undefined;
233
- };
234
- const keyPath = (raw) => raw.split('.').map((part) => part.trim().replace(/^(['"])(.*)\1$/, '$2'));
235
- const assign = (prefix, rawKey, rawValue) => {
236
- const key = [...(prefix ? [prefix] : []), ...keyPath(rawKey)].join('.');
237
- const value = rawValue.trim();
238
- if (value.startsWith('{')) {
239
- const body = value.slice(1, value.lastIndexOf('}'));
240
- for (const pair of body.split(/,(?=(?:[^"']|"[^"]*"|'[^']*')*$)/)) {
241
- const equals = pair.indexOf('=');
242
- if (equals !== -1)
243
- assign(key, pair.slice(0, equals), pair.slice(equals + 1));
244
- }
245
- return;
246
- }
247
- if (value.startsWith('[')) {
248
- values[key] = [...value.matchAll(/"((?:[^"\\]|\\.)*)"|'([^']*)'/g)].map((m) => m[1] ?? m[2]);
249
- return;
250
- }
251
- const parsed = unquote(value);
252
- if (parsed !== undefined)
253
- values[key] = parsed;
254
- };
255
- for (let index = 0; index < lines.length; index += 1) {
256
- const line = lines[index].trim();
257
- if (!line || line.startsWith('#'))
258
- continue;
259
- const header = /^\[([^[\]]+)\]/.exec(line);
260
- if (header) {
261
- table = keyPath(header[1]).join('.');
262
- continue;
263
- }
264
- if (line.startsWith('[[')) {
265
- table = '\0';
266
- continue;
267
- }
268
- if (table === '\0')
269
- continue;
270
- const equals = line.indexOf('=');
271
- if (equals === -1)
272
- continue;
273
- let rawValue = line.slice(equals + 1);
274
- if (rawValue.trim().startsWith('[')) {
275
- // A comment inside an array may hold quotes or a `]` of its own.
276
- rawValue = withoutTomlComment(rawValue);
277
- // An array may span lines; gather it before assigning.
278
- while (index + 1 < lines.length && !rawValue.includes(']')) {
279
- index += 1;
280
- rawValue += ` ${withoutTomlComment(lines[index]).trim()}`;
281
- }
282
- }
283
- assign(table, line.slice(0, equals), rawValue);
284
- }
285
- return values;
286
- }
287
- /** The last value given to any of `names` on a command line. */
288
- export function optionValue(args, names) {
289
- let value = null;
290
- for (let i = 0; i < args.length - 1; i += 1) {
291
- if (names.includes(args[i]))
292
- value = args[i + 1];
293
- }
294
- return value;
295
- }
296
- /** `-xrm 'XTerm*faceName: Hack'` entries, in the shape `xResources` returns. */
297
- export function xrmResources(args) {
298
- const entries = [];
299
- for (let i = 0; i < args.length - 1; i += 1) {
300
- if (args[i] !== '-xrm')
301
- continue;
302
- const colon = args[i + 1].indexOf(':');
303
- if (colon !== -1)
304
- entries.push([args[i + 1].slice(0, colon).trim(), args[i + 1].slice(colon + 1).trim()]);
305
- }
306
- return entries;
307
- }
308
- /**
309
- * The value X would give `<class>.<...>.<resource>`, simplified to what
310
- * terminal users write: `XTerm*faceName`, `xterm.vt100.faceName`, `*faceName`.
311
- * A named class wins over the bare wildcard.
312
- */
313
- export function xResource(resources, classes, resource) {
314
- let named = null;
315
- let wildcard = null;
316
- // Later entries win: `-xrm` options are appended after the database.
317
- for (const [name, value] of resources) {
318
- const parts = name.split(/[.*]/).filter(Boolean);
319
- if (parts[parts.length - 1] !== resource || !value)
320
- continue;
321
- if (parts.length === 1)
322
- wildcard = value;
323
- else if (classes.includes(parts[0]))
324
- named = value;
325
- }
326
- return named ?? wildcard;
327
- }
328
- /**
329
- * An X terminal's font name: `xft:JetBrains Mono:size=11` (FreeType, any list
330
- * takes its first entry) or an XLFD `-misc-dejavu sans mono-medium-r-normal--14-…`.
331
- * A bare core-font alias (`fixed`, `9x15`) names a bitmap font: no answer —
332
- * except in xterm's `faceName`, which is always a FreeType pattern.
333
- */
334
- export function parseXFontName(value, { bareIsPattern = false } = {}) {
335
- const first = String(value || '')
336
- .split(/,(?=\s*(?:xft:|-))/)[0]
337
- .trim();
338
- if (/^xft:/i.test(first))
339
- return parseFontconfigPattern(first.slice(4));
340
- if (first.startsWith('-')) {
341
- const fields = first.split('-');
342
- const family = (fields[2] || '').trim();
343
- if (!family || family === '*' || /^(fixed|misc)$/i.test(family))
344
- return null;
345
- const pixels = Number(fields[7]);
346
- const decipoints = Number(fields[8]);
347
- return {
348
- family,
349
- sizePx: pixels > 0 ? pixels : decipoints > 0 ? pointsToPx(decipoints / 10) : undefined,
350
- };
351
- }
352
- if (!first)
353
- return null;
354
- return bareIsPattern || first.includes(':') ? parseFontconfigPattern(first) : null;
355
- }
@@ -1,187 +0,0 @@
1
- /**
2
- * Reads the font the workstation's terminal draws with.
3
- *
4
- * Colors can be asked for with OSC queries (see terminal-palette.js); a font
5
- * cannot. No escape sequence that mainstream emulators answer reports the
6
- * family, so the host works out which terminal it is running in and reads that
7
- * terminal's own settings — the same settings dialog the user edited.
8
- *
9
- * The answer travels to the browser in `host_hello` as a family name, a size,
10
- * and the font files the browser may fetch by hash, so a phone draws the
11
- * session in the face the workstation shows even though it has no such font
12
- * installed. Nothing here guesses: a terminal this module does not know yields
13
- * `null`, and the browser keeps its own monospace stack.
14
- */
15
- import fs from 'node:fs';
16
- import { sanitizeTerminalFont, TERMINAL_FONT_CHUNK_BYTES, } from 'herdr-remote-relay/protocol';
17
- import { readRuntime, writeRuntime } from '../runtime.js';
18
- import { withDefaults } from './deps.js';
19
- import { concreteFamily, resolveFontFaces, resolveSubsetSources, } from './files.js';
20
- import { READERS } from './readers.js';
21
- import { identifyTerminal, processArguments, processLineage, terminalProcess, } from './terminals.js';
22
- /** Reads one terminal's configured font: `{ source, family, sizePx }` or null. */
23
- export function readTerminalFont(source, deps = {}) {
24
- const resolved = withDefaults(deps);
25
- const reader = READERS[source];
26
- if (!reader)
27
- return null;
28
- let font;
29
- try {
30
- font = reader(resolved);
31
- }
32
- catch {
33
- return null;
34
- }
35
- if (!font?.family)
36
- return null;
37
- const family = concreteFamily(font.family, resolved);
38
- if (!family)
39
- return null;
40
- return sanitizeDetected({ source, family, sizePx: font.sizePx });
41
- }
42
- /** Identifies the terminal and reads its font. */
43
- export function detectTerminalFont({ env = process.env, ancestry, lineage, deps = {}, } = {}) {
44
- const processes = lineage ||
45
- (ancestry ? ancestry.map((name) => ({ pid: 0, name })) : processLineage({ run: deps.run }));
46
- const source = identifyTerminal({ env, ancestry: processes.map((entry) => entry.name) });
47
- if (!source)
48
- return null;
49
- // A terminal configured on its command line (xterm's `-fa`) says so there.
50
- const emulator = terminalProcess(source, processes);
51
- const terminalArgs = deps.terminalArgs || (emulator?.pid ? processArguments(emulator.pid) : []);
52
- return readTerminalFont(source, { ...deps, env, terminalArgs });
53
- }
54
- /** What detection reports, before any files are attached. */
55
- function sanitizeDetected(value) {
56
- const clean = sanitizeTerminalFont(value);
57
- if (!clean)
58
- return null;
59
- const detected = { source: clean.source || null, family: clean.family };
60
- if (clean.sizePx)
61
- detected.sizePx = clean.sizePx;
62
- return detected;
63
- }
64
- /** The full record a connector serves from: detection plus local files. */
65
- function describeTerminalFont(detected, deps = {}) {
66
- if (!detected?.family)
67
- return null;
68
- const faces = resolveFontFaces(detected.family, deps);
69
- return { ...detected, faces, subsets: resolveSubsetSources(detected.family, faces, deps) };
70
- }
71
- /** The same record with the file paths removed: what may leave this machine. */
72
- export function publicTerminalFont(record) {
73
- return record ? sanitizeTerminalFont(record) : null;
74
- }
75
- /**
76
- * One slice of a face, by hash. The file is re-checked against what was
77
- * announced, so an edited font is refused rather than spliced into a browser's
78
- * copy of the old one.
79
- */
80
- export function readFontChunk(record, sha256, index) {
81
- const face = record?.faces?.find((candidate) => candidate.sha256 === sha256);
82
- if (!face)
83
- return null;
84
- const total = Math.ceil(face.bytes / TERMINAL_FONT_CHUNK_BYTES);
85
- if (!Number.isInteger(index) || index < 0 || index >= total)
86
- return null;
87
- let fd;
88
- try {
89
- const stat = fs.statSync(face.path);
90
- if (stat.size !== face.bytes || stat.mtimeMs !== face.mtimeMs)
91
- return null;
92
- fd = fs.openSync(face.path, 'r');
93
- const start = index * TERMINAL_FONT_CHUNK_BYTES;
94
- const length = Math.min(TERMINAL_FONT_CHUNK_BYTES, face.bytes - start);
95
- const buffer = Buffer.alloc(length);
96
- const read = fs.readSync(fd, buffer, 0, length, start);
97
- if (read !== length)
98
- return null;
99
- return { data: buffer, total };
100
- }
101
- catch {
102
- return null;
103
- }
104
- finally {
105
- if (fd !== undefined)
106
- try {
107
- fs.closeSync(fd);
108
- }
109
- catch {
110
- // Closing a read-only descriptor cannot lose data.
111
- }
112
- }
113
- }
114
- /** Reads a font a parent process already detected. */
115
- export function fontFromEnvironment(env = process.env) {
116
- const raw = env.HERDR_TERM_FONT_JSON;
117
- if (!raw)
118
- return null;
119
- try {
120
- return sanitizeDetected(JSON.parse(raw));
121
- }
122
- catch {
123
- return null;
124
- }
125
- }
126
- /**
127
- * Remembers the font for a start that has no terminal around it: a service
128
- * manager's, or Herdr's detached server's.
129
- */
130
- function rememberTerminalFont(detected) {
131
- const clean = sanitizeDetected(detected);
132
- if (!clean)
133
- return null;
134
- try {
135
- const state = readRuntime();
136
- const previous = state.terminalFont;
137
- if (!previous || JSON.stringify(previous) !== JSON.stringify(clean)) {
138
- writeRuntime({ ...state, terminalFont: clean });
139
- }
140
- }
141
- catch {
142
- // A font is a nicety; failing to remember it must not break a start.
143
- }
144
- return clean;
145
- }
146
- function rememberedTerminalFont() {
147
- try {
148
- return sanitizeDetected(readRuntime().terminalFont);
149
- }
150
- catch {
151
- return null;
152
- }
153
- }
154
- /**
155
- * Detect the terminal's font at the entry point, where the terminal's
156
- * variables and process ancestry are still this process's own, and hand the
157
- * answer to everything this command starts.
158
- */
159
- export function captureTerminalFont({ env = process.env, detect = detectTerminalFont, } = {}) {
160
- if (env.HERDR_TERM_FONT_JSON)
161
- return fontFromEnvironment(env);
162
- const detected = detect({ env });
163
- if (!detected)
164
- return null;
165
- env.HERDR_TERM_FONT_JSON = JSON.stringify(detected);
166
- rememberTerminalFont(detected);
167
- return detected;
168
- }
169
- /** The font a service start hands its children: inherited, else remembered. */
170
- export function resolveHostFont({ env = process.env } = {}) {
171
- return fontFromEnvironment(env) || rememberedTerminalFont();
172
- }
173
- /**
174
- * What the connector serves: the terminal's settings read again now, when the
175
- * terminal is known, so a font changed since the last start is not reported
176
- * stale; otherwise what was handed down. `refresh` also re-identifies a
177
- * terminal nobody had named, for a browser that asks to sync.
178
- */
179
- export function loadHostTerminalFont({ env = process.env, refresh = false, deps = {}, } = {}) {
180
- const known = resolveHostFont({ env });
181
- let current = known?.source ? readTerminalFont(known.source, { ...deps, env }) : null;
182
- if (!current && refresh && !known)
183
- current = detectTerminalFont({ env, deps });
184
- if (current && JSON.stringify(current) !== JSON.stringify(known))
185
- rememberTerminalFont(current);
186
- return describeTerminalFont(current || known, deps);
187
- }