herdr-remote 0.2.39 → 0.3.31

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 (203) hide show
  1. package/dist/chunks/353DMT35.js +3 -0
  2. package/dist/chunks/42KARAL4.js +31 -0
  3. package/dist/chunks/7AUQHB3O.js +10 -0
  4. package/dist/chunks/7SMZ43LI.js +9 -0
  5. package/dist/chunks/AM3OEMFK.js +16 -0
  6. package/dist/chunks/ANNJESOL.js +10 -0
  7. package/dist/chunks/AWB4A65P.js +63 -0
  8. package/dist/chunks/C2UUZY6J.js +2 -0
  9. package/dist/chunks/DGUZCGQN.js +3 -0
  10. package/dist/chunks/H24ZP5D5.js +16 -0
  11. package/dist/chunks/I6NRCYLI.js +3 -0
  12. package/dist/chunks/JJGEV4EZ.js +3 -0
  13. package/dist/chunks/KRSURB5L.js +5 -0
  14. package/dist/chunks/KRVF37OW.js +3 -0
  15. package/dist/chunks/KUDCC7ZO.js +6 -0
  16. package/dist/chunks/M3NRPJK2.js +77 -0
  17. package/dist/chunks/M4AFZHKH.js +3 -0
  18. package/dist/chunks/M52LJEES.js +6 -0
  19. package/dist/chunks/O2LNVFUT.js +3 -0
  20. package/dist/chunks/OJPBI7KB.js +3 -0
  21. package/dist/chunks/OMN6C7J6.js +3 -0
  22. package/dist/chunks/PBMBFWLQ.js +3 -0
  23. package/dist/chunks/PBRQ3HEG.js +3 -0
  24. package/dist/chunks/PFFVGFPL.js +45 -0
  25. package/dist/chunks/QRGNEKME.js +5 -0
  26. package/dist/chunks/TNLCNVA2.js +3 -0
  27. package/dist/chunks/U27JJKJ6.js +3 -0
  28. package/dist/chunks/U6QAYG3E.js +3 -0
  29. package/dist/chunks/UEOQE77N.js +4 -0
  30. package/dist/chunks/WSACSE45.js +4 -0
  31. package/dist/chunks/WY7WHEJ4.js +3 -0
  32. package/dist/chunks/XJKWLK43.js +4 -0
  33. package/dist/chunks/XN6D5S55.js +3 -0
  34. package/dist/chunks/YD3MIP7B.js +5 -0
  35. package/dist/chunks/YXDQ62BB.js +4 -0
  36. package/dist/cli.js +21 -294
  37. package/dist/connector/admin-broker.js +3 -303
  38. package/dist/connector/main.js +20 -18
  39. package/dist/harfbuzz-subset.wasm +0 -0
  40. package/dist/postinstall.js +17 -0
  41. package/herdr-plugin.toml +1 -1
  42. package/node_modules/herdr-remote-relay/bin/herdr-remote-relay.js +33 -92
  43. package/node_modules/herdr-remote-relay/package.json +3 -46
  44. package/package.json +8 -27
  45. package/dist/agent-status.js +0 -130
  46. package/dist/binary-plist.js +0 -157
  47. package/dist/config.js +0 -278
  48. package/dist/connector/admin-broker-setup.js +0 -40
  49. package/dist/connector/admin-shell.js +0 -67
  50. package/dist/connector/admin-tab.js +0 -90
  51. package/dist/connector/adminBrokerEndpoint.js +0 -68
  52. package/dist/connector/agent-watch.js +0 -123
  53. package/dist/connector/elevatedPty.js +0 -141
  54. package/dist/connector/font-server.js +0 -165
  55. package/dist/connector/herdr-prefix-keys.js +0 -28
  56. package/dist/connector/host-connector.js +0 -433
  57. package/dist/connector/host-hello.js +0 -32
  58. package/dist/connector/lock.js +0 -73
  59. package/dist/connector/sessions.js +0 -381
  60. package/dist/connector/update-report.js +0 -70
  61. package/dist/connector/wire.js +0 -42
  62. package/dist/exit-codes.js +0 -13
  63. package/dist/font-subset.js +0 -116
  64. package/dist/herdr-api.js +0 -254
  65. package/dist/herdr-command.js +0 -245
  66. package/dist/herdr-plugin.js +0 -120
  67. package/dist/herdr-server.js +0 -190
  68. package/dist/i18n/en.js +0 -257
  69. package/dist/i18n/index.js +0 -48
  70. package/dist/i18n/zh.js +0 -256
  71. package/dist/keepalive/detached.js +0 -97
  72. package/dist/keepalive/environment.js +0 -44
  73. package/dist/keepalive/index.js +0 -129
  74. package/dist/keepalive/launchd.js +0 -117
  75. package/dist/keepalive/systemd.js +0 -112
  76. package/dist/keepalive/types.js +0 -1
  77. package/dist/keepalive/windows-service-setup.js +0 -138
  78. package/dist/keepalive/windows-service.js +0 -272
  79. package/dist/keepalive/windows-task.js +0 -191
  80. package/dist/lib/process.js +0 -132
  81. package/dist/lifecycle.js +0 -79
  82. package/dist/net-interfaces.js +0 -82
  83. package/dist/node-pty-loader.js +0 -80
  84. package/dist/pasted-files.js +0 -115
  85. package/dist/paths.js +0 -74
  86. package/dist/pty-session.js +0 -133
  87. package/dist/relay-client.js +0 -106
  88. package/dist/relay-urls.js +0 -99
  89. package/dist/runtime.js +0 -107
  90. package/dist/service.js +0 -267
  91. package/dist/settings-model.js +0 -298
  92. package/dist/socket-discovery.js +0 -70
  93. package/dist/supervisor.js +0 -323
  94. package/dist/terminal-font/deps.js +0 -83
  95. package/dist/terminal-font/files.js +0 -357
  96. package/dist/terminal-font/formats.js +0 -355
  97. package/dist/terminal-font/index.js +0 -187
  98. package/dist/terminal-font/readers.js +0 -373
  99. package/dist/terminal-font/terminals.js +0 -147
  100. package/dist/terminal-palette.js +0 -399
  101. package/dist/tui/App.js +0 -199
  102. package/dist/tui/api.js +0 -91
  103. package/dist/tui/components/TextField.js +0 -50
  104. package/dist/tui/components/common.js +0 -80
  105. package/dist/tui/index.js +0 -36
  106. package/dist/tui/mouse/index.js +0 -164
  107. package/dist/tui/mouse/protocol.js +0 -96
  108. package/dist/tui/mouse/source.js +0 -79
  109. package/dist/tui/screens/About.js +0 -172
  110. package/dist/tui/screens/Herdr.js +0 -75
  111. package/dist/tui/screens/Keepalive.js +0 -141
  112. package/dist/tui/screens/Overview.js +0 -36
  113. package/dist/tui/screens/Pair.js +0 -74
  114. package/dist/tui/screens/Relay.js +0 -243
  115. package/dist/tui/screens/Services.js +0 -63
  116. package/dist/tui/screens/Wizard.js +0 -241
  117. package/dist/tui/terminal.js +0 -39
  118. package/dist/tui/theme.js +0 -33
  119. package/dist/update-command.js +0 -118
  120. package/dist/updater/in-place.js +0 -83
  121. package/dist/updater/npm.js +0 -62
  122. package/dist/updater.js +0 -401
  123. package/node_modules/herdr-remote-relay/README.md +0 -76
  124. package/node_modules/herdr-remote-relay/README.zh-CN.md +0 -76
  125. package/node_modules/herdr-remote-relay/config.example.json +0 -18
  126. package/node_modules/herdr-remote-relay/deploy/cloudflared/config.yml.example +0 -9
  127. package/node_modules/herdr-remote-relay/deploy/docker/compose.yaml +0 -23
  128. package/node_modules/herdr-remote-relay/deploy/nginx/herdr-remote-relay.conf.example +0 -49
  129. package/node_modules/herdr-remote-relay/deploy/systemd/herdr-remote-relay.service +0 -41
  130. package/node_modules/herdr-remote-relay/deploy/systemd/relay.env.example +0 -25
  131. package/node_modules/herdr-remote-relay/dist/auth-store.d.ts +0 -114
  132. package/node_modules/herdr-remote-relay/dist/auth-store.js +0 -289
  133. package/node_modules/herdr-remote-relay/dist/metrics.d.ts +0 -76
  134. package/node_modules/herdr-remote-relay/dist/metrics.js +0 -249
  135. package/node_modules/herdr-remote-relay/dist/protocol/frames.d.ts +0 -27
  136. package/node_modules/herdr-remote-relay/dist/protocol/frames.js +0 -110
  137. package/node_modules/herdr-remote-relay/dist/protocol/herdr-keys.d.ts +0 -8
  138. package/node_modules/herdr-remote-relay/dist/protocol/herdr-keys.js +0 -34
  139. package/node_modules/herdr-remote-relay/dist/protocol/http.d.ts +0 -165
  140. package/node_modules/herdr-remote-relay/dist/protocol/http.js +0 -3
  141. package/node_modules/herdr-remote-relay/dist/protocol/index.d.ts +0 -7
  142. package/node_modules/herdr-remote-relay/dist/protocol/index.js +0 -9
  143. package/node_modules/herdr-remote-relay/dist/protocol/messages.d.ts +0 -362
  144. package/node_modules/herdr-remote-relay/dist/protocol/messages.js +0 -20
  145. package/node_modules/herdr-remote-relay/dist/protocol/paste.d.ts +0 -14
  146. package/node_modules/herdr-remote-relay/dist/protocol/paste.js +0 -50
  147. package/node_modules/herdr-remote-relay/dist/protocol/release.d.ts +0 -8
  148. package/node_modules/herdr-remote-relay/dist/protocol/release.js +0 -17
  149. package/node_modules/herdr-remote-relay/dist/protocol/terminal.d.ts +0 -79
  150. package/node_modules/herdr-remote-relay/dist/protocol/terminal.js +0 -150
  151. package/node_modules/herdr-remote-relay/dist/relay-config.d.ts +0 -74
  152. package/node_modules/herdr-remote-relay/dist/relay-config.js +0 -316
  153. package/node_modules/herdr-remote-relay/dist/relay-server.d.ts +0 -67
  154. package/node_modules/herdr-remote-relay/dist/relay-server.js +0 -196
  155. package/node_modules/herdr-remote-relay/dist/scroll-input.d.ts +0 -17
  156. package/node_modules/herdr-remote-relay/dist/scroll-input.js +0 -124
  157. package/node_modules/herdr-remote-relay/dist/server/client-channel.d.ts +0 -3
  158. package/node_modules/herdr-remote-relay/dist/server/client-channel.js +0 -396
  159. package/node_modules/herdr-remote-relay/dist/server/font-proxy.d.ts +0 -19
  160. package/node_modules/herdr-remote-relay/dist/server/font-proxy.js +0 -155
  161. package/node_modules/herdr-remote-relay/dist/server/host-broadcasts.d.ts +0 -27
  162. package/node_modules/herdr-remote-relay/dist/server/host-broadcasts.js +0 -83
  163. package/node_modules/herdr-remote-relay/dist/server/host-channel.d.ts +0 -3
  164. package/node_modules/herdr-remote-relay/dist/server/host-channel.js +0 -271
  165. package/node_modules/herdr-remote-relay/dist/server/http-api.d.ts +0 -4
  166. package/node_modules/herdr-remote-relay/dist/server/http-api.js +0 -219
  167. package/node_modules/herdr-remote-relay/dist/server/maintenance.d.ts +0 -5
  168. package/node_modules/herdr-remote-relay/dist/server/maintenance.js +0 -64
  169. package/node_modules/herdr-remote-relay/dist/server/requests.d.ts +0 -34
  170. package/node_modules/herdr-remote-relay/dist/server/requests.js +0 -142
  171. package/node_modules/herdr-remote-relay/dist/server/sessions.d.ts +0 -36
  172. package/node_modules/herdr-remote-relay/dist/server/sessions.js +0 -230
  173. package/node_modules/herdr-remote-relay/dist/server/sockets.d.ts +0 -12
  174. package/node_modules/herdr-remote-relay/dist/server/sockets.js +0 -64
  175. package/node_modules/herdr-remote-relay/dist/server/status.d.ts +0 -11
  176. package/node_modules/herdr-remote-relay/dist/server/status.js +0 -99
  177. package/node_modules/herdr-remote-relay/dist/server/transport.d.ts +0 -16
  178. package/node_modules/herdr-remote-relay/dist/server/transport.js +0 -110
  179. package/node_modules/herdr-remote-relay/dist/server/types.d.ts +0 -133
  180. package/node_modules/herdr-remote-relay/dist/server/types.js +0 -3
  181. package/node_modules/herdr-remote-relay/dist/server/version.d.ts +0 -2
  182. package/node_modules/herdr-remote-relay/dist/server/version.js +0 -5
  183. package/node_modules/herdr-remote-relay/dist/state.d.ts +0 -6
  184. package/node_modules/herdr-remote-relay/dist/state.js +0 -47
  185. package/node_modules/ws/LICENSE +0 -20
  186. package/node_modules/ws/README.md +0 -548
  187. package/node_modules/ws/browser.js +0 -8
  188. package/node_modules/ws/index.js +0 -22
  189. package/node_modules/ws/lib/buffer-util.js +0 -131
  190. package/node_modules/ws/lib/constants.js +0 -19
  191. package/node_modules/ws/lib/event-target.js +0 -292
  192. package/node_modules/ws/lib/extension.js +0 -203
  193. package/node_modules/ws/lib/limiter.js +0 -55
  194. package/node_modules/ws/lib/permessage-deflate.js +0 -530
  195. package/node_modules/ws/lib/receiver.js +0 -743
  196. package/node_modules/ws/lib/sender.js +0 -607
  197. package/node_modules/ws/lib/stream.js +0 -161
  198. package/node_modules/ws/lib/subprotocol.js +0 -62
  199. package/node_modules/ws/lib/validation.js +0 -152
  200. package/node_modules/ws/lib/websocket-server.js +0 -562
  201. package/node_modules/ws/lib/websocket.js +0 -1407
  202. package/node_modules/ws/package.json +0 -74
  203. 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
- }