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.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,238 @@ __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");
|
|
48
53
|
|
|
49
|
-
// src/
|
|
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 WebSocketAdapter = class {
|
|
115
|
+
constructor(options = {}) {
|
|
116
|
+
this.ws = null;
|
|
117
|
+
this.screen = null;
|
|
118
|
+
this.status = { connected: false, status: "disconnected" };
|
|
119
|
+
this.pendingResolvers = /* @__PURE__ */ new Map();
|
|
120
|
+
this.screenListeners = /* @__PURE__ */ new Set();
|
|
121
|
+
this.statusListeners = /* @__PURE__ */ new Set();
|
|
122
|
+
this.workerUrl = (options.workerUrl || "http://localhost:3001").replace(/\/+$/, "");
|
|
123
|
+
}
|
|
124
|
+
/** Subscribe to real-time screen updates */
|
|
125
|
+
onScreen(listener) {
|
|
126
|
+
this.screenListeners.add(listener);
|
|
127
|
+
return () => this.screenListeners.delete(listener);
|
|
128
|
+
}
|
|
129
|
+
/** Subscribe to status changes */
|
|
130
|
+
onStatus(listener) {
|
|
131
|
+
this.statusListeners.add(listener);
|
|
132
|
+
return () => this.statusListeners.delete(listener);
|
|
133
|
+
}
|
|
134
|
+
async getScreen() {
|
|
135
|
+
return this.screen;
|
|
136
|
+
}
|
|
137
|
+
async getStatus() {
|
|
138
|
+
return this.status;
|
|
139
|
+
}
|
|
140
|
+
async sendText(text) {
|
|
141
|
+
return this.sendAndWaitForScreen({ type: "text", text });
|
|
142
|
+
}
|
|
143
|
+
async sendKey(key) {
|
|
144
|
+
return this.sendAndWaitForScreen({ type: "key", key });
|
|
145
|
+
}
|
|
146
|
+
async connect(config) {
|
|
147
|
+
await this.ensureWebSocket();
|
|
148
|
+
if (!config) {
|
|
149
|
+
return { success: false, error: "ConnectConfig required" };
|
|
150
|
+
}
|
|
151
|
+
return new Promise((resolve) => {
|
|
152
|
+
const timeout = setTimeout(() => {
|
|
153
|
+
resolve({ success: false, error: "Connection timeout" });
|
|
154
|
+
}, 3e4);
|
|
155
|
+
const onMessage = (event) => {
|
|
156
|
+
try {
|
|
157
|
+
const msg = JSON.parse(event.data);
|
|
158
|
+
if (msg.type === "connected") {
|
|
159
|
+
clearTimeout(timeout);
|
|
160
|
+
this.ws?.removeEventListener("message", onMessage);
|
|
161
|
+
resolve({ success: true });
|
|
162
|
+
} else if (msg.type === "error") {
|
|
163
|
+
clearTimeout(timeout);
|
|
164
|
+
this.ws?.removeEventListener("message", onMessage);
|
|
165
|
+
resolve({ success: false, error: msg.message });
|
|
166
|
+
}
|
|
167
|
+
} catch {
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
this.ws?.addEventListener("message", onMessage);
|
|
171
|
+
this.wsSend({
|
|
172
|
+
type: "connect",
|
|
173
|
+
host: config.host,
|
|
174
|
+
port: config.port,
|
|
175
|
+
protocol: config.protocol,
|
|
176
|
+
username: config.username,
|
|
177
|
+
password: config.password
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
async disconnect() {
|
|
182
|
+
this.wsSend({ type: "disconnect" });
|
|
183
|
+
this.status = { connected: false, status: "disconnected" };
|
|
184
|
+
if (this.ws) {
|
|
185
|
+
this.ws.close();
|
|
186
|
+
this.ws = null;
|
|
187
|
+
}
|
|
188
|
+
return { success: true };
|
|
189
|
+
}
|
|
190
|
+
async reconnect() {
|
|
191
|
+
return { success: false, error: "Use disconnect() then connect() instead" };
|
|
192
|
+
}
|
|
193
|
+
/** Close the WebSocket without sending disconnect */
|
|
194
|
+
dispose() {
|
|
195
|
+
if (this.ws) {
|
|
196
|
+
this.ws.close();
|
|
197
|
+
this.ws = null;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
async ensureWebSocket() {
|
|
201
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) return;
|
|
202
|
+
return new Promise((resolve, reject) => {
|
|
203
|
+
const wsUrl = this.workerUrl.replace(/^http/, "ws") + "/ws";
|
|
204
|
+
this.ws = new WebSocket(wsUrl);
|
|
205
|
+
this.ws.onopen = () => resolve();
|
|
206
|
+
this.ws.onerror = () => reject(new Error("WebSocket connection failed"));
|
|
207
|
+
this.ws.onmessage = (event) => {
|
|
208
|
+
try {
|
|
209
|
+
const msg = JSON.parse(event.data);
|
|
210
|
+
this.handleMessage(msg);
|
|
211
|
+
} catch {
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
this.ws.onclose = () => {
|
|
215
|
+
this.status = { connected: false, status: "disconnected" };
|
|
216
|
+
for (const listener of this.statusListeners) listener(this.status);
|
|
217
|
+
};
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
handleMessage(msg) {
|
|
221
|
+
switch (msg.type) {
|
|
222
|
+
case "screen":
|
|
223
|
+
this.screen = msg.data;
|
|
224
|
+
for (const listener of this.screenListeners) listener(msg.data);
|
|
225
|
+
const screenResolver = this.pendingResolvers.get("screen");
|
|
226
|
+
if (screenResolver) {
|
|
227
|
+
this.pendingResolvers.delete("screen");
|
|
228
|
+
screenResolver(msg.data);
|
|
229
|
+
}
|
|
230
|
+
break;
|
|
231
|
+
case "status":
|
|
232
|
+
this.status = msg.data;
|
|
233
|
+
for (const listener of this.statusListeners) listener(msg.data);
|
|
234
|
+
break;
|
|
235
|
+
case "error":
|
|
236
|
+
const errorResolver = this.pendingResolvers.get("screen");
|
|
237
|
+
if (errorResolver) {
|
|
238
|
+
this.pendingResolvers.delete("screen");
|
|
239
|
+
errorResolver(null);
|
|
240
|
+
}
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
sendAndWaitForScreen(msg) {
|
|
245
|
+
return new Promise((resolve) => {
|
|
246
|
+
const timeout = setTimeout(() => {
|
|
247
|
+
this.pendingResolvers.delete("screen");
|
|
248
|
+
resolve({ success: true, ...this.screenToResult() });
|
|
249
|
+
}, 5e3);
|
|
250
|
+
this.pendingResolvers.set("screen", (screen) => {
|
|
251
|
+
clearTimeout(timeout);
|
|
252
|
+
if (screen) {
|
|
253
|
+
resolve({
|
|
254
|
+
success: true,
|
|
255
|
+
cursor_row: screen.cursor_row,
|
|
256
|
+
cursor_col: screen.cursor_col,
|
|
257
|
+
content: screen.content,
|
|
258
|
+
screen_signature: screen.screen_signature
|
|
259
|
+
});
|
|
260
|
+
} else {
|
|
261
|
+
resolve({ success: false, error: "No screen data received" });
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
this.wsSend(msg);
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
screenToResult() {
|
|
268
|
+
if (!this.screen) return {};
|
|
269
|
+
return {
|
|
270
|
+
cursor_row: this.screen.cursor_row,
|
|
271
|
+
cursor_col: this.screen.cursor_col,
|
|
272
|
+
content: this.screen.content,
|
|
273
|
+
screen_signature: this.screen.screen_signature
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
wsSend(data) {
|
|
277
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
278
|
+
this.ws.send(JSON.stringify(data));
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
// src/hooks/useTerminal.ts
|
|
50
284
|
var import_react = require("react");
|
|
51
285
|
function useTerminalConnection(adapter) {
|
|
52
286
|
const [status, setStatus] = (0, import_react.useState)(null);
|
|
@@ -167,9 +401,6 @@ function useTerminalInput(adapter) {
|
|
|
167
401
|
}, [adapter]);
|
|
168
402
|
return { loading, error, sendText, sendKey };
|
|
169
403
|
}
|
|
170
|
-
var useTN5250Connection = useTerminalConnection;
|
|
171
|
-
var useTN5250Screen = useTerminalScreen;
|
|
172
|
-
var useTN5250Terminal = useTerminalInput;
|
|
173
404
|
|
|
174
405
|
// src/hooks/useTypingAnimation.ts
|
|
175
406
|
var import_react2 = require("react");
|
|
@@ -617,7 +848,7 @@ function TerminalBootLoader({
|
|
|
617
848
|
);
|
|
618
849
|
}
|
|
619
850
|
|
|
620
|
-
// src/components/
|
|
851
|
+
// src/components/Icons.tsx
|
|
621
852
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
622
853
|
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
854
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("polyline", { points: "4 17 10 11 4 5" }),
|
|
@@ -650,6 +881,10 @@ var RefreshIcon = ({ size = 12, className }) => /* @__PURE__ */ (0, import_jsx_r
|
|
|
650
881
|
] });
|
|
651
882
|
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
883
|
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" }) });
|
|
884
|
+
|
|
885
|
+
// src/components/InlineSignIn.tsx
|
|
886
|
+
var import_react4 = require("react");
|
|
887
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
653
888
|
var PROTOCOL_OPTIONS = [
|
|
654
889
|
{ value: "tn5250", label: "TN5250 (IBM i)" },
|
|
655
890
|
{ value: "tn3270", label: "TN3270 (Mainframe)" },
|
|
@@ -692,42 +927,56 @@ function InlineSignIn({ defaultProtocol, loading, error, onConnect }) {
|
|
|
692
927
|
color: "var(--gs-muted, #94a3b8)",
|
|
693
928
|
fontFamily: "var(--gs-font)"
|
|
694
929
|
};
|
|
695
|
-
return /* @__PURE__ */ (0,
|
|
696
|
-
/* @__PURE__ */ (0,
|
|
697
|
-
/* @__PURE__ */ (0,
|
|
698
|
-
/* @__PURE__ */ (0,
|
|
930
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { onSubmit: handleSubmit, className: "gs-signin", children: [
|
|
931
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { textAlign: "center", marginBottom: "16px" }, children: [
|
|
932
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TerminalIcon, { size: 28 }),
|
|
933
|
+
/* @__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
934
|
] }),
|
|
700
|
-
/* @__PURE__ */ (0,
|
|
701
|
-
/* @__PURE__ */ (0,
|
|
702
|
-
/* @__PURE__ */ (0,
|
|
703
|
-
/* @__PURE__ */ (0,
|
|
935
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "gs-signin-row", children: [
|
|
936
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { flex: 1 }, children: [
|
|
937
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: labelStyle, children: "Host" }),
|
|
938
|
+
/* @__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
939
|
] }),
|
|
705
|
-
/* @__PURE__ */ (0,
|
|
706
|
-
/* @__PURE__ */ (0,
|
|
707
|
-
/* @__PURE__ */ (0,
|
|
940
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { width: "72px" }, children: [
|
|
941
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: labelStyle, children: "Port" }),
|
|
942
|
+
/* @__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
943
|
] })
|
|
709
944
|
] }),
|
|
710
|
-
/* @__PURE__ */ (0,
|
|
711
|
-
/* @__PURE__ */ (0,
|
|
712
|
-
/* @__PURE__ */ (0,
|
|
945
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
|
|
946
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: labelStyle, children: "Protocol" }),
|
|
947
|
+
/* @__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
948
|
] }),
|
|
714
|
-
/* @__PURE__ */ (0,
|
|
715
|
-
/* @__PURE__ */ (0,
|
|
716
|
-
/* @__PURE__ */ (0,
|
|
949
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
|
|
950
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: labelStyle, children: "Username" }),
|
|
951
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { style: inputStyle, value: username, onChange: (e) => setUsername(e.target.value), required: true, autoComplete: "username" })
|
|
717
952
|
] }),
|
|
718
|
-
/* @__PURE__ */ (0,
|
|
719
|
-
/* @__PURE__ */ (0,
|
|
720
|
-
/* @__PURE__ */ (0,
|
|
953
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
|
|
954
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: labelStyle, children: "Password" }),
|
|
955
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { style: inputStyle, type: "password", value: password, onChange: (e) => setPassword(e.target.value), required: true, autoComplete: "current-password" })
|
|
721
956
|
] }),
|
|
722
|
-
error && /* @__PURE__ */ (0,
|
|
723
|
-
/* @__PURE__ */ (0,
|
|
724
|
-
/* @__PURE__ */ (0,
|
|
957
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { color: "#FF6B00", fontSize: "11px", fontFamily: "var(--gs-font)", display: "flex", alignItems: "center", gap: "6px" }, children: [
|
|
958
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AlertTriangleIcon, { size: 12 }),
|
|
959
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: error })
|
|
725
960
|
] }),
|
|
726
|
-
/* @__PURE__ */ (0,
|
|
961
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "submit", disabled: loading || !host || !username || !password, className: "gs-signin-btn", children: loading ? "Connecting..." : "Connect" })
|
|
727
962
|
] });
|
|
728
963
|
}
|
|
964
|
+
|
|
965
|
+
// src/components/GreenScreenTerminal.tsx
|
|
966
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
967
|
+
var noopResult = { success: false, error: "No adapter configured" };
|
|
968
|
+
var noopAdapter = {
|
|
969
|
+
getScreen: async () => null,
|
|
970
|
+
getStatus: async () => ({ connected: false, status: "disconnected" }),
|
|
971
|
+
sendText: async () => noopResult,
|
|
972
|
+
sendKey: async () => noopResult,
|
|
973
|
+
connect: async () => noopResult,
|
|
974
|
+
disconnect: async () => noopResult,
|
|
975
|
+
reconnect: async () => noopResult
|
|
976
|
+
};
|
|
729
977
|
function GreenScreenTerminal({
|
|
730
|
-
adapter,
|
|
978
|
+
adapter: externalAdapter,
|
|
979
|
+
baseUrl,
|
|
731
980
|
protocol,
|
|
732
981
|
protocolProfile: customProfile,
|
|
733
982
|
screenData: externalScreenData,
|
|
@@ -740,7 +989,7 @@ function GreenScreenTerminal({
|
|
|
740
989
|
showHeader = true,
|
|
741
990
|
typingAnimation = true,
|
|
742
991
|
typingBudgetMs = 60,
|
|
743
|
-
inlineSignIn =
|
|
992
|
+
inlineSignIn = true,
|
|
744
993
|
defaultProtocol: signInDefaultProtocol,
|
|
745
994
|
onSignIn,
|
|
746
995
|
bootLoader,
|
|
@@ -753,6 +1002,16 @@ function GreenScreenTerminal({
|
|
|
753
1002
|
style
|
|
754
1003
|
}) {
|
|
755
1004
|
const profile = customProfile ?? getProtocolProfile(protocol);
|
|
1005
|
+
const [internalAdapter, setInternalAdapter] = (0, import_react5.useState)(null);
|
|
1006
|
+
const baseUrlAdapter = (0, import_react5.useMemo)(
|
|
1007
|
+
() => baseUrl ? new RestAdapter({ baseUrl }) : null,
|
|
1008
|
+
[baseUrl]
|
|
1009
|
+
);
|
|
1010
|
+
const defaultWsAdapter = (0, import_react5.useMemo)(
|
|
1011
|
+
() => !externalAdapter && !baseUrl ? new WebSocketAdapter() : null,
|
|
1012
|
+
[externalAdapter, baseUrl]
|
|
1013
|
+
);
|
|
1014
|
+
const adapter = externalAdapter ?? baseUrlAdapter ?? internalAdapter ?? defaultWsAdapter ?? noopAdapter;
|
|
756
1015
|
const shouldPoll = pollInterval > 0 && !externalScreenData;
|
|
757
1016
|
const { data: polledScreenData, error: screenError } = useTerminalScreen(adapter, pollInterval, shouldPoll);
|
|
758
1017
|
const { sendText: _sendText, sendKey: _sendKey } = useTerminalInput(adapter);
|
|
@@ -764,26 +1023,26 @@ function GreenScreenTerminal({
|
|
|
764
1023
|
typingAnimation,
|
|
765
1024
|
typingBudgetMs
|
|
766
1025
|
);
|
|
767
|
-
const screenData = (0,
|
|
1026
|
+
const screenData = (0, import_react5.useMemo)(() => {
|
|
768
1027
|
if (!rawScreenData) return null;
|
|
769
1028
|
return { ...rawScreenData, content: displayedContent };
|
|
770
1029
|
}, [rawScreenData, displayedContent]);
|
|
771
|
-
const prevScreenSigRef = (0,
|
|
772
|
-
(0,
|
|
1030
|
+
const prevScreenSigRef = (0, import_react5.useRef)(void 0);
|
|
1031
|
+
(0, import_react5.useEffect)(() => {
|
|
773
1032
|
if (screenData && onScreenChange && screenData.screen_signature !== prevScreenSigRef.current) {
|
|
774
1033
|
prevScreenSigRef.current = screenData.screen_signature;
|
|
775
1034
|
onScreenChange(screenData);
|
|
776
1035
|
}
|
|
777
1036
|
}, [screenData, onScreenChange]);
|
|
778
|
-
const sendText = (0,
|
|
779
|
-
const sendKey = (0,
|
|
780
|
-
const [inputText, setInputText] = (0,
|
|
781
|
-
const [isFocused, setIsFocused] = (0,
|
|
782
|
-
const terminalRef = (0,
|
|
783
|
-
const inputRef = (0,
|
|
784
|
-
const [syncedCursor, setSyncedCursor] = (0,
|
|
785
|
-
const prevRawContentRef = (0,
|
|
786
|
-
(0,
|
|
1037
|
+
const sendText = (0, import_react5.useCallback)(async (text) => _sendText(text), [_sendText]);
|
|
1038
|
+
const sendKey = (0, import_react5.useCallback)(async (key) => _sendKey(key), [_sendKey]);
|
|
1039
|
+
const [inputText, setInputText] = (0, import_react5.useState)("");
|
|
1040
|
+
const [isFocused, setIsFocused] = (0, import_react5.useState)(false);
|
|
1041
|
+
const terminalRef = (0, import_react5.useRef)(null);
|
|
1042
|
+
const inputRef = (0, import_react5.useRef)(null);
|
|
1043
|
+
const [syncedCursor, setSyncedCursor] = (0, import_react5.useState)(null);
|
|
1044
|
+
const prevRawContentRef = (0, import_react5.useRef)("");
|
|
1045
|
+
(0, import_react5.useEffect)(() => {
|
|
787
1046
|
const newContent = rawScreenData?.content || "";
|
|
788
1047
|
if (prevRawContentRef.current && newContent && newContent !== prevRawContentRef.current) {
|
|
789
1048
|
setSyncedCursor(null);
|
|
@@ -791,15 +1050,15 @@ function GreenScreenTerminal({
|
|
|
791
1050
|
}
|
|
792
1051
|
prevRawContentRef.current = newContent;
|
|
793
1052
|
}, [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,
|
|
1053
|
+
const [autoReconnectAttempt, setAutoReconnectAttempt] = (0, import_react5.useState)(0);
|
|
1054
|
+
const [isAutoReconnecting, setIsAutoReconnecting] = (0, import_react5.useState)(false);
|
|
1055
|
+
const reconnectTimeoutRef = (0, import_react5.useRef)(null);
|
|
1056
|
+
const wasConnectedRef = (0, import_react5.useRef)(false);
|
|
1057
|
+
const isConnectedRef = (0, import_react5.useRef)(false);
|
|
1058
|
+
(0, import_react5.useEffect)(() => {
|
|
800
1059
|
isConnectedRef.current = connStatus?.connected ?? false;
|
|
801
1060
|
}, [connStatus?.connected]);
|
|
802
|
-
(0,
|
|
1061
|
+
(0, import_react5.useEffect)(() => {
|
|
803
1062
|
if (!autoReconnectEnabled) return;
|
|
804
1063
|
const isConnected = connStatus?.connected;
|
|
805
1064
|
if (isConnected) {
|
|
@@ -835,13 +1094,20 @@ function GreenScreenTerminal({
|
|
|
835
1094
|
if (reconnectTimeoutRef.current) clearTimeout(reconnectTimeoutRef.current);
|
|
836
1095
|
};
|
|
837
1096
|
}, [connStatus?.connected, autoReconnectAttempt, isAutoReconnecting, reconnecting, reconnect, autoReconnectEnabled, maxAttempts, onNotification]);
|
|
838
|
-
const handleSignIn = (0,
|
|
1097
|
+
const handleSignIn = (0, import_react5.useCallback)(async (config) => {
|
|
839
1098
|
onSignIn?.(config);
|
|
1099
|
+
if (!externalAdapter && !baseUrlAdapter) {
|
|
1100
|
+
const port = config.port ? `:${config.port}` : "";
|
|
1101
|
+
const newAdapter = new RestAdapter({ baseUrl: `http://${config.host}${port}` });
|
|
1102
|
+
setInternalAdapter(newAdapter);
|
|
1103
|
+
await newAdapter.connect(config);
|
|
1104
|
+
return;
|
|
1105
|
+
}
|
|
840
1106
|
await connect(config);
|
|
841
|
-
}, [connect, onSignIn]);
|
|
842
|
-
const [showBootLoader, setShowBootLoader] = (0,
|
|
843
|
-
const [bootFadingOut, setBootFadingOut] = (0,
|
|
844
|
-
(0,
|
|
1107
|
+
}, [connect, onSignIn, externalAdapter, baseUrlAdapter]);
|
|
1108
|
+
const [showBootLoader, setShowBootLoader] = (0, import_react5.useState)(bootLoader !== false);
|
|
1109
|
+
const [bootFadingOut, setBootFadingOut] = (0, import_react5.useState)(false);
|
|
1110
|
+
(0, import_react5.useEffect)(() => {
|
|
845
1111
|
if (screenData?.content && showBootLoader) {
|
|
846
1112
|
setBootFadingOut(true);
|
|
847
1113
|
setShowBootLoader(false);
|
|
@@ -849,25 +1115,25 @@ function GreenScreenTerminal({
|
|
|
849
1115
|
return () => clearTimeout(timer);
|
|
850
1116
|
}
|
|
851
1117
|
}, [screenData?.content, showBootLoader]);
|
|
852
|
-
(0,
|
|
1118
|
+
(0, import_react5.useEffect)(() => {
|
|
853
1119
|
const handleClickOutside = (event) => {
|
|
854
1120
|
if (terminalRef.current && !terminalRef.current.contains(event.target)) setIsFocused(false);
|
|
855
1121
|
};
|
|
856
1122
|
if (isFocused) document.addEventListener("mousedown", handleClickOutside);
|
|
857
1123
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
858
1124
|
}, [isFocused]);
|
|
859
|
-
(0,
|
|
1125
|
+
(0, import_react5.useEffect)(() => {
|
|
860
1126
|
if (readOnly && isFocused) {
|
|
861
1127
|
setIsFocused(false);
|
|
862
1128
|
inputRef.current?.blur();
|
|
863
1129
|
}
|
|
864
1130
|
}, [readOnly, isFocused]);
|
|
865
|
-
const handleTerminalClick = (0,
|
|
1131
|
+
const handleTerminalClick = (0, import_react5.useCallback)(() => {
|
|
866
1132
|
if (readOnly) return;
|
|
867
1133
|
setIsFocused(true);
|
|
868
1134
|
inputRef.current?.focus();
|
|
869
1135
|
}, [readOnly]);
|
|
870
|
-
const getCurrentField = (0,
|
|
1136
|
+
const getCurrentField = (0, import_react5.useCallback)(() => {
|
|
871
1137
|
const fields = screenData?.fields || [];
|
|
872
1138
|
const cursorRow = syncedCursor?.row ?? screenData?.cursor_row ?? 0;
|
|
873
1139
|
const cursorCol = syncedCursor?.col ?? screenData?.cursor_col ?? 0;
|
|
@@ -876,7 +1142,7 @@ function GreenScreenTerminal({
|
|
|
876
1142
|
}
|
|
877
1143
|
return null;
|
|
878
1144
|
}, [screenData, syncedCursor]);
|
|
879
|
-
const canTypeMore = (0,
|
|
1145
|
+
const canTypeMore = (0, import_react5.useCallback)((additionalChars = 1) => {
|
|
880
1146
|
const currentField = getCurrentField();
|
|
881
1147
|
if (!currentField) return true;
|
|
882
1148
|
const cursorCol = (syncedCursor?.col ?? screenData?.cursor_col ?? 0) + inputText.length;
|
|
@@ -978,28 +1244,28 @@ function GreenScreenTerminal({
|
|
|
978
1244
|
}
|
|
979
1245
|
return { row: cursorRow, col: cursorCol };
|
|
980
1246
|
};
|
|
981
|
-
const renderTextWithUnderlines = (0,
|
|
1247
|
+
const renderTextWithUnderlines = (0, import_react5.useCallback)((text, keyPrefix) => {
|
|
982
1248
|
const underscoreRegex = /_{2,}/g;
|
|
983
1249
|
const segments = [];
|
|
984
1250
|
let lastIndex = 0;
|
|
985
1251
|
let match;
|
|
986
1252
|
let segmentIndex = 0;
|
|
987
1253
|
while ((match = underscoreRegex.exec(text)) !== null) {
|
|
988
|
-
if (match.index > lastIndex) segments.push(/* @__PURE__ */ (0,
|
|
1254
|
+
if (match.index > lastIndex) segments.push(/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: text.substring(lastIndex, match.index) }, `${keyPrefix}-t-${segmentIndex}`));
|
|
989
1255
|
const count = match[0].length;
|
|
990
|
-
segments.push(/* @__PURE__ */ (0,
|
|
1256
|
+
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
1257
|
lastIndex = match.index + match[0].length;
|
|
992
1258
|
segmentIndex++;
|
|
993
1259
|
}
|
|
994
|
-
if (lastIndex < text.length) segments.push(/* @__PURE__ */ (0,
|
|
995
|
-
return segments.length > 0 ? /* @__PURE__ */ (0,
|
|
1260
|
+
if (lastIndex < text.length) segments.push(/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: text.substring(lastIndex) }, `${keyPrefix}-e`));
|
|
1261
|
+
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
1262
|
}, []);
|
|
997
|
-
const renderRowWithFields = (0,
|
|
1263
|
+
const renderRowWithFields = (0, import_react5.useCallback)((line, rowIndex, fields) => {
|
|
998
1264
|
const inputFields = fields.filter((f) => f.row === rowIndex && f.is_input);
|
|
999
1265
|
const highlightedFields = fields.filter((f) => f.row === rowIndex && f.is_protected && f.is_highlighted);
|
|
1000
1266
|
const reverseFields = fields.filter((f) => f.row === rowIndex && f.is_protected && f.is_reverse);
|
|
1001
1267
|
const allRowFields = [...inputFields, ...highlightedFields, ...reverseFields];
|
|
1002
|
-
if (allRowFields.length === 0) return /* @__PURE__ */ (0,
|
|
1268
|
+
if (allRowFields.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: renderTextWithUnderlines(line, `r${rowIndex}`) });
|
|
1003
1269
|
const sorted = [...allRowFields].sort((a, b) => a.col - b.col);
|
|
1004
1270
|
const segs = [];
|
|
1005
1271
|
let lastEnd = 0;
|
|
@@ -1007,35 +1273,35 @@ function GreenScreenTerminal({
|
|
|
1007
1273
|
sorted.forEach((field, idx) => {
|
|
1008
1274
|
const fs = field.col;
|
|
1009
1275
|
const fe = Math.min(field.col + field.length, cols);
|
|
1010
|
-
if (fs > lastEnd) segs.push(/* @__PURE__ */ (0,
|
|
1276
|
+
if (fs > lastEnd) segs.push(/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: renderTextWithUnderlines(line.substring(lastEnd, fs), `r${rowIndex}p${idx}`) }, `t${idx}`));
|
|
1011
1277
|
const fc = line.substring(fs, fe);
|
|
1012
1278
|
if (field.is_input) {
|
|
1013
1279
|
const w = field.length >= 30 ? Math.max(field.length, 40) : field.length;
|
|
1014
|
-
segs.push(/* @__PURE__ */ (0,
|
|
1280
|
+
segs.push(/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "gs-input-field", style: { borderBottom: "2px solid var(--gs-green, #10b981)", display: "inline-block", minWidth: `${w}ch` }, children: fc }, `f${idx}`));
|
|
1015
1281
|
} else if (field.is_reverse) {
|
|
1016
|
-
segs.push(/* @__PURE__ */ (0,
|
|
1282
|
+
segs.push(/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { color: "#ef4444", fontWeight: "bold" }, children: fc }, `v${idx}`));
|
|
1017
1283
|
} else {
|
|
1018
|
-
segs.push(/* @__PURE__ */ (0,
|
|
1284
|
+
segs.push(/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { color: "var(--gs-white, #FFFFFF)" }, children: fc }, `h${idx}`));
|
|
1019
1285
|
}
|
|
1020
1286
|
lastEnd = fe;
|
|
1021
1287
|
});
|
|
1022
|
-
if (lastEnd < line.length) segs.push(/* @__PURE__ */ (0,
|
|
1023
|
-
return /* @__PURE__ */ (0,
|
|
1288
|
+
if (lastEnd < line.length) segs.push(/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: renderTextWithUnderlines(line.substring(lastEnd), `r${rowIndex}e`) }, "te"));
|
|
1289
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: segs });
|
|
1024
1290
|
}, [renderTextWithUnderlines, screenData?.cols, profile.defaultCols]);
|
|
1025
1291
|
const renderScreen = () => {
|
|
1026
1292
|
if (showBootLoader && !screenData?.content) {
|
|
1027
1293
|
if (bootLoader === false) return null;
|
|
1028
|
-
if (bootLoader) return /* @__PURE__ */ (0,
|
|
1029
|
-
return /* @__PURE__ */ (0,
|
|
1294
|
+
if (bootLoader) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: bootLoader });
|
|
1295
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TerminalBootLoader, { brandText: profile.bootText });
|
|
1030
1296
|
}
|
|
1031
|
-
if (bootFadingOut && screenData?.content) return /* @__PURE__ */ (0,
|
|
1297
|
+
if (bootFadingOut && screenData?.content) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "gs-fade-in", children: renderScreenContent() });
|
|
1032
1298
|
if (!screenData?.content) {
|
|
1033
1299
|
if (inlineSignIn && !connStatus?.connected) {
|
|
1034
|
-
return /* @__PURE__ */ (0,
|
|
1300
|
+
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.jsx)(InlineSignIn, { defaultProtocol: signInDefaultProtocol || protocol || "tn5250", loading: reconnecting, error: connectError, onConnect: handleSignIn }) });
|
|
1035
1301
|
}
|
|
1036
|
-
return /* @__PURE__ */ (0,
|
|
1037
|
-
/* @__PURE__ */ (0,
|
|
1038
|
-
/* @__PURE__ */ (0,
|
|
1302
|
+
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: [
|
|
1303
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: "#808080", marginBottom: "12px" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TerminalIcon, { size: 40 }) }),
|
|
1304
|
+
/* @__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" })
|
|
1039
1305
|
] }) });
|
|
1040
1306
|
}
|
|
1041
1307
|
return renderScreenContent();
|
|
@@ -1052,7 +1318,7 @@ function GreenScreenTerminal({
|
|
|
1052
1318
|
const cursor = getCursorPos();
|
|
1053
1319
|
const hasInputFields = fields.some((f) => f.is_input);
|
|
1054
1320
|
const hasCursor = screenData.cursor_row !== void 0 && screenData.cursor_col !== void 0 && (hasInputFields || screenData.cursor_row !== 0 || screenData.cursor_col !== 0);
|
|
1055
|
-
return /* @__PURE__ */ (0,
|
|
1321
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { fontFamily: "var(--gs-font)", fontSize: "13px", position: "relative", width: `${cols}ch` }, children: [
|
|
1056
1322
|
rows.map((line, index) => {
|
|
1057
1323
|
let displayLine = line;
|
|
1058
1324
|
if (hasCursor && index === cursor.row && inputText && !animatedCursorPos) {
|
|
@@ -1060,12 +1326,12 @@ function GreenScreenTerminal({
|
|
|
1060
1326
|
displayLine = (line.substring(0, baseCol) + inputText + line.substring(baseCol + inputText.length)).substring(0, cols).padEnd(cols, " ");
|
|
1061
1327
|
}
|
|
1062
1328
|
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,
|
|
1329
|
+
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: [
|
|
1330
|
+
headerSegments ? headerSegments.map((seg, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: seg.colorClass, children: seg.text }, i)) : renderRowWithFields(displayLine, index, fields),
|
|
1331
|
+
hasCursor && 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
1332
|
] }, index);
|
|
1067
1333
|
}),
|
|
1068
|
-
screenData.cursor_row !== void 0 && screenData.cursor_col !== void 0 && /* @__PURE__ */ (0,
|
|
1334
|
+
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
1335
|
String(screenData.cursor_row + 1).padStart(2, "0"),
|
|
1070
1336
|
"/",
|
|
1071
1337
|
String(screenData.cursor_col + 1).padStart(3, "0")
|
|
@@ -1095,49 +1361,49 @@ function GreenScreenTerminal({
|
|
|
1095
1361
|
return "#64748b";
|
|
1096
1362
|
}
|
|
1097
1363
|
};
|
|
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,
|
|
1364
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `gs-terminal ${isFocused ? "gs-terminal-focused" : ""} ${className || ""}`, style, children: [
|
|
1365
|
+
showHeader && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "gs-header", children: embedded ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
1366
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "gs-header-left", children: [
|
|
1367
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TerminalIcon, { size: 14 }),
|
|
1368
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "TERMINAL" }),
|
|
1369
|
+
isFocused && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "gs-badge-focused", children: "FOCUSED" }),
|
|
1370
|
+
screenData?.timestamp && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "gs-timestamp", children: new Date(screenData.timestamp).toLocaleTimeString() }),
|
|
1371
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "gs-hint", children: readOnly ? "Read-only" : isFocused ? "ESC to exit focus" : "Click to control" })
|
|
1106
1372
|
] }),
|
|
1107
|
-
/* @__PURE__ */ (0,
|
|
1108
|
-
connStatus?.status && /* @__PURE__ */ (0,
|
|
1109
|
-
connStatus && (connStatus.connected ? /* @__PURE__ */ (0,
|
|
1110
|
-
onMinimize && /* @__PURE__ */ (0,
|
|
1373
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "gs-header-right", children: [
|
|
1374
|
+
connStatus?.status && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(KeyIcon, { size: 12, style: { color: getStatusColor(connStatus.status) } }),
|
|
1375
|
+
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" } })),
|
|
1376
|
+
onMinimize && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { onClick: (e) => {
|
|
1111
1377
|
e.stopPropagation();
|
|
1112
1378
|
onMinimize();
|
|
1113
|
-
}, className: "gs-btn-icon", title: "Minimize terminal", children: /* @__PURE__ */ (0,
|
|
1379
|
+
}, className: "gs-btn-icon", title: "Minimize terminal", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MinimizeIcon, {}) }),
|
|
1114
1380
|
headerRight
|
|
1115
1381
|
] })
|
|
1116
|
-
] }) : /* @__PURE__ */ (0,
|
|
1117
|
-
/* @__PURE__ */ (0,
|
|
1118
|
-
/* @__PURE__ */ (0,
|
|
1119
|
-
/* @__PURE__ */ (0,
|
|
1120
|
-
isFocused && /* @__PURE__ */ (0,
|
|
1121
|
-
screenData?.timestamp && /* @__PURE__ */ (0,
|
|
1122
|
-
/* @__PURE__ */ (0,
|
|
1382
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
1383
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "gs-header-left", children: [
|
|
1384
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TerminalIcon, { size: 14 }),
|
|
1385
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: profile.headerLabel }),
|
|
1386
|
+
isFocused && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "gs-badge-focused", children: "FOCUSED" }),
|
|
1387
|
+
screenData?.timestamp && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "gs-timestamp", children: new Date(screenData.timestamp).toLocaleTimeString() }),
|
|
1388
|
+
/* @__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
1389
|
] }),
|
|
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,
|
|
1390
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "gs-header-right", children: [
|
|
1391
|
+
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: [
|
|
1392
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(WifiIcon, { size: 12, style: { color: "var(--gs-green, #10b981)" } }),
|
|
1393
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "gs-host", children: connStatus.host })
|
|
1394
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
1395
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(WifiOffIcon, { size: 12, style: { color: "#FF6B00" } }),
|
|
1396
|
+
/* @__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" }),
|
|
1397
|
+
/* @__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
1398
|
] }) }),
|
|
1133
|
-
connStatus?.status && /* @__PURE__ */ (0,
|
|
1134
|
-
/* @__PURE__ */ (0,
|
|
1135
|
-
connStatus.username && /* @__PURE__ */ (0,
|
|
1399
|
+
connStatus?.status && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "gs-status-group", children: [
|
|
1400
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(KeyIcon, { size: 12, style: { color: getStatusColor(connStatus.status) } }),
|
|
1401
|
+
connStatus.username && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "gs-host", children: connStatus.username })
|
|
1136
1402
|
] }),
|
|
1137
1403
|
headerRight
|
|
1138
1404
|
] })
|
|
1139
1405
|
] }) }),
|
|
1140
|
-
/* @__PURE__ */ (0,
|
|
1406
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "gs-body", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1141
1407
|
"div",
|
|
1142
1408
|
{
|
|
1143
1409
|
ref: terminalRef,
|
|
@@ -1145,17 +1411,17 @@ function GreenScreenTerminal({
|
|
|
1145
1411
|
className: `gs-screen ${embedded ? "gs-screen-embedded" : ""}`,
|
|
1146
1412
|
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
1413
|
children: [
|
|
1148
|
-
screenError != null && /* @__PURE__ */ (0,
|
|
1149
|
-
/* @__PURE__ */ (0,
|
|
1150
|
-
/* @__PURE__ */ (0,
|
|
1414
|
+
screenError != null && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "gs-error-banner", children: [
|
|
1415
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(AlertTriangleIcon, { size: 14 }),
|
|
1416
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: String(screenError) })
|
|
1151
1417
|
] }),
|
|
1152
|
-
/* @__PURE__ */ (0,
|
|
1418
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "gs-screen-content", children: renderScreen() }),
|
|
1153
1419
|
overlay,
|
|
1154
|
-
connStatus && !connStatus.connected && screenData && /* @__PURE__ */ (0,
|
|
1155
|
-
/* @__PURE__ */ (0,
|
|
1156
|
-
/* @__PURE__ */ (0,
|
|
1420
|
+
connStatus && !connStatus.connected && screenData && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "gs-overlay", children: [
|
|
1421
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(WifiOffIcon, { size: 28 }),
|
|
1422
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: isAutoReconnecting || reconnecting ? "Reconnecting..." : "Disconnected" })
|
|
1157
1423
|
] }),
|
|
1158
|
-
/* @__PURE__ */ (0,
|
|
1424
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1159
1425
|
"input",
|
|
1160
1426
|
{
|
|
1161
1427
|
ref: inputRef,
|
|
@@ -1175,66 +1441,6 @@ function GreenScreenTerminal({
|
|
|
1175
1441
|
) })
|
|
1176
1442
|
] });
|
|
1177
1443
|
}
|
|
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
1444
|
|
|
1239
1445
|
// src/utils/rendering.ts
|
|
1240
1446
|
function getRowColorClass(rowIndex, rowContent) {
|
|
@@ -1245,10 +1451,18 @@ function parseHeaderRow(line) {
|
|
|
1245
1451
|
}
|
|
1246
1452
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1247
1453
|
0 && (module.exports = {
|
|
1454
|
+
AlertTriangleIcon,
|
|
1248
1455
|
GreenScreenTerminal,
|
|
1456
|
+
InlineSignIn,
|
|
1457
|
+
KeyIcon,
|
|
1458
|
+
MinimizeIcon,
|
|
1459
|
+
RefreshIcon,
|
|
1249
1460
|
RestAdapter,
|
|
1250
|
-
TN5250Terminal,
|
|
1251
1461
|
TerminalBootLoader,
|
|
1462
|
+
TerminalIcon,
|
|
1463
|
+
WebSocketAdapter,
|
|
1464
|
+
WifiIcon,
|
|
1465
|
+
WifiOffIcon,
|
|
1252
1466
|
getProtocolProfile,
|
|
1253
1467
|
getRowColorClass,
|
|
1254
1468
|
hp6530Profile,
|
|
@@ -1257,9 +1471,6 @@ function parseHeaderRow(line) {
|
|
|
1257
1471
|
positionToRowCol,
|
|
1258
1472
|
tn3270Profile,
|
|
1259
1473
|
tn5250Profile,
|
|
1260
|
-
useTN5250Connection,
|
|
1261
|
-
useTN5250Screen,
|
|
1262
|
-
useTN5250Terminal,
|
|
1263
1474
|
useTerminalConnection,
|
|
1264
1475
|
useTerminalInput,
|
|
1265
1476
|
useTerminalScreen,
|