- Remove detectClipboard Process and clipboardCmd property - Add try-fallback-cached copy logic: attempt cached tool first, then cycle through hardcoded safe tools (dms, wl-copy, clipmanctl) - Add stderr StdioCollector to all Process components for error details - Update lib.js: replace parseClipboardDetection with validateClipboardTool, buildCopyCommand, nextClipboardTool, allClipboardTools - Update tests to match new lib.js API Benefits: - No startup delay from clipboard detection process - Robust fallback if clipboard tool becomes unavailable - Better error messages with stderr details from failed commands
92 lines
2.9 KiB
JavaScript
92 lines
2.9 KiB
JavaScript
function parsePeers(peerMap) {
|
|
if (!peerMap) return []
|
|
return Object.keys(peerMap).map(function (key) {
|
|
var p = peerMap[key]
|
|
return {
|
|
hostname: p.HostName || key,
|
|
ip: (p.TailscaleIPs && p.TailscaleIPs.length) ? p.TailscaleIPs[0] : "",
|
|
online: p.Online || false,
|
|
exitNode: p.ExitNodeOption || false
|
|
}
|
|
})
|
|
}
|
|
|
|
function makeExitNodeCommand(hostname) {
|
|
return ["tailscale", "set", "--exit-node=" + hostname]
|
|
}
|
|
|
|
function findActiveExitNode(peerMap) {
|
|
if (!peerMap) return ""
|
|
for (const key in peerMap) {
|
|
const p = peerMap[key]
|
|
if (p.ExitNode) {
|
|
return p.HostName || key
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
var safeClipboardTools = ["dms", "wl-copy", "clipmanctl"]
|
|
|
|
var clipboardCmdMap = {
|
|
"dms": "dms cl copy",
|
|
"wl-copy": "wl-copy",
|
|
"clipmanctl": "clipmanctl copy"
|
|
}
|
|
|
|
function validateClipboardTool(tool) {
|
|
return typeof tool === "string" && safeClipboardTools.includes(tool)
|
|
}
|
|
|
|
function buildCopyCommand(text, tool) {
|
|
if (!validateClipboardTool(tool)) return null
|
|
var cmd = clipboardCmdMap[tool]
|
|
if (!cmd) return null
|
|
var escaped = text.replace(/'/g, "'\\''")
|
|
return ["sh", "-c", "printf '%s' '" + escaped + "' | " + cmd]
|
|
}
|
|
|
|
function nextClipboardTool(currentTool) {
|
|
var idx = safeClipboardTools.indexOf(currentTool)
|
|
if (idx < 0) return safeClipboardTools[0]
|
|
return safeClipboardTools[(idx + 1) % safeClipboardTools.length]
|
|
}
|
|
|
|
function allClipboardTools() {
|
|
return safeClipboardTools.slice()
|
|
}
|
|
|
|
function parseStatusResult(jsonText) {
|
|
try {
|
|
const data = JSON.parse(jsonText)
|
|
return {
|
|
isConnected: data.BackendState === "Running",
|
|
tailscaleIP: (data.Self && data.Self.TailscaleIPs && data.Self.TailscaleIPs[0]) || "",
|
|
currentExitNode: findActiveExitNode(data.Peer || {}),
|
|
peers: parsePeers(data.Peer || {})
|
|
}
|
|
} catch (e) {
|
|
return { isConnected: false, tailscaleIP: "", currentExitNode: "", peers: [] }
|
|
}
|
|
}
|
|
|
|
function buildToggleCommand(isConnected) {
|
|
return isConnected ? ["tailscale", "down"] : ["tailscale", "up"]
|
|
}
|
|
|
|
function errorMessage(cmd) {
|
|
var messages = {
|
|
"up": "Failed to connect to Tailscale",
|
|
"connect": "Failed to connect to Tailscale",
|
|
"down": "Failed to disconnect from Tailscale",
|
|
"disconnect": "Failed to disconnect from Tailscale",
|
|
"set": "Failed to set exit node",
|
|
"status": "Failed to read Tailscale status"
|
|
}
|
|
return messages[cmd] || "Tailscale command failed"
|
|
}
|
|
|
|
// CommonJS export for Node.js tests (ignored by QML)
|
|
if (typeof module !== "undefined" && module.exports) {
|
|
module.exports = { parsePeers, makeExitNodeCommand, findActiveExitNode, errorMessage, validateClipboardTool, buildCopyCommand, nextClipboardTool, allClipboardTools, buildToggleCommand, parseStatusResult }
|
|
}
|