green-screen-react 0.3.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 ADDED
@@ -0,0 +1,1269 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ GreenScreenTerminal: () => GreenScreenTerminal,
24
+ RestAdapter: () => RestAdapter,
25
+ TN5250Terminal: () => TN5250Terminal,
26
+ TerminalBootLoader: () => TerminalBootLoader,
27
+ getProtocolProfile: () => getProtocolProfile,
28
+ getRowColorClass: () => getRowColorClass,
29
+ hp6530Profile: () => hp6530Profile,
30
+ isFieldEntry: () => isFieldEntry,
31
+ parseHeaderRow: () => parseHeaderRow,
32
+ positionToRowCol: () => positionToRowCol,
33
+ tn3270Profile: () => tn3270Profile,
34
+ tn5250Profile: () => tn5250Profile,
35
+ useTN5250Connection: () => useTN5250Connection,
36
+ useTN5250Screen: () => useTN5250Screen,
37
+ useTN5250Terminal: () => useTN5250Terminal,
38
+ useTerminalConnection: () => useTerminalConnection,
39
+ useTerminalInput: () => useTerminalInput,
40
+ useTerminalScreen: () => useTerminalScreen,
41
+ useTypingAnimation: () => useTypingAnimation,
42
+ vtProfile: () => vtProfile
43
+ });
44
+ module.exports = __toCommonJS(index_exports);
45
+
46
+ // src/components/GreenScreenTerminal.tsx
47
+ var import_react4 = require("react");
48
+
49
+ // src/hooks/useTN5250.ts
50
+ var import_react = require("react");
51
+ function useTerminalConnection(adapter) {
52
+ const [status, setStatus] = (0, import_react.useState)(null);
53
+ const [loading, setLoading] = (0, import_react.useState)(false);
54
+ const [error, setError] = (0, import_react.useState)(null);
55
+ const fetchStatus = (0, import_react.useCallback)(async () => {
56
+ try {
57
+ const result = await adapter.getStatus();
58
+ setStatus(result);
59
+ return result;
60
+ } catch (err) {
61
+ const message = err instanceof Error ? err.message : String(err);
62
+ setError(message);
63
+ return null;
64
+ }
65
+ }, [adapter]);
66
+ const connect = (0, import_react.useCallback)(async (config) => {
67
+ setLoading(true);
68
+ setError(null);
69
+ try {
70
+ const result = await adapter.connect(config);
71
+ return { ...result, success: true };
72
+ } catch (err) {
73
+ const message = err instanceof Error ? err.message : String(err);
74
+ setError(message);
75
+ return { success: false, error: message };
76
+ } finally {
77
+ setLoading(false);
78
+ }
79
+ }, [adapter]);
80
+ const disconnect = (0, import_react.useCallback)(async () => {
81
+ setLoading(true);
82
+ setError(null);
83
+ try {
84
+ await adapter.disconnect();
85
+ setStatus((prev) => prev ? { ...prev, connected: false, status: "disconnected" } : null);
86
+ return { success: true };
87
+ } catch (err) {
88
+ const message = err instanceof Error ? err.message : String(err);
89
+ setError(message);
90
+ return { success: false, error: message };
91
+ } finally {
92
+ setLoading(false);
93
+ }
94
+ }, [adapter]);
95
+ const reconnect = (0, import_react.useCallback)(async () => {
96
+ setLoading(true);
97
+ setError(null);
98
+ try {
99
+ const result = await adapter.reconnect();
100
+ return { ...result, success: true };
101
+ } catch (err) {
102
+ const message = err instanceof Error ? err.message : String(err);
103
+ setError(message);
104
+ return { success: false, error: message };
105
+ } finally {
106
+ setLoading(false);
107
+ }
108
+ }, [adapter]);
109
+ return { status, loading, error, fetchStatus, connect, disconnect, reconnect };
110
+ }
111
+ function useTerminalScreen(adapter, interval = 2e3, enabled = true) {
112
+ const [data, setData] = (0, import_react.useState)(null);
113
+ const [error, setError] = (0, import_react.useState)(null);
114
+ const intervalRef = (0, import_react.useRef)(null);
115
+ (0, import_react.useEffect)(() => {
116
+ if (!enabled) {
117
+ if (intervalRef.current) clearInterval(intervalRef.current);
118
+ return;
119
+ }
120
+ const poll = async () => {
121
+ try {
122
+ const screen = await adapter.getScreen();
123
+ if (screen) setData(screen);
124
+ setError(null);
125
+ } catch (err) {
126
+ setError(err);
127
+ }
128
+ };
129
+ poll();
130
+ intervalRef.current = setInterval(poll, interval);
131
+ return () => {
132
+ if (intervalRef.current) clearInterval(intervalRef.current);
133
+ };
134
+ }, [adapter, interval, enabled]);
135
+ return { data, error };
136
+ }
137
+ function useTerminalInput(adapter) {
138
+ const [loading, setLoading] = (0, import_react.useState)(false);
139
+ const [error, setError] = (0, import_react.useState)(null);
140
+ const sendText = (0, import_react.useCallback)(async (text) => {
141
+ setLoading(true);
142
+ setError(null);
143
+ try {
144
+ const result = await adapter.sendText(text);
145
+ return { ...result, success: true };
146
+ } catch (err) {
147
+ const message = err instanceof Error ? err.message : String(err);
148
+ setError(message);
149
+ return { success: false, error: message };
150
+ } finally {
151
+ setLoading(false);
152
+ }
153
+ }, [adapter]);
154
+ const sendKey = (0, import_react.useCallback)(async (key) => {
155
+ setLoading(true);
156
+ setError(null);
157
+ try {
158
+ const result = await adapter.sendKey(key);
159
+ return { ...result, success: true };
160
+ } catch (err) {
161
+ const message = err instanceof Error ? err.message : String(err);
162
+ setError(message);
163
+ return { success: false, error: message };
164
+ } finally {
165
+ setLoading(false);
166
+ }
167
+ }, [adapter]);
168
+ return { loading, error, sendText, sendKey };
169
+ }
170
+ var useTN5250Connection = useTerminalConnection;
171
+ var useTN5250Screen = useTerminalScreen;
172
+ var useTN5250Terminal = useTerminalInput;
173
+
174
+ // src/hooks/useTypingAnimation.ts
175
+ var import_react2 = require("react");
176
+
177
+ // src/utils/grid.ts
178
+ function positionToRowCol(content, position) {
179
+ let row = 0;
180
+ let col = 0;
181
+ for (let i = 0; i < position && i < content.length; i++) {
182
+ if (content[i] === "\n") {
183
+ row++;
184
+ col = 0;
185
+ } else {
186
+ col++;
187
+ }
188
+ }
189
+ return { row, col };
190
+ }
191
+ function isFieldEntry(previousContent, content) {
192
+ if (!previousContent || !content) return false;
193
+ let diffCount = 0;
194
+ let diffStart = -1;
195
+ let diffEnd = 0;
196
+ const maxLen = Math.max(previousContent.length, content.length);
197
+ for (let i = 0; i < maxLen; i++) {
198
+ const oldChar = previousContent[i] || " ";
199
+ const newChar = content[i] || " ";
200
+ if (oldChar !== newChar) {
201
+ diffCount++;
202
+ if (diffStart === -1) diffStart = i;
203
+ diffEnd = i;
204
+ }
205
+ }
206
+ const diffSpan = diffEnd - diffStart + 1;
207
+ return diffCount > 2 && diffCount < 50 && diffSpan < 100;
208
+ }
209
+
210
+ // src/hooks/useTypingAnimation.ts
211
+ function useTypingAnimation(content, enabled = true, typingBudgetMs = 100) {
212
+ const FRAME_DELAY = 16;
213
+ const previousContentRef = (0, import_react2.useRef)("");
214
+ const targetContentRef = (0, import_react2.useRef)("");
215
+ const contentQueueRef = (0, import_react2.useRef)([]);
216
+ const isAnimatingRef = (0, import_react2.useRef)(false);
217
+ const timeoutRef = (0, import_react2.useRef)(null);
218
+ const rafRef = (0, import_react2.useRef)(null);
219
+ const [animatedContent, setAnimatedContent] = (0, import_react2.useState)("");
220
+ const [isAnimating, setIsAnimating] = (0, import_react2.useState)(false);
221
+ const [animatedCursorPos, setAnimatedCursorPos] = (0, import_react2.useState)(null);
222
+ const cancelAnimation = () => {
223
+ if (timeoutRef.current) {
224
+ clearTimeout(timeoutRef.current);
225
+ timeoutRef.current = null;
226
+ }
227
+ if (rafRef.current) {
228
+ cancelAnimationFrame(rafRef.current);
229
+ rafRef.current = null;
230
+ }
231
+ };
232
+ const showInstant = (0, import_react2.useCallback)((c) => {
233
+ cancelAnimation();
234
+ setAnimatedContent(c);
235
+ setAnimatedCursorPos(null);
236
+ setIsAnimating(false);
237
+ isAnimatingRef.current = false;
238
+ previousContentRef.current = c;
239
+ targetContentRef.current = "";
240
+ }, []);
241
+ const processNextRef = (0, import_react2.useRef)(null);
242
+ const startFieldAnimation = (0, import_react2.useCallback)((fromContent, toContent) => {
243
+ let diffStart = -1;
244
+ let diffEnd = 0;
245
+ const maxLen = Math.max(fromContent.length, toContent.length);
246
+ for (let i = 0; i < maxLen; i++) {
247
+ if ((fromContent[i] || " ") !== (toContent[i] || " ")) {
248
+ if (diffStart === -1) diffStart = i;
249
+ diffEnd = i;
250
+ }
251
+ }
252
+ if (diffStart === -1) {
253
+ showInstant(toContent);
254
+ return;
255
+ }
256
+ setIsAnimating(true);
257
+ isAnimatingRef.current = true;
258
+ targetContentRef.current = toContent;
259
+ const diffLength = diffEnd - diffStart + 1;
260
+ const oldRegion = fromContent.substring(diffStart, diffEnd + 1);
261
+ const isCorrection = oldRegion.replace(/[_\s\n]/g, "").length > 0;
262
+ const finishAnimation = () => {
263
+ setAnimatedContent(toContent);
264
+ setIsAnimating(false);
265
+ isAnimatingRef.current = false;
266
+ setAnimatedCursorPos(null);
267
+ previousContentRef.current = toContent;
268
+ targetContentRef.current = "";
269
+ timeoutRef.current = null;
270
+ rafRef.current = null;
271
+ if (processNextRef.current) processNextRef.current();
272
+ };
273
+ if (isCorrection) {
274
+ const CORRECTION_BUDGET = 400;
275
+ const removalBudget = Math.floor(CORRECTION_BUDGET * 0.4);
276
+ const typingBudget = CORRECTION_BUDGET - removalBudget;
277
+ const removalFrames = Math.max(2, Math.floor(removalBudget / FRAME_DELAY));
278
+ const typingFrames = Math.max(2, Math.floor(typingBudget / FRAME_DELAY));
279
+ const removalCPF = Math.max(1, Math.ceil(diffLength / removalFrames));
280
+ const typingCPF = Math.max(1, Math.ceil(diffLength / typingFrames));
281
+ let removePos = diffEnd + 1;
282
+ let typePos = diffStart;
283
+ const buildRemovalContent = (erasedFrom) => {
284
+ const before = fromContent.substring(0, diffStart);
285
+ const remaining = fromContent.substring(diffStart, erasedFrom);
286
+ const erased = " ".repeat(Math.max(0, diffEnd + 1 - erasedFrom));
287
+ const after = toContent.substring(diffEnd + 1);
288
+ return before + remaining + erased + after;
289
+ };
290
+ const buildTypingContent = (revealedUpTo) => {
291
+ const before = toContent.substring(0, diffStart);
292
+ const revealed = toContent.substring(diffStart, revealedUpTo);
293
+ const hidden = toContent.substring(revealedUpTo, diffEnd + 1).replace(/[^\n]/g, " ");
294
+ const after = toContent.substring(diffEnd + 1);
295
+ return before + revealed + hidden + after;
296
+ };
297
+ const animateTyping = () => {
298
+ typePos += typingCPF;
299
+ if (typePos >= diffEnd + 1) {
300
+ finishAnimation();
301
+ } else {
302
+ setAnimatedContent(buildTypingContent(typePos));
303
+ setAnimatedCursorPos(positionToRowCol(toContent, typePos));
304
+ timeoutRef.current = setTimeout(() => {
305
+ rafRef.current = requestAnimationFrame(animateTyping);
306
+ }, FRAME_DELAY);
307
+ }
308
+ };
309
+ const animateRemoval = () => {
310
+ removePos -= removalCPF;
311
+ if (removePos <= diffStart) {
312
+ setAnimatedContent(buildTypingContent(diffStart));
313
+ setAnimatedCursorPos(positionToRowCol(toContent, diffStart));
314
+ timeoutRef.current = setTimeout(() => {
315
+ rafRef.current = requestAnimationFrame(animateTyping);
316
+ }, FRAME_DELAY);
317
+ } else {
318
+ setAnimatedContent(buildRemovalContent(removePos));
319
+ setAnimatedCursorPos(positionToRowCol(fromContent, removePos));
320
+ timeoutRef.current = setTimeout(() => {
321
+ rafRef.current = requestAnimationFrame(animateRemoval);
322
+ }, FRAME_DELAY);
323
+ }
324
+ };
325
+ setAnimatedCursorPos(positionToRowCol(fromContent, diffEnd));
326
+ setAnimatedContent(buildRemovalContent(diffEnd + 1));
327
+ timeoutRef.current = setTimeout(() => {
328
+ rafRef.current = requestAnimationFrame(animateRemoval);
329
+ }, FRAME_DELAY);
330
+ } else {
331
+ let currentPos = diffStart;
332
+ const totalFrames = Math.max(1, Math.floor(typingBudgetMs / FRAME_DELAY));
333
+ const charsPerFrame = Math.max(1, Math.ceil(diffLength / totalFrames));
334
+ setAnimatedCursorPos(positionToRowCol(toContent, diffStart));
335
+ const buildDisplayContent = (revealedUpTo) => {
336
+ const before = toContent.substring(0, diffStart);
337
+ const revealed = toContent.substring(diffStart, revealedUpTo);
338
+ const hidden = toContent.substring(revealedUpTo, diffEnd + 1).replace(/[^\n]/g, " ");
339
+ const after = toContent.substring(diffEnd + 1);
340
+ return before + revealed + hidden + after;
341
+ };
342
+ const animate = () => {
343
+ currentPos += charsPerFrame;
344
+ if (currentPos >= diffEnd + 1) {
345
+ finishAnimation();
346
+ } else {
347
+ setAnimatedContent(buildDisplayContent(currentPos));
348
+ setAnimatedCursorPos(positionToRowCol(toContent, currentPos));
349
+ timeoutRef.current = setTimeout(() => {
350
+ rafRef.current = requestAnimationFrame(animate);
351
+ }, FRAME_DELAY);
352
+ }
353
+ };
354
+ setAnimatedContent(buildDisplayContent(diffStart));
355
+ timeoutRef.current = setTimeout(() => {
356
+ rafRef.current = requestAnimationFrame(animate);
357
+ }, FRAME_DELAY);
358
+ }
359
+ }, [showInstant, typingBudgetMs]);
360
+ processNextRef.current = () => {
361
+ const queue = contentQueueRef.current;
362
+ if (queue.length === 0) return;
363
+ while (queue.length > 2) queue.shift();
364
+ const next = queue.shift();
365
+ const base = previousContentRef.current;
366
+ if (isFieldEntry(base, next)) {
367
+ startFieldAnimation(base, next);
368
+ } else {
369
+ showInstant(next);
370
+ if (queue.length > 0 && processNextRef.current) processNextRef.current();
371
+ }
372
+ };
373
+ const shouldAnimate = enabled && content && isFieldEntry(previousContentRef.current, content);
374
+ if (!shouldAnimate && !isAnimatingRef.current && content !== previousContentRef.current) {
375
+ previousContentRef.current = content || "";
376
+ }
377
+ (0, import_react2.useEffect)(() => {
378
+ if (!enabled || !content) {
379
+ cancelAnimation();
380
+ contentQueueRef.current = [];
381
+ showInstant(content || "");
382
+ previousContentRef.current = content || "";
383
+ return;
384
+ }
385
+ const currentTarget = targetContentRef.current || previousContentRef.current;
386
+ if (content === currentTarget) return;
387
+ if (isAnimatingRef.current) {
388
+ if (!isFieldEntry(currentTarget, content)) {
389
+ contentQueueRef.current = [];
390
+ showInstant(content);
391
+ } else {
392
+ contentQueueRef.current.push(content);
393
+ if (contentQueueRef.current.length > 3) {
394
+ contentQueueRef.current = contentQueueRef.current.slice(-2);
395
+ }
396
+ }
397
+ return;
398
+ }
399
+ const prev = previousContentRef.current;
400
+ if (!isFieldEntry(prev, content)) {
401
+ showInstant(content);
402
+ return;
403
+ }
404
+ startFieldAnimation(prev, content);
405
+ return cancelAnimation;
406
+ }, [content, enabled, showInstant, startFieldAnimation]);
407
+ const animationActive = isAnimatingRef.current;
408
+ const displayedContent = (0, import_react2.useMemo)(() => {
409
+ if (!enabled || !content) return content || "";
410
+ if (animationActive) return animatedContent;
411
+ return content;
412
+ }, [enabled, content, animationActive, animatedContent]);
413
+ const effectiveCursorPos = animationActive ? animatedCursorPos : null;
414
+ return { displayedContent, isAnimating, animatedCursorPos: effectiveCursorPos };
415
+ }
416
+
417
+ // src/protocols/tn5250.ts
418
+ var tn5250Profile = {
419
+ protocol: "tn5250",
420
+ displayName: "IBM TN5250",
421
+ defaultRows: 24,
422
+ defaultCols: 80,
423
+ headerLabel: "TN5250 TERMINAL",
424
+ bootText: "TN5250",
425
+ colors: {
426
+ getRowColorClass(rowIndex, rowContent, totalRows) {
427
+ if (rowIndex === 0 && rowContent.trim().length > 0) return "gs-row-title";
428
+ if (rowIndex >= totalRows - 2) return "gs-row-fkey";
429
+ if (/F\d{1,2}=/.test(rowContent)) return "gs-row-fkey";
430
+ if (/Select one of the following/i.test(rowContent)) return "gs-row-subtitle";
431
+ return "gs-row-content";
432
+ },
433
+ parseHeaderRow(line) {
434
+ if (line.trim().length === 0) return null;
435
+ const trimmed = line.trimStart();
436
+ const leftMatch = trimmed.match(/^(\S+)/);
437
+ if (leftMatch) {
438
+ const leftId = leftMatch[1];
439
+ const leftEnd = line.indexOf(leftId) + leftId.length;
440
+ const rightMatch = line.trimEnd().match(/(\S+)$/);
441
+ if (rightMatch && rightMatch[1] !== leftId) {
442
+ const rightStart = line.lastIndexOf(rightMatch[1]);
443
+ return [
444
+ { text: line.substring(0, leftEnd), colorClass: "gs-row-fkey" },
445
+ { text: line.substring(leftEnd, rightStart), colorClass: "gs-row-title" },
446
+ { text: line.substring(rightStart), colorClass: "gs-row-content" }
447
+ ];
448
+ }
449
+ }
450
+ return [{ text: line, colorClass: "gs-row-title" }];
451
+ }
452
+ }
453
+ };
454
+
455
+ // src/protocols/tn3270.ts
456
+ var tn3270Profile = {
457
+ protocol: "tn3270",
458
+ displayName: "IBM TN3270",
459
+ defaultRows: 24,
460
+ defaultCols: 80,
461
+ headerLabel: "TN3270 TERMINAL",
462
+ bootText: "TN3270",
463
+ colors: {
464
+ getRowColorClass(rowIndex, rowContent, totalRows) {
465
+ if (rowIndex === 0 && rowContent.trim().length > 0) return "gs-row-title";
466
+ if (rowIndex >= totalRows - 2) return "gs-row-fkey";
467
+ if (/PF\d{1,2}[=/]/.test(rowContent) || /F\d{1,2}=/.test(rowContent)) return "gs-row-fkey";
468
+ if (/COMMAND\s*===>/i.test(rowContent) || /===>/.test(rowContent)) return "gs-row-subtitle";
469
+ if (/OPTION\s*===>/i.test(rowContent)) return "gs-row-subtitle";
470
+ return "gs-row-content";
471
+ },
472
+ parseHeaderRow(line) {
473
+ if (line.trim().length === 0) return null;
474
+ const trimmed = line.trimStart();
475
+ const leftMatch = trimmed.match(/^(\S+)/);
476
+ if (leftMatch) {
477
+ const leftId = leftMatch[1];
478
+ const leftEnd = line.indexOf(leftId) + leftId.length;
479
+ const rightMatch = line.trimEnd().match(/(\S+)$/);
480
+ if (rightMatch && rightMatch[1] !== leftId) {
481
+ const rightStart = line.lastIndexOf(rightMatch[1]);
482
+ return [
483
+ { text: line.substring(0, leftEnd), colorClass: "gs-row-fkey" },
484
+ { text: line.substring(leftEnd, rightStart), colorClass: "gs-row-title" },
485
+ { text: line.substring(rightStart), colorClass: "gs-row-content" }
486
+ ];
487
+ }
488
+ }
489
+ return [{ text: line, colorClass: "gs-row-title" }];
490
+ }
491
+ }
492
+ };
493
+
494
+ // src/protocols/vt.ts
495
+ var vtProfile = {
496
+ protocol: "vt",
497
+ displayName: "VT Terminal",
498
+ defaultRows: 24,
499
+ defaultCols: 80,
500
+ headerLabel: "VT TERMINAL",
501
+ bootText: "VT220",
502
+ colors: {
503
+ getRowColorClass(_rowIndex, _rowContent, _totalRows) {
504
+ return "gs-row-content";
505
+ },
506
+ parseHeaderRow(_line) {
507
+ return null;
508
+ }
509
+ }
510
+ };
511
+
512
+ // src/protocols/hp6530.ts
513
+ var hp6530Profile = {
514
+ protocol: "hp6530",
515
+ displayName: "HP 6530",
516
+ defaultRows: 24,
517
+ defaultCols: 80,
518
+ headerLabel: "6530 TERMINAL",
519
+ bootText: "HP6530",
520
+ colors: {
521
+ getRowColorClass(rowIndex, rowContent, totalRows) {
522
+ if (rowIndex === 0 && rowContent.trim().length > 0) return "gs-row-title";
523
+ if (rowIndex >= totalRows - 1) return "gs-row-fkey";
524
+ if (/F\d{1,2}[=\-]/.test(rowContent) || /SF\d{1,2}/.test(rowContent)) return "gs-row-fkey";
525
+ return "gs-row-content";
526
+ },
527
+ parseHeaderRow(line) {
528
+ if (line.trim().length === 0) return null;
529
+ return [{ text: line, colorClass: "gs-row-title" }];
530
+ }
531
+ }
532
+ };
533
+
534
+ // src/protocols/registry.ts
535
+ var profiles = {
536
+ tn5250: tn5250Profile,
537
+ tn3270: tn3270Profile,
538
+ vt: vtProfile,
539
+ hp6530: hp6530Profile
540
+ };
541
+ function getProtocolProfile(protocol) {
542
+ return profiles[protocol || "tn5250"];
543
+ }
544
+
545
+ // src/components/TerminalBootLoader.tsx
546
+ var import_react3 = require("react");
547
+ var import_jsx_runtime = require("react/jsx-runtime");
548
+ function TerminalBootLoader({
549
+ brandText = "TERMINAL",
550
+ charSpeed = 80,
551
+ logo,
552
+ height = 504
553
+ }) {
554
+ const [charCount, setCharCount] = (0, import_react3.useState)(0);
555
+ const intervalRef = (0, import_react3.useRef)(null);
556
+ (0, import_react3.useEffect)(() => {
557
+ intervalRef.current = setInterval(() => {
558
+ setCharCount((prev) => {
559
+ if (prev >= brandText.length) {
560
+ if (intervalRef.current) clearInterval(intervalRef.current);
561
+ return prev;
562
+ }
563
+ return prev + 1;
564
+ });
565
+ }, charSpeed);
566
+ return () => {
567
+ if (intervalRef.current) clearInterval(intervalRef.current);
568
+ };
569
+ }, [brandText, charSpeed]);
570
+ const displayed = brandText.substring(0, charCount);
571
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
572
+ "div",
573
+ {
574
+ className: "gs-boot-loader",
575
+ style: {
576
+ height: typeof height === "number" ? `${height}px` : height,
577
+ backgroundColor: "var(--gs-bg, #000)",
578
+ display: "flex",
579
+ alignItems: "center",
580
+ justifyContent: "center",
581
+ fontFamily: "var(--gs-font)",
582
+ fontSize: "13px"
583
+ },
584
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "16px" }, children: [
585
+ logo,
586
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
587
+ "h1",
588
+ {
589
+ style: {
590
+ fontFamily: "var(--gs-font)",
591
+ fontSize: "1.5rem",
592
+ letterSpacing: "0.1em",
593
+ color: "var(--gs-green, #10b981)",
594
+ textShadow: "0 0 12px rgba(16, 185, 129, 0.4)",
595
+ margin: 0
596
+ },
597
+ children: [
598
+ displayed,
599
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
600
+ "span",
601
+ {
602
+ className: "tn5250-cursor",
603
+ style: {
604
+ display: "inline-block",
605
+ width: "0.6ch",
606
+ height: "1.2em",
607
+ verticalAlign: "middle",
608
+ marginLeft: "2px"
609
+ }
610
+ }
611
+ )
612
+ ]
613
+ }
614
+ )
615
+ ] })
616
+ }
617
+ );
618
+ }
619
+
620
+ // src/components/GreenScreenTerminal.tsx
621
+ var import_jsx_runtime2 = require("react/jsx-runtime");
622
+ 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
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("polyline", { points: "4 17 10 11 4 5" }),
624
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "12", y1: "19", x2: "20", y2: "19" })
625
+ ] });
626
+ var WifiIcon = ({ size = 12, className, style: s }) => /* @__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", className, style: s, children: [
627
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M5 12.55a11 11 0 0 1 14.08 0" }),
628
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M1.42 9a16 16 0 0 1 21.16 0" }),
629
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8.53 16.11a6 6 0 0 1 6.95 0" }),
630
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "12", y1: "20", x2: "12.01", y2: "20" })
631
+ ] });
632
+ var WifiOffIcon = ({ size = 12, className, style: s }) => /* @__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", className, style: s, children: [
633
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "1", y1: "1", x2: "23", y2: "23" }),
634
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M16.72 11.06A10.94 10.94 0 0 1 19 12.55" }),
635
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M5 12.55a10.94 10.94 0 0 1 5.17-2.39" }),
636
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M10.71 5.05A16 16 0 0 1 22.56 9" }),
637
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M1.42 9a15.91 15.91 0 0 1 4.7-2.88" }),
638
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8.53 16.11a6 6 0 0 1 6.95 0" }),
639
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "12", y1: "20", x2: "12.01", y2: "20" })
640
+ ] });
641
+ var AlertTriangleIcon = ({ 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: [
642
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" }),
643
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "12", y1: "9", x2: "12", y2: "13" }),
644
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
645
+ ] });
646
+ var RefreshIcon = ({ size = 12, className }) => /* @__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", className, children: [
647
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("polyline", { points: "23 4 23 10 17 10" }),
648
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("polyline", { points: "1 20 1 14 7 14" }),
649
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15" })
650
+ ] });
651
+ 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
+ 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" }) });
653
+ var PROTOCOL_OPTIONS = [
654
+ { value: "tn5250", label: "TN5250 (IBM i)" },
655
+ { value: "tn3270", label: "TN3270 (Mainframe)" },
656
+ { value: "vt", label: "VT220" },
657
+ { value: "hp6530", label: "HP 6530 (NonStop)" }
658
+ ];
659
+ function InlineSignIn({ defaultProtocol, loading, error, onConnect }) {
660
+ const [host, setHost] = (0, import_react4.useState)("");
661
+ const [port, setPort] = (0, import_react4.useState)("");
662
+ const [selectedProtocol, setSelectedProtocol] = (0, import_react4.useState)(defaultProtocol);
663
+ const [username, setUsername] = (0, import_react4.useState)("");
664
+ const [password, setPassword] = (0, import_react4.useState)("");
665
+ const handleSubmit = (e) => {
666
+ e.preventDefault();
667
+ onConnect({
668
+ host,
669
+ port: port ? parseInt(port, 10) : void 0,
670
+ protocol: selectedProtocol,
671
+ username,
672
+ password
673
+ });
674
+ };
675
+ const inputStyle = {
676
+ width: "100%",
677
+ padding: "6px 8px",
678
+ backgroundColor: "rgba(16, 185, 129, 0.05)",
679
+ border: "1px solid var(--gs-card-border, #1e293b)",
680
+ color: "var(--gs-green, #10b981)",
681
+ fontFamily: "var(--gs-font)",
682
+ fontSize: "13px",
683
+ outline: "none",
684
+ boxSizing: "border-box"
685
+ };
686
+ const labelStyle = {
687
+ display: "block",
688
+ marginBottom: "4px",
689
+ fontSize: "10px",
690
+ letterSpacing: "0.1em",
691
+ textTransform: "uppercase",
692
+ color: "var(--gs-muted, #94a3b8)",
693
+ fontFamily: "var(--gs-font)"
694
+ };
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" })
699
+ ] }),
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 })
704
+ ] }),
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" })
708
+ ] })
709
+ ] }),
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)) })
713
+ ] }),
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" })
717
+ ] }),
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" })
721
+ ] }),
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 })
725
+ ] }),
726
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "submit", disabled: loading || !host || !username || !password, className: "gs-signin-btn", children: loading ? "Connecting..." : "Connect" })
727
+ ] });
728
+ }
729
+ function GreenScreenTerminal({
730
+ adapter,
731
+ protocol,
732
+ protocolProfile: customProfile,
733
+ screenData: externalScreenData,
734
+ connectionStatus: externalStatus,
735
+ readOnly = false,
736
+ pollInterval = 2e3,
737
+ autoReconnect: autoReconnectEnabled = true,
738
+ maxReconnectAttempts: maxAttempts = 5,
739
+ embedded = false,
740
+ showHeader = true,
741
+ typingAnimation = true,
742
+ typingBudgetMs = 60,
743
+ inlineSignIn = false,
744
+ defaultProtocol: signInDefaultProtocol,
745
+ onSignIn,
746
+ bootLoader,
747
+ headerRight,
748
+ overlay,
749
+ onNotification,
750
+ onScreenChange,
751
+ onMinimize,
752
+ className,
753
+ style
754
+ }) {
755
+ const profile = customProfile ?? getProtocolProfile(protocol);
756
+ const shouldPoll = pollInterval > 0 && !externalScreenData;
757
+ const { data: polledScreenData, error: screenError } = useTerminalScreen(adapter, pollInterval, shouldPoll);
758
+ const { sendText: _sendText, sendKey: _sendKey } = useTerminalInput(adapter);
759
+ const { connect, reconnect, loading: reconnecting, error: connectError } = useTerminalConnection(adapter);
760
+ const rawScreenData = externalScreenData ?? polledScreenData;
761
+ const connStatus = externalStatus ?? (rawScreenData ? { connected: true, status: "authenticated" } : { connected: false, status: "disconnected" });
762
+ const { displayedContent, animatedCursorPos } = useTypingAnimation(
763
+ rawScreenData?.content,
764
+ typingAnimation,
765
+ typingBudgetMs
766
+ );
767
+ const screenData = (0, import_react4.useMemo)(() => {
768
+ if (!rawScreenData) return null;
769
+ return { ...rawScreenData, content: displayedContent };
770
+ }, [rawScreenData, displayedContent]);
771
+ const prevScreenSigRef = (0, import_react4.useRef)(void 0);
772
+ (0, import_react4.useEffect)(() => {
773
+ if (screenData && onScreenChange && screenData.screen_signature !== prevScreenSigRef.current) {
774
+ prevScreenSigRef.current = screenData.screen_signature;
775
+ onScreenChange(screenData);
776
+ }
777
+ }, [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)(() => {
787
+ const newContent = rawScreenData?.content || "";
788
+ if (prevRawContentRef.current && newContent && newContent !== prevRawContentRef.current) {
789
+ setSyncedCursor(null);
790
+ setInputText("");
791
+ }
792
+ prevRawContentRef.current = newContent;
793
+ }, [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)(() => {
800
+ isConnectedRef.current = connStatus?.connected ?? false;
801
+ }, [connStatus?.connected]);
802
+ (0, import_react4.useEffect)(() => {
803
+ if (!autoReconnectEnabled) return;
804
+ const isConnected = connStatus?.connected;
805
+ if (isConnected) {
806
+ wasConnectedRef.current = true;
807
+ if (reconnectTimeoutRef.current) {
808
+ clearTimeout(reconnectTimeoutRef.current);
809
+ reconnectTimeoutRef.current = null;
810
+ }
811
+ setAutoReconnectAttempt(0);
812
+ setIsAutoReconnecting(false);
813
+ } else if (wasConnectedRef.current && !isConnected && !isAutoReconnecting && !reconnecting) {
814
+ if (autoReconnectAttempt < maxAttempts) {
815
+ setIsAutoReconnecting(true);
816
+ const delay = Math.pow(2, autoReconnectAttempt) * 1e3;
817
+ reconnectTimeoutRef.current = setTimeout(async () => {
818
+ if (isConnectedRef.current) {
819
+ setIsAutoReconnecting(false);
820
+ return;
821
+ }
822
+ onNotification?.(`Auto-reconnect attempt ${autoReconnectAttempt + 1}/${maxAttempts}`, "info");
823
+ try {
824
+ const result = await reconnect();
825
+ if (!result?.success) setAutoReconnectAttempt((prev) => prev + 1);
826
+ } catch {
827
+ onNotification?.("Auto-reconnect failed", "error");
828
+ setAutoReconnectAttempt((prev) => prev + 1);
829
+ }
830
+ setIsAutoReconnecting(false);
831
+ }, delay);
832
+ }
833
+ }
834
+ return () => {
835
+ if (reconnectTimeoutRef.current) clearTimeout(reconnectTimeoutRef.current);
836
+ };
837
+ }, [connStatus?.connected, autoReconnectAttempt, isAutoReconnecting, reconnecting, reconnect, autoReconnectEnabled, maxAttempts, onNotification]);
838
+ const handleSignIn = (0, import_react4.useCallback)(async (config) => {
839
+ onSignIn?.(config);
840
+ 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)(() => {
845
+ if (screenData?.content && showBootLoader) {
846
+ setBootFadingOut(true);
847
+ setShowBootLoader(false);
848
+ const timer = setTimeout(() => setBootFadingOut(false), 400);
849
+ return () => clearTimeout(timer);
850
+ }
851
+ }, [screenData?.content, showBootLoader]);
852
+ (0, import_react4.useEffect)(() => {
853
+ const handleClickOutside = (event) => {
854
+ if (terminalRef.current && !terminalRef.current.contains(event.target)) setIsFocused(false);
855
+ };
856
+ if (isFocused) document.addEventListener("mousedown", handleClickOutside);
857
+ return () => document.removeEventListener("mousedown", handleClickOutside);
858
+ }, [isFocused]);
859
+ (0, import_react4.useEffect)(() => {
860
+ if (readOnly && isFocused) {
861
+ setIsFocused(false);
862
+ inputRef.current?.blur();
863
+ }
864
+ }, [readOnly, isFocused]);
865
+ const handleTerminalClick = (0, import_react4.useCallback)(() => {
866
+ if (readOnly) return;
867
+ setIsFocused(true);
868
+ inputRef.current?.focus();
869
+ }, [readOnly]);
870
+ const getCurrentField = (0, import_react4.useCallback)(() => {
871
+ const fields = screenData?.fields || [];
872
+ const cursorRow = syncedCursor?.row ?? screenData?.cursor_row ?? 0;
873
+ const cursorCol = syncedCursor?.col ?? screenData?.cursor_col ?? 0;
874
+ for (const field of fields) {
875
+ if (field.is_input && field.row === cursorRow && cursorCol >= field.col && cursorCol < field.col + field.length) return field;
876
+ }
877
+ return null;
878
+ }, [screenData, syncedCursor]);
879
+ const canTypeMore = (0, import_react4.useCallback)((additionalChars = 1) => {
880
+ const currentField = getCurrentField();
881
+ if (!currentField) return true;
882
+ const cursorCol = (syncedCursor?.col ?? screenData?.cursor_col ?? 0) + inputText.length;
883
+ return cursorCol + additionalChars <= currentField.col + currentField.length;
884
+ }, [getCurrentField, syncedCursor, screenData, inputText]);
885
+ const handleKeyDown = async (e) => {
886
+ if (readOnly) {
887
+ e.preventDefault();
888
+ return;
889
+ }
890
+ if (e.key === "Escape") {
891
+ e.preventDefault();
892
+ setIsFocused(false);
893
+ inputRef.current?.blur();
894
+ return;
895
+ }
896
+ if (e.key === "Backspace") {
897
+ e.preventDefault();
898
+ if (inputText.length > 0) {
899
+ setInputText((prev) => prev.slice(0, -1));
900
+ } else {
901
+ const keyResult = await sendKey("BACKSPACE");
902
+ if (keyResult.cursor_row !== void 0) setSyncedCursor({ row: keyResult.cursor_row, col: keyResult.cursor_col });
903
+ }
904
+ return;
905
+ }
906
+ const keyMap = {
907
+ Enter: "ENTER",
908
+ Tab: "TAB",
909
+ Delete: "DELETE",
910
+ ArrowUp: "UP",
911
+ ArrowDown: "DOWN",
912
+ ArrowLeft: "LEFT",
913
+ ArrowRight: "RIGHT",
914
+ PageUp: "PAGEUP",
915
+ PageDown: "PAGEDOWN",
916
+ Home: "HOME",
917
+ End: "END",
918
+ Insert: "INSERT"
919
+ };
920
+ if (e.key.startsWith("F") && e.key.length <= 3) {
921
+ e.preventDefault();
922
+ const fKey = e.key.toUpperCase();
923
+ if (/^F([1-9]|1[0-9]|2[0-4])$/.test(fKey)) {
924
+ if (inputText) {
925
+ const r = await sendText(inputText);
926
+ setInputText("");
927
+ if (r.cursor_row !== void 0) setSyncedCursor({ row: r.cursor_row, col: r.cursor_col });
928
+ }
929
+ const kr = await sendKey(fKey);
930
+ if (kr.cursor_row !== void 0) setSyncedCursor({ row: kr.cursor_row, col: kr.cursor_col });
931
+ return;
932
+ }
933
+ }
934
+ if (keyMap[e.key]) {
935
+ e.preventDefault();
936
+ if (inputText) {
937
+ const r = await sendText(inputText);
938
+ setInputText("");
939
+ if (r.cursor_row !== void 0) setSyncedCursor({ row: r.cursor_row, col: r.cursor_col });
940
+ }
941
+ const kr = await sendKey(keyMap[e.key]);
942
+ if (kr.cursor_row !== void 0) setSyncedCursor({ row: kr.cursor_row, col: kr.cursor_col });
943
+ }
944
+ };
945
+ const handleInput = async (e) => {
946
+ if (readOnly) {
947
+ e.target.value = "";
948
+ return;
949
+ }
950
+ const newText = e.target.value;
951
+ if (newText.includes("\n")) {
952
+ const textToSend = newText.replace("\n", "");
953
+ if (textToSend) {
954
+ const r = await sendText(textToSend);
955
+ if (r.cursor_row !== void 0) setSyncedCursor({ row: r.cursor_row, col: r.cursor_col });
956
+ }
957
+ const kr = await sendKey("ENTER");
958
+ if (kr.cursor_row !== void 0) setSyncedCursor({ row: kr.cursor_row, col: kr.cursor_col });
959
+ setInputText("");
960
+ e.target.value = "";
961
+ } else {
962
+ const charsToAdd = newText.length - inputText.length;
963
+ if (charsToAdd > 0 && !canTypeMore(charsToAdd)) {
964
+ e.target.value = inputText;
965
+ return;
966
+ }
967
+ setInputText(newText);
968
+ }
969
+ };
970
+ const termCols = screenData?.cols || profile.defaultCols;
971
+ const getCursorPos = () => {
972
+ if (animatedCursorPos) return animatedCursorPos;
973
+ let cursorRow = syncedCursor?.row ?? screenData?.cursor_row ?? 0;
974
+ let cursorCol = (syncedCursor?.col ?? screenData?.cursor_col ?? 0) + inputText.length;
975
+ while (cursorCol >= termCols) {
976
+ cursorCol -= termCols;
977
+ cursorRow += 1;
978
+ }
979
+ return { row: cursorRow, col: cursorCol };
980
+ };
981
+ const renderTextWithUnderlines = (0, import_react4.useCallback)((text, keyPrefix) => {
982
+ const underscoreRegex = /_{2,}/g;
983
+ const segments = [];
984
+ let lastIndex = 0;
985
+ let match;
986
+ let segmentIndex = 0;
987
+ 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}`));
989
+ 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}`));
991
+ lastIndex = match.index + match[0].length;
992
+ segmentIndex++;
993
+ }
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 });
996
+ }, []);
997
+ const renderRowWithFields = (0, import_react4.useCallback)((line, rowIndex, fields) => {
998
+ const inputFields = fields.filter((f) => f.row === rowIndex && f.is_input);
999
+ const highlightedFields = fields.filter((f) => f.row === rowIndex && f.is_protected && f.is_highlighted);
1000
+ const reverseFields = fields.filter((f) => f.row === rowIndex && f.is_protected && f.is_reverse);
1001
+ const allRowFields = [...inputFields, ...highlightedFields, ...reverseFields];
1002
+ if (allRowFields.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: renderTextWithUnderlines(line, `r${rowIndex}`) });
1003
+ const sorted = [...allRowFields].sort((a, b) => a.col - b.col);
1004
+ const segs = [];
1005
+ let lastEnd = 0;
1006
+ const cols = screenData?.cols || profile.defaultCols;
1007
+ sorted.forEach((field, idx) => {
1008
+ const fs = field.col;
1009
+ 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}`));
1011
+ const fc = line.substring(fs, fe);
1012
+ if (field.is_input) {
1013
+ 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}`));
1015
+ } else if (field.is_reverse) {
1016
+ segs.push(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#ef4444", fontWeight: "bold" }, children: fc }, `v${idx}`));
1017
+ } else {
1018
+ segs.push(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "var(--gs-white, #FFFFFF)" }, children: fc }, `h${idx}`));
1019
+ }
1020
+ lastEnd = fe;
1021
+ });
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 });
1024
+ }, [renderTextWithUnderlines, screenData?.cols, profile.defaultCols]);
1025
+ const renderScreen = () => {
1026
+ if (showBootLoader && !screenData?.content) {
1027
+ 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 });
1030
+ }
1031
+ if (bootFadingOut && screenData?.content) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "gs-fade-in", children: renderScreenContent() });
1032
+ if (!screenData?.content) {
1033
+ 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 }) });
1035
+ }
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" })
1039
+ ] }) });
1040
+ }
1041
+ return renderScreenContent();
1042
+ };
1043
+ const renderScreenContent = () => {
1044
+ if (!screenData?.content) return null;
1045
+ const termRows = screenData.rows || profile.defaultRows;
1046
+ const cols = screenData.cols || profile.defaultCols;
1047
+ const lines = screenData.content.split("\n");
1048
+ const rows = [];
1049
+ for (let i = 0; i < termRows; i++) rows.push((lines[i] || "").padEnd(cols, " "));
1050
+ const fields = screenData.fields || [];
1051
+ const ROW_HEIGHT = 21;
1052
+ const cursor = getCursorPos();
1053
+ const hasInputFields = fields.some((f) => f.is_input);
1054
+ 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: [
1056
+ rows.map((line, index) => {
1057
+ let displayLine = line;
1058
+ if (hasCursor && index === cursor.row && inputText && !animatedCursorPos) {
1059
+ const baseCol = syncedCursor?.col ?? screenData.cursor_col ?? 0;
1060
+ displayLine = (line.substring(0, baseCol) + inputText + line.substring(baseCol + inputText.length)).substring(0, cols).padEnd(cols, " ");
1061
+ }
1062
+ 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" } })
1066
+ ] }, index);
1067
+ }),
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: [
1069
+ String(screenData.cursor_row + 1).padStart(2, "0"),
1070
+ "/",
1071
+ String(screenData.cursor_col + 1).padStart(3, "0")
1072
+ ] })
1073
+ ] });
1074
+ };
1075
+ const handleReconnect = async () => {
1076
+ setAutoReconnectAttempt(0);
1077
+ setIsAutoReconnecting(false);
1078
+ if (reconnectTimeoutRef.current) {
1079
+ clearTimeout(reconnectTimeoutRef.current);
1080
+ reconnectTimeoutRef.current = null;
1081
+ }
1082
+ await reconnect();
1083
+ };
1084
+ const getStatusColor = (status) => {
1085
+ switch (status) {
1086
+ case "authenticated":
1087
+ return "var(--gs-green, #10b981)";
1088
+ case "connected":
1089
+ return "#F59E0B";
1090
+ case "connecting":
1091
+ return "#64748b";
1092
+ case "error":
1093
+ return "#EF4444";
1094
+ default:
1095
+ return "#64748b";
1096
+ }
1097
+ };
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" })
1106
+ ] }),
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) => {
1111
+ e.stopPropagation();
1112
+ onMinimize();
1113
+ }, className: "gs-btn-icon", title: "Minimize terminal", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MinimizeIcon, {}) }),
1114
+ headerRight
1115
+ ] })
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" })
1123
+ ] }),
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" : "" }) })
1132
+ ] }) }),
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 })
1136
+ ] }),
1137
+ headerRight
1138
+ ] })
1139
+ ] }) }),
1140
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "gs-body", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1141
+ "div",
1142
+ {
1143
+ ref: terminalRef,
1144
+ onClick: handleTerminalClick,
1145
+ className: `gs-screen ${embedded ? "gs-screen-embedded" : ""}`,
1146
+ 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
+ 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) })
1151
+ ] }),
1152
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "gs-screen-content", children: renderScreen() }),
1153
+ 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" })
1157
+ ] }),
1158
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1159
+ "input",
1160
+ {
1161
+ ref: inputRef,
1162
+ type: "text",
1163
+ value: inputText,
1164
+ onChange: handleInput,
1165
+ onKeyDown: handleKeyDown,
1166
+ style: { position: "absolute", opacity: 0, pointerEvents: "none" },
1167
+ autoComplete: "off",
1168
+ autoCorrect: "off",
1169
+ autoCapitalize: "off",
1170
+ spellCheck: false
1171
+ }
1172
+ )
1173
+ ]
1174
+ }
1175
+ ) })
1176
+ ] });
1177
+ }
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
+
1239
+ // src/utils/rendering.ts
1240
+ function getRowColorClass(rowIndex, rowContent) {
1241
+ return getProtocolProfile("tn5250").colors.getRowColorClass(rowIndex, rowContent, 24);
1242
+ }
1243
+ function parseHeaderRow(line) {
1244
+ return getProtocolProfile("tn5250").colors.parseHeaderRow(line);
1245
+ }
1246
+ // Annotate the CommonJS export names for ESM import in node:
1247
+ 0 && (module.exports = {
1248
+ GreenScreenTerminal,
1249
+ RestAdapter,
1250
+ TN5250Terminal,
1251
+ TerminalBootLoader,
1252
+ getProtocolProfile,
1253
+ getRowColorClass,
1254
+ hp6530Profile,
1255
+ isFieldEntry,
1256
+ parseHeaderRow,
1257
+ positionToRowCol,
1258
+ tn3270Profile,
1259
+ tn5250Profile,
1260
+ useTN5250Connection,
1261
+ useTN5250Screen,
1262
+ useTN5250Terminal,
1263
+ useTerminalConnection,
1264
+ useTerminalInput,
1265
+ useTerminalScreen,
1266
+ useTypingAnimation,
1267
+ vtProfile
1268
+ });
1269
+ //# sourceMappingURL=index.js.map