green-screen-react 0.3.0 → 0.4.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 +245 -50
- package/dist/index.d.mts +81 -17
- package/dist/index.d.ts +81 -17
- package/dist/index.js +405 -194
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +374 -168
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,236 @@
|
|
|
1
1
|
// src/components/GreenScreenTerminal.tsx
|
|
2
|
-
import { useState as
|
|
2
|
+
import { useState as useState5, useEffect as useEffect4, useRef as useRef4, useCallback as useCallback3, useMemo as useMemo2 } from "react";
|
|
3
3
|
|
|
4
|
-
// src/
|
|
4
|
+
// src/adapters/RestAdapter.ts
|
|
5
|
+
var RestAdapter = class {
|
|
6
|
+
constructor(options) {
|
|
7
|
+
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
8
|
+
this.staticHeaders = options.headers || {};
|
|
9
|
+
this.getHeaders = options.getHeaders;
|
|
10
|
+
}
|
|
11
|
+
async buildHeaders() {
|
|
12
|
+
const dynamic = this.getHeaders ? await this.getHeaders() : {};
|
|
13
|
+
return {
|
|
14
|
+
"Content-Type": "application/json",
|
|
15
|
+
...this.staticHeaders,
|
|
16
|
+
...dynamic
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
async request(method, path, body) {
|
|
20
|
+
const headers = await this.buildHeaders();
|
|
21
|
+
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
22
|
+
method,
|
|
23
|
+
headers,
|
|
24
|
+
body: body ? JSON.stringify(body) : void 0
|
|
25
|
+
});
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
const detail = await response.json().catch(() => ({}));
|
|
28
|
+
throw new Error(detail?.detail || `HTTP ${response.status}`);
|
|
29
|
+
}
|
|
30
|
+
return response.json();
|
|
31
|
+
}
|
|
32
|
+
async getScreen() {
|
|
33
|
+
try {
|
|
34
|
+
return await this.request("GET", "/screen");
|
|
35
|
+
} catch (e) {
|
|
36
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
37
|
+
if (message.includes("503") || message.includes("404")) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
throw e;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async getStatus() {
|
|
44
|
+
return this.request("GET", "/status");
|
|
45
|
+
}
|
|
46
|
+
async sendText(text) {
|
|
47
|
+
return this.request("POST", "/send-text", { text });
|
|
48
|
+
}
|
|
49
|
+
async sendKey(key) {
|
|
50
|
+
return this.request("POST", "/send-key", { key });
|
|
51
|
+
}
|
|
52
|
+
async connect(config) {
|
|
53
|
+
return this.request("POST", "/connect", config);
|
|
54
|
+
}
|
|
55
|
+
async disconnect() {
|
|
56
|
+
return this.request("POST", "/disconnect");
|
|
57
|
+
}
|
|
58
|
+
async reconnect() {
|
|
59
|
+
return this.request("POST", "/reconnect");
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// src/adapters/WebSocketAdapter.ts
|
|
64
|
+
var WebSocketAdapter = class {
|
|
65
|
+
constructor(options = {}) {
|
|
66
|
+
this.ws = null;
|
|
67
|
+
this.screen = null;
|
|
68
|
+
this.status = { connected: false, status: "disconnected" };
|
|
69
|
+
this.pendingResolvers = /* @__PURE__ */ new Map();
|
|
70
|
+
this.screenListeners = /* @__PURE__ */ new Set();
|
|
71
|
+
this.statusListeners = /* @__PURE__ */ new Set();
|
|
72
|
+
this.workerUrl = (options.workerUrl || "http://localhost:3001").replace(/\/+$/, "");
|
|
73
|
+
}
|
|
74
|
+
/** Subscribe to real-time screen updates */
|
|
75
|
+
onScreen(listener) {
|
|
76
|
+
this.screenListeners.add(listener);
|
|
77
|
+
return () => this.screenListeners.delete(listener);
|
|
78
|
+
}
|
|
79
|
+
/** Subscribe to status changes */
|
|
80
|
+
onStatus(listener) {
|
|
81
|
+
this.statusListeners.add(listener);
|
|
82
|
+
return () => this.statusListeners.delete(listener);
|
|
83
|
+
}
|
|
84
|
+
async getScreen() {
|
|
85
|
+
return this.screen;
|
|
86
|
+
}
|
|
87
|
+
async getStatus() {
|
|
88
|
+
return this.status;
|
|
89
|
+
}
|
|
90
|
+
async sendText(text) {
|
|
91
|
+
return this.sendAndWaitForScreen({ type: "text", text });
|
|
92
|
+
}
|
|
93
|
+
async sendKey(key) {
|
|
94
|
+
return this.sendAndWaitForScreen({ type: "key", key });
|
|
95
|
+
}
|
|
96
|
+
async connect(config) {
|
|
97
|
+
await this.ensureWebSocket();
|
|
98
|
+
if (!config) {
|
|
99
|
+
return { success: false, error: "ConnectConfig required" };
|
|
100
|
+
}
|
|
101
|
+
return new Promise((resolve) => {
|
|
102
|
+
const timeout = setTimeout(() => {
|
|
103
|
+
resolve({ success: false, error: "Connection timeout" });
|
|
104
|
+
}, 3e4);
|
|
105
|
+
const onMessage = (event) => {
|
|
106
|
+
try {
|
|
107
|
+
const msg = JSON.parse(event.data);
|
|
108
|
+
if (msg.type === "connected") {
|
|
109
|
+
clearTimeout(timeout);
|
|
110
|
+
this.ws?.removeEventListener("message", onMessage);
|
|
111
|
+
resolve({ success: true });
|
|
112
|
+
} else if (msg.type === "error") {
|
|
113
|
+
clearTimeout(timeout);
|
|
114
|
+
this.ws?.removeEventListener("message", onMessage);
|
|
115
|
+
resolve({ success: false, error: msg.message });
|
|
116
|
+
}
|
|
117
|
+
} catch {
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
this.ws?.addEventListener("message", onMessage);
|
|
121
|
+
this.wsSend({
|
|
122
|
+
type: "connect",
|
|
123
|
+
host: config.host,
|
|
124
|
+
port: config.port,
|
|
125
|
+
protocol: config.protocol,
|
|
126
|
+
username: config.username,
|
|
127
|
+
password: config.password
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
async disconnect() {
|
|
132
|
+
this.wsSend({ type: "disconnect" });
|
|
133
|
+
this.status = { connected: false, status: "disconnected" };
|
|
134
|
+
if (this.ws) {
|
|
135
|
+
this.ws.close();
|
|
136
|
+
this.ws = null;
|
|
137
|
+
}
|
|
138
|
+
return { success: true };
|
|
139
|
+
}
|
|
140
|
+
async reconnect() {
|
|
141
|
+
return { success: false, error: "Use disconnect() then connect() instead" };
|
|
142
|
+
}
|
|
143
|
+
/** Close the WebSocket without sending disconnect */
|
|
144
|
+
dispose() {
|
|
145
|
+
if (this.ws) {
|
|
146
|
+
this.ws.close();
|
|
147
|
+
this.ws = null;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async ensureWebSocket() {
|
|
151
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) return;
|
|
152
|
+
return new Promise((resolve, reject) => {
|
|
153
|
+
const wsUrl = this.workerUrl.replace(/^http/, "ws") + "/ws";
|
|
154
|
+
this.ws = new WebSocket(wsUrl);
|
|
155
|
+
this.ws.onopen = () => resolve();
|
|
156
|
+
this.ws.onerror = () => reject(new Error("WebSocket connection failed"));
|
|
157
|
+
this.ws.onmessage = (event) => {
|
|
158
|
+
try {
|
|
159
|
+
const msg = JSON.parse(event.data);
|
|
160
|
+
this.handleMessage(msg);
|
|
161
|
+
} catch {
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
this.ws.onclose = () => {
|
|
165
|
+
this.status = { connected: false, status: "disconnected" };
|
|
166
|
+
for (const listener of this.statusListeners) listener(this.status);
|
|
167
|
+
};
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
handleMessage(msg) {
|
|
171
|
+
switch (msg.type) {
|
|
172
|
+
case "screen":
|
|
173
|
+
this.screen = msg.data;
|
|
174
|
+
for (const listener of this.screenListeners) listener(msg.data);
|
|
175
|
+
const screenResolver = this.pendingResolvers.get("screen");
|
|
176
|
+
if (screenResolver) {
|
|
177
|
+
this.pendingResolvers.delete("screen");
|
|
178
|
+
screenResolver(msg.data);
|
|
179
|
+
}
|
|
180
|
+
break;
|
|
181
|
+
case "status":
|
|
182
|
+
this.status = msg.data;
|
|
183
|
+
for (const listener of this.statusListeners) listener(msg.data);
|
|
184
|
+
break;
|
|
185
|
+
case "error":
|
|
186
|
+
const errorResolver = this.pendingResolvers.get("screen");
|
|
187
|
+
if (errorResolver) {
|
|
188
|
+
this.pendingResolvers.delete("screen");
|
|
189
|
+
errorResolver(null);
|
|
190
|
+
}
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
sendAndWaitForScreen(msg) {
|
|
195
|
+
return new Promise((resolve) => {
|
|
196
|
+
const timeout = setTimeout(() => {
|
|
197
|
+
this.pendingResolvers.delete("screen");
|
|
198
|
+
resolve({ success: true, ...this.screenToResult() });
|
|
199
|
+
}, 5e3);
|
|
200
|
+
this.pendingResolvers.set("screen", (screen) => {
|
|
201
|
+
clearTimeout(timeout);
|
|
202
|
+
if (screen) {
|
|
203
|
+
resolve({
|
|
204
|
+
success: true,
|
|
205
|
+
cursor_row: screen.cursor_row,
|
|
206
|
+
cursor_col: screen.cursor_col,
|
|
207
|
+
content: screen.content,
|
|
208
|
+
screen_signature: screen.screen_signature
|
|
209
|
+
});
|
|
210
|
+
} else {
|
|
211
|
+
resolve({ success: false, error: "No screen data received" });
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
this.wsSend(msg);
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
screenToResult() {
|
|
218
|
+
if (!this.screen) return {};
|
|
219
|
+
return {
|
|
220
|
+
cursor_row: this.screen.cursor_row,
|
|
221
|
+
cursor_col: this.screen.cursor_col,
|
|
222
|
+
content: this.screen.content,
|
|
223
|
+
screen_signature: this.screen.screen_signature
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
wsSend(data) {
|
|
227
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
228
|
+
this.ws.send(JSON.stringify(data));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
// src/hooks/useTerminal.ts
|
|
5
234
|
import { useState, useCallback, useEffect, useRef } from "react";
|
|
6
235
|
function useTerminalConnection(adapter) {
|
|
7
236
|
const [status, setStatus] = useState(null);
|
|
@@ -122,9 +351,6 @@ function useTerminalInput(adapter) {
|
|
|
122
351
|
}, [adapter]);
|
|
123
352
|
return { loading, error, sendText, sendKey };
|
|
124
353
|
}
|
|
125
|
-
var useTN5250Connection = useTerminalConnection;
|
|
126
|
-
var useTN5250Screen = useTerminalScreen;
|
|
127
|
-
var useTN5250Terminal = useTerminalInput;
|
|
128
354
|
|
|
129
355
|
// src/hooks/useTypingAnimation.ts
|
|
130
356
|
import { useState as useState2, useEffect as useEffect2, useRef as useRef2, useCallback as useCallback2, useMemo } from "react";
|
|
@@ -572,8 +798,8 @@ function TerminalBootLoader({
|
|
|
572
798
|
);
|
|
573
799
|
}
|
|
574
800
|
|
|
575
|
-
// src/components/
|
|
576
|
-
import {
|
|
801
|
+
// src/components/Icons.tsx
|
|
802
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
577
803
|
var TerminalIcon = ({ size = 14 }) => /* @__PURE__ */ jsxs2("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
578
804
|
/* @__PURE__ */ jsx2("polyline", { points: "4 17 10 11 4 5" }),
|
|
579
805
|
/* @__PURE__ */ jsx2("line", { x1: "12", y1: "19", x2: "20", y2: "19" })
|
|
@@ -605,6 +831,10 @@ var RefreshIcon = ({ size = 12, className }) => /* @__PURE__ */ jsxs2("svg", { w
|
|
|
605
831
|
] });
|
|
606
832
|
var KeyIcon = ({ size = 12, style: s }) => /* @__PURE__ */ jsx2("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", style: s, children: /* @__PURE__ */ jsx2("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" }) });
|
|
607
833
|
var MinimizeIcon = () => /* @__PURE__ */ jsx2("svg", { width: 14, height: 14, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx2("path", { d: "M4 14h6v6M3 21l7-7M20 10h-6V4M21 3l-7 7" }) });
|
|
834
|
+
|
|
835
|
+
// src/components/InlineSignIn.tsx
|
|
836
|
+
import { useState as useState4 } from "react";
|
|
837
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
608
838
|
var PROTOCOL_OPTIONS = [
|
|
609
839
|
{ value: "tn5250", label: "TN5250 (IBM i)" },
|
|
610
840
|
{ value: "tn3270", label: "TN3270 (Mainframe)" },
|
|
@@ -647,42 +877,56 @@ function InlineSignIn({ defaultProtocol, loading, error, onConnect }) {
|
|
|
647
877
|
color: "var(--gs-muted, #94a3b8)",
|
|
648
878
|
fontFamily: "var(--gs-font)"
|
|
649
879
|
};
|
|
650
|
-
return /* @__PURE__ */
|
|
651
|
-
/* @__PURE__ */
|
|
652
|
-
/* @__PURE__ */
|
|
653
|
-
/* @__PURE__ */
|
|
880
|
+
return /* @__PURE__ */ jsxs3("form", { onSubmit: handleSubmit, className: "gs-signin", children: [
|
|
881
|
+
/* @__PURE__ */ jsxs3("div", { style: { textAlign: "center", marginBottom: "16px" }, children: [
|
|
882
|
+
/* @__PURE__ */ jsx3(TerminalIcon, { size: 28 }),
|
|
883
|
+
/* @__PURE__ */ jsx3("div", { style: { fontSize: "11px", letterSpacing: "0.15em", textTransform: "uppercase", color: "var(--gs-muted)", marginTop: "8px" }, children: "Connect to Host" })
|
|
654
884
|
] }),
|
|
655
|
-
/* @__PURE__ */
|
|
656
|
-
/* @__PURE__ */
|
|
657
|
-
/* @__PURE__ */
|
|
658
|
-
/* @__PURE__ */
|
|
885
|
+
/* @__PURE__ */ jsxs3("div", { className: "gs-signin-row", children: [
|
|
886
|
+
/* @__PURE__ */ jsxs3("div", { style: { flex: 1 }, children: [
|
|
887
|
+
/* @__PURE__ */ jsx3("label", { style: labelStyle, children: "Host" }),
|
|
888
|
+
/* @__PURE__ */ jsx3("input", { style: inputStyle, value: host, onChange: (e) => setHost(e.target.value), placeholder: "192.168.1.100", required: true, autoFocus: true })
|
|
659
889
|
] }),
|
|
660
|
-
/* @__PURE__ */
|
|
661
|
-
/* @__PURE__ */
|
|
662
|
-
/* @__PURE__ */
|
|
890
|
+
/* @__PURE__ */ jsxs3("div", { style: { width: "72px" }, children: [
|
|
891
|
+
/* @__PURE__ */ jsx3("label", { style: labelStyle, children: "Port" }),
|
|
892
|
+
/* @__PURE__ */ jsx3("input", { style: inputStyle, value: port, onChange: (e) => setPort(e.target.value), placeholder: "23", type: "number", min: "1", max: "65535" })
|
|
663
893
|
] })
|
|
664
894
|
] }),
|
|
665
|
-
/* @__PURE__ */
|
|
666
|
-
/* @__PURE__ */
|
|
667
|
-
/* @__PURE__ */
|
|
895
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
896
|
+
/* @__PURE__ */ jsx3("label", { style: labelStyle, children: "Protocol" }),
|
|
897
|
+
/* @__PURE__ */ jsx3("select", { style: { ...inputStyle, appearance: "none" }, value: selectedProtocol, onChange: (e) => setSelectedProtocol(e.target.value), children: PROTOCOL_OPTIONS.map((o) => /* @__PURE__ */ jsx3("option", { value: o.value, children: o.label }, o.value)) })
|
|
668
898
|
] }),
|
|
669
|
-
/* @__PURE__ */
|
|
670
|
-
/* @__PURE__ */
|
|
671
|
-
/* @__PURE__ */
|
|
899
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
900
|
+
/* @__PURE__ */ jsx3("label", { style: labelStyle, children: "Username" }),
|
|
901
|
+
/* @__PURE__ */ jsx3("input", { style: inputStyle, value: username, onChange: (e) => setUsername(e.target.value), required: true, autoComplete: "username" })
|
|
672
902
|
] }),
|
|
673
|
-
/* @__PURE__ */
|
|
674
|
-
/* @__PURE__ */
|
|
675
|
-
/* @__PURE__ */
|
|
903
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
904
|
+
/* @__PURE__ */ jsx3("label", { style: labelStyle, children: "Password" }),
|
|
905
|
+
/* @__PURE__ */ jsx3("input", { style: inputStyle, type: "password", value: password, onChange: (e) => setPassword(e.target.value), required: true, autoComplete: "current-password" })
|
|
676
906
|
] }),
|
|
677
|
-
error && /* @__PURE__ */
|
|
678
|
-
/* @__PURE__ */
|
|
679
|
-
/* @__PURE__ */
|
|
907
|
+
error && /* @__PURE__ */ jsxs3("div", { style: { color: "#FF6B00", fontSize: "11px", fontFamily: "var(--gs-font)", display: "flex", alignItems: "center", gap: "6px" }, children: [
|
|
908
|
+
/* @__PURE__ */ jsx3(AlertTriangleIcon, { size: 12 }),
|
|
909
|
+
/* @__PURE__ */ jsx3("span", { children: error })
|
|
680
910
|
] }),
|
|
681
|
-
/* @__PURE__ */
|
|
911
|
+
/* @__PURE__ */ jsx3("button", { type: "submit", disabled: loading || !host || !username || !password, className: "gs-signin-btn", children: loading ? "Connecting..." : "Connect" })
|
|
682
912
|
] });
|
|
683
913
|
}
|
|
914
|
+
|
|
915
|
+
// src/components/GreenScreenTerminal.tsx
|
|
916
|
+
import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
917
|
+
var noopResult = { success: false, error: "No adapter configured" };
|
|
918
|
+
var noopAdapter = {
|
|
919
|
+
getScreen: async () => null,
|
|
920
|
+
getStatus: async () => ({ connected: false, status: "disconnected" }),
|
|
921
|
+
sendText: async () => noopResult,
|
|
922
|
+
sendKey: async () => noopResult,
|
|
923
|
+
connect: async () => noopResult,
|
|
924
|
+
disconnect: async () => noopResult,
|
|
925
|
+
reconnect: async () => noopResult
|
|
926
|
+
};
|
|
684
927
|
function GreenScreenTerminal({
|
|
685
|
-
adapter,
|
|
928
|
+
adapter: externalAdapter,
|
|
929
|
+
baseUrl,
|
|
686
930
|
protocol,
|
|
687
931
|
protocolProfile: customProfile,
|
|
688
932
|
screenData: externalScreenData,
|
|
@@ -695,7 +939,7 @@ function GreenScreenTerminal({
|
|
|
695
939
|
showHeader = true,
|
|
696
940
|
typingAnimation = true,
|
|
697
941
|
typingBudgetMs = 60,
|
|
698
|
-
inlineSignIn =
|
|
942
|
+
inlineSignIn = true,
|
|
699
943
|
defaultProtocol: signInDefaultProtocol,
|
|
700
944
|
onSignIn,
|
|
701
945
|
bootLoader,
|
|
@@ -708,6 +952,16 @@ function GreenScreenTerminal({
|
|
|
708
952
|
style
|
|
709
953
|
}) {
|
|
710
954
|
const profile = customProfile ?? getProtocolProfile(protocol);
|
|
955
|
+
const [internalAdapter, setInternalAdapter] = useState5(null);
|
|
956
|
+
const baseUrlAdapter = useMemo2(
|
|
957
|
+
() => baseUrl ? new RestAdapter({ baseUrl }) : null,
|
|
958
|
+
[baseUrl]
|
|
959
|
+
);
|
|
960
|
+
const defaultWsAdapter = useMemo2(
|
|
961
|
+
() => !externalAdapter && !baseUrl ? new WebSocketAdapter() : null,
|
|
962
|
+
[externalAdapter, baseUrl]
|
|
963
|
+
);
|
|
964
|
+
const adapter = externalAdapter ?? baseUrlAdapter ?? internalAdapter ?? defaultWsAdapter ?? noopAdapter;
|
|
711
965
|
const shouldPoll = pollInterval > 0 && !externalScreenData;
|
|
712
966
|
const { data: polledScreenData, error: screenError } = useTerminalScreen(adapter, pollInterval, shouldPoll);
|
|
713
967
|
const { sendText: _sendText, sendKey: _sendKey } = useTerminalInput(adapter);
|
|
@@ -732,11 +986,11 @@ function GreenScreenTerminal({
|
|
|
732
986
|
}, [screenData, onScreenChange]);
|
|
733
987
|
const sendText = useCallback3(async (text) => _sendText(text), [_sendText]);
|
|
734
988
|
const sendKey = useCallback3(async (key) => _sendKey(key), [_sendKey]);
|
|
735
|
-
const [inputText, setInputText] =
|
|
736
|
-
const [isFocused, setIsFocused] =
|
|
989
|
+
const [inputText, setInputText] = useState5("");
|
|
990
|
+
const [isFocused, setIsFocused] = useState5(false);
|
|
737
991
|
const terminalRef = useRef4(null);
|
|
738
992
|
const inputRef = useRef4(null);
|
|
739
|
-
const [syncedCursor, setSyncedCursor] =
|
|
993
|
+
const [syncedCursor, setSyncedCursor] = useState5(null);
|
|
740
994
|
const prevRawContentRef = useRef4("");
|
|
741
995
|
useEffect4(() => {
|
|
742
996
|
const newContent = rawScreenData?.content || "";
|
|
@@ -746,8 +1000,8 @@ function GreenScreenTerminal({
|
|
|
746
1000
|
}
|
|
747
1001
|
prevRawContentRef.current = newContent;
|
|
748
1002
|
}, [rawScreenData?.content]);
|
|
749
|
-
const [autoReconnectAttempt, setAutoReconnectAttempt] =
|
|
750
|
-
const [isAutoReconnecting, setIsAutoReconnecting] =
|
|
1003
|
+
const [autoReconnectAttempt, setAutoReconnectAttempt] = useState5(0);
|
|
1004
|
+
const [isAutoReconnecting, setIsAutoReconnecting] = useState5(false);
|
|
751
1005
|
const reconnectTimeoutRef = useRef4(null);
|
|
752
1006
|
const wasConnectedRef = useRef4(false);
|
|
753
1007
|
const isConnectedRef = useRef4(false);
|
|
@@ -792,10 +1046,17 @@ function GreenScreenTerminal({
|
|
|
792
1046
|
}, [connStatus?.connected, autoReconnectAttempt, isAutoReconnecting, reconnecting, reconnect, autoReconnectEnabled, maxAttempts, onNotification]);
|
|
793
1047
|
const handleSignIn = useCallback3(async (config) => {
|
|
794
1048
|
onSignIn?.(config);
|
|
1049
|
+
if (!externalAdapter && !baseUrlAdapter) {
|
|
1050
|
+
const port = config.port ? `:${config.port}` : "";
|
|
1051
|
+
const newAdapter = new RestAdapter({ baseUrl: `http://${config.host}${port}` });
|
|
1052
|
+
setInternalAdapter(newAdapter);
|
|
1053
|
+
await newAdapter.connect(config);
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
795
1056
|
await connect(config);
|
|
796
|
-
}, [connect, onSignIn]);
|
|
797
|
-
const [showBootLoader, setShowBootLoader] =
|
|
798
|
-
const [bootFadingOut, setBootFadingOut] =
|
|
1057
|
+
}, [connect, onSignIn, externalAdapter, baseUrlAdapter]);
|
|
1058
|
+
const [showBootLoader, setShowBootLoader] = useState5(bootLoader !== false);
|
|
1059
|
+
const [bootFadingOut, setBootFadingOut] = useState5(false);
|
|
799
1060
|
useEffect4(() => {
|
|
800
1061
|
if (screenData?.content && showBootLoader) {
|
|
801
1062
|
setBootFadingOut(true);
|
|
@@ -940,21 +1201,21 @@ function GreenScreenTerminal({
|
|
|
940
1201
|
let match;
|
|
941
1202
|
let segmentIndex = 0;
|
|
942
1203
|
while ((match = underscoreRegex.exec(text)) !== null) {
|
|
943
|
-
if (match.index > lastIndex) segments.push(/* @__PURE__ */
|
|
1204
|
+
if (match.index > lastIndex) segments.push(/* @__PURE__ */ jsx4("span", { children: text.substring(lastIndex, match.index) }, `${keyPrefix}-t-${segmentIndex}`));
|
|
944
1205
|
const count = match[0].length;
|
|
945
|
-
segments.push(/* @__PURE__ */
|
|
1206
|
+
segments.push(/* @__PURE__ */ jsx4("span", { style: { borderBottom: "1px solid var(--gs-green, #10b981)", display: "inline-block", width: `${count}ch` }, children: " ".repeat(count) }, `${keyPrefix}-u-${segmentIndex}`));
|
|
946
1207
|
lastIndex = match.index + match[0].length;
|
|
947
1208
|
segmentIndex++;
|
|
948
1209
|
}
|
|
949
|
-
if (lastIndex < text.length) segments.push(/* @__PURE__ */
|
|
950
|
-
return segments.length > 0 ? /* @__PURE__ */
|
|
1210
|
+
if (lastIndex < text.length) segments.push(/* @__PURE__ */ jsx4("span", { children: text.substring(lastIndex) }, `${keyPrefix}-e`));
|
|
1211
|
+
return segments.length > 0 ? /* @__PURE__ */ jsx4(Fragment, { children: segments }) : /* @__PURE__ */ jsx4(Fragment, { children: text });
|
|
951
1212
|
}, []);
|
|
952
1213
|
const renderRowWithFields = useCallback3((line, rowIndex, fields) => {
|
|
953
1214
|
const inputFields = fields.filter((f) => f.row === rowIndex && f.is_input);
|
|
954
1215
|
const highlightedFields = fields.filter((f) => f.row === rowIndex && f.is_protected && f.is_highlighted);
|
|
955
1216
|
const reverseFields = fields.filter((f) => f.row === rowIndex && f.is_protected && f.is_reverse);
|
|
956
1217
|
const allRowFields = [...inputFields, ...highlightedFields, ...reverseFields];
|
|
957
|
-
if (allRowFields.length === 0) return /* @__PURE__ */
|
|
1218
|
+
if (allRowFields.length === 0) return /* @__PURE__ */ jsx4("span", { children: renderTextWithUnderlines(line, `r${rowIndex}`) });
|
|
958
1219
|
const sorted = [...allRowFields].sort((a, b) => a.col - b.col);
|
|
959
1220
|
const segs = [];
|
|
960
1221
|
let lastEnd = 0;
|
|
@@ -962,35 +1223,35 @@ function GreenScreenTerminal({
|
|
|
962
1223
|
sorted.forEach((field, idx) => {
|
|
963
1224
|
const fs = field.col;
|
|
964
1225
|
const fe = Math.min(field.col + field.length, cols);
|
|
965
|
-
if (fs > lastEnd) segs.push(/* @__PURE__ */
|
|
1226
|
+
if (fs > lastEnd) segs.push(/* @__PURE__ */ jsx4("span", { children: renderTextWithUnderlines(line.substring(lastEnd, fs), `r${rowIndex}p${idx}`) }, `t${idx}`));
|
|
966
1227
|
const fc = line.substring(fs, fe);
|
|
967
1228
|
if (field.is_input) {
|
|
968
1229
|
const w = field.length >= 30 ? Math.max(field.length, 40) : field.length;
|
|
969
|
-
segs.push(/* @__PURE__ */
|
|
1230
|
+
segs.push(/* @__PURE__ */ jsx4("span", { className: "gs-input-field", style: { borderBottom: "2px solid var(--gs-green, #10b981)", display: "inline-block", minWidth: `${w}ch` }, children: fc }, `f${idx}`));
|
|
970
1231
|
} else if (field.is_reverse) {
|
|
971
|
-
segs.push(/* @__PURE__ */
|
|
1232
|
+
segs.push(/* @__PURE__ */ jsx4("span", { style: { color: "#ef4444", fontWeight: "bold" }, children: fc }, `v${idx}`));
|
|
972
1233
|
} else {
|
|
973
|
-
segs.push(/* @__PURE__ */
|
|
1234
|
+
segs.push(/* @__PURE__ */ jsx4("span", { style: { color: "var(--gs-white, #FFFFFF)" }, children: fc }, `h${idx}`));
|
|
974
1235
|
}
|
|
975
1236
|
lastEnd = fe;
|
|
976
1237
|
});
|
|
977
|
-
if (lastEnd < line.length) segs.push(/* @__PURE__ */
|
|
978
|
-
return /* @__PURE__ */
|
|
1238
|
+
if (lastEnd < line.length) segs.push(/* @__PURE__ */ jsx4("span", { children: renderTextWithUnderlines(line.substring(lastEnd), `r${rowIndex}e`) }, "te"));
|
|
1239
|
+
return /* @__PURE__ */ jsx4(Fragment, { children: segs });
|
|
979
1240
|
}, [renderTextWithUnderlines, screenData?.cols, profile.defaultCols]);
|
|
980
1241
|
const renderScreen = () => {
|
|
981
1242
|
if (showBootLoader && !screenData?.content) {
|
|
982
1243
|
if (bootLoader === false) return null;
|
|
983
|
-
if (bootLoader) return /* @__PURE__ */
|
|
984
|
-
return /* @__PURE__ */
|
|
1244
|
+
if (bootLoader) return /* @__PURE__ */ jsx4(Fragment, { children: bootLoader });
|
|
1245
|
+
return /* @__PURE__ */ jsx4(TerminalBootLoader, { brandText: profile.bootText });
|
|
985
1246
|
}
|
|
986
|
-
if (bootFadingOut && screenData?.content) return /* @__PURE__ */
|
|
1247
|
+
if (bootFadingOut && screenData?.content) return /* @__PURE__ */ jsx4("div", { className: "gs-fade-in", children: renderScreenContent() });
|
|
987
1248
|
if (!screenData?.content) {
|
|
988
1249
|
if (inlineSignIn && !connStatus?.connected) {
|
|
989
|
-
return /* @__PURE__ */
|
|
1250
|
+
return /* @__PURE__ */ jsx4("div", { style: { width: `${screenData?.cols || profile.defaultCols}ch`, height: `${(screenData?.rows || profile.defaultRows) * 21}px`, display: "flex", alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ jsx4(InlineSignIn, { defaultProtocol: signInDefaultProtocol || protocol || "tn5250", loading: reconnecting, error: connectError, onConnect: handleSignIn }) });
|
|
990
1251
|
}
|
|
991
|
-
return /* @__PURE__ */
|
|
992
|
-
/* @__PURE__ */
|
|
993
|
-
/* @__PURE__ */
|
|
1252
|
+
return /* @__PURE__ */ jsx4("div", { style: { width: `${screenData?.cols || profile.defaultCols}ch`, height: `${(screenData?.rows || profile.defaultRows) * 21}px`, display: "flex", alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ jsxs4("div", { style: { textAlign: "center" }, children: [
|
|
1253
|
+
/* @__PURE__ */ jsx4("div", { style: { color: "#808080", marginBottom: "12px" }, children: /* @__PURE__ */ jsx4(TerminalIcon, { size: 40 }) }),
|
|
1254
|
+
/* @__PURE__ */ jsx4("p", { style: { fontFamily: "var(--gs-font)", fontSize: "12px", color: "#808080" }, children: connStatus?.connected ? "Waiting for screen data..." : "Not connected" })
|
|
994
1255
|
] }) });
|
|
995
1256
|
}
|
|
996
1257
|
return renderScreenContent();
|
|
@@ -1007,7 +1268,7 @@ function GreenScreenTerminal({
|
|
|
1007
1268
|
const cursor = getCursorPos();
|
|
1008
1269
|
const hasInputFields = fields.some((f) => f.is_input);
|
|
1009
1270
|
const hasCursor = screenData.cursor_row !== void 0 && screenData.cursor_col !== void 0 && (hasInputFields || screenData.cursor_row !== 0 || screenData.cursor_col !== 0);
|
|
1010
|
-
return /* @__PURE__ */
|
|
1271
|
+
return /* @__PURE__ */ jsxs4("div", { style: { fontFamily: "var(--gs-font)", fontSize: "13px", position: "relative", width: `${cols}ch` }, children: [
|
|
1011
1272
|
rows.map((line, index) => {
|
|
1012
1273
|
let displayLine = line;
|
|
1013
1274
|
if (hasCursor && index === cursor.row && inputText && !animatedCursorPos) {
|
|
@@ -1015,12 +1276,12 @@ function GreenScreenTerminal({
|
|
|
1015
1276
|
displayLine = (line.substring(0, baseCol) + inputText + line.substring(baseCol + inputText.length)).substring(0, cols).padEnd(cols, " ");
|
|
1016
1277
|
}
|
|
1017
1278
|
const headerSegments = index === 0 ? profile.colors.parseHeaderRow(displayLine) : null;
|
|
1018
|
-
return /* @__PURE__ */
|
|
1019
|
-
headerSegments ? headerSegments.map((seg, i) => /* @__PURE__ */
|
|
1020
|
-
hasCursor && index === cursor.row && /* @__PURE__ */
|
|
1279
|
+
return /* @__PURE__ */ jsxs4("div", { className: headerSegments ? "" : profile.colors.getRowColorClass(index, displayLine, termRows), style: { height: `${ROW_HEIGHT}px`, lineHeight: `${ROW_HEIGHT}px`, whiteSpace: "pre", position: "relative" }, children: [
|
|
1280
|
+
headerSegments ? headerSegments.map((seg, i) => /* @__PURE__ */ jsx4("span", { className: seg.colorClass, children: seg.text }, i)) : renderRowWithFields(displayLine, index, fields),
|
|
1281
|
+
hasCursor && index === cursor.row && /* @__PURE__ */ jsx4("span", { className: "gs-cursor", style: { position: "absolute", left: `${cursor.col}ch`, width: "1ch", height: `${ROW_HEIGHT}px`, top: 0, pointerEvents: "none" } })
|
|
1021
1282
|
] }, index);
|
|
1022
1283
|
}),
|
|
1023
|
-
screenData.cursor_row !== void 0 && screenData.cursor_col !== void 0 && /* @__PURE__ */
|
|
1284
|
+
screenData.cursor_row !== void 0 && screenData.cursor_col !== void 0 && /* @__PURE__ */ jsxs4("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: [
|
|
1024
1285
|
String(screenData.cursor_row + 1).padStart(2, "0"),
|
|
1025
1286
|
"/",
|
|
1026
1287
|
String(screenData.cursor_col + 1).padStart(3, "0")
|
|
@@ -1050,49 +1311,49 @@ function GreenScreenTerminal({
|
|
|
1050
1311
|
return "#64748b";
|
|
1051
1312
|
}
|
|
1052
1313
|
};
|
|
1053
|
-
return /* @__PURE__ */
|
|
1054
|
-
showHeader && /* @__PURE__ */
|
|
1055
|
-
/* @__PURE__ */
|
|
1056
|
-
/* @__PURE__ */
|
|
1057
|
-
/* @__PURE__ */
|
|
1058
|
-
isFocused && /* @__PURE__ */
|
|
1059
|
-
screenData?.timestamp && /* @__PURE__ */
|
|
1060
|
-
/* @__PURE__ */
|
|
1314
|
+
return /* @__PURE__ */ jsxs4("div", { className: `gs-terminal ${isFocused ? "gs-terminal-focused" : ""} ${className || ""}`, style, children: [
|
|
1315
|
+
showHeader && /* @__PURE__ */ jsx4("div", { className: "gs-header", children: embedded ? /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
1316
|
+
/* @__PURE__ */ jsxs4("span", { className: "gs-header-left", children: [
|
|
1317
|
+
/* @__PURE__ */ jsx4(TerminalIcon, { size: 14 }),
|
|
1318
|
+
/* @__PURE__ */ jsx4("span", { children: "TERMINAL" }),
|
|
1319
|
+
isFocused && /* @__PURE__ */ jsx4("span", { className: "gs-badge-focused", children: "FOCUSED" }),
|
|
1320
|
+
screenData?.timestamp && /* @__PURE__ */ jsx4("span", { className: "gs-timestamp", children: new Date(screenData.timestamp).toLocaleTimeString() }),
|
|
1321
|
+
/* @__PURE__ */ jsx4("span", { className: "gs-hint", children: readOnly ? "Read-only" : isFocused ? "ESC to exit focus" : "Click to control" })
|
|
1061
1322
|
] }),
|
|
1062
|
-
/* @__PURE__ */
|
|
1063
|
-
connStatus?.status && /* @__PURE__ */
|
|
1064
|
-
connStatus && (connStatus.connected ? /* @__PURE__ */
|
|
1065
|
-
onMinimize && /* @__PURE__ */
|
|
1323
|
+
/* @__PURE__ */ jsxs4("div", { className: "gs-header-right", children: [
|
|
1324
|
+
connStatus?.status && /* @__PURE__ */ jsx4(KeyIcon, { size: 12, style: { color: getStatusColor(connStatus.status) } }),
|
|
1325
|
+
connStatus && (connStatus.connected ? /* @__PURE__ */ jsx4(WifiIcon, { size: 12, style: { color: "var(--gs-green, #10b981)" } }) : /* @__PURE__ */ jsx4(WifiOffIcon, { size: 12, style: { color: "#FF6B00" } })),
|
|
1326
|
+
onMinimize && /* @__PURE__ */ jsx4("button", { onClick: (e) => {
|
|
1066
1327
|
e.stopPropagation();
|
|
1067
1328
|
onMinimize();
|
|
1068
|
-
}, className: "gs-btn-icon", title: "Minimize terminal", children: /* @__PURE__ */
|
|
1329
|
+
}, className: "gs-btn-icon", title: "Minimize terminal", children: /* @__PURE__ */ jsx4(MinimizeIcon, {}) }),
|
|
1069
1330
|
headerRight
|
|
1070
1331
|
] })
|
|
1071
|
-
] }) : /* @__PURE__ */
|
|
1072
|
-
/* @__PURE__ */
|
|
1073
|
-
/* @__PURE__ */
|
|
1074
|
-
/* @__PURE__ */
|
|
1075
|
-
isFocused && /* @__PURE__ */
|
|
1076
|
-
screenData?.timestamp && /* @__PURE__ */
|
|
1077
|
-
/* @__PURE__ */
|
|
1332
|
+
] }) : /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
1333
|
+
/* @__PURE__ */ jsxs4("span", { className: "gs-header-left", children: [
|
|
1334
|
+
/* @__PURE__ */ jsx4(TerminalIcon, { size: 14 }),
|
|
1335
|
+
/* @__PURE__ */ jsx4("span", { children: profile.headerLabel }),
|
|
1336
|
+
isFocused && /* @__PURE__ */ jsx4("span", { className: "gs-badge-focused", children: "FOCUSED" }),
|
|
1337
|
+
screenData?.timestamp && /* @__PURE__ */ jsx4("span", { className: "gs-timestamp", children: new Date(screenData.timestamp).toLocaleTimeString() }),
|
|
1338
|
+
/* @__PURE__ */ jsx4("span", { className: "gs-hint", children: readOnly ? "Read-only mode" : isFocused ? "ESC to exit focus" : "Click terminal to control" })
|
|
1078
1339
|
] }),
|
|
1079
|
-
/* @__PURE__ */
|
|
1080
|
-
connStatus && /* @__PURE__ */
|
|
1081
|
-
/* @__PURE__ */
|
|
1082
|
-
/* @__PURE__ */
|
|
1083
|
-
] }) : /* @__PURE__ */
|
|
1084
|
-
/* @__PURE__ */
|
|
1085
|
-
/* @__PURE__ */
|
|
1086
|
-
/* @__PURE__ */
|
|
1340
|
+
/* @__PURE__ */ jsxs4("div", { className: "gs-header-right", children: [
|
|
1341
|
+
connStatus && /* @__PURE__ */ jsx4("div", { className: "gs-status-group", children: connStatus.connected ? /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
1342
|
+
/* @__PURE__ */ jsx4(WifiIcon, { size: 12, style: { color: "var(--gs-green, #10b981)" } }),
|
|
1343
|
+
/* @__PURE__ */ jsx4("span", { className: "gs-host", children: connStatus.host })
|
|
1344
|
+
] }) : /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
1345
|
+
/* @__PURE__ */ jsx4(WifiOffIcon, { size: 12, style: { color: "#FF6B00" } }),
|
|
1346
|
+
/* @__PURE__ */ jsx4("span", { className: "gs-disconnected-text", children: isAutoReconnecting || reconnecting ? `RECONNECTING${autoReconnectAttempt > 0 ? ` (${autoReconnectAttempt}/${maxAttempts})` : "..."}` : autoReconnectAttempt >= maxAttempts ? "DISCONNECTED (auto-retry exhausted)" : "DISCONNECTED" }),
|
|
1347
|
+
/* @__PURE__ */ jsx4("button", { onClick: handleReconnect, disabled: reconnecting || isAutoReconnecting, className: "gs-btn-icon", title: "Reconnect", children: /* @__PURE__ */ jsx4(RefreshIcon, { size: 12, className: reconnecting || isAutoReconnecting ? "gs-spin" : "" }) })
|
|
1087
1348
|
] }) }),
|
|
1088
|
-
connStatus?.status && /* @__PURE__ */
|
|
1089
|
-
/* @__PURE__ */
|
|
1090
|
-
connStatus.username && /* @__PURE__ */
|
|
1349
|
+
connStatus?.status && /* @__PURE__ */ jsxs4("div", { className: "gs-status-group", children: [
|
|
1350
|
+
/* @__PURE__ */ jsx4(KeyIcon, { size: 12, style: { color: getStatusColor(connStatus.status) } }),
|
|
1351
|
+
connStatus.username && /* @__PURE__ */ jsx4("span", { className: "gs-host", children: connStatus.username })
|
|
1091
1352
|
] }),
|
|
1092
1353
|
headerRight
|
|
1093
1354
|
] })
|
|
1094
1355
|
] }) }),
|
|
1095
|
-
/* @__PURE__ */
|
|
1356
|
+
/* @__PURE__ */ jsx4("div", { className: "gs-body", children: /* @__PURE__ */ jsxs4(
|
|
1096
1357
|
"div",
|
|
1097
1358
|
{
|
|
1098
1359
|
ref: terminalRef,
|
|
@@ -1100,17 +1361,17 @@ function GreenScreenTerminal({
|
|
|
1100
1361
|
className: `gs-screen ${embedded ? "gs-screen-embedded" : ""}`,
|
|
1101
1362
|
style: !embedded ? { width: `calc(${screenData?.cols || profile.defaultCols}ch + 24px)`, fontSize: (screenData?.cols ?? profile.defaultCols) > 80 ? "11px" : "13px", fontFamily: "var(--gs-font)" } : void 0,
|
|
1102
1363
|
children: [
|
|
1103
|
-
screenError != null && /* @__PURE__ */
|
|
1104
|
-
/* @__PURE__ */
|
|
1105
|
-
/* @__PURE__ */
|
|
1364
|
+
screenError != null && /* @__PURE__ */ jsxs4("div", { className: "gs-error-banner", children: [
|
|
1365
|
+
/* @__PURE__ */ jsx4(AlertTriangleIcon, { size: 14 }),
|
|
1366
|
+
/* @__PURE__ */ jsx4("span", { children: String(screenError) })
|
|
1106
1367
|
] }),
|
|
1107
|
-
/* @__PURE__ */
|
|
1368
|
+
/* @__PURE__ */ jsx4("div", { className: "gs-screen-content", children: renderScreen() }),
|
|
1108
1369
|
overlay,
|
|
1109
|
-
connStatus && !connStatus.connected && screenData && /* @__PURE__ */
|
|
1110
|
-
/* @__PURE__ */
|
|
1111
|
-
/* @__PURE__ */
|
|
1370
|
+
connStatus && !connStatus.connected && screenData && /* @__PURE__ */ jsxs4("div", { className: "gs-overlay", children: [
|
|
1371
|
+
/* @__PURE__ */ jsx4(WifiOffIcon, { size: 28 }),
|
|
1372
|
+
/* @__PURE__ */ jsx4("span", { children: isAutoReconnecting || reconnecting ? "Reconnecting..." : "Disconnected" })
|
|
1112
1373
|
] }),
|
|
1113
|
-
/* @__PURE__ */
|
|
1374
|
+
/* @__PURE__ */ jsx4(
|
|
1114
1375
|
"input",
|
|
1115
1376
|
{
|
|
1116
1377
|
ref: inputRef,
|
|
@@ -1130,66 +1391,6 @@ function GreenScreenTerminal({
|
|
|
1130
1391
|
) })
|
|
1131
1392
|
] });
|
|
1132
1393
|
}
|
|
1133
|
-
var TN5250Terminal = GreenScreenTerminal;
|
|
1134
|
-
|
|
1135
|
-
// src/adapters/RestAdapter.ts
|
|
1136
|
-
var RestAdapter = class {
|
|
1137
|
-
constructor(options) {
|
|
1138
|
-
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
1139
|
-
this.staticHeaders = options.headers || {};
|
|
1140
|
-
this.getHeaders = options.getHeaders;
|
|
1141
|
-
}
|
|
1142
|
-
async buildHeaders() {
|
|
1143
|
-
const dynamic = this.getHeaders ? await this.getHeaders() : {};
|
|
1144
|
-
return {
|
|
1145
|
-
"Content-Type": "application/json",
|
|
1146
|
-
...this.staticHeaders,
|
|
1147
|
-
...dynamic
|
|
1148
|
-
};
|
|
1149
|
-
}
|
|
1150
|
-
async request(method, path, body) {
|
|
1151
|
-
const headers = await this.buildHeaders();
|
|
1152
|
-
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
1153
|
-
method,
|
|
1154
|
-
headers,
|
|
1155
|
-
body: body ? JSON.stringify(body) : void 0
|
|
1156
|
-
});
|
|
1157
|
-
if (!response.ok) {
|
|
1158
|
-
const detail = await response.json().catch(() => ({}));
|
|
1159
|
-
throw new Error(detail?.detail || `HTTP ${response.status}`);
|
|
1160
|
-
}
|
|
1161
|
-
return response.json();
|
|
1162
|
-
}
|
|
1163
|
-
async getScreen() {
|
|
1164
|
-
try {
|
|
1165
|
-
return await this.request("GET", "/screen");
|
|
1166
|
-
} catch (e) {
|
|
1167
|
-
const message = e instanceof Error ? e.message : String(e);
|
|
1168
|
-
if (message.includes("503") || message.includes("404")) {
|
|
1169
|
-
return null;
|
|
1170
|
-
}
|
|
1171
|
-
throw e;
|
|
1172
|
-
}
|
|
1173
|
-
}
|
|
1174
|
-
async getStatus() {
|
|
1175
|
-
return this.request("GET", "/status");
|
|
1176
|
-
}
|
|
1177
|
-
async sendText(text) {
|
|
1178
|
-
return this.request("POST", "/send-text", { text });
|
|
1179
|
-
}
|
|
1180
|
-
async sendKey(key) {
|
|
1181
|
-
return this.request("POST", "/send-key", { key });
|
|
1182
|
-
}
|
|
1183
|
-
async connect(config) {
|
|
1184
|
-
return this.request("POST", "/connect", config);
|
|
1185
|
-
}
|
|
1186
|
-
async disconnect() {
|
|
1187
|
-
return this.request("POST", "/disconnect");
|
|
1188
|
-
}
|
|
1189
|
-
async reconnect() {
|
|
1190
|
-
return this.request("POST", "/reconnect");
|
|
1191
|
-
}
|
|
1192
|
-
};
|
|
1193
1394
|
|
|
1194
1395
|
// src/utils/rendering.ts
|
|
1195
1396
|
function getRowColorClass(rowIndex, rowContent) {
|
|
@@ -1199,10 +1400,18 @@ function parseHeaderRow(line) {
|
|
|
1199
1400
|
return getProtocolProfile("tn5250").colors.parseHeaderRow(line);
|
|
1200
1401
|
}
|
|
1201
1402
|
export {
|
|
1403
|
+
AlertTriangleIcon,
|
|
1202
1404
|
GreenScreenTerminal,
|
|
1405
|
+
InlineSignIn,
|
|
1406
|
+
KeyIcon,
|
|
1407
|
+
MinimizeIcon,
|
|
1408
|
+
RefreshIcon,
|
|
1203
1409
|
RestAdapter,
|
|
1204
|
-
TN5250Terminal,
|
|
1205
1410
|
TerminalBootLoader,
|
|
1411
|
+
TerminalIcon,
|
|
1412
|
+
WebSocketAdapter,
|
|
1413
|
+
WifiIcon,
|
|
1414
|
+
WifiOffIcon,
|
|
1206
1415
|
getProtocolProfile,
|
|
1207
1416
|
getRowColorClass,
|
|
1208
1417
|
hp6530Profile,
|
|
@@ -1211,9 +1420,6 @@ export {
|
|
|
1211
1420
|
positionToRowCol,
|
|
1212
1421
|
tn3270Profile,
|
|
1213
1422
|
tn5250Profile,
|
|
1214
|
-
useTN5250Connection,
|
|
1215
|
-
useTN5250Screen,
|
|
1216
|
-
useTN5250Terminal,
|
|
1217
1423
|
useTerminalConnection,
|
|
1218
1424
|
useTerminalInput,
|
|
1219
1425
|
useTerminalScreen,
|