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/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 import_react4 = require("react");
52
+ var import_react5 = require("react");
48
53
 
49
- // src/hooks/useTN5250.ts
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/GreenScreenTerminal.tsx
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, import_jsx_runtime2.jsxs)("form", { onSubmit: handleSubmit, className: "gs-signin", children: [
696
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { textAlign: "center", marginBottom: "16px" }, children: [
697
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TerminalIcon, { size: 28 }),
698
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { fontSize: "11px", letterSpacing: "0.15em", textTransform: "uppercase", color: "var(--gs-muted)", marginTop: "8px" }, children: "Connect to Host" })
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, import_jsx_runtime2.jsxs)("div", { className: "gs-signin-row", children: [
701
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { flex: 1 }, children: [
702
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { style: labelStyle, children: "Host" }),
703
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { style: inputStyle, value: host, onChange: (e) => setHost(e.target.value), placeholder: "192.168.1.100", required: true, autoFocus: true })
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, import_jsx_runtime2.jsxs)("div", { style: { width: "72px" }, children: [
706
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { style: labelStyle, children: "Port" }),
707
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { style: inputStyle, value: port, onChange: (e) => setPort(e.target.value), placeholder: "23", type: "number", min: "1", max: "65535" })
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, import_jsx_runtime2.jsxs)("div", { children: [
711
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { style: labelStyle, children: "Protocol" }),
712
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("select", { style: { ...inputStyle, appearance: "none" }, value: selectedProtocol, onChange: (e) => setSelectedProtocol(e.target.value), children: PROTOCOL_OPTIONS.map((o) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: o.value, children: o.label }, o.value)) })
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, import_jsx_runtime2.jsxs)("div", { children: [
715
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { style: labelStyle, children: "Username" }),
716
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { style: inputStyle, value: username, onChange: (e) => setUsername(e.target.value), required: true, autoComplete: "username" })
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, import_jsx_runtime2.jsxs)("div", { children: [
719
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { style: labelStyle, children: "Password" }),
720
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { style: inputStyle, type: "password", value: password, onChange: (e) => setPassword(e.target.value), required: true, autoComplete: "current-password" })
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, import_jsx_runtime2.jsxs)("div", { style: { color: "#FF6B00", fontSize: "11px", fontFamily: "var(--gs-font)", display: "flex", alignItems: "center", gap: "6px" }, children: [
723
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AlertTriangleIcon, { size: 12 }),
724
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: error })
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, import_jsx_runtime2.jsx)("button", { type: "submit", disabled: loading || !host || !username || !password, className: "gs-signin-btn", children: loading ? "Connecting..." : "Connect" })
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 = false,
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, import_react4.useMemo)(() => {
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, import_react4.useRef)(void 0);
772
- (0, import_react4.useEffect)(() => {
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, import_react4.useCallback)(async (text) => _sendText(text), [_sendText]);
779
- const sendKey = (0, import_react4.useCallback)(async (key) => _sendKey(key), [_sendKey]);
780
- const [inputText, setInputText] = (0, import_react4.useState)("");
781
- const [isFocused, setIsFocused] = (0, import_react4.useState)(false);
782
- const terminalRef = (0, import_react4.useRef)(null);
783
- const inputRef = (0, import_react4.useRef)(null);
784
- const [syncedCursor, setSyncedCursor] = (0, import_react4.useState)(null);
785
- const prevRawContentRef = (0, import_react4.useRef)("");
786
- (0, import_react4.useEffect)(() => {
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, import_react4.useState)(0);
795
- const [isAutoReconnecting, setIsAutoReconnecting] = (0, import_react4.useState)(false);
796
- const reconnectTimeoutRef = (0, import_react4.useRef)(null);
797
- const wasConnectedRef = (0, import_react4.useRef)(false);
798
- const isConnectedRef = (0, import_react4.useRef)(false);
799
- (0, import_react4.useEffect)(() => {
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, import_react4.useEffect)(() => {
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, import_react4.useCallback)(async (config) => {
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, import_react4.useState)(bootLoader !== false);
843
- const [bootFadingOut, setBootFadingOut] = (0, import_react4.useState)(false);
844
- (0, import_react4.useEffect)(() => {
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, import_react4.useEffect)(() => {
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, import_react4.useEffect)(() => {
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, import_react4.useCallback)(() => {
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, import_react4.useCallback)(() => {
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, import_react4.useCallback)((additionalChars = 1) => {
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, import_react4.useCallback)((text, keyPrefix) => {
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, import_jsx_runtime2.jsx)("span", { children: text.substring(lastIndex, match.index) }, `${keyPrefix}-t-${segmentIndex}`));
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, import_jsx_runtime2.jsx)("span", { style: { borderBottom: "1px solid var(--gs-green, #10b981)", display: "inline-block", width: `${count}ch` }, children: " ".repeat(count) }, `${keyPrefix}-u-${segmentIndex}`));
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, import_jsx_runtime2.jsx)("span", { children: text.substring(lastIndex) }, `${keyPrefix}-e`));
995
- return segments.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: segments }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: text });
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, import_react4.useCallback)((line, rowIndex, fields) => {
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, import_jsx_runtime2.jsx)("span", { children: renderTextWithUnderlines(line, `r${rowIndex}`) });
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, import_jsx_runtime2.jsx)("span", { children: renderTextWithUnderlines(line.substring(lastEnd, fs), `r${rowIndex}p${idx}`) }, `t${idx}`));
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, import_jsx_runtime2.jsx)("span", { className: "gs-input-field", style: { borderBottom: "2px solid var(--gs-green, #10b981)", display: "inline-block", minWidth: `${w}ch` }, children: fc }, `f${idx}`));
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, import_jsx_runtime2.jsx)("span", { style: { color: "#ef4444", fontWeight: "bold" }, children: fc }, `v${idx}`));
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, import_jsx_runtime2.jsx)("span", { style: { color: "var(--gs-white, #FFFFFF)" }, children: fc }, `h${idx}`));
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, import_jsx_runtime2.jsx)("span", { children: renderTextWithUnderlines(line.substring(lastEnd), `r${rowIndex}e`) }, "te"));
1023
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: segs });
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, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: bootLoader });
1029
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TerminalBootLoader, { brandText: profile.bootText });
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, import_jsx_runtime2.jsx)("div", { className: "gs-fade-in", children: renderScreenContent() });
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, import_jsx_runtime2.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_runtime2.jsx)(InlineSignIn, { defaultProtocol: signInDefaultProtocol || protocol || "tn5250", loading: reconnecting, error: connectError, onConnect: handleSignIn }) });
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, import_jsx_runtime2.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_runtime2.jsxs)("div", { style: { textAlign: "center" }, children: [
1037
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { color: "#808080", marginBottom: "12px" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TerminalIcon, { size: 40 }) }),
1038
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: { fontFamily: "var(--gs-font)", fontSize: "12px", color: "#808080" }, children: connStatus?.connected ? "Waiting for screen data..." : "Not connected" })
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, import_jsx_runtime2.jsxs)("div", { style: { fontFamily: "var(--gs-font)", fontSize: "13px", position: "relative", width: `${cols}ch` }, children: [
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, import_jsx_runtime2.jsxs)("div", { className: headerSegments ? "" : profile.colors.getRowColorClass(index, displayLine, termRows), style: { height: `${ROW_HEIGHT}px`, lineHeight: `${ROW_HEIGHT}px`, whiteSpace: "pre", position: "relative" }, children: [
1064
- headerSegments ? headerSegments.map((seg, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: seg.colorClass, children: seg.text }, i)) : renderRowWithFields(displayLine, index, fields),
1065
- hasCursor && index === cursor.row && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gs-cursor", style: { position: "absolute", left: `${cursor.col}ch`, width: "1ch", height: `${ROW_HEIGHT}px`, top: 0, pointerEvents: "none" } })
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, import_jsx_runtime2.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: [
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, import_jsx_runtime2.jsxs)("div", { className: `gs-terminal ${isFocused ? "gs-terminal-focused" : ""} ${className || ""}`, style, children: [
1099
- showHeader && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "gs-header", children: embedded ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
1100
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "gs-header-left", children: [
1101
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TerminalIcon, { size: 14 }),
1102
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "TERMINAL" }),
1103
- isFocused && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gs-badge-focused", children: "FOCUSED" }),
1104
- screenData?.timestamp && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gs-timestamp", children: new Date(screenData.timestamp).toLocaleTimeString() }),
1105
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gs-hint", children: readOnly ? "Read-only" : isFocused ? "ESC to exit focus" : "Click to control" })
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, import_jsx_runtime2.jsxs)("div", { className: "gs-header-right", children: [
1108
- connStatus?.status && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(KeyIcon, { size: 12, style: { color: getStatusColor(connStatus.status) } }),
1109
- connStatus && (connStatus.connected ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(WifiIcon, { size: 12, style: { color: "var(--gs-green, #10b981)" } }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(WifiOffIcon, { size: 12, style: { color: "#FF6B00" } })),
1110
- onMinimize && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: (e) => {
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, import_jsx_runtime2.jsx)(MinimizeIcon, {}) }),
1379
+ }, className: "gs-btn-icon", title: "Minimize terminal", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MinimizeIcon, {}) }),
1114
1380
  headerRight
1115
1381
  ] })
1116
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
1117
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "gs-header-left", children: [
1118
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TerminalIcon, { size: 14 }),
1119
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: profile.headerLabel }),
1120
- isFocused && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gs-badge-focused", children: "FOCUSED" }),
1121
- screenData?.timestamp && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gs-timestamp", children: new Date(screenData.timestamp).toLocaleTimeString() }),
1122
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gs-hint", children: readOnly ? "Read-only mode" : isFocused ? "ESC to exit focus" : "Click terminal to control" })
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, import_jsx_runtime2.jsxs)("div", { className: "gs-header-right", children: [
1125
- connStatus && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "gs-status-group", children: connStatus.connected ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
1126
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(WifiIcon, { size: 12, style: { color: "var(--gs-green, #10b981)" } }),
1127
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gs-host", children: connStatus.host })
1128
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
1129
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(WifiOffIcon, { size: 12, style: { color: "#FF6B00" } }),
1130
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gs-disconnected-text", children: isAutoReconnecting || reconnecting ? `RECONNECTING${autoReconnectAttempt > 0 ? ` (${autoReconnectAttempt}/${maxAttempts})` : "..."}` : autoReconnectAttempt >= maxAttempts ? "DISCONNECTED (auto-retry exhausted)" : "DISCONNECTED" }),
1131
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: handleReconnect, disabled: reconnecting || isAutoReconnecting, className: "gs-btn-icon", title: "Reconnect", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RefreshIcon, { size: 12, className: reconnecting || isAutoReconnecting ? "gs-spin" : "" }) })
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, import_jsx_runtime2.jsxs)("div", { className: "gs-status-group", children: [
1134
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(KeyIcon, { size: 12, style: { color: getStatusColor(connStatus.status) } }),
1135
- connStatus.username && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gs-host", children: connStatus.username })
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, import_jsx_runtime2.jsx)("div", { className: "gs-body", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
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, import_jsx_runtime2.jsxs)("div", { className: "gs-error-banner", children: [
1149
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AlertTriangleIcon, { size: 14 }),
1150
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: String(screenError) })
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, import_jsx_runtime2.jsx)("div", { className: "gs-screen-content", children: renderScreen() }),
1418
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "gs-screen-content", children: renderScreen() }),
1153
1419
  overlay,
1154
- connStatus && !connStatus.connected && screenData && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gs-overlay", children: [
1155
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(WifiOffIcon, { size: 28 }),
1156
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: isAutoReconnecting || reconnecting ? "Reconnecting..." : "Disconnected" })
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, import_jsx_runtime2.jsx)(
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,