summaryrefslogtreecommitdiff
path: root/stacks/web/share/reverse-proxy/docroot/max25-websocket/index.php
diff options
context:
space:
mode:
Diffstat (limited to 'stacks/web/share/reverse-proxy/docroot/max25-websocket/index.php')
-rw-r--r--stacks/web/share/reverse-proxy/docroot/max25-websocket/index.php180
1 files changed, 180 insertions, 0 deletions
diff --git a/stacks/web/share/reverse-proxy/docroot/max25-websocket/index.php b/stacks/web/share/reverse-proxy/docroot/max25-websocket/index.php
new file mode 100644
index 0000000..9876c61
--- /dev/null
+++ b/stacks/web/share/reverse-proxy/docroot/max25-websocket/index.php
@@ -0,0 +1,180 @@
+<?php
+/**
+ * MAX25 browser terminal — remote operator UI (M25/1 via WebSocket proxy).
+ */
+require_once __DIR__ . '/config.php';
+
+$base = rtrim(dirname($_SERVER['SCRIPT_NAME'] ?? ''), '/');
+$ws_path = ($base !== '' ? $base : '/max25-websocket') . '/ws';
+$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'wss' : 'ws';
+$host = $_SERVER['HTTP_HOST'] ?? 'localhost';
+$dev_ws = getenv('MAX25_DEV_WS_URL');
+$ws_url = ($dev_ws !== false && $dev_ws !== '') ? $dev_ws : ($scheme . '://' . $host . $ws_path);
+$session_defaults = max25_web_session_defaults();
+?><!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <title>MAX25 Terminal</title>
+ <style>
+ html, body { height: 100%; margin: 0; }
+ body {
+ display: flex;
+ flex-direction: column;
+ background: #0a0a12;
+ color: #c8d0d8;
+ font-family: ui-monospace, "Cascadia Mono", "Consolas", monospace;
+ font-size: 14px;
+ }
+ #topbar {
+ flex: none;
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: .5rem 1rem;
+ padding: .45rem .75rem;
+ background: #12121c;
+ border-bottom: 1px solid #2a2a38;
+ z-index: 2;
+ }
+ #topbar .brand { font-weight: 600; color: #e8ecf0; margin-right: .25rem; }
+ #topbar .field { color: #9aa4b2; font-size: .85rem; }
+ #topbar .field b { color: #dce2ea; font-weight: 500; }
+ #conn-status { margin-left: auto; font-size: .8rem; color: #8a94a4; }
+ #menu-wrap { position: relative; }
+ #menu-btn {
+ background: #1e1e2e;
+ border: 1px solid #3a3a4a;
+ color: #c8d0d8;
+ font: inherit;
+ font-size: .85rem;
+ padding: .25rem .65rem;
+ border-radius: 4px;
+ cursor: pointer;
+ }
+ #menu-btn:hover, #menu-btn[aria-expanded="true"] { background: #262638; }
+ #menu-panel {
+ display: none;
+ position: absolute;
+ top: calc(100% + 4px);
+ left: 0;
+ min-width: 15rem;
+ background: #161622;
+ border: 1px solid #3a3a4a;
+ border-radius: 4px;
+ box-shadow: 0 8px 24px rgba(0,0,0,.45);
+ z-index: 10;
+ padding: .35rem 0;
+ }
+ #menu-panel.open { display: block; }
+ #menu-panel button {
+ display: block;
+ width: 100%;
+ text-align: left;
+ background: none;
+ border: none;
+ color: #c8d0d8;
+ font: inherit;
+ font-size: .85rem;
+ padding: .4rem .85rem;
+ cursor: pointer;
+ }
+ #menu-panel button:hover { background: #222230; }
+ #menu-panel hr { border: none; border-top: 1px solid #2a2a38; margin: .25rem 0; }
+ #device-select {
+ background: #1e1e2e;
+ border: 1px solid #3a3a4a;
+ color: #dce2ea;
+ font: inherit;
+ font-size: .85rem;
+ padding: .2rem .45rem;
+ border-radius: 4px;
+ max-width: 11rem;
+ }
+ #term {
+ flex: 1;
+ min-height: 0;
+ box-sizing: border-box;
+ width: 100%;
+ margin: 0;
+ padding: .75rem 1rem;
+ white-space: pre-wrap;
+ overflow-y: auto;
+ overflow-x: hidden;
+ }
+ #input-bar {
+ flex: none;
+ display: flex;
+ width: 100%;
+ box-sizing: border-box;
+ border-top: 1px solid #2a2a38;
+ background: #12121c;
+ }
+ #cmd {
+ flex: 1;
+ width: 100%;
+ min-width: 0;
+ box-sizing: border-box;
+ border: none;
+ border-radius: 0;
+ background: #12121c;
+ color: #e0e4e8;
+ font: inherit;
+ font-size: 1rem;
+ line-height: 1.4;
+ padding: .75rem 1rem;
+ outline: none;
+ }
+ #cmd::placeholder { color: #6a7280; }
+ #cmd:focus { background: #161622; }
+ #cmd:disabled { opacity: .55; cursor: not-allowed; }
+ .hint { font-size: .75rem; color: #6a7280; padding: 0 .75rem .35rem; }
+ </style>
+</head>
+<body>
+ <header id="topbar">
+ <span class="brand">MAX25</span>
+ <div id="menu-wrap">
+ <button type="button" id="menu-btn" aria-expanded="false" aria-haspopup="true">Menu ▾</button>
+ <div id="menu-panel" role="menu">
+ <button type="button" data-action="callerid">/callerid — Change CALLERID</button>
+ <button type="button" data-action="callid">/callid — Change CALLID</button>
+ <button type="button" data-action="status">/status — GET STATUS</button>
+ <button type="button" data-action="send">/send — Send line</button>
+ <button type="button" data-action="monitor">/monitor — Toggle MONITOR</button>
+ <button type="button" data-action="connect">/connect — Connection on/off</button>
+ <hr>
+ <button type="button" data-action="devices">/devices — List devices</button>
+ <button type="button" data-action="ax25_ui">/ax25_ui — Toggle AX.25 UI</button>
+ <hr>
+ <button type="button" data-action="help">/help — Commands</button>
+ </div>
+ </div>
+ <span class="field">DEVICE: <b id="hdr-device">-</b></span>
+ <label class="field" for="device-select">select
+ <select id="device-select" disabled title="max25d registered devices only">
+ <option value="">—</option>
+ </select>
+ </label>
+ <span class="field">CALLERID: <b id="hdr-callerid">-</b></span>
+ <span class="field">CALLID: <b id="hdr-callid">-</b></span>
+ <span class="field">ax25-ui: <b id="hdr-ax25">-</b></span>
+ <span class="field">connected: <b id="hdr-connected">-</b></span>
+ <span id="conn-status">disconnected</span>
+ </header>
+ <pre id="term"></pre>
+ <form id="input-bar" autocomplete="off">
+ <input type="text" id="cmd" disabled
+ autocomplete="off" autocapitalize="off" autocorrect="off" spellcheck="false"
+ enterkeyhint="send"
+ placeholder="Type to SEND — or /help for commands">
+ </form>
+ <div class="hint">One device per session · /device &lt;id&gt; · plain text = SEND · only max25d devices</div>
+ <script>
+ window.MAX25_WS_URL = <?php echo json_encode($ws_url, JSON_UNESCAPED_SLASHES); ?>;
+ window.MAX25_SESSION_DEFAULTS = <?php echo json_encode($session_defaults, JSON_UNESCAPED_SLASHES); ?>;
+ </script>
+ <script src="max25-terminal.js"></script>
+</body>
+</html>
git clone -b <branch> https://cgit.mode42.com/<repo>.git
git clone -b <branch> git://cgit.mode42.com/<repo>.git

info@mode42.com