feat: implement local terminal support with shell configuration and UI integration
This commit is contained in:
@@ -541,6 +541,9 @@
|
||||
this._bindClick("btn-new-tab", () => this.profileEditor.openNew());
|
||||
this._bindClick("btn-broadcast", () => this.toggleBroadcast());
|
||||
|
||||
// Wire the "Local Machine" sidebar button
|
||||
this.sidebar.onConnectLocal = () => this.connectLocal();
|
||||
|
||||
// Right-click on broadcast button opens target selector
|
||||
const broadcastBtn = document.getElementById("btn-broadcast");
|
||||
if (broadcastBtn) {
|
||||
@@ -809,6 +812,30 @@
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a local terminal session.
|
||||
*/
|
||||
async connectLocal() {
|
||||
try {
|
||||
const sessionInfo = await window.go.api.Service.ConnectLocal();
|
||||
this.state.addSession(sessionInfo);
|
||||
this.tabs.addTab(sessionInfo);
|
||||
|
||||
const panelId = `panel-${sessionInfo.id}`;
|
||||
const prefs = this.state.preferences || {};
|
||||
this.terminals.createTerminal(sessionInfo.id, panelId, {
|
||||
terminalFontFamily: prefs.defaultFontFamily,
|
||||
terminalFontSize: prefs.defaultFontSize,
|
||||
terminalScrollback: prefs.defaultScrollback,
|
||||
terminalCursorStyle: prefs.defaultCursorStyle,
|
||||
});
|
||||
|
||||
this.notifications.info("Opening local terminal…");
|
||||
} catch (err) {
|
||||
this.notifications.error("Local terminal failed: " + err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a quick connect dialog for ad-hoc connections without saving a profile.
|
||||
*/
|
||||
|
||||
@@ -107,6 +107,14 @@
|
||||
action: () => this.app.quickConnect(),
|
||||
});
|
||||
|
||||
// Local terminal
|
||||
cmds.push({
|
||||
id: "local-terminal",
|
||||
label: "Open Local Terminal",
|
||||
section: "Actions",
|
||||
action: () => this.app.connectLocal(),
|
||||
});
|
||||
|
||||
// Tab actions
|
||||
cmds.push({
|
||||
id: "close-tab",
|
||||
|
||||
@@ -53,6 +53,12 @@
|
||||
this._knownHosts = [];
|
||||
}
|
||||
|
||||
try {
|
||||
this._defaultShell = await window.go.api.Service.GetDefaultShell();
|
||||
} catch (_) {
|
||||
this._defaultShell = "";
|
||||
}
|
||||
|
||||
this._render();
|
||||
}
|
||||
|
||||
@@ -281,6 +287,45 @@
|
||||
{ min: 180, max: 500 },
|
||||
),
|
||||
);
|
||||
|
||||
// Local shell path with browse button
|
||||
const shellGroup = createElement("div", "form-group settings-field");
|
||||
const shellLabel = createElement("label", "form-label", {
|
||||
textContent: "Local shell",
|
||||
for: "setting-localShell",
|
||||
});
|
||||
shellGroup.appendChild(shellLabel);
|
||||
|
||||
const shellRow = createElement("div", "settings-shell-row");
|
||||
const shellInput = createElement("input", "form-input", {
|
||||
type: "text",
|
||||
id: "setting-localShell",
|
||||
name: "setting-localShell",
|
||||
value: p.localShell || "",
|
||||
placeholder: this._defaultShell || "/bin/zsh",
|
||||
});
|
||||
shellRow.appendChild(shellInput);
|
||||
|
||||
const browseBtn = createElement("button", "btn btn-sm btn-secondary", {
|
||||
textContent: "Browse…",
|
||||
type: "button",
|
||||
});
|
||||
browseBtn.addEventListener("click", async () => {
|
||||
try {
|
||||
const path = await window.go.api.Service.BrowseForShell();
|
||||
if (path) {
|
||||
shellInput.value = path;
|
||||
}
|
||||
} catch (_) {}
|
||||
});
|
||||
shellRow.appendChild(browseBtn);
|
||||
shellGroup.appendChild(shellRow);
|
||||
|
||||
const shellHint = createElement("span", "form-hint", {
|
||||
textContent: "Leave empty to use system default",
|
||||
});
|
||||
shellGroup.appendChild(shellHint);
|
||||
body.appendChild(shellGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1050,6 +1095,7 @@
|
||||
"defaultScrollback",
|
||||
"defaultCursorStyle",
|
||||
"useNativePaste",
|
||||
"localShell",
|
||||
];
|
||||
|
||||
for (const field of fields) {
|
||||
|
||||
@@ -68,6 +68,9 @@
|
||||
this.el.appendChild(this._renderBulkActions());
|
||||
}
|
||||
|
||||
// Local Machine quick-connect button
|
||||
this.el.appendChild(this._renderLocalMachineItem());
|
||||
|
||||
// Favourites
|
||||
const favourites = profiles.filter((p) => p.favourite);
|
||||
if (favourites.length > 0) {
|
||||
@@ -531,6 +534,36 @@
|
||||
);
|
||||
}
|
||||
|
||||
/** @private */
|
||||
_renderLocalMachineItem() {
|
||||
const item = createElement("div", "sidebar-item sidebar-item-local");
|
||||
item.addEventListener("mousedown", (e) => e.preventDefault());
|
||||
|
||||
const indicator = createElement(
|
||||
"span",
|
||||
"protocol-indicator proto-local",
|
||||
{
|
||||
title: "Local",
|
||||
},
|
||||
);
|
||||
item.appendChild(indicator);
|
||||
|
||||
const textWrapper = createElement("div", "sidebar-item-text");
|
||||
const nameEl = createElement("span", "sidebar-item-name", {
|
||||
textContent: "Local Machine",
|
||||
});
|
||||
textWrapper.appendChild(nameEl);
|
||||
item.appendChild(textWrapper);
|
||||
|
||||
item.addEventListener("click", () => {
|
||||
if (this.onConnectLocal) {
|
||||
this.onConnectLocal();
|
||||
}
|
||||
});
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
/** @private */
|
||||
_renderBulkActions() {
|
||||
const bar = createElement("div", "sidebar-bulk-actions");
|
||||
|
||||
@@ -480,7 +480,9 @@
|
||||
const protoLabel =
|
||||
sessionInfo.protocol === "github"
|
||||
? "GitHub"
|
||||
: sessionInfo.protocol.toUpperCase();
|
||||
: sessionInfo.protocol === "local"
|
||||
? "Local"
|
||||
: sessionInfo.protocol.toUpperCase();
|
||||
const badge = createElement(
|
||||
"span",
|
||||
`tab-badge badge-${sessionInfo.protocol}`,
|
||||
|
||||
@@ -229,6 +229,8 @@
|
||||
return "proto-github";
|
||||
case "vnc":
|
||||
return "proto-vnc";
|
||||
case "local":
|
||||
return "proto-local";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user