@tn3270/gui 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +50 -2
- package/app/BUNDLED +1 -0
- package/app/atlas.bin +0 -0
- package/app/atlas.json +1 -0
- package/app/connect.html +27 -0
- package/app/connectBoot.js +64 -0
- package/app/connectBoot.js.map +7 -0
- package/app/connectPreload.cjs +19 -0
- package/app/connectPreload.cjs.map +7 -0
- package/app/icon.png +0 -0
- package/app/index.html +6 -0
- package/app/keyboard.html +10 -0
- package/app/keyboardBoot.js +799 -0
- package/app/keyboardBoot.js.map +7 -0
- package/app/keyboardPreload.cjs +18 -0
- package/app/keyboardPreload.cjs.map +7 -0
- package/app/keypad.html +10 -0
- package/app/keypadBoot.js +501 -0
- package/app/keypadBoot.js.map +7 -0
- package/app/keypadPreload.cjs +18 -0
- package/app/keypadPreload.cjs.map +7 -0
- package/app/main.js +11808 -0
- package/app/main.js.map +7 -0
- package/app/package.json +9 -0
- package/app/preload.cjs +37 -0
- package/app/preload.cjs.map +7 -0
- package/app/renderer.js +559 -0
- package/app/renderer.js.map +7 -0
- package/app/transfer.html +15 -0
- package/app/transferBoot.js +531 -0
- package/app/transferBoot.js.map +7 -0
- package/app/transferPreload.cjs +33 -0
- package/app/transferPreload.cjs.map +7 -0
- package/app/ui.css +843 -0
- package/bin/launch.d.mts +10 -0
- package/bin/launch.mjs +56 -0
- package/bin/tn3270-gui.mjs +97 -0
- package/package.json +29 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Adam Thornton
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,51 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @tn3270/gui — the Electron client
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
An Electron TN3270 emulator with a canvas renderer that blits glyphs from an atlas baked
|
|
4
|
+
out of x3270's own 3270 bitmap font, at integer scale with antialiasing off.
|
|
5
|
+
|
|
6
|
+
```sh
|
|
7
|
+
npm install -g @tn3270/gui
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
**This needs Electron's binary, roughly 100 MB.** Electron 44 fetches it the first time
|
|
11
|
+
it is used rather than during `npm install`, so your first `tn3270-gui` prints "Downloading
|
|
12
|
+
Electron binary..." and takes a while: that is the download, not a hang. If it fails (no
|
|
13
|
+
network, a proxy, a wrong `ELECTRON_MIRROR`), `tn3270-gui` says so; fix the network and run
|
|
14
|
+
it again. There is no slimmer, native-only build yet; that is planned as a follow-up.
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
tn3270-gui -insecure -model 3278-4-E HOST:PORT
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
It takes the TUI's flags unchanged (`-model`, `--terminal-type`, `-oversize`, `-tn3270e
|
|
21
|
+
on|off`, the TLS set) because all three interactive front ends parse them with the same
|
|
22
|
+
code. See the root README's *Using the GUI* section
|
|
23
|
+
(https://github.com/athornton/tn3270#readme) for keys, the keypad, the keyboard window
|
|
24
|
+
and copy/paste.
|
|
25
|
+
|
|
26
|
+
Electron's own switches (`--no-sandbox`, `--disable-gpu`, ...) can be given on the same
|
|
27
|
+
command line; they go to Chromium, not to the TN3270 flag parser.
|
|
28
|
+
|
|
29
|
+
### Linux: the sandbox
|
|
30
|
+
|
|
31
|
+
`tn3270-gui` leaves Chromium's sandbox ON. npm cannot install Electron's `chrome-sandbox`
|
|
32
|
+
helper setuid root, but Chromium does not need it where unprivileged user namespaces are
|
|
33
|
+
allowed (most distributions), and runs sandboxed. Where they are blocked — Ubuntu 23.10 and
|
|
34
|
+
later, through AppArmor, and some hardened kernels — Electron aborts with *"The SUID sandbox
|
|
35
|
+
helper binary was found, but is not configured correctly"*, and `tn3270-gui` prints two
|
|
36
|
+
ways out:
|
|
37
|
+
|
|
38
|
+
- as root, make the helper what Chromium expects (keeps the sandbox):
|
|
39
|
+
`chown root:root <path>/chrome-sandbox && chmod 4755 <path>/chrome-sandbox` — the path is
|
|
40
|
+
in the message, under the global `node_modules/@tn3270/gui/node_modules/electron/dist/`;
|
|
41
|
+
- or run without the sandbox: `tn3270-gui --no-sandbox ...`. In normal use the app loads
|
|
42
|
+
only its own bundled pages, but a sandbox is defence in depth against a
|
|
43
|
+
renderer bug, and this switches it off. The launcher never does this for you.
|
|
44
|
+
|
|
45
|
+
### Displays and signals
|
|
46
|
+
|
|
47
|
+
It needs a display: on a headless Linux box, run it under `xvfb-run`. Without a GPU, add
|
|
48
|
+
`--disable-gpu`. SIGINT, SIGTERM and SIGHUP sent to `tn3270-gui` are forwarded to Electron,
|
|
49
|
+
and Electron is killed if the launcher exits first; a SIGKILL cannot be forwarded by
|
|
50
|
+
anything, so kill Electron's own PID if you need that. On Windows (not yet tested), stopping
|
|
51
|
+
the launcher terminates Electron outright.
|
package/app/BUNDLED
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Marker read by src/appRoot.ts: this directory is the flat esbuild layout. Contents unused.
|
package/app/atlas.bin
ADDED
|
Binary file
|
package/app/atlas.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"cellWidth":9,"cellHeight":14,"cols":431,"index":{"0":0,"1":1,"2":2,"3":3,"4":4,"5":5,"6":6,"7":7,"8":8,"9":9,"10":10,"11":11,"12":12,"13":13,"14":14,"15":15,"16":16,"17":17,"18":18,"19":19,"20":20,"21":21,"22":22,"23":23,"24":24,"25":25,"26":26,"27":27,"28":28,"29":29,"30":30,"31":31,"32":32,"33":33,"34":34,"35":35,"36":36,"37":37,"38":38,"39":39,"40":40,"41":41,"42":42,"43":43,"44":44,"45":45,"46":46,"47":47,"48":48,"49":49,"50":50,"51":51,"52":52,"53":53,"54":54,"55":55,"56":56,"57":57,"58":58,"59":59,"60":60,"61":61,"62":62,"63":63,"64":64,"65":65,"66":66,"67":67,"68":68,"69":69,"70":70,"71":71,"72":72,"73":73,"74":74,"75":75,"76":76,"77":77,"78":78,"79":79,"80":80,"81":81,"82":82,"83":83,"84":84,"85":85,"86":86,"87":87,"88":88,"89":89,"90":90,"91":91,"92":92,"93":93,"94":94,"95":95,"96":96,"97":97,"98":98,"99":99,"100":100,"101":101,"102":102,"103":103,"104":104,"105":105,"106":106,"107":107,"108":108,"109":109,"110":110,"111":111,"112":112,"113":113,"114":114,"115":115,"116":116,"117":117,"118":118,"119":119,"120":120,"121":121,"122":122,"123":123,"124":124,"125":125,"126":126,"127":127,"128":128,"129":129,"130":130,"131":131,"132":132,"133":133,"134":134,"135":135,"136":136,"137":137,"138":138,"139":139,"140":140,"141":141,"142":142,"143":143,"144":144,"145":145,"146":146,"147":147,"148":148,"149":149,"150":150,"151":151,"152":152,"153":153,"154":154,"155":155,"156":156,"157":157,"158":158,"159":159,"160":160,"161":161,"162":162,"163":163,"164":164,"165":165,"166":166,"167":167,"168":168,"169":169,"170":170,"171":171,"172":172,"173":173,"174":174,"175":175,"176":176,"177":177,"178":178,"179":179,"180":180,"181":181,"182":182,"183":183,"184":184,"185":185,"186":186,"187":187,"188":188,"189":189,"190":190,"191":191,"192":192,"193":193,"194":194,"195":195,"196":196,"197":197,"198":198,"199":199,"200":200,"201":201,"202":202,"203":203,"204":204,"205":205,"206":206,"207":207,"208":208,"209":209,"210":210,"211":211,"212":212,"213":213,"214":214,"215":215,"216":216,"217":217,"218":218,"219":219,"220":220,"221":221,"222":222,"223":223,"224":224,"225":225,"226":226,"227":227,"228":228,"229":229,"230":230,"231":231,"232":232,"233":233,"234":234,"235":235,"236":236,"237":237,"238":238,"239":239,"240":240,"241":241,"242":242,"243":243,"244":244,"245":245,"246":246,"247":247,"248":248,"249":249,"250":250,"251":251,"252":252,"253":253,"254":254,"255":255,"257":256,"263":257,"266":258,"267":259,"270":260,"271":261,"272":262,"277":263,"284":264,"285":265,"286":266,"287":267,"288":268,"289":269,"290":270,"291":271,"292":272,"293":273,"294":274,"295":275,"296":276,"297":277,"298":278,"311":279,"312":280,"313":281,"314":282,"315":283,"316":284,"318":285,"319":286,"320":287,"321":288,"322":289,"323":290,"324":291,"325":292,"326":293,"327":294,"328":295,"329":296,"330":297,"331":298,"332":299,"334":300,"335":301,"336":302,"337":303,"338":304,"339":305,"340":306,"341":307,"342":308,"343":309,"344":310,"345":311,"346":312,"347":313,"348":314,"349":315,"350":316,"351":317,"352":318,"353":319,"354":320,"355":321,"356":322,"357":323,"358":324,"359":325,"360":326,"362":327,"363":328,"364":329,"365":330,"366":331,"367":332,"368":333,"369":334,"370":335,"371":336,"372":337,"373":338,"374":339,"375":340,"376":341,"377":342,"378":343,"379":344,"380":345,"381":346,"382":347,"383":348,"384":349,"385":350,"386":351,"387":352,"388":353,"393":354,"394":355,"395":356,"396":357,"397":358,"402":359,"403":360,"404":361,"410":362,"411":363,"412":364,"413":365,"416":366,"417":367,"418":368,"419":369,"420":370,"421":371,"422":372,"423":373,"425":374,"426":375,"427":376,"428":377,"429":378,"430":379,"431":380,"432":381,"434":382,"435":383,"436":384,"437":385,"438":386,"439":387,"442":388,"443":389,"444":390,"445":391,"464":392,"465":393,"470":394,"471":395,"473":396,"503":397,"505":398,"512":399,"513":400,"514":401,"515":402,"516":403,"517":404,"518":405,"519":406,"520":407,"521":408,"522":409,"523":410,"524":411,"525":412,"526":413,"527":414,"528":415,"529":416,"530":417,"531":418,"532":419,"533":420,"534":421,"535":422,"536":423,"537":424,"538":425,"539":426,"540":427,"541":428,"542":429,"543":430}}
|
package/app/connect.html
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<!-- Generated by packages/gui/scripts/bundle.mjs from packages/gui/connect.html; edit that. -->
|
|
3
|
+
<html><head><meta charset="utf-8"><title>Connect</title>
|
|
4
|
+
<link rel="stylesheet" href="./ui.css">
|
|
5
|
+
</head>
|
|
6
|
+
<body class="ui-surface">
|
|
7
|
+
<form id="connect-form" autocomplete="off">
|
|
8
|
+
<div class="row">
|
|
9
|
+
<label for="host">Host</label>
|
|
10
|
+
<input id="host" type="text" placeholder="host:port" spellcheck="false">
|
|
11
|
+
</div>
|
|
12
|
+
<div class="row">
|
|
13
|
+
<label for="model">Model</label>
|
|
14
|
+
<select id="model"></select>
|
|
15
|
+
</div>
|
|
16
|
+
<div class="row">
|
|
17
|
+
<label for="tls">TLS</label>
|
|
18
|
+
<input id="tls" type="checkbox" checked>
|
|
19
|
+
</div>
|
|
20
|
+
<div id="connect-error" role="alert"></div>
|
|
21
|
+
<div id="buttons">
|
|
22
|
+
<button id="quit" type="button">Quit</button>
|
|
23
|
+
<button id="connect" type="submit" disabled>Connect</button>
|
|
24
|
+
</div>
|
|
25
|
+
</form>
|
|
26
|
+
<script type="module" src="./connectBoot.js"></script>
|
|
27
|
+
</body></html>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// packages/gui/dist/connectBoot.js
|
|
2
|
+
var bridge = window.tn3270connect;
|
|
3
|
+
var form = document.querySelector("#connect-form");
|
|
4
|
+
var host = document.querySelector("#host");
|
|
5
|
+
var model = document.querySelector("#model");
|
|
6
|
+
var tls = document.querySelector("#tls");
|
|
7
|
+
var connect = document.querySelector("#connect");
|
|
8
|
+
var quit = document.querySelector("#quit");
|
|
9
|
+
var error = document.querySelector("#connect-error");
|
|
10
|
+
if (form === null || host === null || model === null || tls === null || connect === null || quit === null || error === null) {
|
|
11
|
+
throw new Error("connect.html is missing one of its controls");
|
|
12
|
+
}
|
|
13
|
+
if (bridge === void 0) {
|
|
14
|
+
throw new Error("connect window: the tn3270connect bridge is absent (preload failed to load?)");
|
|
15
|
+
}
|
|
16
|
+
var busy = true;
|
|
17
|
+
var setBusy = (b) => {
|
|
18
|
+
busy = b;
|
|
19
|
+
connect.disabled = b;
|
|
20
|
+
};
|
|
21
|
+
setBusy(true);
|
|
22
|
+
void bridge.models().then(({ models, default: chosen }) => {
|
|
23
|
+
for (const m of models) {
|
|
24
|
+
const o = document.createElement("option");
|
|
25
|
+
o.value = m;
|
|
26
|
+
o.textContent = m;
|
|
27
|
+
if (m === chosen)
|
|
28
|
+
o.selected = true;
|
|
29
|
+
model.append(o);
|
|
30
|
+
}
|
|
31
|
+
setBusy(false);
|
|
32
|
+
}, (err) => {
|
|
33
|
+
error.textContent = `could not list the models: ${String(err)}`;
|
|
34
|
+
});
|
|
35
|
+
form.addEventListener("submit", (e) => {
|
|
36
|
+
e.preventDefault();
|
|
37
|
+
if (busy)
|
|
38
|
+
return;
|
|
39
|
+
setBusy(true);
|
|
40
|
+
error.textContent = "";
|
|
41
|
+
bridge.submit({ host: host.value, model: model.value, tls: tls.checked }).then((r) => {
|
|
42
|
+
if (r.ok === true)
|
|
43
|
+
return;
|
|
44
|
+
error.textContent = r.error ?? "refused, with no reason given";
|
|
45
|
+
setBusy(false);
|
|
46
|
+
host.focus();
|
|
47
|
+
host.select();
|
|
48
|
+
}, (err) => {
|
|
49
|
+
error.textContent = String(err);
|
|
50
|
+
setBusy(false);
|
|
51
|
+
host.focus();
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
quit.addEventListener("click", () => {
|
|
55
|
+
bridge.quit();
|
|
56
|
+
});
|
|
57
|
+
document.addEventListener("keydown", (e) => {
|
|
58
|
+
if (e.key === "Escape") {
|
|
59
|
+
e.preventDefault();
|
|
60
|
+
bridge.quit();
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
host.focus();
|
|
64
|
+
//# sourceMappingURL=connectBoot.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/connectBoot.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * The startup Connect dialog's page: real elements in, the preload's bridge out.\n *\n * DELIBERATELY THIN, the same split as `transferBoot.ts`: there is no `document` under vitest, so\n * every decision about what the form MEANS lives in main -- `submitConnect` in `connectDialog.ts`,\n * which runs the real `parseGuiArgs`. This file only reads controls, shows the answer and moves\n * focus. It imports NOTHING, so `connect.html` needs no import map and the bundle no graph.\n */\ndeclare global {\n interface Window {\n tn3270connect?: {\n models(): Promise<{ models: readonly string[]; default: string }>;\n submit(form: { host: string; model: string; tls: boolean }): Promise<{ ok?: true; error?: string }>;\n quit(): void;\n };\n }\n}\n\nconst bridge = window.tn3270connect;\nconst form = document.querySelector<HTMLFormElement>('#connect-form');\nconst host = document.querySelector<HTMLInputElement>('#host');\nconst model = document.querySelector<HTMLSelectElement>('#model');\nconst tls = document.querySelector<HTMLInputElement>('#tls');\nconst connect = document.querySelector<HTMLButtonElement>('#connect');\nconst quit = document.querySelector<HTMLButtonElement>('#quit');\nconst error = document.querySelector<HTMLDivElement>('#connect-error');\nif (form === null || host === null || model === null || tls === null || connect === null\n || quit === null || error === null) {\n throw new Error('connect.html is missing one of its controls');\n}\nif (bridge === undefined) {\n throw new Error('connect window: the tn3270connect bridge is absent (preload failed to load?)');\n}\n\n/**\n * DISABLED UNTIL THE MODELS ARRIVE, and while a submit is in flight. An empty dropdown would submit\n * `model: ''`, and two submits in flight would race main's one-time session start.\n */\nlet busy = true;\nconst setBusy = (b: boolean): void => {\n busy = b;\n connect.disabled = b;\n};\nsetBusy(true);\n\nvoid bridge.models().then(({ models, default: chosen }) => {\n for (const m of models) {\n const o = document.createElement('option');\n o.value = m;\n o.textContent = m;\n if (m === chosen) o.selected = true;\n model.append(o);\n }\n setBusy(false);\n}, (err: unknown) => {\n error.textContent = `could not list the models: ${String(err)}`;\n});\n\n/**\n * ENTER IS THE FORM'S OWN SUBMIT: Connect is the form's `type=\"submit\"` button, so Enter in the\n * Host field submits through this listener exactly as a click does -- one path, not two.\n */\nform.addEventListener('submit', (e) => {\n e.preventDefault();\n // ONE SUBMIT IN FLIGHT. Main refuses a second start too; this keeps the page from asking.\n if (busy) return;\n setBusy(true);\n error.textContent = '';\n bridge.submit({ host: host.value, model: model.value, tls: tls.checked }).then((r) => {\n if (r.ok === true) return; // main closes this window and starts the session\n error.textContent = r.error ?? 'refused, with no reason given';\n setBusy(false);\n host.focus();\n host.select();\n }, (err: unknown) => {\n error.textContent = String(err);\n setBusy(false);\n host.focus();\n });\n});\n\nquit.addEventListener('click', () => { bridge.quit(); });\ndocument.addEventListener('keydown', (e) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n bridge.quit();\n }\n});\n\nhost.focus();\n\nexport {};\n"],
|
|
5
|
+
"mappings": ";AAkBA,IAAM,SAAS,OAAO;AACtB,IAAM,OAAO,SAAS,cAA+B,eAAe;AACpE,IAAM,OAAO,SAAS,cAAgC,OAAO;AAC7D,IAAM,QAAQ,SAAS,cAAiC,QAAQ;AAChE,IAAM,MAAM,SAAS,cAAgC,MAAM;AAC3D,IAAM,UAAU,SAAS,cAAiC,UAAU;AACpE,IAAM,OAAO,SAAS,cAAiC,OAAO;AAC9D,IAAM,QAAQ,SAAS,cAA8B,gBAAgB;AACrE,IAAI,SAAS,QAAQ,SAAS,QAAQ,UAAU,QAAQ,QAAQ,QAAQ,YAAY,QAC/E,SAAS,QAAQ,UAAU,MAAM;AACpC,QAAM,IAAI,MAAM,6CAA6C;AAC/D;AACA,IAAI,WAAW,QAAW;AACxB,QAAM,IAAI,MAAM,8EAA8E;AAChG;AAMA,IAAI,OAAO;AACX,IAAM,UAAU,CAAC,MAAoB;AACnC,SAAO;AACP,UAAQ,WAAW;AACrB;AACA,QAAQ,IAAI;AAEZ,KAAK,OAAO,OAAM,EAAG,KAAK,CAAC,EAAE,QAAQ,SAAS,OAAM,MAAM;AACxD,aAAW,KAAK,QAAQ;AACtB,UAAM,IAAI,SAAS,cAAc,QAAQ;AACzC,MAAE,QAAQ;AACV,MAAE,cAAc;AAChB,QAAI,MAAM;AAAQ,QAAE,WAAW;AAC/B,UAAM,OAAO,CAAC;EAChB;AACA,UAAQ,KAAK;AACf,GAAG,CAAC,QAAgB;AAClB,QAAM,cAAc,8BAA8B,OAAO,GAAG,CAAC;AAC/D,CAAC;AAMD,KAAK,iBAAiB,UAAU,CAAC,MAAK;AACpC,IAAE,eAAc;AAEhB,MAAI;AAAM;AACV,UAAQ,IAAI;AACZ,QAAM,cAAc;AACpB,SAAO,OAAO,EAAE,MAAM,KAAK,OAAO,OAAO,MAAM,OAAO,KAAK,IAAI,QAAO,CAAE,EAAE,KAAK,CAAC,MAAK;AACnF,QAAI,EAAE,OAAO;AAAM;AACnB,UAAM,cAAc,EAAE,SAAS;AAC/B,YAAQ,KAAK;AACb,SAAK,MAAK;AACV,SAAK,OAAM;EACb,GAAG,CAAC,QAAgB;AAClB,UAAM,cAAc,OAAO,GAAG;AAC9B,YAAQ,KAAK;AACb,SAAK,MAAK;EACZ,CAAC;AACH,CAAC;AAED,KAAK,iBAAiB,SAAS,MAAK;AAAG,SAAO,KAAI;AAAI,CAAC;AACvD,SAAS,iBAAiB,WAAW,CAAC,MAAK;AACzC,MAAI,EAAE,QAAQ,UAAU;AACtB,MAAE,eAAc;AAChB,WAAO,KAAI;EACb;AACF,CAAC;AAED,KAAK,MAAK;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// packages/gui/dist/connectPreload.cjs
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
var electron_1 = require("electron");
|
|
6
|
+
electron_1.contextBridge.exposeInMainWorld("tn3270connect", {
|
|
7
|
+
/** The dropdown's models and its default, from `KNOWN_MODELS` in main. */
|
|
8
|
+
models: () => electron_1.ipcRenderer.invoke("connect:models"),
|
|
9
|
+
/**
|
|
10
|
+
* `invoke`, not `send`: a refusal is the ANSWER, shown in the dialog, which stays open. On
|
|
11
|
+
* `{ ok: true }` main closes this window and starts the session.
|
|
12
|
+
*/
|
|
13
|
+
submit: (form) => electron_1.ipcRenderer.invoke("connect:submit", form),
|
|
14
|
+
/** Quit the application: there is nothing to show without a host. */
|
|
15
|
+
quit: () => {
|
|
16
|
+
electron_1.ipcRenderer.send("connect:quit");
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
//# sourceMappingURL=connectPreload.cjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/connectPreload.cts"],
|
|
4
|
+
"sourcesContent": ["import { contextBridge, ipcRenderer } from 'electron';\n\n/**\n * The startup Connect dialog's bridge. A `.cts` FILE ON PURPOSE, like every preload here: an ESM\n * preload fails with \"Cannot use import statement outside a module\" and the bridge then silently\n * never appears -- a dialog whose Connect does nothing. See `transferPreload.cts`.\n *\n * `tn3270connect` AND NOT AN ADDITION TO `tn3270`: `exposeInMainWorld` defines a NON-WRITABLE\n * property, and two bridges sharing a name is a measured failure recorded in `main.ts`.\n *\n * THREE FUNCTIONS, all of them main's: the model list (so the page imports nothing from core), the\n * submit (whose answer is the parser's own words), and quit.\n */\ncontextBridge.exposeInMainWorld('tn3270connect', {\n /** The dropdown's models and its default, from `KNOWN_MODELS` in main. */\n models: (): Promise<{ models: readonly string[]; default: string }> =>\n ipcRenderer.invoke('connect:models') as Promise<{ models: readonly string[]; default: string }>,\n /**\n * `invoke`, not `send`: a refusal is the ANSWER, shown in the dialog, which stays open. On\n * `{ ok: true }` main closes this window and starts the session.\n */\n submit: (form: { host: string; model: string; tls: boolean }): Promise<{ ok?: true; error?: string }> =>\n ipcRenderer.invoke('connect:submit', form) as Promise<{ ok?: true; error?: string }>,\n /** Quit the application: there is nothing to show without a host. */\n quit: (): void => { ipcRenderer.send('connect:quit'); },\n});\n"],
|
|
5
|
+
"mappings": ";;;;AAAA,IAAA,aAAA,QAAA,UAAA;AAaA,WAAA,cAAc,kBAAkB,iBAAiB;;EAE/C,QAAQ,MACN,WAAA,YAAY,OAAO,gBAAgB;;;;;EAKrC,QAAQ,CAAC,SACP,WAAA,YAAY,OAAO,kBAAkB,IAAI;;EAE3C,MAAM,MAAW;AAAG,eAAA,YAAY,KAAK,cAAc;EAAG;CACvD;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/app/icon.png
ADDED
|
Binary file
|
package/app/index.html
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<!-- Generated by packages/gui/scripts/bundle.mjs from packages/gui/index.html; edit that. -->
|
|
3
|
+
<html><head><meta charset="utf-8"><title>tn3270</title>
|
|
4
|
+
<style>html,body{margin:0;background:#000;overflow:hidden}canvas{display:block}</style>
|
|
5
|
+
</head><body><canvas id="screen"></canvas>
|
|
6
|
+
<script type="module" src="./renderer.js"></script></body></html>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<!-- Generated by packages/gui/scripts/bundle.mjs from packages/gui/keyboard.html; edit that. -->
|
|
3
|
+
<html><head><meta charset="utf-8"><title>Keyboard</title>
|
|
4
|
+
<link rel="stylesheet" href="./ui.css">
|
|
5
|
+
</head>
|
|
6
|
+
<body class="ui-surface">
|
|
7
|
+
<h1>Keyboard</h1>
|
|
8
|
+
<div id="keyboard-root" class="keyboard-surface"></div>
|
|
9
|
+
<script type="module" src="./keyboardBoot.js"></script>
|
|
10
|
+
</body></html>
|