9a2171e4ea
Makes the terminal container DOM node as large as the window (except for the header) via flexbox. The xterm.js terminal is then sized to fit via xterm-addon-fit. Once we have a computed rows/columns size, and we can tell the SSH session of the computed size. Required introducing an IPNSSHSession type to allow the JS to control the SSH session once opened. That alse allows us to programatically close it, which we do when the user closes the window with the session still active. I initially wanted to open the terminal in a new window instead (so that it could be resizable independently of the main window), but xterm.js does not appear to work well in that mode (possibly because it adds an IntersectionObserver to pause rendering when the window is not visible, and it ends up doing that when the parent window is hidden -- see xtermjs/xterm.js@87dca56dee) Fixes #5150 Signed-off-by: Mihai Parparita <mihai@tailscale.com>
75 lines
1.9 KiB
TypeScript
75 lines
1.9 KiB
TypeScript
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
import * as qrcode from "qrcode"
|
|
import { getContentNode } from "./index"
|
|
|
|
export async function showLoginURL(url: string) {
|
|
if (loginNode) {
|
|
loginNode.remove()
|
|
}
|
|
loginNode = document.createElement("div")
|
|
loginNode.className = "flex flex-col items-center justify-items-center"
|
|
const linkNode = document.createElement("a")
|
|
linkNode.className = "link"
|
|
linkNode.href = url
|
|
linkNode.target = "_blank"
|
|
loginNode.appendChild(linkNode)
|
|
|
|
try {
|
|
const dataURL = await qrcode.toDataURL(url, { width: 512 })
|
|
const imageNode = document.createElement("img")
|
|
imageNode.className = "mx-auto"
|
|
imageNode.src = dataURL
|
|
imageNode.width = 256
|
|
imageNode.height = 256
|
|
linkNode.appendChild(imageNode)
|
|
} catch (err) {
|
|
console.error("Could not generate QR code:", err)
|
|
}
|
|
|
|
linkNode.appendChild(document.createTextNode(url))
|
|
|
|
getContentNode().appendChild(loginNode)
|
|
}
|
|
|
|
export function hideLoginURL() {
|
|
if (!loginNode) {
|
|
return
|
|
}
|
|
loginNode.remove()
|
|
loginNode = undefined
|
|
}
|
|
|
|
let loginNode: HTMLDivElement | undefined
|
|
|
|
export function showLogoutButton(ipn: IPN) {
|
|
if (logoutButtonNode) {
|
|
logoutButtonNode.remove()
|
|
}
|
|
logoutButtonNode = document.createElement("button")
|
|
logoutButtonNode.className =
|
|
"button bg-gray-500 border-gray-500 text-white hover:bg-gray-600 hover:border-gray-600 ml-2 font-bold"
|
|
logoutButtonNode.textContent = "Logout"
|
|
logoutButtonNode.addEventListener(
|
|
"click",
|
|
() => {
|
|
ipn.logout()
|
|
},
|
|
{ once: true }
|
|
)
|
|
const headerNode = document.getElementsByTagName("header")[0]!
|
|
headerNode.appendChild(logoutButtonNode)
|
|
}
|
|
|
|
export function hideLogoutButton() {
|
|
if (!logoutButtonNode) {
|
|
return
|
|
}
|
|
logoutButtonNode.remove()
|
|
logoutButtonNode = undefined
|
|
}
|
|
|
|
let logoutButtonNode: HTMLButtonElement | undefined
|