green-screen-react 0.3.0 → 1.0.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.
- package/README.md +66 -97
- package/dist/index.d.mts +112 -99
- package/dist/index.d.ts +112 -99
- package/dist/index.js +559 -204
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +527 -178
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +28 -1
- package/package.json +7 -3
- package/LICENSE +0 -21
package/dist/index.js
CHANGED
|
@@ -20,10 +20,18 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
AlertTriangleIcon: () => AlertTriangleIcon,
|
|
23
24
|
GreenScreenTerminal: () => GreenScreenTerminal,
|
|
25
|
+
InlineSignIn: () => InlineSignIn,
|
|
26
|
+
KeyIcon: () => KeyIcon,
|
|
27
|
+
MinimizeIcon: () => MinimizeIcon,
|
|
28
|
+
RefreshIcon: () => RefreshIcon,
|
|
24
29
|
RestAdapter: () => RestAdapter,
|
|
25
|
-
TN5250Terminal: () => TN5250Terminal,
|
|
26
30
|
TerminalBootLoader: () => TerminalBootLoader,
|
|
31
|
+
TerminalIcon: () => TerminalIcon,
|
|
32
|
+
WebSocketAdapter: () => WebSocketAdapter,
|
|
33
|
+
WifiIcon: () => WifiIcon,
|
|
34
|
+
WifiOffIcon: () => WifiOffIcon,
|
|
27
35
|
getProtocolProfile: () => getProtocolProfile,
|
|
28
36
|
getRowColorClass: () => getRowColorClass,
|
|
29
37
|
hp6530Profile: () => hp6530Profile,
|
|
@@ -32,9 +40,6 @@ __export(index_exports, {
|
|
|
32
40
|
positionToRowCol: () => positionToRowCol,
|
|
33
41
|
tn3270Profile: () => tn3270Profile,
|
|
34
42
|
tn5250Profile: () => tn5250Profile,
|
|
35
|
-
useTN5250Connection: () => useTN5250Connection,
|
|
36
|
-
useTN5250Screen: () => useTN5250Screen,
|
|
37
|
-
useTN5250Terminal: () => useTN5250Terminal,
|
|
38
43
|
useTerminalConnection: () => useTerminalConnection,
|
|
39
44
|
useTerminalInput: () => useTerminalInput,
|
|
40
45
|
useTerminalScreen: () => useTerminalScreen,
|
|
@@ -44,9 +49,295 @@ __export(index_exports, {
|
|
|
44
49
|
module.exports = __toCommonJS(index_exports);
|
|
45
50
|
|
|
46
51
|
// src/components/GreenScreenTerminal.tsx
|
|
47
|
-
var
|
|
52
|
+
var import_react5 = require("react");
|
|
53
|
+
|
|
54
|
+
// src/adapters/RestAdapter.ts
|
|
55
|
+
var RestAdapter = class {
|
|
56
|
+
constructor(options) {
|
|
57
|
+
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
58
|
+
this.staticHeaders = options.headers || {};
|
|
59
|
+
this.getHeaders = options.getHeaders;
|
|
60
|
+
}
|
|
61
|
+
async buildHeaders() {
|
|
62
|
+
const dynamic = this.getHeaders ? await this.getHeaders() : {};
|
|
63
|
+
return {
|
|
64
|
+
"Content-Type": "application/json",
|
|
65
|
+
...this.staticHeaders,
|
|
66
|
+
...dynamic
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
async request(method, path, body) {
|
|
70
|
+
const headers = await this.buildHeaders();
|
|
71
|
+
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
72
|
+
method,
|
|
73
|
+
headers,
|
|
74
|
+
body: body ? JSON.stringify(body) : void 0
|
|
75
|
+
});
|
|
76
|
+
if (!response.ok) {
|
|
77
|
+
const detail = await response.json().catch(() => ({}));
|
|
78
|
+
throw new Error(detail?.detail || `HTTP ${response.status}`);
|
|
79
|
+
}
|
|
80
|
+
return response.json();
|
|
81
|
+
}
|
|
82
|
+
async getScreen() {
|
|
83
|
+
try {
|
|
84
|
+
return await this.request("GET", "/screen");
|
|
85
|
+
} catch (e) {
|
|
86
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
87
|
+
if (message.includes("503") || message.includes("404")) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
throw e;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async getStatus() {
|
|
94
|
+
return this.request("GET", "/status");
|
|
95
|
+
}
|
|
96
|
+
async sendText(text) {
|
|
97
|
+
return this.request("POST", "/send-text", { text });
|
|
98
|
+
}
|
|
99
|
+
async sendKey(key) {
|
|
100
|
+
return this.request("POST", "/send-key", { key });
|
|
101
|
+
}
|
|
102
|
+
async connect(config) {
|
|
103
|
+
return this.request("POST", "/connect", config);
|
|
104
|
+
}
|
|
105
|
+
async disconnect() {
|
|
106
|
+
return this.request("POST", "/disconnect");
|
|
107
|
+
}
|
|
108
|
+
async reconnect() {
|
|
109
|
+
return this.request("POST", "/reconnect");
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// src/adapters/WebSocketAdapter.ts
|
|
114
|
+
var import_meta = {};
|
|
115
|
+
var WebSocketAdapter = class _WebSocketAdapter {
|
|
116
|
+
constructor(options = {}) {
|
|
117
|
+
this.ws = null;
|
|
118
|
+
this.screen = null;
|
|
119
|
+
this.status = { connected: false, status: "disconnected" };
|
|
120
|
+
this.pendingScreenResolver = null;
|
|
121
|
+
this.pendingConnectResolver = null;
|
|
122
|
+
this.connectingPromise = null;
|
|
123
|
+
this.screenListeners = /* @__PURE__ */ new Set();
|
|
124
|
+
this.statusListeners = /* @__PURE__ */ new Set();
|
|
125
|
+
this._sessionId = null;
|
|
126
|
+
this.workerUrl = (options.workerUrl || _WebSocketAdapter.detectEnvUrl() || "http://localhost:3001").replace(/\/+$/, "");
|
|
127
|
+
}
|
|
128
|
+
static detectEnvUrl() {
|
|
129
|
+
try {
|
|
130
|
+
if (typeof import_meta !== "undefined" && import_meta.env) {
|
|
131
|
+
const env = import_meta.env;
|
|
132
|
+
return env.VITE_GREEN_SCREEN_URL || env.VITE_WORKER_URL || void 0;
|
|
133
|
+
}
|
|
134
|
+
} catch {
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
const p = typeof globalThis !== "undefined" && globalThis.process;
|
|
138
|
+
if (p && p.env) {
|
|
139
|
+
return p.env.NEXT_PUBLIC_GREEN_SCREEN_URL || p.env.REACT_APP_GREEN_SCREEN_URL || void 0;
|
|
140
|
+
}
|
|
141
|
+
} catch {
|
|
142
|
+
}
|
|
143
|
+
return void 0;
|
|
144
|
+
}
|
|
145
|
+
/** The proxy-side session ID (available after connect or reattach) */
|
|
146
|
+
get sessionId() {
|
|
147
|
+
return this._sessionId;
|
|
148
|
+
}
|
|
149
|
+
/** Subscribe to real-time screen updates */
|
|
150
|
+
onScreen(listener) {
|
|
151
|
+
this.screenListeners.add(listener);
|
|
152
|
+
return () => this.screenListeners.delete(listener);
|
|
153
|
+
}
|
|
154
|
+
/** Subscribe to status changes */
|
|
155
|
+
onStatus(listener) {
|
|
156
|
+
this.statusListeners.add(listener);
|
|
157
|
+
return () => this.statusListeners.delete(listener);
|
|
158
|
+
}
|
|
159
|
+
async getScreen() {
|
|
160
|
+
return this.screen;
|
|
161
|
+
}
|
|
162
|
+
async getStatus() {
|
|
163
|
+
return this.status;
|
|
164
|
+
}
|
|
165
|
+
async sendText(text) {
|
|
166
|
+
return this.sendAndWaitForScreen({ type: "text", text });
|
|
167
|
+
}
|
|
168
|
+
async sendKey(key) {
|
|
169
|
+
return this.sendAndWaitForScreen({ type: "key", key });
|
|
170
|
+
}
|
|
171
|
+
async connect(config) {
|
|
172
|
+
await this.ensureWebSocket();
|
|
173
|
+
if (!config) {
|
|
174
|
+
return { success: false, error: "ConnectConfig required" };
|
|
175
|
+
}
|
|
176
|
+
return new Promise((resolve) => {
|
|
177
|
+
const timeout = setTimeout(() => {
|
|
178
|
+
this.pendingConnectResolver = null;
|
|
179
|
+
resolve({ success: false, error: "Connection timeout" });
|
|
180
|
+
}, 3e4);
|
|
181
|
+
this.pendingConnectResolver = (result) => {
|
|
182
|
+
clearTimeout(timeout);
|
|
183
|
+
resolve(result);
|
|
184
|
+
};
|
|
185
|
+
this.wsSend({
|
|
186
|
+
type: "connect",
|
|
187
|
+
host: config.host,
|
|
188
|
+
port: config.port,
|
|
189
|
+
protocol: config.protocol,
|
|
190
|
+
username: config.username,
|
|
191
|
+
password: config.password
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Reattach to an existing proxy session (e.g. after page reload).
|
|
197
|
+
* The proxy keeps the TCP connection alive; this just reconnects the
|
|
198
|
+
* WebSocket and receives the current screen.
|
|
199
|
+
*/
|
|
200
|
+
async reattach(sessionId) {
|
|
201
|
+
await this.ensureWebSocket();
|
|
202
|
+
return new Promise((resolve) => {
|
|
203
|
+
const timeout = setTimeout(() => {
|
|
204
|
+
this.pendingConnectResolver = null;
|
|
205
|
+
resolve({ success: false, error: "Reattach timeout" });
|
|
206
|
+
}, 1e4);
|
|
207
|
+
this.pendingConnectResolver = (result) => {
|
|
208
|
+
clearTimeout(timeout);
|
|
209
|
+
resolve(result);
|
|
210
|
+
};
|
|
211
|
+
this.wsSend({ type: "reattach", sessionId });
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
async disconnect() {
|
|
215
|
+
this.wsSend({ type: "disconnect" });
|
|
216
|
+
this.status = { connected: false, status: "disconnected" };
|
|
217
|
+
this._sessionId = null;
|
|
218
|
+
if (this.ws) {
|
|
219
|
+
this.ws.close();
|
|
220
|
+
this.ws = null;
|
|
221
|
+
}
|
|
222
|
+
return { success: true };
|
|
223
|
+
}
|
|
224
|
+
async reconnect() {
|
|
225
|
+
return { success: false, error: "Use disconnect() then connect() instead" };
|
|
226
|
+
}
|
|
227
|
+
/** Close the WebSocket without sending disconnect (session stays alive on proxy) */
|
|
228
|
+
dispose() {
|
|
229
|
+
if (this.ws) {
|
|
230
|
+
this.ws.close();
|
|
231
|
+
this.ws = null;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
async ensureWebSocket() {
|
|
235
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) return;
|
|
236
|
+
if (this.connectingPromise) return this.connectingPromise;
|
|
237
|
+
this.connectingPromise = new Promise((resolve, reject) => {
|
|
238
|
+
const wsUrl = this.workerUrl.replace(/^http/, "ws") + "/ws";
|
|
239
|
+
this.ws = new WebSocket(wsUrl);
|
|
240
|
+
this.ws.onopen = () => resolve();
|
|
241
|
+
this.ws.onerror = () => reject(new Error("WebSocket connection failed"));
|
|
242
|
+
this.ws.onmessage = (event) => {
|
|
243
|
+
try {
|
|
244
|
+
const msg = JSON.parse(event.data);
|
|
245
|
+
this.handleMessage(msg);
|
|
246
|
+
} catch {
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
this.ws.onclose = () => {
|
|
250
|
+
this.status = { connected: false, status: "disconnected" };
|
|
251
|
+
for (const listener of this.statusListeners) listener(this.status);
|
|
252
|
+
};
|
|
253
|
+
}).finally(() => {
|
|
254
|
+
this.connectingPromise = null;
|
|
255
|
+
});
|
|
256
|
+
return this.connectingPromise;
|
|
257
|
+
}
|
|
258
|
+
handleMessage(msg) {
|
|
259
|
+
switch (msg.type) {
|
|
260
|
+
case "screen": {
|
|
261
|
+
this.screen = msg.data;
|
|
262
|
+
for (const listener of this.screenListeners) listener(msg.data);
|
|
263
|
+
if (this.pendingScreenResolver) {
|
|
264
|
+
const resolver = this.pendingScreenResolver;
|
|
265
|
+
this.pendingScreenResolver = null;
|
|
266
|
+
resolver(msg.data);
|
|
267
|
+
}
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
case "status":
|
|
271
|
+
this.status = msg.data;
|
|
272
|
+
for (const listener of this.statusListeners) listener(msg.data);
|
|
273
|
+
break;
|
|
274
|
+
case "connected":
|
|
275
|
+
this._sessionId = msg.sessionId ?? null;
|
|
276
|
+
if (this.pendingConnectResolver) {
|
|
277
|
+
const resolver = this.pendingConnectResolver;
|
|
278
|
+
this.pendingConnectResolver = null;
|
|
279
|
+
resolver({ success: true });
|
|
280
|
+
}
|
|
281
|
+
break;
|
|
282
|
+
case "error": {
|
|
283
|
+
if (this.pendingConnectResolver) {
|
|
284
|
+
const resolver = this.pendingConnectResolver;
|
|
285
|
+
this.pendingConnectResolver = null;
|
|
286
|
+
resolver({ success: false, error: msg.message });
|
|
287
|
+
} else if (this.pendingScreenResolver) {
|
|
288
|
+
const resolver = this.pendingScreenResolver;
|
|
289
|
+
this.pendingScreenResolver = null;
|
|
290
|
+
resolver(null);
|
|
291
|
+
}
|
|
292
|
+
break;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
sendAndWaitForScreen(msg) {
|
|
297
|
+
return new Promise((resolve) => {
|
|
298
|
+
if (this.pendingScreenResolver) {
|
|
299
|
+
const old = this.pendingScreenResolver;
|
|
300
|
+
this.pendingScreenResolver = null;
|
|
301
|
+
old(this.screen);
|
|
302
|
+
}
|
|
303
|
+
const timeout = setTimeout(() => {
|
|
304
|
+
this.pendingScreenResolver = null;
|
|
305
|
+
resolve({ success: true, ...this.screenToResult() });
|
|
306
|
+
}, 5e3);
|
|
307
|
+
this.pendingScreenResolver = (screen) => {
|
|
308
|
+
clearTimeout(timeout);
|
|
309
|
+
if (screen) {
|
|
310
|
+
resolve({
|
|
311
|
+
success: true,
|
|
312
|
+
cursor_row: screen.cursor_row,
|
|
313
|
+
cursor_col: screen.cursor_col,
|
|
314
|
+
content: screen.content,
|
|
315
|
+
screen_signature: screen.screen_signature
|
|
316
|
+
});
|
|
317
|
+
} else {
|
|
318
|
+
resolve({ success: false, error: "No screen data received" });
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
this.wsSend(msg);
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
screenToResult() {
|
|
325
|
+
if (!this.screen) return {};
|
|
326
|
+
return {
|
|
327
|
+
cursor_row: this.screen.cursor_row,
|
|
328
|
+
cursor_col: this.screen.cursor_col,
|
|
329
|
+
content: this.screen.content,
|
|
330
|
+
screen_signature: this.screen.screen_signature
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
wsSend(data) {
|
|
334
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
335
|
+
this.ws.send(JSON.stringify(data));
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
};
|
|
48
339
|
|
|
49
|
-
// src/hooks/
|
|
340
|
+
// src/hooks/useTerminal.ts
|
|
50
341
|
var import_react = require("react");
|
|
51
342
|
function useTerminalConnection(adapter) {
|
|
52
343
|
const [status, setStatus] = (0, import_react.useState)(null);
|
|
@@ -167,9 +458,6 @@ function useTerminalInput(adapter) {
|
|
|
167
458
|
}, [adapter]);
|
|
168
459
|
return { loading, error, sendText, sendKey };
|
|
169
460
|
}
|
|
170
|
-
var useTN5250Connection = useTerminalConnection;
|
|
171
|
-
var useTN5250Screen = useTerminalScreen;
|
|
172
|
-
var useTN5250Terminal = useTerminalInput;
|
|
173
461
|
|
|
174
462
|
// src/hooks/useTypingAnimation.ts
|
|
175
463
|
var import_react2 = require("react");
|
|
@@ -617,7 +905,7 @@ function TerminalBootLoader({
|
|
|
617
905
|
);
|
|
618
906
|
}
|
|
619
907
|
|
|
620
|
-
// src/components/
|
|
908
|
+
// src/components/Icons.tsx
|
|
621
909
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
622
910
|
var TerminalIcon = ({ size = 14 }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
623
911
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("polyline", { points: "4 17 10 11 4 5" }),
|
|
@@ -650,26 +938,33 @@ var RefreshIcon = ({ size = 12, className }) => /* @__PURE__ */ (0, import_jsx_r
|
|
|
650
938
|
] });
|
|
651
939
|
var KeyIcon = ({ size = 12, style: s }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", style: s, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4" }) });
|
|
652
940
|
var MinimizeIcon = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: 14, height: 14, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M4 14h6v6M3 21l7-7M20 10h-6V4M21 3l-7 7" }) });
|
|
941
|
+
|
|
942
|
+
// src/components/InlineSignIn.tsx
|
|
943
|
+
var import_react4 = require("react");
|
|
944
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
653
945
|
var PROTOCOL_OPTIONS = [
|
|
654
946
|
{ value: "tn5250", label: "TN5250 (IBM i)" },
|
|
655
947
|
{ value: "tn3270", label: "TN3270 (Mainframe)" },
|
|
656
948
|
{ value: "vt", label: "VT220" },
|
|
657
949
|
{ value: "hp6530", label: "HP 6530 (NonStop)" }
|
|
658
950
|
];
|
|
659
|
-
function InlineSignIn({ defaultProtocol, loading, error, onConnect }) {
|
|
951
|
+
function InlineSignIn({ defaultProtocol, loading: externalLoading, error, onConnect }) {
|
|
660
952
|
const [host, setHost] = (0, import_react4.useState)("");
|
|
661
953
|
const [port, setPort] = (0, import_react4.useState)("");
|
|
662
954
|
const [selectedProtocol, setSelectedProtocol] = (0, import_react4.useState)(defaultProtocol);
|
|
663
955
|
const [username, setUsername] = (0, import_react4.useState)("");
|
|
664
956
|
const [password, setPassword] = (0, import_react4.useState)("");
|
|
957
|
+
const [submitted, setSubmitted] = (0, import_react4.useState)(false);
|
|
958
|
+
const loading = externalLoading || submitted;
|
|
665
959
|
const handleSubmit = (e) => {
|
|
666
960
|
e.preventDefault();
|
|
961
|
+
setSubmitted(true);
|
|
667
962
|
onConnect({
|
|
668
963
|
host,
|
|
669
|
-
port: port ? parseInt(port, 10) :
|
|
964
|
+
port: port ? parseInt(port, 10) : 23,
|
|
670
965
|
protocol: selectedProtocol,
|
|
671
|
-
username,
|
|
672
|
-
password
|
|
966
|
+
...username.trim() ? { username: username.trim() } : {},
|
|
967
|
+
...password ? { password } : {}
|
|
673
968
|
});
|
|
674
969
|
};
|
|
675
970
|
const inputStyle = {
|
|
@@ -692,42 +987,73 @@ function InlineSignIn({ defaultProtocol, loading, error, onConnect }) {
|
|
|
692
987
|
color: "var(--gs-muted, #94a3b8)",
|
|
693
988
|
fontFamily: "var(--gs-font)"
|
|
694
989
|
};
|
|
695
|
-
|
|
696
|
-
/* @__PURE__ */ (0,
|
|
697
|
-
/* @__PURE__ */ (0,
|
|
698
|
-
/* @__PURE__ */ (0,
|
|
990
|
+
if (loading) {
|
|
991
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "gs-signin", style: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: "12px" }, children: [
|
|
992
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RefreshIcon, { size: 28, className: "gs-spin" }),
|
|
993
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { fontSize: "11px", letterSpacing: "0.15em", textTransform: "uppercase", color: "var(--gs-muted)", fontFamily: "var(--gs-font)" }, children: [
|
|
994
|
+
"Connecting to ",
|
|
995
|
+
host || "host",
|
|
996
|
+
"..."
|
|
997
|
+
] })
|
|
998
|
+
] });
|
|
999
|
+
}
|
|
1000
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { onSubmit: handleSubmit, className: "gs-signin", children: [
|
|
1001
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { textAlign: "center", marginBottom: "16px" }, children: [
|
|
1002
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TerminalIcon, { size: 28 }),
|
|
1003
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { fontSize: "11px", letterSpacing: "0.15em", textTransform: "uppercase", color: "var(--gs-muted)", marginTop: "8px" }, children: "Connect to Host" })
|
|
699
1004
|
] }),
|
|
700
|
-
/* @__PURE__ */ (0,
|
|
701
|
-
/* @__PURE__ */ (0,
|
|
702
|
-
/* @__PURE__ */ (0,
|
|
703
|
-
|
|
1005
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "gs-signin-row", children: [
|
|
1006
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { flex: 1 }, children: [
|
|
1007
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { style: labelStyle, children: [
|
|
1008
|
+
"Host ",
|
|
1009
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: "#ef4444" }, children: "*" })
|
|
1010
|
+
] }),
|
|
1011
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { style: inputStyle, value: host, onChange: (e) => setHost(e.target.value), placeholder: "192.168.1.100", required: true, autoFocus: true })
|
|
704
1012
|
] }),
|
|
705
|
-
/* @__PURE__ */ (0,
|
|
706
|
-
/* @__PURE__ */ (0,
|
|
707
|
-
/* @__PURE__ */ (0,
|
|
1013
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { width: "72px" }, children: [
|
|
1014
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: labelStyle, children: "Port" }),
|
|
1015
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { style: inputStyle, value: port, onChange: (e) => setPort(e.target.value), placeholder: "23", type: "number", min: "1", max: "65535" })
|
|
708
1016
|
] })
|
|
709
1017
|
] }),
|
|
710
|
-
/* @__PURE__ */ (0,
|
|
711
|
-
/* @__PURE__ */ (0,
|
|
712
|
-
|
|
1018
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
|
|
1019
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { style: labelStyle, children: [
|
|
1020
|
+
"Protocol ",
|
|
1021
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: "#ef4444" }, children: "*" })
|
|
1022
|
+
] }),
|
|
1023
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("select", { style: { ...inputStyle, appearance: "none" }, value: selectedProtocol, onChange: (e) => setSelectedProtocol(e.target.value), children: PROTOCOL_OPTIONS.map((o) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: o.value, children: o.label }, o.value)) })
|
|
713
1024
|
] }),
|
|
714
|
-
/* @__PURE__ */ (0,
|
|
715
|
-
/* @__PURE__ */ (0,
|
|
716
|
-
/* @__PURE__ */ (0,
|
|
1025
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
|
|
1026
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: labelStyle, children: "Username" }),
|
|
1027
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { style: inputStyle, value: username, onChange: (e) => setUsername(e.target.value), autoComplete: "username" })
|
|
717
1028
|
] }),
|
|
718
|
-
/* @__PURE__ */ (0,
|
|
719
|
-
/* @__PURE__ */ (0,
|
|
720
|
-
/* @__PURE__ */ (0,
|
|
1029
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
|
|
1030
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: labelStyle, children: "Password" }),
|
|
1031
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { style: inputStyle, type: "password", value: password, onChange: (e) => setPassword(e.target.value), autoComplete: "current-password" })
|
|
721
1032
|
] }),
|
|
722
|
-
error && /* @__PURE__ */ (0,
|
|
723
|
-
/* @__PURE__ */ (0,
|
|
724
|
-
/* @__PURE__ */ (0,
|
|
1033
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { color: "#FF6B00", fontSize: "11px", fontFamily: "var(--gs-font)", display: "flex", alignItems: "center", gap: "6px" }, children: [
|
|
1034
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AlertTriangleIcon, { size: 12 }),
|
|
1035
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: error })
|
|
725
1036
|
] }),
|
|
726
|
-
/* @__PURE__ */ (0,
|
|
1037
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "submit", disabled: !host, className: "gs-signin-btn", children: "Connect" })
|
|
727
1038
|
] });
|
|
728
1039
|
}
|
|
1040
|
+
|
|
1041
|
+
// src/components/GreenScreenTerminal.tsx
|
|
1042
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1043
|
+
var noopResult = { success: false, error: "No adapter configured" };
|
|
1044
|
+
var noopAdapter = {
|
|
1045
|
+
getScreen: async () => null,
|
|
1046
|
+
getStatus: async () => ({ connected: false, status: "disconnected" }),
|
|
1047
|
+
sendText: async () => noopResult,
|
|
1048
|
+
sendKey: async () => noopResult,
|
|
1049
|
+
connect: async () => noopResult,
|
|
1050
|
+
disconnect: async () => noopResult,
|
|
1051
|
+
reconnect: async () => noopResult
|
|
1052
|
+
};
|
|
729
1053
|
function GreenScreenTerminal({
|
|
730
|
-
adapter,
|
|
1054
|
+
adapter: externalAdapter,
|
|
1055
|
+
baseUrl,
|
|
1056
|
+
workerUrl,
|
|
731
1057
|
protocol,
|
|
732
1058
|
protocolProfile: customProfile,
|
|
733
1059
|
screenData: externalScreenData,
|
|
@@ -740,9 +1066,11 @@ function GreenScreenTerminal({
|
|
|
740
1066
|
showHeader = true,
|
|
741
1067
|
typingAnimation = true,
|
|
742
1068
|
typingBudgetMs = 60,
|
|
743
|
-
inlineSignIn =
|
|
1069
|
+
inlineSignIn = true,
|
|
744
1070
|
defaultProtocol: signInDefaultProtocol,
|
|
745
1071
|
onSignIn,
|
|
1072
|
+
autoSignedIn,
|
|
1073
|
+
autoFocusDisabled = false,
|
|
746
1074
|
bootLoader,
|
|
747
1075
|
headerRight,
|
|
748
1076
|
overlay,
|
|
@@ -753,6 +1081,21 @@ function GreenScreenTerminal({
|
|
|
753
1081
|
style
|
|
754
1082
|
}) {
|
|
755
1083
|
const profile = customProfile ?? getProtocolProfile(protocol);
|
|
1084
|
+
const [internalAdapter, setInternalAdapter] = (0, import_react5.useState)(null);
|
|
1085
|
+
const baseUrlAdapter = (0, import_react5.useMemo)(
|
|
1086
|
+
() => baseUrl ? new RestAdapter({ baseUrl }) : null,
|
|
1087
|
+
[baseUrl]
|
|
1088
|
+
);
|
|
1089
|
+
const workerUrlAdapter = (0, import_react5.useMemo)(
|
|
1090
|
+
() => workerUrl ? new WebSocketAdapter({ workerUrl }) : null,
|
|
1091
|
+
[workerUrl]
|
|
1092
|
+
);
|
|
1093
|
+
const defaultWsAdapter = (0, import_react5.useMemo)(
|
|
1094
|
+
() => !externalAdapter && !baseUrl && !workerUrl ? new WebSocketAdapter() : null,
|
|
1095
|
+
[externalAdapter, baseUrl, workerUrl]
|
|
1096
|
+
);
|
|
1097
|
+
const adapter = externalAdapter ?? baseUrlAdapter ?? workerUrlAdapter ?? internalAdapter ?? defaultWsAdapter ?? noopAdapter;
|
|
1098
|
+
const isUsingDefaultAdapter = adapter === defaultWsAdapter;
|
|
756
1099
|
const shouldPoll = pollInterval > 0 && !externalScreenData;
|
|
757
1100
|
const { data: polledScreenData, error: screenError } = useTerminalScreen(adapter, pollInterval, shouldPoll);
|
|
758
1101
|
const { sendText: _sendText, sendKey: _sendKey } = useTerminalInput(adapter);
|
|
@@ -764,42 +1107,49 @@ function GreenScreenTerminal({
|
|
|
764
1107
|
typingAnimation,
|
|
765
1108
|
typingBudgetMs
|
|
766
1109
|
);
|
|
767
|
-
const screenData = (0,
|
|
1110
|
+
const screenData = (0, import_react5.useMemo)(() => {
|
|
768
1111
|
if (!rawScreenData) return null;
|
|
769
1112
|
return { ...rawScreenData, content: displayedContent };
|
|
770
1113
|
}, [rawScreenData, displayedContent]);
|
|
771
|
-
const prevScreenSigRef = (0,
|
|
772
|
-
(0,
|
|
1114
|
+
const prevScreenSigRef = (0, import_react5.useRef)(void 0);
|
|
1115
|
+
(0, import_react5.useEffect)(() => {
|
|
773
1116
|
if (screenData && onScreenChange && screenData.screen_signature !== prevScreenSigRef.current) {
|
|
774
1117
|
prevScreenSigRef.current = screenData.screen_signature;
|
|
775
1118
|
onScreenChange(screenData);
|
|
776
1119
|
}
|
|
777
1120
|
}, [screenData, onScreenChange]);
|
|
778
|
-
const sendText = (0,
|
|
779
|
-
const sendKey = (0,
|
|
780
|
-
const [inputText, setInputText] = (0,
|
|
781
|
-
const [isFocused, setIsFocused] = (0,
|
|
782
|
-
const
|
|
783
|
-
const
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
1121
|
+
const sendText = (0, import_react5.useCallback)(async (text) => _sendText(text), [_sendText]);
|
|
1122
|
+
const sendKey = (0, import_react5.useCallback)(async (key) => _sendKey(key), [_sendKey]);
|
|
1123
|
+
const [inputText, setInputText] = (0, import_react5.useState)("");
|
|
1124
|
+
const [isFocused, setIsFocused] = (0, import_react5.useState)(false);
|
|
1125
|
+
const [showSignInHint, setShowSignInHint] = (0, import_react5.useState)(false);
|
|
1126
|
+
const prevAutoSignedIn = (0, import_react5.useRef)(false);
|
|
1127
|
+
(0, import_react5.useEffect)(() => {
|
|
1128
|
+
if (autoSignedIn && !prevAutoSignedIn.current) setShowSignInHint(true);
|
|
1129
|
+
prevAutoSignedIn.current = !!autoSignedIn;
|
|
1130
|
+
}, [autoSignedIn]);
|
|
1131
|
+
const terminalRef = (0, import_react5.useRef)(null);
|
|
1132
|
+
const inputRef = (0, import_react5.useRef)(null);
|
|
1133
|
+
const [syncedCursor, setSyncedCursor] = (0, import_react5.useState)(null);
|
|
1134
|
+
const prevRawContentRef = (0, import_react5.useRef)("");
|
|
1135
|
+
(0, import_react5.useEffect)(() => {
|
|
787
1136
|
const newContent = rawScreenData?.content || "";
|
|
788
1137
|
if (prevRawContentRef.current && newContent && newContent !== prevRawContentRef.current) {
|
|
789
1138
|
setSyncedCursor(null);
|
|
790
1139
|
setInputText("");
|
|
1140
|
+
if (showSignInHint) setShowSignInHint(false);
|
|
791
1141
|
}
|
|
792
1142
|
prevRawContentRef.current = newContent;
|
|
793
1143
|
}, [rawScreenData?.content]);
|
|
794
|
-
const [autoReconnectAttempt, setAutoReconnectAttempt] = (0,
|
|
795
|
-
const [isAutoReconnecting, setIsAutoReconnecting] = (0,
|
|
796
|
-
const reconnectTimeoutRef = (0,
|
|
797
|
-
const wasConnectedRef = (0,
|
|
798
|
-
const isConnectedRef = (0,
|
|
799
|
-
(0,
|
|
1144
|
+
const [autoReconnectAttempt, setAutoReconnectAttempt] = (0, import_react5.useState)(0);
|
|
1145
|
+
const [isAutoReconnecting, setIsAutoReconnecting] = (0, import_react5.useState)(false);
|
|
1146
|
+
const reconnectTimeoutRef = (0, import_react5.useRef)(null);
|
|
1147
|
+
const wasConnectedRef = (0, import_react5.useRef)(false);
|
|
1148
|
+
const isConnectedRef = (0, import_react5.useRef)(false);
|
|
1149
|
+
(0, import_react5.useEffect)(() => {
|
|
800
1150
|
isConnectedRef.current = connStatus?.connected ?? false;
|
|
801
1151
|
}, [connStatus?.connected]);
|
|
802
|
-
(0,
|
|
1152
|
+
(0, import_react5.useEffect)(() => {
|
|
803
1153
|
if (!autoReconnectEnabled) return;
|
|
804
1154
|
const isConnected = connStatus?.connected;
|
|
805
1155
|
if (isConnected) {
|
|
@@ -835,13 +1185,37 @@ function GreenScreenTerminal({
|
|
|
835
1185
|
if (reconnectTimeoutRef.current) clearTimeout(reconnectTimeoutRef.current);
|
|
836
1186
|
};
|
|
837
1187
|
}, [connStatus?.connected, autoReconnectAttempt, isAutoReconnecting, reconnecting, reconnect, autoReconnectEnabled, maxAttempts, onNotification]);
|
|
838
|
-
const
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
1188
|
+
const [connecting, setConnecting] = (0, import_react5.useState)(false);
|
|
1189
|
+
const [signInError, setSignInError] = (0, import_react5.useState)(null);
|
|
1190
|
+
const handleSignIn = (0, import_react5.useCallback)(async (config) => {
|
|
1191
|
+
if (onSignIn) {
|
|
1192
|
+
onSignIn(config);
|
|
1193
|
+
return;
|
|
1194
|
+
}
|
|
1195
|
+
setConnecting(true);
|
|
1196
|
+
setSignInError(null);
|
|
1197
|
+
try {
|
|
1198
|
+
if (!externalAdapter && !baseUrlAdapter) {
|
|
1199
|
+
const port = config.port ? `:${config.port}` : "";
|
|
1200
|
+
const newAdapter = new RestAdapter({ baseUrl: `http://${config.host}${port}` });
|
|
1201
|
+
setInternalAdapter(newAdapter);
|
|
1202
|
+
await newAdapter.connect(config);
|
|
1203
|
+
if (config.username && config.password) setShowSignInHint(true);
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
await connect(config);
|
|
1207
|
+
if (config.username && config.password) setShowSignInHint(true);
|
|
1208
|
+
} catch (err) {
|
|
1209
|
+
setSignInError(err instanceof Error ? err.message : String(err));
|
|
1210
|
+
setConnecting(false);
|
|
1211
|
+
}
|
|
1212
|
+
}, [connect, onSignIn, externalAdapter, baseUrlAdapter]);
|
|
1213
|
+
(0, import_react5.useEffect)(() => {
|
|
1214
|
+
if (connecting && screenData?.content) setConnecting(false);
|
|
1215
|
+
}, [connecting, screenData?.content]);
|
|
1216
|
+
const [showBootLoader, setShowBootLoader] = (0, import_react5.useState)(bootLoader !== false);
|
|
1217
|
+
const [bootFadingOut, setBootFadingOut] = (0, import_react5.useState)(false);
|
|
1218
|
+
(0, import_react5.useEffect)(() => {
|
|
845
1219
|
if (screenData?.content && showBootLoader) {
|
|
846
1220
|
setBootFadingOut(true);
|
|
847
1221
|
setShowBootLoader(false);
|
|
@@ -849,25 +1223,54 @@ function GreenScreenTerminal({
|
|
|
849
1223
|
return () => clearTimeout(timer);
|
|
850
1224
|
}
|
|
851
1225
|
}, [screenData?.content, showBootLoader]);
|
|
852
|
-
|
|
1226
|
+
const FOCUS_STORAGE_KEY = "gs-terminal-focused";
|
|
1227
|
+
(0, import_react5.useEffect)(() => {
|
|
1228
|
+
if (!autoFocusDisabled && !readOnly) {
|
|
1229
|
+
try {
|
|
1230
|
+
if (localStorage.getItem(FOCUS_STORAGE_KEY) === "true") {
|
|
1231
|
+
setIsFocused(true);
|
|
1232
|
+
}
|
|
1233
|
+
} catch {
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
}, []);
|
|
1237
|
+
(0, import_react5.useEffect)(() => {
|
|
1238
|
+
if (autoFocusDisabled) return;
|
|
1239
|
+
try {
|
|
1240
|
+
localStorage.setItem(FOCUS_STORAGE_KEY, String(isFocused));
|
|
1241
|
+
} catch {
|
|
1242
|
+
}
|
|
1243
|
+
}, [isFocused, autoFocusDisabled]);
|
|
1244
|
+
(0, import_react5.useEffect)(() => {
|
|
1245
|
+
if (isFocused) inputRef.current?.focus();
|
|
1246
|
+
}, [isFocused]);
|
|
1247
|
+
const hadScreenData = (0, import_react5.useRef)(false);
|
|
1248
|
+
(0, import_react5.useEffect)(() => {
|
|
1249
|
+
if (screenData?.content && !hadScreenData.current && !autoFocusDisabled && !readOnly) {
|
|
1250
|
+
hadScreenData.current = true;
|
|
1251
|
+
setIsFocused(true);
|
|
1252
|
+
}
|
|
1253
|
+
if (!screenData?.content) hadScreenData.current = false;
|
|
1254
|
+
}, [screenData?.content, autoFocusDisabled, readOnly]);
|
|
1255
|
+
(0, import_react5.useEffect)(() => {
|
|
853
1256
|
const handleClickOutside = (event) => {
|
|
854
1257
|
if (terminalRef.current && !terminalRef.current.contains(event.target)) setIsFocused(false);
|
|
855
1258
|
};
|
|
856
1259
|
if (isFocused) document.addEventListener("mousedown", handleClickOutside);
|
|
857
1260
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
858
1261
|
}, [isFocused]);
|
|
859
|
-
(0,
|
|
1262
|
+
(0, import_react5.useEffect)(() => {
|
|
860
1263
|
if (readOnly && isFocused) {
|
|
861
1264
|
setIsFocused(false);
|
|
862
1265
|
inputRef.current?.blur();
|
|
863
1266
|
}
|
|
864
1267
|
}, [readOnly, isFocused]);
|
|
865
|
-
const handleTerminalClick = (0,
|
|
1268
|
+
const handleTerminalClick = (0, import_react5.useCallback)(() => {
|
|
866
1269
|
if (readOnly) return;
|
|
867
1270
|
setIsFocused(true);
|
|
868
1271
|
inputRef.current?.focus();
|
|
869
1272
|
}, [readOnly]);
|
|
870
|
-
const getCurrentField = (0,
|
|
1273
|
+
const getCurrentField = (0, import_react5.useCallback)(() => {
|
|
871
1274
|
const fields = screenData?.fields || [];
|
|
872
1275
|
const cursorRow = syncedCursor?.row ?? screenData?.cursor_row ?? 0;
|
|
873
1276
|
const cursorCol = syncedCursor?.col ?? screenData?.cursor_col ?? 0;
|
|
@@ -876,7 +1279,7 @@ function GreenScreenTerminal({
|
|
|
876
1279
|
}
|
|
877
1280
|
return null;
|
|
878
1281
|
}, [screenData, syncedCursor]);
|
|
879
|
-
const canTypeMore = (0,
|
|
1282
|
+
const canTypeMore = (0, import_react5.useCallback)((additionalChars = 1) => {
|
|
880
1283
|
const currentField = getCurrentField();
|
|
881
1284
|
if (!currentField) return true;
|
|
882
1285
|
const cursorCol = (syncedCursor?.col ?? screenData?.cursor_col ?? 0) + inputText.length;
|
|
@@ -978,28 +1381,28 @@ function GreenScreenTerminal({
|
|
|
978
1381
|
}
|
|
979
1382
|
return { row: cursorRow, col: cursorCol };
|
|
980
1383
|
};
|
|
981
|
-
const renderTextWithUnderlines = (0,
|
|
1384
|
+
const renderTextWithUnderlines = (0, import_react5.useCallback)((text, keyPrefix) => {
|
|
982
1385
|
const underscoreRegex = /_{2,}/g;
|
|
983
1386
|
const segments = [];
|
|
984
1387
|
let lastIndex = 0;
|
|
985
1388
|
let match;
|
|
986
1389
|
let segmentIndex = 0;
|
|
987
1390
|
while ((match = underscoreRegex.exec(text)) !== null) {
|
|
988
|
-
if (match.index > lastIndex) segments.push(/* @__PURE__ */ (0,
|
|
1391
|
+
if (match.index > lastIndex) segments.push(/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: text.substring(lastIndex, match.index) }, `${keyPrefix}-t-${segmentIndex}`));
|
|
989
1392
|
const count = match[0].length;
|
|
990
|
-
segments.push(/* @__PURE__ */ (0,
|
|
1393
|
+
segments.push(/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { borderBottom: "1px solid var(--gs-green, #10b981)", display: "inline-block", width: `${count}ch` }, children: " ".repeat(count) }, `${keyPrefix}-u-${segmentIndex}`));
|
|
991
1394
|
lastIndex = match.index + match[0].length;
|
|
992
1395
|
segmentIndex++;
|
|
993
1396
|
}
|
|
994
|
-
if (lastIndex < text.length) segments.push(/* @__PURE__ */ (0,
|
|
995
|
-
return segments.length > 0 ? /* @__PURE__ */ (0,
|
|
1397
|
+
if (lastIndex < text.length) segments.push(/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: text.substring(lastIndex) }, `${keyPrefix}-e`));
|
|
1398
|
+
return segments.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: segments }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: text });
|
|
996
1399
|
}, []);
|
|
997
|
-
const renderRowWithFields = (0,
|
|
1400
|
+
const renderRowWithFields = (0, import_react5.useCallback)((line, rowIndex, fields) => {
|
|
998
1401
|
const inputFields = fields.filter((f) => f.row === rowIndex && f.is_input);
|
|
999
1402
|
const highlightedFields = fields.filter((f) => f.row === rowIndex && f.is_protected && f.is_highlighted);
|
|
1000
1403
|
const reverseFields = fields.filter((f) => f.row === rowIndex && f.is_protected && f.is_reverse);
|
|
1001
1404
|
const allRowFields = [...inputFields, ...highlightedFields, ...reverseFields];
|
|
1002
|
-
if (allRowFields.length === 0) return /* @__PURE__ */ (0,
|
|
1405
|
+
if (allRowFields.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: line });
|
|
1003
1406
|
const sorted = [...allRowFields].sort((a, b) => a.col - b.col);
|
|
1004
1407
|
const segs = [];
|
|
1005
1408
|
let lastEnd = 0;
|
|
@@ -1007,35 +1410,42 @@ function GreenScreenTerminal({
|
|
|
1007
1410
|
sorted.forEach((field, idx) => {
|
|
1008
1411
|
const fs = field.col;
|
|
1009
1412
|
const fe = Math.min(field.col + field.length, cols);
|
|
1010
|
-
if (fs > lastEnd) segs.push(/* @__PURE__ */ (0,
|
|
1413
|
+
if (fs > lastEnd) segs.push(/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: line.substring(lastEnd, fs) }, `t${idx}`));
|
|
1011
1414
|
const fc = line.substring(fs, fe);
|
|
1012
1415
|
if (field.is_input) {
|
|
1013
|
-
const
|
|
1014
|
-
|
|
1416
|
+
const fieldWidth = Math.min(field.length, cols - fs);
|
|
1417
|
+
const fieldClass = showSignInHint ? "gs-confirmed-field" : field.is_underscored ? "gs-input-field" : void 0;
|
|
1418
|
+
segs.push(/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: fieldClass || void 0, style: { display: "inline-block", width: `${fieldWidth}ch`, overflow: "hidden" }, children: fc }, `f${idx}`));
|
|
1015
1419
|
} else if (field.is_reverse) {
|
|
1016
|
-
segs.push(/* @__PURE__ */ (0,
|
|
1420
|
+
segs.push(/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { color: "#ef4444", fontWeight: "bold" }, children: fc }, `v${idx}`));
|
|
1421
|
+
} else if (field.is_highlighted) {
|
|
1422
|
+
segs.push(/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { color: "var(--gs-white, #FFFFFF)" }, children: fc }, `h${idx}`));
|
|
1017
1423
|
} else {
|
|
1018
|
-
segs.push(/* @__PURE__ */ (0,
|
|
1424
|
+
segs.push(/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: fc }, `h${idx}`));
|
|
1019
1425
|
}
|
|
1020
1426
|
lastEnd = fe;
|
|
1021
1427
|
});
|
|
1022
|
-
if (lastEnd < line.length) segs.push(/* @__PURE__ */ (0,
|
|
1023
|
-
return /* @__PURE__ */ (0,
|
|
1024
|
-
}, [renderTextWithUnderlines, screenData?.cols, profile.defaultCols]);
|
|
1428
|
+
if (lastEnd < line.length) segs.push(/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: line.substring(lastEnd) }, "te"));
|
|
1429
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: segs });
|
|
1430
|
+
}, [renderTextWithUnderlines, screenData?.cols, profile.defaultCols, showSignInHint]);
|
|
1025
1431
|
const renderScreen = () => {
|
|
1026
1432
|
if (showBootLoader && !screenData?.content) {
|
|
1027
1433
|
if (bootLoader === false) return null;
|
|
1028
|
-
if (bootLoader) return /* @__PURE__ */ (0,
|
|
1029
|
-
return /* @__PURE__ */ (0,
|
|
1434
|
+
if (bootLoader) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: bootLoader });
|
|
1435
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TerminalBootLoader, { brandText: profile.bootText });
|
|
1030
1436
|
}
|
|
1031
|
-
if (bootFadingOut && screenData?.content) return /* @__PURE__ */ (0,
|
|
1437
|
+
if (bootFadingOut && screenData?.content) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "gs-fade-in", children: renderScreenContent() });
|
|
1032
1438
|
if (!screenData?.content) {
|
|
1033
1439
|
if (inlineSignIn && !connStatus?.connected) {
|
|
1034
|
-
return /* @__PURE__ */ (0,
|
|
1440
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { width: "100%", height: `${(screenData?.rows || profile.defaultRows) * 21}px`, display: "flex", alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(InlineSignIn, { defaultProtocol: signInDefaultProtocol || protocol || "tn5250", loading: connecting || reconnecting, error: signInError || connectError, onConnect: handleSignIn }) });
|
|
1035
1441
|
}
|
|
1036
|
-
return /* @__PURE__ */ (0,
|
|
1037
|
-
/* @__PURE__ */ (0,
|
|
1038
|
-
/* @__PURE__ */ (0,
|
|
1442
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { width: `${screenData?.cols || profile.defaultCols}ch`, height: `${(screenData?.rows || profile.defaultRows) * 21}px`, display: "flex", alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { textAlign: "center" }, children: [
|
|
1443
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: "#808080", marginBottom: "12px" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TerminalIcon, { size: 40 }) }),
|
|
1444
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { style: { fontFamily: "var(--gs-font)", fontSize: "12px", color: "#808080" }, children: connStatus?.connected ? "Waiting for screen data..." : "Not connected" }),
|
|
1445
|
+
!connStatus?.connected && isUsingDefaultAdapter && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { style: { fontFamily: "var(--gs-font)", fontSize: "11px", color: "#606060", marginTop: "8px" }, children: [
|
|
1446
|
+
"Start the proxy: ",
|
|
1447
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("code", { style: { color: "#10b981" }, children: "npx green-screen-proxy --mock" })
|
|
1448
|
+
] })
|
|
1039
1449
|
] }) });
|
|
1040
1450
|
}
|
|
1041
1451
|
return renderScreenContent();
|
|
@@ -1050,9 +1460,8 @@ function GreenScreenTerminal({
|
|
|
1050
1460
|
const fields = screenData.fields || [];
|
|
1051
1461
|
const ROW_HEIGHT = 21;
|
|
1052
1462
|
const cursor = getCursorPos();
|
|
1053
|
-
const
|
|
1054
|
-
|
|
1055
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { fontFamily: "var(--gs-font)", fontSize: "13px", position: "relative", width: `${cols}ch` }, children: [
|
|
1463
|
+
const hasCursor = screenData.cursor_row !== void 0 && screenData.cursor_col !== void 0;
|
|
1464
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { fontFamily: "var(--gs-font)", fontSize: "13px", position: "relative", width: `${cols}ch` }, children: [
|
|
1056
1465
|
rows.map((line, index) => {
|
|
1057
1466
|
let displayLine = line;
|
|
1058
1467
|
if (hasCursor && index === cursor.row && inputText && !animatedCursorPos) {
|
|
@@ -1060,12 +1469,13 @@ function GreenScreenTerminal({
|
|
|
1060
1469
|
displayLine = (line.substring(0, baseCol) + inputText + line.substring(baseCol + inputText.length)).substring(0, cols).padEnd(cols, " ");
|
|
1061
1470
|
}
|
|
1062
1471
|
const headerSegments = index === 0 ? profile.colors.parseHeaderRow(displayLine) : null;
|
|
1063
|
-
return /* @__PURE__ */ (0,
|
|
1064
|
-
headerSegments ? headerSegments.map((seg, i) => /* @__PURE__ */ (0,
|
|
1065
|
-
hasCursor && index === cursor.row && /* @__PURE__ */ (0,
|
|
1472
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: headerSegments ? "" : profile.colors.getRowColorClass(index, displayLine, termRows), style: { height: `${ROW_HEIGHT}px`, lineHeight: `${ROW_HEIGHT}px`, whiteSpace: "pre", position: "relative" }, children: [
|
|
1473
|
+
headerSegments ? headerSegments.map((seg, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: seg.colorClass, children: seg.text }, i)) : renderRowWithFields(displayLine, index, fields),
|
|
1474
|
+
hasCursor && !showSignInHint && index === cursor.row && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "gs-cursor", style: { position: "absolute", left: `${cursor.col}ch`, width: "1ch", height: `${ROW_HEIGHT}px`, top: 0, pointerEvents: "none" } })
|
|
1066
1475
|
] }, index);
|
|
1067
1476
|
}),
|
|
1068
|
-
|
|
1477
|
+
showSignInHint && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "gs-signin-hint", children: "Signed in \u2014 press Enter to continue" }),
|
|
1478
|
+
screenData.cursor_row !== void 0 && screenData.cursor_col !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { style: { position: "absolute", bottom: 0, right: 0, fontFamily: "var(--gs-font)", fontSize: "10px", color: "var(--gs-green, #10b981)", pointerEvents: "none", opacity: 0.6 }, children: [
|
|
1069
1479
|
String(screenData.cursor_row + 1).padStart(2, "0"),
|
|
1070
1480
|
"/",
|
|
1071
1481
|
String(screenData.cursor_col + 1).padStart(3, "0")
|
|
@@ -1095,49 +1505,49 @@ function GreenScreenTerminal({
|
|
|
1095
1505
|
return "#64748b";
|
|
1096
1506
|
}
|
|
1097
1507
|
};
|
|
1098
|
-
return /* @__PURE__ */ (0,
|
|
1099
|
-
showHeader && /* @__PURE__ */ (0,
|
|
1100
|
-
/* @__PURE__ */ (0,
|
|
1101
|
-
/* @__PURE__ */ (0,
|
|
1102
|
-
/* @__PURE__ */ (0,
|
|
1103
|
-
isFocused && /* @__PURE__ */ (0,
|
|
1104
|
-
screenData?.timestamp && /* @__PURE__ */ (0,
|
|
1105
|
-
/* @__PURE__ */ (0,
|
|
1508
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `gs-terminal ${isFocused ? "gs-terminal-focused" : ""} ${className || ""}`, style, children: [
|
|
1509
|
+
showHeader && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "gs-header", children: embedded ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
1510
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "gs-header-left", children: [
|
|
1511
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TerminalIcon, { size: 14 }),
|
|
1512
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "TERMINAL" }),
|
|
1513
|
+
isFocused && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "gs-badge-focused", children: "FOCUSED" }),
|
|
1514
|
+
screenData?.timestamp && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "gs-timestamp", children: new Date(screenData.timestamp).toLocaleTimeString() }),
|
|
1515
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "gs-hint", children: readOnly ? "Read-only" : isFocused ? "ESC to exit focus" : "Click to control" })
|
|
1106
1516
|
] }),
|
|
1107
|
-
/* @__PURE__ */ (0,
|
|
1108
|
-
connStatus?.status && /* @__PURE__ */ (0,
|
|
1109
|
-
connStatus && (connStatus.connected ? /* @__PURE__ */ (0,
|
|
1110
|
-
onMinimize && /* @__PURE__ */ (0,
|
|
1517
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "gs-header-right", children: [
|
|
1518
|
+
connStatus?.status && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(KeyIcon, { size: 12, style: { color: getStatusColor(connStatus.status) } }),
|
|
1519
|
+
connStatus && (connStatus.connected ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(WifiIcon, { size: 12, style: { color: "var(--gs-green, #10b981)" } }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(WifiOffIcon, { size: 12, style: { color: "#FF6B00" } })),
|
|
1520
|
+
onMinimize && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { onClick: (e) => {
|
|
1111
1521
|
e.stopPropagation();
|
|
1112
1522
|
onMinimize();
|
|
1113
|
-
}, className: "gs-btn-icon", title: "Minimize terminal", children: /* @__PURE__ */ (0,
|
|
1523
|
+
}, className: "gs-btn-icon", title: "Minimize terminal", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MinimizeIcon, {}) }),
|
|
1114
1524
|
headerRight
|
|
1115
1525
|
] })
|
|
1116
|
-
] }) : /* @__PURE__ */ (0,
|
|
1117
|
-
/* @__PURE__ */ (0,
|
|
1118
|
-
/* @__PURE__ */ (0,
|
|
1119
|
-
/* @__PURE__ */ (0,
|
|
1120
|
-
isFocused && /* @__PURE__ */ (0,
|
|
1121
|
-
screenData?.timestamp && /* @__PURE__ */ (0,
|
|
1122
|
-
/* @__PURE__ */ (0,
|
|
1526
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
1527
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "gs-header-left", children: [
|
|
1528
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TerminalIcon, { size: 14 }),
|
|
1529
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: profile.headerLabel }),
|
|
1530
|
+
isFocused && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "gs-badge-focused", children: "FOCUSED" }),
|
|
1531
|
+
screenData?.timestamp && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "gs-timestamp", children: new Date(screenData.timestamp).toLocaleTimeString() }),
|
|
1532
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "gs-hint", children: readOnly ? "Read-only mode" : isFocused ? "ESC to exit focus" : "Click terminal to control" })
|
|
1123
1533
|
] }),
|
|
1124
|
-
/* @__PURE__ */ (0,
|
|
1125
|
-
connStatus && /* @__PURE__ */ (0,
|
|
1126
|
-
/* @__PURE__ */ (0,
|
|
1127
|
-
/* @__PURE__ */ (0,
|
|
1128
|
-
] }) : /* @__PURE__ */ (0,
|
|
1129
|
-
/* @__PURE__ */ (0,
|
|
1130
|
-
/* @__PURE__ */ (0,
|
|
1131
|
-
/* @__PURE__ */ (0,
|
|
1534
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "gs-header-right", children: [
|
|
1535
|
+
connStatus && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "gs-status-group", children: connStatus.connected ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
1536
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(WifiIcon, { size: 12, style: { color: "var(--gs-green, #10b981)" } }),
|
|
1537
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "gs-host", children: connStatus.host })
|
|
1538
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
1539
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(WifiOffIcon, { size: 12, style: { color: "#FF6B00" } }),
|
|
1540
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "gs-disconnected-text", children: isAutoReconnecting || reconnecting ? `RECONNECTING${autoReconnectAttempt > 0 ? ` (${autoReconnectAttempt}/${maxAttempts})` : "..."}` : autoReconnectAttempt >= maxAttempts ? "DISCONNECTED (auto-retry exhausted)" : "DISCONNECTED" }),
|
|
1541
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { onClick: handleReconnect, disabled: reconnecting || isAutoReconnecting, className: "gs-btn-icon", title: "Reconnect", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RefreshIcon, { size: 12, className: reconnecting || isAutoReconnecting ? "gs-spin" : "" }) })
|
|
1132
1542
|
] }) }),
|
|
1133
|
-
connStatus?.status && /* @__PURE__ */ (0,
|
|
1134
|
-
/* @__PURE__ */ (0,
|
|
1135
|
-
connStatus.username && /* @__PURE__ */ (0,
|
|
1543
|
+
connStatus?.status && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "gs-status-group", children: [
|
|
1544
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(KeyIcon, { size: 12, style: { color: getStatusColor(connStatus.status) } }),
|
|
1545
|
+
connStatus.username && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "gs-host", children: connStatus.username })
|
|
1136
1546
|
] }),
|
|
1137
1547
|
headerRight
|
|
1138
1548
|
] })
|
|
1139
1549
|
] }) }),
|
|
1140
|
-
/* @__PURE__ */ (0,
|
|
1550
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "gs-body", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1141
1551
|
"div",
|
|
1142
1552
|
{
|
|
1143
1553
|
ref: terminalRef,
|
|
@@ -1145,17 +1555,17 @@ function GreenScreenTerminal({
|
|
|
1145
1555
|
className: `gs-screen ${embedded ? "gs-screen-embedded" : ""}`,
|
|
1146
1556
|
style: !embedded ? { width: `calc(${screenData?.cols || profile.defaultCols}ch + 24px)`, fontSize: (screenData?.cols ?? profile.defaultCols) > 80 ? "11px" : "13px", fontFamily: "var(--gs-font)" } : void 0,
|
|
1147
1557
|
children: [
|
|
1148
|
-
screenError != null && /* @__PURE__ */ (0,
|
|
1149
|
-
/* @__PURE__ */ (0,
|
|
1150
|
-
/* @__PURE__ */ (0,
|
|
1558
|
+
screenError != null && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "gs-error-banner", children: [
|
|
1559
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(AlertTriangleIcon, { size: 14 }),
|
|
1560
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: String(screenError) })
|
|
1151
1561
|
] }),
|
|
1152
|
-
/* @__PURE__ */ (0,
|
|
1562
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "gs-screen-content", children: renderScreen() }),
|
|
1153
1563
|
overlay,
|
|
1154
|
-
connStatus && !connStatus.connected && screenData && /* @__PURE__ */ (0,
|
|
1155
|
-
/* @__PURE__ */ (0,
|
|
1156
|
-
/* @__PURE__ */ (0,
|
|
1564
|
+
connStatus && !connStatus.connected && screenData && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "gs-overlay", children: [
|
|
1565
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(WifiOffIcon, { size: 28 }),
|
|
1566
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: isAutoReconnecting || reconnecting ? "Reconnecting..." : "Disconnected" })
|
|
1157
1567
|
] }),
|
|
1158
|
-
/* @__PURE__ */ (0,
|
|
1568
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1159
1569
|
"input",
|
|
1160
1570
|
{
|
|
1161
1571
|
ref: inputRef,
|
|
@@ -1175,66 +1585,6 @@ function GreenScreenTerminal({
|
|
|
1175
1585
|
) })
|
|
1176
1586
|
] });
|
|
1177
1587
|
}
|
|
1178
|
-
var TN5250Terminal = GreenScreenTerminal;
|
|
1179
|
-
|
|
1180
|
-
// src/adapters/RestAdapter.ts
|
|
1181
|
-
var RestAdapter = class {
|
|
1182
|
-
constructor(options) {
|
|
1183
|
-
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
1184
|
-
this.staticHeaders = options.headers || {};
|
|
1185
|
-
this.getHeaders = options.getHeaders;
|
|
1186
|
-
}
|
|
1187
|
-
async buildHeaders() {
|
|
1188
|
-
const dynamic = this.getHeaders ? await this.getHeaders() : {};
|
|
1189
|
-
return {
|
|
1190
|
-
"Content-Type": "application/json",
|
|
1191
|
-
...this.staticHeaders,
|
|
1192
|
-
...dynamic
|
|
1193
|
-
};
|
|
1194
|
-
}
|
|
1195
|
-
async request(method, path, body) {
|
|
1196
|
-
const headers = await this.buildHeaders();
|
|
1197
|
-
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
1198
|
-
method,
|
|
1199
|
-
headers,
|
|
1200
|
-
body: body ? JSON.stringify(body) : void 0
|
|
1201
|
-
});
|
|
1202
|
-
if (!response.ok) {
|
|
1203
|
-
const detail = await response.json().catch(() => ({}));
|
|
1204
|
-
throw new Error(detail?.detail || `HTTP ${response.status}`);
|
|
1205
|
-
}
|
|
1206
|
-
return response.json();
|
|
1207
|
-
}
|
|
1208
|
-
async getScreen() {
|
|
1209
|
-
try {
|
|
1210
|
-
return await this.request("GET", "/screen");
|
|
1211
|
-
} catch (e) {
|
|
1212
|
-
const message = e instanceof Error ? e.message : String(e);
|
|
1213
|
-
if (message.includes("503") || message.includes("404")) {
|
|
1214
|
-
return null;
|
|
1215
|
-
}
|
|
1216
|
-
throw e;
|
|
1217
|
-
}
|
|
1218
|
-
}
|
|
1219
|
-
async getStatus() {
|
|
1220
|
-
return this.request("GET", "/status");
|
|
1221
|
-
}
|
|
1222
|
-
async sendText(text) {
|
|
1223
|
-
return this.request("POST", "/send-text", { text });
|
|
1224
|
-
}
|
|
1225
|
-
async sendKey(key) {
|
|
1226
|
-
return this.request("POST", "/send-key", { key });
|
|
1227
|
-
}
|
|
1228
|
-
async connect(config) {
|
|
1229
|
-
return this.request("POST", "/connect", config);
|
|
1230
|
-
}
|
|
1231
|
-
async disconnect() {
|
|
1232
|
-
return this.request("POST", "/disconnect");
|
|
1233
|
-
}
|
|
1234
|
-
async reconnect() {
|
|
1235
|
-
return this.request("POST", "/reconnect");
|
|
1236
|
-
}
|
|
1237
|
-
};
|
|
1238
1588
|
|
|
1239
1589
|
// src/utils/rendering.ts
|
|
1240
1590
|
function getRowColorClass(rowIndex, rowContent) {
|
|
@@ -1245,10 +1595,18 @@ function parseHeaderRow(line) {
|
|
|
1245
1595
|
}
|
|
1246
1596
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1247
1597
|
0 && (module.exports = {
|
|
1598
|
+
AlertTriangleIcon,
|
|
1248
1599
|
GreenScreenTerminal,
|
|
1600
|
+
InlineSignIn,
|
|
1601
|
+
KeyIcon,
|
|
1602
|
+
MinimizeIcon,
|
|
1603
|
+
RefreshIcon,
|
|
1249
1604
|
RestAdapter,
|
|
1250
|
-
TN5250Terminal,
|
|
1251
1605
|
TerminalBootLoader,
|
|
1606
|
+
TerminalIcon,
|
|
1607
|
+
WebSocketAdapter,
|
|
1608
|
+
WifiIcon,
|
|
1609
|
+
WifiOffIcon,
|
|
1252
1610
|
getProtocolProfile,
|
|
1253
1611
|
getRowColorClass,
|
|
1254
1612
|
hp6530Profile,
|
|
@@ -1257,9 +1615,6 @@ function parseHeaderRow(line) {
|
|
|
1257
1615
|
positionToRowCol,
|
|
1258
1616
|
tn3270Profile,
|
|
1259
1617
|
tn5250Profile,
|
|
1260
|
-
useTN5250Connection,
|
|
1261
|
-
useTN5250Screen,
|
|
1262
|
-
useTN5250Terminal,
|
|
1263
1618
|
useTerminalConnection,
|
|
1264
1619
|
useTerminalInput,
|
|
1265
1620
|
useTerminalScreen,
|